Packages

Quickly create API clients using module attributes.

Current section

Files

Jump to
exrequester lib request.ex
Raw

lib/request.ex

defmodule EXRequester.Request do
@moduledoc """
Structure that holds a request
"""
defstruct [:method, :base_url, :path, :headers_template, :body, :query_keys, :decoder, :body_block]
@doc """
Return a new request
Parameters:
* `method` - The http method
* `path` - The path to request
"""
def new(method: method, path: path), do: %EXRequester.Request{method: method, path: path}
@doc """
Adds a base url to the request
Parameters:
* `request` - request to update
* `base_url` - base url to add
"""
def add_base_url(request, base_url), do: Map.put(request, :base_url, base_url)
@doc """
Adds headers key to the request
Parameters:
* `request` - request to update
* `headers_keys` - header keys to format
"""
def add_headers_keys(request, headers_keys), do: Map.put(request, :headers_template, headers_keys)
@doc """
Adds body to the request
Parameters:
* `request` - request to update
* `body` - body to add
"""
def add_body(request, body) do
Map.put(request, :body, body)
end
@doc """
* `request` - request to update
Adds a decoder block that will be called with the response
* `decoder` - decoder block to call
"""
def add_decoder(request, nil), do: request
def add_decoder(request, decoder) do
Map.put(request, :decoder, decoder)
end
@doc """
Adds a body block to the request
Parameters:
* `request` - request to update
* `body_block` - body block to be called after request
"""
def add_body_block(request, body_block) do
Map.put(request, :body_block, body_block)
end
@doc """
Adds query keys to the request
Parameters:
* `request` - request to update
* `query_keys` - query keys to add
"""
def add_query_keys(request, query_keys), do: Map.put(request, :query_keys, query_keys)
def has_params(request) do
header_keys = Map.get(request, :headers_template) || []
|> Keyword.values
|> Enum.filter(fn item -> is_atom(item) end)
all_params = header_keys ++
(Map.get(request, :path) |> url_params) ++
(Map.get(request, :query_keys) || [])
all_params |> length > 0
end
@doc """
Return a prepared url
Parameters:
* `request` - the request
* `params` - the url to fill in the format url
"""
def prepared_url(request, nil) do
join_parts(request.base_url, request.path)
end
def prepared_url(request, params) do
full_url =
params
|> filter_body
|> filter_body_decoder
|> Enum.reduce(request.path, fn ({key, value}, acc) ->
String.replace(acc, "{#{to_string(key)}}", to_string(value))
end)
|> (fn item -> join_parts(request.base_url, item) end).()
url_query = prepared_query(request, params)
join_urls(full_url, url_query)
end
@doc """
Return prepared header keyword list
Parameters:
* `request` - the request
* `header_params` - the header parametrs to update with the keys
"""
def prepared_headers(request, nil) do
header_keys = Map.get(request, :headers_template, [])
end
def prepared_headers(request, header_params) do
header_params = header_params |> filter_body
header_keys = Map.get(request, :headers_template, [])
Enum.map(header_keys, fn {key, value} ->
prepare_header_item(template_key: key, template_value: value, header_params: header_params)
end) || []
end
@doc """
Return a prepared query string
Parameters:
* `request` - the request
* `params` - the parameters to use to create the query string
"""
def prepared_query(request, params) do
query_keys = Map.get(request, :query_keys) || []
params
|> Enum.filter(fn {key, _} ->
key in query_keys
end)
|> Enum.map(fn {key, value} ->
"#{key}=#{prepare_query_value(value)}"
end)
|> Enum.join("&")
end
# Private
defp join_urls(url, ""), do: url
defp join_urls(url, query), do: "#{url}?#{query}"
def prepared_body(request) do
_prepared_body(Map.get(request, :body)) || ""
end
defp _prepared_body(nil), do: nil
defp _prepared_body(body) when is_tuple(body) do
body_to_json(Tuple.to_list(body))
end
defp _prepared_body(body) when is_map(body) or is_list(body) do
body_to_json(body)
end
defp _prepared_body(body), do: body
defp body_to_json(body) do
case Poison.encode(body) do
{:ok, json} -> json
_ -> ""
end
end
defp prepare_header_item(template_key: key, template_value: value, header_params: _)
when is_binary(value) do
{key, value}
end
defp prepare_header_item(template_key: key, template_value: value, header_params: header_params) do
{key, header_params[value]}
end
defp prepare_query_value(value) when is_list(value) do
Enum.join(value, ",")
end
defp prepare_query_value(value), do: value
defp join_parts(base, path) do
join_parts(String.slice(base, 0..-2),
String.slice(base, -1..-1),
String.slice(path, 0..0),
String.slice(path, 1..-1))
end
defp join_parts(base, "/", "/", path) do
base <> "/" <> path
end
defp join_parts(base, "/", path_start, path) do
base <> "/" <> path_start <> path
end
defp join_parts(base, base_end, "/", path) do
base <> base_end <> "/" <> path
end
defp join_parts(base, base_end, path_start, path) do
base <> base_end <> "/" <> path_start <> path
end
defp filter_body(params) do
Enum.filter(params, fn {key, _} ->
key != :body
end)
end
defp filter_body_decoder(params) do
Enum.filter(params, fn {key, _} ->
key != :decoder
end)
end
defp url_params(nil), do: []
defp url_params(url) do
Regex.scan(~r/{.*}/r, url)
|> Enum.map(fn ([i]) -> String.slice(i, 1..-2) end)
end
end