mirror of
https://github.com/ash-project/ash.git
synced 2024-09-17 03:53:02 +12:00
improvement: add :* as a valid value in skip_unknown_inputs
improvement: add `skip_unknown_inputs` to individual actions improvement: add `skip_unknown_inputs` constraint to embedded resources
This commit is contained in:
parent
4763181ff9
commit
32c8da1f8d
17 changed files with 75 additions and 33 deletions
|
@ -187,6 +187,7 @@ spark_locals_without_parens = [
|
|||
sensitive?: 1,
|
||||
short_name: 1,
|
||||
skip_global_validations?: 1,
|
||||
skip_unknown_inputs: 1,
|
||||
soft?: 1,
|
||||
sort: 1,
|
||||
sortable?: 1,
|
||||
|
|
|
@ -471,7 +471,7 @@ end
|
|||
| [`return_stream?`](#reactor-bulk_create-return_stream?){: #reactor-bulk_create-return_stream? } | `boolean` | `false` | If set to `true`, instead of an `Ash.BulkResult`, a mixed stream is returned. |
|
||||
| [`rollback_on_error?`](#reactor-bulk_create-rollback_on_error?){: #reactor-bulk_create-rollback_on_error? } | `boolean` | `true` | Whether or not to rollback the transaction on error, if the resource is in a transaction. |
|
||||
| [`select`](#reactor-bulk_create-select){: #reactor-bulk_create-select } | `atom \| list(atom)` | | A select statement to apply to records. Ignored if `return_records?` is not `true`. |
|
||||
| [`skip_unknown_inputs`](#reactor-bulk_create-skip_unknown_inputs){: #reactor-bulk_create-skip_unknown_inputs } | `atom \| list(atom)` | | A list of inputs that, if provided, will be ignored if they are not recognized by the action. |
|
||||
| [`skip_unknown_inputs`](#reactor-bulk_create-skip_unknown_inputs){: #reactor-bulk_create-skip_unknown_inputs } | `atom \| String.t \| list(atom \| String.t)` | | A list of inputs that, if provided, will be ignored if they are not recognized by the action. Use `:*` to indicate all unknown keys. |
|
||||
| [`sorted?`](#reactor-bulk_create-sorted?){: #reactor-bulk_create-sorted? } | `boolean` | `false` | Whether or not to sort results by their input position, in cases where `return_records?` is set to `true`. |
|
||||
| [`stop_on_error?`](#reactor-bulk_create-stop_on_error?){: #reactor-bulk_create-stop_on_error? } | `boolean` | `false` | If `true`, the first encountered error will stop the action and be returned. Otherwise, errors will be skipped. |
|
||||
| [`success_state`](#reactor-bulk_create-success_state){: #reactor-bulk_create-success_state } | `:success \| :partial_success` | `:success` | Bulk results can be entirely or partially successful. Chooses the `Ash.BulkResult` state to consider the step a success. |
|
||||
|
@ -673,7 +673,7 @@ end
|
|||
| [`reuse_values?`](#reactor-bulk_update-reuse_values?){: #reactor-bulk_update-reuse_values? } | `boolean` | `false` | Whether calculations are allowed to reuse values that have already been loaded, or must refetch them from the data layer. |
|
||||
| [`rollback_on_error?`](#reactor-bulk_update-rollback_on_error?){: #reactor-bulk_update-rollback_on_error? } | `boolean` | `true` | Whether or not to rollback the transaction on error, if the resource is in a transaction. |
|
||||
| [`select`](#reactor-bulk_update-select){: #reactor-bulk_update-select } | `atom \| list(atom)` | | A select statement to apply to records. Ignored if `return_records?` is not `true`. |
|
||||
| [`skip_unknown_inputs`](#reactor-bulk_update-skip_unknown_inputs){: #reactor-bulk_update-skip_unknown_inputs } | `atom \| list(atom)` | | A list of inputs that, if provided, will be ignored if they are not recognized by the action. |
|
||||
| [`skip_unknown_inputs`](#reactor-bulk_update-skip_unknown_inputs){: #reactor-bulk_update-skip_unknown_inputs } | `atom \| String.t \| list(atom \| String.t)` | | A list of inputs that, if provided, will be ignored if they are not recognized by the action. Use `:*` to indicate all unknown keys. |
|
||||
| [`sorted?`](#reactor-bulk_update-sorted?){: #reactor-bulk_update-sorted? } | `boolean` | `false` | Whether or not to sort results by their input position, in cases where `return_records?` is set to `true`. |
|
||||
| [`stop_on_error?`](#reactor-bulk_update-stop_on_error?){: #reactor-bulk_update-stop_on_error? } | `boolean` | `false` | If `true`, the first encountered error will stop the action and be returned. Otherwise, errors will be skipped. |
|
||||
| [`strategy`](#reactor-bulk_update-strategy){: #reactor-bulk_update-strategy } | `list(:atomic \| :atomic_batches \| :stream)` | `[:atomic]` | The strategy or strategies to enable. `:stream` is used in all cases if the data layer does not support atomics. |
|
||||
|
|
|
@ -1028,6 +1028,7 @@ end
|
|||
| [`skip_global_validations?`](#actions-create-skip_global_validations?){: #actions-create-skip_global_validations? } | `boolean` | `false` | If true, global validations will be skipped. Useful for manual actions. |
|
||||
| [`error_handler`](#actions-create-error_handler){: #actions-create-error_handler } | `mfa` | | Sets the error handler on the changeset. See `Ash.Changeset.handle_errors/2` for more |
|
||||
| [`notifiers`](#actions-create-notifiers){: #actions-create-notifiers } | `list(module)` | | Notifiers that will be called specifically for this action. |
|
||||
| [`skip_unknown_inputs`](#actions-create-skip_unknown_inputs){: #actions-create-skip_unknown_inputs } | `atom \| String.t \| list(atom \| String.t)` | `[]` | A list of unknown fields to skip, or `:*` to skip all unknown fields. |
|
||||
| [`manual?`](#actions-create-manual?){: #actions-create-manual? } | `boolean` | | Instructs Ash to *skip* the actual update/create/destroy step at the data layer. See the [manual actions guide](/documentation/topics/manual-actions.md) for more. |
|
||||
|
||||
|
||||
|
@ -1510,6 +1511,7 @@ update :flag_for_review, primary?: true
|
|||
| [`skip_global_validations?`](#actions-update-skip_global_validations?){: #actions-update-skip_global_validations? } | `boolean` | `false` | If true, global validations will be skipped. Useful for manual actions. |
|
||||
| [`error_handler`](#actions-update-error_handler){: #actions-update-error_handler } | `mfa` | | Sets the error handler on the changeset. See `Ash.Changeset.handle_errors/2` for more |
|
||||
| [`notifiers`](#actions-update-notifiers){: #actions-update-notifiers } | `list(module)` | | Notifiers that will be called specifically for this action. |
|
||||
| [`skip_unknown_inputs`](#actions-update-skip_unknown_inputs){: #actions-update-skip_unknown_inputs } | `atom \| String.t \| list(atom \| String.t)` | `[]` | A list of unknown fields to skip, or `:*` to skip all unknown fields. |
|
||||
| [`manual?`](#actions-update-manual?){: #actions-update-manual? } | `boolean` | | Instructs Ash to *skip* the actual update/create/destroy step at the data layer. See the [manual actions guide](/documentation/topics/manual-actions.md) for more. |
|
||||
|
||||
|
||||
|
@ -1752,6 +1754,7 @@ end
|
|||
| [`skip_global_validations?`](#actions-destroy-skip_global_validations?){: #actions-destroy-skip_global_validations? } | `boolean` | `false` | If true, global validations will be skipped. Useful for manual actions. |
|
||||
| [`error_handler`](#actions-destroy-error_handler){: #actions-destroy-error_handler } | `mfa` | | Sets the error handler on the changeset. See `Ash.Changeset.handle_errors/2` for more |
|
||||
| [`notifiers`](#actions-destroy-notifiers){: #actions-destroy-notifiers } | `list(module)` | | Notifiers that will be called specifically for this action. |
|
||||
| [`skip_unknown_inputs`](#actions-destroy-skip_unknown_inputs){: #actions-destroy-skip_unknown_inputs } | `atom \| String.t \| list(atom \| String.t)` | `[]` | A list of unknown fields to skip, or `:*` to skip all unknown fields. |
|
||||
| [`manual?`](#actions-destroy-manual?){: #actions-destroy-manual? } | `boolean` | | Instructs Ash to *skip* the actual update/create/destroy step at the data layer. See the [manual actions guide](/documentation/topics/manual-actions.md) for more. |
|
||||
|
||||
|
||||
|
|
29
lib/ash.ex
29
lib/ash.ex
|
@ -98,6 +98,11 @@ defmodule Ash do
|
|||
""",
|
||||
default: false
|
||||
],
|
||||
skip_unknown_inputs: [
|
||||
type: {:wrap_list, {:or, [:atom, :string]}},
|
||||
doc:
|
||||
"A list of inputs that, if provided, will be ignored if they are not recognized by the action. Use `:*` to indicate all unknown keys."
|
||||
],
|
||||
reuse_values?: [
|
||||
type: :boolean,
|
||||
default: false,
|
||||
|
@ -248,6 +253,11 @@ defmodule Ash do
|
|||
Metadata to be merged into the metadata field for all notifications sent from this operation.
|
||||
"""
|
||||
],
|
||||
skip_unknown_inputs: [
|
||||
type: {:wrap_list, {:or, [:atom, :string]}},
|
||||
doc:
|
||||
"A list of inputs that, if provided, will be ignored if they are not recognized by the action. Use `:*` to indicate all unknown keys."
|
||||
],
|
||||
load: [
|
||||
type: :any,
|
||||
doc: "A load statement to add onto the changeset"
|
||||
|
@ -402,9 +412,9 @@ defmodule Ash do
|
|||
"If set to a value greater than 0, up to that many tasks will be started to run batches asynchronously"
|
||||
],
|
||||
skip_unknown_inputs: [
|
||||
type: {:list, {:or, [:atom, :string]}},
|
||||
type: {:wrap_list, {:or, [:atom, :string]}},
|
||||
doc:
|
||||
"A list of inputs that, if provided, will be ignored if they are not recognized by the action."
|
||||
"A list of inputs that, if provided, will be ignored if they are not recognized by the action. Use `:*` to indicate all unknown keys."
|
||||
]
|
||||
]
|
||||
|
||||
|
@ -452,11 +462,6 @@ defmodule Ash do
|
|||
doc:
|
||||
"The strategy or strategies to enable. :stream is used in all cases if the data layer does not support atomics."
|
||||
],
|
||||
skip_unknown_inputs: [
|
||||
type: {:list, {:or, [:atom, :string]}},
|
||||
doc:
|
||||
"A list of inputs that, if provided, will be ignored if they are not recognized by the action."
|
||||
],
|
||||
load: [
|
||||
type: :any,
|
||||
doc: "A load statement to apply on the resulting records."
|
||||
|
@ -516,11 +521,6 @@ defmodule Ash do
|
|||
type: :any,
|
||||
doc:
|
||||
"A filter to apply to records. This is also applied to a stream of inputs."
|
||||
],
|
||||
skip_unknown_inputs: [
|
||||
type: {:list, {:or, [:atom, :string]}},
|
||||
doc:
|
||||
"A list of inputs that, if provided, will be ignored if they are not recognized by the action."
|
||||
]
|
||||
]
|
||||
|> Spark.Options.merge(
|
||||
|
@ -572,11 +572,6 @@ defmodule Ash do
|
|||
]},
|
||||
doc:
|
||||
"The fields to upsert. If not set, the action's `upsert_fields` is used. Unlike singular `create`, `bulk_create` with `upsert?` requires that `upsert_fields` be specified explicitly in one of these two locations."
|
||||
],
|
||||
skip_unknown_inputs: [
|
||||
type: {:list, {:or, [:atom, :string]}},
|
||||
doc:
|
||||
"A list of inputs that, if provided, will be ignored if they are not recognized by the action."
|
||||
]
|
||||
]
|
||||
|> Spark.Options.merge(
|
||||
|
|
|
@ -61,8 +61,22 @@ defmodule Ash.Actions.Helpers do
|
|||
defp set_context(%Ash.ActionInput{} = action_input, context),
|
||||
do: Ash.ActionInput.set_context(action_input, context)
|
||||
|
||||
defp set_skip_unknown_opts(opts, %{action: %{skip_unknown_inputs: skip_unknown_inputs}}) do
|
||||
Keyword.update(
|
||||
opts,
|
||||
:skip_unknown_inputs,
|
||||
skip_unknown_inputs,
|
||||
&Enum.concat(List.wrap(&1), skip_unknown_inputs)
|
||||
)
|
||||
end
|
||||
|
||||
defp set_skip_unknown_opts(opts, _query_or_changeset) do
|
||||
opts
|
||||
end
|
||||
|
||||
def set_context_and_get_opts(domain, query_or_changeset, opts) do
|
||||
opts = transform_tenant(opts)
|
||||
opts = set_skip_unknown_opts(opts, query_or_changeset)
|
||||
query_or_changeset = set_context(query_or_changeset, opts[:context] || %{})
|
||||
|
||||
domain =
|
||||
|
|
|
@ -962,6 +962,9 @@ defmodule Ash.Changeset do
|
|||
match?("_" <> _, key) ->
|
||||
{:cont, changeset}
|
||||
|
||||
:* in List.wrap(opts[:skip_unknown_inputs]) ->
|
||||
{:cont, changeset}
|
||||
|
||||
key in List.wrap(opts[:skip_unknown_inputs]) ->
|
||||
{:cont, changeset}
|
||||
|
||||
|
@ -998,6 +1001,9 @@ defmodule Ash.Changeset do
|
|||
{:halt, {:not_atomic, reason}}
|
||||
end
|
||||
|
||||
:* in List.wrap(opts[:skip_unknown_inputs]) ->
|
||||
{:cont, changeset}
|
||||
|
||||
key in List.wrap(opts[:skip_unknown_inputs]) ->
|
||||
{:cont, changeset}
|
||||
|
||||
|
@ -1020,6 +1026,9 @@ defmodule Ash.Changeset do
|
|||
match?("_" <> _, key) ->
|
||||
{:cont, changeset}
|
||||
|
||||
:* in List.wrap(opts[:skip_unknown_inputs]) ->
|
||||
{:cont, changeset}
|
||||
|
||||
key in List.wrap(opts[:skip_unknown_inputs]) ->
|
||||
{:cont, changeset}
|
||||
|
||||
|
@ -1112,9 +1121,9 @@ defmodule Ash.Changeset do
|
|||
doc: "set the tenant on the changeset"
|
||||
],
|
||||
skip_unknown_inputs: [
|
||||
type: {:list, {:or, [:atom, :string]}},
|
||||
type: {:wrap_list, {:or, [:atom, :string]}},
|
||||
doc:
|
||||
"A list of inputs that, if provided, will be ignored if they are not recognized by the action."
|
||||
"A list of inputs that, if provided, will be ignored if they are not recognized by the action. Use `:*` to indicate all unknown keys."
|
||||
],
|
||||
context: [
|
||||
type: :map,
|
||||
|
@ -2037,6 +2046,9 @@ defmodule Ash.Changeset do
|
|||
cond do
|
||||
!Ash.Resource.Info.action_input?(changeset.resource, action.name, name) ->
|
||||
cond do
|
||||
:* in List.wrap(opts[:skip_unknown_inputs]) ->
|
||||
changeset
|
||||
|
||||
name in skip_unknown_inputs ->
|
||||
changeset
|
||||
|
||||
|
|
|
@ -364,6 +364,7 @@ defmodule Ash.EmbeddableType do
|
|||
:destroy_action,
|
||||
:update_action,
|
||||
:domain,
|
||||
:skip_unknown_inputs,
|
||||
:__source__
|
||||
])
|
||||
|> Keyword.put(:on_update,
|
||||
|
@ -448,6 +449,8 @@ defmodule Ash.EmbeddableType do
|
|||
|> Enum.concat(
|
||||
Enum.flat_map(Ash.Resource.Info.primary_key(__MODULE__), &[&1, to_string(&1)])
|
||||
)
|
||||
|> Enum.concat(List.wrap(constraints[:skip_unknown_inputs]))
|
||||
|> Enum.concat(List.wrap(constraints[:items][:skip_unknown_inputs]))
|
||||
end
|
||||
|
||||
def prepare_change(old_value, "", constraints) do
|
||||
|
|
|
@ -474,10 +474,6 @@ defmodule Ash.Helpers do
|
|||
get_domain(resource, opts)
|
||||
end
|
||||
|
||||
def get_domain(%resource{}, opts) do
|
||||
get_domain(resource, opts)
|
||||
end
|
||||
|
||||
def get_domain(nil, opts) do
|
||||
cond do
|
||||
domain = opts[:domain] ->
|
||||
|
|
|
@ -472,9 +472,9 @@ defmodule Ash.Query do
|
|||
doc: "set the tenant on the query"
|
||||
],
|
||||
skip_unknown_inputs: [
|
||||
type: {:list, {:or, [:atom, :string]}},
|
||||
type: {:wrap_list, {:or, [:atom, :string]}},
|
||||
doc:
|
||||
"A list of inputs that, if provided, will be ignored if they are not recognized by the action."
|
||||
"A list of inputs that, if provided, will be ignored if they are not recognized by the action. Use `:*` to indicate all unknown keys."
|
||||
]
|
||||
]
|
||||
|
||||
|
@ -648,6 +648,9 @@ defmodule Ash.Query do
|
|||
has_argument?(action, name) ->
|
||||
set_argument(query, name, value)
|
||||
|
||||
:* in List.wrap(opts[:skip_unknown_inputs]) ->
|
||||
query
|
||||
|
||||
name in skip_unknown_inputs ->
|
||||
query
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ defmodule Ash.Reactor.Dsl.BulkCreate do
|
|||
return_stream?: boolean,
|
||||
rollback_on_error?: boolean,
|
||||
select: [atom],
|
||||
skip_unknown_inputs: [atom],
|
||||
skip_unknown_inputs: list(atom | String.t()),
|
||||
sorted?: boolean,
|
||||
stop_on_error?: boolean,
|
||||
success_state: :success | :partial_success,
|
||||
|
@ -226,9 +226,9 @@ defmodule Ash.Reactor.Dsl.BulkCreate do
|
|||
required: false
|
||||
],
|
||||
skip_unknown_inputs: [
|
||||
type: {:wrap_list, :atom},
|
||||
type: {:wrap_list, {:or, [:atom, :string]}},
|
||||
doc:
|
||||
"A list of inputs that, if provided, will be ignored if they are not recognized by the action.",
|
||||
"A list of inputs that, if provided, will be ignored if they are not recognized by the action. Use `:*` to indicate all unknown keys.",
|
||||
required: false
|
||||
],
|
||||
sorted?: [
|
||||
|
|
|
@ -88,7 +88,7 @@ defmodule Ash.Reactor.Dsl.BulkUpdate do
|
|||
reuse_values?: boolean,
|
||||
rollback_on_error?: boolean,
|
||||
select: [atom],
|
||||
skip_unknown_inputs: [atom],
|
||||
skip_unknown_inputs: list(atom | String.t()),
|
||||
sorted?: boolean,
|
||||
stop_on_error?: boolean,
|
||||
strategy: :atomic | :atomic_batches | :stream,
|
||||
|
@ -290,9 +290,9 @@ defmodule Ash.Reactor.Dsl.BulkUpdate do
|
|||
required: false
|
||||
],
|
||||
skip_unknown_inputs: [
|
||||
type: {:wrap_list, :atom},
|
||||
type: {:wrap_list, {:or, [:atom, :string]}},
|
||||
doc:
|
||||
"A list of inputs that, if provided, will be ignored if they are not recognized by the action.",
|
||||
"A list of inputs that, if provided, will be ignored if they are not recognized by the action. Use `:*` to indicate all unknown keys.",
|
||||
required: false
|
||||
],
|
||||
sorted?: [
|
||||
|
|
|
@ -8,6 +8,7 @@ defmodule Ash.Resource.Actions.Action do
|
|||
:run,
|
||||
constraints: [],
|
||||
touches_resources: [],
|
||||
skip_unknown_inputs: [],
|
||||
arguments: [],
|
||||
allow_nil?: false,
|
||||
transaction?: false,
|
||||
|
@ -20,6 +21,7 @@ defmodule Ash.Resource.Actions.Action do
|
|||
name: atom,
|
||||
description: String.t() | nil,
|
||||
arguments: [Ash.Resource.Actions.Argument.t()],
|
||||
skip_unknown_inputs: list(atom() | String.t()),
|
||||
allow_nil?: boolean,
|
||||
touches_resources: [Ash.Resource.t()],
|
||||
constraints: Keyword.t(),
|
||||
|
|
|
@ -13,6 +13,7 @@ defmodule Ash.Resource.Actions.Create do
|
|||
touches_resources: [],
|
||||
delay_global_validations?: false,
|
||||
skip_global_validations?: false,
|
||||
skip_unknown_inputs: [],
|
||||
upsert?: false,
|
||||
upsert_identity: nil,
|
||||
upsert_fields: nil,
|
||||
|
@ -32,6 +33,7 @@ defmodule Ash.Resource.Actions.Create do
|
|||
allow_nil_input: list(atom),
|
||||
manual: module | nil,
|
||||
upsert?: boolean,
|
||||
skip_unknown_inputs: list(atom | String.t()),
|
||||
notifiers: [module()],
|
||||
delay_global_validations?: boolean,
|
||||
skip_global_validations?: boolean,
|
||||
|
|
|
@ -11,6 +11,7 @@ defmodule Ash.Resource.Actions.Destroy do
|
|||
:error_handler,
|
||||
manual: nil,
|
||||
require_atomic?: Application.compile_env(:ash, :require_atomic_by_default?, true),
|
||||
skip_unknown_inputs: [],
|
||||
atomic_upgrade?: true,
|
||||
atomic_upgrade_with: nil,
|
||||
arguments: [],
|
||||
|
@ -35,6 +36,7 @@ defmodule Ash.Resource.Actions.Destroy do
|
|||
notifiers: list(module),
|
||||
arguments: list(Ash.Resource.Actions.Argument.t()),
|
||||
atomic_upgrade?: boolean(),
|
||||
skip_unknown_inputs: list(atom() | String.t()),
|
||||
atomic_upgrade_with: nil | atom(),
|
||||
require_atomic?: boolean,
|
||||
accept: nil | list(atom),
|
||||
|
|
|
@ -9,6 +9,7 @@ defmodule Ash.Resource.Actions.Read do
|
|||
get?: nil,
|
||||
manual: nil,
|
||||
metadata: [],
|
||||
skip_unknown_inputs: [],
|
||||
modify_query: nil,
|
||||
multitenancy: nil,
|
||||
name: nil,
|
||||
|
@ -29,6 +30,7 @@ defmodule Ash.Resource.Actions.Read do
|
|||
filters: [any],
|
||||
manual: atom | {atom, Keyword.t()} | nil,
|
||||
metadata: [Ash.Resource.Actions.Metadata.t()],
|
||||
skip_unknown_inputs: list(atom | String.t()),
|
||||
modify_query: nil | mfa,
|
||||
multitenancy: atom,
|
||||
name: atom,
|
||||
|
|
|
@ -69,6 +69,11 @@ defmodule Ash.Resource.Actions.SharedOptions do
|
|||
type: {:list, {:behaviour, Ash.Notifier}},
|
||||
doc: "Notifiers that will be called specifically for this action."
|
||||
],
|
||||
skip_unknown_inputs: [
|
||||
type: {:wrap_list, {:or, [:atom, :string]}},
|
||||
default: [],
|
||||
doc: "A list of unknown fields to skip, or `:*` to skip all unknown fields."
|
||||
],
|
||||
manual?: [
|
||||
type: :boolean,
|
||||
doc: """
|
||||
|
|
|
@ -11,6 +11,7 @@ defmodule Ash.Resource.Actions.Update do
|
|||
accept: nil,
|
||||
require_attributes: [],
|
||||
allow_nil_input: [],
|
||||
skip_unknown_inputs: [],
|
||||
manual: nil,
|
||||
manual?: false,
|
||||
require_atomic?: Application.compile_env(:ash, :require_atomic_by_default?, true),
|
||||
|
@ -33,6 +34,7 @@ defmodule Ash.Resource.Actions.Update do
|
|||
type: :update,
|
||||
name: atom,
|
||||
manual: module | nil,
|
||||
skip_unknown_inputs: list(atom | String.t()),
|
||||
atomic_upgrade?: boolean(),
|
||||
atomic_upgrade_with: nil | atom(),
|
||||
notifiers: list(module),
|
||||
|
|
Loading…
Reference in a new issue