lib/jsonapi_plug/document/resource_identifier_object.ex

defmodule JSONAPIPlug.Document.ResourceIdentifierObject do
  @moduledoc """
  JSON:API Resource Identifier object

  https://jsonapi.org/format/#document-resource-object-linkage
  """

  alias JSONAPIPlug.{Document, Document.ResourceObject, Exceptions.InvalidDocument}

  @type t :: %__MODULE__{
          id: ResourceObject.id(),
          type: ResourceObject.type(),
          meta: Document.meta() | nil
        }
  defstruct [:id, :type, :meta]

  @spec deserialize(Document.payload()) :: t() | no_return()
  def deserialize(data) do
    %__MODULE__{}
    |> deserialize_id(data)
    |> deserialize_type(data)
    |> deserialize_meta(data)
  end

  defp deserialize_type(resource_identifier_object, %{"type" => type})
       when is_binary(type) and byte_size(type) > 0,
       do: %__MODULE__{resource_identifier_object | type: type}

  defp deserialize_type(_resource_identifier_object, type) do
    raise InvalidDocument,
      message: "Resource Identifier object type (#{type}) is invalid",
      reference: "https://jsonapi.org/format/#document-resource-objects"
  end

  defp deserialize_id(resource_identifier_object, %{"id" => id})
       when is_binary(id) and byte_size(id) > 0,
       do: %__MODULE__{resource_identifier_object | id: id}

  defp deserialize_id(resource_identifier_object, _data),
    do: resource_identifier_object

  defp deserialize_meta(resource_identifier_object, %{"meta" => meta})
       when is_map(meta),
       do: %__MODULE__{resource_identifier_object | meta: meta}

  defp deserialize_meta(_resource_identifier_object, %{"meta" => _meta}) do
    raise InvalidDocument,
      message: "Resource Identifier object 'meta' must be an object",
      reference: "https://jsonapi.org/format/#document-resource-identifier-objects"
  end

  defp deserialize_meta(resource_identifier_object, _payload),
    do: resource_identifier_object
end