mirror of
https://github.com/ash-project/ash.git
synced 2024-09-19 13:03:02 +12:00
fix: allow api.load/2 to load calculations
improvement: add `allow_nil_input` to create actions for api layers improvement: add `load/1` builtin change feat: change `get?: true` interface functions to raise on `nil`
This commit is contained in:
parent
00608e00d7
commit
e353ea49c3
8 changed files with 98 additions and 8 deletions
|
@ -4,6 +4,7 @@ locals_without_parens = [
|
|||
accept: 1,
|
||||
action: 1,
|
||||
allow_nil?: 1,
|
||||
allow_nil_input: 1,
|
||||
always_select?: 1,
|
||||
args: 1,
|
||||
argument: 2,
|
||||
|
|
|
@ -279,7 +279,9 @@ defmodule Ash.Actions.Read do
|
|||
initial_query
|
||||
) do
|
||||
if params[:initial_data] do
|
||||
List.wrap(params[:initial_data])
|
||||
Request.resolve([], fn _ ->
|
||||
add_calculation_values(initial_query, params[:initial_data], initial_query.calculations)
|
||||
end)
|
||||
else
|
||||
relationship_filter_paths =
|
||||
Enum.map(filter_requests, fn request ->
|
||||
|
|
|
@ -58,7 +58,15 @@ defmodule Ash.Api.Interface do
|
|||
)
|
||||
|
||||
if unquote(interface.get?) do
|
||||
unquote(api).read_one(query, Keyword.drop(opts, [:query, :tenant]))
|
||||
query
|
||||
|> unquote(api).read_one(Keyword.drop(opts, [:query, :tenant]))
|
||||
|> case do
|
||||
{:ok, nil} ->
|
||||
{:error, Ash.Error.Query.NotFound.exception(resource: query.resource)}
|
||||
|
||||
{:ok, result} ->
|
||||
{:ok, result}
|
||||
end
|
||||
else
|
||||
unquote(api).read(query, Keyword.drop(opts, [:query, :tenant]))
|
||||
end
|
||||
|
@ -92,7 +100,15 @@ defmodule Ash.Api.Interface do
|
|||
)
|
||||
|
||||
if unquote(interface.get?) do
|
||||
unquote(api).read_one!(query, Keyword.drop(opts, [:query, :tenant]))
|
||||
query
|
||||
|> unquote(api).read_one!(Keyword.drop(opts, [:query, :tenant]))
|
||||
|> case do
|
||||
{:ok, nil} ->
|
||||
{:error, Ash.Error.Query.NotFound.exception(resource: query.resource)}
|
||||
|
||||
{:ok, result} ->
|
||||
{:ok, result}
|
||||
end
|
||||
else
|
||||
unquote(api).read!(query, Keyword.drop(opts, [:query, :tenant]))
|
||||
end
|
||||
|
|
|
@ -7,6 +7,7 @@ defmodule Ash.Resource.Actions.Create do
|
|||
accept: nil,
|
||||
arguments: [],
|
||||
changes: [],
|
||||
allow_nil: [],
|
||||
reject: [],
|
||||
type: :create
|
||||
]
|
||||
|
@ -15,6 +16,7 @@ defmodule Ash.Resource.Actions.Create do
|
|||
type: :create,
|
||||
name: atom,
|
||||
accept: [atom],
|
||||
allow_nil: [atom],
|
||||
arguments: [Ash.Resource.Actions.Argument.t()],
|
||||
primary?: boolean,
|
||||
description: String.t()
|
||||
|
@ -25,7 +27,21 @@ defmodule Ash.Resource.Actions.Create do
|
|||
@global_opts shared_options()
|
||||
@create_update_opts create_update_opts()
|
||||
|
||||
@opt_schema []
|
||||
@opt_schema [
|
||||
allow_nil_input: [
|
||||
type: {:list, :atom},
|
||||
doc: """
|
||||
A list of attributes that would normally be required, but should not be for this action.
|
||||
|
||||
This exists because extensions like ash_graphql and ash_json_api will add non-null validations to their input for any attribute
|
||||
that is accepted by an action that has `allow_nil?: false`. This tells those extensions that some `change` on the resource might
|
||||
set that attribute, and so they should not require it at the API layer.
|
||||
|
||||
Ash core doesn't actually use the values in this list, because it does its `nil` validation *after* running all resource
|
||||
changes. If the value is still `nil` by the time Ash would submit to the data layer, then an error is returned.
|
||||
"""
|
||||
]
|
||||
]
|
||||
|> Ash.OptionsHelpers.merge_schemas(
|
||||
@global_opts,
|
||||
"Action Options"
|
||||
|
|
|
@ -21,15 +21,14 @@ defmodule Ash.Resource.Actions.SharedOptions do
|
|||
@create_update_opts [
|
||||
accept: [
|
||||
type: {:custom, Ash.OptionsHelpers, :list_of_atoms, []},
|
||||
doc:
|
||||
"The list of attributes and relationships to accept. Defaults to all attributes on the resource"
|
||||
doc: "The list of attributes to accept. Defaults to all attributes on the resource"
|
||||
],
|
||||
reject: [
|
||||
type: {:custom, Ash.OptionsHelpers, :list_of_atoms, []},
|
||||
doc: """
|
||||
A list of attributes and relationships not to accept. This is useful if you want to say 'accept all but x'
|
||||
A list of attributes not to accept. This is useful if you want to say 'accept all but x'
|
||||
|
||||
If this is specified along with `accept`, then everything in the `accept` list minuse any matches in the
|
||||
If this is specified along with `accept`, then everything in the `accept` list minus any matches in the
|
||||
`reject` list will be accepted.
|
||||
"""
|
||||
]
|
||||
|
|
|
@ -52,4 +52,11 @@ defmodule Ash.Resource.Change.Builtins do
|
|||
def set_context(context) do
|
||||
{Ash.Resource.Change.SetContext, context: context}
|
||||
end
|
||||
|
||||
@doc """
|
||||
Passes the provided value into `changeset.api.load()`, after the action has completed.
|
||||
"""
|
||||
def load(value) do
|
||||
{Ash.Resource.Change.Load, target: value}
|
||||
end
|
||||
end
|
||||
|
|
11
lib/ash/resource/change/load.ex
Normal file
11
lib/ash/resource/change/load.ex
Normal file
|
@ -0,0 +1,11 @@
|
|||
defmodule Ash.Resource.Change.Load do
|
||||
@moduledoc false
|
||||
use Ash.Resource.Change
|
||||
alias Ash.Changeset
|
||||
|
||||
def change(changeset, opts, _) do
|
||||
Changeset.after_action(changeset, fn changeset, result ->
|
||||
changeset.api.load(result, opts[:target])
|
||||
end)
|
||||
end
|
||||
end
|
38
test/resource/changes/load_test.exs
Normal file
38
test/resource/changes/load_test.exs
Normal file
|
@ -0,0 +1,38 @@
|
|||
defmodule Ash.Test.Resource.Changes.LoadTest do
|
||||
@moduledoc false
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
defmodule Post do
|
||||
use Ash.Resource,
|
||||
data_layer: Ash.DataLayer.Ets
|
||||
|
||||
attributes do
|
||||
uuid_primary_key :id
|
||||
attribute :text, :string
|
||||
attribute :second_text, :string
|
||||
end
|
||||
|
||||
actions do
|
||||
create :create do
|
||||
change load(:full_text)
|
||||
end
|
||||
end
|
||||
|
||||
calculations do
|
||||
calculate :full_text, :string, concat([:text, :second_text])
|
||||
end
|
||||
end
|
||||
|
||||
defmodule Api do
|
||||
use Ash.Api
|
||||
|
||||
resources do
|
||||
resource Post
|
||||
end
|
||||
end
|
||||
|
||||
test "you can use it to load on create" do
|
||||
assert Api.create!(Ash.Changeset.for_create(Post, :create, text: "foo", second_text: "bar")).full_text ==
|
||||
"foobar"
|
||||
end
|
||||
end
|
Loading…
Reference in a new issue