lib/ex_teal/resource/serializer.ex

defmodule ExTeal.Resource.Serializer do
  @moduledoc """
  Serializes a resource response into json
  """

  import Plug.Conn

  alias ExTeal.Api.ErrorSerializer

  def send(conn, :index, body) do
    {:ok, encoded} = Jason.encode(body)
    as_json(conn, encoded)
  end

  def error(conn, :not_found) do
    {:ok, body} =
      Jason.encode(%{
        errors: %{
          "detail" => "The resource was not found",
          "status" => 404,
          "title" => "Not Found"
        }
      })

    as_json(conn, body, 404)
  end

  def render_index(
        %{
          results: models,
          all: all,
          total: total
        },
        resource,
        conn
      ) do
    meta = resource.meta_for(:index, models, all, total, resource, conn)

    policy = resource.policy()
    filtered_models = Enum.filter(models, &policy.view?(conn, &1))
    data = resource.serialize_response(:index, resource, filtered_models, conn)

    {:ok, response} =
      Jason.encode(%{
        meta: meta,
        data: data
      })

    as_json(conn, response)
  end

  def render_related(models, resource, conn) do
    data = Enum.map(models, &schema_summary(&1, resource))
    {:ok, response} = Jason.encode(%{data: data})
    as_json(conn, response)
  end

  @doc """
  Generates a map that summarizes the specified schema
  for global and relational searches.
  """
  def schema_summary(schema, resource) do
    %{
      id: schema.id,
      title: resource.title_for_schema(schema),
      subtitle: resource.subtitle_for_schema(schema),
      thumbnail: resource.thumbnail_for_schema(schema)
    }
  end

  def render_show(model, resource, conn) do
    case resource.policy().view?(conn, model) do
      false ->
        ErrorSerializer.handle_error(conn, :not_authorized)

      true ->
        data = resource.serialize_response(:show, resource, model, conn)
        {:ok, response} = Jason.encode(data)
        as_json(conn, response)
    end
  end

  def render_create(model, resource, conn) do
    data = resource.serialize_response(:show, resource, model, conn)
    {:ok, response} = Jason.encode(data)
    as_json(conn, response, 201)
  end

  def render_update(model, resource, conn) do
    data = resource.serialize_response(:show, resource, model, conn)
    {:ok, response} = Jason.encode(data)
    as_json(conn, response, 200)
  end

  def render_errors(conn, errors) do
    {:ok, body} = errors |> ErrorSerializer.render() |> Jason.encode()
    as_json(conn, body, 422)
  end

  def as_json(conn, body, status \\ 200) do
    conn
    |> put_resp_content_type("text/json")
    |> resp(status, body)
    |> send_resp()
  end
end