View Source Ash.DataLayer behaviour (ash v2.9.20)
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
Functions
The data layer of the resource, or nil if it does not have one
Whether or not the data layer supports a specific feature
Custom functions supported by the data layer of the resource
Rolls back the current transaction
Wraps the execution of the function in a transaction with the resource's data_layer
Link to this section Types
@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
@callback add_aggregate( data_layer_query(), Ash.Query.Aggregate.t(), Ash.Resource.t() ) :: {:ok, data_layer_query()} | {:error, term()}
@callback add_aggregates( data_layer_query(), [Ash.Query.Aggregate.t()], Ash.Resource.t() ) :: {:ok, data_layer_query()} | {:error, term()}
@callback add_calculation( data_layer_query(), Ash.Query.Calculation.t(), expression :: any(), Ash.Resource.t() ) :: {:ok, data_layer_query()} | {:error, term()}
@callback add_calculations( data_layer_query(), [{Ash.Query.Calculation.t(), expression :: any()}], Ash.Resource.t() ) :: {:ok, data_layer_query()} | {:error, term()}
@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()}
@callback destroy(Ash.Resource.t(), Ash.Changeset.t()) :: :ok | {:error, term()}
@callback distinct(data_layer_query(), [atom()], resource :: Ash.Resource.t()) :: {:ok, data_layer_query()} | {:error, term()}
@callback filter(data_layer_query(), Ash.Filter.t(), resource :: Ash.Resource.t()) :: {:ok, data_layer_query()} | {:error, term()}
@callback functions(Ash.Resource.t()) :: [module()]
@callback in_transaction?(Ash.Resource.t()) :: boolean()
@callback limit( data_layer_query(), limit :: non_neg_integer(), resource :: Ash.Resource.t() ) :: {:ok, data_layer_query()} | {:error, term()}
@callback lock(data_layer_query(), lock_type(), resource :: Ash.Resource.t()) :: {:ok, data_layer_query()} | {:error, term()}
@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()
@callback rollback(Ash.Resource.t(), term()) :: no_return()
@callback run_aggregate_query( data_layer_query(), [Ash.Query.Aggregate.t()], Ash.Resource.t() ) :: {:ok, map()} | {:error, term()}
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()}
@callback run_query(data_layer_query(), Ash.Resource.t()) :: {:ok, [Ash.Resource.record()]} | {:error, term()}
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()}
@callback select( data_layer_query(), select :: [atom()], resource :: Ash.Resource.t() ) :: {:ok, data_layer_query()} | {:error, term()}
@callback set_context(Ash.Resource.t(), data_layer_query(), map()) :: {:ok, data_layer_query()} | {:error, term()}
@callback set_tenant(Ash.Resource.t(), data_layer_query(), term()) :: {:ok, data_layer_query()} | {:error, term()}
@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()
@callback transaction( Ash.Resource.t(), (() -> term()), nil | pos_integer(), reason :: transaction_reason() ) :: {:ok, term()} | {:error, term()}
@callback transform_query(Ash.Query.t()) :: Ash.Query.t()
@callback update(Ash.Resource.t(), Ash.Changeset.t()) :: {:ok, Ash.Resource.record()} | {:error, term()}
@callback upsert(Ash.Resource.t(), Ash.Changeset.t(), [atom()]) :: {:ok, Ash.Resource.record()} | {:error, term()}
Link to this section Functions
@spec add_aggregates(data_layer_query(), [Ash.Query.Aggregate.t()], Ash.Resource.t()) :: {:ok, data_layer_query()} | {:error, term()}
@spec add_calculations( data_layer_query(), [{Ash.Query.Calculation.t(), expression :: term()}], Ash.Resource.t() ) :: {:ok, data_layer_query()} | {:error, term()}
@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()
@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
@spec data_layer_can?(Ash.Resource.t() | Spark.Dsl.t(), feature()) :: boolean()
Whether or not the data layer supports a specific feature
@spec data_layer_functions(Ash.Resource.t()) :: map()
Custom functions supported by the data layer of the resource
@spec destroy(Ash.Resource.t(), Ash.Changeset.t()) :: :ok | {:error, term()}
@spec distinct(data_layer_query(), [atom()] | nil, Ash.Resource.t()) :: {:ok, data_layer_query()} | {:error, term()}
@spec filter(data_layer_query(), Ash.Filter.t(), Ash.Resource.t()) :: {:ok, data_layer_query()} | {:error, term()}
@spec limit(data_layer_query(), limit :: non_neg_integer(), Ash.Resource.t()) :: {:ok, data_layer_query()} | {:error, term()}
@spec lock( data_layer_query(), lock_type :: lock_type() | nil, resource :: Ash.Resource.t() ) :: {:ok, data_layer_query()} | {:error, term()}
@spec offset(data_layer_query(), offset :: non_neg_integer(), Ash.Resource.t()) :: {:ok, data_layer_query()} | {:error, term()}
@spec resource_to_query(Ash.Resource.t(), Ash.Api.t()) :: data_layer_query()
@spec rollback(Ash.Resource.t(), term()) :: no_return()
Rolls back the current transaction
@spec run_aggregate_query( data_layer_query(), [Ash.Query.Aggregate.t()], Ash.Resource.t() ) :: {:ok, map()} | {:error, term()}
run_aggregate_query_with_lateral_join(query, aggregates, root_data, destination_resource, path)
View Source@spec run_query(data_layer_query(), central_resource :: Ash.Resource.t()) :: {:ok, [Ash.Resource.record()]} | {:error, term()}
run_query_with_lateral_join(query, root_data, destination_resource, path)
View Source@spec select(data_layer_query(), select :: [atom()], Ash.Resource.t()) :: {:ok, data_layer_query()} | {:error, term()}
@spec set_context(Ash.Resource.t(), data_layer_query(), map()) :: {:ok, data_layer_query()} | {:error, term()}
@spec set_tenant(Ash.Resource.t(), data_layer_query(), term()) :: {:ok, data_layer_query()} | {:error, term()}
@spec sort(data_layer_query(), Ash.Sort.t(), Ash.Resource.t()) :: {:ok, data_layer_query()} | {:error, term()}
@spec source(Ash.Resource.t()) :: String.t()
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
@spec update(Ash.Resource.t(), Ash.Changeset.t()) :: {:ok, Ash.Resource.record()} | {:error, term()}
@spec upsert(Ash.Resource.t(), Ash.Changeset.t(), [atom()]) :: {:ok, Ash.Resource.record()} | {:error, term()}