federation
federation copied to clipboard
Input type merging during composition can break default values
Suppose we have the 2 following simple subgraphs:
# Subgraph S1
type Query {
q1(a: A): String
}
input A {
x: Int
}
and
# Subgraph S2
type Query {
q2(a: A = { x: 1, y: 2 }): String
}
input A {
x: Int
y: Int
}
Given that input types are merged by "intersection", the result of merging the 2 version of type A
will result in the supergraph having:
input A {
x: Int
}
Indeed, the rational being that if we were exposing A.y
, then subgraph S1 would get values for q1
with a y
and wouldn't know what to do of it.
The problem however is that we do not do anything for default values when we merge, so the q2
operation is merged "as is", including with it's default value of type A
that does contain a y
.
Currently, this result in this example failing during composition saying:
Invalid default value (got: {x: 1, y: 2}) provided for argument Query.q2(a:) of type A.
And even if we were to decide that failing is the right behiavour here, at a minimum, the error message is confusing.
But failing also doesn't seem ideal: it feels inconsistent to be lenient toward differing definitions for input type, but to stop be lenient just because there is default values.
The 2 other options that come to mind are:
- remove any non-merged field from default values of input types (with a warning).
- do not merge a default value at all if is invalid for the merged type (again, with a warning).
Of those, I would propose to go with the 2nd option here. Tinkering with default values by removing parts of them feels a bit dodgy to me, and a bit too "magical" and so the 2nd option feels "safer" to me, and maybe a bit more consistent with the rest of our merging rules.
But another argument (for the 2nd option) is that all of this actually also apply to enum values (at least when enum types are only used as input types, as those are also merged by intersection), and we should fix this case here too, but the 1st option above wouldn't really apply in that case, so it would have to be option 2, and so doing option 2 all the time is more consistent.