diff --git a/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebFluxGraphQlAutoConfiguration.java b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebFluxGraphQlAutoConfiguration.java index 1a7ec1a2..2854c1b0 100644 --- a/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebFluxGraphQlAutoConfiguration.java +++ b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebFluxGraphQlAutoConfiguration.java @@ -45,13 +45,15 @@ import org.springframework.graphql.web.webflux.GraphQlWebSocketHandler; import org.springframework.http.MediaType; import org.springframework.http.codec.ServerCodecConfigurer; import org.springframework.web.reactive.HandlerMapping; -import org.springframework.web.reactive.function.server.RequestPredicates; import org.springframework.web.reactive.function.server.RouterFunction; import org.springframework.web.reactive.function.server.RouterFunctions; import org.springframework.web.reactive.function.server.ServerResponse; import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping; import org.springframework.web.reactive.socket.server.support.WebSocketUpgradeHandlerPredicate; +import static org.springframework.web.reactive.function.server.RequestPredicates.accept; +import static org.springframework.web.reactive.function.server.RequestPredicates.contentType; + /** * {@link EnableAutoConfiguration Auto-configuration} for enabling Spring GraphQL over * WebFlux. @@ -90,15 +92,18 @@ public class WebFluxGraphQlAutoConfiguration { if (logger.isInfoEnabled()) { logger.info("GraphQL endpoint HTTP POST " + path); } + // @formatter:off RouterFunctions.Builder builder = RouterFunctions.route() .GET(path, (req) -> ServerResponse.ok().bodyValue(resource)) - .POST(path, RequestPredicates.accept(MediaType.APPLICATION_JSON) - .and(RequestPredicates.contentType(MediaType.APPLICATION_JSON)), handler::handleRequest); + .POST(path, accept(MediaType.APPLICATION_JSON).and(contentType(MediaType.APPLICATION_JSON)), handler::handleRequest); if (properties.getSchema().getPrinter().isEnabled()) { SchemaPrinter printer = new SchemaPrinter(); - builder = builder.GET(path + properties.getSchema().getPrinter().getPath(), (req) -> ServerResponse.ok() - .contentType(MediaType.TEXT_PLAIN).bodyValue(printer.print(graphQlSource.schema()))); + builder = builder.GET(path + properties.getSchema().getPrinter().getPath(), + (req) -> ServerResponse.ok() + .contentType(MediaType.TEXT_PLAIN) + .bodyValue(printer.print(graphQlSource.schema()))); } + // @formatter:on return builder.build(); } diff --git a/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebMvcGraphQlAutoConfiguration.java b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebMvcGraphQlAutoConfiguration.java index 992a3a84..b1d30dee 100644 --- a/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebMvcGraphQlAutoConfiguration.java +++ b/graphql-spring-boot-starter/src/main/java/org/springframework/graphql/boot/WebMvcGraphQlAutoConfiguration.java @@ -50,7 +50,6 @@ import org.springframework.graphql.web.webmvc.GraphQlWebSocketHandler; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.web.servlet.HandlerMapping; -import org.springframework.web.servlet.function.RequestPredicates; import org.springframework.web.servlet.function.RouterFunction; import org.springframework.web.servlet.function.RouterFunctions; import org.springframework.web.servlet.function.ServerResponse; @@ -59,6 +58,9 @@ import org.springframework.web.socket.server.support.DefaultHandshakeHandler; import org.springframework.web.socket.server.support.WebSocketHandlerMapping; import org.springframework.web.socket.server.support.WebSocketHttpRequestHandler; +import static org.springframework.web.servlet.function.RequestPredicates.accept; +import static org.springframework.web.servlet.function.RequestPredicates.contentType; + /** * {@link EnableAutoConfiguration Auto-configuration} for enabling Spring GraphQL over * Spring MVC. @@ -100,14 +102,18 @@ public class WebMvcGraphQlAutoConfiguration { if (logger.isInfoEnabled()) { logger.info("GraphQL endpoint HTTP POST " + path); } - RouterFunctions.Builder builder = RouterFunctions.route().GET(path, (req) -> ServerResponse.ok().body(resource)) - .POST(path, RequestPredicates.contentType(MediaType.APPLICATION_JSON) - .and(RequestPredicates.accept(MediaType.APPLICATION_JSON)), handler::handleRequest); + // @formatter:off + RouterFunctions.Builder builder = RouterFunctions.route() + .GET(path, (req) -> ServerResponse.ok().body(resource)) + .POST(path, contentType(MediaType.APPLICATION_JSON).and(accept(MediaType.APPLICATION_JSON)), handler::handleRequest); if (properties.getSchema().getPrinter().isEnabled()) { SchemaPrinter printer = new SchemaPrinter(); - builder = builder.GET(path + properties.getSchema().getPrinter().getPath(), (req) -> ServerResponse.ok() - .contentType(MediaType.TEXT_PLAIN).body(printer.print(graphQlSource.schema()))); + builder = builder.GET(path + properties.getSchema().getPrinter().getPath(), + (req) -> ServerResponse.ok() + .contentType(MediaType.TEXT_PLAIN) + .body(printer.print(graphQlSource.schema()))); } + // @formatter:on return builder.build(); } @@ -121,9 +127,12 @@ public class WebMvcGraphQlAutoConfiguration { public GraphQlWebSocketHandler graphQlWebSocketHandler(WebGraphQlHandler webGraphQlHandler, GraphQlProperties properties, HttpMessageConverters converters) { + // @formatter:off HttpMessageConverter converter = converters.getConverters().stream() - .filter((candidate) -> candidate.canRead(Map.class, MediaType.APPLICATION_JSON)).findFirst() + .filter((candidate) -> candidate.canRead(Map.class, MediaType.APPLICATION_JSON)) + .findFirst() .orElseThrow(() -> new IllegalStateException("No JSON converter")); + // @formatter:on return new GraphQlWebSocketHandler(webGraphQlHandler, converter, properties.getWebsocket().getConnectionInitTimeout()); diff --git a/samples/webflux-security/src/main/java/io/spring/sample/graphql/SampleWiring.java b/samples/webflux-security/src/main/java/io/spring/sample/graphql/SampleWiring.java index 69fdf12d..e08d2709 100644 --- a/samples/webflux-security/src/main/java/io/spring/sample/graphql/SampleWiring.java +++ b/samples/webflux-security/src/main/java/io/spring/sample/graphql/SampleWiring.java @@ -5,7 +5,6 @@ import java.util.Map; import graphql.schema.idl.RuntimeWiring; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.graphql.boot.RuntimeWiringCustomizer; import org.springframework.stereotype.Component; diff --git a/samples/webflux-security/src/main/java/io/spring/sample/graphql/SecurityDataFetcherExceptionResolver.java b/samples/webflux-security/src/main/java/io/spring/sample/graphql/SecurityDataFetcherExceptionResolver.java index eb13ffb0..28d2f7b1 100644 --- a/samples/webflux-security/src/main/java/io/spring/sample/graphql/SecurityDataFetcherExceptionResolver.java +++ b/samples/webflux-security/src/main/java/io/spring/sample/graphql/SecurityDataFetcherExceptionResolver.java @@ -1,9 +1,13 @@ package io.spring.sample.graphql; -import graphql.ErrorClassification; +import java.util.Arrays; +import java.util.List; + import graphql.GraphQLError; import graphql.GraphqlErrorBuilder; import graphql.schema.DataFetchingEnvironment; +import reactor.core.publisher.Mono; + import org.springframework.graphql.execution.DataFetcherExceptionResolver; import org.springframework.graphql.execution.ErrorType; import org.springframework.security.access.AccessDeniedException; @@ -13,19 +17,18 @@ import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.security.core.context.SecurityContext; import org.springframework.stereotype.Component; -import reactor.core.publisher.Mono; -import java.util.Arrays; -import java.util.List; +// @formatter:off @Component public class SecurityDataFetcherExceptionResolver implements DataFetcherExceptionResolver { + private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl(); @Override public Mono> resolveException(Throwable exception, DataFetchingEnvironment environment) { if (exception instanceof AuthenticationException) { - + // TOTO: should this be empty ? } if (exception instanceof AccessDeniedException) { return ReactiveSecurityContextHolder.getContext() @@ -38,10 +41,19 @@ public class SecurityDataFetcherExceptionResolver implements DataFetcherExceptio } private Mono> unauthorized(DataFetchingEnvironment environment) { - return Mono.fromCallable(() -> Arrays.asList(GraphqlErrorBuilder.newError(environment).errorType(ErrorType.UNAUTHORIZED).message("Unauthorized").build())); + return Mono.fromCallable(() -> Arrays.asList( + GraphqlErrorBuilder.newError(environment) + .errorType(ErrorType.UNAUTHORIZED) + .message("Unauthorized") + .build())); } private Mono> forbidden(DataFetchingEnvironment environment) { - return Mono.fromCallable(() -> Arrays.asList(GraphqlErrorBuilder.newError(environment).errorType(ErrorType.FORBIDDEN).message("Forbidden").build())); + return Mono.fromCallable(() -> Arrays.asList( + GraphqlErrorBuilder.newError(environment) + .errorType(ErrorType.FORBIDDEN) + .message("Forbidden") + .build())); } + } diff --git a/samples/webflux-security/src/test/java/io/spring/sample/graphql/SampleApplicationTests.java b/samples/webflux-security/src/test/java/io/spring/sample/graphql/SampleApplicationTests.java index 060b09af..b3cd3bb5 100644 --- a/samples/webflux-security/src/test/java/io/spring/sample/graphql/SampleApplicationTests.java +++ b/samples/webflux-security/src/test/java/io/spring/sample/graphql/SampleApplicationTests.java @@ -1,7 +1,10 @@ package io.spring.sample.graphql; +import java.util.Collections; + import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.web.reactive.context.ReactiveWebApplicationContext; @@ -10,7 +13,7 @@ import org.springframework.security.test.web.reactive.server.SecurityMockServerC import org.springframework.test.web.reactive.server.WebTestClient; import org.springframework.web.reactive.function.client.ExchangeFilterFunctions; -import java.util.Collections; +// @formatter:off @SpringBootTest() class SampleApplicationTests { diff --git a/samples/webflux-websocket/src/main/java/io/spring/sample/graphql/SampleWiring.java b/samples/webflux-websocket/src/main/java/io/spring/sample/graphql/SampleWiring.java index fe99ff3d..a4c88d52 100644 --- a/samples/webflux-websocket/src/main/java/io/spring/sample/graphql/SampleWiring.java +++ b/samples/webflux-websocket/src/main/java/io/spring/sample/graphql/SampleWiring.java @@ -24,24 +24,20 @@ import org.springframework.stereotype.Component; @Component public class SampleWiring implements RuntimeWiringCustomizer { - private final DataRepository dataRepository; + private final DataRepository repository; public SampleWiring(@Autowired DataRepository dataRepository) { - this.dataRepository = dataRepository; + this.repository = dataRepository; } @Override - public void customize(RuntimeWiring.Builder builder) { - - builder.type("Query", typeBuilder -> typeBuilder.dataFetcher("greeting", this.dataRepository::getBasic)); - - builder.type("Query", typeBuilder -> typeBuilder.dataFetcher("greetingMono", this.dataRepository::getGreeting)); - - builder.type("Query", - typeBuilder -> typeBuilder.dataFetcher("greetingsFlux", this.dataRepository::getGreetings)); - - builder.type("Subscription", - typeBuilder -> typeBuilder.dataFetcher("greetings", this.dataRepository::getGreetingsStream)); + public void customize(RuntimeWiring.Builder wiringBuilder) { + // @formatter:off + wiringBuilder.type("Query", builder -> builder.dataFetcher("greeting", this.repository::getBasic)); + wiringBuilder.type("Query", builder -> builder.dataFetcher("greetingMono", this.repository::getGreeting)); + wiringBuilder.type("Query", builder -> builder.dataFetcher("greetingsFlux", this.repository::getGreetings)); + wiringBuilder.type("Subscription", builder -> builder.dataFetcher("greetings", this.repository::getGreetingsStream)); + // @formatter:on } } diff --git a/samples/webflux-websocket/src/test/java/io/spring/sample/graphql/QueryTests.java b/samples/webflux-websocket/src/test/java/io/spring/sample/graphql/QueryTests.java index 6b02a7a7..972c3386 100644 --- a/samples/webflux-websocket/src/test/java/io/spring/sample/graphql/QueryTests.java +++ b/samples/webflux-websocket/src/test/java/io/spring/sample/graphql/QueryTests.java @@ -21,8 +21,10 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.graphql.web.WebGraphQlHandler; import org.springframework.graphql.test.tester.GraphQlTester; +import org.springframework.graphql.web.WebGraphQlHandler; + +// @formatter:off /** * GraphQL query tests directly via {@link GraphQL}. @@ -34,19 +36,25 @@ public class QueryTests { @BeforeEach public void setUp(@Autowired WebGraphQlHandler handler) { - this.graphQlTester = GraphQlTester - .create(webInput -> handler.handle(webInput).contextWrite(context -> context.put("name", "James"))); + this.graphQlTester = GraphQlTester.create(webInput -> + handler.handle(webInput).contextWrite(context -> context.put("name", "James"))); } @Test void greetingMono() { - this.graphQlTester.query("{greetingMono}").execute().path("greetingMono").entity(String.class) + this.graphQlTester.query("{greetingMono}") + .execute() + .path("greetingMono") + .entity(String.class) .isEqualTo("Hello James"); } @Test void greetingsFlux() { - this.graphQlTester.query("{greetingsFlux}").execute().path("greetingsFlux").entityList(String.class) + this.graphQlTester.query("{greetingsFlux}") + .execute() + .path("greetingsFlux") + .entityList(String.class) .containsExactly("Hi James", "Bonjour James", "Hola James", "Ciao James", "Zdravo James"); } diff --git a/samples/webflux-websocket/src/test/java/io/spring/sample/graphql/SubscriptionTests.java b/samples/webflux-websocket/src/test/java/io/spring/sample/graphql/SubscriptionTests.java index cefd9613..69336e63 100644 --- a/samples/webflux-websocket/src/test/java/io/spring/sample/graphql/SubscriptionTests.java +++ b/samples/webflux-websocket/src/test/java/io/spring/sample/graphql/SubscriptionTests.java @@ -23,8 +23,10 @@ import reactor.test.StepVerifier; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.graphql.web.WebGraphQlHandler; import org.springframework.graphql.test.tester.GraphQlTester; +import org.springframework.graphql.web.WebGraphQlHandler; + +// @formatter:off /** * GraphQL subscription tests directly via {@link GraphQL}. @@ -36,29 +38,36 @@ public class SubscriptionTests { @BeforeEach public void setUp(@Autowired WebGraphQlHandler handler) { - this.graphQlTester = GraphQlTester - .create(webInput -> handler.handle(webInput).contextWrite(context -> context.put("name", "James"))); + this.graphQlTester = GraphQlTester.create(webInput -> + handler.handle(webInput).contextWrite(context -> context.put("name", "James"))); } @Test void subscriptionWithEntityPath() { - String query = "subscription { greetings }"; + Flux result = this.graphQlTester.query("subscription { greetings }") + .executeSubscription() + .toFlux("greetings", String.class); - Flux result = this.graphQlTester.query(query).executeSubscription().toFlux("greetings", String.class); - - StepVerifier.create(result).expectNext("Hi James").expectNext("Bonjour James").expectNext("Hola James") - .expectNext("Ciao James").expectNext("Zdravo James").verifyComplete(); + StepVerifier.create(result) + .expectNext("Hi James") + .expectNext("Bonjour James") + .expectNext("Hola James") + .expectNext("Ciao James") + .expectNext("Zdravo James") + .verifyComplete(); } @Test void subscriptionWithResponseSpec() { - String query = "subscription { greetings }"; + Flux result = this.graphQlTester.query("subscription { greetings }") + .executeSubscription() + .toFlux(); - Flux result = this.graphQlTester.query(query).executeSubscription().toFlux(); - - StepVerifier.create(result).consumeNextWith(spec -> spec.path("greetings").valueExists()) + StepVerifier.create(result) + .consumeNextWith(spec -> spec.path("greetings").valueExists()) .consumeNextWith(spec -> spec.path("greetings").matchesJson("\"Bonjour James\"")) - .consumeNextWith(spec -> spec.path("greetings").matchesJson("\"Hola James\"")).expectNextCount(2) + .consumeNextWith(spec -> spec.path("greetings").matchesJson("\"Hola James\"")) + .expectNextCount(2) .verifyComplete(); } diff --git a/samples/webmvc-http/src/main/java/io/spring/sample/graphql/project/ProjectStatus.java b/samples/webmvc-http/src/main/java/io/spring/sample/graphql/project/ProjectStatus.java index 5a1306bc..9d57f707 100644 --- a/samples/webmvc-http/src/main/java/io/spring/sample/graphql/project/ProjectStatus.java +++ b/samples/webmvc-http/src/main/java/io/spring/sample/graphql/project/ProjectStatus.java @@ -10,8 +10,12 @@ public enum ProjectStatus { @JsonCreator public static ProjectStatus fromName(String name) { - return Arrays.stream(ProjectStatus.values()).filter(type -> type.name().equals(name)).findFirst() + // @formatter:off + return Arrays.stream(ProjectStatus.values()) + .filter(type -> type.name().equals(name)) + .findFirst() .orElse(ProjectStatus.ACTIVE); + // @formatter:on } } diff --git a/samples/webmvc-http/src/main/java/io/spring/sample/graphql/project/ReleaseStatus.java b/samples/webmvc-http/src/main/java/io/spring/sample/graphql/project/ReleaseStatus.java index a31de550..f4264324 100644 --- a/samples/webmvc-http/src/main/java/io/spring/sample/graphql/project/ReleaseStatus.java +++ b/samples/webmvc-http/src/main/java/io/spring/sample/graphql/project/ReleaseStatus.java @@ -10,8 +10,12 @@ public enum ReleaseStatus { @JsonCreator public static ReleaseStatus fromName(String name) { - return Arrays.stream(ReleaseStatus.values()).filter(type -> type.name().equals(name)).findFirst() + // @formatter:off + return Arrays.stream(ReleaseStatus.values()) + .filter(type -> type.name().equals(name)) + .findFirst() .orElse(ReleaseStatus.GENERAL_AVAILABILITY); + // @formatter:on } } diff --git a/samples/webmvc-http/src/main/java/io/spring/sample/graphql/project/SpringProjectsClient.java b/samples/webmvc-http/src/main/java/io/spring/sample/graphql/project/SpringProjectsClient.java index e625c83e..58695610 100644 --- a/samples/webmvc-http/src/main/java/io/spring/sample/graphql/project/SpringProjectsClient.java +++ b/samples/webmvc-http/src/main/java/io/spring/sample/graphql/project/SpringProjectsClient.java @@ -2,10 +2,7 @@ package io.spring.sample.graphql.project; import java.net.URI; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; import java.util.List; -import java.util.Optional; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.hateoas.CollectionModel; @@ -13,27 +10,35 @@ import org.springframework.hateoas.MediaTypes; import org.springframework.hateoas.client.Hop; import org.springframework.hateoas.client.Traverson; import org.springframework.hateoas.server.core.TypeReferences; +import org.springframework.http.converter.HttpMessageConverter; import org.springframework.stereotype.Component; import org.springframework.web.client.RestTemplate; @Component public class SpringProjectsClient { - private static final TypeReferences.CollectionModelType releaseCollection = new TypeReferences.CollectionModelType() { - }; + // @formatter:off + + private static final TypeReferences.CollectionModelType releaseCollection = + new TypeReferences.CollectionModelType() {}; + + // @formatter:on private final Traverson traverson; public SpringProjectsClient(RestTemplateBuilder builder) { - RestTemplate restTemplate = builder - .messageConverters(Traverson.getDefaultMessageConverters(MediaTypes.HAL_JSON)).build(); + List> converters = Traverson.getDefaultMessageConverters(MediaTypes.HAL_JSON); + RestTemplate restTemplate = builder.messageConverters(converters).build(); this.traverson = new Traverson(URI.create("https://spring.io/api/"), MediaTypes.HAL_JSON); this.traverson.setRestOperations(restTemplate); } public Project fetchProject(String projectSlug) { - return this.traverson.follow("projects").follow(Hop.rel("project").withParameter("id", projectSlug)) + // @formatter:off + return this.traverson.follow("projects") + .follow(Hop.rel("project").withParameter("id", projectSlug)) .toObject(Project.class); + // @formatter:on } public List fetchProjectReleases(String projectSlug) { diff --git a/samples/webmvc-http/src/main/java/io/spring/sample/graphql/repository/ArtifactRepositoriesInitializer.java b/samples/webmvc-http/src/main/java/io/spring/sample/graphql/repository/ArtifactRepositoriesInitializer.java index 02370662..01cace4e 100644 --- a/samples/webmvc-http/src/main/java/io/spring/sample/graphql/repository/ArtifactRepositoriesInitializer.java +++ b/samples/webmvc-http/src/main/java/io/spring/sample/graphql/repository/ArtifactRepositoriesInitializer.java @@ -18,12 +18,12 @@ public class ArtifactRepositoriesInitializer implements ApplicationRunner { @Override public void run(ApplicationArguments args) throws Exception { + // @formatter:off List repositoryList = Arrays.asList( new ArtifactRepository("spring-releases", "Spring Releases", "https://repo.spring.io/libs-releases"), - new ArtifactRepository("spring-milestones", "Spring Milestones", - "https://repo.spring.io/libs-milestones"), - new ArtifactRepository("spring-snapshots", "Spring Snapshots", - "https://repo.spring.io/libs-snapshots")); + new ArtifactRepository("spring-milestones", "Spring Milestones", "https://repo.spring.io/libs-milestones"), + new ArtifactRepository("spring-snapshots", "Spring Snapshots", "https://repo.spring.io/libs-snapshots")); + // @formatter:on repositories.saveAll(repositoryList); } diff --git a/samples/webmvc-http/src/test/java/io/spring/sample/graphql/project/MockMvcGraphQlTests.java b/samples/webmvc-http/src/test/java/io/spring/sample/graphql/project/MockMvcGraphQlTests.java index 5298957b..40152676 100644 --- a/samples/webmvc-http/src/test/java/io/spring/sample/graphql/project/MockMvcGraphQlTests.java +++ b/samples/webmvc-http/src/test/java/io/spring/sample/graphql/project/MockMvcGraphQlTests.java @@ -26,6 +26,8 @@ import org.springframework.test.web.servlet.MockMvc; import static org.assertj.core.api.Assertions.assertThat; +// @formatter:off + /** * GraphQL requests via {@link GraphQlTester} connecting to {@link MockMvc}. */ @@ -39,28 +41,50 @@ public class MockMvcGraphQlTests { @Test void jsonPath() { - String query = "{" + " project(slug:\"spring-framework\") {" + " releases {" + " version" + " }" - + " }" + "}"; + String query = "{" + + " project(slug:\"spring-framework\") {" + + " releases {" + + " version" + + " }"+ + " }" + + "}"; - this.graphQlTester.query(query).execute().path("project.releases[*].version").entityList(String.class) + this.graphQlTester.query(query) + .execute() + .path("project.releases[*].version") + .entityList(String.class) .hasSizeGreaterThan(1); } @Test void jsonContent() { - String query = "{" + " project(slug:\"spring-framework\") {" + " repositoryUrl" + " }" + "}"; + String query = "{" + + " project(slug:\"spring-framework\") {" + + " repositoryUrl" + + " }" + + "}"; - this.graphQlTester.query(query).execute().path("project") + this.graphQlTester.query(query) + .execute() + .path("project") .matchesJson("{\"repositoryUrl\":\"http://github.com/spring-projects/spring-framework\"}"); } @Test void decodedResponse() { - String query = "{" + " project(slug:\"spring-framework\") {" + " releases {" + " version" + " }" - + " }" + "}"; + String query = "{" + + " project(slug:\"spring-framework\") {" + + " releases {" + + " version" + + " }" + + " }" + + "}"; - this.graphQlTester.query(query).execute().path("project").entity(Project.class) + this.graphQlTester.query(query) + .execute() + .path("project") + .entity(Project.class) .satisfies(project -> assertThat(project.getReleases()).hasSizeGreaterThan(1)); } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/execution/ExceptionResolversExceptionHandler.java b/spring-graphql/src/main/java/org/springframework/graphql/execution/ExceptionResolversExceptionHandler.java index 08d22f22..502d3fda 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/execution/ExceptionResolversExceptionHandler.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/execution/ExceptionResolversExceptionHandler.java @@ -43,7 +43,7 @@ import org.springframework.web.client.ExtractingResponseErrorHandler; */ class ExceptionResolversExceptionHandler implements DataFetcherExceptionHandler { - private static Log logger = LogFactory.getLog(ExtractingResponseErrorHandler.class); + private static final Log logger = LogFactory.getLog(ExtractingResponseErrorHandler.class); private final List resolvers; @@ -63,17 +63,23 @@ class ExceptionResolversExceptionHandler implements DataFetcherExceptionHandler return invokeChain(exception, parameters.getDataFetchingEnvironment()); } - @SuppressWarnings("ConstantConditions") + // @formatter:off + DataFetcherExceptionHandlerResult invokeChain(Throwable ex, DataFetchingEnvironment env) { // For now we have to block: // https://github.com/graphql-java/graphql-java/issues/2356 try { - return Flux.fromIterable(this.resolvers).flatMap((resolver) -> resolver.resolveException(ex, env)).next() + return Flux.fromIterable(this.resolvers) + .flatMap((resolver) -> resolver.resolveException(ex, env)) + .next() .map((errors) -> DataFetcherExceptionHandlerResult.newResult().errors(errors).build()) - .switchIfEmpty(Mono.fromCallable(() -> applyDefaultHandling(ex, env))).contextWrite((context) -> { + .switchIfEmpty(Mono.fromCallable(() -> applyDefaultHandling(ex, env))) + .contextWrite((context) -> { ContextView contextView = ContextManager.getReactorContext(env); return (contextView.isEmpty() ? context : context.putAll(contextView)); - }).toFuture().get(); + }) + .toFuture() + .get(); } catch (Exception ex2) { if (logger.isWarnEnabled()) { @@ -84,9 +90,13 @@ class ExceptionResolversExceptionHandler implements DataFetcherExceptionHandler } private DataFetcherExceptionHandlerResult applyDefaultHandling(Throwable ex, DataFetchingEnvironment env) { - GraphQLError error = GraphqlErrorBuilder.newError(env).message(ex.getMessage()) - .errorType(ErrorType.INTERNAL_ERROR).build(); + GraphQLError error = GraphqlErrorBuilder.newError(env) + .message(ex.getMessage()) + .errorType(ErrorType.INTERNAL_ERROR) + .build(); return DataFetcherExceptionHandlerResult.newResult(error).build(); } + // @formatter:on + } diff --git a/spring-graphql/src/main/java/org/springframework/graphql/web/DefaultWebGraphQlHandlerBuilder.java b/spring-graphql/src/main/java/org/springframework/graphql/web/DefaultWebGraphQlHandlerBuilder.java index 26ca35be..2de97b3e 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/web/DefaultWebGraphQlHandlerBuilder.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/web/DefaultWebGraphQlHandlerBuilder.java @@ -90,9 +90,12 @@ class DefaultWebGraphQlHandlerBuilder implements WebGraphQlHandler.Builder { return this.service.execute(executionInput).map((result) -> new WebOutput(webInput, result)); }; - WebGraphQlHandler interceptionChain = interceptorsToUse.stream().reduce(WebInterceptor::andThen) + // @formatter:off + WebGraphQlHandler interceptionChain = interceptorsToUse.stream() + .reduce(WebInterceptor::andThen) .map((interceptor) -> (WebGraphQlHandler) (input) -> interceptor.intercept(input, targetHandler)) .orElse(targetHandler); + // @formatter:on return (CollectionUtils.isEmpty(this.accessors) ? interceptionChain : new ThreadLocalExtractingHandler(interceptionChain, ThreadLocalAccessor.composite(this.accessors))); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/web/webflux/GraphQlHttpHandler.java b/spring-graphql/src/main/java/org/springframework/graphql/web/webflux/GraphQlHttpHandler.java index e9afac7c..63683f1b 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/web/webflux/GraphQlHttpHandler.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/web/webflux/GraphQlHttpHandler.java @@ -39,8 +39,10 @@ public class GraphQlHttpHandler { private static final Log logger = LogFactory.getLog(GraphQlHttpHandler.class); - private static final ParameterizedTypeReference> MAP_PARAMETERIZED_TYPE_REF = new ParameterizedTypeReference>() { - }; + // @formatter:off + private static final ParameterizedTypeReference> MAP_PARAMETERIZED_TYPE_REF = + new ParameterizedTypeReference>() {}; + // @formatter:on private final WebGraphQlHandler graphQlHandler; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/web/webflux/GraphQlWebSocketHandler.java b/spring-graphql/src/main/java/org/springframework/graphql/web/webflux/GraphQlWebSocketHandler.java index 90adb3bf..9cc81513 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/web/webflux/GraphQlWebSocketHandler.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/web/webflux/GraphQlWebSocketHandler.java @@ -69,12 +69,15 @@ public class GraphQlWebSocketHandler implements WebSocketHandler { private static final Log logger = LogFactory.getLog(GraphQlWebSocketHandler.class); - private static final List SUB_PROTOCOL_LIST = Arrays.asList("graphql-transport-ws", - "subscriptions-transport-ws"); + // @formatter:off - static final ResolvableType MAP_RESOLVABLE_TYPE = ResolvableType - .forType(new ParameterizedTypeReference>() { - }); + private static final List SUB_PROTOCOL_LIST = + Arrays.asList("graphql-transport-ws", "subscriptions-transport-ws"); + + static final ResolvableType MAP_RESOLVABLE_TYPE = + ResolvableType.forType(new ParameterizedTypeReference>() {}); + + // @formatter:off private final WebGraphQlHandler graphQlHandler; @@ -101,20 +104,26 @@ public class GraphQlWebSocketHandler implements WebSocketHandler { this.initTimeoutDuration = connectionInitTimeout; } + // @formatter:off + private static Decoder initDecoder(ServerCodecConfigurer configurer) { return configurer.getReaders().stream() .filter((reader) -> reader.canRead(MAP_RESOLVABLE_TYPE, MediaType.APPLICATION_JSON)) - .map((reader) -> ((DecoderHttpMessageReader) reader).getDecoder()).findFirst() + .map((reader) -> ((DecoderHttpMessageReader) reader).getDecoder()) + .findFirst() .orElseThrow(() -> new IllegalArgumentException("No JSON Decoder")); } private static Encoder initEncoder(ServerCodecConfigurer configurer) { return configurer.getWriters().stream() .filter((writer) -> writer.canWrite(MAP_RESOLVABLE_TYPE, MediaType.APPLICATION_JSON)) - .map((writer) -> ((EncoderHttpMessageWriter) writer).getEncoder()).findFirst() + .map((writer) -> ((EncoderHttpMessageWriter) writer).getEncoder()) + .findFirst() .orElseThrow(() -> new IllegalArgumentException("No JSON Encoder")); } + // @formatter:on + @Override public List getSubProtocols() { return SUB_PROTOCOL_LIST; @@ -135,8 +144,16 @@ public class GraphQlWebSocketHandler implements WebSocketHandler { AtomicBoolean connectionInitProcessed = new AtomicBoolean(); Map subscriptions = new ConcurrentHashMap<>(); - Mono.delay(this.initTimeoutDuration).then(Mono.defer(() -> connectionInitProcessed.compareAndSet(false, true) - ? session.close(GraphQlStatus.INIT_TIMEOUT_STATUS) : Mono.empty())).subscribe(); + // @formatter:off + + Mono.delay(this.initTimeoutDuration) + .then(Mono.defer(() -> + connectionInitProcessed.compareAndSet(false, true) ? + session.close(GraphQlStatus.INIT_TIMEOUT_STATUS) : + Mono.empty())) + .subscribe(); + + // @formatter:on return session.send(session.receive().flatMap((message) -> { Map map = decode(message); @@ -192,6 +209,8 @@ public class GraphQlWebSocketHandler implements WebSocketHandler { return payload; } + // @formatter:off + @SuppressWarnings("unchecked") private Flux handleWebOutput(WebSocketSession session, String id, Map subscriptions, WebOutput output) { @@ -205,35 +224,42 @@ public class GraphQlWebSocketHandler implements WebSocketHandler { Flux outputFlux; if (output.getData() instanceof Publisher) { // Subscription - outputFlux = Flux.from((Publisher) output.getData()).doOnSubscribe((subscription) -> { - Subscription previous = subscriptions.putIfAbsent(id, subscription); - if (previous != null) { - throw new SubscriptionExistsException(); - } - }); + outputFlux = Flux.from((Publisher) output.getData()) + .doOnSubscribe((subscription) -> { + Subscription previous = subscriptions.putIfAbsent(id, subscription); + if (previous != null) { + throw new SubscriptionExistsException(); + } + }); } else { // Single response operation (query or mutation) - outputFlux = (CollectionUtils.isEmpty(output.getErrors()) ? Flux.just(output) - : Flux.error(new IllegalStateException("Execution failed: " + output.getErrors()))); + outputFlux = (CollectionUtils.isEmpty(output.getErrors()) ? Flux.just(output) : + Flux.error(new IllegalStateException("Execution failed: " + output.getErrors()))); } - return outputFlux.map((result) -> { - Map dataMap = result.toSpecification(); - return encode(session, id, MessageType.NEXT, dataMap); - }).concatWith(Mono.fromCallable(() -> encode(session, id, MessageType.COMPLETE, null))).onErrorResume((ex) -> { - if (ex instanceof SubscriptionExistsException) { - CloseStatus status = new CloseStatus(4409, "Subscriber for " + id + " already exists"); - return GraphQlStatus.close(session, status); - } - ErrorType errorType = ErrorType.DataFetchingException; - String message = ex.getMessage(); - Map errorMap = GraphqlErrorBuilder.newError().errorType(errorType).message(message).build() - .toSpecification(); - return Mono.just(encode(session, id, MessageType.ERROR, errorMap)); - }); + return outputFlux + .map((result) -> { + Map dataMap = result.toSpecification(); + return encode(session, id, MessageType.NEXT, dataMap); + }) + .concatWith(Mono.fromCallable(() -> encode(session, id, MessageType.COMPLETE, null))) + .onErrorResume((ex) -> { + if (ex instanceof SubscriptionExistsException) { + CloseStatus status = new CloseStatus(4409, "Subscriber for " + id + " already exists"); + return GraphQlStatus.close(session, status); + } + Map errorMap = GraphqlErrorBuilder.newError() + .errorType(ErrorType.DataFetchingException) + .message(ex.getMessage()) + .build() + .toSpecification(); + return Mono.just(encode(session, id, MessageType.ERROR, errorMap)); + }); } + // @formatter:on + @SuppressWarnings("unchecked") private WebSocketMessage encode(WebSocketSession session, @Nullable String id, MessageType messageType, @Nullable Object payload) { @@ -255,8 +281,16 @@ public class GraphQlWebSocketHandler implements WebSocketHandler { private enum MessageType { - CONNECTION_INIT("connection_init"), CONNECTION_ACK("connection_ack"), SUBSCRIBE("subscribe"), NEXT( - "next"), ERROR("error"), COMPLETE("complete"); + // @formatter:off + + CONNECTION_INIT("connection_init"), + CONNECTION_ACK("connection_ack"), + SUBSCRIBE("subscribe"), + NEXT("next"), + ERROR("error"), + COMPLETE("complete"); + + // @formatter:on private static final Map messageTypes = new HashMap<>(6); @@ -285,14 +319,17 @@ public class GraphQlWebSocketHandler implements WebSocketHandler { private static class GraphQlStatus { + // @formatter:off + static final CloseStatus INVALID_MESSAGE_STATUS = new CloseStatus(4400, "Invalid message"); static final CloseStatus UNAUTHORIZED_STATUS = new CloseStatus(4401, "Unauthorized"); static final CloseStatus INIT_TIMEOUT_STATUS = new CloseStatus(4408, "Connection initialisation timeout"); - static final CloseStatus TOO_MANY_INIT_REQUESTS_STATUS = new CloseStatus(4429, - "Too many initialisation requests"); + static final CloseStatus TOO_MANY_INIT_REQUESTS_STATUS = new CloseStatus(4429, "Too many initialisation requests"); + + // @formatter:on static Flux close(WebSocketSession session, CloseStatus status) { return session.close(status).thenMany(Mono.empty()); diff --git a/spring-graphql/src/main/java/org/springframework/graphql/web/webmvc/GraphQlHttpHandler.java b/spring-graphql/src/main/java/org/springframework/graphql/web/webmvc/GraphQlHttpHandler.java index 0e782e39..3f4cc9ea 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/web/webmvc/GraphQlHttpHandler.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/web/webmvc/GraphQlHttpHandler.java @@ -46,8 +46,10 @@ public class GraphQlHttpHandler { private static final Log logger = LogFactory.getLog(GraphQlHttpHandler.class); - private static final ParameterizedTypeReference> MAP_PARAMETERIZED_TYPE_REF = new ParameterizedTypeReference>() { - }; + // @formatter:off + private static final ParameterizedTypeReference> MAP_PARAMETERIZED_TYPE_REF = + new ParameterizedTypeReference>() {}; + // @formatter:on private final WebGraphQlHandler graphQlHandler; diff --git a/spring-graphql/src/main/java/org/springframework/graphql/web/webmvc/GraphQlWebSocketHandler.java b/spring-graphql/src/main/java/org/springframework/graphql/web/webmvc/GraphQlWebSocketHandler.java index c5b51d39..0283c8a5 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/web/webmvc/GraphQlWebSocketHandler.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/web/webmvc/GraphQlWebSocketHandler.java @@ -72,8 +72,12 @@ public class GraphQlWebSocketHandler extends TextWebSocketHandler implements Sub private static final Log logger = LogFactory.getLog(GraphQlWebSocketHandler.class); - private static final List SUB_PROTOCOL_LIST = Arrays.asList("graphql-transport-ws", - "subscriptions-transport-ws"); + // @formatter:off + + private static final List SUB_PROTOCOL_LIST = + Arrays.asList("graphql-transport-ws", "subscriptions-transport-ws"); + + // @formatter:on private final WebGraphQlHandler graphQlHandler; @@ -119,11 +123,18 @@ public class GraphQlWebSocketHandler extends TextWebSocketHandler implements Sub SessionState sessionState = new SessionState(session.getId()); this.sessionInfoMap.put(session.getId(), sessionState); - Mono.delay(this.initTimeoutDuration).then(Mono.fromRunnable(() -> { - if (sessionState.isConnectionInitNotProcessed()) { - GraphQlStatus.closeSession(session, GraphQlStatus.INIT_TIMEOUT_STATUS); - } - })).subscribe(); + // @formatter:off + + Mono.delay(this.initTimeoutDuration) + .then(Mono.fromRunnable(() -> { + if (sessionState.isConnectionInitNotProcessed()) { + GraphQlStatus.closeSession(session, GraphQlStatus.INIT_TIMEOUT_STATUS); + } + })) + .subscribe(); + + // @formatter:on + } @Override @@ -154,10 +165,12 @@ public class GraphQlWebSocketHandler extends TextWebSocketHandler implements Sub if (logger.isDebugEnabled()) { logger.debug("Executing: " + input); } - this.graphQlHandler.handle(input).flatMapMany((output) -> handleWebOutput(session, input.getId(), output)) - .publishOn(sessionState.getScheduler()) // Serial blocking send via - // single thread + // @formatter:off + this.graphQlHandler.handle(input) + .flatMapMany((output) -> handleWebOutput(session, input.getId(), output)) + .publishOn(sessionState.getScheduler()) // Serial blocking send via single thread .subscribe(new SendMessageSubscriber(id, session, sessionState)); + // @formatter:on return; case COMPLETE: if (id != null) { @@ -199,6 +212,8 @@ public class GraphQlWebSocketHandler extends TextWebSocketHandler implements Sub return info; } + // @formatter:off + @SuppressWarnings("unchecked") private Flux handleWebOutput(WebSocketSession session, String id, WebOutput output) { if (logger.isDebugEnabled()) { @@ -209,12 +224,13 @@ public class GraphQlWebSocketHandler extends TextWebSocketHandler implements Sub Flux outputFlux; if (output.getData() instanceof Publisher) { // Subscription - outputFlux = Flux.from((Publisher) output.getData()).doOnSubscribe((subscription) -> { - Subscription prev = getSessionInfo(session).getSubscriptions().putIfAbsent(id, subscription); - if (prev != null) { - throw new SubscriptionExistsException(); - } - }); + outputFlux = Flux.from((Publisher) output.getData()) + .doOnSubscribe((subscription) -> { + Subscription prev = getSessionInfo(session).getSubscriptions().putIfAbsent(id, subscription); + if (prev != null) { + throw new SubscriptionExistsException(); + } + }); } else { // Single response operation (query or mutation) @@ -222,23 +238,28 @@ public class GraphQlWebSocketHandler extends TextWebSocketHandler implements Sub : Flux.error(new IllegalStateException("Execution failed: " + output.getErrors()))); } - return outputFlux.map((result) -> { - Map dataMap = result.toSpecification(); - return encode(id, MessageType.NEXT, dataMap); - }).concatWith(Mono.fromCallable(() -> encode(id, MessageType.COMPLETE, null))).onErrorResume((ex) -> { - if (ex instanceof SubscriptionExistsException) { - CloseStatus status = new CloseStatus(4409, "Subscriber for " + id + " already exists"); - GraphQlStatus.closeSession(session, status); - return Flux.empty(); - } - ErrorType errorType = ErrorType.DataFetchingException; - String message = ex.getMessage(); - Map errorMap = GraphqlErrorBuilder.newError().errorType(errorType).message(message).build() - .toSpecification(); - return Mono.just(encode(id, MessageType.ERROR, errorMap)); - }); + return outputFlux + .map((result) -> { + Map dataMap = result.toSpecification(); + return encode(id, MessageType.NEXT, dataMap); + }) + .concatWith(Mono.fromCallable(() -> encode(id, MessageType.COMPLETE, null))) + .onErrorResume((ex) -> { + if (ex instanceof SubscriptionExistsException) { + CloseStatus status = new CloseStatus(4409, "Subscriber for " + id + " already exists"); + GraphQlStatus.closeSession(session, status); + return Flux.empty(); + } + ErrorType errorType = ErrorType.DataFetchingException; + String message = ex.getMessage(); + Map errorMap = GraphqlErrorBuilder.newError().errorType(errorType).message(message).build() + .toSpecification(); + return Mono.just(encode(id, MessageType.ERROR, errorMap)); + }); } + // @formatter:on + @SuppressWarnings("unchecked") private TextMessage encode(@Nullable String id, MessageType messageType, @Nullable Object payload) { Map payloadMap = new HashMap<>(3); @@ -282,8 +303,16 @@ public class GraphQlWebSocketHandler extends TextWebSocketHandler implements Sub private enum MessageType { - CONNECTION_INIT("connection_init"), CONNECTION_ACK("connection_ack"), SUBSCRIBE("subscribe"), NEXT( - "next"), ERROR("error"), COMPLETE("complete"); + // @formatter:off + + CONNECTION_INIT("connection_init"), + CONNECTION_ACK("connection_ack"), + SUBSCRIBE("subscribe"), + NEXT("next"), + ERROR("error"), + COMPLETE("complete"); + + // @formatter:on private static final Map messageTypes = new HashMap<>(6); @@ -312,15 +341,17 @@ public class GraphQlWebSocketHandler extends TextWebSocketHandler implements Sub private static class GraphQlStatus { + // @formatter:off + private static final CloseStatus INVALID_MESSAGE_STATUS = new CloseStatus(4400, "Invalid message"); private static final CloseStatus UNAUTHORIZED_STATUS = new CloseStatus(4401, "Unauthorized"); - private static final CloseStatus INIT_TIMEOUT_STATUS = new CloseStatus(4408, - "Connection initialisation timeout"); + private static final CloseStatus INIT_TIMEOUT_STATUS = new CloseStatus(4408, "Connection initialisation timeout"); - private static final CloseStatus TOO_MANY_INIT_REQUESTS_STATUS = new CloseStatus(4429, - "Too many initialisation requests"); + private static final CloseStatus TOO_MANY_INIT_REQUESTS_STATUS = new CloseStatus(4429, "Too many initialisation requests"); + + // @formatter:on static void closeSession(WebSocketSession session, CloseStatus status) { try { diff --git a/spring-graphql/src/test/java/org/springframework/graphql/GraphQlTestUtils.java b/spring-graphql/src/test/java/org/springframework/graphql/GraphQlTestUtils.java index 21daa91d..2c0bee12 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/GraphQlTestUtils.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/GraphQlTestUtils.java @@ -27,28 +27,34 @@ import org.springframework.core.io.ByteArrayResource; import org.springframework.graphql.execution.DataFetcherExceptionResolver; import org.springframework.graphql.execution.GraphQlSource; +// @formatter:off + /** * Utility methods for GraphQL tests. */ public abstract class GraphQlTestUtils { public static GraphQL initGraphQl(String schemaContent, String typeName, String fieldName, DataFetcher fetcher) { - - return initGraphQlSource(schemaContent, typeName, fieldName, fetcher).build().graphQl(); + return initGraphQlSource(schemaContent, typeName, fieldName, fetcher) + .build() + .graphQl(); } public static GraphQL initGraphQl(String schemaContent, String typeName, String fieldName, DataFetcher fetcher, DataFetcherExceptionResolver... resolvers) { return initGraphQlSource(schemaContent, typeName, fieldName, fetcher) - .exceptionResolvers(Arrays.asList(resolvers)).build().graphQl(); + .exceptionResolvers(Arrays.asList(resolvers)) + .build() + .graphQl(); } public static GraphQlSource.Builder initGraphQlSource(String schemaContent, String typeName, String fieldName, DataFetcher fetcher) { RuntimeWiring wiring = RuntimeWiring.newRuntimeWiring() - .type(typeName, (builder) -> builder.dataFetcher(fieldName, fetcher)).build(); + .type(typeName, (builder) -> builder.dataFetcher(fieldName, fetcher)) + .build(); return GraphQlSource.builder() .schemaResource(new ByteArrayResource(schemaContent.getBytes(StandardCharsets.UTF_8))) diff --git a/spring-graphql/src/test/java/org/springframework/graphql/execution/ContextDataFetcherDecoratorTests.java b/spring-graphql/src/test/java/org/springframework/graphql/execution/ContextDataFetcherDecoratorTests.java index 70055cfd..742ffff4 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/execution/ContextDataFetcherDecoratorTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/execution/ContextDataFetcherDecoratorTests.java @@ -35,6 +35,8 @@ import org.springframework.graphql.TestThreadLocalAccessor; import static org.assertj.core.api.Assertions.assertThat; +// @formatter:off + /** * Tests for {@link ContextDataFetcherDecorator}. */ @@ -76,10 +78,11 @@ public class ContextDataFetcherDecoratorTests { void fluxDataFetcherSubscription() throws Exception { GraphQL graphQl = GraphQlTestUtils.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); - }))); + (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); + }))); ExecutionInput input = ExecutionInput.newExecutionInput().query("subscription { greetings }").build(); ContextManager.setReactorContext(Context.of("name", "007"), input); @@ -87,7 +90,9 @@ public class ContextDataFetcherDecoratorTests { Publisher publisher = graphQl.executeAsync(input).get().getData(); List actual = Flux.from(publisher).cast(ExecutionResult.class) - .map((result) -> ((Map) result.getData()).get("greetings")).cast(String.class).collectList() + .map((result) -> ((Map) result.getData()).get("greetings")) + .cast(String.class) + .collectList() .block(); assertThat(actual).containsExactly("Hi 007", "Bonjour 007", "Hola 007"); @@ -99,7 +104,8 @@ public class ContextDataFetcherDecoratorTests { nameThreadLocal.set("007"); TestThreadLocalAccessor accessor = new TestThreadLocalAccessor<>(nameThreadLocal); try { - GraphQL graphQl = GraphQlTestUtils.initGraphQl("type Query { greeting: String }", "Query", "greeting", + GraphQL graphQl = GraphQlTestUtils.initGraphQl( + "type Query { greeting: String }", "Query", "greeting", (env) -> "Hello " + nameThreadLocal.get()); ExecutionInput input = ExecutionInput.newExecutionInput().query("{ greeting }").build(); @@ -107,7 +113,8 @@ public class ContextDataFetcherDecoratorTests { ContextManager.setReactorContext(view, input); ExecutionResult result = Mono.delay(Duration.ofMillis(10)) - .flatMap((aLong) -> Mono.fromFuture(graphQl.executeAsync(input))).block(); + .flatMap((aLong) -> Mono.fromFuture(graphQl.executeAsync(input))) + .block(); Map data = result.getData(); assertThat(data).hasSize(1).containsEntry("greeting", "Hello 007"); diff --git a/spring-graphql/src/test/java/org/springframework/graphql/execution/ExceptionResolversExceptionHandlerTests.java b/spring-graphql/src/test/java/org/springframework/graphql/execution/ExceptionResolversExceptionHandlerTests.java index 6fc9c504..b607b5fa 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/execution/ExceptionResolversExceptionHandlerTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/execution/ExceptionResolversExceptionHandlerTests.java @@ -36,6 +36,8 @@ import org.springframework.graphql.TestThreadLocalAccessor; import static org.assertj.core.api.Assertions.assertThat; +// @formatter:off + /** * Tests for {@link ExceptionResolversExceptionHandler}. */ @@ -46,8 +48,11 @@ public class ExceptionResolversExceptionHandlerTests { GraphQL graphQl = GraphQlTestUtils.initGraphQl("type Query { greeting: String }", "Query", "greeting", (env) -> { throw new IllegalArgumentException("Invalid greeting"); - }, (ex, env) -> Mono.just(Collections.singletonList(GraphqlErrorBuilder.newError(env) - .message("Resolved error: " + ex.getMessage()).errorType(ErrorType.BAD_REQUEST).build()))); + }, + (ex, env) -> Mono.just(Collections.singletonList( + GraphqlErrorBuilder.newError(env) + .message("Resolved error: " + ex.getMessage()) + .errorType(ErrorType.BAD_REQUEST).build()))); ExecutionInput input = ExecutionInput.newExecutionInput().query("{ greeting }").build(); ExecutionResult result = graphQl.executeAsync(input).get(); @@ -67,9 +72,10 @@ public class ExceptionResolversExceptionHandlerTests { (env) -> { throw new IllegalArgumentException("Invalid greeting"); }, - (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())))); + (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())))); ExecutionInput input = ExecutionInput.newExecutionInput().query("{ greeting }").build(); ContextManager.setReactorContext(Context.of("name", "007"), input); @@ -90,17 +96,19 @@ public class ExceptionResolversExceptionHandlerTests { (env) -> { throw new IllegalArgumentException("Invalid greeting"); }, - (SyncDataFetcherExceptionResolver) (ex, - env) -> Collections.singletonList(GraphqlErrorBuilder.newError(env) + (SyncDataFetcherExceptionResolver) (ex, env) -> Collections.singletonList( + GraphqlErrorBuilder.newError(env) .message("Resolved error: " + ex.getMessage() + ", name=" + nameThreadLocal.get()) - .errorType(ErrorType.BAD_REQUEST).build())); + .errorType(ErrorType.BAD_REQUEST) + .build())); ExecutionInput input = ExecutionInput.newExecutionInput().query("{ greeting }").build(); ContextView view = ContextManager.extractThreadLocalValues(accessor); ContextManager.setReactorContext(view, input); ExecutionResult result = Mono.delay(Duration.ofMillis(10)) - .flatMap((aLong) -> Mono.fromFuture(graphQl.executeAsync(input))).block(); + .flatMap((aLong) -> Mono.fromFuture(graphQl.executeAsync(input))) + .block(); List errors = result.getErrors(); assertThat(errors.get(0).getMessage()).isEqualTo("Resolved error: Invalid greeting, name=007"); @@ -115,7 +123,8 @@ public class ExceptionResolversExceptionHandlerTests { GraphQL graphQl = GraphQlTestUtils.initGraphQl("type Query { greeting: String }", "Query", "greeting", (env) -> { throw new IllegalArgumentException("Invalid greeting"); - }, (exception, environment) -> Mono.empty()); + }, + (exception, environment) -> Mono.empty()); ExecutionInput input = ExecutionInput.newExecutionInput().query("{ greeting }").build(); ExecutionResult result = graphQl.executeAsync(input).get(); @@ -135,7 +144,8 @@ public class ExceptionResolversExceptionHandlerTests { GraphQL graphQl = GraphQlTestUtils.initGraphQl("type Query { greeting: String }", "Query", "greeting", (env) -> { throw new IllegalArgumentException("Invalid greeting"); - }, (ex, env) -> Mono.just(Collections.emptyList())); + }, + (ex, env) -> Mono.just(Collections.emptyList())); ExecutionInput input = ExecutionInput.newExecutionInput().query("{ greeting }").build(); ExecutionResult result = graphQl.executeAsync(input).get(); diff --git a/spring-graphql/src/test/java/org/springframework/graphql/web/BookTestUtils.java b/spring-graphql/src/test/java/org/springframework/graphql/web/BookTestUtils.java index 57c6f0c2..208491c1 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/web/BookTestUtils.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/web/BookTestUtils.java @@ -28,17 +28,33 @@ import org.springframework.core.io.ClassPathResource; import org.springframework.graphql.execution.ExecutionGraphQlService; import org.springframework.graphql.execution.GraphQlSource; +// @formatter:off + public abstract class BookTestUtils { public static final String SUBSCRIPTION_ID = "1"; - public static final String BOOK_QUERY = "{" + "\"id\":\"" + BookTestUtils.SUBSCRIPTION_ID + "\"," - + "\"type\":\"subscribe\"," + "\"payload\":{\"query\": \"" + " query TestQuery {" - + " bookById(id: \\\"1\\\"){ " + " id" + " name" + " author" + " }}\"}" + "}"; + public static final String BOOK_QUERY = "{" + + "\"id\":\"" + BookTestUtils.SUBSCRIPTION_ID + "\"," + + "\"type\":\"subscribe\"," + + "\"payload\":{\"query\": \"" + + " query TestQuery {" + + " bookById(id: \\\"1\\\"){ " + + " id" + " name" + + " author" + " }}\"}" + + "}"; - public static final String BOOK_SUBSCRIPTION = "{" + "\"id\":\"" + SUBSCRIPTION_ID + "\"," - + "\"type\":\"subscribe\"," + "\"payload\":{\"query\": \"" + " subscription TestSubscription {" - + " bookSearch(author: \\\"George\\\") {" + " id" + " name" + " author" + " }}\"}" + "}"; + public static final String BOOK_SUBSCRIPTION = "{" + + "\"id\":\"" + SUBSCRIPTION_ID + "\"," + + "\"type\":\"subscribe\"," + + "\"payload\":{\"query\": \"" + + " subscription TestSubscription {" + + " bookSearch(author: \\\"George\\\") {" + + " id" + + " name" + + " author" + + " }}\"}" + + "}"; private static final Map booksMap = new HashMap<>(4); static { @@ -51,21 +67,26 @@ public abstract class BookTestUtils { public static WebGraphQlHandler initWebGraphQlHandler(WebInterceptor... interceptors) { return WebGraphQlHandler.builder(new ExecutionGraphQlService(graphQlSource())) - .interceptors(Arrays.asList(interceptors)).build(); + .interceptors(Arrays.asList(interceptors)) + .build(); } private static GraphQlSource graphQlSource() { RuntimeWiring.Builder builder = RuntimeWiring.newRuntimeWiring(); - builder.type(TypeRuntimeWiring.newTypeWiring("Query").dataFetcher("bookById", (env) -> { - Long id = Long.parseLong(env.getArgument("id")); - return booksMap.get(id); - })); - builder.type(TypeRuntimeWiring.newTypeWiring("Subscription").dataFetcher("bookSearch", (env) -> { - String author = env.getArgument("author"); - return Flux.fromIterable(booksMap.values()).filter((book) -> book.getAuthor().contains(author)); - })); - return GraphQlSource.builder().schemaResource(new ClassPathResource("books/schema.graphqls")) - .runtimeWiring(builder.build()).build(); + builder.type(TypeRuntimeWiring.newTypeWiring("Query") + .dataFetcher("bookById", (env) -> { + Long id = Long.parseLong(env.getArgument("id")); + return booksMap.get(id); + })); + builder.type(TypeRuntimeWiring.newTypeWiring("Subscription") + .dataFetcher("bookSearch", (env) -> { + String author = env.getArgument("author"); + return Flux.fromIterable(booksMap.values()).filter((book) -> book.getAuthor().contains(author)); + })); + return GraphQlSource.builder() + .schemaResource(new ClassPathResource("books/schema.graphqls")) + .runtimeWiring(builder.build()) + .build(); } } diff --git a/spring-graphql/src/test/java/org/springframework/graphql/web/WebGraphQlHandlerTests.java b/spring-graphql/src/test/java/org/springframework/graphql/web/WebGraphQlHandlerTests.java index d68349c8..0a10fdf6 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/web/WebGraphQlHandlerTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/web/WebGraphQlHandlerTests.java @@ -39,17 +39,20 @@ import org.springframework.http.HttpHeaders; import static org.assertj.core.api.Assertions.assertThat; +// @formatter:off + /** * Tests for {@link WebGraphQlHandler}, common to both HTTP and WebSocket. */ public class WebGraphQlHandlerTests { - private static final WebInput webInput = new WebInput(URI.create("http://abc.org"), new HttpHeaders(), - Collections.singletonMap("query", "{ greeting }"), "1"); + private static final WebInput webInput = new WebInput( + URI.create("http://abc.org"), new HttpHeaders(), Collections.singletonMap("query", "{ greeting }"), "1"); @Test void reactorContextPropagation() { - GraphQL graphQl = GraphQlTestUtils.initGraphQl("type Query { greeting: String }", "Query", "greeting", + GraphQL graphQl = GraphQlTestUtils.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); @@ -70,7 +73,8 @@ public class WebGraphQlHandlerTests { (env) -> { throw new IllegalArgumentException("Invalid greeting"); }, - (ex, env) -> Mono.deferContextual((view) -> Mono.just(Collections.singletonList(GraphqlErrorBuilder + (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())))); @@ -93,15 +97,16 @@ public class WebGraphQlHandlerTests { nameThreadLocal.set("007"); TestThreadLocalAccessor threadLocalAccessor = new TestThreadLocalAccessor<>(nameThreadLocal); try { - GraphQL graphQl = GraphQlTestUtils.initGraphQl("type Query { greeting: String }", "Query", "greeting", + GraphQL graphQl = GraphQlTestUtils.initGraphQl( + "type Query { greeting: String }", "Query", "greeting", (env) -> "Hello " + nameThreadLocal.get()); GraphQlService service = new ExecutionGraphQlService(new TestGraphQlSource(graphQl)); WebGraphQlHandler handler = WebGraphQlHandler.builder(service) - .interceptor( - (input, next) -> Mono.delay(Duration.ofMillis(10)).flatMap((aLong) -> next.handle(input))) - .threadLocalAccessor(threadLocalAccessor).build(); + .interceptor((input, next) -> Mono.delay(Duration.ofMillis(10)).flatMap((aLong) -> next.handle(input))) + .threadLocalAccessor(threadLocalAccessor) + .build(); Map data = handler.handle(webInput).block().getData(); @@ -122,17 +127,17 @@ public class WebGraphQlHandlerTests { (env) -> { throw new IllegalArgumentException("Invalid greeting"); }, - (SyncDataFetcherExceptionResolver) (ex, - env) -> Collections.singletonList(GraphqlErrorBuilder.newError(env) + (SyncDataFetcherExceptionResolver) (ex, env) -> Collections.singletonList( + GraphqlErrorBuilder.newError(env) .message("Resolved error: " + ex.getMessage() + ", name=" + nameThreadLocal.get()) .errorType(ErrorType.BAD_REQUEST).build())); GraphQlService service = new ExecutionGraphQlService(new TestGraphQlSource(graphQl)); WebGraphQlHandler handler = WebGraphQlHandler.builder(service) - .interceptor( - (input, next) -> Mono.delay(Duration.ofMillis(10)).flatMap((aLong) -> next.handle(input))) - .threadLocalAccessor(threadLocalAccessor).build(); + .interceptor((input, next) -> Mono.delay(Duration.ofMillis(10)).flatMap((aLong) -> next.handle(input))) + .threadLocalAccessor(threadLocalAccessor) + .build(); WebOutput webOutput = handler.handle(webInput).block(); diff --git a/spring-graphql/src/test/java/org/springframework/graphql/web/WebInterceptorTests.java b/spring-graphql/src/test/java/org/springframework/graphql/web/WebInterceptorTests.java index 77690922..19904a60 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/web/WebInterceptorTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/web/WebInterceptorTests.java @@ -31,6 +31,8 @@ import org.springframework.http.HttpHeaders; import static org.assertj.core.api.Assertions.assertThat; +// @formatter:off + /** * Unit tests for a {@link WebInterceptor} chain. */ @@ -44,7 +46,9 @@ public class WebInterceptorTests { StringBuilder output = new StringBuilder(); WebGraphQlHandler handler = WebGraphQlHandler.builder((input) -> emptyExecutionResult()) - .interceptors(Arrays.asList(new OrderInterceptor(1, output), new OrderInterceptor(2, output), + .interceptors(Arrays.asList( + new OrderInterceptor(1, output), + new OrderInterceptor(2, output), new OrderInterceptor(3, output))) .build(); @@ -55,8 +59,7 @@ public class WebInterceptorTests { @Test void responseHeader() { WebGraphQlHandler handler = WebGraphQlHandler.builder((input) -> emptyExecutionResult()) - .interceptor((input, next) -> next.handle(input).map( - (output) -> output.transform((builder) -> builder.responseHeader("testHeader", "testValue")))) + .interceptor((input, next) -> next.handle(input).map((output) -> output.transform((builder) -> builder.responseHeader("testHeader", "testValue")))) .build(); HttpHeaders headers = handler.handle(webInput).block().getResponseHeaders(); @@ -68,13 +71,16 @@ public class WebInterceptorTests { void executionInputCustomization() { AtomicReference actualName = new AtomicReference<>(); - WebGraphQlHandler handler = WebGraphQlHandler.builder((input) -> { - actualName.set(input.getOperationName()); - return emptyExecutionResult(); - }).interceptor((webInput, next) -> { - webInput.configureExecutionInput((input, builder) -> builder.operationName("testOp").build()); - return next.handle(webInput); - }).build(); + WebGraphQlHandler handler = WebGraphQlHandler.builder( + (input) -> { + actualName.set(input.getOperationName()); + return emptyExecutionResult(); + }) + .interceptor((webInput, next) -> { + webInput.configureExecutionInput((input, builder) -> builder.operationName("testOp").build()); + return next.handle(webInput); + }) + .build(); handler.handle(webInput).block(); @@ -99,10 +105,12 @@ public class WebInterceptorTests { @Override public Mono intercept(WebInput input, WebGraphQlHandler next) { this.output.append(":pre").append(this.order); - return next.handle(input).map((output) -> { - this.output.append(":post").append(this.order); - return output; - }).subscribeOn(Schedulers.boundedElastic()); + return next.handle(input) + .map((output) -> { + this.output.append(":post").append(this.order); + return output; + }) + .subscribeOn(Schedulers.boundedElastic()); } } diff --git a/spring-graphql/src/test/java/org/springframework/graphql/web/webflux/GraphQlWebSocketHandlerTests.java b/spring-graphql/src/test/java/org/springframework/graphql/web/webflux/GraphQlWebSocketHandlerTests.java index 412baea3..a1157f04 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/web/webflux/GraphQlWebSocketHandlerTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/web/webflux/GraphQlWebSocketHandlerTests.java @@ -44,6 +44,8 @@ import org.springframework.web.reactive.socket.WebSocketMessage; import static org.assertj.core.api.Assertions.as; import static org.assertj.core.api.Assertions.assertThat; +// @formatter:off + /** * Unit tests for {@link GraphQlWebSocketHandler}. */ @@ -53,7 +55,8 @@ public class GraphQlWebSocketHandlerTests { @Test void query() { - TestWebSocketSession session = handle(Flux.just(toWebSocketMessage("{\"type\":\"connection_init\"}"), + TestWebSocketSession session = handle(Flux.just( + toWebSocketMessage("{\"type\":\"connection_init\"}"), toWebSocketMessage(BookTestUtils.BOOK_QUERY))); StepVerifier.create(session.getOutput()) @@ -64,51 +67,61 @@ public class GraphQlWebSocketHandlerTests { .extractingByKey("data", as(InstanceOfAssertFactories.map(String.class, Object.class))) .extractingByKey("bookById", as(InstanceOfAssertFactories.map(String.class, Object.class))) .containsEntry("name", "Nineteen Eighty-Four")) - .consumeNextWith((message) -> assertMessageType(message, "complete")).verifyComplete(); + .consumeNextWith((message) -> assertMessageType(message, "complete")) + .verifyComplete(); } @Test void subscription() { - TestWebSocketSession session = handle(Flux.just(toWebSocketMessage("{\"type\":\"connection_init\"}"), + TestWebSocketSession session = handle(Flux.just( + toWebSocketMessage("{\"type\":\"connection_init\"}"), toWebSocketMessage(BookTestUtils.BOOK_SUBSCRIPTION))); - BiConsumer bookPayloadAssertion = (message, bookId) -> assertThat(decode(message)) - .hasSize(3).containsEntry("id", BookTestUtils.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))) - .containsEntry("id", bookId); + BiConsumer bookPayloadAssertion = (message, bookId) -> + assertThat(decode(message)) + .hasSize(3).containsEntry("id", BookTestUtils.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))) + .containsEntry("id", bookId); StepVerifier.create(session.getOutput()) .consumeNextWith((message) -> assertMessageType(message, "connection_ack")) .consumeNextWith((message) -> bookPayloadAssertion.accept(message, "1")) .consumeNextWith((message) -> bookPayloadAssertion.accept(message, "5")) - .consumeNextWith((message) -> assertMessageType(message, "complete")).verifyComplete(); + .consumeNextWith((message) -> assertMessageType(message, "complete")) + .verifyComplete(); } @Test void unauthorizedWithoutMessageType() { - TestWebSocketSession session = handle(Flux.just(toWebSocketMessage("{\"type\":\"connection_init\"}"), + TestWebSocketSession session = handle(Flux.just( + toWebSocketMessage("{\"type\":\"connection_init\"}"), toWebSocketMessage("{\"id\":\"" + BookTestUtils.SUBSCRIPTION_ID + "\"}"))); StepVerifier.create(session.getOutput()) - .consumeNextWith((message) -> assertMessageType(message, "connection_ack")).verifyComplete(); + .consumeNextWith((message) -> assertMessageType(message, "connection_ack")) + .verifyComplete(); - StepVerifier.create(session.closeStatus()).expectNext(new CloseStatus(4400, "Invalid message")) + StepVerifier.create(session.closeStatus()) + .expectNext(new CloseStatus(4400, "Invalid message")) .verifyComplete(); } @Test void invalidMessageWithoutId() { - Flux input = Flux.just(toWebSocketMessage("{\"type\":\"connection_init\"}"), + Flux input = Flux.just( + toWebSocketMessage("{\"type\":\"connection_init\"}"), toWebSocketMessage("{\"type\":\"subscribe\"}")); // No message id TestWebSocketSession session = handle(input); StepVerifier.create(session.getOutput()) - .consumeNextWith((message) -> assertMessageType(message, "connection_ack")).verifyComplete(); + .consumeNextWith((message) -> assertMessageType(message, "connection_ack")) + .verifyComplete(); - StepVerifier.create(session.closeStatus()).expectNext(new CloseStatus(4400, "Invalid message")) + StepVerifier.create(session.closeStatus()) + .expectNext(new CloseStatus(4400, "Invalid message")) .verifyComplete(); } @@ -122,43 +135,47 @@ public class GraphQlWebSocketHandlerTests { @Test void tooManyConnectionInitRequests() { - TestWebSocketSession session = handle(Flux.just(toWebSocketMessage("{\"type\":\"connection_init\"}"), + TestWebSocketSession session = handle(Flux.just( + toWebSocketMessage("{\"type\":\"connection_init\"}"), toWebSocketMessage("{\"type\":\"connection_init\"}"))); StepVerifier.create(session.getOutput()) - .consumeNextWith((message) -> assertMessageType(message, "connection_ack")).verifyComplete(); + .consumeNextWith((message) -> assertMessageType(message, "connection_ack")) + .verifyComplete(); - StepVerifier.create(session.closeStatus()).expectNext(new CloseStatus(4429, "Too many initialisation requests")) + StepVerifier.create(session.closeStatus()) + .expectNext(new CloseStatus(4429, "Too many initialisation requests")) .verifyComplete(); } @Test void connectionInitTimeout() { - GraphQlWebSocketHandler handler = new GraphQlWebSocketHandler(BookTestUtils.initWebGraphQlHandler(), - ServerCodecConfigurer.create(), Duration.ofMillis(50)); + GraphQlWebSocketHandler handler = new GraphQlWebSocketHandler( + BookTestUtils.initWebGraphQlHandler(), ServerCodecConfigurer.create(), Duration.ofMillis(50)); TestWebSocketSession session = new TestWebSocketSession(Flux.empty()); handler.handle(session).block(); StepVerifier.create(session.closeStatus()) - .expectNext(new CloseStatus(4408, "Connection initialisation timeout")).verifyComplete(); + .expectNext(new CloseStatus(4408, "Connection initialisation timeout")) + .verifyComplete(); } @Test void subscriptionExists() { - TestWebSocketSession session = handle( - Flux.just(toWebSocketMessage("{\"type\":\"connection_init\"}"), - toWebSocketMessage(BookTestUtils.BOOK_SUBSCRIPTION), - toWebSocketMessage(BookTestUtils.BOOK_SUBSCRIPTION)), - new ConsumeOneAndNeverCompleteInterceptor()); + Flux messageFlux = Flux.just( + toWebSocketMessage("{\"type\":\"connection_init\"}"), + toWebSocketMessage(BookTestUtils.BOOK_SUBSCRIPTION), + toWebSocketMessage(BookTestUtils.BOOK_SUBSCRIPTION)); + + TestWebSocketSession session = handle(messageFlux, new ConsumeOneAndNeverCompleteInterceptor()); // Collect messages until session closed List> messages = new ArrayList<>(); 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 " + BookTestUtils.SUBSCRIPTION_ID + " already exists")) .verifyComplete(); assertThat(messages.size()).isEqualTo(2); @@ -188,8 +205,10 @@ public class GraphQlWebSocketHandlerTests { } private TestWebSocketSession handle(Flux input, WebInterceptor... interceptors) { - GraphQlWebSocketHandler handler = new GraphQlWebSocketHandler(BookTestUtils.initWebGraphQlHandler(interceptors), - ServerCodecConfigurer.create(), Duration.ofSeconds(60)); + GraphQlWebSocketHandler handler = new GraphQlWebSocketHandler( + BookTestUtils.initWebGraphQlHandler(interceptors), + ServerCodecConfigurer.create(), + Duration.ofSeconds(60)); TestWebSocketSession session = new TestWebSocketSession(input); handler.handle(session).block(); diff --git a/spring-graphql/src/test/java/org/springframework/graphql/web/webmvc/GraphQlWebSocketHandlerTests.java b/spring-graphql/src/test/java/org/springframework/graphql/web/webmvc/GraphQlWebSocketHandlerTests.java index c7bb2725..07f75d3e 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/web/webmvc/GraphQlWebSocketHandlerTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/web/webmvc/GraphQlWebSocketHandlerTests.java @@ -44,6 +44,8 @@ import org.springframework.web.socket.WebSocketMessage; import static org.assertj.core.api.Assertions.as; import static org.assertj.core.api.Assertions.assertThat; +// @formatter:off + /** * Unit tests for {@link GraphQlWebSocketHandler}. */ @@ -57,7 +59,8 @@ public class GraphQlWebSocketHandlerTests { @Test void query() throws Exception { - handle(this.handler, new TextMessage("{\"type\":\"connection_init\"}"), + handle(this.handler, + new TextMessage("{\"type\":\"connection_init\"}"), new TextMessage(BookTestUtils.BOOK_QUERY)); StepVerifier.create(this.session.getOutput()) @@ -68,9 +71,8 @@ public class GraphQlWebSocketHandlerTests { .extractingByKey("data", as(InstanceOfAssertFactories.map(String.class, Object.class))) .extractingByKey("bookById", as(InstanceOfAssertFactories.map(String.class, Object.class))) .containsEntry("name", "Nineteen Eighty-Four")) - .consumeNextWith((message) -> assertMessageType(message, "complete")).then(this.session::close) // Complete - // output - // Flux + .consumeNextWith((message) -> assertMessageType(message, "complete")) + .then(this.session::close) // Complete output Flux .verifyComplete(); } @@ -79,41 +81,46 @@ public class GraphQlWebSocketHandlerTests { handle(this.handler, new TextMessage("{\"type\":\"connection_init\"}"), new TextMessage(BookTestUtils.BOOK_SUBSCRIPTION)); - BiConsumer, String> bookPayloadAssertion = (message, bookId) -> assertThat(decode(message)) - .hasSize(3).containsEntry("id", BookTestUtils.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))) - .containsEntry("id", bookId); + BiConsumer, String> bookPayloadAssertion = (message, bookId) -> + assertThat(decode(message)) + .hasSize(3).containsEntry("id", BookTestUtils.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))) + .containsEntry("id", bookId); StepVerifier.create(this.session.getOutput()) .consumeNextWith((message) -> assertMessageType(message, "connection_ack")) .consumeNextWith((message) -> bookPayloadAssertion.accept(message, "1")) .consumeNextWith((message) -> bookPayloadAssertion.accept(message, "5")) - .consumeNextWith((message) -> assertMessageType(message, "complete")).then(this.session::close) - // Complete output Flux + .consumeNextWith((message) -> assertMessageType(message, "complete")) + .then(this.session::close)// Complete output Flux .verifyComplete(); } @Test void unauthorizedWithoutMessageType() throws Exception { - handle(this.handler, new TextMessage("{\"type\":\"connection_init\"}"), + handle(this.handler, + new TextMessage("{\"type\":\"connection_init\"}"), new TextMessage("{\"id\":\"" + BookTestUtils.SUBSCRIPTION_ID + "\"}")); // No message type StepVerifier.create(this.session.getOutput()) - .consumeNextWith((message) -> assertMessageType(message, "connection_ack")).verifyComplete(); + .consumeNextWith((message) -> assertMessageType(message, "connection_ack")) + .verifyComplete(); assertThat(this.session.getCloseStatus()).isEqualTo(new CloseStatus(4400, "Invalid message")); } @Test void invalidMessageWithoutId() throws Exception { - handle(this.handler, new TextMessage("{\"type\":\"connection_init\"}"), + handle(this.handler, + new TextMessage("{\"type\":\"connection_init\"}"), new TextMessage("{\"type\":\"subscribe\"}")); // No message id StepVerifier.create(this.session.getOutput()) - .consumeNextWith((message) -> assertMessageType(message, "connection_ack")).verifyComplete(); + .consumeNextWith((message) -> assertMessageType(message, "connection_ack")) + .verifyComplete(); assertThat(this.session.getCloseStatus()).isEqualTo(new CloseStatus(4400, "Invalid message")); } @@ -128,30 +135,34 @@ public class GraphQlWebSocketHandlerTests { @Test void tooManyConnectionInitRequests() throws Exception { - handle(this.handler, new TextMessage("{\"type\":\"connection_init\"}"), + handle(this.handler, + new TextMessage("{\"type\":\"connection_init\"}"), new TextMessage("{\"type\":\"connection_init\"}")); StepVerifier.create(this.session.getOutput()) - .consumeNextWith((message) -> assertMessageType(message, "connection_ack")).verifyComplete(); + .consumeNextWith((message) -> assertMessageType(message, "connection_ack")) + .verifyComplete(); assertThat(this.session.getCloseStatus()).isEqualTo(new CloseStatus(4429, "Too many initialisation requests")); } @Test void connectionInitTimeout() { - GraphQlWebSocketHandler handler = new GraphQlWebSocketHandler(BookTestUtils.initWebGraphQlHandler(), converter, - Duration.ofMillis(50)); + GraphQlWebSocketHandler handler = new GraphQlWebSocketHandler( + BookTestUtils.initWebGraphQlHandler(), converter, Duration.ofMillis(50)); handler.afterConnectionEstablished(this.session); StepVerifier.create(this.session.closeStatus()) - .expectNext(new CloseStatus(4408, "Connection initialisation timeout")).verifyComplete(); + .expectNext(new CloseStatus(4408, "Connection initialisation timeout")) + .verifyComplete(); } @Test void subscriptionExists() throws Exception { handle(initWebSocketHandler(new ConsumeOneAndNeverCompleteInterceptor()), - new TextMessage("{\"type\":\"connection_init\"}"), new TextMessage(BookTestUtils.BOOK_SUBSCRIPTION), + new TextMessage("{\"type\":\"connection_init\"}"), + new TextMessage(BookTestUtils.BOOK_SUBSCRIPTION), new TextMessage(BookTestUtils.BOOK_SUBSCRIPTION)); // Collect messages until session closed @@ -159,8 +170,7 @@ public class GraphQlWebSocketHandlerTests { 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 " + BookTestUtils.SUBSCRIPTION_ID + " already exists")) .verifyComplete(); assertThat(messages.size()).isEqualTo(2); @@ -172,7 +182,8 @@ public class GraphQlWebSocketHandlerTests { void clientCompletion() throws Exception { GraphQlWebSocketHandler handler = initWebSocketHandler(new ConsumeOneAndNeverCompleteInterceptor()); - handle(handler, new TextMessage("{\"type\":\"connection_init\"}"), + handle(handler, + new TextMessage("{\"type\":\"connection_init\"}"), new TextMessage(BookTestUtils.BOOK_SUBSCRIPTION)); String completeMessage = "{\"id\":\"" + BookTestUtils.SUBSCRIPTION_ID + "\",\"type\":\"complete\"}"; @@ -192,7 +203,8 @@ public class GraphQlWebSocketHandlerTests { .as("Second subscription with same id is possible only if the first was properly removed") .then(() -> messageSender.accept(BookTestUtils.BOOK_SUBSCRIPTION)) .consumeNextWith((message) -> assertMessageType(message, "next")) - .then(() -> messageSender.accept(completeMessage)).verifyTimeout(Duration.ofMillis(500)); + .then(() -> messageSender.accept(completeMessage)) + .verifyTimeout(Duration.ofMillis(500)); } private void handle(GraphQlWebSocketHandler handler, TextMessage... textMessages) throws Exception { @@ -204,8 +216,8 @@ public class GraphQlWebSocketHandlerTests { private GraphQlWebSocketHandler initWebSocketHandler(WebInterceptor... interceptors) { try { - return new GraphQlWebSocketHandler(BookTestUtils.initWebGraphQlHandler(interceptors), converter, - Duration.ofSeconds(60)); + return new GraphQlWebSocketHandler( + BookTestUtils.initWebGraphQlHandler(interceptors), converter, Duration.ofSeconds(60)); } catch (Exception ex) { throw new IllegalStateException(ex);