lib/ecto_juno/helpers/sorting_params.ex

defmodule EctoJuno.Helpers.SortingParams do
  @moduledoc """
    Validator module for sorting parameters
  """

  use Ecto.Schema

  import Ecto.Changeset

  alias EctoJuno.Helpers.SortingHelpers, as: Helpers

  @default_sort_by Application.compile_env(:ecto_juno, :sort_by, :inserted_at)
  @default_sort_direction Application.compile_env(:ecto_juno, :sort_direction, :asc)

  @primary_key false
  embedded_schema do
    field :sort_by, :string, default: @default_sort_by, skip_default_validation: true

    field :sort_direction, :string,
      default: @default_sort_direction,
      skip_default_validation: true
  end

  @doc """
    Validation function. Relies on `Ecto.Changeset`. In the end invokes `apply_action!/2`
  """
  @spec changeset!(map(), atom() | list()) :: map()
  def changeset!(attrs, schema_or_list) do
    %__MODULE__{}
    |> cast(attrs, [:sort_by, :sort_direction])
    |> atomize_sort_by(schema_or_list)
    |> atomize_sort_direction()
    |> validate_required([:sort_by, :sort_direction])
    |> apply_action!(:insert)
  end

  defp atomize_sort_by(%{changes: %{sort_by: sort_by}} = changeset, schema_or_list)
       when is_binary(sort_by) do
    field =
      sort_by
      |> Helpers.map_string_field_to_atom(schema_or_list)
      |> traverse_nil_value(@default_sort_by)

    put_change(changeset, :sort_by, field)
  end

  defp atomize_sort_by(changeset, _schema_or_list),
    do: changeset

  defp atomize_sort_direction(%{changes: %{sort_direction: direction}} = changeset)
       when is_binary(direction) do
    sort_direction =
      direction
      |> Helpers.map_string_field_to_atom([:desc, :asc])
      |> traverse_nil_value(@default_sort_direction)

    put_change(changeset, :sort_direction, sort_direction)
  end

  defp atomize_sort_direction(changeset),
    do: changeset

  defp traverse_nil_value(nil, default), do: default

  defp traverse_nil_value(field, _default), do: field
end