V3: nested list input coercion rejects valid singleton items
Component
GraphQL Engine (c/v3-engine), specifically GraphQL input value normalization in lang-graphql.
What is the current behaviour?
V3 rejects valid nested-list input when an outer list contains singleton values that should be coerced into inner lists.
For example, given an echo-style field:
type Query {
echo(value: [[Int]]): [[Int]]!
}this valid query is rejected during input normalization:
query {
echo(value: [1, 2, 3])
}The underlying normalization error is:
IncorrectFormat { expected_type: "LIST", actual_type: "LIST" }The same behavior is encoded in
test_input_coercion_for_list_example_6,
which currently expects [1, 2, 3] to fail for [[Int]].
Observed on master at 94915fe51d6d21bd7f6d4452dc16221bef8cfefd.
What is the expected behaviour?
List coercion should be applied recursively. Each scalar item should be coerced into a singleton inner list, so the resolver should receive [[1], [2], [3]].
For an echo resolver, the response would be:
{
"data": {
"echo": [[1], [2], [3]]
}
}This is the behavior required by the GraphQL specification's
List Input Coercion
table for input type [[Int]] and input value [1, 2, 3].
How to reproduce the issue?
- In
v3/crates/graphql/lang-graphql, normalize the parsed constant value[1, 2, 3]at location type[[Int]]. - Observe that normalization returns
Error::IncorrectFormat. - Compare with the specification result
[[1], [2], [3]].
The existing test_input_coercion_for_list_example_6 is also a focused reproduction: it passes because it currently asserts the incorrect rejection.
Screenshots or Screencast
Not applicable.
Please provide any traces or logs that could help here.
The relevant implementation is
validation/input/normalize.rs.
While iterating an outer list, it rejects a non-list item whenever the wrapped type is itself a list:
if wrapped_type.is_list() && !v.is_list() {
Err(Error::IncorrectFormat {
expected_type: "LIST",
actual_type: value.kind(),
})
}That prevents the recursive singleton coercion required for nested list types.
Any possible solutions/workarounds you're aware of?
Always recursively normalize each list item against wrapped_type. The existing non-list branch of the recursive list normalizer can then wrap a scalar as a singleton list at each nested list level. The explicit wrapped_type.is_list() && !v.is_list() rejection should not be necessary.
Applications can manually send [[1], [2], [3]], but requiring clients to pre-wrap every item is not GraphQL-spec-compliant.
Keywords
GraphQL v3, nested list, list input coercion, singleton coercion, [[Int]], lang-graphql, input normalization
Source: hasura/graphql-engine