Current section

Files

Jump to
json_api_client lib json_api_client.ex
Raw

lib/json_api_client.ex

defmodule JsonApiClient do
@moduledoc """
A client library for interacting with REST APIs that comply with
the JSON API spec described at http://jsonapi.org
"""
@timeout Application.get_env(:json_api_client, :timeout, 500)
@version Mix.Project.config[:version]
@package_name JsonApiClient.Mixfile.project[:app]
alias __MODULE__.Request
alias __MODULE__.Middleware.Runner
alias Mix.Project
@doc "Execute a JSON API Request using HTTP GET"
def fetch(req), do: req |> Request.method(:get) |> execute
@doc "Error raising version of `fetch/1`"
def fetch!(req), do: req |> Request.method(:get) |> execute!
@doc "Execute a JSON API Request using HTTP POST"
def create(req), do: req |> Request.method(:post) |> execute
@doc "Error raising version of `create/1`"
def create!(req), do: req |> Request.method(:post) |> execute!
@doc "Execute a JSON API Request using HTTP PATCH"
def update(req), do: req |> Request.method(:patch) |> execute
@doc "Error raising version of `update/1`"
def update!(req), do: req |> Request.method(:patch) |> execute!
@doc "Execute a JSON API Request using HTTP DELETE"
def delete(req), do: req |> Request.method(:delete) |> execute
@doc "Error raising version of `delete/1`"
def delete!(req), do: req |> Request.method(:delete) |> execute!
@doc """
Execute a JSON API Request
Takes a JsonApiClient.Request and preforms the described request.
Returns either a tuple with `:ok` and a `JsonApiClient.Response` struct (or
nil) or `:error` and a `JsonApiClient.RequestError` struct depending on the
http response code and whether the server response was valid according to the
JSON API spec.
| Scenario | Server Response Valid | Return Value |
|--------------|-----------------------|--------------------------------------------------------------------------------------|
| 2** | yes | `{:ok, %Response{status: 2**, doc: %Document{}}` |
| 4** | yes | `{:ok, %Response{status: 4**, doc: %Document{} or nil}` |
| 5** | yes | `{:ok, %Response{status: 5**, doc: %Document{} or nil}` |
| 2** | no | `{:error, %RequestError{status: 2**, message: "Invalid response body"}}` |
| 4** | no | `{:ok, %Response{status: 4**, doc: nil}}` |
| 5** | no | `{:ok, %Response{status: 3**, doc: nil}}` |
| socket error | n/a | `{:error, %RequestError{status: nil, message: "Error completing HTTP request econnrefused", original_error: error}}` |
"""
def execute(req) do
url = Request.get_url(req)
query_params = Request.get_query_params(req)
headers = default_headers()
|> Map.merge(req.headers)
|> Enum.into([])
http_options = default_options()
|> Map.merge(req.options)
|> Map.put(:params, query_params)
|> Enum.into([])
body = Request.get_body(req)
Runner.run %{
method: req.method,
url: url, body: body,
headers: headers,
http_options: http_options,
service_name: req.service_name
}
end
@doc "Error raising version of `execute/1`"
def execute!(req) do
case execute(req) do
{:ok, response} -> response
{:error, error} -> raise error
end
end
defp default_options do
%{
timeout: timeout(),
recv_timeout: timeout(),
}
end
defp default_headers do
%{
"Accept" => "application/vnd.api+json",
"Content-Type" => "application/vnd.api+json",
"User-Agent" => user_agent() ,
}
end
defp user_agent do
[@package_name, @version, user_agent_suffix()]
|> Enum.reject(&is_nil/1)
|> Enum.join("/")
end
defp user_agent_suffix do
Application.get_env(:json_api_client, :user_agent_suffix, Project.config[:app])
end
defp timeout do
@timeout
end
end