LIVEBOOK.livemd

# 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
```