Map type with specific field/values

Hi, lately I've been creating more Graphql Mutations and I'm really missing a good way to type the arguments. I tried using embedded resources as types and that gave me nicely typed inputs but it did not work correctly as the underlying datastructure is a struct and all fields are always present, which lead to fields which were not being sent in the mutation being overwritten with nil. Is there a way to create typed maps right now?
10 Replies
ZachDaniel
ZachDaniel3y ago
If you use use Ash.Type.NewType, subtype_of: :map and define graphql_type and then hand write an absinthe type? Could you show me an example of what you’re trying to do and where embedded resources didn’t work?
barnabasj
barnabasjOP3y ago
That would work I guess, but it feels like a lot of work for a non optimal solution. This way you would only get the validation in the graphql layer not if you call the action inside the code itself. Maybe it would be possible to add some constraints to map types where you can specify keys and possible values and ash_graphql could also be aware of them and create the types. What I tried with embedded types is something like this.
defmodule Features do
use Ash.Resource, data_layer: :embedded, extensions: [AshGraphql.Resource]

graphql do
type :features
end

attributes do
attribute :feature1, :boolean
attribute :feature2, :boolean do
allow_nil? false
end
attribute :feature3, :boolean
end
end


defmodule Resource do
...

actions do
create :new do
argument :features, :Features do
allow_nil? false
end

change fn changeset, _ ->
%Feature{} = changeset.argument.features
end
end
end
end
defmodule Features do
use Ash.Resource, data_layer: :embedded, extensions: [AshGraphql.Resource]

graphql do
type :features
end

attributes do
attribute :feature1, :boolean
attribute :feature2, :boolean do
allow_nil? false
end
attribute :feature3, :boolean
end
end


defmodule Resource do
...

actions do
create :new do
argument :features, :Features do
allow_nil? false
end

change fn changeset, _ ->
%Feature{} = changeset.argument.features
end
end
end
end
This would give me the correct graphql type and validations, but the problem is inside the change I'm always getting the complete Feature struct with all values and I was unable to come up with a good way to detect if the value was set to nil or if the value was not set in the graphql mutation at all.
mutation createResource(input: {features: {feature1: true, feature2: true}}) {
...
}
mutation createResource(input: {features: {feature1: true, feature2: true}}) {
...
}
and
mutation createResource(input: {features: {feature1: true, feature2: true, feature3: nil}}) {
...
}
mutation createResource(input: {features: {feature1: true, feature2: true, feature3: nil}}) {
...
}
both result in the same values inside the Features struct.
ZachDaniel
ZachDaniel3y ago
Yeah, I think that makes sense type: :map, constraints: types: [ ] If you want to open an issue for this on ash core, we can get it into core, and then we'd just need to add support for it in AshGraphql
barnabasj
barnabasjOP3y ago
Will do, if you think this is something I could do and you give me a couple pointers on where to start I'd like to try and implement this
ZachDaniel
ZachDaniel3y ago
I definitely think this would be a good contribution 😄 It will have some complexity but its not too huge I think. So it will be a two step process https://github.com/ash-project/ash/blob/main/lib/ash/type/map.ex In that file, we'd want to add constraints, similar to how we do it with string: https://github.com/ash-project/ash/blob/main/lib/ash/type/string.ex Then, in the apply_constraints callback, we'd enumerate over the allowed keys:
constraints[:types]
|> Enum.reduce_while(%{}, fn {key, type}, acc ->
case Map.get(value, key) || Map.get(value, to_string(key)) do
...try to cast it, if its good, keep going, if its bad halt
end
end)
constraints[:types]
|> Enum.reduce_while(%{}, fn {key, type}, acc ->
case Map.get(value, key) || Map.get(value, to_string(key)) do
...try to cast it, if its good, keep going, if its bad halt
end
end)
And we can test that/ship that separately. Once we like that, we can add some logic for it to AshGraphql 🙂
barnabasj
barnabasjOP3y ago
GitHub
Constraints for maps · Issue #555 · ash-project/ash
Is your feature request related to a problem? Please describe. Maps are nice to get going, but it would be nice to be able to specify constraints on them, like what key/value pairs should be possib...
\ ឵឵឵
\ ឵឵឵3y ago
Something I've had on my list as well, thanks mate!
ZachDaniel
ZachDaniel3y ago
@barnabasj great work on the ash core side of this! The gql type derivation part might be a bit more complex, but my hope is that we can basically hitchhike on the "enum" code generation that we have. where we search for all enums on the resources and define their types
barnabasj
barnabasjOP3y ago
I'll take a look next time I'm coding.
ZachDaniel
ZachDaniel3y ago
I'll close this support forum now, as we've begun down the road of implementation.

Did you find this page helpful?