From 10e372244671d5798b7b0278ac014d9f0cc0b3f3 Mon Sep 17 00:00:00 2001 From: Lukas Ender Date: Fri, 12 Apr 2024 16:26:46 +0200 Subject: [PATCH] fix: `code_interface` actions accept `@context` (#1016) --- lib/ash/changeset/changeset.ex | 4 ++ lib/ash/code_interface.ex | 10 ++-- test/code_interface_test.exs | 86 ++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 5 deletions(-) diff --git a/lib/ash/changeset/changeset.ex b/lib/ash/changeset/changeset.ex index cdd9ae072..c379331b9 100644 --- a/lib/ash/changeset/changeset.ex +++ b/lib/ash/changeset/changeset.ex @@ -1040,6 +1040,10 @@ defmodule Ash.Changeset do type: {:list, {:or, [:atom, :string]}}, doc: "A list of inputs that, if provided, will be ignored if they are not recognized by the action." + ], + context: [ + type: :map, + doc: "Context to set on the query, changeset, or input" ] ] diff --git a/lib/ash/code_interface.ex b/lib/ash/code_interface.ex index 179a4ed9c..4784ed675 100644 --- a/lib/ash/code_interface.ex +++ b/lib/ash/code_interface.ex @@ -564,7 +564,7 @@ defmodule Ash.CodeInterface do resolve_subject = quote do {query_opts, opts} = - Keyword.split(opts, [:query, :actor, :tenant, :authorize?, :tracer]) + Keyword.split(opts, [:query, :actor, :tenant, :authorize?, :tracer, :context]) {query, query_opts} = Keyword.pop(query_opts, :query) @@ -658,7 +658,7 @@ defmodule Ash.CodeInterface do {changeset, opts} = Keyword.pop(opts, :changeset) {changeset_opts, opts} = - Keyword.split(opts, [:changeset, :actor, :tenant, :authorize?, :tracer]) + Keyword.split(opts, [:actor, :tenant, :authorize?, :tracer, :context]) changeset_opts = Keyword.put(changeset_opts, :domain, unquote(domain)) @@ -699,7 +699,7 @@ defmodule Ash.CodeInterface do |> Enum.concat(changeset_opts) Ash.bulk_create( - Stream.map(inputs, &Map.merge(&1, params)), + inputs, unquote(resource), unquote(action.name), bulk_opts @@ -741,7 +741,7 @@ defmodule Ash.CodeInterface do resolve_subject = quote do {changeset_opts, opts} = - Keyword.split(opts, [:actor, :tenant, :authorize?, :tracer]) + Keyword.split(opts, [:actor, :tenant, :authorize?, :tracer, :context]) changeset_opts = Keyword.put(changeset_opts, :domain, unquote(domain)) @@ -901,7 +901,7 @@ defmodule Ash.CodeInterface do resolve_subject = quote do {changeset_opts, opts} = - Keyword.split(opts, [:actor, :tenant, :authorize?, :tracer]) + Keyword.split(opts, [:actor, :tenant, :authorize?, :tracer, :context]) changeset_opts = Keyword.put(changeset_opts, :domain, unquote(domain)) diff --git a/test/code_interface_test.exs b/test/code_interface_test.exs index 822de3d6b..c0c67359a 100644 --- a/test/code_interface_test.exs +++ b/test/code_interface_test.exs @@ -20,7 +20,9 @@ defmodule Ash.Test.CodeInterfaceTest do define :create, args: [{:optional, :first_name}] define :hello, args: [:name] + define :bulk_create, action: :create define :update, action: :update + define :destroy, action: :destroy define_calculation(:full_name, args: [:first_name, :last_name]) @@ -43,6 +45,8 @@ defmodule Ash.Test.CodeInterfaceTest do update :update + destroy :destroy + read :by_id do argument :id, :uuid, allow_nil?: false @@ -91,6 +95,8 @@ defmodule Ash.Test.CodeInterfaceTest do end end + @context %{test: "value"} + describe "generic actions" do test "can be invoked" do assert "Hello fred" == User.hello!("fred") @@ -148,6 +154,14 @@ defmodule Ash.Test.CodeInterfaceTest do User.get_user_safely!(Ash.UUID.generate(), not_found_error?: true) end end + + test "can take a @context options" do + assert {:ok, nil} == + User.get_user(Ash.UUID.generate(), not_found_error?: false, context: @context) + + assert nil == + User.get_user!(Ash.UUID.generate(), not_found_error?: false, context: @context) + end end describe "create actions" do @@ -165,6 +179,78 @@ defmodule Ash.Test.CodeInterfaceTest do assert User.can_create?(nil) assert User.can_create?(nil, "bob") end + + test "can take a @context options" do + assert {:ok, _record} = User.create("bob", context: @context) + assert _record = User.create!("bob", context: @context) + end + + test "bulk_create can take a @context options" do + assert %Ash.BulkResult{status: :success} = + User.bulk_create([%{first_name: "bob"}, %{first_name: "other_bob"}], + context: @context + ) + end + end + + describe "update actions" do + test "have a helper methods to produce changeset" do + bob = User.create!("bob", context: @context) + + assert %Ash.Changeset{action: %{name: :update}, attributes: %{first_name: "fred"}} = + User.changeset_to_update(bob, %{first_name: "fred"}) + end + + test "can take a @context options" do + bob = User.create!("bob", context: @context) + + assert {:ok, _record} = User.update(bob, %{first_name: "bob_updated"}, context: @context) + assert _record = User.update!(bob, %{first_name: "bob_updated"}, context: @context) + end + + test "bulk update can take a @context options" do + bob1 = User.create!("bob", context: @context) + bob2 = User.create!("bob", context: @context) + + assert %Ash.BulkResult{status: :success} = + User.update([bob1, bob2], %{first_name: "other_bob"}, context: @context) + + assert result = + User.update!([bob1, bob2], %{first_name: "different_bob"}, + context: @context, + bulk_options: [return_records?: true] + ) + + Enum.map(result.records, &assert(&1.first_name == "different_bob")) + end + end + + describe "destroy actions" do + test "have a helper methods to produce changeset" do + bob = User.create!("bob", context: @context) + + assert %Ash.Changeset{action: %{name: :destroy}} = User.changeset_to_destroy(bob) + end + + test "can take a @context options" do + bob1 = User.create!("bob", context: @context) + bob2 = User.create!("bob", context: @context) + + assert :ok = User.destroy(bob1, context: @context) + assert :ok = User.destroy!(bob2, context: @context) + end + + test "bulk destroy can take a @context options" do + bob1 = User.create!("bob", context: @context) + bob2 = User.create!("bob", context: @context) + + assert %Ash.BulkResult{status: :success} = User.destroy([bob1, bob2], context: @context) + + bob3 = User.create!("bob", context: @context) + bob4 = User.create!("bob", context: @context) + + assert %Ash.BulkResult{status: :success} = User.destroy!([bob3, bob4], context: @context) + end end describe "calculations" do