defmodule Elastix.HTTP do
@moduledoc """
A thin [HTTPoison](https://github.com/edgurgel/httpoison) wrapper.
"""
use HTTPoison.Base
alias Elastix.JSON
@type resp :: {:ok, HTTPoison.Response.t()} | {:error, HTTPoison.Error.t()}
@doc false
def prepare_url(url, path) when is_binary(path), do: URI.merge(url, path) |> to_string
def prepare_url(url, parts) when is_list(parts), do: prepare_url(url, Path.join(parts))
@doc false
def request(method, url, body \\ "", headers \\ [], options \\ []) do
query_url =
if Keyword.has_key?(options, :params) do
url <> "?" <> URI.encode_query(options[:params])
else
url
end
full_url = to_string(query_url)
body = process_request_body(body)
full_headers =
headers
|> add_content_type_header
|> add_shield_header
|> add_custom_headers(method, full_url, body)
options = Keyword.merge(default_httpoison_options(), options)
{m, f, _a} = Elastix.config(:test_request_mfa) || {HTTPoison.Base, :request, []}
request = %HTTPoison.Request{
method: method,
url: full_url,
headers: process_request_headers(full_headers),
body: process_request_body(body),
options: options
}
apply(m, f, [
__MODULE__,
request,
&process_response_status_code/1,
&process_response_headers/1,
&process_response_body/1,
&process_response/1
])
end
@doc false
def process_response_body(""), do: ""
def process_response_body(body) do
case body |> to_string |> JSON.decode() do
{:error, _} -> body
{:ok, decoded} -> decoded
end
end
@doc """
Encodes an enumerable (`params`) into a query string and appends it to `root`.
## Examples
iex> Elastix.HTTP.append_query_string("/path", %{a: 1, b: 2})
"/path?a=1&b=2"
"""
@spec append_query_string(String.t(), term()) :: String.t()
def append_query_string(root, params), do: "#{root}?#{URI.encode_query(params)}"
defp default_httpoison_options do
Elastix.config(:httpoison_options, [])
end
defp add_content_type_header(headers) do
[{"Content-Type", "application/json; charset=UTF-8"} | headers]
end
defp add_shield_header(headers) do
if Elastix.config(:shield) do
username = Elastix.config(:username)
password = Elastix.config(:password)
encoded = Base.encode64("#{username}:#{password}")
Keyword.put(headers, :Authorization, "Basic " <> encoded)
else
headers
end
end
defp add_custom_headers(headers, method, url, body) do
case Elastix.config(:custom_headers) do
nil ->
headers
{mod, fun, args} ->
request = %{method: method, headers: headers, url: url, body: body}
case apply(mod, fun, [request | args]) do
headers when is_list(headers) -> headers
_ -> raise("custom headers must return a header list (keyword list)")
end
_ ->
raise("Custom headers accepts a tuple of `{Module, :fun, []}` only.")
end
end
end