Repository navigation
Fix subscription resolver return type validation - #826
Merged
Merged
Conversation
The Publisher return type check for subscriptions was applied whenever the resolver instance implemented GraphQLSubscriptionResolver, so a class that is also a query or mutation resolver lost all of its non-Publisher methods and failed with "No method found". The check and the missing method note now depend on the root type being scanned, which RootResolverInfo records. The checks were also inverted: they accepted supertypes of Publisher and CompletableFuture rather than subtypes, so Publisher implementations such as Flux were rejected. Accept Publisher subtypes, futures of them (Future or CompletionStage, with class, parameterized or wildcard type arguments) and ReceiveChannel subtypes that have a wrapper registered for that exact type. Methods erased to Object, such as suspend functions, are still accepted since their return type can't be checked here. Generic wrappers only unwrap their exact type, so subscription return types are now matched against the schema as a Publisher of their events. Otherwise a Flux<Item> return, or a covariant override of a Publisher<Item> method, would map the Item type to the Flux class. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Fixes #766
Checklist
Description
Since 13.0.4, a class that implements
GraphQLMutationResolver(orGraphQLQueryResolver) together withGraphQLSubscriptionResolverfails to build withFieldResolverError: No method found as defined in schema ... addItem(~name) ... Note that a Subscription data fetcher must return a Publisher of events, even though the method is right there. The Publisher filter added in #756 checkedsearch.source is GraphQLSubscriptionResolver. That's the resolver instance, not the root type being scanned, so every non-Publisher method on such a class was dropped when scanning Mutation or Query as well.getMissingFieldMessageused the same check, which is where the misleading note came from.RootResolverInfonow records whether it's the subscription root, and both places read that throughSearch.isSubscription.The return type checks were also inverted.
method.returnType.isAssignableFrom(Publisher::class.java)accepts supertypes ofPublisherinstead of subtypes, so a subscription returning aFlux, aFlowable, a customPublishersubinterface or a concretePublisherclass got the same error.resolverMethodReturnsPublishernow acceptsPublishersubtypes, futures of them (CompletionStageorFuture, with a class, parameterized or? extendstype argument), andReceiveChannelsubtypes that have a wrapper registered for that exact type. Generic wrappers only unwrap their exact type, soMethodFieldResolver#scanForMatchesnow matches a subscription's return type against the schema as aPublisherof its events. Without that, aFlux<Item>return, or a covariant override of aPublisher<Item>method, mapped theItemtype to theFluxclass and failed with "Two different classes used for type Item".Methods whose return type is erased to
Object, like suspend functions, are still accepted without a check, same as before, because the real return type isn't known at that point. I left the transformer check inreceiveChannelToPublisherWrapperalone. Kotlin lambdas are erased to returnObject, so flipping it would reject every wrapper.DataFetcherResult<Publisher<T>>,Channel<T>with only the defaultReceiveChannelwrapper,java.util.concurrent.Flow.Publisher, and types that only become aPublisherthrough a transforming wrapper (e.g. a KotlinFlow) are still rejected, as they are on master. Each of those needs more than this fix.Behaviour change: a class that is both a query/mutation resolver and a subscription resolver exposes all its methods to the Query/Mutation type again, as it did before 13.0.4. So if a non-Publisher method on it has the same name as a field another resolver already implements, the build now fails with "Found more than one matching resolver" instead of silently ignoring the method. Subscription methods returning
Publishersubtypes, or futures of them, are now accepted. If a class has both aPublishersubtype method and a plainPublisherone for the same field, e.g.onItem(): ItemPublisherandgetOnItem(): Publisher<Item>,onItemnow wins.🤖 Generated with Claude Code