diff --git a/config/config.exs b/config/config.exs index f55b03d7..e96efde5 100644 --- a/config/config.exs +++ b/config/config.exs @@ -42,6 +42,8 @@ config :music_library, LastFm, config :music_library, MusicBrainz, user_agent: user_agent +config :music_library, Discogs, personal_access_token: "change me", user_agent: user_agent + # Configure esbuild (the version is required) config :esbuild, version: "0.17.11", diff --git a/config/runtime.exs b/config/runtime.exs index bbe74d22..71c3d955 100644 --- a/config/runtime.exs +++ b/config/runtime.exs @@ -32,6 +32,10 @@ if openai_key = System.get_env("OPENAI_KEY") do config :music_library, OpenAI, api_key: openai_key end +if personal_access_token = System.get_env("DISCOGS_PERSONAL_ACCESS_TOKEN") do + config :music_library, Discogs, personal_access_token: personal_access_token +end + if timezone = System.get_env("TIMEZONE") do config :music_library, :timezone, timezone end diff --git a/config/test.exs b/config/test.exs index 48085727..c59478bf 100644 --- a/config/test.exs +++ b/config/test.exs @@ -53,6 +53,12 @@ config :music_library, MusicBrainz, max_retries: 0 ] +config :music_library, Discogs, + req_options: [ + plug: {Req.Test, Discogs.API}, + max_retries: 0 + ] + config :phoenix_test, :endpoint, MusicLibraryWeb.Endpoint config :floki, :html_parser, Floki.HTMLParser.FastHtml diff --git a/lib/discogs.ex b/lib/discogs.ex new file mode 100644 index 00000000..df538ed8 --- /dev/null +++ b/lib/discogs.ex @@ -0,0 +1,11 @@ +defmodule Discogs do + alias Discogs.API + + def get_artist(id) do + discogs_config = discogs_config() + + API.get_artist(id, discogs_config) + end + + defp discogs_config, do: Discogs.Config.resolve(:music_library) +end diff --git a/lib/discogs/api.ex b/lib/discogs/api.ex new file mode 100644 index 00000000..979d0d85 --- /dev/null +++ b/lib/discogs/api.ex @@ -0,0 +1,45 @@ +defmodule Discogs.API do + @moduledoc """ + Interface to the Discogs API. + """ + + require Logger + + def get_artist(id, config) do + config + |> new_request() + |> Req.merge(url: "/artists/#{id}") + |> get_request() + end + + defp new_request(config) do + Req.new( + base_url: "https://api.discogs.com", + max_retries: 1, + user_agent: config.user_agent, + auth: "Discogs token=#{config.personal_access_token}" + ) + |> Req.Request.merge_options(config.req_options) + |> Req.Request.append_request_steps(log_attempt: &log_attempt/1) + end + + defp get_request(request) do + case Req.get(request) do + {:ok, response} when response.status == 200 -> + {:ok, response.body} + + # all non-success responses can be treated as errors + {:ok, response} -> + {:error, response.body} + + error -> + error + end + end + + defp log_attempt(request) do + url = URI.to_string(request.url) + Logger.debug("Fetching data from #{url}") + request + end +end diff --git a/lib/discogs/config.ex b/lib/discogs/config.ex new file mode 100644 index 00000000..e05dfefd --- /dev/null +++ b/lib/discogs/config.ex @@ -0,0 +1,39 @@ +defmodule Discogs.Config do + @type t :: %__MODULE__{ + personal_access_token: String.t(), + user_agent: String.t(), + req_options: Keyword.t() + } + + @enforce_keys [:personal_access_token] + defstruct personal_access_token: "", + user_agent: "change me", + req_options: [] + + @schema NimbleOptions.new!( + personal_access_token: [ + type: :string, + required: true + ], + user_agent: [ + type: :string, + required: false, + default: "change me" + ], + req_options: [ + type: :keyword_list, + required: false, + default: [] + ] + ) + + @doc NimbleOptions.docs(@schema) + @spec resolve(Application.app()) :: t | no_return + def resolve(otp_app) do + app_config = + Application.get_env(otp_app, Discogs) + |> NimbleOptions.validate!(@schema) + + struct(__MODULE__, app_config) + end +end