1defmodule Atex.XRPC do
2 alias Atex.{HTTP, XRPC}
3
4 # TODO: automatic user-agent, and env for changing it
5
6 # TODO: consistent struct shape/protocol for Lexicon schemas so that user can pass in
7 # an object (hopefully validated by its module) without needing to specify the
8 # name & opts separately, and possibly verify the output response against it?
9
10 # TODO: auto refresh, will need to return a client instance in each method.
11
12 @doc """
13 Perform a HTTP GET on a XRPC resource. Called a "query" in lexicons.
14 """
15 @spec get(XRPC.Client.t(), String.t(), keyword()) :: HTTP.Adapter.result()
16 def get(%XRPC.Client{} = client, name, opts \\ []) do
17 opts = put_auth(opts, client.access_token)
18 HTTP.get(url(client, name), opts)
19 end
20
21 @doc """
22 Perform a HTTP POST on a XRPC resource. Called a "prodecure" in lexicons.
23 """
24 @spec post(XRPC.Client.t(), String.t(), keyword()) :: HTTP.Adapter.result()
25 def post(%XRPC.Client{} = client, name, opts \\ []) do
26 # TODO: look through available HTTP clients and see if they have a
27 # consistent way of providing JSON bodies with auto content-type. If not,
28 # create one for adapters.
29 opts = put_auth(opts, client.access_token)
30 HTTP.post(url(client, name), opts)
31 end
32
33 @doc """
34 Like `get/3` but is unauthenticated by default.
35 """
36 @spec unauthed_get(String.t(), String.t(), keyword()) :: HTTP.Adapter.result()
37 def unauthed_get(endpoint, name, opts \\ []) do
38 HTTP.get(url(endpoint, name), opts)
39 end
40
41 @doc """
42 Like `post/3` but is unauthenticated by default.
43 """
44 @spec unauthed_post(String.t(), String.t(), keyword()) :: HTTP.Adapter.result()
45 def unauthed_post(endpoint, name, opts \\ []) do
46 HTTP.post(url(endpoint, name), opts)
47 end
48
49 # TODO: use URI module for joining instead?
50 @spec url(XRPC.Client.t() | String.t(), String.t()) :: String.t()
51 defp url(%XRPC.Client{endpoint: endpoint}, name), do: url(endpoint, name)
52 defp url(endpoint, name) when is_binary(endpoint), do: "#{endpoint}/xrpc/#{name}"
53
54 @doc """
55 Put an `authorization` header into a keyword list of options to pass to a HTTP client.
56 """
57 @spec put_auth(keyword(), String.t()) :: keyword()
58 def put_auth(opts, token),
59 do: put_headers(opts, authorization: "Bearer #{token}")
60
61 @spec put_headers(keyword(), keyword()) :: keyword()
62 defp put_headers(opts, headers) do
63 opts
64 |> Keyword.put_new(:headers, [])
65 |> Keyword.update(:headers, [], &Keyword.merge(&1, headers))
66 end
67end