Move client and testing docs to compiled code snippets

Prior to this commit, the "client" and "testing" sections of the
reference documentation were using inline code snippets.
Because of this, several snippets were out of date or invalid.

This commit moves all those code snippets to actual Java classes
compiled with the documentation.

Fixes gh-1042
This commit is contained in:
Brian Clozel
2024-08-21 10:44:54 +02:00
parent 66cece6a2c
commit 311531b74f
24 changed files with 1204 additions and 495 deletions

View File

@@ -19,14 +19,18 @@ dependencies {
dependencyManagement(enforcedPlatform(dependencies.project(path: ":platform")))
api project(':spring-graphql')
api project(':spring-graphql-test')
api 'org.springframework:spring-webflux'
api 'org.springframework:spring-webmvc'
api 'org.springframework:spring-websocket'
api 'org.springframework:spring-messaging'
api 'org.springframework.data:spring-data-commons'
api 'com.querydsl:querydsl-core'
api "org.springframework.boot:spring-boot-starter-graphql:${springBootVersion}"
api "org.springframework.boot:spring-boot-starter-web:${springBootVersion}"
implementation 'org.springframework:spring-webflux'
implementation 'org.springframework:spring-webmvc'
implementation 'org.springframework:spring-websocket'
implementation 'org.springframework:spring-messaging'
implementation 'org.springframework.data:spring-data-commons'
implementation 'com.querydsl:querydsl-core'
implementation "org.springframework.boot:spring-boot-starter-graphql:${springBootVersion}"
implementation "org.springframework.boot:spring-boot-starter-web:${springBootVersion}"
implementation 'io.rsocket:rsocket-core'
implementation 'io.rsocket:rsocket-transport-netty'
implementation 'io.projectreactor:reactor-test'
implementation 'org.assertj:assertj-core'
}
jar {

View File

@@ -40,35 +40,14 @@ above `GraphQlClient` extensions.
to execute GraphQL requests over HTTP through a blocking transport contract and chain of
interceptors.
[source,java,indent=0,subs="verbatim,quotes"]
----
RestClient restClient = ... ;
HttpSyncGraphQlClient graphQlClient = HttpSyncGraphQlClient.create(restClient);
----
include-code::SyncClientUsage[tag=create,indent=0]
Once `HttpSyncGraphQlClient` is created, you can begin to
xref:client.adoc#client.requests[execute requests] using the same API, independent of the underlying
transport. If you need to change any transport specific details, use `mutate()` on an
existing `HttpSyncGraphQlClient` to create a new instance with customized settings:
[source,java,indent=0,subs="verbatim,quotes"]
----
RestClient restClient = ... ;
HttpSyncGraphQlClient graphQlClient = HttpSyncGraphQlClient.builder(restClient)
.headers(headers -> headers.setBasicAuth("joe", "..."))
.build();
// Perform requests with graphQlClient...
HttpSyncGraphQlClient anotherGraphQlClient = graphQlClient.mutate()
.headers(headers -> headers.setBasicAuth("peter", "..."))
.build();
// Perform requests with anotherGraphQlClient...
----
include-code::SyncClientUsage[tag=mutate,indent=0]
[[client.httpgraphqlclient]]
@@ -79,35 +58,14 @@ existing `HttpSyncGraphQlClient` to create a new instance with customized settin
GraphQL requests over HTTP through a non-blocking transport contract and chain of
interceptors.
[source,java,indent=0,subs="verbatim,quotes"]
----
WebClient webClient = ... ;
HttpGraphQlClient graphQlClient = HttpGraphQlClient.create(webClient);
----
include-code::ClientUsage[tag=create,indent=0]
Once `HttpGraphQlClient` is created, you can begin to
xref:client.adoc#client.requests[execute requests] using the same API, independent of the underlying
transport. If you need to change any transport specific details, use `mutate()` on an
existing `HttpGraphQlClient` to create a new instance with customized settings:
[source,java,indent=0,subs="verbatim,quotes"]
----
WebClient webClient = ... ;
HttpGraphQlClient graphQlClient = HttpGraphQlClient.builder(webClient)
.headers(headers -> headers.setBasicAuth("joe", "..."))
.build();
// Perform requests with graphQlClient...
HttpGraphQlClient anotherGraphQlClient = graphQlClient.mutate()
.headers(headers -> headers.setBasicAuth("peter", "..."))
.build();
// Perform requests with anotherGraphQlClient...
----
include-code::ClientUsage[tag=mutate,indent=0]
[[client.websocketgraphqlclient]]
@@ -118,13 +76,7 @@ It is built using the
{spring-framework-ref-docs}/web/webflux-websocket.html#webflux-websocket-client[WebSocketClient]
from Spring WebFlux and you can create it as follows:
[source,java,indent=0,subs="verbatim,quotes"]
----
String url = "wss://localhost:8080/graphql";
WebSocketClient client = new ReactorNettyWebSocketClient();
WebSocketGraphQlClient graphQlClient = WebSocketGraphQlClient.builder(url, client).build();
----
include-code::WebSocketClientUsage[tag=create,indent=0]
In contrast to `HttpGraphQlClient`, the `WebSocketGraphQlClient` is connection oriented,
which means it needs to establish a connection before making any requests. As you begin
@@ -146,37 +98,12 @@ xref:client.adoc#client.requests[execute requests] using the same API, independe
transport. If you need to change any transport specific details, use `mutate()` on an
existing `WebSocketGraphQlClient` to create a new instance with customized settings:
[source,java,indent=0,subs="verbatim,quotes"]
----
URI url = ... ;
WebSocketClient client = ... ;
WebSocketGraphQlClient graphQlClient = WebSocketGraphQlClient.builder(url, client)
.headers(headers -> headers.setBasicAuth("joe", "..."))
.build();
// Use graphQlClient...
WebSocketGraphQlClient anotherGraphQlClient = graphQlClient.mutate()
.headers(headers -> headers.setBasicAuth("peter", "..."))
.build();
// Use anotherGraphQlClient...
----
include-code::WebSocketClientUsage[tag=mutate,indent=0]
`WebSocketGraphQlClient` supports sending periodic ping messages to keep the connection
active when no other messages are sent or received. You can enable that as follows:
[source,java,indent=0,subs="verbatim,quotes"]
----
URI url = ... ;
WebSocketClient client = ... ;
WebSocketGraphQlClient graphQlClient = WebSocketGraphQlClient.builder(url, client)
.keepAlive(Duration.ofSeconds(30))
.build();
----
include-code::WebSocketClientUsage[tag=keepAlive,indent=0]
[[client.websocketgraphqlclient.interceptor]]
@@ -220,15 +147,7 @@ can be at most one interceptor of type `WebSocketGraphQlClientInterceptor`.
{spring-framework-ref-docs}/rsocket.html#rsocket-requester[RSocketRequester]
to execute GraphQL requests over RSocket requests.
[source,java,indent=0,subs="verbatim,quotes"]
----
URI uri = URI.create("wss://localhost:8080/rsocket");
WebsocketClientTransport transport = WebsocketClientTransport.create(url);
RSocketGraphQlClient client = RSocketGraphQlClient.builder()
.clientTransport(transport)
.build();
----
include-code::RSocketClientUsage[tag=create,indent=0]
In contrast to `HttpGraphQlClient`, the `RSocketGraphQlClient` is connection oriented,
which means it needs to establish a session before making any requests. As you begin
@@ -266,7 +185,6 @@ builders of all extensions. Currently, it has lets you configure:
[[client.requests]]
== Requests
@@ -287,36 +205,14 @@ Sync::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
String document = "{" +
" project(slug:\"spring-framework\") {" +
" name" +
" releases {" +
" version" +
" }"+
" }" +
"}";
Project project = graphQlClient.document(document) <1>
.retrieveSync("project") <2>
.toEntity(Project.class); <3>
include::{include-java}/client/requests/retrieve/Retrieve.java[tag=retrieveSync,indent=0]
----
Non-Blocking::
+
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
----
String document = "{" +
" project(slug:\"spring-framework\") {" +
" name" +
" releases {" +
" version" +
" }"+
" }" +
"}";
Mono<Project> projectMono = graphQlClient.document(document) <1>
.retrieve("project") <2>
.toEntity(Project.class); <3>
include::{include-java}/client/requests/retrieve/Retrieve.java[tag=retrieve,indent=0]
----
======
@@ -342,32 +238,14 @@ Sync::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
try {
Project project = graphQlClient.document(document)
.retrieveSync("project")
.toEntity(Project.class);
}
catch (FieldAccessException ex) {
ClientGraphQlResponse response = ex.getResponse();
// ...
ClientResponseField field = ex.getField();
// ...
}
include::{include-java}/client/requests/retrieve/Retrieve.java[tag=fieldErrorSync,indent=0]
----
Non-Blocking::
+
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
----
Mono<Project> projectMono = graphQlClient.document(document)
.retrieve("project")
.toEntity(Project.class)
.onErrorResume(FieldAccessException.class, ex -> {
ClientGraphQlResponse response = ex.getResponse();
// ...
ClientResponseField field = ex.getField();
// ...
});
include::{include-java}/client/requests/retrieve/Retrieve.java[tag=fieldError,indent=0]
----
======
@@ -387,54 +265,20 @@ Sync::
+
[source,java,indent=0,subs="verbatim,quotes",role="primary"]
----
ClientGraphQlResponse response = graphQlClient.document(document).executeSync();
if (!response.isValid()) {
// Request failure... <1>
}
ClientResponseField field = response.field("project");
if (!field.hasValue()) {
if (field.getError() != null) {
// Field failure... <2>
}
else {
// Optional field set to null... <3>
}
}
Project project = field.toEntity(Project.class); <4>
include::{include-java}/client/requests/execute/Execute.java[tag=executeSync,indent=0]
----
Non-Blocking::
+
[source,java,indent=0,subs="verbatim,quotes",role="secondary"]
----
Mono<Project> projectMono = graphQlClient.document(document)
.execute()
.map(response -> {
if (!response.isValid()) {
// Request failure... <1>
}
ClientResponseField field = response.field("project");
if (!field.hasValue()) {
if (field.getError() != null) {
// Field failure... <2>
}
else {
// Optional field set to null... <3>
}
}
return field.toEntity(Project.class); <4>
});
include::{include-java}/client/requests/execute/Execute.java[tag=execute,indent=0]
----
======
<1> The response does not have data, only errors
<2> Field that is `null` and has an associated error
<3> Field that was set to `null` by its `DataFetcher`
<2> Field that was set to `null` by its `DataFetcher`
<3> Field that is `null` and has an associated error
<4> Decode the data at the given path
@@ -468,63 +312,12 @@ You can then:
[source,java,indent=0,subs="verbatim,quotes"]
----
Project project = graphQlClient.documentName("projectReleases") <1>
.variable("slug", "spring-framework") <2>
.retrieveSync()
.toEntity(Project.class);
include::{include-java}/client/requests/documentsource/DocumentSource.java[tag=documentSource,indent=0]
----
<1> Load the document from "projectReleases.graphql"
<2> Provide variable values.
This approach also works for loading fragments for your queries.
Fragments are reusable field selection sets that avoid repetition in a request document.
For example, we can use a `...releases` fragment in multiple queries:
[source,graphql,indent=0,subs="verbatim,quotes"]
.src/main/resources/graphql-documents/projectReleases.graphql
----
query frameworkReleases {
project(slug: "spring-framework") {
name
...releases
}
}
query graphqlReleases {
project(slug: "spring-graphql") {
name
...releases
}
}
----
This fragment can be defined in a separate file for reuse:
[source,graphql,indent=0,subs="verbatim,quotes"]
.src/main/resources/graphql-documents/releases.graphql
----
fragment releases on Project {
releases {
version
}
}
----
You can then send this fragment along the query document:
[source,java,indent=0,subs="verbatim,quotes"]
----
Project project = graphQlClient.documentName("projectReleases") <1>
.fragmentName("releases") <2>
.retrieveSync()
.toEntity(Project.class);
----
<1> Load the document from "projectReleases.graphql"
<2> Load the fragment from "releases.graphql" and append it to the document
The "JS GraphQL" plugin for IntelliJ supports GraphQL query files with code completion.
You can use the `GraphQlClient` xref:client.adoc#client.graphqlclient.builder[Builder] to customize the
@@ -552,18 +345,13 @@ To start a subscription stream, use `retrieveSubscription` which is similar to
xref:client.adoc#client.requests.retrieve[retrieve] for a single response but returning a stream of
responses, each decoded to some data:
[source,java,indent=0,subs="verbatim,quotes"]
----
Flux<String> greetingFlux = client.document("subscription { greetings }")
.retrieveSubscription("greeting")
.toEntity(String.class);
----
include-code::RetrieveSubscription[tag=subscriptionRetrieve,indent=0]
The `Flux` may terminate with `SubscriptionErrorException` if the subscription ends from
the server side with an "error" message. The exception provides access to GraphQL errors
decoded from the "error" message.
The `Flux` may termiate with `GraphQlTransportException` such as
The `Flux` may terminate with `GraphQlTransportException` such as
`WebSocketDisconnectedException` if the underlying connection is closed or lost. In that
case you can use the `retry` operator to restart the subscription.
@@ -582,29 +370,7 @@ xref:client.adoc#client.subscriptions.retrieve[Retrieve] is only a shortcut to d
response map. For more control, use the `executeSubscription` method and handle each
response directly:
[source,java,indent=0,subs="verbatim,quotes"]
----
Flux<String> greetingFlux = client.document("subscription { greetings }")
.executeSubscription()
.map(response -> {
if (!response.isValid()) {
// Request failure...
}
ClientResponseField field = response.field("project");
if (!field.hasValue()) {
if (field.getError() != null) {
// Field failure...
}
else {
// Optional field set to null... <3>
}
}
return field.toEntity(String.class)
});
----
include-code::ExecuteSubscription[tag=subscriptionExecute,indent=0]
@@ -614,52 +380,16 @@ response directly:
For blocking transports created with the `GraphQlClient.SyncBuilder`, you create a
`SyncGraphQlClientInterceptor` to intercept all requests through the client:
[source,java,indent=0,subs="verbatim,quotes"]
----
static class MyInterceptor implements SyncGraphQlClientInterceptor {
@Override
public ClientGraphQlResponse intercept(ClientGraphQlRequest request, Chain chain) {
// ...
return chain.next(request);
}
}
----
include-code::SyncInterceptor[]
For non-blocking transports created with `GraphQlClient.Builder`, you create a
`GraphQlClientInterceptor` to intercept all requests through the client:
[source,java,indent=0,subs="verbatim,quotes"]
----
static class MyInterceptor implements GraphQlClientInterceptor {
@Override
public Mono<ClientGraphQlResponse> intercept(ClientGraphQlRequest request, Chain chain) {
// ...
return chain.next(request);
}
@Override
public Flux<ClientGraphQlResponse> interceptSubscription(ClientGraphQlRequest request, SubscriptionChain chain) {
// ...
return chain.next(request);
}
}
----
include-code::MyInterceptor[]
Once the interceptor is created, register it through the client builder. For example:
[source,java,indent=0,subs="verbatim,quotes"]
----
URI url = ... ;
WebSocketClient client = ... ;
WebSocketGraphQlClient graphQlClient = WebSocketGraphQlClient.builder(url, client)
.interceptor(new MyInterceptor())
.build();
----
include-code::UseInterceptor[tag=register,indent=0]
@@ -697,7 +427,7 @@ You can perform a request as follows:
List<Book> books = dgsClient.request(new BooksGraphQLQuery()) // <2>
.projection(new BooksProjectionRoot<>().id().name()) // <3>
.retrieveSync()
.retrieveSync("books")
.toEntityList(Book.class);
----

View File

@@ -74,66 +74,22 @@ GraphQL requests over HTTP, with or without a live server, depending on how
To test in Spring WebFlux, without a live server, point to your Spring configuration
that declares the GraphQL HTTP endpoint:
[source,java,indent=0,subs="verbatim,quotes"]
----
ApplicationContext context = ... ;
WebTestClient client =
WebTestClient.bindToApplicationContext(context)
.configureClient()
.baseUrl("/graphql")
.build();
HttpGraphQlTester tester = HttpGraphQlTester.create(client);
----
include-code::HttpSetup[tag=webTestClient,indent=0]
To test in Spring MVC, without a live server, do the same using `MockMvcWebTestClient`:
[source,java,indent=0,subs="verbatim,quotes"]
----
ApplicationContext context = ... ;
WebTestClient client =
MockMvcWebTestClient.bindToApplicationContext(context)
.configureClient()
.baseUrl("/graphql")
.build();
HttpGraphQlTester tester = HttpGraphQlTester.create(client);
----
include-code::HttpSetup[tag=mockMvc,indent=0]
Or to test against a live server running on a port:
[source,java,indent=0,subs="verbatim,quotes"]
----
WebTestClient client =
WebTestClient.bindToServer()
.baseUrl("http://localhost:8080/graphql")
.build();
HttpGraphQlTester tester = HttpGraphQlTester.create(client);
----
include-code::HttpSetup[tag=liveServer,indent=0]
Once `HttpGraphQlTester` is created, you can begin to
xref:testing.adoc#testing.requests[execute requests] using the same API, independent of the underlying
transport. If you need to change any transport specific details, use `mutate()` on an
existing `HttpSocketGraphQlTester` to create a new instance with customized settings:
[source,java,indent=0,subs="verbatim,quotes"]
----
HttpGraphQlTester tester = HttpGraphQlTester.builder(clientBuilder)
.headers(headers -> headers.setBasicAuth("joe", "..."))
.build();
// Use tester...
HttpGraphQlTester anotherTester = tester.mutate()
.headers(headers -> headers.setBasicAuth("peter", "..."))
.build();
// Use anotherTester...
----
include-code::HttpSetup[tag=executeRequests,indent=0]
@@ -145,13 +101,7 @@ It is built using the
{spring-framework-ref-docs}/web/webflux-websocket.html#webflux-websocket-client[WebSocketClient]
from Spring WebFlux and you can create it as follows:
[source,java,indent=0,subs="verbatim,quotes"]
----
String url = "http://localhost:8080/graphql";
WebSocketClient client = new ReactorNettyWebSocketClient();
WebSocketGraphQlTester tester = WebSocketGraphQlTester.builder(url, client).build();
----
include-code::WsSetup[tag=setup,indent=0]
`WebSocketGraphQlTester` is connection oriented and multiplexed. Each instance establishes
its own single, shared connection for all requests. Typically, you'll want to use a single
@@ -162,24 +112,7 @@ xref:testing.adoc#testing.requests[execute requests] using the same API, indepen
transport. If you need to change any transport specific details, use `mutate()` on an
existing `WebSocketGraphQlTester` to create a new instance with customized settings:
[source,java,indent=0,subs="verbatim,quotes"]
----
URI url = ... ;
WebSocketClient client = ... ;
WebSocketGraphQlTester tester = WebSocketGraphQlTester.builder(url, client)
.headers(headers -> headers.setBasicAuth("joe", "..."))
.build();
// Use tester...
WebSocketGraphQlTester anotherTester = tester.mutate()
.headers(headers -> headers.setBasicAuth("peter", "..."))
.build();
// Use anotherTester...
----
include-code::WsSetup[tag=customSetup,indent=0]
`WebSocketGraphQlTester` provides a `stop()` method that you can use to have the WebSocket
connection closed, e.g. after a test runs.
@@ -192,15 +125,7 @@ connection closed, e.g. after a test runs.
`RSocketGraphQlTester` uses `RSocketRequester` from spring-messaging to execute GraphQL
requests over RSocket:
[source,java,indent=0,subs="verbatim,quotes"]
----
URI uri = URI.create("wss://localhost:8080/rsocket");
WebsocketClientTransport transport = WebsocketClientTransport.create(url);
RSocketGraphQlTester client = RSocketGraphQlTester.builder()
.clientTransport(transport)
.build();
----
include-code::RSocketSetup[tag=rsocketSetup,indent=0]
`RSocketGraphQlTester` is connection oriented and multiplexed. Each instance establishes
its own single, shared session for all requests. Typically, you'll want to use a single
@@ -219,11 +144,7 @@ Many times it's enough to test GraphQL requests on the server side, without the
client to send requests over a transport protocol. To test directly against a
`ExecutionGraphQlService`, use the `ExecutionGraphQlServiceTester` extension:
[source,java,indent=0,subs="verbatim,quotes"]
----
ExecutionGraphQlService service = ... ;
ExecutionGraphQlServiceTester tester = ExecutionGraphQlServiceTester.create(service);
----
include-code::ServiceSetup[tag=serviceSetup,indent=0]
Once `ExecutionGraphQlServiceTester` is created, you can begin to
xref:testing.adoc#testing.requests[execute requests] using the same API, independent of the underlying
@@ -231,13 +152,7 @@ transport.
`ExecutionGraphQlServiceTester.Builder` provides an option to customize `ExecutionInput` details:
[source,java,indent=0,subs="verbatim,quotes"]
----
ExecutionGraphQlService service = ... ;
ExecutionGraphQlServiceTester tester = ExecutionGraphQlServiceTester.builder(service)
.configureExecutionInput((executionInput, builder) -> builder.executionId(id).build())
.build();
----
include-code::ServiceSetup[tag=customServiceSetup,indent=0]
@@ -252,22 +167,11 @@ The `WebGraphQlTester` extension lets you processes request through the
`WebGraphQlInterceptor` chain before handing off to `ExecutionGraphQlService` for
request execution:
[source,java,indent=0,subs="verbatim,quotes"]
----
WebGraphQlHandler handler = ... ;
WebGraphQlTester tester = WebGraphQlTester.create(handler);
----
include-code::WebSetup[tag=webSetup,indent=0]
The builder for this extension allows you to define HTTP request details:
[source,java,indent=0,subs="verbatim,quotes"]
----
WebGraphQlHandler handler = ... ;
WebGraphQlTester tester = WebGraphQlTester.builder(handler)
.headers(headers -> headers.setBasicAuth("joe", "..."))
.build();
----
include-code::WebSetup[tag=customWebSetup,indent=0]
Once `WebGraphQlTester` is created, you can begin to
xref:testing.adoc#testing.requests[execute requests] using the same API, independent of the underlying transport.
@@ -297,22 +201,7 @@ Once you have a `GraphQlTester`, you can begin to test requests. The below execu
query for a project and uses https://github.com/json-path/JsonPath[JsonPath] to extract
project release versions from the response:
[source,java,indent=0,subs="verbatim,quotes"]
----
String document = "{" +
" project(slug:\"spring-framework\") {" +
" releases {" +
" version" +
" }"+
" }" +
"}";
graphQlTester.document(document)
.execute()
.path("project.releases[*].version")
.entityList(String.class)
.hasSizeGreaterThan(1);
----
include-code::TesterRequests[tag=inlineDocument,indent=0]
The JsonPath is relative to the "data" section of the response.
@@ -335,18 +224,52 @@ For example, given a file called `projectReleases.graphql` in
You can then use:
[source,java,indent=0,subs="verbatim,quotes"]
----
graphQlTester.documentName("projectReleases") <1>
.variable("slug", "spring-framework") <2>
.execute()
.path("project.releases[*].version")
.entityList(String.class)
.hasSizeGreaterThan(1);
----
include-code::TesterRequests[tag=documentName,indent=0]
<1> Refer to the document in the file named "project".
<2> Set the `slug` variable.
This approach also works for loading fragments for your queries.
Fragments are reusable field selection sets that avoid repetition in a request document.
For example, we can use a `...releases` fragment in multiple queries:
[source,graphql,indent=0,subs="verbatim,quotes"]
.src/main/resources/graphql-documents/projectReleases.graphql
----
query frameworkReleases {
project(slug: "spring-framework") {
name
...releases
}
}
query graphqlReleases {
project(slug: "spring-graphql") {
name
...releases
}
}
----
This fragment can be defined in a separate file for reuse:
[source,graphql,indent=0,subs="verbatim,quotes"]
.src/main/resources/graphql-documents/releases.graphql
----
fragment releases on Project {
releases {
version
}
}
----
You can then send this fragment along the query document:
include-code::TesterRequests[tag=fragment,indent=0]
<1> Load the document from "projectReleases.graphql"
<2> Load the fragment from "releases.graphql" and append it to the document
[TIP]
====
The "JS GraphQL" plugin for IntelliJ supports GraphQL query files with code completion.
@@ -364,45 +287,24 @@ See xref:testing.adoc#testing.errors[Errors] for more details on error handling.
[[testing.requests.nestedPaths]]
[[testing.requests.nestedpaths]]
=== Nested Paths
By default, paths are relative to the "data" section of the GraphQL response. You can also
nest down to a path, and inspect multiple paths relative to it as follows:
[source,java,indent=0,subs="verbatim,quotes"]
----
graphQlTester.document(document)
.execute()
.path("project", project -> project // <1>
.path("name").entity(String.class).isEqualTo("spring-framework")
.path("releases[*].version").entityList(String.class).hasSizeGreaterThan(1));
----
include-code::NestedPaths[tag=nestedPaths,indent=0]
<1> Use a callback to inspect paths relative to "project".
[[testing.subscriptions]]
== Subscriptions
To test subscriptions, call `executeSubscription` instead of `execute` to obtain a stream
of responses and then use `StepVerifier` from Project Reactor to inspect the stream:
[source,java,indent=0,subs="verbatim,quotes"]
----
Flux<String> greetingFlux = tester.document("subscription { greetings }")
.executeSubscription()
.toFlux("greetings", String.class); // decode at JSONPath
StepVerifier.create(greetingFlux)
.expectNext("Hi")
.expectNext("Bonjour")
.expectNext("Hola")
.verifyComplete();
----
include-code::TestSubscriptions[tag=testSubscriptions,indent=0]
Subscriptions are supported only with xref:testing.adoc#testing.websocketgraphqltester[WebSocketGraphQlTester]
, or with the server side
@@ -417,51 +319,19 @@ When you use `verify()`, any errors under the "errors" key in the response will
an assertion failure. To suppress a specific error, use the error filter before
`verify()`:
[source,java,indent=0,subs="verbatim,quotes"]
----
graphQlTester.query(query)
.execute()
.errors()
.filter(error -> ...)
.verify()
.path("project.releases[*].version")
.entityList(String.class)
.hasSizeGreaterThan(1);
----
include-code::TestErrors[tag=verifyErrors,indent=0]
You can register an error filter at the builder level, to apply to all tests:
[source,java,indent=0,subs="verbatim,quotes"]
----
WebGraphQlTester graphQlTester = WebGraphQlTester.builder(client)
.errorFilter(error -> ...)
.build();
----
include-code::TestErrors[tag=setupErrorFilter,indent=0]
If you want to verify that an error does exist, and in contrast to `filter`, throw an
assertion error if it doesn't, then use `expect` instead:
[source,java,indent=0,subs="verbatim,quotes"]
----
graphQlTester.query(query)
.execute()
.errors()
.expect(error -> ...)
.verify()
.path("project.releases[*].version")
.entityList(String.class)
.hasSizeGreaterThan(1);
----
include-code::TestErrors[tag=expectedErrors,indent=0]
You can also inspect all errors through a `Consumer`, and doing so also marks them as
filtered, so you can then also inspect the data in the response:
[source,java,indent=0,subs="verbatim,quotes"]
----
graphQlTester.query(query)
.execute()
.errors()
.satisfy(errors -> {
// ...
});
----
include-code::TestErrors[tag=satisfyErrors,indent=0]

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2020-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.docs.client.httpgraphqlclient;
import org.springframework.graphql.client.HttpGraphQlClient;
import org.springframework.web.reactive.function.client.WebClient;
public class ClientUsage {
void create() {
// tag::create[]
WebClient webClient = WebClient.create("https://spring.io/graphql");
HttpGraphQlClient graphQlClient = HttpGraphQlClient.create(webClient);
// end::create[]
}
void mutateClient() {
// tag::mutate[]
WebClient webClient = WebClient.create("https://spring.io/graphql");
HttpGraphQlClient graphQlClient = HttpGraphQlClient.builder(webClient)
.headers((headers) -> headers.setBasicAuth("joe", "..."))
.build();
// Perform requests with graphQlClient...
HttpGraphQlClient anotherGraphQlClient = graphQlClient.mutate()
.headers((headers) -> headers.setBasicAuth("peter", "..."))
.build();
// Perform requests with anotherGraphQlClient...
// end::mutate[]
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2020-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.docs.client.httpsyncgraphqlclient;
import org.springframework.graphql.client.HttpSyncGraphQlClient;
import org.springframework.web.client.RestClient;
public class SyncClientUsage {
void createClient() {
// tag::create[]
RestClient restClient = RestClient.create("https://spring.io/graphql");
HttpSyncGraphQlClient graphQlClient = HttpSyncGraphQlClient.create(restClient);
// end::create[]
}
void mutateClient() {
// tag::mutate[]
RestClient restClient = RestClient.create("https://spring.io/graphql");
HttpSyncGraphQlClient graphQlClient = HttpSyncGraphQlClient.builder(restClient)
.headers((headers) -> headers.setBasicAuth("joe", "..."))
.build();
// Perform requests with graphQlClient...
HttpSyncGraphQlClient anotherGraphQlClient = graphQlClient.mutate()
.headers((headers) -> headers.setBasicAuth("peter", "..."))
.build();
// Perform requests with anotherGraphQlClient...
// end::mutate[]
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2020-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.docs.client.interception;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.graphql.client.ClientGraphQlRequest;
import org.springframework.graphql.client.ClientGraphQlResponse;
import org.springframework.graphql.client.GraphQlClientInterceptor;
public class MyInterceptor implements GraphQlClientInterceptor {
@Override
public Mono<ClientGraphQlResponse> intercept(ClientGraphQlRequest request, Chain chain) {
// ...
return chain.next(request);
}
@Override
public Flux<ClientGraphQlResponse> interceptSubscription(ClientGraphQlRequest request, SubscriptionChain chain) {
// ...
return chain.next(request);
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2020-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.docs.client.interception;
import org.springframework.graphql.client.ClientGraphQlRequest;
import org.springframework.graphql.client.ClientGraphQlResponse;
import org.springframework.graphql.client.SyncGraphQlClientInterceptor;
public class SyncInterceptor implements SyncGraphQlClientInterceptor {
@Override
public ClientGraphQlResponse intercept(ClientGraphQlRequest request, Chain chain) {
// ...
return chain.next(request);
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2020-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.docs.client.interception;
import java.net.URI;
import org.springframework.graphql.client.WebSocketGraphQlClient;
import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient;
import org.springframework.web.reactive.socket.client.WebSocketClient;
public class UseInterceptor {
void registerInterceptor() {
// tag::register[]
URI url = URI.create("wss://localhost:8080/graphql");
WebSocketClient client = new ReactorNettyWebSocketClient();
WebSocketGraphQlClient graphQlClient = WebSocketGraphQlClient.builder(url, client)
.interceptor(new MyInterceptor())
.build();
// end::register[]
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2020-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.docs.client.requests.documentsource;
import org.springframework.graphql.client.GraphQlClient;
public class DocumentSource {
void documentSource() {
GraphQlClient graphQlClient = null;
// tag::documentSource[]
Project project = graphQlClient.documentName("projectReleases") // <1>
.variable("slug", "spring-framework") // <2>
.retrieveSync("projectReleases.project")
.toEntity(Project.class);
// end::documentSource[]
}
record Project() {
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright 2020-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.docs.client.requests.execute;
import reactor.core.publisher.Mono;
import org.springframework.graphql.client.ClientGraphQlResponse;
import org.springframework.graphql.client.ClientResponseField;
import org.springframework.graphql.client.GraphQlClient;
public class Execute {
void executeSync() {
GraphQlClient graphQlClient = null;
String document = "";
// tag::executeSync[]
ClientGraphQlResponse response = graphQlClient.document(document).executeSync();
if (!response.isValid()) {
// Request failure... <1>
}
ClientResponseField field = response.field("project");
if (field.getValue() == null) {
if (field.getErrors().isEmpty()) {
// Optional field set to null... <2>
}
else {
// Field failure... <3>
}
}
Project project = field.toEntity(Project.class); // <4>
// end::executeSync[]
}
void execute() {
GraphQlClient graphQlClient = null;
String document = "";
// tag::execute[]
Mono<Project> projectMono = graphQlClient.document(document)
.execute()
.map((response) -> {
if (!response.isValid()) {
// Request failure... <1>
}
ClientResponseField field = response.field("project");
if (field.getValue() == null) {
if (field.getErrors().isEmpty()) {
// Optional field set to null... <2>
}
else {
// Field failure... <3>
}
}
return field.toEntity(Project.class); // <4>
});
// end::execute[]
}
record Project() {
}
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright 2020-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.docs.client.requests.retrieve;
import reactor.core.publisher.Mono;
import org.springframework.graphql.client.ClientGraphQlResponse;
import org.springframework.graphql.client.ClientResponseField;
import org.springframework.graphql.client.FieldAccessException;
import org.springframework.graphql.client.GraphQlClient;
public class Retrieve {
void retrieveSync() {
GraphQlClient graphQlClient = null;
// tag::retrieveSync[]
String document =
"""
{
project(slug:"spring-framework") {
name
releases {
version
}
}
}
""";
Project project = graphQlClient.document(document) // <1>
.retrieveSync("project") // <2>
.toEntity(Project.class); // <3>
// end::retrieveSync[]
}
void retrieve() {
GraphQlClient graphQlClient = null;
// tag::retrieve[]
String document =
"""
{
project(slug:"spring-framework") {
name
releases {
version
}
}
}
""";
Mono<Project> project = graphQlClient.document(document) // <1>
.retrieve("project") // <2>
.toEntity(Project.class); // <3>
// end::retrieve[]
}
Project fieldErrorSync() {
GraphQlClient graphQlClient = null;
String document = "";
// tag::fieldErrorSync[]
try {
Project project = graphQlClient.document(document)
.retrieveSync("project")
.toEntity(Project.class);
return project;
}
catch (FieldAccessException ex) {
ClientGraphQlResponse response = ex.getResponse();
// ...
ClientResponseField field = ex.getField();
// return fallback value
return new Project();
}
// end::fieldErrorSync[]
}
void fieldError() {
GraphQlClient graphQlClient = null;
String document = "";
// tag::fieldError[]
Mono<Project> projectMono = graphQlClient.document(document)
.retrieve("project")
.toEntity(Project.class)
.onErrorResume(FieldAccessException.class, (ex) -> {
ClientGraphQlResponse response = ex.getResponse();
// ...
ClientResponseField field = ex.getField();
// return fallback value
return Mono.just(new Project());
});
// end::fieldError[]
}
record Project() {
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2020-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.docs.client.rsocketgraphqlclient;
import java.net.URI;
import io.rsocket.transport.netty.client.WebsocketClientTransport;
import org.springframework.graphql.client.RSocketGraphQlClient;
public class RSocketClientUsage {
void create() {
// tag::create[]
URI uri = URI.create("wss://localhost:8080/rsocket");
WebsocketClientTransport transport = WebsocketClientTransport.create(uri);
RSocketGraphQlClient client = RSocketGraphQlClient.builder()
.clientTransport(transport)
.build();
// end::create[]
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2020-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.docs.client.subscriptions.execute;
import reactor.core.publisher.Flux;
import org.springframework.graphql.client.ClientResponseField;
import org.springframework.graphql.client.GraphQlClient;
public class ExecuteSubscription {
void subscriptionExecute() {
GraphQlClient client = null;
// tag::subscriptionExecute[]
Flux<String> greetingFlux = client.document("subscription { greetings }")
.executeSubscription()
.map((response) -> {
if (!response.isValid()) {
// Request failure...
}
ClientResponseField field = response.field("project");
if (field.getValue() == null) {
if (field.getErrors().isEmpty()) {
// Optional field set to null...
}
else {
// Field failure...
}
}
return field.toEntity(String.class);
});
// end::subscriptionExecute[]
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2020-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.docs.client.subscriptions.retrieve;
import reactor.core.publisher.Flux;
import org.springframework.graphql.client.GraphQlClient;
public class RetrieveSubscription {
void subscriptionRetrieve() {
GraphQlClient client = null;
// tag::subscriptionRetrieve[]
Flux<String> greetingFlux = client.document("subscription { greetings }")
.retrieveSubscription("greeting")
.toEntity(String.class);
// end::subscriptionRetrieve[]
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2020-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.docs.client.websocketgraphqlclient;
import java.time.Duration;
import org.springframework.graphql.client.WebSocketGraphQlClient;
import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient;
import org.springframework.web.reactive.socket.client.WebSocketClient;
public class WebSocketClientUsage {
void create() {
// tag::create[]
String url = "wss://spring.io/graphql";
WebSocketClient client = new ReactorNettyWebSocketClient();
WebSocketGraphQlClient graphQlClient = WebSocketGraphQlClient.builder(url, client).build();
// end::create[]
}
void mutate() {
// tag::mutate[]
String url = "wss://spring.io/graphql";
WebSocketClient client = new ReactorNettyWebSocketClient();
WebSocketGraphQlClient graphQlClient = WebSocketGraphQlClient.builder(url, client)
.headers((headers) -> headers.setBasicAuth("joe", "..."))
.build();
// Use graphQlClient...
WebSocketGraphQlClient anotherGraphQlClient = graphQlClient.mutate()
.headers((headers) -> headers.setBasicAuth("peter", "..."))
.build();
// Use anotherGraphQlClient...
// end::mutate[]
}
void keepAlive() {
// tag::keepAlive[]
String url = "wss://spring.io/graphql";
WebSocketClient client = new ReactorNettyWebSocketClient();
WebSocketGraphQlClient graphQlClient = WebSocketGraphQlClient.builder(url, client)
.keepAlive(Duration.ofSeconds(30))
.build();
// end::keepAlive[]
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2020-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.docs.testing.errors;
import org.springframework.graphql.server.WebGraphQlHandler;
import org.springframework.graphql.test.tester.GraphQlTester;
import org.springframework.graphql.test.tester.WebGraphQlTester;
import static org.assertj.core.api.Assertions.assertThat;
public class TestErrors {
void verifyErrors() {
GraphQlTester graphQlTester = null;
String query = "";
// tag::verifyErrors[]
graphQlTester.document(query)
.execute()
.errors()
.filter((error) -> error.getMessage().equals("ignored error"))
.verify()
.path("project.releases[*].version")
.entityList(String.class)
.hasSizeGreaterThan(1);
// end::verifyErrors[]
}
void setupErrorFilter() {
WebGraphQlHandler handler = null;
// tag::setupErrorFilter[]
WebGraphQlTester graphQlTester = WebGraphQlTester.builder(handler)
.errorFilter((error) -> error.getMessage().equals("ignored error"))
.build();
// end::setupErrorFilter[]
}
void expectedErrors() {
GraphQlTester graphQlTester = null;
String query = "";
// tag::expectedErrors[]
graphQlTester.document(query)
.execute()
.errors()
.expect((error) -> error.getMessage().equals("expected error"))
.verify()
.path("project.releases[*].version")
.entityList(String.class)
.hasSizeGreaterThan(1);
// end::expectedErrors[]
}
void satisfyErrors() {
GraphQlTester graphQlTester = null;
String document = "";
// tag::satisfyErrors[]
graphQlTester.document(document)
.execute()
.errors()
.satisfy((errors) ->
assertThat(errors)
.anyMatch((error) -> error.getMessage().contains("ignored error"))
);
// end::satisfyErrors[]
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2020-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.docs.testing.graphqlservicetester;
import graphql.execution.ExecutionId;
import org.springframework.graphql.ExecutionGraphQlService;
import org.springframework.graphql.test.tester.ExecutionGraphQlServiceTester;
public class ServiceSetup {
void serviceSetup() {
// tag::serviceSetup[]
ExecutionGraphQlService service = /**/ null;
ExecutionGraphQlServiceTester tester = ExecutionGraphQlServiceTester.create(service);
// end::serviceSetup[]
}
void customServiceSetup() {
// tag::customServiceSetup[]
ExecutionGraphQlService service = /**/ null;
ExecutionId executionId = ExecutionId.generate();
ExecutionGraphQlServiceTester tester = ExecutionGraphQlServiceTester.builder(service)
.configureExecutionInput((executionInput, builder) -> builder.executionId(executionId).build())
.build();
// end::customServiceSetup[]
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright 2020-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.docs.testing.httpgraphqltester;
import org.springframework.graphql.test.tester.HttpGraphQlTester;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.test.web.servlet.client.MockMvcWebTestClient;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
public class HttpSetup {
void webTestClient() {
// tag::webTestClient[]
AnnotationConfigWebApplicationContext context = /**/ new AnnotationConfigWebApplicationContext();
WebTestClient client =
WebTestClient.bindToApplicationContext(context)
.configureClient()
.baseUrl("/graphql")
.build();
HttpGraphQlTester tester = HttpGraphQlTester.create(client);
// end::webTestClient[]
}
void mockMvc() {
// tag::mockMvc[]
AnnotationConfigWebApplicationContext context = /**/ new AnnotationConfigWebApplicationContext();
WebTestClient client =
MockMvcWebTestClient.bindToApplicationContext(context)
.configureClient()
.baseUrl("/graphql")
.build();
HttpGraphQlTester tester = HttpGraphQlTester.create(client);
// end::mockMvc[]
}
void liveServer() {
// tag::liveServer[]
WebTestClient client =
WebTestClient.bindToServer()
.baseUrl("http://localhost:8080/graphql")
.build();
HttpGraphQlTester tester = HttpGraphQlTester.create(client);
// end::liveServer[]
}
void executeRequests() {
// tag::executeRequests[]
WebTestClient.Builder clientBuilder =
WebTestClient.bindToServer()
.baseUrl("http://localhost:8080/graphql");
HttpGraphQlTester tester = HttpGraphQlTester.builder(clientBuilder)
.headers((headers) -> headers.setBasicAuth("joe", "..."))
.build();
// Use tester...
HttpGraphQlTester anotherTester = tester.mutate()
.headers((headers) -> headers.setBasicAuth("peter", "..."))
.build();
// Use anotherTester...
// end::executeRequests[]
}
static class Configuration {
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2020-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.docs.testing.requests;
import org.springframework.graphql.test.tester.GraphQlTester;
public class TesterRequests {
void inlineDocument() {
GraphQlTester graphQlTester = null;
// tag::inlineDocument[]
String document =
"""
{
project(slug:"spring-framework") {
releases {
version
}
}
}
""";
graphQlTester.document(document)
.execute()
.path("project.releases[*].version")
.entityList(String.class)
.hasSizeGreaterThan(1);
// end::inlineDocument[]
}
void documentName() {
GraphQlTester graphQlTester = null;
// tag::documentName[]
graphQlTester.documentName("projectReleases") // <1>
.variable("slug", "spring-framework") // <2>
.execute()
.path("projectReleases.project.releases[*].version")
.entityList(String.class)
.hasSizeGreaterThan(1);
// end::documentName[]
}
void fragment() {
GraphQlTester graphQlTester = null;
// tag::fragment[]
graphQlTester.documentName("projectReleases") // <1>
.fragmentName("releases") // <2>
.execute()
.path("frameworkReleases.project.releases[*].version")
.entityList(String.class)
.hasSizeGreaterThan(1);
// end::fragment[]
}
record Project() {
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2020-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.docs.testing.requests.nestedpaths;
import org.springframework.graphql.test.tester.GraphQlTester;
public class NestedPaths {
void nestedPaths() {
GraphQlTester graphQlTester = null;
String document = "";
// tag::nestedPaths[]
graphQlTester.document(document)
.execute()
.path("project", (project) -> project // <1>
.path("name").entity(String.class).isEqualTo("spring-framework")
.path("releases[*].version").entityList(String.class).hasSizeGreaterThan(1));
// end::nestedPaths[]
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2020-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.docs.testing.rsocketgraphqltester;
import java.net.URI;
import io.rsocket.transport.netty.client.WebsocketClientTransport;
import org.springframework.graphql.test.tester.RSocketGraphQlTester;
public class RSocketSetup {
void rSocketSetup() {
// tag::rsocketSetup[]
URI url = URI.create("wss://localhost:8080/rsocket");
WebsocketClientTransport transport = WebsocketClientTransport.create(url);
RSocketGraphQlTester client = RSocketGraphQlTester.builder()
.clientTransport(transport)
.build();
// end::rsocketSetup[]
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2020-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.docs.testing.subscriptions;
import reactor.core.publisher.Flux;
import reactor.test.StepVerifier;
import org.springframework.graphql.test.tester.GraphQlTester;
public class TestSubscriptions {
void testSubscriptions() {
GraphQlTester tester = null;
// tag::testSubscriptions[]
Flux<String> greetingFlux = tester.document("subscription { greetings }")
.executeSubscription()
.toFlux("greetings", String.class); // decode at JSONPath
StepVerifier.create(greetingFlux)
.expectNext("Hi")
.expectNext("Bonjour")
.expectNext("Hola")
.verifyComplete();
// end::testSubscriptions[]
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2020-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.docs.testing.webgraphqltester;
import org.springframework.graphql.server.WebGraphQlHandler;
import org.springframework.graphql.test.tester.WebGraphQlTester;
public class WebSetup {
void webSetup() {
// tag::webSetup[]
WebGraphQlHandler handler = /**/ null;
WebGraphQlTester tester = WebGraphQlTester.create(handler);
// end::webSetup[]
}
void customWebSetup() {
// tag::customWebSetup[]
WebGraphQlHandler handler = /**/ null;
WebGraphQlTester tester = WebGraphQlTester.builder(handler)
.headers((headers) -> headers.setBasicAuth("joe", "..."))
.build();
// end::customWebSetup[]
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2020-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.docs.testing.websocketgraphqltester;
import java.net.URI;
import org.springframework.graphql.test.tester.WebSocketGraphQlTester;
import org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient;
import org.springframework.web.reactive.socket.client.WebSocketClient;
public class WsSetup {
void setup() {
// tag::setup[]
String url = "http://localhost:8080/graphql";
WebSocketClient client = new ReactorNettyWebSocketClient();
WebSocketGraphQlTester tester = WebSocketGraphQlTester.builder(url, client).build();
// end::setup[]
}
void customSetup() {
// tag::customSetup[]
URI url = URI.create("ws://localhost:8080/graphql");
WebSocketClient client = new ReactorNettyWebSocketClient();
WebSocketGraphQlTester tester = WebSocketGraphQlTester.builder(url, client)
.headers((headers) -> headers.setBasicAuth("joe", "..."))
.build();
// Use tester...
WebSocketGraphQlTester anotherTester = tester.mutate()
.headers((headers) -> headers.setBasicAuth("peter", "..."))
.build();
// Use anotherTester...
// end::customSetup[]
}
}