Title: Backport 26.1: Fix schema rebuild after root type deletion by andimarek · Pull Request #4446 · graphql-java/graphql-java · GitHub
Open Graph Title: Backport 26.1: Fix schema rebuild after root type deletion by andimarek · Pull Request #4446 · graphql-java/graphql-java
X Title: Backport 26.1: Fix schema rebuild after root type deletion by andimarek · Pull Request #4446 · graphql-java/graphql-java
Description: Backport of #4390 to the 26.x maintenance branch. Summary Fixes #3384. The core issue is that a built GraphQLSchema can contain two views of the same relationship: the original child list used for rebuild traversal, which may still contain GraphQLTypeReference("Node") the resolved accessor view used by runtime/schema APIs, where Person.getInterfaces() returns the actual GraphQLInterfaceType Node instance In the #3384 repro, Person was originally created with .withInterface(typeRef("Node")). During schema build, that reference is resolved and Person.getInterfaces() starts returning the real Node interface. Later, when the mutation root is removed, Node is no longer reachable through the deleted mutation field. The rebuilt schema still reaches Person from Query, and Person still implements Node, so Node must remain in the rebuilt type map. The catch is that schema build collection intentionally traverses getChildrenWithTypeReferences(). For Person, that traversal sees the original GraphQLTypeReference("Node"), not the already-resolved Node object returned by Person.getInterfaces(). So the collector can visit Person without collecting the resolved Node interface. Later, GraphQLTypeResolvingVisitor uses Person.getInterfaces(), looks up Node in the rebuilt type map, and fails because Node was missed during collection. This is what the code calls an indirect strong reference: indirect: the traverser did not visit it as a normal child node, because the child list exposes the original type reference strong: the schema element already holds the actual GraphQLNamedType object through an accessor such as getInterfaces() or getTypes() GraphQLTypeCollectingVisitor already handled this pattern for field, argument, input field, and applied directive argument types. This PR extends the same collection logic to: object implemented interfaces interface implemented interfaces union member types The same original-reference versus resolved-accessor split exists for unions and interface inheritance: GraphQLUnionType.getChildrenWithTypeReferences() exposes original possible types, while getTypes() can return resolved object types GraphQLInterfaceType.getChildrenWithTypeReferences() exposes original implemented interfaces, while getInterfaces() can return resolved interfaces This does not keep every deleted-path extra type alive. It only preserves resolved named types that are still referenced by a schema element that remains reachable in the rebuilt schema. Repro Coverage Adds #3384 Spock coverage for these schema rebuild cases: object implementing an interface through a type reference union containing a member through a type reference interface implementing another interface through a type reference The object/interface case is covered through both paths: direct rebuild with GraphQLSchema.newSchema(schema).mutation((GraphQLObjectType) null).build() traversal-based deletion with SchemaTransformer.transformSchemaWithDeletes(...) The union and interface-inheritance cases specifically prove why GraphQLTypeCollectingVisitor needs to follow getTypes() and getInterfaces(), not just field/argument/input references. Before the collector change, these cases fail during rebuild with an NPE in GraphQLTypeResolvingVisitor; they now pass. Local Test Stability This PR also keeps the local build cleanup from the previous version of #4390: markGeneratedEqualsHashCode now cleans classes-jacoco before rewriting and does not append duplicate graphql.coverage.Generated annotations. That prevents local-only ArchUnit failures in JMHForkArchRuleTest after interrupted or repeated incremental runs. Verification ./gradlew test --tests "graphql.schema.impl.SchemaUtilTest.can rebuild schema after removing root type that made an implemented interface reachable" --tests "graphql.schema.impl.SchemaUtilTest.can transform schema after deleting root type that made an implemented interface reachable" --tests "graphql.schema.impl.SchemaUtilTest.can rebuild schema after removing root type that made a union member reachable" --tests "graphql.schema.impl.SchemaUtilTest.can rebuild schema after removing root type that made an interface implemented by another interface reachable" ./gradlew test --tests graphql.schema.impl.SchemaUtilTest Also ran: git diff --check
Open Graph Description: Backport of #4390 to the 26.x maintenance branch. Summary Fixes #3384. The core issue is that a built GraphQLSchema can contain two views of the same relationship: the original child list used for...
X Description: Backport of #4390 to the 26.x maintenance branch. Summary Fixes #3384. The core issue is that a built GraphQLSchema can contain two views of the same relationship: the original child list used for...
Opengraph URL: https://github.com/graphql-java/graphql-java/pull/4446
X: @github
Domain: redirect.github.com
| react-profiling | 0 |
| react-import-map | react,react-dom,react-dom/client,react-dom/profiling,react-is,react-reconciler,react/compiler-runtime,react/jsx-dev-runtime,react/jsx-runtime,scheduler@d5750093facf |
| route-pattern | /:user_id/:repository/pull/:id/files(.:format) |
| route-controller | pull_requests |
| route-action | files |
| fetch-nonce | v2:45786d7c-a61e-132c-1ef6-cded62ecadf0 |
| current-catalog-service-hash | ae870bc5e265a340912cde392f23dad3671a0a881730ffdadd82f2f57d81641b |
| request-id | D5FC:39A2F9:11CA8FA:17C4451:6AED5CED |
| html-safe-nonce | c095af103c5fdecfdb8147927f95545c4acd330201baefa9c21479f3959515ef |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJENUZDOjM5QTJGOToxMUNBOEZBOjE3QzQ0NTE6NkFFRDVDRUQiLCJ2aXNpdG9yX2lkIjoiNDA4NTU4NDgzNzUwMDI5NjQzMCIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9 |
| visitor-hmac | cb4b2b2413538d58d63270149d1e3a6b29368a833fe7b072ad8501eca72a3b24 |
| hovercard-subject-tag | pull_request:4318552808 |
| github-keyboard-shortcuts | repository,pull-request-list,pull-request-conversation,pull-request-files-changed,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/graphql-java/graphql-java/pull/4446/files |
| twitter:image | https://avatars.githubusercontent.com/u/1706744?s=400&v=4 |
| twitter:card | summary_large_image |
| og:image | https://avatars.githubusercontent.com/u/1706744?s=400&v=4 |
| og:image:alt | Backport of #4390 to the 26.x maintenance branch. Summary Fixes #3384. The core issue is that a built GraphQLSchema can contain two views of the same relationship: the original child list used for... |
| og:site_name | GitHub |
| og:type | object |
| hostname | github.com |
| expected-hostname | github.com |
| None | 56fb42677ae87ed3e64410323d67980aa65d0485568a7858f874e7edfc6d1c11 |
| turbo-cache-control | no-preview |
| diff-view | unified |
| go-import | github.com/graphql-java/graphql-java git https://github.com/graphql-java/graphql-java.git |
| octolytics-dimension-user_id | 14289921 |
| octolytics-dimension-user_login | graphql-java |
| octolytics-dimension-repository_id | 38602457 |
| octolytics-dimension-repository_nwo | graphql-java/graphql-java |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 38602457 |
| octolytics-dimension-repository_network_root_nwo | graphql-java/graphql-java |
| turbo-body-classes | logged-out env-production page-responsive full-width |
| disable-turbo | true |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 9c9a37e2423ed382cf39f70b5aa4b4858b40a3b0 |
| ui-target | canary-1 |
| theme-color | #1e2327 |
| color-scheme | light dark |
Links:
Viewport: width=device-width