defmodule Ash.DataLayer.Simple do @moduledoc """ A data layer that returns structs. This is the data layer that is used under the hood by embedded resources, and resources without data layers. """ use Spark.Dsl.Extension, transformers: [], sections: [] @doc false def can?(_, :create), do: true def can?(_, :update), do: true def can?(_, :destroy), do: true def can?(_, :sort), do: true def can?(_, :limit), do: true def can?(_, {:sort, _}), do: true def can?(_, :filter), do: true def can?(_, :boolean_filter), do: true def can?(_, :nested_expressions), do: true def can?(_, {:filter_expr, _}), do: true def can?(_, :multitenancy), do: true def can?(_, _), do: false defmodule Query do @moduledoc false defstruct [:data, :resource, :filter, :api, :limit, sort: [], data_set?: false] end @doc """ Sets the data for a query against a data-layer-less resource """ def set_data(query, data) do query = Ash.Query.to_query(query) Ash.Query.set_context(query, %{data_layer: %{data: %{query.resource => data}}}) end @doc false def resource_to_query(resource, api) do %Query{data: [], resource: resource, api: api} end @doc false def run_query(%{data_set?: false}, resource) do {:error, Ash.Error.SimpleDataLayer.NoDataProvided.exception( message: """ No data provided to resource #{resource}. Perhaps you are missing a call to `Ash.DataLayer.Simple.set_data/2`?. Another common cause of this is failing to add a data layer for a resource. You can add a data layer like so: `use Ash.Resource, data_layer: Ash.DataLayer.Ets` """ )} end def run_query(%{data: data, sort: sort, api: api, filter: filter, limit: limit}, _resource) do data |> do_filter_matches(filter, api) |> case do {:ok, results} -> {:ok, results |> Ash.Actions.Sort.runtime_sort(sort) |> then(fn data -> if limit do Enum.take(data, limit) else data end end)} {:error, error} -> {:error, error} end end defp do_filter_matches(data, filter, api) do Ash.Filter.Runtime.filter_matches(api, data, filter) end @doc false def limit(query, limit, _) do {:ok, %{query | limit: limit}} end @doc false def set_tenant(_, query, _), do: {:ok, query} @doc false def filter(query, filter, _resource) do {:ok, %{query | filter: filter}} end @doc false def sort(query, sort, _resource) do {:ok, %{query | sort: sort}} end @doc false def set_context(_resource, query, context) do with {:ok, data_layer_context} <- Map.fetch(context, :data_layer), {:ok, data} <- Map.fetch(data_layer_context, :data), {:ok, resource_data} <- Map.fetch(data, query.resource) do {:ok, %{query | data_set?: true, data: resource_data || []}} else _ -> {:ok, query} end end @doc false def create(_resource, changeset) do Ash.Changeset.apply_attributes(changeset) end @doc false def update(_resource, changeset) do Ash.Changeset.apply_attributes(changeset) end @doc false def destroy(_resource, _changeset) do :ok end end