# Getting Started With Authoritex
## About
An Elixir library for searching and fetching controlled vocabulary authority terms, inspired by the [Samvera Community's](https://github.com/samvera) [Questioning Authority](https://github.com/samvera/questioning_authority).
`Authoritex` provides an [Elixir behaviour](https://elixir-lang.org/getting-started/typespecs-and-behaviours.html#behaviours) that defines a specification for creating authorities. Each authority is a module which has to implement at least 5 public functions: `can_resolve?/1`, `code/0`, `description/0`, `fetch/1`, and `search/2`.
* `can_resolve?/1` Returns true if the module can resolve the given identifier
* `code/0` Returns the unique short code for the authority
* `description/0` Returns a human-readable description of the authority
* `fetch/1` Fetches a label (and optional hint string) for a specified resource
* `search/2` Returns a list of search results (and optional hints) matching a query
## Installation
```elixir
Mix.install([
{:authoritex, "~> 0.7.0"},
{:kino, "~> 0.5.2"}
])
```
## Configuration
```elixir
Application.put_env(:authoritex, :authorities, [
Authoritex.FAST.CorporateName,
Authoritex.FAST.EventName,
Authoritex.FAST.Form,
Authoritex.FAST.Geographic,
Authoritex.FAST.Personal,
Authoritex.FAST.Topical,
Authoritex.FAST.UniformTitle,
Authoritex.FAST,
Authoritex.GeoNames,
Authoritex.Getty.AAT,
Authoritex.Getty.TGN,
Authoritex.Getty.ULAN,
Authoritex.Getty,
Authoritex.LOC.Languages,
Authoritex.LOC.Names,
Authoritex.LOC.SubjectHeadings
])
```
## List configured authorities
`Authoritex.authorities/0` returns a list of tuples describing all configured authorities.
```elixir
Authoritex.authorities()
```
## Fetch records by id
`Authoritex.fetch/1` returns a map with the `:label`, `:id`, `:hint`, and `:qualified_label` for an authority record given an id (must be a string).
```elixir
# Known authority and record identifier
Authoritex.fetch("http://id.loc.gov/authorities/names/no2011087251")
```
`Authoritex.fetch/1` returns the error tuple `{:error, 404}` given an unknown id for a properly configured authority.
```elixir
# Known authority with unknown record identifier
Authoritex.fetch("http://id.loc.gov/authorities/names/unknown-id")
```
`Authoritex.fetch/1` returns the error tuple `{:error, :unknown_authority}` given an id for an unknown authority.
```elixir
# Unknown authority
Authoritex.fetch("http://fake.authority.org/not-a-real-thing")
```
## Search an authority
`Authoritex.search/2` performs a search with an authority and a search term. The following select menu lets you choose a supported authority:
```elixir
search_authority =
Kino.Input.select(
"Select Authority",
[
{"aat", "Getty Art & Architecture Thesaurus (AAT)"},
{"fast", "Faceted Application of Subject Terminology (FAST) -- Base"},
{"fast-corporate-name", "Faceted Application of Subject Terminology -- Corporate Name"},
{"fast-event-name", "Faceted Application of Subject Terminology -- Event Name"},
{"fast-form", "Faceted Application of Subject Terminology -- Form/Genre"},
{"fast-geographic", "Faceted Application of Subject Terminology -- Geographic"},
{"fast-personal", "Faceted Application of Subject Terminology -- Personal"},
{"fast-topical", "Faceted Application of Subject Terminology -- Topical"},
{"fast-uniform-title", "Faceted Application of Subject Terminology -- Uniform Title"},
{"getty", "Getty -- Base"},
{"lclang", "Library of Congress MARC List for Languages"},
{"lcnaf", "Library of Congress Name Authority File"},
{"lcsh", "Library of Congress Subject Headings"},
{"tgn", "Getty Thesaurus of Geographic Names (TGN)"},
{"ulan", "Getty Union List of Artist Names (ULAN)"}
]
)
```
Enter a search term in the text input:
```elixir
search_term = Kino.Input.text("Search term")
```
Perform the search using the values chosen above:
<!-- livebook:{"reevaluate_automatically":true} -->
```elixir
Authoritex.search(Kino.Input.read(search_authority), Kino.Input.read(search_term))
```
`Authoritex.search/3` takes the same arguments as `Authoritex.search/2` along with a third argument (must be an integer) to limit the result count of the search.
```elixir
Authoritex.search("lcsh", "library", 3)
```
```elixir
# Error recevied when searching an unknown authority
Authoritex.search("not-an-authority", "test")
```
## Testing
`Authoritex` provides a mock for testing purposes in consuming applications.
Configure the mock with a few lines of code:
<!-- livebook:{"force_markdown":true} -->
```elixir
# In test.exs:
config :authoritex, authorities: [Authoritex.Mock]
# In test_helper.exs:
Authoritex.Mock.init()
```
Use the `Authoritex.Mock.set_data/1` function to add data for testing purposes, typically in a `setup` block. The example module below demonstrates an `ExUnit` test using the `Authoritex.Mock`. The `Authoritex.Mock.search/1` function returns all mock data regardless of the string passed to the function. (Note: the following code is a code sample that is not runnable like the examples above):
<!-- livebook:{"force_markdown":true} -->
```elixir
defmodule MyTest do
alias Authoritex.Mock
use ExUnit.Case, async: true
@data [
%{
id: "mock:result1",
label: "First Result",
qualified_label: "First Result (1)",
hint: "(1)"
},
%{
id: "mock:result2",
label: "Second Result",
qualified_label: "Second Result (2)",
hint: "(2)"
},
%{id: "mock:result3", label: "Third Result", qualified_label: "Third Result (3)", hint: "(3)"}
]
setup do
Mock.set_data(@data)
:ok
end
test "results" do
with {:ok, results} <- Mock.search("any") do
assert length(results) == length(@data)
end
end
end
```