Open ended scalars don't support list values
:wave: Thanks for all the hard work on Absinthe, I think I've found a bug in open ended scalars, we're using them to transport values where we don't own the structure for from a third party to our UI, I actually tried to type these but unions don't support all the types we would need and for queries the open ended scalar works fine, the problem happens trying to use this scalar for mutations, one of the sub type data we need to send is a list of booleans which causes Absinthe to decide the argument is invalid (because its a list).
I can appreciate you might decide this is not a bug, because why is a scalar sending down a list, but given that its valid json to have a root list and given that the inverse works its at least inconsistent if not a bug.
Environment
- Elixir version (elixir -v):
- Absinthe version (mix deps | grep absinthe): 1.7.6
- Client Framework and version (Relay, Apollo, etc): N/A
Expected behavior
Absinthe sees the field is an open ended scalar and passes the parsed JSON result through as is.
Actual behavior
Absinthe sees a list and decides its invalid.
Relevant Schema/Middleware Code
scalar :magic, open_ended: true do
parse(& {:ok, &1})
serialize(& &1)
end
input_object :something do
field :value, :magic
end
Hi @JonRowe can you show a concrete example?
defmodule MagicValuesTest do
use ExUnit.Case, async: true
defmodule Schema do
use Absinthe.Schema
scalar :magic, open_ended: true do
parse(&{:ok, &1})
serialize(& &1)
end
object :something do
field :name, non_null(:string)
field :value, :magic
end
input_object :something_input do
field :name, non_null(:string)
field :value, :magic
end
query do
field :get_values, non_null(list_of(non_null(:something))) do
resolve(fn _, _, _ ->
{:ok, [%{name: "thing_one", value: ["magic"]}]}
end)
end
end
mutation do
field :patch_values, type: non_null(list_of(non_null(:something))) do
arg(:somethings, non_null(list_of(non_null(:something_input))))
resolve(fn _, %{somethings: somethings}, _ -> {:ok, somethings} end)
end
end
end
setup do
Absinthe.Test.prime(Schema)
end
test "query passes" do
assert {:ok, %{data: %{"getValues" => [%{"name" => "thing_one", "value" => ["magic"]}]}}} =
"""
query {
getValues {
name
value
}
}
"""
|> Absinthe.run(Schema, context: %{}, variables: %{})
end
test "mutation passes without a list" do
assert {:ok, %{data: %{"patchValues" => [%{"name" => "thing_one", "value" => _}]}}} =
"""
mutation patchValues($somethings: [SomethingInput!]!) {
patchValues(somethings: $somethings) {
name
value
}
}
"""
|> Absinthe.run(
Schema,
context: %{},
variables: %{
"somethings" => [
%{"name" => "thing_one", "value" => "magic"}
]
}
)
end
test "mutation fails" do
assert {:ok, %{data: %{"patchValues" => [%{"name" => "thing_one", "value" => ["magic"]}]}}} =
"""
mutation patchValues($somethings: [SomethingInput!]!) {
patchValues(somethings: $somethings) {
name
value
}
}
"""
|> Absinthe.run(
Schema,
context: %{},
variables: %{
"somethings" => [
%{"name" => "thing_one", "value" => ["magic"]}
]
}
)
end
end
Hey Jon I think you're confused abut how open ended scalars are supposed to work. You've got your open ended scalar deep inside other structures. Those structures are not invalidated or consumed by the scalar just because they have an open ended scalar inside. If you want to have your open ended scalar consume the entire input you'd do something more like:
arg :somethings, :magic
I can try to find time later to edit your example to show what I mean. The reason you have versions that pass is that you are passing in JSON structures that are just straight up compatible with the values you have. It'd work even if you used :string instead of :magic.
Those structures are not invalidated or consumed by the scalar just because they have an open ended scalar inside.
I know that, thats the point, I want name to be a mandatory string, and I only want value to be open ended, hence why I showed if I use another non list value it works, objects, strings, booleans all work in the fashion I expect its only the list that triggers the invalid argument
Apologies, I misread the test. Let me dig into this later today.
the problem happens trying to use this scalar for mutations, one of the sub type data we need to send is a list
I'm facing the same requirements right now, and I was hoping that open-ended scalars would help here so I could avoid needing to have a JSON string scalar. Any advice if that could be accomplished?
As a workaround you can use a structure like {entries: list} as the scalar, its not great but it works