Identity on `attribute :some_attribute, {:array , EmbeddedResource}`

So if I have the following resource.
defmodule Resource do
...
actions do
defaults [:create, :read, :update, :destroy]
end

attributes do
...
uuid_primary_key :id

attribute :some_attribute, {:array, EmbeddedResource}
...
end

identities do
...
identity :unique_some_attribute, [:some_attribute]
...
end
end
defmodule Resource do
...
actions do
defaults [:create, :read, :update, :destroy]
end

attributes do
...
uuid_primary_key :id

attribute :some_attribute, {:array, EmbeddedResource}
...
end

identities do
...
identity :unique_some_attribute, [:some_attribute]
...
end
end
And the following embedded resource.
defmodule EmbeddedResource do
use Ash.Resource,
data_layer: :embedded

attributes do
attribute :key, :string do
constraints trim?: true
allow_nil? false
end

attribute :value, :string do
constraints trim?: true
allow_nil? false
end
end

identities do
identity :unique_key, [:key]
end
end
defmodule EmbeddedResource do
use Ash.Resource,
data_layer: :embedded

attributes do
attribute :key, :string do
constraints trim?: true
allow_nil? false
end

attribute :value, :string do
constraints trim?: true
allow_nil? false
end
end

identities do
identity :unique_key, [:key]
end
end
If I do the following changeset twice.
Resource
|> Ash.Changeset.for_create(:create, %{})
|> Api.create()
Resource
|> Ash.Changeset.for_create(:create, %{})
|> Api.create()
It succeeds once and then fails a second time, which is as expected. The :unique_some_attribute identity prevents two records with some_attribute: nil. However If the next changeset is repeated twice.
Resource
|> Ash.Changeset.for_create(:create, %{some_attribute: [%{key: "color", value: "ash"}]})
|> Api.create()
Resource
|> Ash.Changeset.for_create(:create, %{some_attribute: [%{key: "color", value: "ash"}]})
|> Api.create()
Two resources with the same :some_attribute field are created. I had hoped this to be prevented by the :unique_some_attribute identity. The resulting records look something like the following.
%Resource<
...
id: "uuid_1",
some_attribute: [
%EmbeddedResource<
...
autogenerated_id: "autogenerated_uuid_1",
key: "color",
value: "ash"
>
]
>

%Resource<
...
id: "uuid_2",
some_attribute: [
%EmbeddedResource<
...
autogenerated_id: "autogenerated_uuid_2",
key: "color",
value: "ash"
>
]
>
%Resource<
...
id: "uuid_1",
some_attribute: [
%EmbeddedResource<
...
autogenerated_id: "autogenerated_uuid_1",
key: "color",
value: "ash"
>
]
>

%Resource<
...
id: "uuid_2",
some_attribute: [
%EmbeddedResource<
...
autogenerated_id: "autogenerated_uuid_2",
key: "color",
value: "ash"
>
]
>
I assume the autogenerated_id fields being unique is preventing the desired outcome. Would that work and can they be disabled?
3 Replies
ZachDaniel
ZachDaniel2y ago
identities on embedded resources are only applied within lists of that attribute, not all instances of the embedded resource Its unlikely that would ever be supported automatically If you want to ensure that you have uniqueness in that way you would need to do something else, some options: 1. make that a resource and relationship. If you want cross-record uniqueness, it seems natural to me that this thing you're creating is not really an embedded resource (scoped to parent) but a regular resource 2. implement some kind of custom validation that checks for conflicts before committing 3. implement some database level constraint
morfertaw
morfertawOP2y ago
I'm not sure how I would do so with a resource and relationship. Regardless thanks for the response.
ZachDaniel
ZachDaniel2y ago
What I mean is that instead of using an embedded resource, you'd make a regular resource and then do:
has_many :things, YourThing
has_many :things, YourThing
Then if you put an identity on that related thing you'll have global uniqueness

Did you find this page helpful?