@@ -6,12 +6,19 @@ import graphql.NestedInputSchema
66import graphql.introspection.Introspection
77import graphql.schema.GraphQLAppliedDirectiveArgument
88import graphql.schema.GraphQLArgument
9+ import graphql.schema.GraphQLCodeRegistry
910import graphql.schema.GraphQLFieldDefinition
1011import graphql.schema.GraphQLInputObjectType
1112import graphql.schema.GraphQLObjectType
13+ import graphql.schema.GraphQLSchema
14+ import graphql.schema.GraphQLSchemaElement
1215import graphql.schema.GraphQLType
16+ import graphql.schema.GraphQLTypeVisitorStub
1317import graphql.schema.GraphQLTypeReference
1418import graphql.schema.GraphQLUnionType
19+ import graphql.schema.SchemaTransformer
20+ import graphql.util.TraversalControl
21+ import graphql.util.TraverserContext
1522import spock.lang.Specification
1623
1724import static graphql.Scalars.GraphQLBoolean
@@ -41,6 +48,7 @@ import static graphql.schema.GraphQLArgument.newArgument
4148import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition
4249import static graphql.schema.GraphQLInputObjectField.newInputObjectField
4350import static graphql.schema.GraphQLInputObjectType.newInputObject
51+ import static graphql.schema.GraphQLInterfaceType.newInterface
4452import static graphql.schema.GraphQLList.list
4553import static graphql.schema.GraphQLObjectType.newObject
4654import static graphql.schema.GraphQLSchema.newSchema
@@ -190,6 +198,161 @@ class SchemaUtilTest extends Specification {
190198 ! (cacheEnabled. getType() instanceof GraphQLTypeReference )
191199 }
192200
201+ def " can rebuild schema after removing root type that made an implemented interface reachable" () {
202+ given :
203+ def schema = schemaWithObjectImplementingInterfaceThroughTypeReference()
204+ def node = schema. getType(" Node" )
205+
206+ when :
207+ def rebuiltSchema = newSchema(schema)
208+ .mutation((GraphQLObjectType ) null )
209+ .build()
210+
211+ then :
212+ rebuiltSchema. getMutationType() == null
213+ rebuiltSchema. getType(" Node" ) == node
214+ rebuiltSchema. getObjectType(" Person" ). interfaces == [node]
215+ }
216+
217+ def " can transform schema after deleting root type that made an implemented interface reachable" () {
218+ given :
219+ def schema = schemaWithObjectImplementingInterfaceThroughTypeReference()
220+ def node = schema. getType(" Node" )
221+
222+ when :
223+ def transformedSchema = SchemaTransformer . transformSchemaWithDeletes(schema, new GraphQLTypeVisitorStub () {
224+ @Override
225+ TraversalControl visitGraphQLObjectType (GraphQLObjectType graphQLObjectType , TraverserContext<GraphQLSchemaElement > context ) {
226+ if (graphQLObjectType. name == " Mutation" ) {
227+ return deleteNode(context)
228+ }
229+ return TraversalControl . CONTINUE
230+ }
231+ })
232+
233+ then :
234+ transformedSchema. getMutationType() == null
235+ transformedSchema. getType(" Node" ) == node
236+ transformedSchema. getObjectType(" Person" ). interfaces == [node]
237+ }
238+
239+ def " can rebuild schema after removing root type that made a union member reachable" () {
240+ given :
241+ def schema = schemaWithUnionMemberThroughTypeReference()
242+ def cat = schema. getObjectType(" Cat" )
243+
244+ when :
245+ def rebuiltSchema = newSchema(schema)
246+ .mutation((GraphQLObjectType ) null )
247+ .build()
248+
249+ then :
250+ rebuiltSchema. getMutationType() == null
251+ rebuiltSchema. getType(" Cat" ) == cat
252+ rebuiltSchema. getType(" Pet" ). types == [cat]
253+ }
254+
255+ def " can rebuild schema after removing root type that made an interface implemented by another interface reachable" () {
256+ given :
257+ def schema = schemaWithInterfaceImplementingInterfaceThroughTypeReference()
258+ def node = schema. getType(" Node" )
259+
260+ when :
261+ def rebuiltSchema = newSchema(schema)
262+ .mutation((GraphQLObjectType ) null )
263+ .build()
264+
265+ then :
266+ rebuiltSchema. getMutationType() == null
267+ rebuiltSchema. getType(" Node" ) == node
268+ rebuiltSchema. getType(" NamedNode" ). interfaces == [node]
269+ }
270+
271+ private GraphQLSchema schemaWithObjectImplementingInterfaceThroughTypeReference () {
272+ def node = newInterface()
273+ .name(" Node" )
274+ .field(newFieldDefinition(). name(" id" ). type(GraphQLString ))
275+ .build()
276+ def person = newObject()
277+ .name(" Person" )
278+ .withInterface(typeRef(" Node" ))
279+ .field(newFieldDefinition(). name(" id" ). type(GraphQLString ))
280+ .build()
281+ def query = newObject()
282+ .name(" Query" )
283+ .field(newFieldDefinition(). name(" person" ). type(person))
284+ .build()
285+ def mutation = newObject()
286+ .name(" Mutation" )
287+ .field(newFieldDefinition(). name(" node" ). type(node))
288+ .build()
289+ def codeRegistry = GraphQLCodeRegistry . newCodeRegistry()
290+ .typeResolver(node, { env -> person })
291+ .build()
292+ return newSchema()
293+ .query(query)
294+ .mutation(mutation)
295+ .codeRegistry(codeRegistry)
296+ .build()
297+ }
298+
299+ private GraphQLSchema schemaWithUnionMemberThroughTypeReference () {
300+ def cat = newObject()
301+ .name(" Cat" )
302+ .field(newFieldDefinition(). name(" name" ). type(GraphQLString ))
303+ .build()
304+ def pet = GraphQLUnionType . newUnionType()
305+ .name(" Pet" )
306+ .possibleType(typeRef(" Cat" ))
307+ .build()
308+ def query = newObject()
309+ .name(" Query" )
310+ .field(newFieldDefinition(). name(" pet" ). type(pet))
311+ .build()
312+ def mutation = newObject()
313+ .name(" Mutation" )
314+ .field(newFieldDefinition(). name(" cat" ). type(cat))
315+ .build()
316+ def codeRegistry = GraphQLCodeRegistry . newCodeRegistry()
317+ .typeResolver(pet, { env -> cat })
318+ .build()
319+ return newSchema()
320+ .query(query)
321+ .mutation(mutation)
322+ .codeRegistry(codeRegistry)
323+ .build()
324+ }
325+
326+ private GraphQLSchema schemaWithInterfaceImplementingInterfaceThroughTypeReference () {
327+ def node = newInterface()
328+ .name(" Node" )
329+ .field(newFieldDefinition(). name(" id" ). type(GraphQLString ))
330+ .build()
331+ def namedNode = newInterface()
332+ .name(" NamedNode" )
333+ .withInterface(typeRef(" Node" ))
334+ .field(newFieldDefinition(). name(" id" ). type(GraphQLString ))
335+ .field(newFieldDefinition(). name(" name" ). type(GraphQLString ))
336+ .build()
337+ def query = newObject()
338+ .name(" Query" )
339+ .field(newFieldDefinition(). name(" node" ). type(namedNode))
340+ .build()
341+ def mutation = newObject()
342+ .name(" Mutation" )
343+ .field(newFieldDefinition(). name(" node" ). type(node))
344+ .build()
345+ def codeRegistry = GraphQLCodeRegistry . newCodeRegistry()
346+ .typeResolver(node, { env -> null })
347+ .typeResolver(namedNode, { env -> null })
348+ .build()
349+ return newSchema()
350+ .query(query)
351+ .mutation(mutation)
352+ .codeRegistry(codeRegistry)
353+ .build()
354+ }
355+
193356 def " redefined types are caught" () {
194357 when :
195358 final GraphQLInputObjectType attributeListInputObjectType = newInputObject(). name(" attributes" )
0 commit comments