Current section
Files
Jump to
Current section
Files
lib/json_api/controller.ex
defmodule ElixirTools.JsonApi.Controller do
@moduledoc """
Works together with the `JsonApi`. The JSON API expects a validated request and converts the
controller into a Json Api Controller by passing only the validated parameters using
`JaSerializer`.
Allows two `action_name` calls:
1. `action_name/2`. Calls the action name with the params: `conn, json_api_params`
2. `action_name/3`. To keep access to original parameter object it will call
`conn, phoenix_params, json_api_params`
Example:
```
defmodule Controller do
use MyPhoenixWeb, :controller
use ElixirTools.JsonApi.Controller
def my_method(conn, json_params), do: conn
def my_method(conn, phoenix_params, json_params), do: conn
end
```
"""
import Ecto.Changeset, only: [apply_changes: 1, traverse_errors: 2]
import Phoenix.Controller, only: [put_view: 2, render: 3]
import Plug.Conn
alias __MODULE__
alias Plug.Conn
@error_view Application.get_env(:pagantis_elixir_tools, Controller)[:error_view]
@type params :: map
@type idempotency_key :: Ecto.UUID.t() | nil
@typep error :: any
defmacro __using__(_) do
quote do
@spec action(Plug.Conn.t(), any) :: Plug.Conn.t()
def action(conn, _) do
json_api_params = Map.get(conn.assigns, :json_api_params, %{})
args =
if function_exported?(__MODULE__, action_name(conn), 2) do
[conn, json_api_params]
else
[conn, conn.params, json_api_params]
end
apply(__MODULE__, action_name(conn), args)
end
defoverridable action: 2
end
end
@spec bad_input(Conn.t(), error) :: Conn.t()
def bad_input(conn, error \\ "Bad Request"), do: do_error(conn, :bad_request, error)
@spec not_found(Conn.t(), error) :: Conn.t()
def not_found(conn, error \\ "Not Found"), do: do_error(conn, :not_found, error)
@spec http_timeout(Conn.t(), error) :: Conn.t()
def http_timeout(conn, error \\ "Request Timeout"), do: do_error(conn, :request_timeout, error)
@spec unprocessable_entity(Conn.t(), error) :: Conn.t()
def unprocessable_entity(conn, error \\ "Unprocessable Entity"),
do: do_error(conn, :unprocessable_entity, error)
@spec server_error(Conn.t(), error) :: Conn.t()
def server_error(conn, error), do: do_error(conn, :internal_server_error, error)
@spec validate_params(map, module) :: {:ok, struct} | {:error, {:bad_request, map}}
def validate_params(params, schema) do
changeset = schema.changeset(params)
case changeset.valid? do
true -> {:ok, apply_changes(changeset)}
false -> {:error, {:bad_request, traverse_errors(changeset, & &1)}}
end
end
@spec do_error(Conn.t(), Conn.status(), error) :: Conn.t()
defp do_error(conn, http_code, error) do
conn
|> put_status(http_code)
|> put_view(@error_view)
|> render("#{http_code}.json-api", error: error)
|> halt
end
end