Current section

Files

Jump to
simplehttp lib simplehttp.ex
Raw

lib/simplehttp.ex

defmodule SimpleHttp do
alias SimpleHttp.Request
alias SimpleHttp.Response
alias SimpleHttp.Exception.BadArgument
defmacro __using__(_opts) do
quote do
import SimpleHttp
end
end
@doc """
Create virtual methods such as:
SimpleHttp.get(...)
SimpleHttp.post(...)
SimpleHttp.delete(...)
SimpleHttp.put(...)
SimpleHttp.put(...)
"""
@methods ["get", "post", "delete", "put", "options"]
Enum.each(@methods, fn method ->
def unquote(:"#{method}")(url, args \\ []) do
request(String.to_atom(unquote(method)), url, args)
end
end)
def request(method, url, args \\ []) do
create_request(method, url, args)
|> execute
end
@doc """
Before you use this module you SHOULD call SimpleHttp.start
"""
def start do
case :inets.start() do
:ok -> :ok
{:error, {:already_started, _}} -> :ok
_ -> :error
end
end
defp execute(%Request{} = request) do
httpc_response = apply(:httpc, :request, params_for_httpc(request))
case httpc_response do
{:ok, {status, headers, body}} ->
response = struct(Response, %{
status: status,
headers: headers,
body: cast_body(body)
})
{:ok, response}
{:error, error} ->
{:error, error}
end
end
defp cast_body(body) when is_binary(body), do: body
defp cast_body(body) when is_list(body), do: to_string(body)
defp cast_body(_body), do: raise BadArgument
defp params_for_httpc(%Request{} = request) do
base_params = {request.url, request.headers}
params = case request.body do
nil -> base_params
_ -> base_params
|> Tuple.append(request.content_type)
|> Tuple.append(request.body)
end
[
request.method, params,
request.http_options,
request.options
]
end
defp create_request(method, url, args) do
struct(Request)
|> add_method_to_request(method)
|> add_url_to_request(url, args)
|> add_headers_to_request(args)
|> add_http_options_to_request(args)
|> add_body_or_params_to_request(args)
|> debug?(args)
end
defp add_method_to_request(%Request{} = request, method), do: %{request | method: method}
defp add_url_to_request(%Request{} = request, url, args) do
url =
if String.valid?(url) do
query_params = Keyword.get(args, :query_params)
if is_map(query_params) || Keyword.keyword?(query_params) do
(url <> "?" <> URI.encode_query(query_params)) |> to_charlist
else
url |> to_charlist
end
else
raise BadArgument
end
%{request | url: url}
end
defp add_headers_to_request(%Request{} = request, args) do
content_type_key = "Content-Type"
content_type = args[:headers][content_type_key]
headers = pop_in(args[:headers][content_type_key])
|> elem(1)
|> Keyword.get(:headers, %{})
|> Map.to_list
|> Enum.map(fn {x, y} ->
{to_charlist(x), to_charlist(y)}
end)
request = case String.valid?(content_type) do
true ->
%{request | content_type: to_charlist(content_type)}
false ->
request
end
case Enum.empty?(headers) do
true ->
request
false ->
%{request | headers: headers}
end
end
defp add_http_options_to_request(%Request{} = request, args) do
keys = [:timeout, :connect_timeout, :autoredirect]
http_options =
Enum.filter_map(
keys,
fn key ->
args[key]
end,
fn key ->
Tuple.append(Tuple.append({}, key), args[key])
end
)
%{request | http_options: http_options}
end
defp add_body_or_params_to_request(%Request{} = request, args) do
with body <- Keyword.get(args, :body, nil),
params <- Keyword.get(args, :params, nil)
do
if body do
%{request | body: body}
else
query =
if params do
URI.encode_query(params) |> to_charlist
else
nil
end
%{request | body: query}
end
end
end
defp debug?(%Request{} = request, args) do
case Keyword.get(args, :debug) do
nil -> request
_ -> IO.inspect(request)
end
end
end