From d4b2266ff75dd1c5d113b1f78b0c4533e184933f Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Tue, 25 Apr 2023 17:59:49 +0100 Subject: [PATCH] Schema inspection handles List from Subscription Normally a multivalue Publisher is collected to a List, so we can treat it as a match for a List field. For subscriptions, however, such a Publisher is left as an infinite stream, and we should not treat as a match for a List, but rather expect it to be a stream of List items. Discovered while working on gh-674, which is closely related. --- .../execution/SchemaMappingInspector.java | 22 ++++++++++--------- .../SchemaMappingInspectorTests.java | 10 ++++----- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaMappingInspector.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaMappingInspector.java index 745c2691..d057ecad 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaMappingInspector.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/SchemaMappingInspector.java @@ -112,14 +112,14 @@ class SchemaMappingInspector { */ public Report inspect() { - inspectSchemaType(this.schema.getQueryType(), null); + inspectSchemaType(this.schema.getQueryType(), null, false); if (this.schema.isSupportingMutations()) { - inspectSchemaType(this.schema.getMutationType(), null); + inspectSchemaType(this.schema.getMutationType(), null, false); } if (this.schema.isSupportingSubscriptions()) { - inspectSchemaType(this.schema.getSubscriptionType(), null); + inspectSchemaType(this.schema.getSubscriptionType(), null, false); } inspectDataFetcherRegistrations(); @@ -128,7 +128,7 @@ class SchemaMappingInspector { } @SuppressWarnings("rawtypes") - private void inspectSchemaType(GraphQLType type, @Nullable ResolvableType resolvableType) { + private void inspectSchemaType(GraphQLType type, @Nullable ResolvableType resolvableType, boolean subscription) { Assert.notNull(type, "No GraphQLType"); type = unwrapNonNull(type); @@ -138,7 +138,7 @@ class SchemaMappingInspector { } else if (type instanceof GraphQLList listType) { type = unwrapNonNull(listType.getWrappedType()); - resolvableType = nestForList(resolvableType, type); + resolvableType = nestForList(resolvableType, type, subscription); } else { resolvableType = (resolvableType != null ? nestIfReactive(resolvableType) : null); @@ -177,7 +177,9 @@ class SchemaMappingInspector { if (dataFetcherMap.containsKey(fieldName)) { DataFetcher fetcher = dataFetcherMap.get(fieldName); if (fetcher instanceof SelfDescribingDataFetcher selfDescribingDataFetcher) { - inspectSchemaType(field.getType(), selfDescribingDataFetcher.getReturnType()); + inspectSchemaType( + field.getType(), selfDescribingDataFetcher.getReturnType(), + (type == this.schema.getSubscriptionType())); } else if (isNotScalarOrEnumType(field.getType())) { if (logger.isDebugEnabled()) { @@ -228,15 +230,15 @@ class SchemaMappingInspector { return type; } - private ResolvableType nestForList(@Nullable ResolvableType type, GraphQLType graphQlType) { + private ResolvableType nestForList(@Nullable ResolvableType type, GraphQLType graphQlType, boolean subscription) { Assert.state(type != null, "No Java type for " + getTypeName(graphQlType)); ReactiveAdapter adapter = this.reactiveAdapterRegistry.getAdapter(type.resolve(Object.class)); if (adapter != null) { - if (adapter.isMultiValue()) { - return type.getNested(2); - } Assert.state(!adapter.isNoValue(), "Expected List compatible type: " + type); type = type.getNested(2); + if (adapter.isMultiValue() && !subscription) { + return type; + } } Assert.state(type.isArray() || type.hasGenerics(), "Expected List compatible type: " + type); return type.getNested(2); diff --git a/spring-graphql/src/test/java/org/springframework/graphql/execution/SchemaMappingInspectorTests.java b/spring-graphql/src/test/java/org/springframework/graphql/execution/SchemaMappingInspectorTests.java index c4e1e8ac..f890bb79 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/execution/SchemaMappingInspectorTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/execution/SchemaMappingInspectorTests.java @@ -219,7 +219,7 @@ class SchemaMappingInspectorTests { greeting: String } type Subscription { - bookSearch(author: String) : Book! + bookSearch(author: String) : [Book!]! } type Book { @@ -238,7 +238,7 @@ class SchemaMappingInspectorTests { greeting: String } type Subscription { - bookSearch(author: String) : Book! + bookSearch(author: String) : [Book!]! } type Book { @@ -259,7 +259,7 @@ class SchemaMappingInspectorTests { type Subscription { } extend type Subscription { - bookSearch(author: String) : Book! + bookSearch(author: String) : [Book!]! } type Book { id: ID @@ -493,7 +493,7 @@ class SchemaMappingInspectorTests { createBook: Book } type Subscription { - bookSearch(author: String) : Book! + bookSearch(author: String) : [Book!]! } type Book { id: ID @@ -598,7 +598,7 @@ class SchemaMappingInspectorTests { } @SubscriptionMapping - public Flux bookSearch(@Argument String author) { + public Flux> bookSearch(@Argument String author) { return Flux.empty(); } }