defmodule Circlex.Api do
@moduledoc """
A module to build requests for Circle API calls.
"""
def env_host(), do: Application.get_env(:circlex_api, :host)
def auth(), do: Application.get_env(:circlex_api, :auth)
@doc """
A hook to give last chance for application to transform, log, or validate a request before
going to circle.
"""
def pre_request_hook(),
do: Application.get_env(:circlex_api, :pre_request_hook, &Function.identity/1)
@doc """
A hook to give chance for application to transform, log, or validate a raw response before
it is decoded into structs
"""
def after_request_hook(),
do: Application.get_env(:circlex_api, :after_request_hook, &Function.identity/1)
# Has to conform to the HTTPoison interface
@http_client Application.get_env(:circlex_api, :http_client, HTTPoison)
def http_client(), do: @http_client
defmodule Tooling do
def not_implemented(), do: {:error, %{error: "Not implemented by Circlex client"}}
def api_get(path, opts) do
api_request(:get, path, nil, opts)
end
def api_post(path, params, opts) do
api_request(:post, path, params, opts)
end
def api_delete(path, opts) do
api_request(:delete, path, nil, opts)
end
# TODO: Handle errors better
defp api_request(method, path, params, opts) do
host = Keyword.get(opts, :host, Circlex.Api.env_host())
auth = Keyword.get(opts, :auth, Circlex.Api.auth())
pre_request_hook = Keyword.get(opts, :pre_request_hook, Circlex.Api.pre_request_hook())
after_request_hook =
Keyword.get(opts, :after_request_hook, Circlex.Api.after_request_hook())
headers =
Keyword.get(opts, :headers, [
{"Content-Type", "application/json"},
{"Accept", "application/json"},
{"Authorization", "Bearer #{auth}"}
])
# just give all the headers you want
no_data_key = Keyword.get(opts, :no_data_key, false)
http_client = Keyword.get(opts, :http_client, Circlex.Api.http_client())
request =
%HTTPoison.Request{
method: method,
url: Path.join(host, path),
body: if(params, do: Jason.encode!(params), else: <<>>),
headers: headers
}
|> pre_request_hook.()
response = http_client.request(request)
after_request_hook.({request, response})
case response do
{:ok, %HTTPoison.Response{status_code: status_code, body: body}} ->
with {:ok, json} <- Jason.decode(body) do
case status_code do
code when code in 200..299 ->
if no_data_key do
{:ok, json}
else
case json do
%{"data" => data} ->
{:ok, data}
_ ->
{:error, %{error: "Expected data key, but not given", response: json}}
end
end
_ ->
case json do
%{"error" => error} ->
{:error, %{error: error}}
%{"code" => code, "message" => message} ->
{:error, %{code: code, message: message}}
_ ->
{:error, json}
end
end
end
{:error, %HTTPoison.Error{reason: reason}} ->
{:error, %{reason: to_string(reason)}}
end
end
end
end