Music streaming on ATProto!
1# AUTOGENERATED: This file was generated using the mix task `lexgen`. 2defmodule Atproto do 3 @default_pds_hostname Application.compile_env!(:comet, :default_pds_hostname) 4 5 @typedoc """ 6 A type representing the names of the options that can be passed to `query/3` and `procedure/3`. 7 """ 8 @type xrpc_opt :: :pds_hostname | :authorization 9 10 @typedoc """ 11 A keyword list of options that can be passed to `query/3` and `procedure/3`. 12 """ 13 @type xrpc_opts :: [{xrpc_opt(), any()}] 14 15 @doc """ 16 Converts a JSON string, or decoded JSON map, into a struct based on the given module. 17 18 This function uses `String.to_existing_atom/1` to convert the keys of the map to atoms, meaning this will throw an error if the input JSON contains keys which are not already defined as atoms in the existing structs or codebase. 19 """ 20 @spec decode_to_struct(module(), binary() | map()) :: map() 21 def decode_to_struct(module, json) when is_binary(json) do 22 decode_to_struct(module, Jason.decode!(json, keys: :atoms!)) 23 end 24 25 def decode_to_struct(module, map) when is_map(map) do 26 Map.merge(module.new(), map) 27 end 28 29 @doc """ 30 Raises an error if any required parameters are missing from the given map. 31 """ 32 @spec ensure_required(map(), [String.t()]) :: map() 33 def ensure_required(params, required) do 34 if Enum.all?(required, fn key -> Map.has_key?(params, key) end) do 35 params 36 else 37 raise ArgumentError, "Missing one or more required parameters: #{Enum.join(required, ", ")}" 38 end 39 end 40 41 @doc """ 42 Executes a "GET" HTTP request and returns the response body as a map. 43 44 If the `:pds_hostname` option is not provided, the default PDS hostname as provided in the compile-time configuration will be used. 45 """ 46 @spec query(map(), String.t(), xrpc_opts()) :: Req.Request.t() 47 def query(params, target, opts \\ []) do 48 target 49 |> endpoint(opts) 50 |> URI.new!() 51 |> URI.append_query(URI.encode_query(params)) 52 |> Req.get(build_req_auth(opts)) 53 |> handle_response(opts) 54 end 55 56 @doc """ 57 Executes a "POST" HTTP request and returns the response body as a map. 58 59 If the `:pds_hostname` option is not provided, the default PDS hostname as provided in the compile-time configuration will be used. 60 """ 61 @spec procedure(map(), String.t(), xrpc_opts()) :: {:ok | :refresh | :error, map()} 62 def procedure(params, target, opts \\ []) do 63 req_opts = 64 opts 65 |> build_req_auth() 66 |> build_req_headers(opts, target) 67 |> build_req_body(params, target) 68 69 target 70 |> endpoint(opts) 71 |> URI.new!() 72 |> Req.post(req_opts) 73 |> handle_response(opts) 74 end 75 76 defp build_req_auth(opts) do 77 case Keyword.get(opts, :access_token) do 78 nil -> 79 case Keyword.get(opts, :admin_token) do 80 nil -> 81 [] 82 83 token -> 84 [auth: {:basic, "admin:#{token}"}] 85 end 86 87 token -> 88 [auth: {:bearer, token}] 89 end 90 end 91 92 defp build_req_headers(req_opts, opts, "com.atproto.repo.uploadBlob") do 93 [ 94 {:headers, 95 [ 96 {"Content-Type", Keyword.fetch!(opts, :content_type)}, 97 {"Content-Length", Keyword.fetch!(opts, :content_length)} 98 ]} 99 | req_opts 100 ] 101 end 102 103 defp build_req_headers(req_opts, _opts, _target), do: req_opts 104 105 defp build_req_body(opts, blob, "com.atproto.repo.uploadBlob") do 106 [{:body, blob} | opts] 107 end 108 109 defp build_req_body(opts, %{} = params, _target) when map_size(params) > 0 do 110 [{:json, params} | opts] 111 end 112 113 defp build_req_body(opts, _params, _target), do: opts 114 115 defp endpoint(target, opts) do 116 (Keyword.get(opts, :pds_hostname) || @default_pds_hostname) <> "/xrpc/" <> target 117 end 118 119 defp handle_response({:ok, %Req.Response{} = response}, opts) do 120 case response.status do 121 x when x in 200..299 -> 122 {:ok, response.body} 123 124 _ -> 125 if response.body["error"] == "ExpiredToken" do 126 {:ok, user} = 127 Com.Atproto.Server.RefreshSession.main(%{}, 128 access_token: Keyword.get(opts, :refresh_token) 129 ) 130 131 {:refresh, user} 132 else 133 {:error, response.body} 134 end 135 end 136 end 137 138 defp handle_response(error, _opts), do: error 139 140 @doc """ 141 Converts a "map-like" entity into a standard map. This will also omit any entries that have a `nil` value. 142 143 This is useful for converting structs or schemas into regular maps before sending them over XRPC requests. 144 145 You may optionally pass in an keyword list of options: 146 147 - `:stringify` - `boolean` - If `true`, converts the keys to strings. Otherwise, converts keys to atoms. Default is `false`. 148 - *Note*: When `false`, this feature uses the `to_existing_atom/1` function to avoid reckless conversion of string keys. 149 """ 150 @spec to_map(map() | struct()) :: map() 151 def to_map(%{__struct__: _} = m, opts \\ []) do 152 string_keys = Keyword.get(opts, :stringify, false) 153 154 m 155 |> Map.drop([:__struct__, :__meta__]) 156 |> Enum.map(fn 157 {_, nil} -> 158 nil 159 160 {k, v} when is_atom(k) -> 161 if string_keys, do: {to_string(k), v}, else: {k, v} 162 163 {k, v} when is_binary(k) -> 164 if string_keys, do: {k, v}, else: {String.to_existing_atom(k), v} 165 end) 166 |> Enum.reject(&is_nil/1) 167 |> Enum.into(%{}) 168 end 169end