Skip to content

Commit

Permalink
improvement: deprecate exclude_upsert_actions, as it had unintended s…
Browse files Browse the repository at this point in the history
…ide effects

docs: document how to handle upserts & identities with `ash_archival`
  • Loading branch information
zachdaniel committed Aug 16, 2024
1 parent 7d3e4db commit f8e405c
Show file tree
Hide file tree
Showing 8 changed files with 44 additions and 41 deletions.
2 changes: 1 addition & 1 deletion documentation/dsls/DSL:-AshArchival.Resource.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ A section for configuring how archival is configured for a resource.
| [`attribute`](#archive-attribute){: #archive-attribute } | `atom` | `:archived_at` | The attribute in which to store the archival flag (the current datetime). |
| [`base_filter?`](#archive-base_filter?){: #archive-base_filter? } | `atom` | `false` | Whether or not a base filter exists that applies the `is_nil(archived_at)` rule. |
| [`exclude_read_actions`](#archive-exclude_read_actions){: #archive-exclude_read_actions } | `atom \| list(atom)` | `[]` | A read action or actions that should show archived items. They will not get the automatic `is_nil(archived_at)` filter. |
| [`exclude_upsert_actions`](#archive-exclude_upsert_actions){: #archive-exclude_upsert_actions } | `atom \| list(atom)` | `[]` | A create action or actions that should not filter archived records out while upserting. |
| [`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. |

Expand Down
31 changes: 31 additions & 0 deletions documentation/topics/upserts-and-identities.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Upserts & Identities

Its important to consider identities when using AshArchival _without_ a `base_filter` set up.

If you are using a `base_filter`, then all identities implicitly include that `base_filter` in their
`where` (handled by the data layer).

Take the following identities, for example:

```elixir
identities do
identity :unique_email, [:email], where: expr(is_nil(archived_at))
# and
identity :unique_email, [:email]
end
```

## With `is_nil(archived_at)`

Using this identity allows multiple archived records with the same email, but only one _non-archived_ record per email.
It enables reuse of archived email addresses for new active records, maintaining data integrity by preventing duplicate
active records while preserving archived data.

When you upsert a record using this identity, it will only consider active records.

## Without `is_nil(archived_at)`

This identity configuration enforces strict email uniqueness across all records. Once an email is used, it can't be used
again, even after that record is archived.

When you upsert a record using this identity, it will consider all records.
21 changes: 0 additions & 21 deletions lib/ash_archival/resource/changes/filter_archived_for_upserts.ex

This file was deleted.

4 changes: 3 additions & 1 deletion lib/ash_archival/resource/resource.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ defmodule AshArchival.Resource do
exclude_upsert_actions: [
type: {:wrap_list, :atom},
default: [],
deprecated:
"Upserts are handled according to the upsert identity. See the upserts guide for more.",
doc: """
A create action or actions that should not filter archived records out while upserting.
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: [
Expand Down
9 changes: 0 additions & 9 deletions lib/ash_archival/resource/transformers/setup_archival.ex
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ defmodule AshArchival.Resource.Transformers.SetupArchival do
|> add_archived_at()
|> update_destroy_actions()
|> add_preparation()
|> add_change()
end
end

Expand Down Expand Up @@ -84,12 +83,4 @@ defmodule AshArchival.Resource.Transformers.SetupArchival do
{AshArchival.Resource.Preparations.FilterArchived, []}
)
end

defp add_change({:ok, dsl_state}) do
Ash.Resource.Builder.add_change(
dsl_state,
{AshArchival.Resource.Changes.FilterArchivedForUpserts, []},
on: [:create]
)
end
end
1 change: 1 addition & 0 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ defmodule AshArchival.MixProject do
"documentation/tutorials/get-started-with-ash-archival.md",
"documentation/topics/unarchiving.md",
"documentation/topics/how-does-ash-archival-work.md",
"documentation/topics/upserts-and-identities.md",
"documentation/dsls/DSL:-AshArchival.Resource.md",
"CHANGELOG.md"
],
Expand Down
2 changes: 0 additions & 2 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
%{
"ash": {:hex, :ash, "3.4.1", "14bfccd4c1e7c17db5aed1ecb5062875f55b56b67f6fba911f3a8ef6739f3cfd", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:ecto, "~> 3.7", [hex: :ecto, repo: "hexpm", optional: false]}, {:ets, "~> 0.8", [hex: :ets, repo: "hexpm", optional: false]}, {:igniter, ">= 0.3.11 and < 1.0.0-0", [hex: :igniter, repo: "hexpm", optional: false]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: false]}, {:picosat_elixir, "~> 0.2", [hex: :picosat_elixir, repo: "hexpm", optional: true]}, {:plug, ">= 0.0.0", [hex: :plug, repo: "hexpm", optional: true]}, {:reactor, "~> 0.9", [hex: :reactor, repo: "hexpm", optional: false]}, {:simple_sat, ">= 0.1.1 and < 1.0.0-0", [hex: :simple_sat, repo: "hexpm", optional: true]}, {:spark, ">= 2.2.8 and < 3.0.0-0", [hex: :spark, repo: "hexpm", optional: false]}, {:splode, "~> 0.2", [hex: :splode, repo: "hexpm", optional: false]}, {:stream_data, "~> 1.0", [hex: :stream_data, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.1", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1e3127e0af0698e652a6bbfb4d4f1a3bb8a48fb42833f4e8f00eda8f1a93082b"},
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
"comparable": {:hex, :comparable, "1.0.0", "bb669e91cedd14ae9937053e5bcbc3c52bb2f22422611f43b6e38367d94a495f", [:mix], [{:typable, "~> 0.1", [hex: :typable, repo: "hexpm", optional: false]}], "hexpm", "277c11eeb1cd726e7cd41c6c199e7e52fa16ee6830b45ad4cdc62e51f62eb60c"},
"credo": {:hex, :credo, "1.7.7", "771445037228f763f9b2afd612b6aa2fd8e28432a95dbbc60d8e03ce71ba4446", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8bc87496c9aaacdc3f90f01b7b0582467b69b4bd2441fe8aae3109d843cc2f2e"},
"decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
"dialyxir": {:hex, :dialyxir, "1.4.3", "edd0124f358f0b9e95bfe53a9fcf806d615d8f838e2202a9f430d59566b6b53b", [:mix], [{:erlex, ">= 0.2.6", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "bf2cfb75cd5c5006bec30141b131663299c661a864ec7fbbc72dfa557487a986"},
Expand Down Expand Up @@ -37,7 +36,6 @@
"splode": {:hex, :splode, "0.2.4", "71046334c39605095ca4bed5d008372e56454060997da14f9868534c17b84b53", [:mix], [], "hexpm", "ca3b95f0d8d4b482b5357954fec857abd0fa3ea509d623334c1328e7382044c2"},
"stream_data": {:hex, :stream_data, "1.1.1", "fd515ca95619cca83ba08b20f5e814aaf1e5ebff114659dc9731f966c9226246", [:mix], [], "hexpm", "45d0cd46bd06738463fd53f22b70042dbb58c384bb99ef4e7576e7bb7d3b8c8c"},
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
"typable": {:hex, :typable, "0.3.0", "0431e121d124cd26f312123e313d2689b9a5322b15add65d424c07779eaa3ca1", [:mix], [], "hexpm", "880a0797752da1a4c508ac48f94711e04c86156f498065a83d160eef945858f8"},
"ucwidth": {:hex, :ucwidth, "0.2.0", "1f0a440f541d895dff142275b96355f7e91e15bca525d4a0cc788ea51f0e3441", [:mix], [], "hexpm", "c1efd1798b8eeb11fb2bec3cafa3dd9c0c3647bee020543f0340b996177355bf"},
"yamerl": {:hex, :yamerl, "0.10.0", "4ff81fee2f1f6a46f1700c0d880b24d193ddb74bd14ef42cb0bcf46e81ef2f8e", [:rebar3], [], "hexpm", "346adb2963f1051dc837a2364e4acf6eb7d80097c0f53cbdc3046ec8ec4b4e6e"},
"yaml_elixir": {:hex, :yaml_elixir, "2.11.0", "9e9ccd134e861c66b84825a3542a1c22ba33f338d82c07282f4f1f52d847bd50", [:mix], [{:yamerl, "~> 0.10", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "53cc28357ee7eb952344995787f4bb8cc3cecbf189652236e9b163e8ce1bc242"},
Expand Down
15 changes: 8 additions & 7 deletions test/archival_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ defmodule ArchivalTest do
archive do
archive_related([:comments])
exclude_read_actions :all_posts
exclude_upsert_actions :upsert
end

actions do
Expand All @@ -81,10 +80,12 @@ defmodule ArchivalTest do
attributes do
uuid_primary_key(:id)
attribute(:name, :string, public?: true)
attribute(:title, :string, public?: true)
end

identities do
identity(:unique_name, [:name], pre_check?: true)
identity(:unique_name, [:name], pre_check?: true, where: expr(is_nil(archived_at)))
identity(:unique_title, [:title], pre_check?: true)
end

relationships do
Expand Down Expand Up @@ -206,7 +207,7 @@ defmodule ArchivalTest do
assert [] = Ash.read!(Post)
end

test "upserts don't consider archived records" do
test "upserts don't consider archived records if included in the identity" do
post =
Post
|> Ash.Changeset.for_create(:create, %{name: "fred"})
Expand All @@ -227,18 +228,18 @@ defmodule ArchivalTest do
|> Ash.read!()
end

test "upserts do consider archived records if the create action is excluded" do
test "upserts do consider archived records if not included in the identity" do
post =
Post
|> Ash.Changeset.for_create(:create, %{name: "fred"})
|> Ash.Changeset.for_create(:create, %{title: "fred"})
|> Ash.create!()

assert :ok = post |> Ash.destroy!()

Post
|> Ash.Changeset.for_create(:upsert, %{name: "fred"},
|> Ash.Changeset.for_create(:upsert, %{title: "fred"},
upsert?: true,
upsert_identity: :unique_name
upsert_identity: :unique_title
)
|> Ash.create!()

Expand Down

0 comments on commit f8e405c

Please sign in to comment.