-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Description
Consider the following type definitions that will be used to build an Apollo Federation schema:
directive @something(rules: [Rule!]!) on OBJECT | FIELD_DEFINITION
enum Number {
one
two
}
input Rule {
numbers: [Number!]!
}
type Something {
id: ID!
}
extend type Query {
getSomething(id: ID!): Something!
@something(rules: [{ numbers: [one, two] }])
}
A single directive called @something is declared, that takes in a non-nullable list of GraphQL input object types. This directive can then be used on objects and field definitions to perform some task. An example of a similar directive can be found here: https://docs.amplify.aws/cli/graphql-transformer/auth#auth.
The problem arises during composition of the schema, namely that input type Rule is being registered twice, leading to this error:
Line 223 in 00eab30
| `Schema must contain uniquely named types but contains multiple types named "${typeName}".`, |
From what it seems, type property name is used to uniquely identify any given type in the schema, as evidenced by:
Line 216 in 00eab30
| const typeName = namedType.name; |
Line 221 in 00eab30
| if (this._typeMap[typeName] !== undefined) { |
During schema composition, all declared types are collected in a set using the following helper function:
Line 403 in 00eab30
| function collectReferencedTypes( |
Inside that function, there is a check that should prevent duplicate types from being added to the set of all referenced types:
Line 409 in 00eab30
| if (!typeSet.has(namedType)) { |
Given this information and understanding the mechanics of Set class in JS, there is a chance that !typeSet.has(namedType)will result in a false positive if two objects have different references. As a consequence, some types may be registered twice, as in the example above with Rule type.
This problem appears to be quite simple to fix. There are two approaches:
-
Use
Setclass fromimmutable-js, which I would not recommend (see: https://stackoverflow.com/a/56353815) -
Change the following check
!typeSet.has(namedType)to something like![...typeSet].find(({ name }) => name === namedType.name)(see the following line:)Line 409 in 00eab30
if (!typeSet.has(namedType)) {
I published an example repository to illustrate the problem: https://github.com/azaxarov/graphql-service-with-complex-directive-parameter
So far, I see no obvious side-effects of implementing a fix using approach nr. 2.