-
-
Notifications
You must be signed in to change notification settings - Fork 33
full_diff change builder crashes on {:array, :time} and other primitive struct types #214
Description
Code of Conduct
- I agree to follow this project's Code of Conduct
AI Policy
- I agree to follow this project's AI Policy, or I agree that AI was not used while creating this issue.
Versions
Elixir: 1.18.4 (compiled with Erlang/OTP 27)
Erlang/OTP: 27 [erts-15.2.7.3]
ash: 3.7.6
ash_paper_trail: 0.5.7
spark: 2.3.12
Operating system
Linux (NixOS btw) - kernel 6.16.0-zen1
Current Behavior
When using the full_diff change builder with attributes that are arrays of primitive struct types (like {:array, :time}, {:array, :date}, {:array, :datetime}), creating or updating records crashes with:
** (ArgumentError) `Time` is not a Spark DSL module.
(elixir 1.18.4) Time.persisted(:primary_key)
(spark 2.3.12) lib/spark/dsl/extension.ex:142: Spark.Dsl.Extension.persisted!/3
(ash_paper_trail 0.5.7) lib/change_builders/full_diff/helpers.ex:102: AshPaperTrail.ChangeBuilders.FullDiff.Helpers.unique_id/2
(ash_paper_trail 0.5.7) lib/change_builders/full_diff/list_change.ex:123: anonymous fn/4 in AshPaperTrail.ChangeBuilders.FullDiff.ListChange.dump_array/2
The root cause is in lib/change_builders/full_diff/helpers.ex:101-108:
def unique_id(%{__struct__: resource}, dump_value) do
case Ash.Resource.Info.primary_key(resource) do
[] ->
nil
primary_keys ->
Enum.reduce(primary_keys, [resource], &(&2 ++ [Map.get(dump_value, &1)]))
end
endThis function pattern-matches on any struct (%{__struct__: resource}), assuming that all structs are Ash resources. However, Elixir's Time, Date, DateTime, NaiveDateTime, and other standard library types are also structs. When Ash.Resource.Info.primary_key/1 is called on Time (or similar), it internally calls Spark.Dsl.Extension.persisted!/3 which fails because Time is not a Spark DSL module.
Reproduction
Expected Behavior
The full_diff change builder should handle arrays of primitive struct types (:time, :date, :datetime, :naive_datetime, etc.) without crashing. These should be treated as simple values rather than Ash embedded resources.
The unique_id/2 function should check if a struct is actually an Ash resource before calling Ash.Resource.Info.primary_key/1 on it. Non-resource structs should fall through to the simple value clause: