Current section

Files

Jump to
gem lib client.ex
Raw

lib/client.ex

defmodule Gem.Client do
@moduledoc """
Documentation for `Gem Client`.
"""
def get(path \\ "/", query_parameters \\ %{}) do
url = encode_query_params(path, query_parameters)
headers = build_request_headers()
Mojito.request(method: :get, url: url, headers: headers)
|> process_response()
end
def post(path \\ "/", body \\ %{}, query_parameters \\ %{}, opts \\ []) do
mutate(:post, path, body, query_parameters, opts)
end
def put(path \\ "/", body \\ %{}, query_parameters \\ %{}) do
mutate(:put, path, body, query_parameters)
end
def delete(path \\ "/", body \\ %{}, query_parameters \\ %{}) do
mutate(:delete, path, body, query_parameters)
end
defp mutate(method, path \\ "/", body \\ %{}, query_parameters \\ %{}, opts \\ []) do
is_multipart? = Keyword.get(opts, :is_multipart?, false)
headers = Keyword.get(opts, :headers, [])
content_type = if is_multipart?, do: "multipart/form-data", else: "application/json"
url = encode_query_params(path, query_parameters)
content_type_headers =
if is_content_type_set?(headers), do: [], else: [{"Content-Type", content_type}]
headers = build_request_headers() ++ headers ++ content_type_headers
body = if is_multipart?, do: body, else: Jason.encode!(body)
Mojito.request(method: method, url: url, headers: headers, body: body)
|> process_response()
end
defp get_config do
Application.get_all_env(:gem)
end
defp process_response(response) do
case response do
{:ok, res = %Mojito.Response{status_code: status}} when status in 200..300 ->
body = Jason.decode!(res.body)
{:ok, body}
{:ok, res = %Mojito.Response{status_code: status}} when status >= 400 ->
body = Jason.decode!(res.body)
e = %Gem.APIError{}
code = Map.get(body, "code", e.code)
description = Map.get(body, "description", e.description)
{:error, %{e | code: code, description: description, status: status}}
e ->
{:error, %Gem.APIError{}, e}
end
end
defp build_request_headers(_req_config \\ []) do
config = get_config()
api_key = Keyword.get(config, :api_key)
timestamp = get_timestamp()
signature = create_signature(timestamp)
[
{"X-Gem-Access-Timestamp", timestamp},
{"X-Gem-Signature", signature},
{"X-Gem-Api-Key", api_key}
]
end
defp encode_query_params(path \\ "/", query \\ nil) do
parsed_path =
URI.parse(path)
|> Map.get(:path)
base =
get_config()
|> Keyword.get(:base_url)
|> URI.parse()
|> Map.put(:path, parsed_path)
if is_map(query) do
Map.put(base, :query, URI.encode_query(query))
|> URI.to_string()
else
URI.to_string(base)
end
end
defp get_timestamp do
DateTime.utc_now()
|> DateTime.to_unix()
end
defp create_signature(timestamp) do
config = get_config()
secret = Keyword.get(config, :secret)
api_key = Keyword.get(config, :api_key)
data = "#{api_key}:#{timestamp}"
:crypto.hmac(:sha256, secret, data)
|> Base.encode16(case: :lower)
end
defp is_content_type_set?(headers \\ []) do
is_header_set? = Enum.find(headers, fn h ->
case h do
{"content-type", _ct} ->
true
{"Content-Type", _ct} ->
true
_ ->
false
end
end)
end
end