Packages

ash

1.0.2
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 api api.ex
Raw

lib/ash/api/api.ex

defmodule Ash.Api do
@moduledoc """
An Api allows you to interact with your resources, and holds non-resource-specific configuration.
Your Api can also house config that is not resource specific.
Defining a resource won't do much for you. Once you have some resources defined,
you include them in an Api like so:
```elixir
defmodule MyApp.Api do
use Ash.Api
resources do
resource OneResource
resource SecondResource
end
```
Then you can interact through that Api with the actions that those resources expose.
For example: `MyApp.Api.create(OneResource, %{attributes: %{name: "thing"}})`, or
`MyApp.Api.read(query)`. Corresponding actions must
be defined in your resources in order to call them through the Api.
"""
import Ash.OptionsHelpers, only: [merge_schemas: 3]
alias Ash.Actions.{Create, Destroy, Read, SideLoad, Update}
alias Ash.Error.NoSuchResource
@global_opts [
verbose?: [
type: :boolean,
default: false,
doc: "Log engine operations (very verbose?)"
],
action: [
type: :any,
doc: "The action to use, either an Action struct or the name of the action"
],
authorize?: [
type: :boolean,
default: false,
doc:
"If an actor is provided, authorization happens automatically. If not, this flag can be used to authorize with no user."
],
actor: [
type: :any,
doc:
"If an actor is provided, it will be used in conjunction with the authorizers of a resource to authorize access"
]
]
@read_opts_schema merge_schemas([], @global_opts, "Global Options")
@doc false
def read_opts_schema, do: @read_opts_schema
@side_load_opts_schema merge_schemas([], @global_opts, "Global Options")
@get_opts_schema [
side_load: [
type: :any,
doc:
"Side loads to include in the query, same as you would pass to `Ash.Query.side_load/2`"
]
]
|> merge_schemas(@global_opts, "Global Options")
@shared_create_and_update_opts_schema [
attributes: [
type: {:custom, Ash.OptionsHelpers, :map, []},
default: %{},
doc: "Changes to be applied to attribute values"
],
relationships: [
type: {:custom, Ash.OptionsHelpers, :map, []},
default: %{},
doc: "Changes to be applied to relationship values"
],
side_load: [
type: :any,
doc:
"Side loads to include in the query, same as you would pass to `Ash.Query.side_load/2`"
]
]
|> merge_schemas(@global_opts, "Global Options")
@create_opts_schema [
upsert?: [
type: :boolean,
default: false,
doc:
"If a conflict is found based on the primary key, the record is updated in the database (requires upsert support)"
]
]
|> merge_schemas(@global_opts, "Global Options")
|> merge_schemas(
@shared_create_and_update_opts_schema,
"Shared Create/Edit Options"
)
@update_opts_schema []
|> merge_schemas(@global_opts, "Global Options")
|> merge_schemas(
@shared_create_and_update_opts_schema,
"Shared Create/Edit Options"
)
@destroy_opts_schema merge_schemas([], @global_opts, "Global Opts")
@doc """
Get a record by a primary key
#{NimbleOptions.docs(@get_opts_schema)}
"""
@callback get!(resource :: Ash.resource(), id_or_filter :: term(), params :: Keyword.t()) ::
Ash.record() | no_return
@doc """
Get a record by a primary key
#{NimbleOptions.docs(@get_opts_schema)}
"""
@callback get(resource :: Ash.resource(), id_or_filter :: term(), params :: Keyword.t()) ::
{:ok, Ash.record()} | {:error, Ash.error()}
@doc """
Run a query on a resource
#{NimbleOptions.docs(@read_opts_schema)}
"""
@callback read!(Ash.query(), params :: Keyword.t()) ::
list(Ash.resource()) | no_return
@doc """
Run a query on a resource
#{NimbleOptions.docs(@read_opts_schema)}
"""
@callback read(Ash.query(), params :: Keyword.t()) ::
{:ok, list(Ash.resource())} | {:error, Ash.error()}
@doc """
Side load on already fetched records
#{NimbleOptions.docs(@side_load_opts_schema)}
"""
@callback side_load!(resource :: Ash.resource(), params :: Keyword.t()) ::
list(Ash.resource()) | no_return
@doc """
Side load on already fetched records
#{NimbleOptions.docs(@side_load_opts_schema)}
"""
@callback side_load(resource :: Ash.resource(), params :: Keyword.t()) ::
{:ok, list(Ash.resource())} | {:error, Ash.error()}
@doc """
Create a record
#{NimbleOptions.docs(@create_opts_schema)}
"""
@callback create!(resource :: Ash.resource(), params :: Keyword.t()) ::
Ash.record() | no_return
@doc """
Create a record
#{NimbleOptions.docs(@create_opts_schema)}
"""
@callback create(resource :: Ash.resource(), params :: Keyword.t()) ::
{:ok, Ash.record()} | {:error, Ash.error()}
@doc """
Update a record
#{NimbleOptions.docs(@update_opts_schema)}
"""
@callback update!(record :: Ash.record(), params :: Keyword.t()) ::
Ash.record() | no_return
@doc """
Update a record
#{NimbleOptions.docs(@update_opts_schema)}
"""
@callback update(record :: Ash.record(), params :: Keyword.t()) ::
{:ok, Ash.record()} | {:error, Ash.error()}
@doc """
Destroy a record
#{NimbleOptions.docs(@destroy_opts_schema)}
"""
@callback destroy!(record :: Ash.record(), params :: Keyword.t()) :: :ok | no_return
@doc """
Destroy a record
#{NimbleOptions.docs(@destroy_opts_schema)}
"""
@callback destroy(record :: Ash.record(), params :: Keyword.t()) ::
:ok | {:error, Ash.error()}
@doc """
Refetches a record from the database, raising on error.
See `reload/1`.
"""
@callback reload!(record :: Ash.record(), params :: Keyword.t()) :: Ash.record() | no_return
@doc """
Refetches a record from the database
"""
@callback reload(record :: Ash.record()) :: {:ok, Ash.record()} | {:error, Ash.error()}
alias Ash.Dsl.Extension
defmacro __using__(opts) do
extensions = [Ash.Api.Dsl | opts[:extensions] || []]
body =
quote do
@before_compile Ash.Api
@behaviour Ash.Api
end
preparations = Extension.prepare(extensions)
[body | preparations]
end
defmacro __before_compile__(_env) do
quote generated: true do
alias Ash.Dsl.Extension
Extension.set_state(false)
Extension.set_state(true)
def raw_dsl do
@ash_dsl_config
end
use Supervisor
def start_link(args) do
Supervisor.start_link(__MODULE__, args, name: __MODULE__)
end
def init(_init_arg) do
Extension.set_state(true)
children =
__MODULE__
|> Ash.Api.resources()
|> Enum.map(fn resource ->
{resource, [api: __MODULE__]}
end)
Supervisor.init(children, strategy: :one_for_one)
end
use Ash.Api.Interface
end
end
alias Ash.Dsl.Extension
def resource(api, resource) do
api
|> resources()
|> Enum.find(&(&1 == resource))
|> case do
nil -> {:error, NoSuchResource.exception(resource: resource)}
resource -> {:ok, resource}
end
end
@spec resources(Ash.api()) :: [Ash.resource()]
def resources(api) do
api
|> Extension.get_entities([:resources])
|> Enum.map(& &1.resource)
end
@doc false
@spec get!(Ash.api(), Ash.resource(), term(), Keyword.t()) :: Ash.record() | no_return
def get!(api, resource, id, opts \\ []) do
opts = NimbleOptions.validate!(opts, @get_opts_schema)
api
|> get(resource, id, opts)
|> unwrap_or_raise!()
end
@doc false
@spec get(Ash.api(), Ash.resource(), term(), Keyword.t()) ::
{:ok, Ash.record()} | {:error, Ash.error()}
def get(api, resource, id, opts) do
with {:ok, opts} <- NimbleOptions.validate(opts, @get_opts_schema),
{:ok, resource} <- Ash.Api.resource(api, resource),
{:pkey, primary_key} when primary_key != [] <- {:pkey, Ash.primary_key(resource)},
{:ok, filter} <- get_filter(primary_key, id) do
resource
|> api.query()
|> Ash.Query.filter(filter)
|> Ash.Query.side_load(opts[:side_load] || [])
|> api.read(Keyword.delete(opts, :side_load))
|> case do
{:ok, [single_result]} ->
{:ok, single_result}
{:ok, []} ->
{:ok, nil}
{:error, error} ->
{:error, error}
{:ok, results} when is_list(results) ->
{:error, :too_many_results}
end
else
{:error, error} ->
{:error, error}
{:pkey, _} ->
{:error, "Resource has no primary key"}
end
end
defp get_filter(primary_key, id) do
case {primary_key, id} do
{[field], [{field, value}]} ->
{:ok, [{field, value}]}
{[field], value} ->
{:ok, [{field, value}]}
{fields, value} ->
if Keyword.keyword?(value) and Enum.sort(Keyword.keys(value)) == Enum.sort(fields) do
{:ok, value}
else
{:error, "invalid primary key provided to `get/3`"}
end
end
end
@doc false
@spec side_load!(
Ash.api(),
Ash.record() | list(Ash.record()),
Ash.query() | list(atom | {atom, list()}),
Keyword.t()
) ::
list(Ash.record()) | Ash.record() | no_return
def side_load!(api, data, query, opts \\ []) do
opts = NimbleOptions.validate!(opts, @side_load_opts_schema)
api
|> side_load(data, query, opts)
|> unwrap_or_raise!()
end
@doc false
@spec side_load(Ash.api(), Ash.query(), Keyword.t()) ::
{:ok, list(Ash.resource())} | {:error, Ash.error()}
def side_load(api, data, query, opts \\ [])
def side_load(_, [], _, _), do: {:ok, []}
def side_load(_, nil, _, _), do: {:ok, nil}
def side_load(api, data, query, opts) when not is_list(data) do
api
|> side_load(List.wrap(data), query, opts)
|> case do
{:ok, [data]} -> {:ok, data}
{:error, error} -> {:error, error}
end
end
def side_load(api, [%resource{} | _] = data, query, opts) do
query =
case query do
%Ash.Query{} = query ->
query
keyword ->
resource
|> api.query()
|> Ash.Query.side_load(keyword)
end
with %{valid?: true} <- query,
{:ok, opts} <- NimbleOptions.validate(opts, @side_load_opts_schema) do
SideLoad.side_load(data, query, opts)
else
{:error, error} ->
{:error, error}
%{errors: errors} ->
{:error, errors}
end
end
@doc false
@spec read!(Ash.api(), Ash.query(), Keyword.t()) ::
list(Ash.record()) | no_return
def read!(api, query, opts \\ []) do
opts = NimbleOptions.validate!(opts, @read_opts_schema)
api
|> read(query, opts)
|> unwrap_or_raise!()
end
@doc false
@spec read(Ash.api(), Ash.query(), Keyword.t()) ::
{:ok, list(Ash.resource())} | {:error, Ash.error()}
def read(_api, query, opts \\ []) do
with {:ok, opts} <- NimbleOptions.validate(opts, @read_opts_schema),
{:ok, action} <- get_action(query.resource, opts, :read) do
Read.run(query, action, opts)
else
{:error, error} ->
{:error, error}
end
end
@doc false
@spec create!(Ash.api(), Ash.resource(), Keyword.t()) ::
Ash.record() | {:error, Ash.error()}
def create!(api, resource, opts) do
opts = NimbleOptions.validate!(opts, @create_opts_schema)
api
|> create(resource, opts)
|> unwrap_or_raise!()
end
@doc false
@spec create(Ash.api(), Ash.resource(), Keyword.t()) ::
{:ok, Ash.resource()} | {:error, Ash.error()}
def create(api, resource, opts) do
with {:ok, opts} <- NimbleOptions.validate(opts, @create_opts_schema),
{:ok, resource} <- Ash.Api.resource(api, resource),
{:ok, action} <- get_action(resource, opts, :create) do
Create.run(api, resource, action, opts)
end
end
@doc false
@spec update!(Ash.api(), Ash.record(), Keyword.t()) :: Ash.resource() | no_return()
def update!(api, record, opts) do
opts = NimbleOptions.validate!(opts, @update_opts_schema)
api
|> update(record, opts)
|> unwrap_or_raise!()
end
@doc false
@spec update(Ash.api(), Ash.record(), Keyword.t()) ::
{:ok, Ash.record()} | {:error, Ash.error()}
def update(api, %resource{} = record, opts) do
with {:ok, opts} <- NimbleOptions.validate(opts, @update_opts_schema),
{:ok, resource} <- Ash.Api.resource(api, resource),
{:ok, action} <- get_action(resource, opts, :update) do
Update.run(api, record, action, opts)
end
end
@doc false
@spec destroy!(Ash.api(), Ash.record(), Keyword.t()) :: :ok | no_return
def destroy!(api, record, opts) do
opts = NimbleOptions.validate!(opts, @destroy_opts_schema)
api
|> destroy(record, opts)
|> unwrap_or_raise!()
end
@doc false
@spec destroy(Ash.api(), Ash.record(), Keyword.t()) :: :ok | {:error, Ash.error()}
def destroy(api, %resource{} = record, opts) do
with {:ok, opts} <- NimbleOptions.validate(opts, @destroy_opts_schema),
{:ok, resource} <- Ash.Api.resource(api, resource),
{:ok, action} <- get_action(resource, opts, :destroy) do
Destroy.run(api, record, action, opts)
end
end
defp get_action(resource, params, type) do
case params[:action] || Ash.primary_action(resource, type) do
nil ->
{:error, "no action provided, and no primary action found for #{to_string(type)}"}
action ->
{:ok, action}
end
end
defp unwrap_or_raise!(:ok), do: :ok
defp unwrap_or_raise!({:ok, result}), do: result
defp unwrap_or_raise!({:error, error}) do
exception = Ash.Error.to_ash_error(error)
raise exception
end
end