AshArchival

Dear team, I am currently trying to implement AshArchival. In order to be able to read the archived and none archived value. the documentation says I need to create a separate resource. Shall this separate resource have everything same as the original one, except it does not have the 'extensions: [AshArchival.Resource]' and instead have the
attributes do
attribute(:archived_at, :utc_datetime_usec, public?: true)
end
attributes do
attribute(:archived_at, :utc_datetime_usec, public?: true)
end
Should I also duplicate all the policies, calculation etc... too ? Regards, Angy.
16 Replies
AngyL75
AngyL75OPβ€’5mo ago
To add to my previous post : should ai only duplicate the attributes and that is all ?
Chaz Watkins
Chaz Watkinsβ€’5mo ago
You shouldn't have to create a separate resource for the archived items.
postgres do
table "foos"
repo MyApp.Repo
base_filter_sql "(archived_at IS NULL)"
end

archive do
exclude_read_actions [:read_archived, :read_all]
base_filter? true
end

resource do
base_filter expr(is_nil(archived_at))
end

actions do
defaults [:read, :update, :create, :destroy]

read :read_all

read :read_archived do
filter expr(not is_nil(archived_at))
end

update :unarchive do
change set_attribute(:archived_at, nil)
atomic_upgrade_with :read_archived
end
end
postgres do
table "foos"
repo MyApp.Repo
base_filter_sql "(archived_at IS NULL)"
end

archive do
exclude_read_actions [:read_archived, :read_all]
base_filter? true
end

resource do
base_filter expr(is_nil(archived_at))
end

actions do
defaults [:read, :update, :create, :destroy]

read :read_all

read :read_archived do
filter expr(not is_nil(archived_at))
end

update :unarchive do
change set_attribute(:archived_at, nil)
atomic_upgrade_with :read_archived
end
end
Then set up your policies to allow some user groups to read_archived, read_all, and unarchive. The base filter means the default read will always filter out archived records The archive block specifies which actions are excluded from the base filter. I forgot why I have base_filter_sql in the postgres block and base_filter in the resource block. Did it awhile back, but seems to work ok. But, it all depends on how you want to model your domain. If you want to have a Foo resource and an ArchivedFoo resource pointing to the same table with different base filters, that works too Sometimes it is helpful to break them apart, but for my use case I didn't
AngyL75
AngyL75OPβ€’5mo ago
How, thanks. I will try to implement it πŸ™‚ and will let you know my progress πŸ™‚
ZachDaniel
ZachDanielβ€’5mo ago
Are you sure that works? I didn't think the base filter was compatible with the exclude read actions option I think you have to do it without the base filter?
Chaz Watkins
Chaz Watkinsβ€’5mo ago
I’d have to ask Rebecca since she fought with the archival stuff for a while on my app. Pretty sure your only need to do the archival block base filter. I’ll check with her when she some online and post the answer here.
ZachDaniel
ZachDanielβ€’5mo ago
I don't know of a way to bypass the base filter on a resource is all πŸ˜‚
Chaz Watkins
Chaz Watkinsβ€’5mo ago
@sevenseacat Any idea why we put both base_filter_sql in the postgres block and base_filter in the archive block on the Contact resource? I provided an example for this support item and Zach thinks that combo shouldn't work. You wrestled with it for awhile and settled on having both.
AngyL75
AngyL75OPβ€’5mo ago
Hi @Chaz Watkins, I have tried in my test both:
data_1 = Ash.read!(WeekDayRate, action: :read_archived, authorize?: false)
dbg(data_1)

data_2 = Ash.read!(WeekDayRate, action: :read_all, authorize?: false)
dbg(data_2)
data_1 = Ash.read!(WeekDayRate, action: :read_archived, authorize?: false)
dbg(data_1)

data_2 = Ash.read!(WeekDayRate, action: :read_all, authorize?: false)
dbg(data_2)
and both return
[]
[]
ZachDaniel
ZachDanielβ€’5mo ago
if you don't use the base filter option, then archival is done with a filter on all read actions so when you exclude actions, you can then read archived items using that action AFAIK this is explained in the ash archival docs
AngyL75
AngyL75OPβ€’5mo ago
@Zach Daniel I have made it work locally with configuration similar to:
postgres do
table "foos"
repo MyApp.Repo
base_filter_sql "(archived_at IS NULL)"
end

archive do
exclude_read_actions [:read_archived, :read_all]
base_filter? false
end

#resource do
# base_filter expr(is_nil(archived_at))
#end

actions do
defaults [:read, :update, :create, :destroy]

read :read_all

read :read_archived do
filter expr(not is_nil(archived_at))
end

update :unarchive do
change set_attribute(:archived_at, nil)
atomic_upgrade_with :read_archived
end
end
postgres do
table "foos"
repo MyApp.Repo
base_filter_sql "(archived_at IS NULL)"
end

archive do
exclude_read_actions [:read_archived, :read_all]
base_filter? false
end

#resource do
# base_filter expr(is_nil(archived_at))
#end

actions do
defaults [:read, :update, :create, :destroy]

read :read_all

read :read_archived do
filter expr(not is_nil(archived_at))
end

update :unarchive do
change set_attribute(:archived_at, nil)
atomic_upgrade_with :read_archived
end
end
So, I am happy this work but I had to remove the
resource do
base_filter expr(is_nil(archived_at))
end
resource do
base_filter expr(is_nil(archived_at))
end
and keep the
postgres do
...
base_filter_sql "(archived_at IS NULL)"
end
postgres do
...
base_filter_sql "(archived_at IS NULL)"
end
to make it work. Do you know why I needed to remove the
resource do
resource do
? Also, I think this could be a good idea to update the documentation as it is not needed to create separate ressource and this example in the code is simpler to use I think. Also, it is working well with PaperTrail. Do you want me to share the code for it... as an example ?
ZachDaniel
ZachDanielβ€’5mo ago
A PR to the docs would be great πŸ˜„ I'm confused that you had to keep base_filter_sql, that doesn't make much sense πŸ€” since you removed the base_filter itself that shouldn't be necessary
sevenseacat
sevenseacatβ€’5mo ago
I don’t think I tested it with the exclude_read stuff, because it wasn’t used anywhere
Chaz Watkins
Chaz Watkinsβ€’5mo ago
gotcha. So, Zach's probably right. You only need base_filter in the archive block
AngyL75
AngyL75OPβ€’5mo ago
after removing the
base_filter_sql "(archived_at IS NULL)"
base_filter_sql "(archived_at IS NULL)"
I have got the following warning
mix ash.codegen update_archival_without_base_filter_sql
Compiling 4 files (.ex)
Generated meal_reports app
Getting extensions in current project...
Running codegen for AshPostgres.DataLayer...
** (RuntimeError) Cannot create a unique index for a resource with a base filter without also configuring `base_filter_sql`.

You must provide the `base_filter_sql` option, or skip unique indexes with `skip_unique_indexes`"

(ash_postgres 2.5.18) lib/migration_generator/migration_generator.ex:3177: AshPostgres.MigrationGenerator.identities/1
(ash_postgres 2.5.18) lib/migration_generator/migration_generator.ex:2886: AshPostgres.MigrationGenerator.do_snapshot/3
(ash_postgres 2.5.18) lib/migration_generator/migration_generator.ex:2879: AshPostgres.MigrationGenerator.get_snapshots/2
(elixir 1.18.2) lib/enum.ex:4438: Enum.flat_map_list/2
(elixir 1.18.2) lib/enum.ex:4441: Enum.flat_map_list/2
(ash_postgres 2.5.18) lib/migration_generator/migration_generator.ex:37: AshPostgres.MigrationGenerator.generate/2
(mix 1.18.2) lib/mix/task.ex:495: anonymous fn/3 in Mix.Task.run_task/5
(elixir 1.18.2) lib/enum.ex:1714: Enum."-map/2-lists^map/1-1-"/2
mix ash.codegen update_archival_without_base_filter_sql
Compiling 4 files (.ex)
Generated meal_reports app
Getting extensions in current project...
Running codegen for AshPostgres.DataLayer...
** (RuntimeError) Cannot create a unique index for a resource with a base filter without also configuring `base_filter_sql`.

You must provide the `base_filter_sql` option, or skip unique indexes with `skip_unique_indexes`"

(ash_postgres 2.5.18) lib/migration_generator/migration_generator.ex:3177: AshPostgres.MigrationGenerator.identities/1
(ash_postgres 2.5.18) lib/migration_generator/migration_generator.ex:2886: AshPostgres.MigrationGenerator.do_snapshot/3
(ash_postgres 2.5.18) lib/migration_generator/migration_generator.ex:2879: AshPostgres.MigrationGenerator.get_snapshots/2
(elixir 1.18.2) lib/enum.ex:4438: Enum.flat_map_list/2
(elixir 1.18.2) lib/enum.ex:4441: Enum.flat_map_list/2
(ash_postgres 2.5.18) lib/migration_generator/migration_generator.ex:37: AshPostgres.MigrationGenerator.generate/2
(mix 1.18.2) lib/mix/task.ex:495: anonymous fn/3 in Mix.Task.run_task/5
(elixir 1.18.2) lib/enum.ex:1714: Enum."-map/2-lists^map/1-1-"/2
Could this explain why we need to keep it ? I cn do that, yes πŸ™‚ once I have finished my PR in my project πŸ™‚
ZachDaniel
ZachDanielβ€’5mo ago
base_filter_sql "(archived_at IS NULL)"
That should only be required if you also have a base filter but with base_filter? false and no base_filter ... setup, you shouldn't need that
AngyL75
AngyL75OPβ€’4mo ago
working well πŸ™‚ thanks

Did you find this page helpful?