mirror of
https://github.com/ash-project/ash.git
synced 2024-09-19 13:03:02 +12:00
improvement: move functions to more conventional places
improvement: move ash.formatter task to `spark.formatter`
This commit is contained in:
parent
6c79519b6c
commit
04744f395f
37 changed files with 561 additions and 317 deletions
|
@ -11,7 +11,7 @@
|
|||
## ...or adjusted (e.g. use one-line formatter for more compact credo output)
|
||||
# {:credo, "mix credo --format oneline"},
|
||||
|
||||
{:check_formatter, command: "mix ash.formatter --check"},
|
||||
{:check_formatter, command: "mix spark.formatter --check"},
|
||||
# TODO: upgrade to the new version of ex_check that should do this on the right elixir version
|
||||
# {:unused_deps, command: "mix deps.unlock --check-unused"}
|
||||
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
# THIS FILE IS AUTOGENERATED USING `mix ash.formatter`
|
||||
# DONT MODIFY IT BY HAND
|
||||
locals_without_parens = [
|
||||
spark_locals_without_parens = [
|
||||
accept: 1,
|
||||
access_type: 1,
|
||||
action: 1,
|
||||
|
@ -29,6 +27,7 @@ locals_without_parens = [
|
|||
before_action?: 1,
|
||||
belongs_to: 2,
|
||||
belongs_to: 3,
|
||||
broadcast_type: 1,
|
||||
bypass: 1,
|
||||
bypass: 2,
|
||||
calculate: 3,
|
||||
|
@ -189,8 +188,8 @@ locals_without_parens = [
|
|||
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"],
|
||||
locals_without_parens: locals_without_parens,
|
||||
locals_without_parens: spark_locals_without_parens,
|
||||
export: [
|
||||
locals_without_parens: locals_without_parens
|
||||
locals_without_parens: spark_locals_without_parens
|
||||
]
|
||||
]
|
||||
|
|
|
@ -44,7 +44,7 @@ defmodule Ash.Actions.Create do
|
|||
actor: actor,
|
||||
authorize?: authorize?,
|
||||
notification_metadata: opts[:notification_metadata],
|
||||
timeout: opts[:timeout] || changeset.timeout || Ash.Api.timeout(api),
|
||||
timeout: opts[:timeout] || changeset.timeout || Ash.Api.Info.timeout(api),
|
||||
return_notifications?: opts[:return_notifications?],
|
||||
transaction?: Keyword.get(opts, :transaction?, true)
|
||||
)
|
||||
|
@ -177,7 +177,7 @@ defmodule Ash.Actions.Create do
|
|||
|
||||
changeset = %{
|
||||
changeset
|
||||
| timeout: timeout || changeset.timeout || Ash.Api.timeout(api)
|
||||
| timeout: timeout || changeset.timeout || Ash.Api.Info.timeout(api)
|
||||
}
|
||||
|
||||
tenant =
|
||||
|
|
|
@ -57,7 +57,7 @@ defmodule Ash.Actions.Destroy do
|
|||
return_notifications?: opts[:return_notifications?],
|
||||
notification_metadata: opts[:notification_metadata],
|
||||
authorize?: authorize?,
|
||||
timeout: opts[:timeout] || changeset.timeout || Ash.Api.timeout(api),
|
||||
timeout: opts[:timeout] || changeset.timeout || Ash.Api.Info.timeout(api),
|
||||
transaction?: true
|
||||
)
|
||||
|> case do
|
||||
|
@ -231,7 +231,7 @@ defmodule Ash.Actions.Destroy do
|
|||
case Ash.DataLayer.destroy(resource, changeset) do
|
||||
:ok ->
|
||||
{:ok,
|
||||
Ash.Resource.Info.set_meta(record, %Ecto.Schema.Metadata{
|
||||
Ash.Resource.set_meta(record, %Ecto.Schema.Metadata{
|
||||
state: :deleted,
|
||||
schema: resource
|
||||
})}
|
||||
|
|
|
@ -85,7 +85,7 @@ defmodule Ash.Actions.Helpers do
|
|||
end
|
||||
|
||||
if api do
|
||||
if !Keyword.has_key?(opts, :actor) && Ash.Api.require_actor?(api) do
|
||||
if !Keyword.has_key?(opts, :actor) && Ash.Api.Info.require_actor?(api) do
|
||||
raise Ash.Error.Forbidden.ApiRequiresActor, api: api
|
||||
end
|
||||
|
||||
|
@ -112,7 +112,7 @@ defmodule Ash.Actions.Helpers do
|
|||
end
|
||||
|
||||
if api do
|
||||
case Ash.Api.authorize(api) do
|
||||
case Ash.Api.Info.authorize(api) do
|
||||
:always ->
|
||||
Keyword.put(opts, :authorize?, true)
|
||||
|
||||
|
@ -232,7 +232,7 @@ defmodule Ash.Actions.Helpers do
|
|||
|> Enum.reduce(result, fn key, record ->
|
||||
Map.put(record, key, nil)
|
||||
end)
|
||||
|> Ash.Resource.Info.put_metadata(:selected, select)
|
||||
|> Ash.Resource.put_metadata(:selected, select)
|
||||
end
|
||||
|
||||
def attributes_to_select(%{select: nil, resource: resource}) do
|
||||
|
|
|
@ -511,7 +511,7 @@ defmodule Ash.Actions.Load do
|
|||
end
|
||||
|
||||
defp lazy_load_or(data, lazy?, relationship, api, related_query, request_opts, func) do
|
||||
if lazy? && Ash.Resource.Info.loaded?(data, relationship) do
|
||||
if lazy? && Ash.Resource.loaded?(data, relationship) do
|
||||
pkey = Ash.Resource.Info.primary_key(related_query.resource)
|
||||
|
||||
data
|
||||
|
|
|
@ -174,11 +174,11 @@ defmodule Ash.Actions.Read do
|
|||
case load do
|
||||
{key, _value} ->
|
||||
calculation_or_aggregate?(resource, key) &&
|
||||
Ash.Resource.Info.loaded?(resource, key)
|
||||
Ash.Resource.loaded?(resource, key)
|
||||
|
||||
key ->
|
||||
calculation_or_aggregate?(resource, key) &&
|
||||
Ash.Resource.Info.loaded?(resource, key)
|
||||
Ash.Resource.loaded?(resource, key)
|
||||
end
|
||||
end)
|
||||
|
||||
|
@ -237,7 +237,7 @@ defmodule Ash.Actions.Read do
|
|||
query = %{
|
||||
query
|
||||
| api: api,
|
||||
timeout: timeout || query.timeout || Ash.Api.timeout(api)
|
||||
timeout: timeout || query.timeout || Ash.Api.Info.timeout(api)
|
||||
}
|
||||
|
||||
query =
|
||||
|
@ -469,7 +469,7 @@ defmodule Ash.Actions.Read do
|
|||
opts
|
||||
|> Keyword.take([:verbose?, :actor, :authorize?, :timeout])
|
||||
|> Keyword.put(:transaction?, action.transaction? || opts[:transaction?])
|
||||
|> Keyword.put(:default_timeout, Ash.Api.timeout(api))
|
||||
|> Keyword.put(:default_timeout, Ash.Api.Info.timeout(api))
|
||||
|> Keyword.put(:resource, resource)
|
||||
|> Keyword.put(:name, "#{inspect(resource)}.#{action.name}")
|
||||
end
|
||||
|
@ -1325,7 +1325,7 @@ defmodule Ash.Actions.Read do
|
|||
loads
|
||||
|> List.wrap()
|
||||
|> Enum.reject(fn load ->
|
||||
Ash.Resource.Info.loaded?(results, load)
|
||||
Ash.Resource.loaded?(results, load)
|
||||
end)
|
||||
end
|
||||
|
||||
|
|
|
@ -38,8 +38,8 @@ defmodule Ash.Actions.Update do
|
|||
notification_metadata: opts[:notification_metadata],
|
||||
return_notifications?: opts[:return_notifications?],
|
||||
authorize?: authorize?,
|
||||
timeout: opts[:timeout] || changeset.timeout || Ash.Api.timeout(api),
|
||||
default_timeout: Ash.Api.timeout(api),
|
||||
timeout: opts[:timeout] || changeset.timeout || Ash.Api.Info.timeout(api),
|
||||
default_timeout: Ash.Api.Info.timeout(api),
|
||||
transaction?: Keyword.get(opts, :transaction?, true)
|
||||
)
|
||||
|> case do
|
||||
|
@ -226,7 +226,7 @@ defmodule Ash.Actions.Update do
|
|||
else
|
||||
changeset = %{
|
||||
changeset
|
||||
| timeout: timeout || changeset.timeout || Ash.Api.timeout(changeset.api)
|
||||
| timeout: timeout || changeset.timeout || Ash.Api.Info.timeout(changeset.api)
|
||||
}
|
||||
|
||||
changeset =
|
||||
|
|
|
@ -34,8 +34,7 @@ defmodule Ash.Api do
|
|||
The functions documented here can be used to call any action on any resource in the Api.
|
||||
For example, `MyApi.read(Myresource, [...])`.
|
||||
|
||||
Additionally, you can define a `code_interface` on each resource to be exposed in the Api module.
|
||||
See the resource DSL documentation for more.
|
||||
Additionally, you can define a `code_interface` on each resource. See the code interface guide for more.
|
||||
"""
|
||||
|
||||
use Spark.Dsl, default_extensions: [extensions: [Ash.Api.Dsl]]
|
||||
|
@ -52,8 +51,6 @@ defmodule Ash.Api do
|
|||
|
||||
alias Ash.Error.Query.NotFound
|
||||
|
||||
alias Spark.Dsl.Extension
|
||||
|
||||
require Ash.Query
|
||||
|
||||
@dialyzer {:nowarn_function, unwrap_or_raise!: 3}
|
||||
|
@ -221,7 +218,7 @@ defmodule Ash.Api do
|
|||
type: :boolean,
|
||||
default: true,
|
||||
doc:
|
||||
"Wether or not an error should be returned or raised when the record is not found. If set to false, `nil` will be returned."
|
||||
"Whether or not an error should be returned or raised when the record is not found. If set to false, `nil` will be returned."
|
||||
],
|
||||
load: [
|
||||
type: :any,
|
||||
|
@ -532,99 +529,43 @@ defmodule Ash.Api do
|
|||
@callback reload(record :: Ash.Resource.record()) ::
|
||||
{:ok, Ash.Resource.record()} | {:error, term}
|
||||
|
||||
@doc false
|
||||
def handle_opts(_) do
|
||||
quote do
|
||||
@behaviour Ash.Api
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def handle_before_compile(_) do
|
||||
quote do
|
||||
use Ash.Api.Interface
|
||||
end
|
||||
end
|
||||
|
||||
def resource(api, resource) do
|
||||
cond do
|
||||
allow_unregistered?(api) ->
|
||||
if Spark.Dsl.is?(resource, Ash.Resource) do
|
||||
resource
|
||||
else
|
||||
nil
|
||||
end
|
||||
@deprecated "use Ash.Api.Info.resource/2 instead"
|
||||
defdelegate resource(api, resource), to: Ash.Api.Info
|
||||
|
||||
Ash.Resource.Info.embedded?(resource) ->
|
||||
resource
|
||||
@deprecated "use Ash.Api.Info.resources/1 instead"
|
||||
defdelegate resources(api), to: Ash.Api.Info
|
||||
|
||||
true ->
|
||||
api
|
||||
|> resources()
|
||||
|> Enum.find(&(&1 == resource))
|
||||
end
|
||||
|> case do
|
||||
nil ->
|
||||
if allowed?(allow(api), resource) do
|
||||
{:ok, resource}
|
||||
else
|
||||
{:error, NoSuchResource.exception(resource: resource)}
|
||||
end
|
||||
@deprecated "use Ash.Api.Info.registry/1 instead"
|
||||
defdelegate registry(api), to: Ash.Api.Info
|
||||
|
||||
resource ->
|
||||
{:ok, resource}
|
||||
end
|
||||
end
|
||||
@deprecated "use Ash.Api.Info.allow/1 instead"
|
||||
defdelegate allow(api), to: Ash.Api.Info
|
||||
|
||||
@spec allowed?(mfa | nil, module()) :: boolean
|
||||
defp allowed?({m, f, a}, resource) do
|
||||
apply(m, f, List.wrap(a) ++ [resource])
|
||||
end
|
||||
@deprecated "use Ash.Api.Info.timeout/1 instead"
|
||||
defdelegate timeout(api), to: Ash.Api.Info
|
||||
|
||||
defp allowed?(_, _), do: false
|
||||
@deprecated "use Ash.Api.Info.require_actor?/1 instead"
|
||||
defdelegate require_actor?(api), to: Ash.Api.Info
|
||||
|
||||
@doc """
|
||||
Gets the resources of an Api module. DO NOT USE AT COMPILE TIME.
|
||||
@deprecated "use Ash.Api.Info.authorize/1 instead"
|
||||
defdelegate authorize(api), to: Ash.Api.Info
|
||||
|
||||
If you need the resource list at compile time, you will need to introduce a compile time
|
||||
dependency on all of the resources, and therefore should use the registry directly. `Registry |> Ash.Registry.entries()`.
|
||||
"""
|
||||
@spec resources(Ash.Api.t()) :: list(Ash.Resource.t())
|
||||
def resources(api) do
|
||||
if registry = registry(api) do
|
||||
Ash.Registry.entries(registry)
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
@spec registry(Ash.Api.t()) :: atom | nil
|
||||
def registry(api) do
|
||||
Extension.get_opt(api, [:resources], :registry, nil, true)
|
||||
end
|
||||
|
||||
@spec allow(Ash.Api.t()) :: mfa() | nil
|
||||
def allow(api) do
|
||||
Extension.get_opt(api, [:resources], :allow, nil, true)
|
||||
end
|
||||
|
||||
@spec timeout(Ash.Api.t()) :: nil | :infinity | integer()
|
||||
def timeout(api) do
|
||||
Extension.get_opt(api, [:execution], :timeout, 30_000, true)
|
||||
end
|
||||
|
||||
@spec require_actor?(Ash.Api.t()) :: boolean
|
||||
def require_actor?(api) do
|
||||
Extension.get_opt(api, [:authorization], :require_actor?, false, true)
|
||||
end
|
||||
|
||||
@spec authorize(Ash.Api.t()) :: :when_requested | :always | :by_default
|
||||
def authorize(api) do
|
||||
Extension.get_opt(api, [:authorization], :authorize, :when_requested, true)
|
||||
end
|
||||
|
||||
@spec allow_unregistered?(Ash.Api.t()) :: atom | nil
|
||||
def allow_unregistered?(api) do
|
||||
Extension.get_opt(api, [:resources], :allow_unregistered?, nil)
|
||||
end
|
||||
@deprecated "use Ash.Api.Info.allow_unregistered?/1 instead"
|
||||
defdelegate allow_unregistered?(api), to: Ash.Api.Info
|
||||
|
||||
@doc false
|
||||
@spec get!(Ash.Api.t(), Ash.Resource.t(), term(), Keyword.t()) ::
|
||||
|
@ -642,7 +583,7 @@ defmodule Ash.Api do
|
|||
{:ok, Ash.Resource.record()} | {:error, term}
|
||||
def get(api, resource, id, opts) do
|
||||
with {:ok, opts} <- Spark.OptionsHelpers.validate(opts, @get_opts_schema),
|
||||
{:ok, resource} <- Ash.Api.resource(api, resource),
|
||||
{:ok, resource} <- Ash.Api.Info.resource(api, resource),
|
||||
{:ok, filter} <- Ash.Filter.get_filter(resource, id) do
|
||||
query =
|
||||
resource
|
||||
|
@ -711,6 +652,7 @@ defmodule Ash.Api do
|
|||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def page!(api, keyset, request) do
|
||||
{_, opts} = keyset.rerun
|
||||
|
||||
|
@ -719,6 +661,7 @@ defmodule Ash.Api do
|
|||
|> unwrap_or_raise!(opts[:stacktraces?])
|
||||
end
|
||||
|
||||
@doc false
|
||||
def page(_, %Ash.Page.Keyset{results: []} = page, :next) do
|
||||
{:ok, page}
|
||||
end
|
||||
|
@ -993,7 +936,7 @@ defmodule Ash.Api do
|
|||
| {:error, term}
|
||||
def create(api, changeset, opts) do
|
||||
with {:ok, opts} <- Spark.OptionsHelpers.validate(opts, @create_opts_schema),
|
||||
{:ok, resource} <- Ash.Api.resource(api, changeset.resource),
|
||||
{:ok, resource} <- Ash.Api.Info.resource(api, changeset.resource),
|
||||
{:ok, action} <- get_action(resource, opts, :create, changeset.action) do
|
||||
Create.run(api, changeset, action, opts)
|
||||
end
|
||||
|
@ -1015,7 +958,7 @@ defmodule Ash.Api do
|
|||
| {:error, term}
|
||||
def update(api, changeset, opts) do
|
||||
with {:ok, opts} <- Spark.OptionsHelpers.validate(opts, @update_opts_schema),
|
||||
{:ok, resource} <- Ash.Api.resource(api, changeset.resource),
|
||||
{:ok, resource} <- Ash.Api.Info.resource(api, changeset.resource),
|
||||
{:ok, action} <- get_action(resource, opts, :update, changeset.action) do
|
||||
Update.run(api, changeset, action, opts)
|
||||
end
|
||||
|
@ -1048,7 +991,7 @@ defmodule Ash.Api do
|
|||
| {:error, term}
|
||||
def destroy(api, %Ash.Changeset{resource: resource} = changeset, opts) do
|
||||
with {:ok, opts} <- Spark.OptionsHelpers.validate(opts, @destroy_opts_schema),
|
||||
{:ok, resource} <- Ash.Api.resource(api, resource),
|
||||
{:ok, resource} <- Ash.Api.Info.resource(api, resource),
|
||||
{:ok, action} <- get_action(resource, opts, :destroy, changeset.action) do
|
||||
Destroy.run(api, changeset, action, opts)
|
||||
end
|
||||
|
|
100
lib/ash/api/info.ex
Normal file
100
lib/ash/api/info.ex
Normal file
|
@ -0,0 +1,100 @@
|
|||
defmodule Ash.Api.Info do
|
||||
@moduledoc "Introspection tools for Ash.Api"
|
||||
|
||||
alias Ash.Error.Invalid.NoSuchResource
|
||||
|
||||
alias Spark.Dsl.Extension
|
||||
|
||||
@doc """
|
||||
Gets the resources of an Api module. DO NOT USE AT COMPILE TIME.
|
||||
|
||||
If you need the resource list at compile time, you will need to introduce a compile time
|
||||
dependency on all of the resources, and therefore should use the registry directly. `Registry |> Ash.Registry.Info.entries()`.
|
||||
"""
|
||||
@spec resources(Ash.Api.t()) :: list(Ash.Resource.t())
|
||||
def resources(api) do
|
||||
if registry = registry(api) do
|
||||
Ash.Registry.Info.entries(registry)
|
||||
else
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
@doc "The resource registry for an api"
|
||||
@spec registry(Ash.Api.t()) :: atom | nil
|
||||
def registry(api) do
|
||||
Extension.get_opt(api, [:resources], :registry, nil, true)
|
||||
end
|
||||
|
||||
@doc "The allow MFA for an api"
|
||||
@spec allow(Ash.Api.t()) :: mfa() | nil
|
||||
def allow(api) do
|
||||
Extension.get_opt(api, [:resources], :allow, nil, true)
|
||||
end
|
||||
|
||||
@doc "The execution timeout for an api"
|
||||
@spec timeout(Ash.Api.t()) :: nil | :infinity | integer()
|
||||
def timeout(api) do
|
||||
Extension.get_opt(api, [:execution], :timeout, 30_000, true)
|
||||
end
|
||||
|
||||
@doc "Whether or not the actor is always required for an api"
|
||||
@spec require_actor?(Ash.Api.t()) :: boolean
|
||||
def require_actor?(api) do
|
||||
Extension.get_opt(api, [:authorization], :require_actor?, false, true)
|
||||
end
|
||||
|
||||
@doc "When authorization should happen for a given api"
|
||||
@spec authorize(Ash.Api.t()) :: :when_requested | :always | :by_default
|
||||
def authorize(api) do
|
||||
Extension.get_opt(api, [:authorization], :authorize, :when_requested, true)
|
||||
end
|
||||
|
||||
@doc "Whether or not the api allows unregistered resources to be used with it"
|
||||
@spec allow_unregistered?(Ash.Api.t()) :: atom | nil
|
||||
def allow_unregistered?(api) do
|
||||
Extension.get_opt(api, [:resources], :allow_unregistered?, nil)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns `{:ok, resource}` if the resource can be used by the api, or `{:error, error}`.
|
||||
"""
|
||||
@spec resource(Ash.Api.t(), Ash.Resource.t()) ::
|
||||
{:ok, Ash.Resource.t()} | {:error, Ash.Error.t()}
|
||||
def resource(api, resource) do
|
||||
cond do
|
||||
allow_unregistered?(api) ->
|
||||
if Spark.Dsl.is?(resource, Ash.Resource) do
|
||||
resource
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
||||
Ash.Resource.Info.embedded?(resource) ->
|
||||
resource
|
||||
|
||||
true ->
|
||||
api
|
||||
|> resources()
|
||||
|> Enum.find(&(&1 == resource))
|
||||
end
|
||||
|> case do
|
||||
nil ->
|
||||
if allowed?(allow(api), resource) do
|
||||
{:ok, resource}
|
||||
else
|
||||
{:error, NoSuchResource.exception(resource: resource)}
|
||||
end
|
||||
|
||||
resource ->
|
||||
{:ok, resource}
|
||||
end
|
||||
end
|
||||
|
||||
@spec allowed?(mfa | nil, module()) :: boolean
|
||||
defp allowed?({m, f, a}, resource) do
|
||||
apply(m, f, List.wrap(a) ++ [resource])
|
||||
end
|
||||
|
||||
defp allowed?(_, _), do: false
|
||||
end
|
|
@ -1849,12 +1849,12 @@ defmodule Ash.Changeset do
|
|||
post1 =
|
||||
changeset
|
||||
|> Api.create!()
|
||||
|> Ash.Resource.Info.put_metadata(:join_keys, %{type: "a"})
|
||||
|> Ash.Resource.put_metadata(:join_keys, %{type: "a"})
|
||||
|
||||
post1 =
|
||||
changeset2
|
||||
|> Api.create!()
|
||||
|> Ash.Resource.Info.put_metadata(:join_keys, %{type: "b"})
|
||||
|> Ash.Resource.put_metadata(:join_keys, %{type: "b"})
|
||||
|
||||
author = Api.create!(author_changeset)
|
||||
|
||||
|
|
|
@ -40,17 +40,12 @@ defmodule Ash.DataLayer.Ets do
|
|||
transformers: [Ash.DataLayer.Transformers.RequirePreCheckWith]
|
||||
|
||||
alias Ash.Actions.Sort
|
||||
alias Spark.Dsl.Extension
|
||||
|
||||
@spec private?(Ash.Resource.t()) :: boolean
|
||||
def private?(resource) do
|
||||
Extension.get_opt(resource, [:ets], :private?, false, true)
|
||||
end
|
||||
@deprecated "use Ash.DataLayer.Ets.Info.private?/1 instead"
|
||||
defdelegate private?(resource), to: Ash.DataLayer.Ets.Info
|
||||
|
||||
@spec table(Ash.Resource.t()) :: boolean
|
||||
def table(resource) do
|
||||
Extension.get_opt(resource, [:ets], :table, resource, true) || resource
|
||||
end
|
||||
@deprecated "use Ash.DataLayer.Ets.Info.table/1 instead"
|
||||
defdelegate table(resource), to: Ash.DataLayer.Ets.Info
|
||||
|
||||
defmodule Query do
|
||||
@moduledoc false
|
||||
|
@ -165,6 +160,7 @@ defmodule Ash.DataLayer.Ets do
|
|||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def can?(resource, :async_engine) do
|
||||
not private?(resource)
|
||||
|
@ -198,6 +194,7 @@ defmodule Ash.DataLayer.Ets do
|
|||
def can?(_, {:sort, _}), do: true
|
||||
def can?(_, _), do: false
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def resource_to_query(resource, api) do
|
||||
%Query{
|
||||
|
@ -206,21 +203,26 @@ defmodule Ash.DataLayer.Ets do
|
|||
}
|
||||
end
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def limit(query, offset, _), do: {:ok, %{query | limit: offset}}
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def offset(query, offset, _), do: {:ok, %{query | offset: offset}}
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def add_calculation(query, calculation, _, _),
|
||||
do: {:ok, %{query | calculations: [calculation | query.calculations]}}
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def set_tenant(_resource, query, tenant) do
|
||||
{:ok, %{query | tenant: tenant}}
|
||||
end
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def filter(query, filter, _resource) do
|
||||
if query.filter do
|
||||
|
@ -230,11 +232,13 @@ defmodule Ash.DataLayer.Ets do
|
|||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def sort(query, sort, _resource) do
|
||||
{:ok, %{query | sort: sort}}
|
||||
end
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def run_aggregate_query(%{api: api} = query, aggregates, resource) do
|
||||
case run_query(query, resource) do
|
||||
|
@ -260,6 +264,7 @@ defmodule Ash.DataLayer.Ets do
|
|||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def run_query(
|
||||
%Query{
|
||||
|
@ -420,6 +425,7 @@ defmodule Ash.DataLayer.Ets do
|
|||
Ash.Filter.Runtime.filter_matches(api, records, filter)
|
||||
end
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def upsert(resource, changeset, keys) do
|
||||
keys = keys || Ash.Resource.Info.primary_key(resource)
|
||||
|
@ -459,6 +465,7 @@ defmodule Ash.DataLayer.Ets do
|
|||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def create(resource, changeset) do
|
||||
pkey =
|
||||
|
@ -525,6 +532,7 @@ defmodule Ash.DataLayer.Ets do
|
|||
end)
|
||||
end
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def destroy(resource, %{data: record} = changeset) do
|
||||
do_destroy(resource, record, changeset.tenant)
|
||||
|
@ -541,6 +549,7 @@ defmodule Ash.DataLayer.Ets do
|
|||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def update(resource, changeset) do
|
||||
pkey = pkey_map(resource, changeset.data)
|
17
lib/ash/data_layer/ets/info.ex
Normal file
17
lib/ash/data_layer/ets/info.ex
Normal file
|
@ -0,0 +1,17 @@
|
|||
defmodule Ash.DataLayer.Ets.Info do
|
||||
@moduledoc "Introspection helpers for the Ets data layer"
|
||||
|
||||
alias Spark.Dsl.Extension
|
||||
|
||||
@doc "Whether or not the ets table for the resource should be private"
|
||||
@spec private?(Ash.Resource.t()) :: boolean
|
||||
def private?(resource) do
|
||||
Extension.get_opt(resource, [:ets], :private?, false, true)
|
||||
end
|
||||
|
||||
@doc "The ets table name for a resource"
|
||||
@spec table(Ash.Resource.t()) :: boolean
|
||||
def table(resource) do
|
||||
Extension.get_opt(resource, [:ets], :table, resource, true) || resource
|
||||
end
|
||||
end
|
10
lib/ash/data_layer/mnesia/info.ex
Normal file
10
lib/ash/data_layer/mnesia/info.ex
Normal file
|
@ -0,0 +1,10 @@
|
|||
defmodule Ash.DataLayer.Mnesia.Info do
|
||||
@moduledoc "Introspection helpers for Ash.DataLayer.Mnesia"
|
||||
|
||||
alias Spark.Dsl.Extension
|
||||
|
||||
@doc "The mnesia table for a resource"
|
||||
def table(resource) do
|
||||
Extension.get_opt(resource, [:ets], :private?, resource, true)
|
||||
end
|
||||
end
|
|
@ -37,9 +37,11 @@ defmodule Ash.DataLayer.Mnesia do
|
|||
transformers: [Ash.DataLayer.Transformers.RequirePreCheckWith]
|
||||
|
||||
alias Ash.Actions.Sort
|
||||
alias Spark.Dsl.Extension
|
||||
alias :mnesia, as: Mnesia
|
||||
|
||||
@doc """
|
||||
Creates the table for each mnesia resource in an api
|
||||
"""
|
||||
def start(api) do
|
||||
Mnesia.create_schema([node()])
|
||||
Mnesia.start()
|
||||
|
@ -47,14 +49,13 @@ defmodule Ash.DataLayer.Mnesia do
|
|||
Code.ensure_compiled(api)
|
||||
|
||||
api
|
||||
|> Ash.Api.resources()
|
||||
|> Enum.each(fn resource ->
|
||||
resource |> table() |> Mnesia.create_table(attributes: [:_pkey, :val])
|
||||
|> Ash.Api.Info.resources()
|
||||
|> Enum.flat_map(fn resource ->
|
||||
resource
|
||||
|> Ash.DataLayer.Mnesia.Info.table()
|
||||
|> List.wrap()
|
||||
end)
|
||||
end
|
||||
|
||||
def table(resource) do
|
||||
Extension.get_opt(resource, [:ets], :private?, resource, true)
|
||||
|> Enum.each(&Mnesia.create_table(&1, attributes: [:_pkey, :val]))
|
||||
end
|
||||
|
||||
defmodule Query do
|
||||
|
@ -62,6 +63,10 @@ defmodule Ash.DataLayer.Mnesia do
|
|||
defstruct [:api, :resource, :filter, :limit, :sort, relationships: %{}, offset: 0]
|
||||
end
|
||||
|
||||
@deprecated "use Ash.DataLayer.Mnesia.Info.table/1 instead"
|
||||
defdelegate table(resource), to: Ash.DataLayer.Mnesia.Info
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def can?(_, :async_engine), do: true
|
||||
def can?(_, :composite_primary_key), do: true
|
||||
|
@ -82,7 +87,7 @@ defmodule Ash.DataLayer.Mnesia do
|
|||
# if someone needs to use these both and *actually* needs real joins for private
|
||||
# ets resources then we can talk about making this only happen in ash tests
|
||||
not (Ash.DataLayer.data_layer(resource) == Ash.DataLayer.Ets &&
|
||||
Ash.DataLayer.Ets.private?(resource))
|
||||
Ash.DataLayer.Ets.Info.private?(resource))
|
||||
end
|
||||
|
||||
def can?(_, {:filter_expr, _}), do: true
|
||||
|
@ -91,6 +96,7 @@ defmodule Ash.DataLayer.Mnesia do
|
|||
|
||||
def can?(_, _), do: false
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def resource_to_query(resource, api) do
|
||||
%Query{
|
||||
|
@ -99,15 +105,19 @@ defmodule Ash.DataLayer.Mnesia do
|
|||
}
|
||||
end
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def in_transaction?(_), do: Mnesia.is_transaction()
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def limit(query, offset, _), do: {:ok, %{query | limit: offset}}
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def offset(query, offset, _), do: {:ok, %{query | offset: offset}}
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def filter(query, filter, _resource) do
|
||||
if query.filter do
|
||||
|
@ -117,11 +127,13 @@ defmodule Ash.DataLayer.Mnesia do
|
|||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def sort(query, sort, _resource) do
|
||||
{:ok, %{query | sort: sort}}
|
||||
end
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def run_query(
|
||||
%Query{
|
||||
|
@ -177,6 +189,7 @@ defmodule Ash.DataLayer.Mnesia do
|
|||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def create(resource, changeset) do
|
||||
{:ok, record} = Ash.Changeset.apply_attributes(changeset)
|
||||
|
@ -209,6 +222,7 @@ defmodule Ash.DataLayer.Mnesia do
|
|||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def destroy(resource, %{data: record}) do
|
||||
pkey =
|
||||
|
@ -227,6 +241,7 @@ defmodule Ash.DataLayer.Mnesia do
|
|||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def update(resource, changeset) do
|
||||
pkey = pkey_list(resource, changeset.data)
|
||||
|
@ -297,6 +312,7 @@ defmodule Ash.DataLayer.Mnesia do
|
|||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def upsert(resource, changeset, keys) do
|
||||
keys = keys || Ash.Resource.Info.primary_key(resource)
|
||||
|
@ -336,6 +352,7 @@ defmodule Ash.DataLayer.Mnesia do
|
|||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
def transaction(_, func, _timeout) do
|
||||
case Mnesia.transaction(func) do
|
||||
|
@ -350,6 +367,7 @@ defmodule Ash.DataLayer.Mnesia do
|
|||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
@impl true
|
||||
@spec rollback(term, term) :: no_return
|
||||
def rollback(_, value) do
|
|
@ -8,8 +8,7 @@ defmodule Ash.DataLayer.Simple do
|
|||
|
||||
use Spark.Dsl.Extension, transformers: [], sections: []
|
||||
|
||||
def show_in_docs?, do: false
|
||||
|
||||
@doc false
|
||||
def can?(_, :create), do: true
|
||||
def can?(_, :update), do: true
|
||||
def can?(_, :destroy), do: true
|
||||
|
@ -35,10 +34,12 @@ defmodule Ash.DataLayer.Simple do
|
|||
Ash.Query.put_context(query, :data, 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(
|
||||
|
@ -61,20 +62,25 @@ defmodule Ash.DataLayer.Simple do
|
|||
end)}
|
||||
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
|
||||
case Map.fetch(context, :data) do
|
||||
{:ok, value} ->
|
||||
|
@ -85,14 +91,17 @@ defmodule Ash.DataLayer.Simple do
|
|||
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
|
||||
|
|
|
@ -32,7 +32,7 @@ defmodule Ash.Filter.Runtime do
|
|||
|> Enum.map(& &1.relationship_path)
|
||||
|> Enum.reject(&(&1 == []))
|
||||
|> Enum.uniq()
|
||||
|> Enum.reject(&Ash.Resource.Info.loaded?(records, &1))
|
||||
|> Enum.reject(&Ash.Resource.loaded?(records, &1))
|
||||
|> Enum.map(&path_to_load/1)
|
||||
|> case do
|
||||
[] ->
|
||||
|
@ -81,7 +81,7 @@ defmodule Ash.Filter.Runtime do
|
|||
|> Enum.uniq()
|
||||
|
||||
relationship_paths
|
||||
|> Enum.reject(&Ash.Resource.Info.loaded?(record, &1))
|
||||
|> Enum.reject(&Ash.Resource.loaded?(record, &1))
|
||||
|> case do
|
||||
[] ->
|
||||
{:ok,
|
||||
|
|
28
lib/ash/notifier/pub_sub/info.ex
Normal file
28
lib/ash/notifier/pub_sub/info.ex
Normal file
|
@ -0,0 +1,28 @@
|
|||
defmodule Ash.Notifier.PubSub.Info do
|
||||
@moduledoc "Introspection helpers for Ash.Notifier.PubSub"
|
||||
|
||||
@doc "The list of publications for a resource"
|
||||
def publications(resource) do
|
||||
Spark.Dsl.Extension.get_entities(resource, [:pub_sub])
|
||||
end
|
||||
|
||||
@doc "The pubsub module for a resource"
|
||||
def module(resource) do
|
||||
Spark.Dsl.Extension.get_opt(resource, [:pub_sub], :module, nil)
|
||||
end
|
||||
|
||||
@doc "The topic prefix for a resource"
|
||||
def prefix(resource) do
|
||||
Spark.Dsl.Extension.get_opt(resource, [:pub_sub], :prefix, nil)
|
||||
end
|
||||
|
||||
@doc "The pubsub name for a resource"
|
||||
def name(resource) do
|
||||
Spark.Dsl.Extension.get_opt(resource, [:pub_sub], :name, nil)
|
||||
end
|
||||
|
||||
@doc "The broadcast type for aresource"
|
||||
def broadcast_type(resource) do
|
||||
Spark.Dsl.Extension.get_opt(resource, [:pub_sub], :broadcast_type, nil)
|
||||
end
|
||||
end
|
|
@ -112,6 +112,14 @@ defmodule Ash.Notifier.PubSub do
|
|||
doc:
|
||||
"A prefix for all pubsub messages, e.g `users`. A message with `created` would be published as `users:created`"
|
||||
],
|
||||
broadcast_type: [
|
||||
type: {:one_of, [:notification, :phoenix_broadcast, :broadcast]},
|
||||
default: :notification,
|
||||
doc: """
|
||||
What shape the event payloads will be in. `:notification` just sends the notification, `phoenix_broadcast` sends a `%Phoenix.Socket.Broadcast{}`, and `:broadcast`
|
||||
sends `%{topic: <topic>, event: <event>, notification: <notification>}`
|
||||
"""
|
||||
],
|
||||
name: [
|
||||
type: :atom,
|
||||
doc: """
|
||||
|
@ -120,9 +128,6 @@ defmodule Ash.Notifier.PubSub do
|
|||
If you are simply using your `Endpoint` module for pubsub then this is unnecessary. If you want to use
|
||||
a custom pub started with something like `{Phoenix.PubSub, name: MyName}`, then you can provide `MyName` to
|
||||
here.
|
||||
|
||||
If this option is provided, we assume we are working with a `Phoenix.PubSub` and not a `Phoenix.Endpoint`, so
|
||||
the payload is sent as a `%Phoenix.Socket.Broadcast{}` if that module is available.
|
||||
"""
|
||||
]
|
||||
]
|
||||
|
@ -136,22 +141,19 @@ defmodule Ash.Notifier.PubSub do
|
|||
|
||||
use Spark.Dsl.Extension, sections: @sections
|
||||
|
||||
def publications(resource) do
|
||||
Spark.Dsl.Extension.get_entities(resource, [:pub_sub])
|
||||
end
|
||||
@deprecated "use Ash.Notifier.PubSub.Info.publications/1 instead"
|
||||
defdelegate publications(resource), to: Ash.Notifier.PubSub.Info
|
||||
|
||||
def module(resource) do
|
||||
Spark.Dsl.Extension.get_opt(resource, [:pub_sub], :module, nil)
|
||||
end
|
||||
@deprecated "use Ash.Notifier.PubSub.Info.module/1 instead"
|
||||
defdelegate module(resource), to: Ash.Notifier.PubSub.Info
|
||||
|
||||
def prefix(resource) do
|
||||
Spark.Dsl.Extension.get_opt(resource, [:pub_sub], :prefix, nil)
|
||||
end
|
||||
@deprecated "use Ash.Notifier.PubSub.Info.prefix/1 instead"
|
||||
defdelegate prefix(resource), to: Ash.Notifier.PubSub.Info
|
||||
|
||||
def name(resource) do
|
||||
Spark.Dsl.Extension.get_opt(resource, [:pub_sub], :name, nil)
|
||||
end
|
||||
@deprecated "use Ash.Notifier.PubSub.Info.name/1 instead"
|
||||
defdelegate name(resource), to: Ash.Notifier.PubSub.Info
|
||||
|
||||
@doc false
|
||||
def notify(%Ash.Notifier.Notification{resource: resource} = notification) do
|
||||
resource
|
||||
|> publications()
|
||||
|
@ -182,7 +184,7 @@ defmodule Ash.Notifier.PubSub do
|
|||
args =
|
||||
case name(notification.resource) do
|
||||
nil ->
|
||||
[prefixed_topic, event, notification]
|
||||
[prefixed_topic, event, to_payload(topic, event, notification)]
|
||||
|
||||
pub_sub ->
|
||||
payload = to_payload(topic, event, notification)
|
||||
|
@ -205,19 +207,41 @@ defmodule Ash.Notifier.PubSub do
|
|||
|
||||
if Code.ensure_loaded?(Phoenix.Socket.Broadcast) do
|
||||
def to_payload(topic, event, notification) do
|
||||
%Phoenix.Socket.Broadcast{
|
||||
topic: topic,
|
||||
event: event,
|
||||
payload: notification
|
||||
}
|
||||
case Ash.Notifier.PubSub.Info.broadcast_type(notification.resource) do
|
||||
:phoenix_broadcast ->
|
||||
%Phoenix.Socket.Broadcast{
|
||||
topic: topic,
|
||||
event: event,
|
||||
payload: notification
|
||||
}
|
||||
|
||||
:broadcast ->
|
||||
%{
|
||||
topic: topic,
|
||||
event: event,
|
||||
payload: notification
|
||||
}
|
||||
|
||||
:notification ->
|
||||
notification
|
||||
end
|
||||
end
|
||||
else
|
||||
def to_payload(topic, event, notification) do
|
||||
%{
|
||||
topic: topic,
|
||||
event: event,
|
||||
payload: notification
|
||||
}
|
||||
case Ash.Notifier.PubSub.Info.broadcast_type(notification.resource) do
|
||||
:phoenix_broadcast ->
|
||||
raise "A resource was configured with `broadcast_type :phoenix_broadcast` but `Phoenix.Socket.Broadcast` was not compiled."
|
||||
|
||||
:broadcast ->
|
||||
%{
|
||||
topic: topic,
|
||||
event: event,
|
||||
payload: notification
|
||||
}
|
||||
|
||||
:notification ->
|
||||
notification
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -49,8 +49,4 @@ defmodule Ash.Registry.Dsl do
|
|||
"""
|
||||
|
||||
use Spark.Dsl.Extension, sections: @sections, transformers: @transformers
|
||||
|
||||
def warn_on_empty?(registry) do
|
||||
Extension.get_opt(registry, [:entries], :warn_on_empty?, false, true)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,7 +12,7 @@ defmodule Ash.Registry.ResourceValidations.Transformers.EnsureNoEmbeds do
|
|||
@impl true
|
||||
def transform(registry, dsl) do
|
||||
registry
|
||||
|> Ash.Registry.entries()
|
||||
|> Ash.Registry.Info.entries()
|
||||
|> Enum.filter(&Ash.Resource.Info.embedded?/1)
|
||||
|> case do
|
||||
[] ->
|
||||
|
|
|
@ -12,7 +12,7 @@ defmodule Ash.Registry.ResourceValidations.Transformers.EnsureResourcesCompiled
|
|||
@impl true
|
||||
def transform(registry, dsl) do
|
||||
registry
|
||||
|> Ash.Registry.entries()
|
||||
|> Ash.Registry.Info.entries()
|
||||
|> Enum.map(fn resource ->
|
||||
try do
|
||||
# This is to get the compiler to ensure that the resource is compiled
|
||||
|
|
|
@ -13,7 +13,7 @@ defmodule Ash.Registry.ResourceValidations.Transformers.ValidateRelatedResourceI
|
|||
|
||||
@impl true
|
||||
def transform(module, dsl) do
|
||||
resources = Ash.Registry.entries(module)
|
||||
resources = Ash.Registry.Info.entries(module)
|
||||
|
||||
resources
|
||||
|> Enum.flat_map(&get_all_related_resources(&1, resources))
|
||||
|
|
23
lib/ash/registry/info.ex
Normal file
23
lib/ash/registry/info.ex
Normal file
|
@ -0,0 +1,23 @@
|
|||
defmodule Ash.Registry.Info do
|
||||
@moduledoc "Introspection helpers for `Ash.Registry`"
|
||||
|
||||
alias Spark.Dsl.Extension
|
||||
|
||||
@doc "Wether or not the registry will warn if it has no entries"
|
||||
@spec warn_on_empty?(Ash.Registry.t()) :: boolean
|
||||
def warn_on_empty?(registry) do
|
||||
Extension.get_opt(registry, [:entries], :warn_on_empty?, true, true)
|
||||
end
|
||||
|
||||
@doc "The list of entries in the registry"
|
||||
@spec entries(Ash.Registry.t()) :: list(module)
|
||||
def entries(registry) do
|
||||
case registry |> Extension.get_entities([:entries]) |> Enum.map(& &1.entry) do
|
||||
[] ->
|
||||
registry |> Extension.get_entities([:resources]) |> Enum.map(& &1.resource)
|
||||
|
||||
other ->
|
||||
other
|
||||
end
|
||||
end
|
||||
end
|
|
@ -23,25 +23,9 @@ defmodule Ash.Registry do
|
|||
|
||||
use Spark.Dsl, default_extensions: [extensions: [Ash.Registry.Dsl]]
|
||||
|
||||
alias Spark.Dsl.Extension
|
||||
@deprecated "use Ash.Registry.Info.warn_on_empty?/1 instead"
|
||||
defdelegate warn_on_empty?(registry), to: Ash.Registry.Info
|
||||
|
||||
@spec entries(t()) :: list(module)
|
||||
def entries(registry) do
|
||||
case registry |> Extension.get_entities([:entries]) |> Enum.map(& &1.entry) do
|
||||
[] ->
|
||||
registry |> Extension.get_entities([:resources]) |> Enum.map(& &1.resource)
|
||||
|
||||
other ->
|
||||
other
|
||||
end
|
||||
end
|
||||
|
||||
@spec warn_on_empty?(t()) :: boolean
|
||||
def warn_on_empty?(registry) do
|
||||
Extension.get_opt(registry, [:entries], :warn_on_empty?, true, true)
|
||||
end
|
||||
|
||||
@spec api_or_api_and_registry(Ash.Api.t() | {Ash.Api.t(), t()}) :: {t(), t()}
|
||||
def api_or_api_and_registry({api, registry}), do: {api, registry}
|
||||
def api_or_api_and_registry(api), do: {api, api}
|
||||
@deprecated "use Ash.Registry.Info.entries/1 instead"
|
||||
defdelegate entries(registry), to: Ash.Registry.Info
|
||||
end
|
||||
|
|
|
@ -3,8 +3,8 @@ defmodule Ash.Registry.Transformers.WarnOnEmpty do
|
|||
use Spark.Dsl.Transformer
|
||||
|
||||
def transform(registry, dsl) do
|
||||
if Ash.Registry.warn_on_empty?(registry) do
|
||||
case Ash.Registry.entries(registry) do
|
||||
if Ash.Registry.Info.warn_on_empty?(registry) do
|
||||
case Ash.Registry.Info.entries(registry) do
|
||||
[] ->
|
||||
{:warn, dsl, "#{inspect(registry)} has no entries."}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ defmodule Ash.Resource do
|
|||
extensions: [Ash.Resource.Dsl]
|
||||
]
|
||||
|
||||
@doc false
|
||||
def init(opts) do
|
||||
if opts[:data_layer] == :embedded do
|
||||
{:ok,
|
||||
|
@ -30,6 +31,7 @@ defmodule Ash.Resource do
|
|||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def handle_opts(opts) do
|
||||
quote bind_quoted: [embedded?: opts[:embedded?]] do
|
||||
if embedded? do
|
||||
|
@ -63,6 +65,7 @@ defmodule Ash.Resource do
|
|||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def handle_before_compile(_opts) do
|
||||
quote do
|
||||
require Ash.Schema
|
||||
|
@ -151,4 +154,116 @@ defmodule Ash.Resource do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
@spec set_metadata(Ash.Resource.record(), map) :: Ash.Resource.record()
|
||||
def set_metadata(record, map) do
|
||||
%{record | __metadata__: Ash.Helpers.deep_merge_maps(record.__metadata__, map)}
|
||||
end
|
||||
|
||||
@doc false
|
||||
def set_meta(%{__meta__: _} = struct, meta) do
|
||||
%{struct | __meta__: meta}
|
||||
end
|
||||
|
||||
def set_meta(struct, _), do: struct
|
||||
|
||||
@spec put_metadata(Ash.Resource.record(), atom, term) :: Ash.Resource.record()
|
||||
def put_metadata(record, key, term) do
|
||||
set_metadata(record, %{key => term})
|
||||
end
|
||||
|
||||
@doc "Sets a list of loaded key or paths to a key back to their original unloaded stated"
|
||||
@spec unload_many(
|
||||
nil | list(Ash.Resource.record()) | Ash.Resource.record() | Ash.Page.page(),
|
||||
list(atom) | list(list(atom))
|
||||
) ::
|
||||
nil | list(Ash.Resource.record()) | Ash.Resource.record() | Ash.Page.page()
|
||||
def unload_many(data, paths) do
|
||||
Enum.reduce(paths, data, &unload(&2, &1))
|
||||
end
|
||||
|
||||
@doc "Sets a loaded key or path to a key back to its original unloaded stated"
|
||||
@spec unload(
|
||||
nil | list(Ash.Resource.record()) | Ash.Resource.record() | Ash.Page.page(),
|
||||
atom | list(atom)
|
||||
) ::
|
||||
nil | list(Ash.Resource.record()) | Ash.Resource.record() | Ash.Page.page()
|
||||
def unload(nil, _), do: nil
|
||||
|
||||
def unload(%struct{results: results} = page, path)
|
||||
when struct in [Ash.Page.Keyset, Ash.Page.Offset] do
|
||||
%{page | results: unload(results, path)}
|
||||
end
|
||||
|
||||
def unload(records, path) when is_list(records) do
|
||||
Enum.map(records, &unload(&1, path))
|
||||
end
|
||||
|
||||
def unload(record, [path]) do
|
||||
unload(record, path)
|
||||
end
|
||||
|
||||
def unload(record, [key | rest]) do
|
||||
Map.update!(record, key, &unload(&1, rest))
|
||||
end
|
||||
|
||||
def unload(%struct{} = record, key) when is_atom(key) do
|
||||
Map.put(record, key, Map.get(struct.__struct__(), key))
|
||||
end
|
||||
|
||||
def unload(other, _), do: other
|
||||
|
||||
@doc "Returns true if the load or path to load has been loaded"
|
||||
@spec loaded?(
|
||||
nil | list(Ash.Resource.record()) | Ash.Resource.record() | Ash.Page.page(),
|
||||
atom | list(atom)
|
||||
) ::
|
||||
boolean
|
||||
def loaded?(nil, _), do: true
|
||||
|
||||
def loaded?(%page{results: results}, path) when page in [Ash.Page.Keyset, Ash.Page.Offset] do
|
||||
loaded?(results, path)
|
||||
end
|
||||
|
||||
def loaded?(records, path) when not is_list(path) do
|
||||
loaded?(records, [path])
|
||||
end
|
||||
|
||||
def loaded?(records, path) when is_list(records) do
|
||||
Enum.all?(records, &loaded?(&1, path))
|
||||
end
|
||||
|
||||
def loaded?(%Ash.NotLoaded{}, _), do: false
|
||||
|
||||
def loaded?(_, []), do: true
|
||||
|
||||
def loaded?(record, [key | rest]) do
|
||||
record
|
||||
|> Map.get(key)
|
||||
|> loaded?(rest)
|
||||
end
|
||||
|
||||
@spec get_metadata(Ash.Resource.record(), atom | list(atom)) :: term
|
||||
def get_metadata(record, key_or_path) do
|
||||
get_in(record.__metadata__ || %{}, List.wrap(key_or_path))
|
||||
end
|
||||
|
||||
@spec selected?(Ash.Resource.record(), atom) :: boolean
|
||||
def selected?(%resource{} = record, field) do
|
||||
case get_metadata(record, :selected) do
|
||||
nil ->
|
||||
attribute = Ash.Resource.Info.attribute(resource, field)
|
||||
|
||||
attribute && (!attribute.private? || attribute.primary_key?)
|
||||
|
||||
select ->
|
||||
if field in select do
|
||||
true
|
||||
else
|
||||
attribute = Ash.Resource.Info.attribute(resource, field)
|
||||
|
||||
attribute && attribute.primary_key?
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -3,23 +3,27 @@ defmodule Ash.Resource.Info do
|
|||
|
||||
alias Spark.Dsl.Extension
|
||||
|
||||
@spec set_metadata(Ash.Resource.record(), map) :: Ash.Resource.record()
|
||||
def set_metadata(record, map) do
|
||||
%{record | __metadata__: Ash.Helpers.deep_merge_maps(record.__metadata__, map)}
|
||||
end
|
||||
@deprecated "Use `Ash.Resource.set_metadata/2` instead"
|
||||
defdelegate set_metadata(record, map), to: Ash.Resource
|
||||
|
||||
@doc false
|
||||
def set_meta(%{__meta__: _} = struct, meta) do
|
||||
%{struct | __meta__: meta}
|
||||
end
|
||||
@deprecated "Use `Ash.Resource.put_metadata/3` instead"
|
||||
defdelegate put_metadata(record, key, term), to: Ash.Resource
|
||||
|
||||
def set_meta(struct, _), do: struct
|
||||
@deprecated "Use `Ash.Resource.unload_many/2` instead"
|
||||
defdelegate unload_many(record, loads), to: Ash.Resource
|
||||
|
||||
@spec put_metadata(Ash.Resource.record(), atom, term) :: Ash.Resource.record()
|
||||
def put_metadata(record, key, term) do
|
||||
set_metadata(record, %{key => term})
|
||||
end
|
||||
@deprecated "Use `Ash.Resource.unload/2` instead"
|
||||
defdelegate unload(record, key_or_path), to: Ash.Resource
|
||||
|
||||
@deprecated "Use `Ash.Resource.get_metadata/2` instead"
|
||||
defdelegate get_metadata(record, key_or_path), to: Ash.Resource
|
||||
|
||||
@deprecated "Use `Ash.Resource.selected?/2` instead"
|
||||
defdelegate selected?(record, field), to: Ash.Resource
|
||||
|
||||
@doc """
|
||||
Retrieves a relationship path from the resource related by path, to the provided resource.
|
||||
"""
|
||||
def reverse_relationship(resource, path, acc \\ [])
|
||||
|
||||
def reverse_relationship(_, [], acc),
|
||||
|
@ -66,136 +70,49 @@ defmodule Ash.Resource.Info do
|
|||
is_nil(rel.context)
|
||||
end
|
||||
|
||||
@doc "Sets a list of loaded key or paths to a key back to their original unloaded stated"
|
||||
@spec unload_many(
|
||||
nil | list(Ash.Resource.record()) | Ash.Resource.record() | Ash.Page.page(),
|
||||
list(atom) | list(list(atom))
|
||||
) ::
|
||||
nil | list(Ash.Resource.record()) | Ash.Resource.record() | Ash.Page.page()
|
||||
def unload_many(data, paths) do
|
||||
Enum.reduce(paths, data, &unload(&2, &1))
|
||||
end
|
||||
|
||||
@doc "Sets a loaded key or path to a key back to its original unloaded stated"
|
||||
@spec unload(
|
||||
nil | list(Ash.Resource.record()) | Ash.Resource.record() | Ash.Page.page(),
|
||||
atom | list(atom)
|
||||
) ::
|
||||
nil | list(Ash.Resource.record()) | Ash.Resource.record() | Ash.Page.page()
|
||||
def unload(nil, _), do: nil
|
||||
|
||||
def unload(%struct{results: results} = page, path)
|
||||
when struct in [Ash.Page.Keyset, Ash.Page.Offset] do
|
||||
%{page | results: unload(results, path)}
|
||||
end
|
||||
|
||||
def unload(records, path) when is_list(records) do
|
||||
Enum.map(records, &unload(&1, path))
|
||||
end
|
||||
|
||||
def unload(record, [path]) do
|
||||
unload(record, path)
|
||||
end
|
||||
|
||||
def unload(record, [key | rest]) do
|
||||
Map.update!(record, key, &unload(&1, rest))
|
||||
end
|
||||
|
||||
def unload(%struct{} = record, key) when is_atom(key) do
|
||||
Map.put(record, key, Map.get(struct.__struct__(), key))
|
||||
end
|
||||
|
||||
def unload(other, _), do: other
|
||||
|
||||
@doc "Returns true if the load or path to load has been loaded"
|
||||
@spec loaded?(
|
||||
nil | list(Ash.Resource.record()) | Ash.Resource.record() | Ash.Page.page(),
|
||||
atom | list(atom)
|
||||
) ::
|
||||
boolean
|
||||
def loaded?(nil, _), do: true
|
||||
|
||||
def loaded?(%page{results: results}, path) when page in [Ash.Page.Keyset, Ash.Page.Offset] do
|
||||
loaded?(results, path)
|
||||
end
|
||||
|
||||
def loaded?(records, path) when not is_list(path) do
|
||||
loaded?(records, [path])
|
||||
end
|
||||
|
||||
def loaded?(records, path) when is_list(records) do
|
||||
Enum.all?(records, &loaded?(&1, path))
|
||||
end
|
||||
|
||||
def loaded?(%Ash.NotLoaded{}, _), do: false
|
||||
|
||||
def loaded?(_, []), do: true
|
||||
|
||||
def loaded?(record, [key | rest]) do
|
||||
record
|
||||
|> Map.get(key)
|
||||
|> loaded?(rest)
|
||||
end
|
||||
|
||||
@spec get_metadata(Ash.Resource.record(), atom | list(atom)) :: term
|
||||
def get_metadata(record, key_or_path) do
|
||||
get_in(record.__metadata__ || %{}, List.wrap(key_or_path))
|
||||
end
|
||||
|
||||
@spec selected?(Ash.Resource.record(), atom) :: boolean
|
||||
def selected?(%resource{} = record, field) do
|
||||
case get_metadata(record, :selected) do
|
||||
nil ->
|
||||
attribute = Ash.Resource.Info.attribute(resource, field)
|
||||
|
||||
attribute && (!attribute.private? || attribute.primary_key?)
|
||||
|
||||
select ->
|
||||
if field in select do
|
||||
true
|
||||
else
|
||||
attribute = Ash.Resource.Info.attribute(resource, field)
|
||||
|
||||
attribute && attribute.primary_key?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@doc """
|
||||
The list of code interface definitions.
|
||||
"""
|
||||
@spec interfaces(Ash.Resource.t()) :: [Ash.Resource.Interface.t()]
|
||||
def interfaces(resource) do
|
||||
Extension.get_entities(resource, [:code_interface])
|
||||
end
|
||||
|
||||
@spec define_interface_in_resource?(Ash.Resource.t()) :: boolean
|
||||
def define_interface_in_resource?(resource) do
|
||||
!!Extension.get_opt(resource, [:code_interface], :define_for, false)
|
||||
end
|
||||
|
||||
@doc """
|
||||
The Api to define the interface for, when defining it in the resource
|
||||
"""
|
||||
@spec define_interface_for(Ash.Resource.t()) :: atom | nil
|
||||
def define_interface_for(resource) do
|
||||
Extension.get_opt(resource, [:code_interface], :define_for, nil)
|
||||
end
|
||||
|
||||
@spec extensions(Ash.Resource.t()) :: [module]
|
||||
def extensions(resource) do
|
||||
Extension.get_persisted(resource, :extensions, [])
|
||||
end
|
||||
|
||||
@doc """
|
||||
Whether or not the resource is an embedded resource
|
||||
"""
|
||||
@spec embedded?(Ash.Resource.t()) :: boolean
|
||||
def embedded?(resource) do
|
||||
Extension.get_persisted(resource, :embedded?, false)
|
||||
end
|
||||
|
||||
@doc """
|
||||
The description of the resource
|
||||
"""
|
||||
@spec description(Ash.Resource.t()) :: String.t() | nil
|
||||
def description(resource) do
|
||||
Extension.get_opt(resource, [:resource], :description, "no description")
|
||||
end
|
||||
|
||||
@doc """
|
||||
The base filter of the resource
|
||||
"""
|
||||
@spec base_filter(Ash.Resource.t()) :: term
|
||||
def base_filter(resource) do
|
||||
Extension.get_opt(resource, [:resource], :base_filter, nil)
|
||||
end
|
||||
|
||||
@doc """
|
||||
The default context of the resource
|
||||
"""
|
||||
@spec default_context(Ash.Resource.t()) :: term
|
||||
def default_context(resource) do
|
||||
Extension.get_opt(resource, [:resource], :default_context, nil)
|
||||
|
@ -227,6 +144,7 @@ defmodule Ash.Resource.Info do
|
|||
Extension.get_persisted(resource, :notifiers, [])
|
||||
end
|
||||
|
||||
@doc "A list of all validations for the resource for a given action type"
|
||||
@spec validations(Ash.Resource.t(), :create | :update | :destroy) :: [
|
||||
Ash.Resource.Validation.t()
|
||||
]
|
||||
|
@ -242,6 +160,7 @@ defmodule Ash.Resource.Info do
|
|||
Extension.get_entities(resource, [:validations])
|
||||
end
|
||||
|
||||
@doc "A list of all changes for the resource for a given action type"
|
||||
@spec changes(Ash.Resource.t(), :create | :update | :destroy) ::
|
||||
list(
|
||||
Ash.Resource.Validation.t()
|
||||
|
@ -344,17 +263,19 @@ defmodule Ash.Resource.Info do
|
|||
|> Enum.find(&(&1.name == relationship_name && !&1.private?))
|
||||
end
|
||||
|
||||
@doc "Get the multitenancy strategy for a resource"
|
||||
@doc "The multitenancy strategy for a resource"
|
||||
@spec multitenancy_strategy(Ash.Resource.t()) :: :context | :attribute | nil
|
||||
def multitenancy_strategy(resource) do
|
||||
Spark.Dsl.Extension.get_opt(resource, [:multitenancy], :strategy, nil)
|
||||
end
|
||||
|
||||
@doc "The multitenancy attribute for a resource"
|
||||
@spec multitenancy_attribute(Ash.Resource.t()) :: atom | nil
|
||||
def multitenancy_attribute(resource) do
|
||||
Spark.Dsl.Extension.get_opt(resource, [:multitenancy], :attribute, nil)
|
||||
end
|
||||
|
||||
@doc "The function to parse the tenant from the attribute"
|
||||
@spec multitenancy_parse_attribute(Ash.Resource.t()) :: {atom, atom, list(any)}
|
||||
def multitenancy_parse_attribute(resource) do
|
||||
Spark.Dsl.Extension.get_opt(
|
||||
|
@ -368,16 +289,19 @@ defmodule Ash.Resource.Info do
|
|||
@doc false
|
||||
def _identity(x), do: x
|
||||
|
||||
@doc "The MFA to parse the tenant from the attribute"
|
||||
@spec multitenancy_global?(Ash.Resource.t()) :: atom | nil
|
||||
def multitenancy_global?(resource) do
|
||||
Spark.Dsl.Extension.get_opt(resource, [:multitenancy], :global?, nil)
|
||||
end
|
||||
|
||||
@doc "The source attribute for multitenancy"
|
||||
@spec multitenancy_source(Ash.Resource.t()) :: atom | nil
|
||||
def multitenancy_source(resource) do
|
||||
Spark.Dsl.Extension.get_opt(resource, [:multitenancy], :source, nil)
|
||||
end
|
||||
|
||||
@doc "The template for creating the tenant name"
|
||||
@spec multitenancy_template(Ash.Resource.t()) :: atom | nil
|
||||
def multitenancy_template(resource) do
|
||||
Spark.Dsl.Extension.get_opt(resource, [:multitenancy], :template, nil)
|
||||
|
|
|
@ -8,7 +8,7 @@ defmodule Ash.Resource.Interface do
|
|||
|
||||
defmacro __using__(_) do
|
||||
quote bind_quoted: [], generated: true do
|
||||
if Ash.Resource.Info.define_interface_in_resource?(__MODULE__) do
|
||||
if Ash.Resource.Info.define_interface_for(__MODULE__) do
|
||||
require Ash.CodeInterface
|
||||
|
||||
Ash.CodeInterface.define_interface(
|
||||
|
|
|
@ -76,7 +76,7 @@ defmodule Ash.Resource.Relationships.SharedOptions do
|
|||
type: :boolean,
|
||||
default: true,
|
||||
doc: """
|
||||
Wether or not the relationship may be managed.
|
||||
Whether or not the relationship may be managed.
|
||||
""",
|
||||
links: []
|
||||
],
|
||||
|
@ -117,7 +117,7 @@ defmodule Ash.Resource.Relationships.SharedOptions do
|
|||
default: false,
|
||||
links: [],
|
||||
doc: """
|
||||
Wether or not related values may exist for this relationship at creation.
|
||||
Whether or not related values may exist for this relationship at creation.
|
||||
"""
|
||||
],
|
||||
violation_message: [
|
||||
|
|
6
mix.exs
6
mix.exs
|
@ -201,7 +201,7 @@ defmodule Ash.MixProject do
|
|||
# Run "mix help deps" to learn about dependencies.
|
||||
defp deps do
|
||||
[
|
||||
{:spark, "~> 0.1 and >= 0.1.6"},
|
||||
{:spark, "~> 0.1 and >= 0.1.7"},
|
||||
{:ecto, "~> 3.7"},
|
||||
{:ets, "~> 0.8.0"},
|
||||
{:decimal, "~> 2.0"},
|
||||
|
@ -228,8 +228,8 @@ defmodule Ash.MixProject do
|
|||
[
|
||||
sobelow: "sobelow --skip",
|
||||
credo: "credo --strict",
|
||||
"ash.formatter":
|
||||
"ash.formatter --extensions Ash.Resource.Dsl,Ash.Api.Dsl,Ash.Flow.Dsl,Ash.Registry.Dsl,Ash.DataLayer.Ets,Ash.DataLayer.Mnesia,Ash.Notifier.PubSub,Ash.Policy.Authorizer"
|
||||
"spark.formatter":
|
||||
"spark.formatter --extensions Ash.Resource.Dsl,Ash.Api.Dsl,Ash.Flow.Dsl,Ash.Registry.Dsl,Ash.DataLayer.Ets,Ash.DataLayer.Mnesia,Ash.Notifier.PubSub,Ash.Policy.Authorizer"
|
||||
]
|
||||
end
|
||||
end
|
||||
|
|
2
mix.lock
2
mix.lock
|
@ -35,7 +35,7 @@
|
|||
"providers": {:hex, :providers, "1.8.1", "70b4197869514344a8a60e2b2a4ef41ca03def43cfb1712ecf076a0f3c62f083", [:rebar3], [{:getopt, "1.0.1", [hex: :getopt, repo: "hexpm", optional: false]}], "hexpm", "e45745ade9c476a9a469ea0840e418ab19360dc44f01a233304e118a44486ba0"},
|
||||
"sobelow": {:hex, :sobelow, "0.11.1", "23438964486f8112b41e743bbfd402da3e5b296fdc9eacab29914b79c48916dd", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "9897363a7eff96f4809304a90aad819e2ad5e5d24db547af502885146746a53c"},
|
||||
"sourceror": {:hex, :sourceror, "0.11.1", "1b80efe84330beefb6b3da95b75c1e1cdefe9dc785bf4c5064fae251a8af615c", [:mix], [], "hexpm", "22b6828ee5572f6cec75cc6357f3ca6c730a02954cef0302c428b3dba31e5e74"},
|
||||
"spark": {:hex, :spark, "0.1.6", "74659fdee3b68d7f8e78e3b479e9fe3cc9f4454de8bbcdc52f8808b86a7483c2", [:mix], [{:nimble_options, "~> 0.4.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:sourceror, "~> 0.11.1", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "877d460958d990784f2e2caa7a0d2dc2b50a8e251b0692caef3c2715c20648d9"},
|
||||
"spark": {:hex, :spark, "0.1.7", "1691e87a4aa08ea4b04751d20308c0d077d90d1e20eff2f5e0d2d4b5a552f23e", [:mix], [{:nimble_options, "~> 0.4.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:sourceror, "~> 0.11.1", [hex: :sourceror, repo: "hexpm", optional: false]}], "hexpm", "e2447df8f717fef3888f4b8fa727b6a878f5165cf55aa97d342488ae37052353"},
|
||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
|
||||
"stream_data": {:hex, :stream_data, "0.5.0", "b27641e58941685c75b353577dc602c9d2c12292dd84babf506c2033cd97893e", [:mix], [], "hexpm", "012bd2eec069ada4db3411f9115ccafa38540a3c78c4c0349f151fc761b9e271"},
|
||||
"telemetry": {:hex, :telemetry, "1.1.0", "a589817034a27eab11144ad24d5c0f9fab1f58173274b1e9bae7074af9cbee51", [:rebar3], [], "hexpm", "b727b2a1f75614774cff2d7565b64d0dfa5bd52ba517f16543e6fc7efcc0df48"},
|
||||
|
|
|
@ -19,6 +19,51 @@ These should all be straight forward enough to do a simple find and replace in y
|
|||
- `destination_field_on_join_table` -> `destination_attribute_on_join_resource`
|
||||
- `no_fields?` -> `no_attributes?`
|
||||
|
||||
## DSL changes
|
||||
|
||||
A new option has been added to the pub_sub notifier. If you are using it with phoenix, and you want it to publish a `%Phoenix.Socket.Broadcast{}` struct (which is what it used to do if you specified the `name` option with pub sub), then you'll need to set `broadcast_type :phoenix_broadcast`
|
||||
|
||||
## Function Changes
|
||||
|
||||
The following functions have been moved from `Ash.Resource.Info` to `Ash.Resource`. The old functions still exist, but will warn as deprecated.
|
||||
|
||||
- `set_metadata/2`
|
||||
- `put_metadata/3`
|
||||
- `unload_many/2`
|
||||
- `unload/2`
|
||||
- `get_metadata/2`
|
||||
- `selected?/2`
|
||||
|
||||
The following functions have been moved from `Ash.Api` to `Ash.Api.Info`. The old functions still exist, but will warn as deprecated.
|
||||
|
||||
- `resource/2`
|
||||
- `resources/1`
|
||||
- `registry/1`
|
||||
- `allow/1`
|
||||
- `timeout/1`
|
||||
- `require_actor?/1`
|
||||
- `authorize/1`
|
||||
- `allow_unregistered?/1`
|
||||
|
||||
The following functions have been moved from `Ash.Notifier.PubSub` to `Ash.Notifier.PubSub.Info`. The old functions still exist, but will warn as deprecated.
|
||||
|
||||
- `publications/1`
|
||||
- `module/1`
|
||||
- `prefix/1`
|
||||
- `name/1`
|
||||
|
||||
The following functions have been moved. The old functions still exist, but will warn as deprecated.
|
||||
|
||||
- `Ash.DataLayer.Ets.private?/1` -> `Ash.DataLayer.Ets.Info.private?/1`
|
||||
- `Ash.DataLayer.Ets.table/1` -> `Ash.DataLayer.Ets.Info.table/1`
|
||||
- `Ash.DataLayer.Mnesia.table/1` -> `Ash.DataLayer.Mnesia.table/1`
|
||||
- `Ash.Registry.warn_on_empty?/1` -> `Ash.Registry.Info.warn_on_empty?/1`
|
||||
- `Ash.Registry.entries/1` -> `Ash.Registry.Info.entries/1`
|
||||
|
||||
The following functions have been moved:
|
||||
|
||||
- `Ash.Resource.extensions/1` -> `Spark.extensions/1`
|
||||
|
||||
## Upgrading to 1.53
|
||||
|
||||
### Default actions
|
||||
|
|
|
@ -29,7 +29,7 @@ have a condition like `actor_attribute_equals(:admin, true)`.
|
|||
If both apply (i.e an admin is using a read action), then both policies must pass.
|
||||
A policy can produce one of three results: `:forbidden`, `:authorized`, or `:unknown`. `:unknown` is treated
|
||||
the same as a `:forbidden`.
|
||||
A policy contains checks, which determine wether or not the policy passes for a given request.
|
||||
A policy contains checks, which determine whether or not the policy passes for a given request.
|
||||
|
||||
#### Bypass
|
||||
|
||||
|
|
|
@ -297,7 +297,7 @@ defmodule Ash.Test.Actions.LoadTest do
|
|||
|> Api.read!(authorize?: true)
|
||||
|
||||
assert author
|
||||
|> Ash.Resource.Info.unload([:posts, :author])
|
||||
|> Ash.Resource.unload([:posts, :author])
|
||||
|> Map.get(:posts)
|
||||
|> Enum.all?(fn post ->
|
||||
%Ash.NotLoaded{} = post.author
|
||||
|
|
|
@ -13,7 +13,7 @@ defmodule Ash.Test.Actions.ReadTest do
|
|||
|
||||
def prepare(query, _, _) do
|
||||
Ash.Query.after_action(query, fn _query, authors ->
|
||||
{:ok, Enum.map(authors, &Ash.Resource.Info.set_metadata(&1, %{prepared?: true}))}
|
||||
{:ok, Enum.map(authors, &Ash.Resource.set_metadata(&1, %{prepared?: true}))}
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -459,8 +459,8 @@ defmodule Ash.Test.Actions.UpdateTest do
|
|||
post
|
||||
|> new()
|
||||
|> replace_relationship(:related_posts, [
|
||||
Ash.Resource.Info.set_metadata(post2, %{join_keys: %{type: "a"}}),
|
||||
Ash.Resource.Info.set_metadata(post3, %{join_keys: %{type: "b"}})
|
||||
Ash.Resource.set_metadata(post2, %{join_keys: %{type: "a"}}),
|
||||
Ash.Resource.set_metadata(post3, %{join_keys: %{type: "b"}})
|
||||
])
|
||||
|> Api.update!()
|
||||
|> Api.load!(:related_posts_join_assoc)
|
||||
|
@ -475,8 +475,8 @@ defmodule Ash.Test.Actions.UpdateTest do
|
|||
|> replace_relationship(
|
||||
:related_posts,
|
||||
[
|
||||
Ash.Resource.Info.set_metadata(post2, %{join_keys: %{type: "c"}}),
|
||||
Ash.Resource.Info.set_metadata(post3, %{join_keys: %{type: "d"}})
|
||||
Ash.Resource.set_metadata(post2, %{join_keys: %{type: "c"}}),
|
||||
Ash.Resource.set_metadata(post3, %{join_keys: %{type: "d"}})
|
||||
],
|
||||
on_match: :update,
|
||||
on_lookup: :relate
|
||||
|
|
Loading…
Reference in a new issue