defmodule Lob.Util do
@moduledoc """
Module responsible for transforming arguments for requests.
"""
@doc """
Transforms a map of request params to a URL encoded query string.
## Example
iex> Lob.Util.build_query_string(%{count: 1, include: ["total_count"], metadata: %{name: "Larry"}})
"count=1&include%5B%5D=total_count&metadata%5Bname%5D=Larry"
"""
@spec build_query_string(map) :: String.t
def build_query_string(params) when is_map(params) do
params
|> Enum.reduce([], &(&2 ++ transform_argument(&1)))
|> URI.encode_query()
end
@doc """
Transforms a map to a tuple recognized by HTTPoison/hackney for use as a
multipart request body.
## Example
iex> Lob.Util.build_body(%{description: "body", to: %{name: "Larry", species: "Lobster"}, front: %{local_path: "a/b/c"}})
{:multipart, [{"description", "body"}, {:file, "a/b/c", {"form-data", [name: "front", filename: "a/b/c"]}, []}, {"to[name]", "Larry"}, {"to[species]", "Lobster"}]}
"""
@spec build_body(map) :: {:multipart, list}
def build_body(body) when is_map(body) do
result = {:multipart, Enum.reduce(body, [], &(&2 ++ transform_argument(&1)))}
result
end
@doc """
Transforms a map to a list of tuples recognized by HTTPoison/hackeny for use
as request headers.
## Example
iex> Lob.Util.build_headers(%{"Idempotency-Key" => "abc123", "Lob-Version" => "2017-11-08"})
[{"Idempotency-Key", "abc123"}, {"Lob-Version", "2017-11-08"}]
"""
@spec build_headers(map) :: [{String.t, String.t}]
def build_headers(headers) do
headers
|> Enum.to_list
|> Enum.map(fn {k, v} -> {to_string(k), to_string(v)} end)
end
@spec transform_argument({any, any}) :: list | no_return
defp transform_argument({:merge_variables, v}), do: transform_argument({"merge_variables", v})
defp transform_argument({"merge_variables", v}) do
[{"merge_variables", Poison.encode!(v), [{"Content-Type", "application/json"}]}]
end
defp transform_argument({k, v}) when is_list(v) do
Enum.map(v, fn e ->
{"#{to_string(k)}[]", to_string(e)}
end)
end
# For context on the format of the struct see:
# https://github.com/benoitc/hackney/issues/292
defp transform_argument({k, %{local_path: file_path}}) do
[{:file, file_path, {"form-data", [name: to_string(k), filename: file_path]}, []}]
end
defp transform_argument({k, v}) when is_map(v) do
Enum.map(v, fn {sub_k, sub_v} ->
{"#{to_string(k)}[#{to_string(sub_k)}]", to_string(sub_v)}
end)
end
defp transform_argument({k, v}) do
[{to_string(k), to_string(v)}]
end
end