Skip to content

Commit

Permalink
feat: allow passing of arguments to related resources (#93)
Browse files Browse the repository at this point in the history
  • Loading branch information
barnabasJ authored Jan 21, 2025
1 parent ab4ddfa commit 09e77eb
Show file tree
Hide file tree
Showing 14 changed files with 370 additions and 8 deletions.
1 change: 1 addition & 0 deletions .formatter.exs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
spark_locals_without_parens = [
archive_related: 1,
archive_related_arguments: 1,
attribute: 1,
attribute_type: 1,
base_filter?: 1,
Expand Down
1 change: 1 addition & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ if Mix.env() == :test do

config :ash_archival, AshArchival.TestRepo,
username: "postgres",
password: "postgres",
database: "ash_archival_test",
hostname: "localhost",
pool: Ecto.Adapters.SQL.Sandbox
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ A section for configuring how archival is configured for a resource.
| [`exclude_upsert_actions`](#archive-exclude_upsert_actions){: #archive-exclude_upsert_actions } | `atom \| list(atom)` | `[]` | This option is deprecated as it no longer has any effect. Upserts are handled according to the upsert identity. See the upserts guide for more. |
| [`exclude_destroy_actions`](#archive-exclude_destroy_actions){: #archive-exclude_destroy_actions } | `atom \| list(atom)` | `[]` | A destroy action or actions that should *not* archive, but instead be left alone. This allows for having a destroy *or* archive pattern. |
| [`archive_related`](#archive-archive_related){: #archive-archive_related } | `list(atom)` | `[]` | A list of relationships that should have all related items archived when this is archived. Notifications are not sent for this operation. |
| [`archive_related_arguments`](#archive-archive_related_arguments){: #archive-archive_related_arguments } | `(any, any -> any) \| module` | | A function to allow passing along some of the arguments to related resources when archiving them. |



Expand Down
10 changes: 10 additions & 0 deletions lib/ash_archival/resource/archive_related_arguments.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
defmodule AshArchival.ArchiveRelatedArguments do
@moduledoc """
The behaviour for specifiying arguments for related resources
"""
@callback arguments(
original_arguments :: map(),
relationship :: atom(),
opts :: Keyword.t()
) :: map()
end
15 changes: 15 additions & 0 deletions lib/ash_archival/resource/archive_related_arguments/function.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
defmodule AshArchival.ArchiveRelatedArguments.Function do
@moduledoc false

@behaviour AshArchival.ArchiveRelatedArguments

@impl true
def arguments(arguments, relationship, [{:fun, {m, f, a}}]) do
apply(m, f, [arguments, relationship, a])
end

@impl true
def arguments(arguments, relationship, [{:fun, fun}]) do
fun.(arguments, relationship)
end
end
31 changes: 25 additions & 6 deletions lib/ash_archival/resource/changes/archive_related.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@ defmodule AshArchival.Resource.Changes.ArchiveRelated do

def change(changeset, _, context) do
Ash.Changeset.after_action(changeset, fn changeset, result ->
archive_related([result], changeset.resource, changeset.domain, context)
archive_related(
[result],
changeset.resource,
changeset.domain,
changeset.arguments,
context
)

{:ok, result}
end)
end
Expand All @@ -15,7 +22,7 @@ defmodule AshArchival.Resource.Changes.ArchiveRelated do
end

def after_atomic(changeset, _, record, context) do
archive_related([record], changeset.resource, changeset.domain, context)
archive_related([record], changeset.resource, changeset.domain, changeset.arguments, context)

:ok
end
Expand All @@ -24,7 +31,13 @@ defmodule AshArchival.Resource.Changes.ArchiveRelated do
records =
Enum.map(changesets_and_results, &elem(&1, 1))

archive_related(records, first_changeset.resource, first_changeset.domain, context)
archive_related(
records,
first_changeset.resource,
first_changeset.domain,
first_changeset.arguments,
context
)

Enum.map(records, fn result ->
{:ok, result}
Expand All @@ -45,11 +58,11 @@ defmodule AshArchival.Resource.Changes.ArchiveRelated do
|> Enum.any?()
end

defp archive_related([], _, _, _) do
defp archive_related([], _, _, _, _) do
:ok
end

defp archive_related(data, resource, domain, context) do
defp archive_related(data, resource, domain, arguments, context) do
opts =
context
|> Ash.Context.to_opts(
Expand All @@ -67,12 +80,18 @@ defmodule AshArchival.Resource.Changes.ArchiveRelated do
destroy_action =
Ash.Resource.Info.primary_action!(relationship.destination, :destroy).name

arguments =
case AshArchival.Resource.Info.archive_archive_related_arguments(resource) do
{:ok, {module, options}} -> module.arguments(arguments, relationship, options)
_ -> %{}
end

case related_query(data, relationship) do
{:ok, query} ->
Ash.bulk_destroy!(
query,
destroy_action,
%{},
arguments,
Keyword.update(
opts,
:context,
Expand Down
8 changes: 8 additions & 0 deletions lib/ash_archival/resource/resource.ex
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ defmodule AshArchival.Resource do
doc: """
A list of relationships that should have all related items archived when this is archived. Notifications are not sent for this operation.
"""
],
archive_related_arguments: [
type:
{:spark_function_behaviour, AshArchival.ArchiveRelatedArguments,
{AshArchival.ArchiveRelatedArguments.Function, 2}},
doc: """
A function to allow passing along some of the arguments to related resources when archiving them.
"""
]
]
}
Expand Down
5 changes: 3 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ defmodule AshArchival.MixProject do
"documentation/topics/unarchiving.md",
"documentation/topics/how-does-ash-archival-work.md",
"documentation/topics/upserts-and-identities.md",
"documentation/dsls/DSL:-AshArchival.Resource.md",
"documentation/dsls/DSL-AshArchival.Resource.md",
"CHANGELOG.md"
],
groups_for_extras: [
Expand Down Expand Up @@ -105,7 +105,8 @@ defmodule AshArchival.MixProject do
{:credo, ">= 0.0.0", only: [:dev, :test], runtime: false},
{:dialyxir, ">= 0.0.0", only: [:dev, :test], runtime: false},
{:sobelow, ">= 0.0.0", only: [:dev, :test], runtime: false},
{:mix_audit, ">= 0.0.0", only: [:dev, :test], runtime: false}
{:mix_audit, ">= 0.0.0", only: [:dev, :test], runtime: false},
{:igniter, "~> 0.5", only: [:dev, :test]}
]
end

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
{
"attributes": [
{
"allow_nil?": false,
"default": "fragment(\"gen_random_uuid()\")",
"generated?": false,
"primary_key?": true,
"references": null,
"size": null,
"source": "id",
"type": "uuid"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "arg1",
"type": "text"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": {
"deferrable": false,
"destination_attribute": "id",
"destination_attribute_default": null,
"destination_attribute_generated": null,
"index?": false,
"match_type": null,
"match_with": null,
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"name": "with_args_children_parent_id_fkey",
"on_delete": null,
"on_update": null,
"primary_key?": true,
"schema": "public",
"table": "with_args_parents"
},
"size": null,
"source": "parent_id",
"type": "uuid"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "archived_at",
"type": "utc_datetime_usec"
}
],
"base_filter": null,
"check_constraints": [],
"custom_indexes": [],
"custom_statements": [],
"has_create_action": true,
"hash": "00ABDD1E513897CE70121C43F7DD565B216E05F18C3C8325864604E5A71EBC94",
"identities": [],
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"repo": "Elixir.AshArchival.TestRepo",
"schema": null,
"table": "with_args_children"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"attributes": [
{
"allow_nil?": false,
"default": "fragment(\"gen_random_uuid()\")",
"generated?": false,
"primary_key?": true,
"references": null,
"size": null,
"source": "id",
"type": "uuid"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "arg1",
"type": "text"
},
{
"allow_nil?": true,
"default": "nil",
"generated?": false,
"primary_key?": false,
"references": null,
"size": null,
"source": "archived_at",
"type": "utc_datetime_usec"
}
],
"base_filter": null,
"check_constraints": [],
"custom_indexes": [],
"custom_statements": [],
"has_create_action": true,
"hash": "0BBBBD5986B09FEED6E467320D9B2A32E2F63FE1297E1E47F97155400F7B113D",
"identities": [],
"multitenancy": {
"attribute": null,
"global": null,
"strategy": null
},
"repo": "Elixir.AshArchival.TestRepo",
"schema": null,
"table": "with_args_parents"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
defmodule AshArchival.TestRepo.Migrations.AddArgTestResources do
@moduledoc """
Updates resources based on their most recent snapshots.
This file was autogenerated with `mix ash_postgres.generate_migrations`
"""

use Ecto.Migration

def up do
create table(:with_args_parents, primary_key: false) do
add(:id, :uuid, null: false, default: fragment("gen_random_uuid()"), primary_key: true)
add(:arg1, :text)
add(:archived_at, :utc_datetime_usec)
end

create table(:with_args_children, primary_key: false) do
add(:id, :uuid, null: false, default: fragment("gen_random_uuid()"), primary_key: true)
add(:arg1, :text)

add(
:parent_id,
references(:with_args_parents,
column: :id,
name: "with_args_children_parent_id_fkey",
type: :uuid,
prefix: "public"
)
)

add(:archived_at, :utc_datetime_usec)
end
end

def down do
drop(constraint(:with_args_children, "with_args_children_parent_id_fkey"))

drop(table(:with_args_children))

drop(table(:with_args_parents))
end
end
35 changes: 35 additions & 0 deletions test/argument_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
defmodule AshArchival.Test.ArgumentTest do
use AshArchival.RepoCase

alias AshArchival.Test.WithArgsParent
alias AshArchival.Test.WithArgsChild

test "can pass arguments when archiving related resources" do
parent =
WithArgsParent
|> Ash.Changeset.for_create(:create)
|> Ash.create!()

WithArgsChild
|> Ash.Changeset.for_create(:create, %{parent_id: parent.id})
|> Ash.create!()

parent
|> Ash.Changeset.for_destroy(:archive, %{arg: "test"})
|> Ash.destroy!()

parent =
WithArgsParent
|> Ash.Query.for_read(:read)
|> Ash.read_one!()

assert parent.arg1 == "test"

child =
WithArgsChild
|> Ash.Query.for_read(:read)
|> Ash.read_one!()

assert child.arg1 == "test"
end
end
2 changes: 2 additions & 0 deletions test/support/domain.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,7 @@ defmodule AshArchival.Test.Domain do

resources do
resource(AshArchival.Test.Post)
resource(AshArchival.Test.WithArgsParent)
resource(AshArchival.Test.WithArgsChild)
end
end
Loading

0 comments on commit 09e77eb

Please sign in to comment.