Packages

ash

3.28.0
3.29.3 3.29.2 3.29.1 3.29.0 3.28.0 3.27.8 3.27.7 3.27.6 3.27.5 3.27.4 3.27.3 3.27.2 3.27.1 3.27.0 3.26.0 3.25.2 3.25.1 3.25.0 retired 3.24.7 3.24.6 3.24.5 3.24.4 3.24.3 3.24.2 3.24.1 3.24.0 3.23.1 3.23.0 3.22.2 3.22.1 3.22.0 3.21.3 3.21.2 3.21.1 3.21.0 3.20.0 3.19.3 3.19.2 3.19.1 3.19.0 3.18.0 3.17.1 3.17.0 3.16.0 3.15.0 3.14.1 3.14.0 retired 3.13.2 3.13.1 3.13.0 3.12.0 3.11.3 3.11.2 3.11.1 3.11.0 3.10.1 3.10.0 3.9.0 3.8.0 3.7.6 3.7.5 3.7.4 3.7.3 3.7.2 3.7.1 3.7.0 retired 3.6.3 retired 3.6.2 3.6.1 3.6.0 3.5.43 3.5.42 3.5.41 3.5.40 3.5.39 3.5.38 3.5.37 3.5.36 3.5.35 3.5.34 3.5.33 3.5.32 3.5.31 3.5.30 3.5.29 3.5.28 3.5.27 3.5.26 3.5.25 3.5.24 3.5.23 3.5.22 3.5.21 3.5.20 3.5.19 3.5.18 3.5.17 3.5.16 3.5.15 3.5.14 3.5.13 3.5.12 3.5.11 3.5.10 3.5.9 3.5.8 3.5.7 3.5.6 3.5.5 3.5.4 3.5.3 3.5.2 3.5.1 3.5.0 3.4.74 retired 3.4.73 3.4.72 3.4.71 3.4.70 3.4.69 3.4.68 3.4.67 3.4.66 3.4.65 3.4.64 3.4.63 3.4.62 3.4.61 3.4.60 3.4.59 3.4.58 3.4.57 3.4.56 3.4.55 3.4.54 3.4.53 3.4.52 3.4.51 3.4.50 3.4.49 3.4.48 3.4.47 3.4.46 3.4.45 3.4.44 3.4.43 3.4.42 3.4.41 3.4.40 3.4.39 3.4.38 3.4.37 3.4.36 3.4.35 3.4.34 3.4.33 3.4.32 3.4.31 3.4.30 3.4.29 3.4.28 3.4.27 3.4.26 3.4.25 3.4.24 3.4.23 3.4.22 3.4.21 3.4.20 3.4.19 3.4.18 3.4.17 3.4.16 3.4.15 3.4.14 3.4.13 3.4.12 3.4.11 3.4.10 3.4.9 3.4.8 3.4.7 3.4.6 3.4.5 3.4.4 3.4.3 3.4.2 3.4.1 3.4.0 3.3.3 3.3.2 3.3.1 3.3.0 3.2.6 3.2.5 3.2.4 3.2.3 3.2.2 3.2.1 3.2.0 3.1.8 3.1.7 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.16 3.0.15 3.0.14 3.0.13 3.0.12 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.4 3.0.3 3.0.2 3.0.1 3.0.0 3.0.0-rc.46 3.0.0-rc.45 3.0.0-rc.44 3.0.0-rc.43 3.0.0-rc.42 3.0.0-rc.41 3.0.0-rc.40 3.0.0-rc.39 3.0.0-rc.38 3.0.0-rc.37 3.0.0-rc.36 3.0.0-rc.35 3.0.0-rc.34 3.0.0-rc.33 3.0.0-rc.32 3.0.0-rc.31 3.0.0-rc.29 3.0.0-rc.28 3.0.0-rc.27 3.0.0-rc.26 3.0.0-rc.25 3.0.0-rc.24 3.0.0-rc.23 3.0.0-rc.22 3.0.0-rc.21 3.0.0-rc.20 3.0.0-rc.19 3.0.0-rc.18 3.0.0-rc.17 3.0.0-rc.16 3.0.0-rc.15 3.0.0-rc.14 3.0.0-rc.13 3.0.0-rc.12 3.0.0-rc.11 3.0.0-rc.10 3.0.0-rc.9 3.0.0-rc.8 3.0.0-rc.7 3.0.0-rc.6 3.0.0-rc.5 3.0.0-rc.4 3.0.0-rc.3 3.0.0-rc.1 3.0.0-rc.0 2.21.15 2.21.14 2.21.13 2.21.12 2.21.11 2.21.10 2.21.9 2.21.8 2.21.7 2.21.6 2.21.5 2.21.4 2.21.3 2.21.2 2.21.1 2.21.0 2.20.3 2.20.2 2.20.1 2.20.0 2.19.14 2.19.13 2.19.12 2.19.11 2.19.10 2.19.9 2.19.8 2.19.7 2.19.6 2.19.5 2.19.4 2.19.3 retired 2.19.2 retired 2.19.1 retired 2.19.0 retired 2.18.2 2.18.1 2.18.0 2.17.24 2.17.23 2.17.22 2.17.21 2.17.20 2.17.19 2.17.18 2.17.17 2.17.16 2.17.15 2.17.14 2.17.13 2.17.12 2.17.11 2.17.10 2.17.9 2.17.8 2.17.7 2.17.6 2.17.5 2.17.4 2.17.3 2.17.2 2.17.1 2.17.0 2.16.1 2.16.0 2.15.20 2.15.19 2.15.18 2.15.17 2.15.16 2.15.15 2.15.14 2.15.13 2.15.12 2.15.11 2.15.10 2.15.9 2.15.8 2.15.7 2.15.6 2.15.5 2.15.4 2.15.2 2.15.1 2.15.0 2.14.21 2.14.20 2.14.19 2.14.18 2.14.17 2.14.16 2.14.15 2.14.14 2.14.13 2.14.12 2.14.11 2.14.10 2.14.9 2.14.8 2.14.7 2.14.6 2.14.5 2.14.4 2.14.3 2.14.2 2.14.1 2.14.0 2.13.4 retired 2.13.3 2.13.2 2.13.1 2.13.0 2.12.1 2.12.0 2.11.11 2.11.10 2.11.9 2.11.8 2.11.7 2.11.6 2.11.5 2.11.4 2.11.3 2.11.2 2.11.1 2.11.0 2.11.0-rc.3 2.11.0-rc.2 2.11.0-rc.1 2.11.0-rc.0 2.10.2 2.10.1 2.10.0 2.9.29 2.9.28 2.9.27 2.9.26 2.9.25 2.9.24 2.9.23 2.9.22 2.9.21 2.9.20 2.9.19 2.9.18 2.9.17 2.9.16 2.9.15 2.9.14 2.9.13 2.9.12 2.9.11 2.9.10 2.9.9 2.9.8 2.9.7 2.9.6 2.9.5 2.9.4 2.9.3 2.9.2 2.9.1 2.9.0 2.8.1 2.8.0 2.7.1 2.7.0 2.6.31 2.6.30 2.6.29 2.6.28 2.6.27 2.6.26 2.6.25 2.6.24 2.6.23 2.6.22 2.6.21 2.6.20 2.6.19 2.6.18 2.6.17 2.6.16 2.6.15 2.6.14 2.6.13 2.6.11 2.6.10 2.6.9 2.6.8 2.6.7 2.6.6 2.6.5 2.6.4 2.6.3 2.6.2 2.6.1 2.6.0 2.5.16 2.5.15 2.5.14 2.5.13 2.5.12 2.5.11 2.5.10 2.5.9 2.5.8 2.5.7 2.5.6 2.5.5 2.5.4 2.5.3 2.5.2 2.5.1 2.5.0 2.5.0-rc.6 2.5.0-rc.5 2.5.0-rc.4 2.5.0-rc.3 2.5.0-rc.2 2.5.0-rc.1 2.5.0-rc.0 2.4.30 2.4.29 2.4.28 2.4.27 2.4.26 2.4.25 2.4.24 2.4.23 2.4.22 2.4.21 2.4.20 2.4.19 2.4.18 2.4.17 2.4.16 2.4.15 2.4.14 2.4.13 2.4.12 2.4.11 2.4.10 2.4.9 2.4.8 2.4.7 2.4.6 2.4.5 2.4.4 2.4.3 2.4.2 2.4.1 2.4.0 2.3.0 2.2.0 2.1.0 2.0.0 2.0.0-rc.15 2.0.0-rc.14 2.0.0-rc.13 2.0.0-rc.12 2.0.0-rc.11 2.0.0-rc.10 2.0.0-rc.9 2.0.0-rc.8 2.0.0-rc.7 2.0.0-rc.6 2.0.0-rc.5 2.0.0-rc.4 2.0.0-rc.3 2.0.0-rc.2 2.0.0-rc.1 2.0.0-rc.0 2.0.0-pre.8 2.0.0-pre.7 2.0.0-pre.6 2.0.0-pre.5 2.0.0-pre.4 2.0.0-pre.3 2.0.0-pre.2 2.0.0-pre.1 2.0.0-pre.0 1.53.3 1.53.2 1.53.0 1.52.0-rc.22 1.52.0-rc.21 1.52.0-rc.20 1.52.0-rc.19 1.52.0-rc.18 1.52.0-rc.17 1.52.0-rc.16 1.52.0-rc.15 1.52.0-rc.14 1.52.0-rc.13 1.52.0-rc.12 1.52.0-rc.11 1.52.0-rc.10 1.52.0-rc.9 1.52.0-rc.8 1.52.0-rc.7 1.52.0-rc.6 1.52.0-rc.5 1.52.0-rc.4 1.52.0-rc.3 1.52.0-rc.2 1.52.0-rc.1 1.52.0-rc.0 1.51.2 1.51.1 retired 1.51.0 1.50.21 1.50.20 1.50.19 1.50.18 1.50.17 1.50.16 1.50.15 1.50.14 1.50.13 1.50.12 1.50.11 1.50.10 1.50.9 1.50.8 1.50.7 1.50.6 1.50.5 1.50.4 1.50.3 1.50.2 1.50.1 1.50.0 1.49.0 1.48.0-rc.30 1.48.0-rc.29 1.48.0-rc.28 1.48.0-rc.27 1.48.0-rc.26 1.48.0-rc.25 1.48.0-rc.24 1.48.0-rc.23 1.48.0-rc.22 1.48.0-rc.21 1.48.0-rc.20 1.48.0-rc.19 1.48.0-rc.18 1.48.0-rc.17 1.48.0-rc.16 1.48.0-rc.15 1.48.0-rc.14 1.48.0-rc.13 1.48.0-rc.12 1.48.0-rc.11 1.48.0-rc.10 1.48.0-rc.9 1.48.0-rc.8 1.48.0-rc.7 1.48.0-rc.6 1.48.0-rc.5 1.48.0-rc.4 1.48.0-rc.3 1.48.0-rc.2 1.48.0-rc.1 1.48.0-rc.0 1.47.12 1.47.11 1.47.10 1.47.9 1.47.8 1.47.7 1.47.6 1.47.5 1.47.4 1.47.3 1.47.2 1.47.1 1.47.0 1.46.13 1.46.12 1.46.11 1.46.10 1.46.9 1.46.8 1.46.7 1.46.6 1.46.5 1.46.4 1.46.3 1.46.2 1.46.1 1.46.0 1.45.0-rc9 1.45.0-rc8 1.45.0-rc7 1.45.0-rc6 1.45.0-rc5 1.45.0-rc4 1.45.0-rc3 1.45.0-rc20 1.45.0-rc2 1.45.0-rc19 1.45.0-rc18 1.45.0-rc17 1.45.0-rc16 1.45.0-rc15 1.45.0-rc14 1.45.0-rc13 1.45.0-rc12 1.45.0-rc11 1.45.0-rc10 1.45.0-rc1 1.45.0-rc0 1.44.13 1.44.12 1.44.11 1.44.10 1.44.9 1.44.8 1.44.7 1.44.6 1.44.5 1.44.4 1.44.3 1.44.2 1.44.1 1.44.0 1.43.12 1.43.11 1.43.10 1.43.9 1.43.8 1.43.7 1.43.6 1.43.5 1.43.4 1.43.3 1.43.2 1.43.1 1.43.0 1.42.0 1.41.12 1.41.11 1.41.10 1.41.9 1.41.8 1.41.7 1.41.6 1.41.5 1.41.4 1.41.3 1.41.2 1.41.1 1.41.0 1.40.0 1.39.7 1.39.6 1.39.5 1.39.4 1.39.3 1.39.2 1.39.1 1.39.0 1.38.0 1.37.2 1.37.1 1.37.0 1.36.22 1.36.21 1.36.19 1.36.18 1.36.17 1.36.16 1.36.15 1.36.14 1.36.13 1.36.12 1.36.11 1.36.10 1.36.9 1.36.8 1.36.7 1.36.6 1.36.5 1.36.4 1.36.3 1.36.2 1.36.0 1.35.1 1.35.0 1.34.9 1.34.8 1.34.7 1.34.6 1.34.5 1.34.4 1.34.3 1.34.2 1.34.1 1.34.0 1.33.0 1.32.2 1.32.1 1.32.0 1.31.1 1.31.0 1.30.2 1.30.1 1.29.0-rc1 1.29.0-rc0 1.28.1 1.28.0 1.27.1 1.27.0 1.26.13 1.26.12 1.26.11 1.26.10 1.26.9 1.26.8 1.26.7 1.26.6 1.26.5 1.26.4 1.26.2 1.26.1 1.26.0 1.25.8 1.25.7 1.25.6 1.25.5 1.25.4 1.25.3 1.25.2 1.25.1 1.25.0 1.24.2 1.24.1 1.24.0 1.23.3 1.23.2 1.23.1 1.23.0 1.22.1 1.22.0 1.20.1 1.20.0 1.19.1 1.19.0 1.18.1 1.18.0 1.17.1 1.17.0 1.16.2 1.15.1 1.15.0 1.14.0 1.13.4 1.13.3 1.13.2 1.13.1 1.13.0 1.12.0 1.11.1 1.11.0 1.10.0 1.9.0 1.8.0 1.7.0 1.6.8 1.6.7 1.6.6 1.6.5 1.6.4 1.6.3 1.6.2 1.6.1 1.6.0 1.5.1 1.5.0 1.4.1 1.4.0 1.3.1 1.3.0 1.2.1 1.2.0 1.1.3 1.1.2 1.1.0 1.0.3 1.0.2 1.0.1 1.0.0 0.13.1 0.13.0 0.12.0 0.10.0 0.9.1 0.9.0 0.8.0 0.7.0 0.6.5 0.6.4 0.6.3 0.6.2 0.6.1 0.6.0 0.5.2 0.5.1 0.5.0 0.4.0 0.3.0 0.2.0 0.1.9 0.1.8 0.1.3 0.1.1 0.1.0

A declarative, extensible framework for building Elixir applications.

Security advisory: This version has known vulnerabilities. View advisories

Current section

Files

Jump to
ash lib ash resource validation.ex
Raw

lib/ash/resource/validation.ex

# SPDX-FileCopyrightText: 2019 ash contributors <https://github.com/ash-project/ash/graphs/contributors>
#
# SPDX-License-Identifier: MIT
defmodule Ash.Resource.Validation do
@moduledoc """
Represents a validation in Ash.
See `Ash.Resource.Validation.Builtins` for a list of builtin validations.
To write your own validation, define a module that implements the `c:init/1` callback
to validate options at compile time, and `c:validate/3` callback to do the validation.
Then, in a resource, you can say:
```
validations do
validate {MyValidation, [foo: :bar]}
end
```
"""
defstruct [
:validation,
:module,
:opts,
:only_when_valid?,
:description,
:message,
:before_action?,
:always_atomic?,
where: [],
on: [],
__spark_metadata__: nil
]
@type t :: %__MODULE__{
validation: {atom(), list(atom())},
module: atom(),
opts: list(atom()),
only_when_valid?: boolean(),
description: String.t() | nil,
where: list({atom(), list(atom())}),
on: list(atom()),
__spark_metadata__: Spark.Dsl.Entity.spark_meta()
}
@type path :: [atom | integer]
@type ref :: {module(), Keyword.t()} | module()
defmodule Context do
@moduledoc """
Context for a validation.
"""
defstruct [:actor, :tenant, :authorize?, :tracer, :message, bulk?: false, source_context: %{}]
@type t :: %__MODULE__{
actor: Ash.Resource.Record.t() | nil,
message: String.t() | nil,
tenant: term(),
source_context: map(),
authorize?: boolean() | nil,
tracer: Ash.Tracer.t() | [Ash.Tracer.t()] | nil,
bulk?: boolean()
}
end
@callback init(opts :: Keyword.t()) :: {:ok, Keyword.t()} | {:error, String.t()}
@callback supports(opts :: Keyword.t()) :: [Ash.Changeset | Ash.Query | Ash.ActionInput]
@callback validate(
changeset_query_or_input :: Ash.Changeset.t() | Ash.ActionInput.t(),
opts :: Keyword.t(),
context :: Context.t()
) ::
:ok | {:error, term}
@callback describe(opts :: Keyword.t()) ::
String.t() | [{:message, String.t()} | {:vars, Keyword.t()}]
@callback atomic(
changeset_query_or_input :: Ash.Changeset.t() | Ash.ActionInput.t(),
opts :: Keyword.t(),
context :: Context.t()
) ::
:ok
| {:atomic, involved_fields :: list(atom) | :*, condition_expr :: Ash.Expr.t(),
error_expr :: Ash.Expr.t()}
| [
{:atomic, involved_fields :: list(atom) | :*, condition_expr :: Ash.Expr.t(),
error_expr :: Ash.Expr.t()}
]
| {:not_atomic, String.t()}
| {:error, term()}
@doc """
Replaces `validate/3` for batch actions, allowing to optimize validations for bulk actions.
Receives all changesets in the batch and returns them with errors added to any that
fail validation. Unlike `validate/3` which returns `:ok | {:error, term}`, this callback
returns the changesets directly (with errors already added via `Ash.Changeset.add_error/2`).
"""
@callback batch_validate(
changesets :: [Ash.Changeset.t()],
opts :: Keyword.t(),
context :: Context.t()
) ::
Enumerable.t(Ash.Changeset.t())
@doc """
Whether or not batch callbacks should be run (if they are defined). Defaults to `true`.
"""
@callback batch_callbacks?(
changesets_or_query :: [Ash.Changeset.t()] | Ash.Query.t(),
opts :: Keyword.t(),
context :: Context.t()
) ::
boolean
@callback atomic?() :: boolean
@callback has_validate?() :: boolean
@callback has_batch_validate?() :: boolean
@optional_callbacks describe: 1, validate: 3, atomic: 3, batch_validate: 3
@validation_type {:spark_function_behaviour, Ash.Resource.Validation,
Ash.Resource.Validation.Builtins, {Ash.Resource.Validation.Function, 2}}
@schema [
validation: [
type: @validation_type,
required: true,
doc:
"The module (or module and opts) that implements the `Ash.Resource.Validation` behaviour. Also accepts a function that receives the changeset and its context."
],
where: [
type: {:wrap_list, @validation_type},
required: false,
default: [],
doc: """
Validations that should pass in order for this validation to apply. Any of these validations failing will result in this validation being ignored.
"""
],
on: [
type: {:wrap_list, {:in, [:create, :update, :destroy, :read, :action]}},
default: [:create, :update],
doc: """
The action types the validation should run on. Many validations don't make sense in the context of destroy, read, or generic actions, so by default they are not included.
"""
],
only_when_valid?: [
type: :boolean,
default: false,
doc:
"If the validation should only run on valid changesets. Useful for expensive validations or validations that depend on valid data."
],
message: [
type: :string,
doc: "If provided, overrides any message set by the validation error"
],
description: [
type: :string,
doc: "An optional description for the validation"
],
before_action?: [
type: :boolean,
default: false,
doc: "If set to `true`, the validation will be run in a before_action hook"
],
always_atomic?: [
type: :boolean,
default: false,
doc:
"By default, validations are only run atomically if all changes will be run atomically or if there is no `validate/3` callback defined. Set this to `true` to run it atomically always."
]
]
@action_schema Keyword.delete(@schema, :on)
defmacro __using__(_) do
quote do
@behaviour Ash.Resource.Validation
@before_compile Ash.Resource.Validation
import Ash.Expr
require Ash.Query
@impl true
def init(opts), do: {:ok, opts}
@impl true
def supports(_opts), do: [Ash.Changeset]
@impl true
def batch_callbacks?(_, _, _), do: true
defp with_description(keyword, opts) do
if Kernel.function_exported?(__MODULE__, :describe, 1) do
keyword ++ Ash.Resource.Validation.describe(__MODULE__, opts)
else
keyword
end
end
defoverridable init: 1, supports: 1, batch_callbacks?: 3
end
end
defmacro __before_compile__(_) do
quote do
if Module.defines?(__MODULE__, {:validate, 2}, :def) or
Module.defines?(__MODULE__, {:validate, 3}, :def) do
@impl true
def has_validate?, do: true
else
@impl true
def has_validate?, do: false
end
if Module.defines?(__MODULE__, {:batch_validate, 3}, :def) do
@impl true
def has_batch_validate?, do: true
else
@impl true
def has_batch_validate?, do: false
end
if Module.defines?(__MODULE__, {:atomic, 3}, :def) do
if !Module.defines?(__MODULE__, {:atomic?, 0}, :def) do
@impl true
def atomic?, do: true
end
else
if !Module.defines?(__MODULE__, {:atomic?, 0}, :def) do
@impl true
def atomic?, do: false
end
@impl true
def atomic(_changeset, _opts, _context),
do: {:not_atomic, "#{inspect(__MODULE__)} does not implement `atomic/3`"}
end
end
end
@doc false
def transform(%{validation: {module, opts}} = validation) do
opts =
Enum.map(opts, fn
{key, %Regex{} = value} when module == Ash.Resource.Validation.Match ->
source = Regex.source(value)
opts = Regex.opts(value)
{key, {Spark.Regex, :cache, [source, opts]}}
{key, value} ->
{key, value}
end)
{:ok,
%{
validation
| validation: {module, opts},
module: module,
opts: opts
}}
end
@doc false
@spec validate(
module(),
Ash.Changeset.t() | Ash.Query.t() | Ash.ActionInput.t(),
Keyword.t(),
Context.t()
) :: :ok | {:error, term()}
def validate(module, changeset_query_or_input, opts, context) do
Ash.BehaviourHelpers.call_and_validate_return(
module,
:validate,
[changeset_query_or_input, opts, context],
[:ok, {:error, :_}],
behaviour: __MODULE__,
callback_name: "validate/3"
)
end
@doc false
@spec describe(module(), Keyword.t() | map()) ::
String.t() | [{:message, String.t()} | {:vars, Keyword.t()}]
def describe(module, opts) do
result = apply(module, :describe, [opts])
if is_binary(result) or (is_list(result) and Keyword.keyword?(result)) do
result
else
raise Ash.Error.Framework.InvalidReturnType,
message: """
Invalid value returned from #{inspect(module)}.describe/1.
The callback #{inspect(__MODULE__)}.describe/1 expects a String.t() or a keyword list of :message/:vars.
"""
end
end
@doc false
@spec init(module(), Keyword.t()) :: {:ok, Keyword.t()} | {:error, String.t()}
def init(module, opts) do
Ash.BehaviourHelpers.call_and_validate_return(
module,
:init,
[opts],
[{:ok, :_}, {:error, :_}],
behaviour: __MODULE__,
callback_name: "init/1"
)
end
@doc false
@spec atomic(
module(),
Ash.Changeset.t() | Ash.ActionInput.t(),
Keyword.t(),
Context.t()
) ::
:ok
| {:atomic, list(atom()) | :*, Ash.Expr.t(), Ash.Expr.t()}
| [{:atomic, list(atom()) | :*, Ash.Expr.t(), Ash.Expr.t()}]
| {:not_atomic, String.t()}
| {:error, term()}
def atomic(module, changeset_query_or_input, opts, context) do
result = apply(module, :atomic, [changeset_query_or_input, opts, context])
if valid_atomic_result?(result) do
result
else
raise Ash.Error.Framework.InvalidReturnType,
message: """
Invalid value returned from #{inspect(module)}.atomic/3.
The callback #{inspect(__MODULE__)}.atomic/3 expects one of the following return types:
:ok
{:atomic, involved_fields, condition_expr, error_expr}
[list of {:atomic, involved_fields, condition_expr, error_expr}]
{:not_atomic, String.t()}
{:error, term()}
"""
end
end
defp valid_atomic_result?(:ok), do: true
defp valid_atomic_result?({:atomic, _, _, _}), do: true
defp valid_atomic_result?({:not_atomic, _}), do: true
defp valid_atomic_result?({:error, _}), do: true
defp valid_atomic_result?(list) when is_list(list) do
Enum.all?(list, &match?({:atomic, _, _, _}, &1))
end
defp valid_atomic_result?(_), do: false
def opt_schema, do: @schema
def action_schema, do: @action_schema
def validation_type, do: @validation_type
@doc false
def maybe_redact(subject, field, value) do
if should_redact?(subject, field) do
Ash.Helpers.redact(value)
else
value
end
end
@doc false
def should_redact?(subject, field) do
Application.get_env(:ash, :redact_sensitive_values_in_errors?, false) &&
sensitive?(subject, field)
end
@doc false
def sensitive?(subject, field) do
argument_sensitive? =
(subject.action.arguments || [])
|> Enum.find(&(&1.name == field))
|> case do
%{sensitive?: true} -> true
_ -> false
end
attribute_sensitive? =
case Ash.Resource.Info.attribute(subject.resource, field) do
%{sensitive?: true} -> true
_ -> false
end
argument_sensitive? or attribute_sensitive?
end
end