Refactoring in spring-graphql tests
Add GraphQlSetup that provides a unified workflow for test setup including the creation of a GraphQsSource, a GraphQlService, a WebGraphQlHandler, and/or HTTP handlers. The workflow also helps to collect input (schema, data fetchers) in ways that are convenient for testing purposes.
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.graphql;
|
||||
|
||||
import org.springframework.graphql.execution.DataLoaderRegistrar;
|
||||
|
||||
/**
|
||||
* Workflow that results in the creation of a {@link GraphQlService} or a
|
||||
* {@link org.springframework.graphql.web.WebGraphQlHandler}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public interface GraphQlServiceSetup extends WebGraphQlSetup {
|
||||
|
||||
GraphQlServiceSetup dataLoaders(DataLoaderRegistrar... registrars);
|
||||
|
||||
GraphQlService toGraphQlService();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.graphql;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import graphql.GraphQL;
|
||||
import graphql.schema.DataFetcher;
|
||||
import graphql.schema.GraphQLTypeVisitor;
|
||||
import graphql.schema.TypeResolver;
|
||||
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.graphql.execution.DataFetcherExceptionResolver;
|
||||
import org.springframework.graphql.execution.DataLoaderRegistrar;
|
||||
import org.springframework.graphql.execution.ExecutionGraphQlService;
|
||||
import org.springframework.graphql.execution.GraphQlSource;
|
||||
import org.springframework.graphql.execution.RuntimeWiringConfigurer;
|
||||
import org.springframework.graphql.execution.ThreadLocalAccessor;
|
||||
import org.springframework.graphql.web.WebGraphQlHandler;
|
||||
import org.springframework.graphql.web.WebInterceptor;
|
||||
|
||||
/**
|
||||
* Workflow for GraphQL tests setup that starts with {@link GraphQlSource.Builder}
|
||||
* related input, and then optionally moving on to the creation of a
|
||||
* {@link GraphQlService} or a {@link WebGraphQlHandler}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class GraphQlSetup implements GraphQlServiceSetup {
|
||||
|
||||
private final GraphQlSource.Builder graphQlSourceBuilder;
|
||||
|
||||
private final List<DataLoaderRegistrar> dataLoaderRegistrars = new ArrayList<>();
|
||||
|
||||
private final List<WebInterceptor> webInterceptors = new ArrayList<>();
|
||||
|
||||
private final List<ThreadLocalAccessor> accessors = new ArrayList<>();
|
||||
|
||||
|
||||
private GraphQlSetup(Resource... schemaResources) {
|
||||
this.graphQlSourceBuilder = GraphQlSource.builder().schemaResources(schemaResources);
|
||||
}
|
||||
|
||||
|
||||
public GraphQlSetup queryFetcher(String field, DataFetcher<?> dataFetcher) {
|
||||
return dataFetcher("Query", field, dataFetcher);
|
||||
}
|
||||
|
||||
public GraphQlSetup mutationFetcher(String field, DataFetcher<?> dataFetcher) {
|
||||
return dataFetcher("Mutation", field, dataFetcher);
|
||||
}
|
||||
|
||||
public GraphQlSetup subscriptionFetcher(String field, DataFetcher<?> dataFetcher) {
|
||||
return dataFetcher("Subscription", field, dataFetcher);
|
||||
}
|
||||
|
||||
public GraphQlSetup dataFetcher(String type, String field, DataFetcher<?> dataFetcher) {
|
||||
return runtimeWiring(wiringBuilder ->
|
||||
wiringBuilder.type(type, typeBuilder -> typeBuilder.dataFetcher(field, dataFetcher)));
|
||||
}
|
||||
|
||||
public GraphQlSetup runtimeWiring(RuntimeWiringConfigurer configurer) {
|
||||
this.graphQlSourceBuilder.configureRuntimeWiring(configurer);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GraphQlSetup exceptionResolver(DataFetcherExceptionResolver... resolvers) {
|
||||
this.graphQlSourceBuilder.exceptionResolvers(Arrays.asList(resolvers));
|
||||
return this;
|
||||
}
|
||||
|
||||
public GraphQlSetup typeResolver(TypeResolver typeResolver) {
|
||||
this.graphQlSourceBuilder.defaultTypeResolver(typeResolver);
|
||||
return this;
|
||||
}
|
||||
|
||||
public GraphQlSetup typeVisitor(GraphQLTypeVisitor... visitors) {
|
||||
this.graphQlSourceBuilder.typeVisitors(Arrays.asList(visitors));
|
||||
return this;
|
||||
}
|
||||
|
||||
public GraphQL toGraphQl() {
|
||||
return this.graphQlSourceBuilder.build().graphQl();
|
||||
}
|
||||
|
||||
public GraphQlSource toGraphQlSource() {
|
||||
return this.graphQlSourceBuilder.build();
|
||||
}
|
||||
|
||||
|
||||
// GraphQlServiceSetup...
|
||||
|
||||
@Override
|
||||
public GraphQlServiceSetup dataLoaders(DataLoaderRegistrar... registrars) {
|
||||
this.dataLoaderRegistrars.addAll(Arrays.asList(registrars));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ExecutionGraphQlService toGraphQlService() {
|
||||
GraphQlSource source = graphQlSourceBuilder.build();
|
||||
ExecutionGraphQlService service = new ExecutionGraphQlService(source);
|
||||
this.dataLoaderRegistrars.forEach(service::addDataLoaderRegistrar);
|
||||
return service;
|
||||
}
|
||||
|
||||
|
||||
// WebGraphQlSetup...
|
||||
|
||||
public WebGraphQlSetup webInterceptor(WebInterceptor... interceptors) {
|
||||
this.webInterceptors.addAll(Arrays.asList(interceptors));
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebGraphQlSetup threadLocalAccessor(ThreadLocalAccessor... accessors) {
|
||||
this.accessors.addAll(Arrays.asList(accessors));
|
||||
return this;
|
||||
}
|
||||
|
||||
public WebGraphQlHandler toWebGraphQlHandler() {
|
||||
ExecutionGraphQlService service = toGraphQlService();
|
||||
return WebGraphQlHandler.builder(service)
|
||||
.interceptors(webInterceptors)
|
||||
.threadLocalAccessors(this.accessors)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.springframework.graphql.web.webmvc.GraphQlHttpHandler toHttpHandler() {
|
||||
return new org.springframework.graphql.web.webmvc.GraphQlHttpHandler(toWebGraphQlHandler());
|
||||
}
|
||||
|
||||
@Override
|
||||
public org.springframework.graphql.web.webflux.GraphQlHttpHandler toHttpHandlerWebFlux() {
|
||||
return new org.springframework.graphql.web.webflux.GraphQlHttpHandler(toWebGraphQlHandler());
|
||||
}
|
||||
|
||||
|
||||
// Factory methods
|
||||
|
||||
public static GraphQlSetup schemaContent(String schema) {
|
||||
return new GraphQlSetup(new ByteArrayResource(schema.getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
|
||||
public static GraphQlSetup schemaResource(Resource... resources) {
|
||||
return new GraphQlSetup(resources);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.graphql;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import graphql.schema.DataFetcher;
|
||||
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.graphql.execution.GraphQlSource;
|
||||
import org.springframework.graphql.execution.RuntimeWiringConfigurer;
|
||||
|
||||
/**
|
||||
* Utility methods for GraphQL tests.
|
||||
*/
|
||||
public abstract class GraphQlTestUtils {
|
||||
|
||||
/**
|
||||
* Initialize a {@link GraphQlSource.Builder} with a single {@link DataFetcher}.
|
||||
*
|
||||
* @param schema either String content or a {@link Resource}.
|
||||
* @param typeName the parent type name (Query, Mutation, or Subscription).
|
||||
* @param fieldName the name of the operation
|
||||
* @param fetcher the fetcher to use
|
||||
*
|
||||
* @return the created builder
|
||||
*/
|
||||
public static GraphQlSource.Builder graphQlSource(
|
||||
Object schema, String typeName, String fieldName, DataFetcher<?> fetcher) {
|
||||
|
||||
return graphQlSource(schema,
|
||||
wiring -> wiring.type(typeName, builder -> builder.dataFetcher(fieldName, fetcher)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a {@link GraphQlSource.Builder} with a {@link RuntimeWiringConfigurer},
|
||||
* which may be useful for multiple {@link DataFetcher} registrations, or for a
|
||||
* built-in implementation (e.g. for annotated handler methods).
|
||||
*
|
||||
* @param schema either String content or a {@link Resource}.
|
||||
* @param configurer the configurer to apply to the RuntimeWiring
|
||||
*
|
||||
* @return the created builder
|
||||
*/
|
||||
public static GraphQlSource.Builder graphQlSource(Object schema, RuntimeWiringConfigurer configurer) {
|
||||
Resource schemaResource = (schema instanceof String ?
|
||||
new ByteArrayResource(((String) schema).getBytes(StandardCharsets.UTF_8)) :
|
||||
(Resource) schema);
|
||||
|
||||
return GraphQlSource.builder()
|
||||
.schemaResources(schemaResource)
|
||||
.configureRuntimeWiring(configurer);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,7 +34,7 @@ public class TestThreadLocalAccessor<T> implements ThreadLocalAccessor {
|
||||
@Nullable
|
||||
private Long threadId;
|
||||
|
||||
private boolean suppressThreadIdCheck;
|
||||
private final boolean suppressThreadIdCheck;
|
||||
|
||||
public TestThreadLocalAccessor(ThreadLocal<T> threadLocal) {
|
||||
this(threadLocal, false);
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.graphql;
|
||||
|
||||
import org.springframework.graphql.execution.ThreadLocalAccessor;
|
||||
import org.springframework.graphql.web.WebGraphQlHandler;
|
||||
import org.springframework.graphql.web.WebInterceptor;
|
||||
|
||||
/**
|
||||
* Workflow that results in the creation of a {@link WebGraphQlHandler} or
|
||||
* an HTTP handler for WebMvc or WebFlux.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public interface WebGraphQlSetup {
|
||||
|
||||
WebGraphQlSetup webInterceptor(WebInterceptor... interceptors);
|
||||
|
||||
WebGraphQlSetup threadLocalAccessor(ThreadLocalAccessor... accessors);
|
||||
|
||||
WebGraphQlHandler toWebGraphQlHandler();
|
||||
|
||||
org.springframework.graphql.web.webmvc.GraphQlHttpHandler toHttpHandler();
|
||||
|
||||
org.springframework.graphql.web.webflux.GraphQlHttpHandler toHttpHandlerWebFlux();
|
||||
|
||||
}
|
||||
@@ -40,14 +40,13 @@ import org.springframework.context.annotation.AnnotationConfigApplicationContext
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.GraphQlService;
|
||||
import org.springframework.graphql.GraphQlTestUtils;
|
||||
import org.springframework.graphql.GraphQlSetup;
|
||||
import org.springframework.graphql.RequestInput;
|
||||
import org.springframework.graphql.data.method.annotation.BatchMapping;
|
||||
import org.springframework.graphql.data.method.annotation.QueryMapping;
|
||||
import org.springframework.graphql.execution.BatchLoaderRegistry;
|
||||
import org.springframework.graphql.execution.DefaultBatchLoaderRegistry;
|
||||
import org.springframework.graphql.execution.ExecutionGraphQlService;
|
||||
import org.springframework.graphql.execution.GraphQlSource;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
||||
@@ -258,10 +257,10 @@ public class BatchMappingInvocationTests {
|
||||
|
||||
@Bean
|
||||
public GraphQlService graphQlService(AnnotatedControllerConfigurer configurer, BatchLoaderRegistry registry) {
|
||||
GraphQlSource source = GraphQlTestUtils.graphQlSource(schema, configurer).build();
|
||||
ExecutionGraphQlService service = new ExecutionGraphQlService(source);
|
||||
service.addDataLoaderRegistrar(registry);
|
||||
return service;
|
||||
return GraphQlSetup.schemaContent(schema)
|
||||
.runtimeWiring(configurer)
|
||||
.dataLoaders(registry)
|
||||
.toGraphQlService();
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -37,7 +37,7 @@ import org.springframework.graphql.BookCriteria;
|
||||
import org.springframework.graphql.BookSource;
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.GraphQlService;
|
||||
import org.springframework.graphql.GraphQlTestUtils;
|
||||
import org.springframework.graphql.GraphQlSetup;
|
||||
import org.springframework.graphql.RequestInput;
|
||||
import org.springframework.graphql.data.method.annotation.Argument;
|
||||
import org.springframework.graphql.data.method.annotation.MutationMapping;
|
||||
@@ -47,7 +47,6 @@ import org.springframework.graphql.data.method.annotation.SubscriptionMapping;
|
||||
import org.springframework.graphql.execution.BatchLoaderRegistry;
|
||||
import org.springframework.graphql.execution.DefaultBatchLoaderRegistry;
|
||||
import org.springframework.graphql.execution.ExecutionGraphQlService;
|
||||
import org.springframework.graphql.execution.GraphQlSource;
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -192,11 +191,11 @@ public class SchemaMappingInvocationTests {
|
||||
}
|
||||
|
||||
@Bean
|
||||
public GraphQlService graphQlService(AnnotatedControllerConfigurer configurer) {
|
||||
GraphQlSource source = GraphQlTestUtils.graphQlSource(BookSource.schema, configurer).build();
|
||||
ExecutionGraphQlService service = new ExecutionGraphQlService(source);
|
||||
service.addDataLoaderRegistrar(batchLoaderRegistry());
|
||||
return service;
|
||||
public GraphQlService graphQlService(AnnotatedControllerConfigurer configurer, BatchLoaderRegistry registry) {
|
||||
return GraphQlSetup.schemaResource(BookSource.schema)
|
||||
.runtimeWiring(configurer)
|
||||
.dataLoaders(registry)
|
||||
.toGraphQlService();
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -44,11 +44,8 @@ import org.springframework.data.repository.Repository;
|
||||
import org.springframework.graphql.Author;
|
||||
import org.springframework.graphql.BookSource;
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.GraphQlTestUtils;
|
||||
import org.springframework.graphql.GraphQlSetup;
|
||||
import org.springframework.graphql.data.GraphQlRepository;
|
||||
import org.springframework.graphql.execution.ExecutionGraphQlService;
|
||||
import org.springframework.graphql.execution.GraphQlSource;
|
||||
import org.springframework.graphql.execution.RuntimeWiringConfigurer;
|
||||
import org.springframework.graphql.web.WebGraphQlHandler;
|
||||
import org.springframework.graphql.web.WebInput;
|
||||
import org.springframework.graphql.web.WebOutput;
|
||||
@@ -77,18 +74,18 @@ class QuerydslDataFetcherTests {
|
||||
Book book = new Book(42L, "Hitchhiker's Guide to the Galaxy", new Author(0L, "Douglas", "Adams"));
|
||||
mockRepository.save(book);
|
||||
|
||||
Consumer<WebGraphQlHandler> tester = (handler) -> {
|
||||
Mono<WebOutput> outputMono = handler.handleRequest(input("{ bookById(id: 42) {name}}"));
|
||||
Consumer<GraphQlSetup> tester = setup -> {
|
||||
Mono<WebOutput> output = setup.toWebGraphQlHandler().handleRequest(input("{ bookById(id: 42) {name}}"));
|
||||
Book actualBook = GraphQlResponse.from(output).toEntity("bookById", Book.class);
|
||||
|
||||
Book actualBook = GraphQlResponse.from(outputMono).toEntity("bookById", Book.class);
|
||||
assertThat(actualBook.getName()).isEqualTo(book.getName());
|
||||
};
|
||||
|
||||
// explicit wiring
|
||||
tester.accept(initHandler("bookById", QuerydslDataFetcher.builder(mockRepository).single()));
|
||||
tester.accept(graphQlSetup("bookById", QuerydslDataFetcher.builder(mockRepository).single()));
|
||||
|
||||
// auto registration
|
||||
tester.accept(initHandler(builder -> {}, mockRepository, null));
|
||||
tester.accept(graphQlSetup(mockRepository));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -97,20 +94,20 @@ class QuerydslDataFetcherTests {
|
||||
Book book2 = new Book(53L, "Breaking Bad", new Author(0L, "", "Heisenberg"));
|
||||
mockRepository.saveAll(Arrays.asList(book1, book2));
|
||||
|
||||
Consumer<WebGraphQlHandler> tester = (handler) -> {
|
||||
Mono<WebOutput> outputMono = handler.handleRequest(input("{ books {name}}"));
|
||||
Consumer<GraphQlSetup> tester = graphQlSetup -> {
|
||||
Mono<WebOutput> output = graphQlSetup.toWebGraphQlHandler().handleRequest(input("{ books {name}}"));
|
||||
|
||||
List<String> names = GraphQlResponse.from(outputMono).toList("books", Book.class)
|
||||
List<String> names = GraphQlResponse.from(output).toList("books", Book.class)
|
||||
.stream().map(Book::getName).collect(Collectors.toList());
|
||||
|
||||
assertThat(names).containsExactlyInAnyOrder(book1.getName(), book2.getName());
|
||||
};
|
||||
|
||||
// explicit wiring
|
||||
tester.accept(initHandler("books", QuerydslDataFetcher.builder(mockRepository).many()));
|
||||
tester.accept(graphQlSetup("books", QuerydslDataFetcher.builder(mockRepository).many()));
|
||||
|
||||
// auto registration
|
||||
tester.accept(initHandler(builder -> {}, mockRepository, null));
|
||||
tester.accept(graphQlSetup(mockRepository));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -120,16 +117,16 @@ class QuerydslDataFetcherTests {
|
||||
when(mockRepository.findBy(any(), any())).thenReturn(Optional.of(book));
|
||||
|
||||
// 1) Automatic registration only
|
||||
WebGraphQlHandler handler = initHandler(builder -> {}, mockRepository, null);
|
||||
WebGraphQlHandler handler = graphQlSetup(mockRepository).toWebGraphQlHandler();
|
||||
Mono<WebOutput> outputMono = handler.handleRequest(input("{ bookById(id: 1) {name}}"));
|
||||
|
||||
Book actualBook = GraphQlResponse.from(outputMono).toEntity("bookById", Book.class);
|
||||
assertThat(actualBook.getName()).isEqualTo("Hitchhiker's Guide to the Galaxy");
|
||||
|
||||
// 2) Automatic registration and explicit wiring
|
||||
handler = initHandler(
|
||||
"bookById", env -> new Book(53L, "Breaking Bad", new Author(0L, "", "Heisenberg")),
|
||||
mockRepository);
|
||||
handler = graphQlSetup(mockRepository)
|
||||
.queryFetcher("bookById", env -> new Book(53L, "Breaking Bad", new Author(0L, "", "Heisenberg")))
|
||||
.toWebGraphQlHandler();
|
||||
|
||||
outputMono = handler.handleRequest(input("{ bookById(id: 1) {name}}"));
|
||||
|
||||
@@ -142,8 +139,8 @@ class QuerydslDataFetcherTests {
|
||||
Book book = new Book(42L, "Hitchhiker's Guide to the Galaxy", new Author(0L, "Douglas", "Adams"));
|
||||
mockRepository.save(book);
|
||||
|
||||
WebGraphQlHandler handler = initHandler("bookById",
|
||||
QuerydslDataFetcher.builder(mockRepository).projectAs(BookProjection.class).single());
|
||||
DataFetcher<?> fetcher = QuerydslDataFetcher.builder(mockRepository).projectAs(BookProjection.class).single();
|
||||
WebGraphQlHandler handler = graphQlSetup("bookById", fetcher).toWebGraphQlHandler();
|
||||
|
||||
Mono<WebOutput> outputMono = handler.handleRequest(input("{ bookById(id: 42) {name}}"));
|
||||
|
||||
@@ -156,8 +153,8 @@ class QuerydslDataFetcherTests {
|
||||
Book book = new Book(42L, "Hitchhiker's Guide to the Galaxy", new Author(0L, "Douglas", "Adams"));
|
||||
mockRepository.save(book);
|
||||
|
||||
WebGraphQlHandler handler = initHandler("bookById",
|
||||
QuerydslDataFetcher.builder(mockRepository).projectAs(BookDto.class).single());
|
||||
DataFetcher<?> fetcher = QuerydslDataFetcher.builder(mockRepository).projectAs(BookDto.class).single();
|
||||
WebGraphQlHandler handler = graphQlSetup("bookById", fetcher).toWebGraphQlHandler();
|
||||
|
||||
Mono<WebOutput> outputMono = handler.handleRequest(input("{ bookById(id: 42) {name}}"));
|
||||
|
||||
@@ -169,11 +166,12 @@ class QuerydslDataFetcherTests {
|
||||
void shouldConstructPredicateProperly() {
|
||||
MockRepository mockRepository = mock(MockRepository.class);
|
||||
|
||||
WebGraphQlHandler handler = initHandler("books",
|
||||
QuerydslDataFetcher.builder(mockRepository)
|
||||
.customizer((QuerydslBinderCustomizer<QBook>) (bindings, book) ->
|
||||
bindings.bind(book.name).firstOptional((path, value) -> value.map(path::startsWith)))
|
||||
.many());
|
||||
DataFetcher<Iterable<Book>> fetcher = QuerydslDataFetcher.builder(mockRepository)
|
||||
.customizer((QuerydslBinderCustomizer<QBook>) (bindings, book) ->
|
||||
bindings.bind(book.name).firstOptional((path, value) -> value.map(path::startsWith)))
|
||||
.many();
|
||||
|
||||
WebGraphQlHandler handler = graphQlSetup("books", fetcher).toWebGraphQlHandler();
|
||||
|
||||
handler.handleRequest(input("{ books(name: \"H\", author: \"Doug\") {name}}")).block();
|
||||
|
||||
@@ -190,18 +188,18 @@ class QuerydslDataFetcherTests {
|
||||
Book book = new Book(42L, "Hitchhiker's Guide to the Galaxy", new Author(0L, "Douglas", "Adams"));
|
||||
when(mockRepository.findBy(any(), any())).thenReturn(Mono.just(book));
|
||||
|
||||
Consumer<WebGraphQlHandler> tester = (handler) -> {
|
||||
Mono<WebOutput> outputMono = handler.handleRequest(input("{ bookById(id: 1) {name}}"));
|
||||
|
||||
Consumer<GraphQlSetup> tester = setup -> {
|
||||
Mono<WebOutput> outputMono = setup.toWebGraphQlHandler().handleRequest(input("{ bookById(id: 1) {name}}"));
|
||||
Book actualBook = GraphQlResponse.from(outputMono).toEntity("bookById", Book.class);
|
||||
|
||||
assertThat(actualBook.getName()).isEqualTo(book.getName());
|
||||
};
|
||||
|
||||
// explicit wiring
|
||||
tester.accept(initHandler("bookById", QuerydslDataFetcher.builder(mockRepository).single()));
|
||||
tester.accept(graphQlSetup("bookById", QuerydslDataFetcher.builder(mockRepository).single()));
|
||||
|
||||
// auto registration
|
||||
tester.accept(initHandler(builder -> {}, null, mockRepository));
|
||||
tester.accept(graphQlSetup(mockRepository));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -211,8 +209,8 @@ class QuerydslDataFetcherTests {
|
||||
Book book2 = new Book(53L, "Breaking Bad", new Author(0L, "", "Heisenberg"));
|
||||
when(mockRepository.findBy(any(), any())).thenReturn(Flux.just(book1, book2));
|
||||
|
||||
Consumer<WebGraphQlHandler> tester = (handler) -> {
|
||||
Mono<WebOutput> outputMono = handler.handleRequest(input("{ books {name}}"));
|
||||
Consumer<GraphQlSetup> tester = setup -> {
|
||||
Mono<WebOutput> outputMono = setup.toWebGraphQlHandler().handleRequest(input("{ books {name}}"));
|
||||
|
||||
List<String> names = GraphQlResponse.from(outputMono).toList("books", Book.class)
|
||||
.stream().map(Book::getName).collect(Collectors.toList());
|
||||
@@ -221,10 +219,37 @@ class QuerydslDataFetcherTests {
|
||||
};
|
||||
|
||||
// explicit wiring
|
||||
tester.accept(initHandler("books", QuerydslDataFetcher.builder(mockRepository).many()));
|
||||
tester.accept(graphQlSetup("books", QuerydslDataFetcher.builder(mockRepository).many()));
|
||||
|
||||
// auto registration
|
||||
tester.accept(initHandler(builder -> {}, null, mockRepository));
|
||||
tester.accept(graphQlSetup(mockRepository));
|
||||
}
|
||||
|
||||
static GraphQlSetup graphQlSetup(String fieldName, DataFetcher<?> fetcher) {
|
||||
return initGraphQlSetup(null, null).queryFetcher(fieldName, fetcher);
|
||||
}
|
||||
|
||||
static GraphQlSetup graphQlSetup(@Nullable QuerydslPredicateExecutor<?> executor) {
|
||||
return initGraphQlSetup(executor, null);
|
||||
}
|
||||
|
||||
static GraphQlSetup graphQlSetup(@Nullable ReactiveQuerydslPredicateExecutor<?> executor) {
|
||||
return initGraphQlSetup(null, executor);
|
||||
}
|
||||
|
||||
private static GraphQlSetup initGraphQlSetup(
|
||||
@Nullable QuerydslPredicateExecutor<?> executor,
|
||||
@Nullable ReactiveQuerydslPredicateExecutor<?> reactiveExecutor) {
|
||||
|
||||
GraphQLTypeVisitor visitor = QuerydslDataFetcher.registrationTypeVisitor(
|
||||
(executor != null ? Collections.singletonList(executor) : Collections.emptyList()),
|
||||
(reactiveExecutor != null ? Collections.singletonList(reactiveExecutor) : Collections.emptyList()));
|
||||
|
||||
return GraphQlSetup.schemaResource(BookSource.schema).typeVisitor(visitor);
|
||||
}
|
||||
|
||||
private WebInput input(String query) {
|
||||
return new WebInput(URI.create("/"), new HttpHeaders(), Collections.singletonMap("query", query), null, "1");
|
||||
}
|
||||
|
||||
|
||||
@@ -233,52 +258,12 @@ class QuerydslDataFetcherTests {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@GraphQlRepository
|
||||
interface ReactiveMockRepository extends Repository<Book, Long>, ReactiveQuerydslPredicateExecutor<Book> {
|
||||
|
||||
}
|
||||
|
||||
static WebGraphQlHandler initHandler(String fieldName, DataFetcher<?> fetcher) {
|
||||
return initHandler(fieldName, fetcher, null);
|
||||
}
|
||||
|
||||
static WebGraphQlHandler initHandler(
|
||||
String fieldName, DataFetcher<?> fetcher, @Nullable QuerydslPredicateExecutor<?> executor) {
|
||||
|
||||
return initHandler(
|
||||
GraphQlTestUtils.graphQlSource(BookSource.schema, "Query", fieldName, fetcher),
|
||||
executor, null);
|
||||
}
|
||||
|
||||
static WebGraphQlHandler initHandler(
|
||||
RuntimeWiringConfigurer configurer,
|
||||
@Nullable QuerydslPredicateExecutor<?> executor,
|
||||
@Nullable ReactiveQuerydslPredicateExecutor<?> reactiveExecutor) {
|
||||
|
||||
return initHandler(
|
||||
GraphQlTestUtils.graphQlSource(BookSource.schema, configurer),
|
||||
executor,
|
||||
reactiveExecutor);
|
||||
}
|
||||
|
||||
private static WebGraphQlHandler initHandler(
|
||||
GraphQlSource.Builder sourceBuilder,
|
||||
@Nullable QuerydslPredicateExecutor<?> executor,
|
||||
@Nullable ReactiveQuerydslPredicateExecutor<?> reactiveExecutor) {
|
||||
|
||||
GraphQLTypeVisitor visitor = QuerydslDataFetcher.registrationTypeVisitor(
|
||||
(executor != null ? Collections.singletonList(executor) : Collections.emptyList()),
|
||||
(reactiveExecutor != null ? Collections.singletonList(reactiveExecutor) : Collections.emptyList()));
|
||||
|
||||
GraphQlSource source = sourceBuilder.typeVisitors(Collections.singletonList(visitor)).build();
|
||||
ExecutionGraphQlService service = new ExecutionGraphQlService(source);
|
||||
return WebGraphQlHandler.builder(service).build();
|
||||
}
|
||||
|
||||
private WebInput input(String query) {
|
||||
return new WebInput(URI.create("/"), new HttpHeaders(), Collections.singletonMap("query", query), null, "1");
|
||||
}
|
||||
|
||||
|
||||
interface BookProjection {
|
||||
|
||||
|
||||
@@ -29,7 +29,8 @@ import org.springframework.graphql.Author;
|
||||
import org.springframework.graphql.Book;
|
||||
import org.springframework.graphql.BookSource;
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.GraphQlTestUtils;
|
||||
import org.springframework.graphql.GraphQlService;
|
||||
import org.springframework.graphql.GraphQlSetup;
|
||||
import org.springframework.graphql.RequestInput;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -59,20 +60,21 @@ public class BatchLoadingTests {
|
||||
this.registry.forTypePair(Long.class, Author.class)
|
||||
.registerBatchLoader((ids, env) -> Flux.fromIterable(ids).map(BookSource::getAuthor));
|
||||
|
||||
ExecutionGraphQlService service = initExecutionGraphQlService(wiring -> {
|
||||
wiring.type("Query", builder -> builder.dataFetcher("booksByCriteria", env -> {
|
||||
Map<String, Object> criteria = env.getArgument("criteria");
|
||||
String authorName = (String) criteria.get("author");
|
||||
return BookSource.findBooksByAuthor(authorName).stream()
|
||||
.map(book -> new Book(book.getId(), book.getName(), book.getAuthorId()))
|
||||
.collect(Collectors.toList());
|
||||
}));
|
||||
wiring.type("Book", builder -> builder.dataFetcher("author", env -> {
|
||||
Book book = env.getSource();
|
||||
DataLoader<Long, Author> dataLoader = env.getDataLoader(Author.class.getName());
|
||||
return dataLoader.load(book.getAuthorId());
|
||||
}));
|
||||
});
|
||||
GraphQlService service = GraphQlSetup.schemaResource(BookSource.schema)
|
||||
.queryFetcher("booksByCriteria", env -> {
|
||||
Map<String, Object> criteria = env.getArgument("criteria");
|
||||
String authorName = (String) criteria.get("author");
|
||||
return BookSource.findBooksByAuthor(authorName).stream()
|
||||
.map(book -> new Book(book.getId(), book.getName(), book.getAuthorId()))
|
||||
.collect(Collectors.toList());
|
||||
})
|
||||
.dataFetcher("Book", "author", env -> {
|
||||
Book book = env.getSource();
|
||||
DataLoader<Long, Author> dataLoader = env.getDataLoader(Author.class.getName());
|
||||
return dataLoader.load(book.getAuthorId());
|
||||
})
|
||||
.dataLoaders(this.registry)
|
||||
.toGraphQlService();
|
||||
|
||||
Mono<ExecutionResult> resultMono = service.execute(new RequestInput(query, null, null, null));
|
||||
|
||||
@@ -85,11 +87,4 @@ public class BatchLoadingTests {
|
||||
assertThat(author.getLastName()).isEqualTo("Orwell");
|
||||
}
|
||||
|
||||
private ExecutionGraphQlService initExecutionGraphQlService(RuntimeWiringConfigurer configurer) {
|
||||
GraphQlSource source = GraphQlTestUtils.graphQlSource(BookSource.schema, configurer).build();
|
||||
ExecutionGraphQlService service = new ExecutionGraphQlService(source);
|
||||
service.addDataLoaderRegistrar(this.registry);
|
||||
return service;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.GraphQlTestUtils;
|
||||
import org.springframework.graphql.GraphQlSetup;
|
||||
import org.springframework.graphql.RequestInput;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -64,13 +64,11 @@ public class ClassNameTypeResolverTests {
|
||||
"}" +
|
||||
"union Sighting = Bird | Mammal | Plant | Vegetable ";
|
||||
|
||||
private final GraphQlSetup graphQlSetup = GraphQlSetup.schemaContent(schema);
|
||||
|
||||
|
||||
@Test
|
||||
void typeResolutionViaSuperHierarchy() {
|
||||
|
||||
GraphQlSource graphQlSource =
|
||||
GraphQlTestUtils.graphQlSource(schema, "Query", "animals", env -> animalList).build();
|
||||
|
||||
String query = "" +
|
||||
"query Animals {" +
|
||||
" animals {" +
|
||||
@@ -85,7 +83,8 @@ public class ClassNameTypeResolverTests {
|
||||
" }" +
|
||||
"}";
|
||||
|
||||
Mono<ExecutionResult> resultMono = new ExecutionGraphQlService(graphQlSource)
|
||||
Mono<ExecutionResult> resultMono = graphQlSetup.queryFetcher("animals", env -> animalList)
|
||||
.toGraphQlService()
|
||||
.execute(new RequestInput(query, null, null, null));
|
||||
|
||||
GraphQlResponse response = GraphQlResponse.from(resultMono);
|
||||
@@ -107,15 +106,6 @@ public class ClassNameTypeResolverTests {
|
||||
|
||||
@Test
|
||||
void typeResolutionViaMapping() {
|
||||
|
||||
ClassNameTypeResolver typeResolver = new ClassNameTypeResolver();
|
||||
typeResolver.addMapping(Tree.class, "Plant");
|
||||
|
||||
GraphQlSource graphQlSource =
|
||||
GraphQlTestUtils.graphQlSource(schema, "Query", "sightings", env -> animalAndPlantList)
|
||||
.defaultTypeResolver(typeResolver)
|
||||
.build();
|
||||
|
||||
String query = "" +
|
||||
"query Sightings {" +
|
||||
" sightings {" +
|
||||
@@ -132,10 +122,15 @@ public class ClassNameTypeResolverTests {
|
||||
" }" +
|
||||
"}";
|
||||
|
||||
Mono<ExecutionResult> resultMono = new ExecutionGraphQlService(graphQlSource)
|
||||
ClassNameTypeResolver typeResolver = new ClassNameTypeResolver();
|
||||
typeResolver.addMapping(Tree.class, "Plant");
|
||||
|
||||
Mono<ExecutionResult> result = graphQlSetup.queryFetcher("sightings", env -> animalAndPlantList)
|
||||
.typeResolver(typeResolver)
|
||||
.toGraphQlService()
|
||||
.execute(new RequestInput(query, null, null, null));
|
||||
|
||||
GraphQlResponse response = GraphQlResponse.from(resultMono);
|
||||
GraphQlResponse response = GraphQlResponse.from(result);
|
||||
for (int i = 0; i < animalAndPlantList.size(); i++) {
|
||||
Object sighting = animalAndPlantList.get(i);
|
||||
if (sighting instanceof Animal) {
|
||||
|
||||
@@ -22,7 +22,6 @@ import java.util.List;
|
||||
import graphql.ExecutionInput;
|
||||
import graphql.ExecutionResult;
|
||||
import graphql.GraphQL;
|
||||
import graphql.schema.DataFetcher;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
@@ -31,7 +30,7 @@ import reactor.util.context.Context;
|
||||
import reactor.util.context.ContextView;
|
||||
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.GraphQlTestUtils;
|
||||
import org.springframework.graphql.GraphQlSetup;
|
||||
import org.springframework.graphql.TestThreadLocalAccessor;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -44,12 +43,13 @@ public class ContextDataFetcherDecoratorTests {
|
||||
|
||||
@Test
|
||||
void monoDataFetcher() throws Exception {
|
||||
GraphQL graphQl = initGraphQl(
|
||||
"type Query { greeting: String }",
|
||||
"Query", "greeting", (env) -> Mono.deferContextual((context) -> {
|
||||
Object name = context.get("name");
|
||||
return Mono.delay(Duration.ofMillis(50)).map((aLong) -> "Hello " + name);
|
||||
}));
|
||||
GraphQL graphQl = GraphQlSetup.schemaContent("type Query { greeting: String }")
|
||||
.queryFetcher("greeting", (env) ->
|
||||
Mono.deferContextual((context) -> {
|
||||
Object name = context.get("name");
|
||||
return Mono.delay(Duration.ofMillis(50)).map((aLong) -> "Hello " + name);
|
||||
}))
|
||||
.toGraphQl();
|
||||
|
||||
ExecutionInput input = ExecutionInput.newExecutionInput().query("{ greeting }").build();
|
||||
ReactorContextManager.setReactorContext(Context.of("name", "007"), input);
|
||||
@@ -62,33 +62,34 @@ public class ContextDataFetcherDecoratorTests {
|
||||
|
||||
@Test
|
||||
void fluxDataFetcher() throws Exception {
|
||||
GraphQL graphQl = initGraphQl(
|
||||
"type Query { greetings: [String] }",
|
||||
"Query", "greetings",
|
||||
(env) -> Mono.delay(Duration.ofMillis(50))
|
||||
.flatMapMany((aLong) -> Flux.deferContextual((context) -> {
|
||||
String name = context.get("name");
|
||||
return Flux.just("Hi", "Bonjour", "Hola").map((s) -> s + " " + name);
|
||||
})));
|
||||
GraphQL graphQl = GraphQlSetup.schemaContent("type Query { greetings: [String] }")
|
||||
.queryFetcher("greetings", (env) ->
|
||||
Mono.delay(Duration.ofMillis(50))
|
||||
.flatMapMany((aLong) -> Flux.deferContextual((context) -> {
|
||||
String name = context.get("name");
|
||||
return Flux.just("Hi", "Bonjour", "Hola").map((s) -> s + " " + name);
|
||||
})))
|
||||
.toGraphQl();
|
||||
|
||||
ExecutionInput input = ExecutionInput.newExecutionInput().query("{ greetings }").build();
|
||||
ReactorContextManager.setReactorContext(Context.of("name", "007"), input);
|
||||
|
||||
ExecutionResult result = graphQl.executeAsync(input).get();
|
||||
|
||||
List<String> data = GraphQlResponse.from(result).toList("greetings", String.class);;
|
||||
List<String> data = GraphQlResponse.from(result).toList("greetings", String.class);
|
||||
assertThat(data).containsExactly("Hi 007", "Bonjour 007", "Hola 007");
|
||||
}
|
||||
|
||||
@Test
|
||||
void fluxDataFetcherSubscription() throws Exception {
|
||||
GraphQL graphQl = initGraphQl(
|
||||
"type Query { greeting: String } type Subscription { greetings: String }",
|
||||
"Subscription", "greetings", (env) -> Mono.delay(Duration.ofMillis(50))
|
||||
.flatMapMany((aLong) -> Flux.deferContextual((context) -> {
|
||||
String name = context.get("name");
|
||||
return Flux.just("Hi", "Bonjour", "Hola").map((s) -> s + " " + name);
|
||||
})));
|
||||
GraphQL graphQl = GraphQlSetup.schemaContent("type Query { greeting: String } type Subscription { greetings: String }")
|
||||
.subscriptionFetcher("greetings", (env) ->
|
||||
Mono.delay(Duration.ofMillis(50))
|
||||
.flatMapMany((aLong) -> Flux.deferContextual((context) -> {
|
||||
String name = context.get("name");
|
||||
return Flux.just("Hi", "Bonjour", "Hola").map((s) -> s + " " + name);
|
||||
})))
|
||||
.toGraphQl();
|
||||
|
||||
ExecutionInput input = ExecutionInput.newExecutionInput().query("subscription { greetings }").build();
|
||||
ReactorContextManager.setReactorContext(Context.of("name", "007"), input);
|
||||
@@ -109,9 +110,9 @@ public class ContextDataFetcherDecoratorTests {
|
||||
nameThreadLocal.set("007");
|
||||
TestThreadLocalAccessor<String> accessor = new TestThreadLocalAccessor<>(nameThreadLocal);
|
||||
try {
|
||||
GraphQL graphQl = initGraphQl(
|
||||
"type Query { greeting: String }",
|
||||
"Query", "greeting", (env) -> "Hello " + nameThreadLocal.get());
|
||||
GraphQL graphQl = GraphQlSetup.schemaContent("type Query { greeting: String }")
|
||||
.queryFetcher("greeting", (env) -> "Hello " + nameThreadLocal.get())
|
||||
.toGraphQl();
|
||||
|
||||
ExecutionInput input = ExecutionInput.newExecutionInput().query("{ greeting }").build();
|
||||
ContextView view = ReactorContextManager.extractThreadLocalValues(accessor, Context.empty());
|
||||
@@ -128,11 +129,4 @@ public class ContextDataFetcherDecoratorTests {
|
||||
}
|
||||
}
|
||||
|
||||
private static GraphQL initGraphQl(
|
||||
String schemaContent, String typeName, String fieldName, DataFetcher<?> fetcher) {
|
||||
|
||||
return GraphQlTestUtils.graphQlSource(schemaContent, typeName, fieldName, fetcher)
|
||||
.build().graphQl();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import reactor.util.context.Context;
|
||||
import reactor.util.context.ContextView;
|
||||
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.GraphQlTestUtils;
|
||||
import org.springframework.graphql.GraphQlSetup;
|
||||
import org.springframework.graphql.TestThreadLocalAccessor;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -45,7 +45,7 @@ public class ExceptionResolversExceptionHandlerTests {
|
||||
|
||||
@Test
|
||||
void resolveException() throws Exception {
|
||||
GraphQL graphQl = initGraphQl((ex, env) ->
|
||||
GraphQL graphQl = graphQl((ex, env) ->
|
||||
Mono.just(Collections.singletonList(
|
||||
GraphqlErrorBuilder.newError(env)
|
||||
.message("Resolved error: " + ex.getMessage())
|
||||
@@ -65,7 +65,7 @@ public class ExceptionResolversExceptionHandlerTests {
|
||||
|
||||
@Test
|
||||
void resolveExceptionWithReactorContext() throws Exception {
|
||||
GraphQL graphQl = initGraphQl((ex, env) ->
|
||||
GraphQL graphQl = graphQl((ex, env) ->
|
||||
Mono.deferContextual((view) -> Mono.just(Collections.singletonList(
|
||||
GraphqlErrorBuilder.newError(env)
|
||||
.message("Resolved error: " + ex.getMessage() + ", name=" + view.get("name"))
|
||||
@@ -87,7 +87,7 @@ public class ExceptionResolversExceptionHandlerTests {
|
||||
nameThreadLocal.set("007");
|
||||
TestThreadLocalAccessor<String> accessor = new TestThreadLocalAccessor<>(nameThreadLocal);
|
||||
try {
|
||||
GraphQL graphQl = initGraphQl(threadLocalContextAwareExceptionResolver((ex, env) ->
|
||||
GraphQL graphQl = graphQl(threadLocalContextAwareResolver((ex, env) ->
|
||||
GraphqlErrorBuilder.newError(env)
|
||||
.message("Resolved error: " + ex.getMessage() + ", name=" + nameThreadLocal.get())
|
||||
.errorType(ErrorType.BAD_REQUEST)
|
||||
@@ -111,7 +111,7 @@ public class ExceptionResolversExceptionHandlerTests {
|
||||
|
||||
@Test
|
||||
void unresolvedException() throws Exception {
|
||||
GraphQL graphQl = initGraphQl((exception, environment) -> Mono.empty());
|
||||
GraphQL graphQl = graphQl((exception, environment) -> Mono.empty());
|
||||
|
||||
ExecutionInput input = ExecutionInput.newExecutionInput().query("{ greeting }").build();
|
||||
ExecutionResult result = graphQl.executeAsync(input).get();
|
||||
@@ -127,7 +127,7 @@ public class ExceptionResolversExceptionHandlerTests {
|
||||
|
||||
@Test
|
||||
void suppressedException() throws Exception {
|
||||
GraphQL graphQl = initGraphQl((ex, env) -> Mono.just(Collections.emptyList()));
|
||||
GraphQL graphQl = graphQl((ex, env) -> Mono.just(Collections.emptyList()));
|
||||
|
||||
ExecutionInput input = ExecutionInput.newExecutionInput().query("{ greeting }").build();
|
||||
ExecutionResult result = graphQl.executeAsync(input).get();
|
||||
@@ -136,18 +136,16 @@ public class ExceptionResolversExceptionHandlerTests {
|
||||
assertThat(greeting).isNull();
|
||||
}
|
||||
|
||||
private static GraphQL initGraphQl(DataFetcherExceptionResolver exceptionResolver) {
|
||||
return GraphQlTestUtils.graphQlSource(
|
||||
"type Query { greeting: String }",
|
||||
"Query", "greeting", (env) -> {
|
||||
throw new IllegalArgumentException("Invalid greeting");
|
||||
})
|
||||
.exceptionResolvers(Collections.singletonList(exceptionResolver))
|
||||
.build()
|
||||
.graphQl();
|
||||
private static GraphQL graphQl(DataFetcherExceptionResolver exceptionResolver) {
|
||||
return GraphQlSetup.schemaContent("type Query { greeting: String }")
|
||||
.queryFetcher("greeting", (env) -> {
|
||||
throw new IllegalArgumentException("Invalid greeting");
|
||||
})
|
||||
.exceptionResolver(exceptionResolver)
|
||||
.toGraphQl();
|
||||
}
|
||||
|
||||
private static DataFetcherExceptionResolver threadLocalContextAwareExceptionResolver(
|
||||
private static DataFetcherExceptionResolver threadLocalContextAwareResolver(
|
||||
BiFunction<Throwable, DataFetchingEnvironment, GraphQLError> resolver) {
|
||||
|
||||
DataFetcherExceptionResolverAdapter adapter = new DataFetcherExceptionResolverAdapter() {
|
||||
|
||||
@@ -28,14 +28,11 @@ import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.graphql.GraphQlResponse;
|
||||
import org.springframework.graphql.GraphQlService;
|
||||
import org.springframework.graphql.GraphQlTestUtils;
|
||||
import org.springframework.graphql.GraphQlSetup;
|
||||
import org.springframework.graphql.TestThreadLocalAccessor;
|
||||
import org.springframework.graphql.execution.DataFetcherExceptionResolver;
|
||||
import org.springframework.graphql.execution.DataFetcherExceptionResolverAdapter;
|
||||
import org.springframework.graphql.execution.ErrorType;
|
||||
import org.springframework.graphql.execution.ExecutionGraphQlService;
|
||||
import org.springframework.graphql.execution.GraphQlSource;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -50,15 +47,13 @@ public class WebGraphQlHandlerTests {
|
||||
|
||||
@Test
|
||||
void reactorContextPropagation() {
|
||||
GraphQlSource graphQlSource = GraphQlTestUtils.graphQlSource(
|
||||
"type Query { greeting: String }",
|
||||
"Query", "greeting", (env) -> Mono.deferContextual((context) -> {
|
||||
Object name = context.get("name");
|
||||
return Mono.delay(Duration.ofMillis(50)).map((aLong) -> "Hello " + name);
|
||||
})).build();
|
||||
|
||||
GraphQlService service = new ExecutionGraphQlService(graphQlSource);
|
||||
WebGraphQlHandler handler = WebGraphQlHandler.builder(service).build();
|
||||
WebGraphQlHandler handler = GraphQlSetup.schemaContent("type Query { greeting: String }")
|
||||
.queryFetcher("greeting", (env) ->
|
||||
Mono.deferContextual((context) -> {
|
||||
Object name = context.get("name");
|
||||
return Mono.delay(Duration.ofMillis(50)).map((aLong) -> "Hello " + name);
|
||||
}))
|
||||
.toWebGraphQlHandler();
|
||||
|
||||
Mono<WebOutput> outputMono = handler.handleRequest(webInput)
|
||||
.contextWrite((context) -> context.put("name", "007"));
|
||||
@@ -69,25 +64,19 @@ public class WebGraphQlHandlerTests {
|
||||
|
||||
@Test
|
||||
void reactorContextPropagationToExceptionResolver() {
|
||||
|
||||
GraphQlSource graphQlSource = GraphQlTestUtils.graphQlSource(
|
||||
"type Query { greeting: String }",
|
||||
"Query", "greeting", (env) -> {
|
||||
throw new IllegalArgumentException("Invalid greeting");
|
||||
})
|
||||
.exceptionResolvers(Collections.singletonList(
|
||||
(ex, env) -> Mono.deferContextual((view) -> Mono.just(Collections.singletonList(
|
||||
WebGraphQlHandler handler = GraphQlSetup.schemaContent("type Query { greeting: String }")
|
||||
.queryFetcher("greeting", (env) -> {
|
||||
throw new IllegalArgumentException("Invalid greeting");
|
||||
})
|
||||
.exceptionResolver((ex, env) -> Mono.deferContextual((view) ->
|
||||
Mono.just(Collections.singletonList(
|
||||
GraphqlErrorBuilder.newError(env)
|
||||
.message("Resolved error: " + ex.getMessage() + ", name=" + view.get("name"))
|
||||
.errorType(ErrorType.BAD_REQUEST)
|
||||
.build())))))
|
||||
.build();
|
||||
.build()))))
|
||||
.toWebGraphQlHandler();
|
||||
|
||||
GraphQlService service = new ExecutionGraphQlService(graphQlSource);
|
||||
WebGraphQlHandler handler = WebGraphQlHandler.builder(service).build();
|
||||
|
||||
Mono<WebOutput> outputMono = handler.handleRequest(webInput)
|
||||
.contextWrite((context) -> context.put("name", "007"));
|
||||
Mono<WebOutput> outputMono = handler.handleRequest(webInput).contextWrite((cxt) -> cxt.put("name", "007"));
|
||||
|
||||
GraphQlResponse response = GraphQlResponse.from(outputMono);
|
||||
assertThat(response.errorCount()).isEqualTo(1);
|
||||
@@ -103,17 +92,11 @@ public class WebGraphQlHandlerTests {
|
||||
nameThreadLocal.set("007");
|
||||
TestThreadLocalAccessor<String> threadLocalAccessor = new TestThreadLocalAccessor<>(nameThreadLocal);
|
||||
try {
|
||||
GraphQlSource graphQlSource = GraphQlTestUtils.graphQlSource(
|
||||
"type Query { greeting: String }",
|
||||
"Query", "greeting", env -> "Hello " + nameThreadLocal.get())
|
||||
.build();
|
||||
|
||||
GraphQlService service = new ExecutionGraphQlService(graphQlSource);
|
||||
|
||||
WebGraphQlHandler handler = WebGraphQlHandler.builder(service)
|
||||
.interceptor((input, next) -> Mono.delay(Duration.ofMillis(10)).flatMap((aLong) -> next.next(input)))
|
||||
WebGraphQlHandler handler = GraphQlSetup.schemaContent("type Query { greeting: String }")
|
||||
.queryFetcher("greeting", env -> "Hello " + nameThreadLocal.get())
|
||||
.webInterceptor((input, next) -> Mono.delay(Duration.ofMillis(10)).flatMap((aLong) -> next.next(input)))
|
||||
.threadLocalAccessor(threadLocalAccessor)
|
||||
.build();
|
||||
.toWebGraphQlHandler();
|
||||
|
||||
Mono<WebOutput> outputMono = handler.handleRequest(webInput);
|
||||
|
||||
@@ -131,25 +114,18 @@ public class WebGraphQlHandlerTests {
|
||||
nameThreadLocal.set("007");
|
||||
TestThreadLocalAccessor<String> threadLocalAccessor = new TestThreadLocalAccessor<>(nameThreadLocal);
|
||||
try {
|
||||
GraphQlSource graphQlSource = GraphQlTestUtils.graphQlSource(
|
||||
"type Query { greeting: String }",
|
||||
"Query", "greeting", env -> {
|
||||
WebGraphQlHandler handler = GraphQlSetup.schemaContent("type Query { greeting: String }")
|
||||
.queryFetcher("greeting", env -> {
|
||||
throw new IllegalArgumentException("Invalid greeting");
|
||||
})
|
||||
.exceptionResolvers(Collections.singletonList(
|
||||
threadLocalContextAwareExceptionResolver((ex, env) ->
|
||||
GraphqlErrorBuilder.newError(env)
|
||||
.message("Resolved error: " + ex.getMessage() + ", name=" + nameThreadLocal.get())
|
||||
.errorType(ErrorType.BAD_REQUEST).build())
|
||||
))
|
||||
.build();
|
||||
|
||||
GraphQlService service = new ExecutionGraphQlService(graphQlSource);
|
||||
|
||||
WebGraphQlHandler handler = WebGraphQlHandler.builder(service)
|
||||
.interceptor((input, next) -> Mono.delay(Duration.ofMillis(10)).flatMap((aLong) -> next.next(input)))
|
||||
.exceptionResolver(threadLocalContextAwareExceptionResolver((ex, env) ->
|
||||
GraphqlErrorBuilder.newError(env)
|
||||
.message("Resolved error: " + ex.getMessage() + ", name=" + nameThreadLocal.get())
|
||||
.errorType(ErrorType.BAD_REQUEST).build())
|
||||
)
|
||||
.webInterceptor((input, next) -> Mono.delay(Duration.ofMillis(10)).flatMap((aLong) -> next.next(input)))
|
||||
.threadLocalAccessor(threadLocalAccessor)
|
||||
.build();
|
||||
.toWebGraphQlHandler();
|
||||
|
||||
Mono<WebOutput> outputMono = handler.handleRequest(webInput);
|
||||
|
||||
|
||||
@@ -16,21 +16,17 @@
|
||||
|
||||
package org.springframework.graphql.web;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
|
||||
import org.springframework.graphql.BookSource;
|
||||
import org.springframework.graphql.GraphQlTestUtils;
|
||||
import org.springframework.graphql.execution.ExecutionGraphQlService;
|
||||
import org.springframework.graphql.execution.GraphQlSource;
|
||||
import org.springframework.graphql.GraphQlSetup;
|
||||
|
||||
public abstract class BookTestUtils {
|
||||
public abstract class WebSocketHandlerTestSupport {
|
||||
|
||||
public static final String SUBSCRIPTION_ID = "1";
|
||||
protected static final String SUBSCRIPTION_ID = "1";
|
||||
|
||||
public static final String BOOK_QUERY = "{" +
|
||||
"\"id\":\"" + BookTestUtils.SUBSCRIPTION_ID + "\"," +
|
||||
protected static final String BOOK_QUERY = "{" +
|
||||
"\"id\":\"" + WebSocketHandlerTestSupport.SUBSCRIPTION_ID + "\"," +
|
||||
"\"type\":\"subscribe\"," +
|
||||
"\"payload\":{\"query\": \"" +
|
||||
" query TestQuery {" +
|
||||
@@ -44,7 +40,7 @@ public abstract class BookTestUtils {
|
||||
" }}\"}" +
|
||||
"}";
|
||||
|
||||
public static final String BOOK_SUBSCRIPTION = "{" +
|
||||
protected static final String BOOK_SUBSCRIPTION = "{" +
|
||||
"\"id\":\"" + SUBSCRIPTION_ID + "\"," +
|
||||
"\"type\":\"subscribe\"," +
|
||||
"\"payload\":{\"query\": \"" +
|
||||
@@ -59,24 +55,20 @@ public abstract class BookTestUtils {
|
||||
" }}\"}" +
|
||||
"}";
|
||||
|
||||
public static WebGraphQlHandler initWebGraphQlHandler(WebInterceptor... interceptors) {
|
||||
|
||||
GraphQlSource source = GraphQlTestUtils.graphQlSource(BookSource.schema,
|
||||
wiringBuilder -> {
|
||||
wiringBuilder.type("Query", builder -> builder.dataFetcher("bookById", (env) -> {
|
||||
Long id = Long.parseLong(env.getArgument("id"));
|
||||
return BookSource.getBook(id);
|
||||
}));
|
||||
wiringBuilder.type("Subscription", builder -> builder.dataFetcher("bookSearch", (env) -> {
|
||||
String author = env.getArgument("author");
|
||||
return Flux.fromIterable(BookSource.books())
|
||||
.filter((book) -> book.getAuthor().getFullName().contains(author));
|
||||
}));
|
||||
}).build();
|
||||
|
||||
return WebGraphQlHandler.builder(new ExecutionGraphQlService(source))
|
||||
.interceptors(Arrays.asList(interceptors))
|
||||
.build();
|
||||
protected WebGraphQlHandler initHandler(WebInterceptor... interceptors) {
|
||||
return GraphQlSetup.schemaResource(BookSource.schema)
|
||||
.queryFetcher("bookById", environment -> {
|
||||
Long id = Long.parseLong(environment.getArgument("id"));
|
||||
return BookSource.getBook(id);
|
||||
})
|
||||
.subscriptionFetcher("bookSearch", environment -> {
|
||||
String author = environment.getArgument("author");
|
||||
return Flux.fromIterable(BookSource.books())
|
||||
.filter((book) -> book.getAuthor().getFullName().contains(author));
|
||||
})
|
||||
.webInterceptor(interceptors)
|
||||
.toWebGraphQlHandler();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -20,15 +20,10 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import graphql.schema.DataFetcher;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.graphql.GraphQlService;
|
||||
import org.springframework.graphql.GraphQlTestUtils;
|
||||
import org.springframework.graphql.execution.ExecutionGraphQlService;
|
||||
import org.springframework.graphql.execution.GraphQlSource;
|
||||
import org.springframework.graphql.web.WebGraphQlHandler;
|
||||
import org.springframework.graphql.GraphQlSetup;
|
||||
import org.springframework.http.codec.EncoderHttpMessageWriter;
|
||||
import org.springframework.http.codec.HttpMessageWriter;
|
||||
import org.springframework.http.codec.json.Jackson2JsonEncoder;
|
||||
@@ -50,9 +45,9 @@ public class GraphQlHttpHandlerTests {
|
||||
|
||||
@Test
|
||||
void locale() {
|
||||
GraphQlHttpHandler handler = createHttpHandler(
|
||||
"type Query { greeting: String }", "Query", "greeting",
|
||||
(env) -> "Hello in " + env.getLocale());
|
||||
GraphQlHttpHandler handler = GraphQlSetup.schemaContent("type Query { greeting: String }")
|
||||
.queryFetcher("greeting", (env) -> "Hello in " + env.getLocale())
|
||||
.toHttpHandlerWebFlux();
|
||||
|
||||
MockServerHttpRequest httpRequest =
|
||||
MockServerHttpRequest.post("/").acceptLanguageAsLocales(Locale.FRENCH).build();
|
||||
@@ -64,14 +59,6 @@ public class GraphQlHttpHandlerTests {
|
||||
.isEqualTo("{\"data\":{\"greeting\":\"Hello in fr\"}}");
|
||||
}
|
||||
|
||||
private GraphQlHttpHandler createHttpHandler(
|
||||
String schemaContent, String type, String field, DataFetcher<Object> dataFetcher) {
|
||||
|
||||
GraphQlSource source = GraphQlTestUtils.graphQlSource(schemaContent, type, field, dataFetcher).build();
|
||||
GraphQlService service = new ExecutionGraphQlService(source);
|
||||
return new GraphQlHttpHandler(WebGraphQlHandler.builder(service).build());
|
||||
}
|
||||
|
||||
private MockServerHttpResponse handleRequest(
|
||||
MockServerHttpRequest httpRequest, GraphQlHttpHandler handler, Map<String, String> body) {
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ import reactor.test.StepVerifier;
|
||||
import org.springframework.core.io.buffer.DataBuffer;
|
||||
import org.springframework.core.io.buffer.DataBufferUtils;
|
||||
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
|
||||
import org.springframework.graphql.web.BookTestUtils;
|
||||
import org.springframework.graphql.web.WebSocketHandlerTestSupport;
|
||||
import org.springframework.graphql.web.ConsumeOneAndNeverCompleteInterceptor;
|
||||
import org.springframework.graphql.web.WebInterceptor;
|
||||
import org.springframework.graphql.web.WebSocketInterceptor;
|
||||
@@ -49,7 +49,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
/**
|
||||
* Unit tests for {@link GraphQlWebSocketHandler}.
|
||||
*/
|
||||
public class GraphQlWebSocketHandlerTests {
|
||||
public class GraphQlWebSocketHandlerTests extends WebSocketHandlerTestSupport {
|
||||
|
||||
private static final Jackson2JsonDecoder decoder = new Jackson2JsonDecoder();
|
||||
|
||||
@@ -57,12 +57,12 @@ public class GraphQlWebSocketHandlerTests {
|
||||
void query() {
|
||||
TestWebSocketSession session = handle(Flux.just(
|
||||
toWebSocketMessage("{\"type\":\"connection_init\"}"),
|
||||
toWebSocketMessage(BookTestUtils.BOOK_QUERY)));
|
||||
toWebSocketMessage(BOOK_QUERY)));
|
||||
|
||||
StepVerifier.create(session.getOutput())
|
||||
.consumeNextWith((message) -> assertMessageType(message, "connection_ack"))
|
||||
.consumeNextWith((message) -> assertThat(decode(message)).hasSize(3)
|
||||
.containsEntry("id", BookTestUtils.SUBSCRIPTION_ID).containsEntry("type", "next")
|
||||
.containsEntry("id", SUBSCRIPTION_ID).containsEntry("type", "next")
|
||||
.extractingByKey("payload", as(InstanceOfAssertFactories.map(String.class, Object.class)))
|
||||
.extractingByKey("data", as(InstanceOfAssertFactories.map(String.class, Object.class)))
|
||||
.extractingByKey("bookById", as(InstanceOfAssertFactories.map(String.class, Object.class)))
|
||||
@@ -75,11 +75,11 @@ public class GraphQlWebSocketHandlerTests {
|
||||
void subscription() {
|
||||
TestWebSocketSession session = handle(Flux.just(
|
||||
toWebSocketMessage("{\"type\":\"connection_init\"}"),
|
||||
toWebSocketMessage(BookTestUtils.BOOK_SUBSCRIPTION)));
|
||||
toWebSocketMessage(BOOK_SUBSCRIPTION)));
|
||||
|
||||
BiConsumer<WebSocketMessage, String> bookPayloadAssertion = (message, bookId) ->
|
||||
assertThat(decode(message))
|
||||
.hasSize(3).containsEntry("id", BookTestUtils.SUBSCRIPTION_ID).containsEntry("type", "next")
|
||||
.hasSize(3).containsEntry("id", SUBSCRIPTION_ID).containsEntry("type", "next")
|
||||
.extractingByKey("payload", as(InstanceOfAssertFactories.map(String.class, Object.class)))
|
||||
.extractingByKey("data", as(InstanceOfAssertFactories.map(String.class, Object.class)))
|
||||
.extractingByKey("bookSearch", as(InstanceOfAssertFactories.map(String.class, Object.class)))
|
||||
@@ -97,7 +97,7 @@ public class GraphQlWebSocketHandlerTests {
|
||||
void unauthorizedWithoutMessageType() {
|
||||
TestWebSocketSession session = handle(Flux.just(
|
||||
toWebSocketMessage("{\"type\":\"connection_init\"}"),
|
||||
toWebSocketMessage("{\"id\":\"" + BookTestUtils.SUBSCRIPTION_ID + "\"}")));
|
||||
toWebSocketMessage("{\"id\":\"" + SUBSCRIPTION_ID + "\"}")));
|
||||
|
||||
StepVerifier.create(session.getOutput())
|
||||
.consumeNextWith((message) -> assertMessageType(message, "connection_ack"))
|
||||
@@ -168,7 +168,7 @@ public class GraphQlWebSocketHandlerTests {
|
||||
|
||||
@Test
|
||||
void unauthorizedWithoutConnectionInit() {
|
||||
TestWebSocketSession session = handle(Flux.just(toWebSocketMessage(BookTestUtils.BOOK_SUBSCRIPTION)));
|
||||
TestWebSocketSession session = handle(Flux.just(toWebSocketMessage(BOOK_SUBSCRIPTION)));
|
||||
|
||||
StepVerifier.create(session.getOutput()).verifyComplete();
|
||||
StepVerifier.create(session.closeStatus()).expectNext(new CloseStatus(4401, "Unauthorized")).verifyComplete();
|
||||
@@ -192,7 +192,7 @@ public class GraphQlWebSocketHandlerTests {
|
||||
@Test
|
||||
void connectionInitTimeout() {
|
||||
GraphQlWebSocketHandler handler = new GraphQlWebSocketHandler(
|
||||
BookTestUtils.initWebGraphQlHandler(), ServerCodecConfigurer.create(), Duration.ofMillis(50));
|
||||
initHandler(), ServerCodecConfigurer.create(), Duration.ofMillis(50));
|
||||
|
||||
TestWebSocketSession session = new TestWebSocketSession(Flux.empty());
|
||||
handler.handle(session).block();
|
||||
@@ -206,8 +206,8 @@ public class GraphQlWebSocketHandlerTests {
|
||||
void subscriptionExists() {
|
||||
Flux<WebSocketMessage> messageFlux = Flux.just(
|
||||
toWebSocketMessage("{\"type\":\"connection_init\"}"),
|
||||
toWebSocketMessage(BookTestUtils.BOOK_SUBSCRIPTION),
|
||||
toWebSocketMessage(BookTestUtils.BOOK_SUBSCRIPTION));
|
||||
toWebSocketMessage(BOOK_SUBSCRIPTION),
|
||||
toWebSocketMessage(BOOK_SUBSCRIPTION));
|
||||
|
||||
TestWebSocketSession session = handle(messageFlux, new ConsumeOneAndNeverCompleteInterceptor());
|
||||
|
||||
@@ -216,7 +216,7 @@ public class GraphQlWebSocketHandlerTests {
|
||||
session.getOutput().subscribe((message) -> messages.add(decode(message)));
|
||||
|
||||
StepVerifier.create(session.closeStatus())
|
||||
.expectNext(new CloseStatus(4409, "Subscriber for " + BookTestUtils.SUBSCRIPTION_ID + " already exists"))
|
||||
.expectNext(new CloseStatus(4409, "Subscriber for " + SUBSCRIPTION_ID + " already exists"))
|
||||
.verifyComplete();
|
||||
|
||||
assertThat(messages.size()).isEqualTo(2);
|
||||
@@ -228,18 +228,18 @@ public class GraphQlWebSocketHandlerTests {
|
||||
void clientCompletion() {
|
||||
Sinks.Many<WebSocketMessage> input = Sinks.many().unicast().onBackpressureBuffer();
|
||||
input.tryEmitNext(toWebSocketMessage("{\"type\":\"connection_init\"}"));
|
||||
input.tryEmitNext(toWebSocketMessage(BookTestUtils.BOOK_SUBSCRIPTION));
|
||||
input.tryEmitNext(toWebSocketMessage(BOOK_SUBSCRIPTION));
|
||||
|
||||
TestWebSocketSession session = handle(input.asFlux(), new ConsumeOneAndNeverCompleteInterceptor());
|
||||
|
||||
String completeMessage = "{\"id\":\"" + BookTestUtils.SUBSCRIPTION_ID + "\",\"type\":\"complete\"}";
|
||||
String completeMessage = "{\"id\":\"" + SUBSCRIPTION_ID + "\",\"type\":\"complete\"}";
|
||||
|
||||
StepVerifier.create(session.getOutput())
|
||||
.consumeNextWith((message) -> assertMessageType(message, "connection_ack"))
|
||||
.consumeNextWith((message) -> assertMessageType(message, "next"))
|
||||
.then(() -> input.tryEmitNext(toWebSocketMessage(completeMessage)))
|
||||
.as("Second subscription with same id is possible only if the first was properly removed")
|
||||
.then(() -> input.tryEmitNext(toWebSocketMessage(BookTestUtils.BOOK_SUBSCRIPTION)))
|
||||
.then(() -> input.tryEmitNext(toWebSocketMessage(BOOK_SUBSCRIPTION)))
|
||||
.consumeNextWith((message) -> assertMessageType(message, "next"))
|
||||
.then(() -> input.tryEmitNext(toWebSocketMessage(completeMessage)))
|
||||
.verifyTimeout(Duration.ofMillis(500));
|
||||
@@ -247,7 +247,7 @@ public class GraphQlWebSocketHandlerTests {
|
||||
|
||||
private TestWebSocketSession handle(Flux<WebSocketMessage> input, WebInterceptor... interceptors) {
|
||||
GraphQlWebSocketHandler handler = new GraphQlWebSocketHandler(
|
||||
BookTestUtils.initWebGraphQlHandler(interceptors),
|
||||
initHandler(interceptors),
|
||||
ServerCodecConfigurer.create(),
|
||||
Duration.ofSeconds(60));
|
||||
|
||||
@@ -271,7 +271,7 @@ public class GraphQlWebSocketHandlerTests {
|
||||
Map<String, Object> map = decode(message);
|
||||
assertThat(map).containsEntry("type", messageType);
|
||||
if (!messageType.equals("connection_ack")) {
|
||||
assertThat(map).containsEntry("id", BookTestUtils.SUBSCRIPTION_ID);
|
||||
assertThat(map).containsEntry("id", SUBSCRIPTION_ID);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,15 +23,10 @@ import java.util.Locale;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
|
||||
import graphql.schema.DataFetcher;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.context.i18n.LocaleContextHolder;
|
||||
import org.springframework.graphql.GraphQlService;
|
||||
import org.springframework.graphql.GraphQlTestUtils;
|
||||
import org.springframework.graphql.execution.ExecutionGraphQlService;
|
||||
import org.springframework.graphql.execution.GraphQlSource;
|
||||
import org.springframework.graphql.web.WebGraphQlHandler;
|
||||
import org.springframework.graphql.GraphQlSetup;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
@@ -54,8 +49,9 @@ public class GraphQlHttpHandlerTests {
|
||||
|
||||
@Test
|
||||
void locale() throws Exception {
|
||||
GraphQlHttpHandler handler = createHttpHandler(
|
||||
"type Query { greeting: String }", "Query", "greeting", (env) -> "Hello in " + env.getLocale());
|
||||
GraphQlHttpHandler handler = GraphQlSetup.schemaContent("type Query { greeting: String }")
|
||||
.queryFetcher("greeting", (env) -> "Hello in " + env.getLocale())
|
||||
.toHttpHandler();
|
||||
|
||||
MockHttpServletRequest servletRequest = new MockHttpServletRequest("POST", "/");
|
||||
servletRequest.setContentType("application/json");
|
||||
@@ -74,14 +70,6 @@ public class GraphQlHttpHandlerTests {
|
||||
}
|
||||
}
|
||||
|
||||
private GraphQlHttpHandler createHttpHandler(
|
||||
String schemaContent, String type, String field, DataFetcher<Object> dataFetcher) {
|
||||
|
||||
GraphQlSource source = GraphQlTestUtils.graphQlSource(schemaContent, type, field, dataFetcher).build();
|
||||
GraphQlService service = new ExecutionGraphQlService(source);
|
||||
return new GraphQlHttpHandler(WebGraphQlHandler.builder(service).build());
|
||||
}
|
||||
|
||||
private MockHttpServletResponse handleRequest(
|
||||
MockHttpServletRequest servletRequest, GraphQlHttpHandler handler) throws ServletException, IOException {
|
||||
|
||||
|
||||
@@ -29,11 +29,10 @@ import java.util.function.Consumer;
|
||||
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.graphql.web.BookTestUtils;
|
||||
import org.springframework.graphql.web.WebSocketHandlerTestSupport;
|
||||
import org.springframework.graphql.web.ConsumeOneAndNeverCompleteInterceptor;
|
||||
import org.springframework.graphql.web.WebInterceptor;
|
||||
import org.springframework.graphql.web.WebSocketInterceptor;
|
||||
@@ -51,7 +50,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
/**
|
||||
* Unit tests for {@link GraphQlWebSocketHandler}.
|
||||
*/
|
||||
public class GraphQlWebSocketHandlerTests {
|
||||
public class GraphQlWebSocketHandlerTests extends WebSocketHandlerTestSupport {
|
||||
|
||||
private static final HttpMessageConverter<?> converter = new MappingJackson2HttpMessageConverter();
|
||||
|
||||
@@ -63,12 +62,12 @@ public class GraphQlWebSocketHandlerTests {
|
||||
void query() throws Exception {
|
||||
handle(this.handler,
|
||||
new TextMessage("{\"type\":\"connection_init\"}"),
|
||||
new TextMessage(BookTestUtils.BOOK_QUERY));
|
||||
new TextMessage(BOOK_QUERY));
|
||||
|
||||
StepVerifier.create(this.session.getOutput())
|
||||
.consumeNextWith((message) -> assertMessageType(message, "connection_ack"))
|
||||
.consumeNextWith((message) -> assertThat(decode(message)).hasSize(3)
|
||||
.containsEntry("id", BookTestUtils.SUBSCRIPTION_ID).containsEntry("type", "next")
|
||||
.containsEntry("id", SUBSCRIPTION_ID).containsEntry("type", "next")
|
||||
.extractingByKey("payload", as(InstanceOfAssertFactories.map(String.class, Object.class)))
|
||||
.extractingByKey("data", as(InstanceOfAssertFactories.map(String.class, Object.class)))
|
||||
.extractingByKey("bookById", as(InstanceOfAssertFactories.map(String.class, Object.class)))
|
||||
@@ -80,12 +79,11 @@ public class GraphQlWebSocketHandlerTests {
|
||||
|
||||
@Test
|
||||
void subscription() throws Exception {
|
||||
handle(this.handler, new TextMessage("{\"type\":\"connection_init\"}"),
|
||||
new TextMessage(BookTestUtils.BOOK_SUBSCRIPTION));
|
||||
handle(this.handler, new TextMessage("{\"type\":\"connection_init\"}"), new TextMessage(BOOK_SUBSCRIPTION));
|
||||
|
||||
BiConsumer<WebSocketMessage<?>, String> bookPayloadAssertion = (message, bookId) ->
|
||||
assertThat(decode(message))
|
||||
.hasSize(3).containsEntry("id", BookTestUtils.SUBSCRIPTION_ID).containsEntry("type", "next")
|
||||
.hasSize(3).containsEntry("id", SUBSCRIPTION_ID).containsEntry("type", "next")
|
||||
.extractingByKey("payload", as(InstanceOfAssertFactories.map(String.class, Object.class)))
|
||||
.extractingByKey("data", as(InstanceOfAssertFactories.map(String.class, Object.class)))
|
||||
.extractingByKey("bookSearch", as(InstanceOfAssertFactories.map(String.class, Object.class)))
|
||||
@@ -104,7 +102,7 @@ public class GraphQlWebSocketHandlerTests {
|
||||
void unauthorizedWithoutMessageType() throws Exception {
|
||||
handle(this.handler,
|
||||
new TextMessage("{\"type\":\"connection_init\"}"),
|
||||
new TextMessage("{\"id\":\"" + BookTestUtils.SUBSCRIPTION_ID + "\"}"));
|
||||
new TextMessage("{\"id\":\"" + SUBSCRIPTION_ID + "\"}"));
|
||||
// No message type
|
||||
|
||||
StepVerifier.create(this.session.getOutput())
|
||||
@@ -173,7 +171,7 @@ public class GraphQlWebSocketHandlerTests {
|
||||
|
||||
@Test
|
||||
void unauthorizedWithoutConnectionInit() throws Exception {
|
||||
handle(this.handler, new TextMessage(BookTestUtils.BOOK_SUBSCRIPTION));
|
||||
handle(this.handler, new TextMessage(BOOK_SUBSCRIPTION));
|
||||
|
||||
StepVerifier.create(this.session.getOutput()).verifyComplete();
|
||||
assertThat(this.session.getCloseStatus()).isEqualTo(new CloseStatus(4401, "Unauthorized"));
|
||||
@@ -194,9 +192,7 @@ public class GraphQlWebSocketHandlerTests {
|
||||
|
||||
@Test
|
||||
void connectionInitTimeout() {
|
||||
GraphQlWebSocketHandler handler = new GraphQlWebSocketHandler(
|
||||
BookTestUtils.initWebGraphQlHandler(), converter, Duration.ofMillis(50));
|
||||
|
||||
GraphQlWebSocketHandler handler = new GraphQlWebSocketHandler(initHandler(), converter, Duration.ofMillis(50));
|
||||
handler.afterConnectionEstablished(this.session);
|
||||
|
||||
StepVerifier.create(this.session.closeStatus())
|
||||
@@ -208,15 +204,15 @@ public class GraphQlWebSocketHandlerTests {
|
||||
void subscriptionExists() throws Exception {
|
||||
handle(initWebSocketHandler(new ConsumeOneAndNeverCompleteInterceptor()),
|
||||
new TextMessage("{\"type\":\"connection_init\"}"),
|
||||
new TextMessage(BookTestUtils.BOOK_SUBSCRIPTION),
|
||||
new TextMessage(BookTestUtils.BOOK_SUBSCRIPTION));
|
||||
new TextMessage(BOOK_SUBSCRIPTION),
|
||||
new TextMessage(BOOK_SUBSCRIPTION));
|
||||
|
||||
// Collect messages until session closed
|
||||
List<Map<String, Object>> messages = new ArrayList<>();
|
||||
this.session.getOutput().subscribe((message) -> messages.add(decode(message)));
|
||||
|
||||
StepVerifier.create(this.session.closeStatus())
|
||||
.expectNext(new CloseStatus(4409, "Subscriber for " + BookTestUtils.SUBSCRIPTION_ID + " already exists"))
|
||||
.expectNext(new CloseStatus(4409, "Subscriber for " + SUBSCRIPTION_ID + " already exists"))
|
||||
.verifyComplete();
|
||||
|
||||
assertThat(messages.size()).isEqualTo(2);
|
||||
@@ -230,9 +226,9 @@ public class GraphQlWebSocketHandlerTests {
|
||||
|
||||
handle(handler,
|
||||
new TextMessage("{\"type\":\"connection_init\"}"),
|
||||
new TextMessage(BookTestUtils.BOOK_SUBSCRIPTION));
|
||||
new TextMessage(BOOK_SUBSCRIPTION));
|
||||
|
||||
String completeMessage = "{\"id\":\"" + BookTestUtils.SUBSCRIPTION_ID + "\",\"type\":\"complete\"}";
|
||||
String completeMessage = "{\"id\":\"" + SUBSCRIPTION_ID + "\",\"type\":\"complete\"}";
|
||||
Consumer<String> messageSender = (body) -> {
|
||||
try {
|
||||
handler.handleTextMessage(this.session, new TextMessage(body));
|
||||
@@ -247,7 +243,7 @@ public class GraphQlWebSocketHandlerTests {
|
||||
.consumeNextWith((message) -> assertMessageType(message, "next"))
|
||||
.then(() -> messageSender.accept(completeMessage))
|
||||
.as("Second subscription with same id is possible only if the first was properly removed")
|
||||
.then(() -> messageSender.accept(BookTestUtils.BOOK_SUBSCRIPTION))
|
||||
.then(() -> messageSender.accept(BOOK_SUBSCRIPTION))
|
||||
.consumeNextWith((message) -> assertMessageType(message, "next"))
|
||||
.then(() -> messageSender.accept(completeMessage))
|
||||
.verifyTimeout(Duration.ofMillis(500));
|
||||
@@ -262,8 +258,7 @@ public class GraphQlWebSocketHandlerTests {
|
||||
|
||||
private GraphQlWebSocketHandler initWebSocketHandler(WebInterceptor... interceptors) {
|
||||
try {
|
||||
return new GraphQlWebSocketHandler(
|
||||
BookTestUtils.initWebGraphQlHandler(interceptors), converter, Duration.ofSeconds(60));
|
||||
return new GraphQlWebSocketHandler(initHandler(interceptors), converter, Duration.ofSeconds(60));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
@@ -274,7 +269,7 @@ public class GraphQlWebSocketHandlerTests {
|
||||
Map<String, Object> map = decode(message, Map.class);
|
||||
assertThat(map).containsEntry("type", messageType);
|
||||
if (!messageType.equals("connection_ack")) {
|
||||
assertThat(map).containsEntry("id", BookTestUtils.SUBSCRIPTION_ID);
|
||||
assertThat(map).containsEntry("id", SUBSCRIPTION_ID);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user