lib/permit/resolver_base.ex

defmodule Permit.ResolverBase do
  alias Permit.Types

  @callback resolve(
              Types.subject(),
              module(),
              Types.resource_module(),
              Types.controller_action(),
              map(),
              :all | :one
            ) :: {:authorized, term() | list()} | :unauthorized | :not_found

  defmacro __using__(_opts) do
    quote do
      @behaviour unquote(__MODULE__)

      @spec authorized?(
              Types.subject(),
              module(),
              Types.resource(),
              Types.controller_action()
            ) :: boolean()

      def authorized?(subject, authorization_module, resource_or_module, action)
          when not is_nil(subject) do
        auth = authorization_module.can(subject)
        actions_module = authorization_module.actions_module()
        verify_fn = &Permit.verify_record(auth, resource_or_module, &1)

        Permit.Actions.verify_transitively!(actions_module, action, verify_fn)
      end

      @spec authorize_and_preload_one!(
              Types.subject(),
              module(),
              Types.resource_module(),
              Types.controller_action(),
              map()
            ) :: {:authorized, [struct()]} | :unauthorized

      def authorize_and_preload_one!(subject, authorization_module, resource_module, action, meta)
          when not is_nil(subject) do
        resolve(subject, authorization_module, resource_module, action, meta, :one)
      end

      @spec authorize_and_preload_all!(
              Types.subject(),
              module(),
              Types.resource_module(),
              Types.controller_action(),
              map()
              # TODO: originally term() was Ecto.Queryable.t(), figure out how to make it play with Ecto separated
            ) :: {:authorized, [struct()]} | :unauthorized | {:not_found, term()}

      def authorize_and_preload_all!(subject, authorization_module, resource_module, action, meta)
          when not is_nil(subject) do
        resolve(subject, authorization_module, resource_module, action, meta, :all)
      end
    end
  end
end