diff --git a/spring-boot-project/spring-boot-docs/build.gradle b/spring-boot-project/spring-boot-docs/build.gradle index 4aa778fc41..2c97dd3bb9 100644 --- a/spring-boot-project/spring-boot-docs/build.gradle +++ b/spring-boot-project/spring-boot-docs/build.gradle @@ -137,6 +137,8 @@ dependencies { implementation("org.springframework.data:spring-data-neo4j") implementation("org.springframework.data:spring-data-redis") implementation("org.springframework.data:spring-data-r2dbc") + implementation("org.springframework.graphql:spring-graphql") + implementation("org.springframework.graphql:spring-graphql-test") implementation("org.springframework.kafka:spring-kafka") implementation("org.springframework.kafka:spring-kafka-test") implementation("org.springframework.restdocs:spring-restdocs-mockmvc") { @@ -277,8 +279,9 @@ tasks.withType(org.asciidoctor.gradle.jvm.AbstractAsciidoctorTask) { "spring-data-r2dbc-version": versionConstraints["org.springframework.data:spring-data-r2dbc"], "spring-data-rest-version": versionConstraints["org.springframework.data:spring-data-rest-core"], "spring-framework-version": versionConstraints["org.springframework:spring-core"], - "spring-kafka-version": versionConstraints["org.springframework.kafka:spring-kafka"], + "spring-graphql-version": versionConstraints["org.springframework.graphql:spring-graphql"], "spring-integration-version": versionConstraints["org.springframework.integration:spring-integration-core"], + "spring-kafka-version": versionConstraints["org.springframework.kafka:spring-kafka"], "spring-security-version": securityVersion, "spring-webservices-version": versionConstraints["org.springframework.ws:spring-ws-core"] } @@ -321,6 +324,9 @@ syncDocumentationSourceForAsciidoctor { from("src/main/groovy") { into "main/groovy" } + from("src/main/resources") { + into "main/resources" + } } syncDocumentationSourceForAsciidoctorMultipage { @@ -342,6 +348,9 @@ syncDocumentationSourceForAsciidoctorMultipage { from("src/main/groovy") { into "main/groovy" } + from("src/main/resources") { + into "main/resources" + } } syncDocumentationSourceForAsciidoctorPdf { @@ -363,6 +372,9 @@ syncDocumentationSourceForAsciidoctorPdf { from("src/main/groovy") { into "main/groovy" } + from("src/main/resources") { + into "main/resources" + } } task zip(type: Zip) { diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/metrics.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/metrics.adoc index da953e32d7..b33232633c 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/metrics.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/actuator/metrics.adoc @@ -873,6 +873,58 @@ A `CacheMetricsRegistrar` bean is made available to make that process easier. +[[actuator.metrics.supported.spring-graphql]] +==== Spring GraphQL Metrics +Auto-configuration enables the instrumentation of GraphQL queries, for any supported transport. + +Spring Boot records a `graphql.request` timer with: + +[cols="1,2,2"] +|=== +|Tag | Description| Sample values + +|outcome +|Request outcome +|"SUCCESS", "ERROR" +|=== + +A single GraphQL query can involve many `DataFetcher` calls, so there is a dedicated `graphql.datafetcher` timer: + +[cols="1,2,2"] +|=== +|Tag | Description| Sample values + +|path +|data fetcher path +|"Query.project" + +|outcome +|data fetching outcome +|"SUCCESS", "ERROR" +|=== + + +The `graphql.request.datafetch.count` https://micrometer.io/docs/concepts#_distribution_summaries[distribution summary] counts the number of non-trivial `DataFetcher` calls made per request. +This metric is useful for detecting "N+1" data fetching issues and consider batch loading; it provides the `"TOTAL"` number of data fetcher calls made over the `"COUNT"` of recorded requests, as well as the `"MAX"` calls made for a single request over the considered period. +More options are available for <>. + +A single response can contain many GraphQL errors, counted by the `graphql.error` counter: + +[cols="1,2,2"] +|=== +|Tag | Description| Sample values + +|errorType +|error type +|"DataFetchingException" + +|errorPath +|error JSON Path +|"$.project" +|=== + + + [[actuator.metrics.supported.jdbc]] ==== DataSource Metrics Auto-configuration enables the instrumentation of all available `DataSource` objects with metrics prefixed with `jdbc.connections`. diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/attributes.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/attributes.adoc index 3ba8afc70c..27fc4a8811 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/attributes.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/attributes.adoc @@ -21,6 +21,7 @@ :github-wiki: https://github.com/{github-repo}/wiki :docs-java: ../../main/java/org/springframework/boot/docs :docs-groovy: ../../main/groovy/org/springframework/boot/docs +:docs-resources: ../../main/resources :spring-boot-code: https://github.com/{github-repo}/tree/{github-tag} :spring-boot-api: https://docs.spring.io/spring-boot/docs/{spring-boot-version}/api :spring-boot-docs: https://docs.spring.io/spring-boot/docs/{spring-boot-version}/reference @@ -81,6 +82,9 @@ :spring-framework: https://spring.io/projects/spring-framework :spring-framework-api: https://docs.spring.io/spring-framework/docs/{spring-framework-version}/javadoc-api/org/springframework :spring-framework-docs: https://docs.spring.io/spring-framework/docs/{spring-framework-version}/reference/html +:spring-graphql: https://spring.io/projects/spring-graphql +:spring-graphql-api: https://docs.spring.io/spring-graphql/docs/{spring-graphql-version}/api/ +:spring-graphql-docs: https://docs.spring.io/spring-graphql/docs/{spring-graphql-version}/reference/html/ :spring-integration: https://spring.io/projects/spring-integration :spring-integration-docs: https://docs.spring.io/spring-integration/docs/{spring-integration-version}/reference/html/ :spring-kafka-docs: https://docs.spring.io/spring-kafka/docs/{spring-kafka-version}/reference/html/ diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/testing.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/testing.adoc index 8838f991c7..78dbe81b58 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/testing.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/features/testing.adoc @@ -448,6 +448,62 @@ TIP: Sometimes writing Spring WebFlux tests is not enough; Spring Boot can help +[[features.testing.spring-boot-applications.spring-graphql-tests]] +==== Auto-configured Spring GraphQL Tests +Spring GraphQL offers a dedicated testing support module; you'll need to add it to your project: + +.Maven +[source,xml,indent=0,subs="verbatim"] +---- + + + org.springframework.graphql + spring-graphql-test + test + + + + org.springframework + spring-webflux + test + + +---- + +.Gradle +[source,gradle,indent=0,subs="verbatim"] +---- + dependencies { + testImplementation("org.springframework.graphql:spring-graphql-test") + // Unless already present in the implementation configuration + testImplementation("org.springframework:spring-webflux") + } +---- + +This testing module ships the {spring-graphql-docs}/testing.html#testing-webgraphqltester[WebGraphQlTester]. +The tester is heavily used in test, so be sure to become familiar with using it. + +Spring Boot helps you to test your {spring-graphql-docs}#controllers[Spring GraphQL Controllers] with the `@GraphQlTest` annotation. +`@GraphQlTest` auto-configures the Spring GraphQL infrastructure, without any transport nor server being involved. +This limits scanned beans to `@Controller`, `RuntimeWiringConfigurer`, `JsonComponent`, `Converter` and `GenericConverter`. +Regular `@Component` and `@ConfigurationProperties` beans are not scanned when the `@GraphQlTest` annotation is used. +`@EnableConfigurationProperties` can be used to include `@ConfigurationProperties` beans. + +TIP: A list of the auto-configurations that are enabled by `@GraphQlTest` can be <>. + +TIP: If you need to register extra components, such as Jackson `Module`, you can import additional configuration classes using `@Import` on your test. + +Often, `@GraphQlTest` is limited to a set of controllers and used in combination with the `@MockBean` annotation to provide mock implementations for required collaborators. + +[source,java,indent=0,subs="verbatim"] +---- +include::{docs-java}/features/testing/springbootapplications/springgraphqltests/GreetingControllerTests.java[] +---- + +TIP: You can also auto-configure `WebGraphQlTester` in a non-`@GraphQlTest` (such as `@SpringBootTest`) by annotating it with `@AutoConfigureWebGraphQlTester`. + + + [[features.testing.spring-boot-applications.autoconfigured-spring-data-cassandra]] ==== Auto-configured Data Cassandra Tests You can use `@DataCassandraTest` to test Cassandra applications. diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/index.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/index.adoc index e9e41e1aca..74af721032 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/index.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/index.adoc @@ -16,7 +16,7 @@ The reference documentation consists of the following sections: <> :: Upgrading from 1.x, Upgrading to a new feature release, and Upgrading the Spring Boot CLI. <> :: Build Systems, Structuring Your Code, Configuration, Spring Beans and Dependency Injection, DevTools, and more. <> :: Profiles, Logging, Security, Caching, Spring Integration, Testing, and more. -<> :: Servlet Web, Reactive Web, Embedded Container Support, Graceful Shutdown, and more. +<> :: Servlet Web, Reactive Web, GraphQL, Embedded Container Support, Graceful Shutdown, and more. <> :: SQL and NOSQL data access. <> :: Caching, Quartz Scheduler, REST clients, Sending email, Spring Web Services, and more. <> :: JMS, AMQP, RSocket, WebSocket, and Spring Integration. diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web.adoc index dc98318881..0bf2c5be45 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web.adoc @@ -19,6 +19,8 @@ include::web/spring-security.adoc[] include::web/spring-session.adoc[] +include::web/spring-graphql.adoc[] + include::web/spring-hateoas.adoc[] include::web/whats-next.adoc[] diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/spring-graphql.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/spring-graphql.adoc new file mode 100644 index 0000000000..f2a4116629 --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/web/spring-graphql.adoc @@ -0,0 +1,135 @@ +[[web.graphql]] +== Spring GraphQL +If you want to build GraphQL applications, you can take advantage of Spring Boot's auto-configuration for {spring-graphql}[Spring GraphQL]. +The Spring GraphQL project is based on https://github.com/graphql-java/graphql-java[GraphQL Java]. +You'll need the `spring-boot-starter-graphql` starter at a minimum. +Because GraphQL is transport-agnostic, you'll also need to have one or more additional starters in your application to expose your GraphQL API over the web: + + +[cols="1,1,1"] +|=== +| Starter | Transport | Implementation + +| `spring-boot-starter-web` +| HTTP +| Spring MVC + +| `spring-boot-starter-websocket` +| WebSocket +| WebSocket for Servlet apps + +| `spring-boot-starter-webflux` +| HTTP, WebSocket +| Spring WebFlux + +|=== + + + +[[web.graphql.schema]] +=== GraphQL Schema + +A Spring GraphQL application requires a defined schema at startup. +By default, you can write ".graphqls" or ".gqls" schema files under `src/main/resources/graphql/**` and Spring Boot will pick them up automatically. +You can customize the locations with configprop:spring.graphql.schema.locations[] and the file extensions with configprop:spring.graphql.schema.file-extensions[]. + +In the following sections, we'll consider this sample GraphQL schema, defining two types and two queries: + +[source,json,indent=0,subs="verbatim,quotes"] +---- +include::{docs-resources}/graphql/schema.graphqls[] +---- + + +[[web.graphql.runtimewiring]] +=== GraphQL RuntimeWiring + +The GraphQL Java `RuntimeWiring.Builder` can be used to register custom scalar types, directives, type resolvers, `DataFetcher`s, and more. +You can declare `RuntimeWiringConfigurer` beans in your Spring config to get access to the `RuntimeWiring.Builder`. +Spring Boot detects such beans and adds them to the {spring-graphql-docs}#execution-graphqlsource[GraphQlSource builder]. + +Typically, however, applications will not implement `DataFetcher` directly and will instead create {spring-graphql-docs}#controllers[annotated controllers]. +Spring Boot will automatically register `@Controller` classes with annotated handler methods and registers those as `DataFetcher`s. +Here's a sample implementation for our greeting query with a `@Controller` class: + +[source,java,indent=0,subs="verbatim"] +---- +include::{docs-java}/web/graphql/GreetingController.java[] +---- + + + +[[web.graphql.data-query]] +=== Querydsl and QueryByExample Repositories support + +Spring Data offers support for both Querydsl and QueryByExample repositories. +Spring GraphQL can {spring-graphql-docs}#data[configure Querydsl and QueryByExample repositories as `DataFetcher`]. + +Spring Data repositories annotated with `@GraphQlRepository` and extending one of: + +* `QuerydslPredicateExecutor` +* `ReactiveQuerydslPredicateExecutor` +* `QueryByExampleExecutor` +* `ReactiveQueryByExampleExecutor` + +are detected by Spring Boot and considered as candidates for `DataFetcher` for matching top-level queries. + + + +[[web.graphql.web-endpoints]] +=== Web Endpoints + +The GraphQL HTTP endpoint is at HTTP POST "/graphql" by default. The path can be customized with configprop:spring.graphql.path[]. + +The GraphQL WebSocket endpoint is off by default. To enable it: + +* For a Servlet application, add the WebSocket starter `spring-boot-starter-websocket` +* For a WebFlux application, no additional dependency is required +* For both, the configprop:spring.graphql.websocket.path[] application property must be set + +Spring GraphQL provides a {spring-graphql-docs}#web-interception[Web Interception] model. +This is quite useful for retrieving information from an HTTP request header and set it in the GraphQL context or fetching information from the same context and writing it to a response header. +With Spring Boot, you can declare a `WebInterceptor` bean to have it registered with the web transport. + + + +[[web.graphql.cors]] +=== CORS + +{spring-framework-docs}/web.html#mvc-cors[Spring MVC] and {spring-framework-docs}/web-reactive.html#webflux-cors[Spring WebFlux] support CORS (Cross-Origin Resource Sharing) requests. +CORS is a critical part of the web config for GraphQL applications that are accessed from browsers using different domains. + +Spring Boot supports many configuration properties under the `spring.graphql.cors.*` namespace; here's a short configuration sample: + +[source,yaml,indent=0,subs="verbatim",configblocks] +---- + spring: + graphql: + cors: + allowed-origins: "https://example.org" + allowed-methods: GET,POST + max-age: 1800s +---- + + + +[[web.graphql.exception-handling]] +=== Exceptions Handling + +Spring GraphQL enables applications to register one or more Spring `DataFetcherExceptionResolver` components that are invoked sequentially. +The Exception must be resolved to a list of `graphql.GraphQLError` objects, see {spring-graphql-docs}#execution-exceptions[Spring GraphQL exception handling documentation]. +Spring Boot will automatically detect `DataFetcherExceptionResolver` beans and register them with the `GraphQlSource.Builder`. + + + +[[web.graphql.graphiql]] +=== GraphiQL and Schema printer + +Spring GraphQL offers infrastructure for helping developers when consuming or developing a GraphQL API. + +Spring GraphQL ships with a default https://github.com/graphql/graphiql[GraphiQL] page that is exposed at "/graphiql" by default. +This page is disabled by default and can be turned on with the configprop:spring.graphql.graphiql.enabled[] property. +Many applications exposing such a page will prefer a custom build. +A default implementation is very useful during development, this is why it is exposed automatically with <> during development. + +You can also choose to expose the GraphQL schema in text format at `/graphql/schema` when the configprop:spring.graphql.schema.printer.enabled[] property is enabled. diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/features/testing/springbootapplications/springgraphqltests/GreetingControllerTests.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/features/testing/springbootapplications/springgraphqltests/GreetingControllerTests.java new file mode 100644 index 0000000000..1812730aca --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/features/testing/springbootapplications/springgraphqltests/GreetingControllerTests.java @@ -0,0 +1,44 @@ +/* + * Copyright 2012-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.boot.docs.features.testing.springbootapplications.springgraphqltests; + +import org.junit.jupiter.api.Test; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.docs.web.graphql.GreetingController; +import org.springframework.boot.test.autoconfigure.graphql.GraphQlTest; +import org.springframework.graphql.test.tester.GraphQlTester; + +@GraphQlTest(GreetingController.class) +class GreetingControllerTests { + + @Autowired + private GraphQlTester graphQlTester; + + @Test + void shouldGreetWithSpecificName() { + this.graphQlTester.query("{ greeting(name: \"Alice\") } ").execute().path("greeting").entity(String.class) + .isEqualTo("Hello, Alice!"); + } + + @Test + void shouldGreetWithDefaultName() { + this.graphQlTester.query("{ greeting } ").execute().path("greeting").entity(String.class) + .isEqualTo("Hello, Spring!"); + } + +} diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/web/graphql/GreetingController.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/web/graphql/GreetingController.java new file mode 100644 index 0000000000..99566d9512 --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/web/graphql/GreetingController.java @@ -0,0 +1,31 @@ +/* + * 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.boot.docs.web.graphql; + +import org.springframework.graphql.data.method.annotation.Argument; +import org.springframework.graphql.data.method.annotation.QueryMapping; +import org.springframework.stereotype.Controller; + +@Controller +public class GreetingController { + + @QueryMapping + public String greeting(@Argument String name) { + return "Hello, " + name + "!"; + } + +} diff --git a/spring-boot-project/spring-boot-docs/src/main/resources/graphql/schema.graphqls b/spring-boot-project/spring-boot-docs/src/main/resources/graphql/schema.graphqls new file mode 100644 index 0000000000..fcbf6b0030 --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/main/resources/graphql/schema.graphqls @@ -0,0 +1,29 @@ +type Query { + greeting(name: String! = "Spring"): String! + project(slug: ID!): Project +} + +""" A Project in the Spring portfolio """ +type Project { + """ Unique string id used in URLs """ + slug: ID! + """ Project name """ + name: String! + """ URL of the git repository """ + repositoryUrl: String! + """ Current support status """ + status: ProjectStatus! +} + +enum ProjectStatus { + """ Actively supported by the Spring team """ + ACTIVE + """ Supported by the community """ + COMMUNITY + """ Prototype, not officially supported yet """ + INCUBATING + """ Project being retired, in maintenance mode """ + ATTIC + """ End-Of-Lifed """ + EOL +} \ No newline at end of file