New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Xuorig Fix PR - Edge case with GraphQLTypeReference and Schema Transforms #2906
Conversation
I debugged this further. The reason this blows up (even for SchemaTransformer changes along with direct ones) is this The first step of the schema build code calls this
At this point the map is meant to contain a map of name -> ACTUAL types (and not type references) Later the type reference replacement code finds all type references and expects to find the actual type for them in the map above and its does not and asserts. However this particular edit means that the schema DOES have a field pointer to a actual type but the This makes sense in SDL generation because its impossible to create a situation where the actual type is not declared but code declaration can make this happen as demonstrated in this PR test case. |
Ok debugging more - this is more about the Traverser related code In this "edit case" we have a field Then the code does and edit and this gets rid of the direct reference to type B (which came via another field chain) but this field So Type B is still around in some form The
and the callback to the
So while the replace type is in place - its never used. The question is - should it be? |
The code rightly wants a DAG - if we change it, it starts to fail with
The challenge here is this. The edit caused the field to be the only reference to the It's as if the specific use case GraphQLTypeCollectingVisitor needs to grab dangling field references and do a fix up if they are missing - eg type referenced but not in the schema in some other way |
} | ||
type = unwrapOne(type); | ||
} | ||
return unwrapAllAs(type); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was probably a mistake to have the well named unwrapAll return GraphQLUnmodifiedType
which excludes GraphqlTypeReferences - but API is API so leave it as is
I did change how it works internally here
GraphQLNamedType type = unwrapAllAs(node.getType()); | ||
if (! (type instanceof GraphQLTypeReference)) { | ||
fieldActualTypes.put(type.getName(), type); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It only puts in fields that unwrap to be an actual type
private Map<String, GraphQLNamedType> fixFieldDanglingTypes(Map<String, GraphQLNamedType> visitedTypes) { | ||
for (GraphQLNamedType fieldPointerType : fieldActualTypes.values()) { | ||
String typeName = fieldPointerType.getName(); | ||
visitedTypes.putIfAbsent(typeName, fieldPointerType); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if we have a type pointed to by a field AND it's not the the list of visited types, we add it since the field is the only pointer to it say
throw new AssertException(format("All types within a GraphQL schema must have unique names. No two provided types may have the same name.\n" + | ||
"No provided type may have a name which conflicts with any built in types (including Scalar and Introspection types).\n" + | ||
"You have redefined the type '%s' from being a '%s' to a '%s'", | ||
type.getName(), existingType.getClass().getSimpleName(), type.getClass().getSimpleName())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
made a common method here
|
||
then: | ||
transformed.containsType("B") | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Andres original test that was failing
GraphQLSchema transformed = SchemaTransformer.transformSchema(schema, visitor) | ||
|
||
then: | ||
transformed.containsType("B") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My SchemaTransformer test that was failing and now passing
then: | ||
schema.getType("A") != null | ||
schema.getType("B") != null | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Direct object test case (no SDL) that was failing and is now passing
then: | ||
typeRef instanceof GraphQLTypeReference | ||
(typeRef as GraphQLTypeReference).name == "A" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extra tests as unwrapAllAs
was not previously covered
|
||
when: | ||
def schema = TestUtil.schema(sdl) | ||
// When field `a` is filtered out |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
b is filtered out
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated
} | ||
|
||
@Override | ||
public TraversalControl visitGraphQLScalarType(GraphQLScalarType node, TraverserContext<GraphQLSchemaElement> context) { | ||
assertTypeUniqueness(node, result); | ||
save(node.getName(), node); | ||
return super.visitGraphQLScalarType(node, context); | ||
return CONTINUE; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
all of this super code boiled down to CONTINUE!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Appreciate the fix! Very similar solution to the workaround I implemented but much much nicer now that it is built in in the type collector. Thanks 🙇
This is the same code as #2905 but with an extra test for
SchemaTransformer
which also fails