View Source Ash.DataLayer behaviour (ash v2.9.24)

The interface for being an ash data layer.

This is a large behaviour, and this capability is not complete, but the idea is to have a large amount of optional callbacks, and use the can?/2 callback to ensure that the engine only ever tries to interact with the data layer in ways that it supports.

Link to this section Summary

Link to this section Types

@type bulk_options() :: %{
  batch_size: pos_integer(),
  return_records?: boolean(),
  upsert?: boolean(),
  upsert_keys: nil | [atom()],
  upsert_fields: nil | [atom()],
  tenant: String.t() | nil
}
@type data_layer_query() :: struct()
@type feature() ::
  :transact
  | :multitenancy
  | {:lateral_join, [Ash.Resource.t()]}
  | {:join, Ash.Resource.t()}
  | {:aggregate, Ash.Query.Aggregate.kind()}
  | {:aggregate_relationship, Ash.Resource.Relationships.relationship()}
  | {:query_aggregate, Ash.Query.Aggregate.kind()}
  | :select
  | :aggregate_filter
  | :aggregate_sort
  | :boolean_filter
  | :async_engine
  | :create
  | :read
  | :update
  | :destroy
  | :limit
  | :offset
  | :transact
  | :filter
  | {:lock, lock_type()}
  | {:filter_expr, struct()}
  | {:filter_relationship, Ash.Resource.Relationships.relationship()}
  | :sort
  | {:sort, Ash.Type.t()}
  | :upsert
  | :composite_primary_key
@type lateral_join_link() ::
  {Ash.Resource.t(), atom(), atom(), Ash.Resource.Relationships.relationship()}
@type lock_type() :: :for_update | term()
@type t() :: module()
@type transaction_reason() ::
  %{type: :create, metadata: %{resource: Ash.Resource.t(), action: atom()}}
  | %{
      type: :update,
      metadata: %{
        resource: Ash.Resource.t(),
        action: atom(),
        record: Ash.Resource.record(),
        actor: term()
      }
    }
  | %{
      type: :destroy,
      metadata: %{
        resource: Ash.Resource.t(),
        action: atom(),
        record: Ash.Resource.record(),
        actor: term()
      }
    }
  | %{
      type: :read,
      metadata: %{
        resource: Ash.Resource.t(),
        query: Ash.Query.t(),
        actor: term()
      }
    }
  | %{
      type: :flow_transaction,
      metadata: %{step_name: atom() | [term()], flow: module(), actor: term()}
    }
  | %{type: :custom, metadata: map()}
  | %{type: atom(), metadata: map()}

Link to this section Callbacks

Link to this callback

add_aggregate( data_layer_query, t, t )

View Source (optional)
@callback add_aggregate(
  data_layer_query(),
  Ash.Query.Aggregate.t(),
  Ash.Resource.t()
) :: {:ok, data_layer_query()} | {:error, term()}
Link to this callback

add_aggregates( data_layer_query, list, t )

View Source (optional)
@callback add_aggregates(
  data_layer_query(),
  [Ash.Query.Aggregate.t()],
  Ash.Resource.t()
) :: {:ok, data_layer_query()} | {:error, term()}
Link to this callback

add_calculation( data_layer_query, t, expression, t )

View Source (optional)
@callback add_calculation(
  data_layer_query(),
  Ash.Query.Calculation.t(),
  expression :: any(),
  Ash.Resource.t()
) :: {:ok, data_layer_query()} | {:error, term()}
Link to this callback

add_calculations( data_layer_query, list, t )

View Source (optional)
@callback add_calculations(
  data_layer_query(),
  [{Ash.Query.Calculation.t(), expression :: any()}],
  Ash.Resource.t()
) :: {:ok, data_layer_query()} | {:error, term()}
Link to this callback

bulk_create( t, t, options )

View Source (optional)
@callback bulk_create(
  Ash.Resource.t(),
  Enumerable.t(Ash.Changeset.t()),
  options :: bulk_options()
) ::
  {:ok,
   Enumerable.t(:ok | {:ok, Ash.Resource.record()} | {:error, Ash.Error.t()})}
  | {:error, Ash.Error.t()}
@callback can?(Ash.Resource.t() | Spark.Dsl.t(), feature()) :: boolean()
@callback create(Ash.Resource.t(), Ash.Changeset.t()) ::
  {:ok, Ash.Resource.record()} | {:error, term()}
Link to this callback

destroy(t, t)

View Source (optional)
@callback destroy(Ash.Resource.t(), Ash.Changeset.t()) :: :ok | {:error, term()}
Link to this callback

distinct(data_layer_query, list, resource)

View Source (optional)
@callback distinct(data_layer_query(), [atom()], resource :: Ash.Resource.t()) ::
  {:ok, data_layer_query()} | {:error, term()}
Link to this callback

filter(data_layer_query, t, resource)

View Source (optional)
@callback filter(data_layer_query(), Ash.Filter.t(), resource :: Ash.Resource.t()) ::
  {:ok, data_layer_query()} | {:error, term()}
@callback functions(Ash.Resource.t()) :: [module()]
Link to this callback

in_transaction?(t)

View Source (optional)
@callback in_transaction?(Ash.Resource.t()) :: boolean()
Link to this callback

limit( data_layer_query, limit, resource )

View Source (optional)
@callback limit(
  data_layer_query(),
  limit :: non_neg_integer(),
  resource :: Ash.Resource.t()
) :: {:ok, data_layer_query()} | {:error, term()}
Link to this callback

lock(data_layer_query, lock_type, resource)

View Source (optional)
@callback lock(data_layer_query(), lock_type(), resource :: Ash.Resource.t()) ::
  {:ok, data_layer_query()} | {:error, term()}
Link to this callback

offset( data_layer_query, offset, resource )

View Source (optional)
@callback offset(
  data_layer_query(),
  offset :: non_neg_integer(),
  resource :: Ash.Resource.t()
) :: {:ok, data_layer_query()} | {:error, term()}
@callback resource_to_query(Ash.Resource.t(), Ash.Api.t()) :: data_layer_query()
Link to this callback

rollback(t, term)

View Source (optional)
@callback rollback(Ash.Resource.t(), term()) :: no_return()
Link to this callback

run_aggregate_query( data_layer_query, list, t )

View Source (optional)
@callback run_aggregate_query(
  data_layer_query(),
  [Ash.Query.Aggregate.t()],
  Ash.Resource.t()
) :: {:ok, map()} | {:error, term()}
Link to this callback

run_aggregate_query_with_lateral_join( data_layer_query, list, list, destination_resource, list )

View Source (optional)
@callback run_aggregate_query_with_lateral_join(
  data_layer_query(),
  [Ash.Query.Aggregate.t()],
  [Ash.Resource.record()],
  destination_resource :: Ash.Resource.t(),
  [lateral_join_link()]
) :: {:ok, [Ash.Resource.t()]} | {:error, term()}
Link to this callback

run_query(data_layer_query, t)

View Source (optional)
@callback run_query(data_layer_query(), Ash.Resource.t()) ::
  {:ok, [Ash.Resource.record()]} | {:error, term()}
Link to this callback

run_query_with_lateral_join( data_layer_query, list, source_resource, list )

View Source (optional)
@callback run_query_with_lateral_join(
  data_layer_query(),
  [Ash.Resource.record()],
  source_resource :: Ash.Resource.t(),
  [lateral_join_link()]
) :: {:ok, [Ash.Resource.record()]} | {:error, term()}
Link to this callback

select( data_layer_query, select, resource )

View Source (optional)
@callback select(
  data_layer_query(),
  select :: [atom()],
  resource :: Ash.Resource.t()
) :: {:ok, data_layer_query()} | {:error, term()}
Link to this callback

set_context(t, data_layer_query, map)

View Source (optional)
@callback set_context(Ash.Resource.t(), data_layer_query(), map()) ::
  {:ok, data_layer_query()} | {:error, term()}
Link to this callback

set_tenant(t, data_layer_query, term)

View Source (optional)
@callback set_tenant(Ash.Resource.t(), data_layer_query(), term()) ::
  {:ok, data_layer_query()} | {:error, term()}
Link to this callback

sort(data_layer_query, t, resource)

View Source (optional)
@callback sort(data_layer_query(), Ash.Sort.t(), resource :: Ash.Resource.t()) ::
  {:ok, data_layer_query()} | {:error, term()}
@callback source(Ash.Resource.t()) :: String.t()
Link to this callback

transaction( t, function, arg3, reason )

View Source (optional)
@callback transaction(
  Ash.Resource.t(),
  (() -> term()),
  nil | pos_integer(),
  reason :: transaction_reason()
) :: {:ok, term()} | {:error, term()}
Link to this callback

transform_query(t)

View Source (optional)
@callback transform_query(Ash.Query.t()) :: Ash.Query.t()
@callback update(Ash.Resource.t(), Ash.Changeset.t()) ::
  {:ok, Ash.Resource.record()} | {:error, term()}
Link to this callback

upsert(t, t, list)

View Source (optional)
@callback upsert(Ash.Resource.t(), Ash.Changeset.t(), [atom()]) ::
  {:ok, Ash.Resource.record()} | {:error, term()}

Link to this section Functions

Link to this function

add_aggregates(query, aggregates, resource)

View Source
@spec add_aggregates(data_layer_query(), [Ash.Query.Aggregate.t()], Ash.Resource.t()) ::
  {:ok, data_layer_query()} | {:error, term()}
Link to this function

add_calculations(query, calculations, resource)

View Source
@spec add_calculations(
  data_layer_query(),
  [{Ash.Query.Calculation.t(), expression :: term()}],
  Ash.Resource.t()
) :: {:ok, data_layer_query()} | {:error, term()}
Link to this function

bulk_create(resource, changesets, options)

View Source
@spec bulk_create(
  Ash.Resource.t(),
  Enumerable.t(Ash.Changeset.t()),
  options :: bulk_options()
) :: :ok | {:ok, Enumerable.t(Ash.Resource.record())} | {:error, Ash.Error.t()}
@spec can?(feature(), Ash.Resource.t() | Spark.Dsl.t()) :: boolean()
Link to this function

create(resource, changeset)

View Source
@spec create(Ash.Resource.t(), Ash.Changeset.t()) ::
  {:ok, Ash.Resource.record()} | {:error, term()}
@spec data_layer(Ash.Resource.t() | Spark.Dsl.t()) :: t()

The data layer of the resource, or nil if it does not have one

Link to this function

data_layer_can?(resource, feature)

View Source
@spec data_layer_can?(Ash.Resource.t() | Spark.Dsl.t(), feature()) :: boolean()

Whether or not the data layer supports a specific feature

Link to this function

data_layer_functions(resource)

View Source
@spec data_layer_functions(Ash.Resource.t()) :: map()

Custom functions supported by the data layer of the resource

Link to this function

destroy(resource, changeset)

View Source
@spec destroy(Ash.Resource.t(), Ash.Changeset.t()) :: :ok | {:error, term()}
Link to this function

distinct(query, distinct, resource)

View Source
@spec distinct(data_layer_query(), [atom()] | nil, Ash.Resource.t()) ::
  {:ok, data_layer_query()} | {:error, term()}
Link to this function

filter(query, filter, resource)

View Source
@spec filter(data_layer_query(), Ash.Filter.t(), Ash.Resource.t()) ::
  {:ok, data_layer_query()} | {:error, term()}
Link to this function

in_transaction?(resource)

View Source
Link to this function

limit(query, limit, resource)

View Source
@spec limit(data_layer_query(), limit :: non_neg_integer(), Ash.Resource.t()) ::
  {:ok, data_layer_query()} | {:error, term()}
Link to this function

lock(query, lock_type, resource)

View Source
@spec lock(
  data_layer_query(),
  lock_type :: lock_type() | nil,
  resource :: Ash.Resource.t()
) ::
  {:ok, data_layer_query()} | {:error, term()}
Link to this function

offset(query, offset, resource)

View Source
@spec offset(data_layer_query(), offset :: non_neg_integer(), Ash.Resource.t()) ::
  {:ok, data_layer_query()} | {:error, term()}
Link to this function

resource_to_query(resource, api)

View Source
@spec resource_to_query(Ash.Resource.t(), Ash.Api.t()) :: data_layer_query()
Link to this function

rollback(resource, term)

View Source
@spec rollback(Ash.Resource.t(), term()) :: no_return()

Rolls back the current transaction

Link to this function

run_aggregate_query(query, aggregates, resource)

View Source
@spec run_aggregate_query(
  data_layer_query(),
  [Ash.Query.Aggregate.t()],
  Ash.Resource.t()
) :: {:ok, map()} | {:error, term()}
Link to this function

run_aggregate_query_with_lateral_join(query, aggregates, root_data, destination_resource, path)

View Source
Link to this function

run_query(query, central_resource)

View Source
@spec run_query(data_layer_query(), central_resource :: Ash.Resource.t()) ::
  {:ok, [Ash.Resource.record()]} | {:error, term()}
Link to this function

run_query_with_lateral_join(query, root_data, destination_resource, path)

View Source
Link to this function

select(query, select, resource)

View Source
@spec select(data_layer_query(), select :: [atom()], Ash.Resource.t()) ::
  {:ok, data_layer_query()} | {:error, term()}
Link to this function

set_context(resource, query, map)

View Source
@spec set_context(Ash.Resource.t(), data_layer_query(), map()) ::
  {:ok, data_layer_query()} | {:error, term()}
Link to this function

set_tenant(resource, query, term)

View Source
@spec set_tenant(Ash.Resource.t(), data_layer_query(), term()) ::
  {:ok, data_layer_query()} | {:error, term()}
Link to this function

sort(query, sort, resource)

View Source
@spec sort(data_layer_query(), Ash.Sort.t(), Ash.Resource.t()) ::
  {:ok, data_layer_query()} | {:error, term()}
@spec source(Ash.Resource.t()) :: String.t()
Link to this function

transaction(resource_or_resources, func, timeout \\ nil, reason \\ %{type: :custom, metadata: %{}})

View Source
@spec transaction(
  Ash.Resource.t() | [Ash.Resource.t()],
  (() -> term()),
  nil | pos_integer(),
  reason :: transaction_reason()
) :: term()

Wraps the execution of the function in a transaction with the resource's data_layer

Link to this function

update(resource, changeset)

View Source
@spec update(Ash.Resource.t(), Ash.Changeset.t()) ::
  {:ok, Ash.Resource.record()} | {:error, term()}
Link to this function

upsert(resource, changeset, keys)

View Source
@spec upsert(Ash.Resource.t(), Ash.Changeset.t(), [atom()]) ::
  {:ok, Ash.Resource.record()} | {:error, term()}