Refactoring in Client and Tester builder implementations

The default transport-specific client and tester extensions contained
their builder implementations, but it makes more sense the other way
around since the extensions are mainly about the builders they provide.
This commit is contained in:
rstoyanchev
2022-03-29 14:47:35 +01:00
parent f9b84cb8d0
commit b3f13b978a
30 changed files with 1228 additions and 1209 deletions

View File

@@ -198,7 +198,7 @@ request execution as any `GraphQlTester`.
Many times it's enough to test GraphQL requests on the server side, without the use of a
client to send requests over a transport protocol. To test directly against a
`ExecutionGraphQlService`, use the `GraphQlServiceTester` extension:
`ExecutionGraphQlService`, use the `ExecutionGraphQlServiceTester` extension:
[source,java,indent=0,subs="verbatim,quotes"]
----

View File

@@ -0,0 +1,89 @@
/*
* Copyright 2002-2022 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.test.tester;
import java.util.function.Consumer;
import org.springframework.graphql.ExecutionGraphQlService;
import org.springframework.util.Assert;
/**
* Default {@link ExecutionGraphQlServiceTester.Builder} implementation that
* wraps an {@code ExecutionGraphQlService}.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class DefaultExecutionGraphQlServiceTesterBuilder
extends AbstractGraphQlTesterBuilder<DefaultExecutionGraphQlServiceTesterBuilder>
implements ExecutionGraphQlServiceTester.Builder<DefaultExecutionGraphQlServiceTesterBuilder> {
private final ExecutionGraphQlService service;
DefaultExecutionGraphQlServiceTesterBuilder(ExecutionGraphQlService service) {
Assert.notNull(service, "GraphQlService is required");
this.service = service;
}
DefaultExecutionGraphQlServiceTesterBuilder(GraphQlServiceGraphQlTransport transport) {
this.service = transport.getGraphQlService();
}
@Override
public ExecutionGraphQlServiceTester build() {
GraphQlServiceGraphQlTransport transport = new GraphQlServiceGraphQlTransport(this.service);
GraphQlTester tester = super.buildGraphQlTester(transport);
return new DefaultExecutionGraphQlServiceTester(tester, transport, getBuilderInitializer());
}
/**
* Default {@link ExecutionGraphQlServiceTester} implementation.
*/
private static class DefaultExecutionGraphQlServiceTester
extends AbstractDelegatingGraphQlTester implements ExecutionGraphQlServiceTester {
private final GraphQlServiceGraphQlTransport transport;
private final Consumer<AbstractGraphQlTesterBuilder<?>> builderInitializer;
private DefaultExecutionGraphQlServiceTester(GraphQlTester tester, GraphQlServiceGraphQlTransport transport,
Consumer<AbstractGraphQlTesterBuilder<?>> builderInitializer) {
super(tester);
Assert.notNull(transport, "GraphQlServiceTransport is required");
Assert.notNull(builderInitializer, "`builderInitializer` is required");
this.transport = transport;
this.builderInitializer = builderInitializer;
}
@Override
public DefaultExecutionGraphQlServiceTesterBuilder mutate() {
DefaultExecutionGraphQlServiceTesterBuilder builder = new DefaultExecutionGraphQlServiceTesterBuilder(this.transport);
this.builderInitializer.accept(builder);
return builder;
}
}
}

View File

@@ -1,87 +0,0 @@
/*
* Copyright 2002-2022 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.test.tester;
import java.util.function.Consumer;
import org.springframework.graphql.ExecutionGraphQlService;
import org.springframework.util.Assert;
/**
* Default {@link GraphQlServiceTester} that uses a {@link ExecutionGraphQlService} for
* request execution.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class DefaultGraphQlServiceTester extends AbstractDelegatingGraphQlTester implements GraphQlServiceTester {
private final GraphQlServiceGraphQlTransport transport;
private final Consumer<AbstractGraphQlTesterBuilder<?>> builderInitializer;
private DefaultGraphQlServiceTester(GraphQlTester tester, GraphQlServiceGraphQlTransport transport,
Consumer<AbstractGraphQlTesterBuilder<?>> builderInitializer) {
super(tester);
Assert.notNull(transport, "GraphQlServiceTransport is required");
Assert.notNull(builderInitializer, "`builderInitializer` is required");
this.transport = transport;
this.builderInitializer = builderInitializer;
}
@Override
public Builder<?> mutate() {
Builder<?> builder = new Builder<>(this.transport);
this.builderInitializer.accept(builder);
return builder;
}
/**
* Default {@link GraphQlServiceTester.Builder} implementation.
*/
static class Builder<B extends Builder<B>> extends AbstractGraphQlTesterBuilder<B>
implements GraphQlServiceTester.Builder<B> {
private final ExecutionGraphQlService service;
Builder(ExecutionGraphQlService service) {
Assert.notNull(service, "GraphQlService is required");
this.service = service;
}
Builder(GraphQlServiceGraphQlTransport transport) {
this.service = transport.getGraphQlService();
}
@Override
public GraphQlServiceTester build() {
GraphQlServiceGraphQlTransport transport = new GraphQlServiceGraphQlTransport(this.service);
GraphQlTester tester = super.buildGraphQlTester(transport);
return new DefaultGraphQlServiceTester(tester, transport, getBuilderInitializer());
}
}
}

View File

@@ -1,128 +0,0 @@
/*
* Copyright 2002-2022 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.test.tester;
import java.net.URI;
import java.util.function.Consumer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.codec.CodecConfigurer;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.util.DefaultUriBuilderFactory;
import org.springframework.web.util.UriBuilderFactory;
import org.springframework.web.util.UriComponentsBuilder;
/**
* Default {@link HttpGraphQlTester} that builds and uses a {@link WebTestClient}
* for request execution.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class DefaultHttpGraphQlTester extends AbstractDelegatingGraphQlTester implements HttpGraphQlTester {
private final WebTestClient webTestClient;
private final Consumer<AbstractGraphQlTesterBuilder<?>> builderInitializer;
private DefaultHttpGraphQlTester(GraphQlTester graphQlTester, WebTestClient webTestClient,
Consumer<AbstractGraphQlTesterBuilder<?>> builderInitializer) {
super(graphQlTester);
this.webTestClient = webTestClient;
this.builderInitializer = builderInitializer;
}
@Override
public Builder mutate() {
Builder builder = new Builder(this.webTestClient.mutate());
this.builderInitializer.accept(builder);
return builder;
}
/**
* Default {@link HttpGraphQlTester.Builder} implementation.
*/
static final class Builder extends AbstractGraphQlTesterBuilder<Builder>
implements HttpGraphQlTester.Builder<Builder> {
private final WebTestClient.Builder webTestClientBuilder;
Builder(WebTestClient.Builder clientBuilder) {
this.webTestClientBuilder = clientBuilder;
}
@Override
public Builder url(String url) {
this.webTestClientBuilder.baseUrl(url);
return this;
}
@Override
public Builder url(URI url) {
UriBuilderFactory factory = new DefaultUriBuilderFactory(UriComponentsBuilder.fromUri(url));
this.webTestClientBuilder.uriBuilderFactory(factory);
return this;
}
@Override
public Builder header(String name, String... values) {
this.webTestClientBuilder.defaultHeader(name, values);
return this;
}
@Override
public Builder headers(Consumer<HttpHeaders> headersConsumer) {
this.webTestClientBuilder.defaultHeaders(headersConsumer);
return this;
}
@Override
public Builder codecConfigurer(Consumer<CodecConfigurer> codecConsumer) {
this.webTestClientBuilder.codecs(codecConsumer::accept);
return this;
}
@Override
public Builder webTestClient(Consumer<WebTestClient.Builder> configurer) {
configurer.accept(this.webTestClientBuilder);
return this;
}
@Override
public HttpGraphQlTester build() {
registerJsonPathMappingProvider();
WebTestClient client = this.webTestClientBuilder.build();
GraphQlTester tester = super.buildGraphQlTester(new WebTestClientTransport(client));
return new DefaultHttpGraphQlTester(tester, client, getBuilderInitializer());
}
private void registerJsonPathMappingProvider() {
this.webTestClientBuilder.codecs(codecConfigurer ->
configureJsonPathConfig(config -> {
EncoderDecoderMappingProvider provider = new EncoderDecoderMappingProvider(codecConfigurer);
return config.mappingProvider(provider);
}));
}
}
}

View File

@@ -0,0 +1,131 @@
/*
* Copyright 2002-2022 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.test.tester;
import java.net.URI;
import java.util.function.Consumer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.codec.CodecConfigurer;
import org.springframework.test.web.reactive.server.WebTestClient;
import org.springframework.web.util.DefaultUriBuilderFactory;
import org.springframework.web.util.UriBuilderFactory;
import org.springframework.web.util.UriComponentsBuilder;
/**
* Default {@link HttpGraphQlTester.Builder} implementation, simply wraps a
* {@link WebTestClient.Builder}.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class DefaultHttpGraphQlTesterBuilder
extends AbstractGraphQlTesterBuilder<DefaultHttpGraphQlTesterBuilder>
implements HttpGraphQlTester.Builder<DefaultHttpGraphQlTesterBuilder> {
private final WebTestClient.Builder webTestClientBuilder;
DefaultHttpGraphQlTesterBuilder(WebTestClient.Builder clientBuilder) {
this.webTestClientBuilder = clientBuilder;
}
@Override
public DefaultHttpGraphQlTesterBuilder url(String url) {
this.webTestClientBuilder.baseUrl(url);
return this;
}
@Override
public DefaultHttpGraphQlTesterBuilder url(URI url) {
UriBuilderFactory factory = new DefaultUriBuilderFactory(UriComponentsBuilder.fromUri(url));
this.webTestClientBuilder.uriBuilderFactory(factory);
return this;
}
@Override
public DefaultHttpGraphQlTesterBuilder header(String name, String... values) {
this.webTestClientBuilder.defaultHeader(name, values);
return this;
}
@Override
public DefaultHttpGraphQlTesterBuilder headers(Consumer<HttpHeaders> headersConsumer) {
this.webTestClientBuilder.defaultHeaders(headersConsumer);
return this;
}
@Override
public DefaultHttpGraphQlTesterBuilder codecConfigurer(Consumer<CodecConfigurer> codecConsumer) {
this.webTestClientBuilder.codecs(codecConsumer::accept);
return this;
}
@Override
public DefaultHttpGraphQlTesterBuilder webTestClient(Consumer<WebTestClient.Builder> configurer) {
configurer.accept(this.webTestClientBuilder);
return this;
}
@Override
public HttpGraphQlTester build() {
registerJsonPathMappingProvider();
WebTestClient client = this.webTestClientBuilder.build();
GraphQlTester tester = super.buildGraphQlTester(new WebTestClientTransport(client));
return new DefaultHttpGraphQlTester(tester, client, getBuilderInitializer());
}
private void registerJsonPathMappingProvider() {
this.webTestClientBuilder.codecs(codecConfigurer ->
configureJsonPathConfig(config -> {
EncoderDecoderMappingProvider provider = new EncoderDecoderMappingProvider(codecConfigurer);
return config.mappingProvider(provider);
}));
}
/**
* Default {@link HttpGraphQlTester} that builds and uses a {@link WebTestClient}
* for request execution.
*/
private static class DefaultHttpGraphQlTester extends AbstractDelegatingGraphQlTester implements HttpGraphQlTester {
private final WebTestClient webTestClient;
private final Consumer<AbstractGraphQlTesterBuilder<?>> builderInitializer;
private DefaultHttpGraphQlTester(GraphQlTester graphQlTester, WebTestClient webTestClient,
Consumer<AbstractGraphQlTesterBuilder<?>> builderInitializer) {
super(graphQlTester);
this.webTestClient = webTestClient;
this.builderInitializer = builderInitializer;
}
@Override
public DefaultHttpGraphQlTesterBuilder mutate() {
DefaultHttpGraphQlTesterBuilder builder = new DefaultHttpGraphQlTesterBuilder(this.webTestClient.mutate());
this.builderInitializer.accept(builder);
return builder;
}
}
}

View File

@@ -1,150 +0,0 @@
/*
* Copyright 2002-2022 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.test.tester;
import java.net.URI;
import java.util.List;
import java.util.function.Consumer;
import io.rsocket.transport.ClientTransport;
import org.springframework.core.codec.Decoder;
import org.springframework.core.codec.Encoder;
import org.springframework.graphql.client.RSocketGraphQlClient;
import org.springframework.messaging.rsocket.RSocketRequester;
import org.springframework.messaging.rsocket.RSocketStrategies;
import org.springframework.util.MimeType;
/**
* Default {@link RSocketGraphQlTester} that builds and uses an
* {@link RSocketGraphQlClient} for request execution.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
public class DefaultRSocketGraphQlTester extends AbstractDelegatingGraphQlTester implements RSocketGraphQlTester {
private final RSocketGraphQlClient rsocketGraphQlClient;
private final Consumer<AbstractGraphQlTesterBuilder<?>> builderInitializer;
DefaultRSocketGraphQlTester(
GraphQlTester delegate, RSocketGraphQlClient rsocketGraphQlClient,
Consumer<AbstractGraphQlTesterBuilder<?>> builderInitializer) {
super(delegate);
this.rsocketGraphQlClient = rsocketGraphQlClient;
this.builderInitializer = builderInitializer;
}
@Override
public RSocketGraphQlTester.Builder<?> mutate() {
Builder builder = new Builder(this.rsocketGraphQlClient);
this.builderInitializer.accept(builder);
return builder;
}
/**
* Default implementation of {@link GraphQlTester.Builder}.
*/
static final class Builder extends AbstractGraphQlTesterBuilder<Builder> implements RSocketGraphQlTester.Builder<Builder> {
private final RSocketGraphQlClient.Builder<?> rsocketGraphQlClientBuilder;
/**
* Constructor to start via {@link RSocketGraphQlTester#builder()}.
*/
Builder() {
this.rsocketGraphQlClientBuilder = RSocketGraphQlClient.builder();
}
/**
* Constructor to start via {@link RSocketGraphQlTester#builder(RSocketRequester.Builder)}.
*/
Builder(RSocketRequester.Builder requesterBuilder) {
this.rsocketGraphQlClientBuilder = RSocketGraphQlClient.builder(requesterBuilder);
}
/**
* Constructor to mutate.
* @param rsocketGraphQlClient the underlying client with the current state
*/
public Builder(RSocketGraphQlClient rsocketGraphQlClient) {
this.rsocketGraphQlClientBuilder = rsocketGraphQlClient.mutate();
}
@Override
public Builder tcp(String host, int port) {
this.rsocketGraphQlClientBuilder.tcp(host, port);
return this;
}
@Override
public Builder webSocket(URI uri) {
this.rsocketGraphQlClientBuilder.webSocket(uri);
return this;
}
@Override
public Builder clientTransport(ClientTransport clientTransport) {
this.rsocketGraphQlClientBuilder.clientTransport(clientTransport);
return this;
}
@Override
public Builder dataMimeType(MimeType dataMimeType) {
this.rsocketGraphQlClientBuilder.dataMimeType(dataMimeType);
return this;
}
@Override
public Builder route(String route) {
this.rsocketGraphQlClientBuilder.route(route);
return this;
}
@Override
public Builder rsocketRequester(Consumer<RSocketRequester.Builder> requesterConsumer) {
this.rsocketGraphQlClientBuilder.rsocketRequester(requesterConsumer);
return this;
}
@Override
public RSocketGraphQlTester build() {
registerJsonPathMappingProvider();
RSocketGraphQlClient rsocketGraphQlClient = this.rsocketGraphQlClientBuilder.build();
GraphQlTester graphQlTester = super.buildGraphQlTester(asTransport(rsocketGraphQlClient));
return new DefaultRSocketGraphQlTester(graphQlTester, rsocketGraphQlClient, getBuilderInitializer());
}
private void registerJsonPathMappingProvider() {
this.rsocketGraphQlClientBuilder.rsocketRequester(builder ->
builder.rsocketStrategies(strategiesBuilder ->
configureJsonPathConfig(config -> {
RSocketStrategies strategies = strategiesBuilder.build();
List<Encoder<?>> encoders = strategies.encoders();
List<Decoder<?>> decoders = strategies.decoders();
return config.mappingProvider(new EncoderDecoderMappingProvider(encoders, decoders));
})));
}
}
}

View File

@@ -0,0 +1,153 @@
/*
* Copyright 2002-2022 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.test.tester;
import java.net.URI;
import java.util.List;
import java.util.function.Consumer;
import io.rsocket.transport.ClientTransport;
import org.springframework.core.codec.Decoder;
import org.springframework.core.codec.Encoder;
import org.springframework.graphql.client.RSocketGraphQlClient;
import org.springframework.messaging.rsocket.RSocketRequester;
import org.springframework.messaging.rsocket.RSocketStrategies;
import org.springframework.util.MimeType;
/**
* Default implementation of {@link RSocketGraphQlTester.Builder} that wraps
* an {@link RSocketGraphQlClient.Builder}.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
public class DefaultRSocketGraphQlTesterBuilder
extends AbstractGraphQlTesterBuilder<DefaultRSocketGraphQlTesterBuilder>
implements RSocketGraphQlTester.Builder<DefaultRSocketGraphQlTesterBuilder> {
private final RSocketGraphQlClient.Builder<?> rsocketGraphQlClientBuilder;
/**
* Constructor to start via {@link RSocketGraphQlTester#builder()}.
*/
DefaultRSocketGraphQlTesterBuilder() {
this.rsocketGraphQlClientBuilder = RSocketGraphQlClient.builder();
}
/**
* Constructor to start via {@link RSocketGraphQlTester#builder(RSocketRequester.Builder)}.
*/
DefaultRSocketGraphQlTesterBuilder(RSocketRequester.Builder requesterBuilder) {
this.rsocketGraphQlClientBuilder = RSocketGraphQlClient.builder(requesterBuilder);
}
/**
* Constructor to mutate.
* @param rsocketGraphQlClient the underlying client with the current state
*/
public DefaultRSocketGraphQlTesterBuilder(RSocketGraphQlClient rsocketGraphQlClient) {
this.rsocketGraphQlClientBuilder = rsocketGraphQlClient.mutate();
}
@Override
public DefaultRSocketGraphQlTesterBuilder tcp(String host, int port) {
this.rsocketGraphQlClientBuilder.tcp(host, port);
return this;
}
@Override
public DefaultRSocketGraphQlTesterBuilder webSocket(URI uri) {
this.rsocketGraphQlClientBuilder.webSocket(uri);
return this;
}
@Override
public DefaultRSocketGraphQlTesterBuilder clientTransport(ClientTransport clientTransport) {
this.rsocketGraphQlClientBuilder.clientTransport(clientTransport);
return this;
}
@Override
public DefaultRSocketGraphQlTesterBuilder dataMimeType(MimeType dataMimeType) {
this.rsocketGraphQlClientBuilder.dataMimeType(dataMimeType);
return this;
}
@Override
public DefaultRSocketGraphQlTesterBuilder route(String route) {
this.rsocketGraphQlClientBuilder.route(route);
return this;
}
@Override
public DefaultRSocketGraphQlTesterBuilder rsocketRequester(Consumer<RSocketRequester.Builder> requesterConsumer) {
this.rsocketGraphQlClientBuilder.rsocketRequester(requesterConsumer);
return this;
}
@Override
public RSocketGraphQlTester build() {
registerJsonPathMappingProvider();
RSocketGraphQlClient rsocketGraphQlClient = this.rsocketGraphQlClientBuilder.build();
GraphQlTester graphQlTester = super.buildGraphQlTester(asTransport(rsocketGraphQlClient));
return new DefaultRSocketGraphQlTester(graphQlTester, rsocketGraphQlClient, getBuilderInitializer());
}
private void registerJsonPathMappingProvider() {
this.rsocketGraphQlClientBuilder.rsocketRequester(builder ->
builder.rsocketStrategies(strategiesBuilder ->
configureJsonPathConfig(config -> {
RSocketStrategies strategies = strategiesBuilder.build();
List<Encoder<?>> encoders = strategies.encoders();
List<Decoder<?>> decoders = strategies.decoders();
return config.mappingProvider(new EncoderDecoderMappingProvider(encoders, decoders));
})));
}
/**
* Default {@link RSocketGraphQlTester} implementation.
*/
private static class DefaultRSocketGraphQlTester extends AbstractDelegatingGraphQlTester implements RSocketGraphQlTester {
private final RSocketGraphQlClient rsocketGraphQlClient;
private final Consumer<AbstractGraphQlTesterBuilder<?>> builderInitializer;
DefaultRSocketGraphQlTester(
GraphQlTester delegate, RSocketGraphQlClient rsocketGraphQlClient,
Consumer<AbstractGraphQlTesterBuilder<?>> builderInitializer) {
super(delegate);
this.rsocketGraphQlClient = rsocketGraphQlClient;
this.builderInitializer = builderInitializer;
}
@Override
public RSocketGraphQlTester.Builder<?> mutate() {
DefaultRSocketGraphQlTesterBuilder builder = new DefaultRSocketGraphQlTesterBuilder(this.rsocketGraphQlClient);
this.builderInitializer.accept(builder);
return builder;
}
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2002-2022 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.test.tester;
import java.util.function.Consumer;
import org.springframework.graphql.client.GraphQlTransport;
import org.springframework.util.Assert;
/**
* Default {@link GraphQlTester.Builder} with a given, externally prepared transport.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class DefaultTransportGraphQlTesterBuilder
extends AbstractGraphQlTesterBuilder<DefaultTransportGraphQlTesterBuilder> {
private final GraphQlTransport transport;
DefaultTransportGraphQlTesterBuilder(GraphQlTransport transport) {
this.transport = transport;
}
@Override
public GraphQlTester build() {
GraphQlTester tester = super.buildGraphQlTester(this.transport);
return new DefaultTransportGraphQlTester(tester, this.transport, getBuilderInitializer());
}
/**
* {@link GraphQlTester} with a given transport.
*/
private static class DefaultTransportGraphQlTester extends AbstractDelegatingGraphQlTester {
private final GraphQlTransport transport;
private final Consumer<AbstractGraphQlTesterBuilder<?>> builderInitializer;
private DefaultTransportGraphQlTester(
GraphQlTester delegate, GraphQlTransport transport,
Consumer<AbstractGraphQlTesterBuilder<?>> builderInitializer) {
super(delegate);
Assert.notNull(transport, "GraphQlTransport is required");
Assert.notNull(builderInitializer, "'builderInitializer' is required");
this.transport = transport;
this.builderInitializer = builderInitializer;
}
@Override
public DefaultTransportGraphQlTesterBuilder mutate() {
DefaultTransportGraphQlTesterBuilder builder = new DefaultTransportGraphQlTesterBuilder(this.transport);
this.builderInitializer.accept(builder);
return builder;
}
}
}

View File

@@ -28,114 +28,111 @@ import org.springframework.http.codec.CodecConfigurer;
import org.springframework.util.Assert;
import org.springframework.web.util.DefaultUriBuilderFactory;
/**
* Default {@link WebGraphQlTester} that uses {@link WebGraphQlHandler} for
* request execution.
* Default implementation for {@link WebGraphQlTester} that initializes a
* {@link WebGraphQlHandler} for request execution.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class DefaultWebGraphQlTester extends AbstractDelegatingGraphQlTester implements WebGraphQlTester {
final class DefaultWebGraphQlTesterBuilder
extends AbstractGraphQlTesterBuilder<DefaultWebGraphQlTesterBuilder>
implements WebGraphQlTester.Builder<DefaultWebGraphQlTesterBuilder> {
private final WebGraphQlHandlerGraphQlTransport transport;
private URI url = URI.create("");
private final Consumer<AbstractGraphQlTesterBuilder<?>> builderInitializer;
private final HttpHeaders headers = new HttpHeaders();
private final WebGraphQlHandler handler;
private CodecConfigurer codecConfigurer = ClientCodecConfigurer.create();
private DefaultWebGraphQlTester(GraphQlTester tester, WebGraphQlHandlerGraphQlTransport transport,
Consumer<AbstractGraphQlTesterBuilder<?>> builderInitializer) {
DefaultWebGraphQlTesterBuilder(WebGraphQlHandler handler) {
Assert.notNull(handler, "WebGraphQlHandler is required");
this.handler = handler;
}
super(tester);
this.transport = transport;
this.builderInitializer = builderInitializer;
DefaultWebGraphQlTesterBuilder(WebGraphQlHandlerGraphQlTransport transport) {
this.url = transport.getUrl();
this.headers.putAll(transport.getHeaders());
this.handler = transport.getGraphQlHandler();
this.codecConfigurer = transport.getCodecConfigurer();
}
@Override
public Builder<?> mutate() {
Builder<?> builder = new Builder<>(this.transport);
this.builderInitializer.accept(builder);
return builder;
public DefaultWebGraphQlTesterBuilder url(String url) {
return url(new DefaultUriBuilderFactory().uriString(url).build());
}
@Override
public DefaultWebGraphQlTesterBuilder url(URI url) {
this.url = url;
return this;
}
@Override
public DefaultWebGraphQlTesterBuilder header(String name, String... values) {
this.headers.put(name, Arrays.asList(values));
return this;
}
@Override
public DefaultWebGraphQlTesterBuilder headers(Consumer<HttpHeaders> headersConsumer) {
headersConsumer.accept(this.headers);
return this;
}
@Override
public DefaultWebGraphQlTesterBuilder codecConfigurer(Consumer<CodecConfigurer> codecConfigurerConsumer) {
codecConfigurerConsumer.accept(this.codecConfigurer);
return this;
}
@Override
public WebGraphQlTester build() {
registerJsonPathMappingProvider();
WebGraphQlHandlerGraphQlTransport transport =
new WebGraphQlHandlerGraphQlTransport(this.url, this.headers, this.handler, this.codecConfigurer);
GraphQlTester tester = super.buildGraphQlTester(transport);
return new DefaultWebGraphQlTester(tester, transport, getBuilderInitializer());
}
private void registerJsonPathMappingProvider() {
configureJsonPathConfig(jsonPathConfig -> {
EncoderDecoderMappingProvider provider = new EncoderDecoderMappingProvider(this.codecConfigurer);
return jsonPathConfig.mappingProvider(provider);
});
}
/**
* Base builder implementation for all Web transport extensions.
* Default {@link WebGraphQlTester} implementation.
*/
static class Builder<B extends Builder<B>> extends AbstractGraphQlTesterBuilder<B>
implements WebGraphQlTester.Builder<B> {
private static class DefaultWebGraphQlTester extends AbstractDelegatingGraphQlTester implements WebGraphQlTester {
private URI url = URI.create("");
private final WebGraphQlHandlerGraphQlTransport transport;
private final HttpHeaders headers = new HttpHeaders();
private final Consumer<AbstractGraphQlTesterBuilder<?>> builderInitializer;
private final WebGraphQlHandler handler;
private DefaultWebGraphQlTester(GraphQlTester tester, WebGraphQlHandlerGraphQlTransport transport,
Consumer<AbstractGraphQlTesterBuilder<?>> builderInitializer) {
private CodecConfigurer codecConfigurer = ClientCodecConfigurer.create();
Builder(WebGraphQlHandler handler) {
Assert.notNull(handler, "WebGraphQlHandler is required");
this.handler = handler;
}
Builder(WebGraphQlHandlerGraphQlTransport transport) {
this.url = transport.getUrl();
this.headers.putAll(transport.getHeaders());
this.handler = transport.getGraphQlHandler();
this.codecConfigurer = transport.getCodecConfigurer();
super(tester);
this.transport = transport;
this.builderInitializer = builderInitializer;
}
@Override
public B url(String url) {
return url(new DefaultUriBuilderFactory().uriString(url).build());
}
@Override
public B url(URI url) {
this.url = url;
return self();
}
@Override
public B header(String name, String... values) {
this.headers.put(name, Arrays.asList(values));
return self();
}
@Override
public B headers(Consumer<HttpHeaders> headersConsumer) {
headersConsumer.accept(this.headers);
return self();
}
@Override
public B codecConfigurer(Consumer<CodecConfigurer> codecConfigurerConsumer) {
codecConfigurerConsumer.accept(this.codecConfigurer);
return self();
}
@SuppressWarnings("unchecked")
protected <T extends B> T self() {
return (T) this;
}
@Override
public WebGraphQlTester build() {
registerJsonPathMappingProvider();
WebGraphQlHandlerGraphQlTransport transport =
new WebGraphQlHandlerGraphQlTransport(this.url, this.headers, this.handler, this.codecConfigurer);
GraphQlTester tester = super.buildGraphQlTester(transport);
return new DefaultWebGraphQlTester(tester, transport, getBuilderInitializer());
}
private void registerJsonPathMappingProvider() {
configureJsonPathConfig(jsonPathConfig -> {
EncoderDecoderMappingProvider provider = new EncoderDecoderMappingProvider(this.codecConfigurer);
return jsonPathConfig.mappingProvider(provider);
});
public DefaultWebGraphQlTesterBuilder mutate() {
DefaultWebGraphQlTesterBuilder builder = new DefaultWebGraphQlTesterBuilder(this.transport);
this.builderInitializer.accept(builder);
return builder;
}
}

View File

@@ -28,127 +28,130 @@ import org.springframework.http.codec.CodecConfigurer;
import org.springframework.util.Assert;
import org.springframework.web.reactive.socket.client.WebSocketClient;
/**
* Default {@link WebSocketGraphQlTester} that builds and uses a
* {@link WebSocketGraphQlClient} for request execution.
* Default {@link WebSocketGraphQlTester.Builder} implementation, wraps a
* {@link WebSocketGraphQlClient.Builder}.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class DefaultWebSocketGraphQlTester extends AbstractDelegatingGraphQlTester implements WebSocketGraphQlTester {
final class DefaultWebSocketGraphQlTesterBuilder
extends AbstractGraphQlTesterBuilder<DefaultWebSocketGraphQlTesterBuilder>
implements WebSocketGraphQlTester.Builder<DefaultWebSocketGraphQlTesterBuilder> {
private final WebSocketGraphQlClient webSocketGraphQlClient;
private final Consumer<AbstractGraphQlTesterBuilder<?>> builderInitializer;
private final WebSocketGraphQlClient.Builder<?> graphQlClientBuilder;
private DefaultWebSocketGraphQlTester(
GraphQlTester graphQlTester, WebSocketGraphQlClient webSocketGraphQlClient,
Consumer<AbstractGraphQlTesterBuilder<?>> builderInitializer) {
/**
* Constructor to start via {@link WebSocketGraphQlTester#builder(String, WebSocketClient)}.
*/
DefaultWebSocketGraphQlTesterBuilder(String url, WebSocketClient webSocketClient) {
Assert.notNull(webSocketClient, "WebSocketClient is required");
this.graphQlClientBuilder = WebSocketGraphQlClient.builder(url, webSocketClient);
}
super(graphQlTester);
this.webSocketGraphQlClient = webSocketGraphQlClient;
this.builderInitializer = builderInitializer;
/**
* Constructor to start via {@link WebSocketGraphQlTester#builder(URI, WebSocketClient)}.
*/
DefaultWebSocketGraphQlTesterBuilder(URI url, WebSocketClient webSocketClient) {
Assert.notNull(webSocketClient, "WebSocketClient is required");
this.graphQlClientBuilder = WebSocketGraphQlClient.builder(url, webSocketClient);
}
/**
* Constructor to mutate.
* @param client the underlying client with the current state
*/
DefaultWebSocketGraphQlTesterBuilder(WebSocketGraphQlClient client) {
Assert.notNull(client, "WebSocketGraphQlClient is required");
this.graphQlClientBuilder = client.mutate();
}
@Override
public Mono<Void> start() {
return this.webSocketGraphQlClient.start();
public DefaultWebSocketGraphQlTesterBuilder url(String url) {
this.graphQlClientBuilder.url(url);
return this;
}
@Override
public Mono<Void> stop() {
return this.webSocketGraphQlClient.stop();
public DefaultWebSocketGraphQlTesterBuilder url(URI url) {
this.graphQlClientBuilder.url(url);
return this;
}
@Override
public Builder mutate() {
Builder builder = new Builder(this.webSocketGraphQlClient);
this.builderInitializer.accept(builder);
return builder;
public DefaultWebSocketGraphQlTesterBuilder header(String name, String... values) {
this.graphQlClientBuilder.header(name, values);
return this;
}
@Override
public DefaultWebSocketGraphQlTesterBuilder headers(Consumer<HttpHeaders> headersConsumer) {
this.graphQlClientBuilder.headers(headersConsumer);
return this;
}
@Override
public DefaultWebSocketGraphQlTesterBuilder codecConfigurer(Consumer<CodecConfigurer> codecsConsumer) {
this.graphQlClientBuilder.codecConfigurer(codecsConsumer);
return this;
}
@Override
public WebSocketGraphQlTester build() {
registerJsonPathMappingProvider();
WebSocketGraphQlClient client = this.graphQlClientBuilder.build();
GraphQlTester graphQlTester = super.buildGraphQlTester(asTransport(client));
return new DefaultWebSocketGraphQlTester(graphQlTester, client, getBuilderInitializer());
}
private void registerJsonPathMappingProvider() {
this.graphQlClientBuilder.codecConfigurer(codecConfigurer -> {
configureJsonPathConfig(jsonPathConfig -> {
EncoderDecoderMappingProvider provider = new EncoderDecoderMappingProvider(codecConfigurer);
return jsonPathConfig.mappingProvider(provider);
});
});
}
/**
* Default {@link WebSocketGraphQlTester.Builder} implementation.
* Default {@link WebSocketGraphQlTester} implementation.
*/
static final class Builder extends AbstractGraphQlTesterBuilder<Builder> implements WebSocketGraphQlTester.Builder<Builder> {
private static class DefaultWebSocketGraphQlTester extends AbstractDelegatingGraphQlTester implements WebSocketGraphQlTester {
private final WebSocketGraphQlClient.Builder<?> graphQlClientBuilder;
private final WebSocketGraphQlClient client;
/**
* Constructor to start via {@link WebSocketGraphQlTester#builder(String, WebSocketClient)}.
*/
Builder(String url, WebSocketClient webSocketClient) {
Assert.notNull(webSocketClient, "WebSocketClient is required");
this.graphQlClientBuilder = WebSocketGraphQlClient.builder(url, webSocketClient);
}
private final Consumer<AbstractGraphQlTesterBuilder<?>> builderInitializer;
/**
* Constructor to start via {@link WebSocketGraphQlTester#builder(URI, WebSocketClient)}.
*/
Builder(URI url, WebSocketClient webSocketClient) {
Assert.notNull(webSocketClient, "WebSocketClient is required");
this.graphQlClientBuilder = WebSocketGraphQlClient.builder(url, webSocketClient);
}
private DefaultWebSocketGraphQlTester(
GraphQlTester graphQlTester, WebSocketGraphQlClient client,
Consumer<AbstractGraphQlTesterBuilder<?>> builderInitializer) {
/**
* Constructor to mutate.
* @param client the underlying client with the current state
*/
Builder(WebSocketGraphQlClient client) {
Assert.notNull(client, "WebSocketGraphQlClient is required");
this.graphQlClientBuilder = client.mutate();
}
@Override
public Builder url(String url) {
this.graphQlClientBuilder.url(url);
return this;
super(graphQlTester);
this.client = client;
this.builderInitializer = builderInitializer;
}
@Override
public Builder url(URI url) {
this.graphQlClientBuilder.url(url);
return this;
public Mono<Void> start() {
return this.client.start();
}
@Override
public Builder header(String name, String... values) {
this.graphQlClientBuilder.header(name, values);
return this;
public Mono<Void> stop() {
return this.client.stop();
}
@Override
public Builder headers(Consumer<HttpHeaders> headersConsumer) {
this.graphQlClientBuilder.headers(headersConsumer);
return this;
public DefaultWebSocketGraphQlTesterBuilder mutate() {
DefaultWebSocketGraphQlTesterBuilder builder = new DefaultWebSocketGraphQlTesterBuilder(this.client);
this.builderInitializer.accept(builder);
return builder;
}
@Override
public Builder codecConfigurer(Consumer<CodecConfigurer> codecsConsumer) {
this.graphQlClientBuilder.codecConfigurer(codecsConsumer);
return this;
}
@Override
public WebSocketGraphQlTester build() {
registerJsonPathMappingProvider();
WebSocketGraphQlClient client = this.graphQlClientBuilder.build();
GraphQlTester graphQlTester = super.buildGraphQlTester(asTransport(client));
return new DefaultWebSocketGraphQlTester(graphQlTester, client, getBuilderInitializer());
}
private void registerJsonPathMappingProvider() {
this.graphQlClientBuilder.codecConfigurer(codecConfigurer -> {
configureJsonPathConfig(jsonPathConfig -> {
EncoderDecoderMappingProvider provider = new EncoderDecoderMappingProvider(codecConfigurer);
return jsonPathConfig.mappingProvider(provider);
});
});
}
}
}

View File

@@ -19,13 +19,13 @@ package org.springframework.graphql.test.tester;
import org.springframework.graphql.ExecutionGraphQlService;
/**
* {@link GraphQlTester} that executes requests through a {@link ExecutionGraphQlService}
* Use it for server-side tests, without a client.
* {@link GraphQlTester} that executes requests through an
* {@link ExecutionGraphQlService} on the server side, without a client.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
public interface GraphQlServiceTester extends GraphQlTester {
public interface ExecutionGraphQlServiceTester extends GraphQlTester {
@Override
@@ -33,30 +33,30 @@ public interface GraphQlServiceTester extends GraphQlTester {
/**
* Create a {@link GraphQlServiceTester} instance.
* Create a {@link ExecutionGraphQlServiceTester} instance.
*/
static GraphQlServiceTester create(ExecutionGraphQlService service) {
static ExecutionGraphQlServiceTester create(ExecutionGraphQlService service) {
return builder(service).build();
}
/**
* Return a builder for {@link GraphQlServiceTester}.
* Return a builder for {@link ExecutionGraphQlServiceTester}.
*/
static GraphQlServiceTester.Builder<?> builder(ExecutionGraphQlService service) {
return new DefaultGraphQlServiceTester.Builder<>(service);
static ExecutionGraphQlServiceTester.Builder<?> builder(ExecutionGraphQlService service) {
return new DefaultExecutionGraphQlServiceTesterBuilder(service);
}
/**
* Default {@link GraphQlServiceTester.Builder} implementation.
* Default {@link ExecutionGraphQlServiceTester.Builder} implementation.
*/
interface Builder<B extends Builder<B>> extends GraphQlTester.Builder<B> {
/**
* Build a {@link GraphQlServiceTester} instance.
* Build a {@link ExecutionGraphQlServiceTester} instance.
*/
@Override
GraphQlServiceTester build();
ExecutionGraphQlServiceTester build();
}

View File

@@ -1,77 +0,0 @@
/*
* Copyright 2002-2022 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.test.tester;
import java.util.function.Consumer;
import org.springframework.graphql.client.GraphQlTransport;
import org.springframework.util.Assert;
/**
* {@link GraphQlTester} with a given, externally prepared transport.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class GenericGraphQlTester extends AbstractDelegatingGraphQlTester {
private final GraphQlTransport transport;
private final Consumer<AbstractGraphQlTesterBuilder<?>> builderInitializer;
private GenericGraphQlTester(
GraphQlTester delegate, GraphQlTransport transport,
Consumer<AbstractGraphQlTesterBuilder<?>> builderInitializer) {
super(delegate);
Assert.notNull(transport, "GraphQlTransport is required");
Assert.notNull(builderInitializer, "'builderInitializer' is required");
this.transport = transport;
this.builderInitializer = builderInitializer;
}
@Override
public Builder mutate() {
Builder builder = new Builder(this.transport);
this.builderInitializer.accept(builder);
return builder;
}
/**
* Default {@link GraphQlTester.Builder} with a given transport.
*/
static final class Builder extends AbstractGraphQlTesterBuilder<Builder> {
private final GraphQlTransport transport;
Builder(GraphQlTransport transport) {
this.transport = transport;
}
@Override
public GraphQlTester build() {
GraphQlTester tester = super.buildGraphQlTester(this.transport);
return new GenericGraphQlTester(tester, this.transport, getBuilderInitializer());
}
}
}

View File

@@ -43,7 +43,7 @@ import org.springframework.lang.Nullable;
*
* <p>To test on the server side, without a client, see the following:
* <ul>
* <li>{@link GraphQlServiceTester}
* <li>{@link ExecutionGraphQlServiceTester}
* <li>{@link WebGraphQlTester}
* </ul>
*
@@ -88,7 +88,7 @@ public interface GraphQlTester {
* @return the builder for further initialization
*/
static GraphQlTester.Builder<?> builder(GraphQlTransport transport) {
return new GenericGraphQlTester.Builder(transport);
return new DefaultTransportGraphQlTesterBuilder(transport);
}

View File

@@ -47,7 +47,7 @@ public interface HttpGraphQlTester extends WebGraphQlTester {
* the underlying {@link WebTestClient} through the given builder.
*/
static HttpGraphQlTester.Builder<?> builder(WebTestClient.Builder webTestClientBuilder) {
return new DefaultHttpGraphQlTester.Builder(webTestClientBuilder);
return new DefaultHttpGraphQlTesterBuilder(webTestClientBuilder);
}

View File

@@ -44,14 +44,14 @@ public interface RSocketGraphQlTester extends GraphQlTester {
* and adding JSON codecs.
*/
static RSocketGraphQlTester.Builder<?> builder() {
return new DefaultRSocketGraphQlTester.Builder();
return new DefaultRSocketGraphQlTesterBuilder();
}
/**
* Start with a given {@link #builder()}.
*/
static RSocketGraphQlTester.Builder<?> builder(RSocketRequester.Builder requesterBuilder) {
return new DefaultRSocketGraphQlTester.Builder(requesterBuilder);
return new DefaultRSocketGraphQlTesterBuilder(requesterBuilder);
}

View File

@@ -25,7 +25,7 @@ import org.springframework.http.codec.CodecConfigurer;
/**
* Server-side tester, without a client, that executes requests through a
* {@link WebGraphQlHandler}. Similar to {@link GraphQlServiceTester} but also
* {@link WebGraphQlHandler}. Similar to {@link ExecutionGraphQlServiceTester} but also
* adding a web processing layer with a {@code WebGraphQlInterceptor}
* chain.
*
@@ -53,7 +53,7 @@ public interface WebGraphQlTester extends GraphQlTester {
* @param graphQlHandler the handler to execute requests
*/
static WebGraphQlTester.Builder<?> builder(WebGraphQlHandler graphQlHandler) {
return new DefaultWebGraphQlTester.Builder<>(graphQlHandler);
return new DefaultWebGraphQlTesterBuilder(graphQlHandler);
}

View File

@@ -62,7 +62,7 @@ public interface WebSocketGraphQlTester extends WebGraphQlTester {
* @param webSocketClient the underlying transport client to use
*/
static WebSocketGraphQlTester.Builder<?> builder(String url, WebSocketClient webSocketClient) {
return new DefaultWebSocketGraphQlTester.Builder(url, webSocketClient);
return new DefaultWebSocketGraphQlTesterBuilder(url, webSocketClient);
}
/**
@@ -71,7 +71,7 @@ public interface WebSocketGraphQlTester extends WebGraphQlTester {
* @param webSocketClient the underlying transport client to use
*/
static WebSocketGraphQlTester.Builder<?> builder(URI url, WebSocketClient webSocketClient) {
return new DefaultWebSocketGraphQlTester.Builder(url, webSocketClient);
return new DefaultWebSocketGraphQlTesterBuilder(url, webSocketClient);
}

View File

@@ -50,7 +50,7 @@ public class GraphQlTesterTestSupport {
private final ExecutionGraphQlService graphQlService = mock(ExecutionGraphQlService.class);
private final GraphQlTester.Builder<?> graphQlTesterBuilder = GraphQlServiceTester.builder(this.graphQlService);
private final GraphQlTester.Builder<?> graphQlTesterBuilder = ExecutionGraphQlServiceTester.builder(this.graphQlService);
private final GraphQlTester graphQlTester = this.graphQlTesterBuilder.build();

View File

@@ -1,147 +0,0 @@
/*
* Copyright 2002-2022 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.client;
import java.net.URI;
import java.util.function.Consumer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.codec.CodecConfigurer;
import org.springframework.util.Assert;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.DefaultUriBuilderFactory;
import org.springframework.web.util.UriBuilderFactory;
import org.springframework.web.util.UriComponentsBuilder;
/**
* Default {@link HttpGraphQlClient} implementation that builds the underlying
* {@code HttpGraphQlTransport} to use.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class DefaultHttpGraphQlClient extends AbstractDelegatingGraphQlClient implements HttpGraphQlClient {
private final WebClient webClient;
private final Consumer<AbstractGraphQlClientBuilder<?>> builderInitializer;
DefaultHttpGraphQlClient(GraphQlClient graphQlClient, WebClient webClient,
Consumer<AbstractGraphQlClientBuilder<?>> builderInitializer) {
super(graphQlClient);
Assert.notNull(webClient, "WebClient is required");
Assert.notNull(builderInitializer, "`builderInitializer` is required");
this.webClient = webClient;
this.builderInitializer = builderInitializer;
}
public Builder mutate() {
Builder builder = new Builder(this.webClient);
this.builderInitializer.accept(builder);
return builder;
}
/**
* Default {@link HttpGraphQlClient.Builder} implementation, simply wrapping
* and delegating to {@link WebClient.Builder}.
*/
static final class Builder extends AbstractGraphQlClientBuilder<Builder> implements HttpGraphQlClient.Builder<Builder> {
private final WebClient.Builder webClientBuilder;
/**
* Constructor to start without a WebClient instance.
*/
Builder() {
this(WebClient.builder());
}
/**
* Constructor to start with a pre-configured {@code WebClient}.
*/
Builder(WebClient client) {
this(client.mutate());
}
/**
* Constructor to start with a pre-configured {@code WebClient}.
*/
Builder(WebClient.Builder clientBuilder) {
this.webClientBuilder = clientBuilder;
}
@Override
public Builder url(String url) {
this.webClientBuilder.baseUrl(url);
return this;
}
@Override
public Builder url(URI url) {
UriBuilderFactory factory = new DefaultUriBuilderFactory(UriComponentsBuilder.fromUri(url));
this.webClientBuilder.uriBuilderFactory(factory);
return this;
}
@Override
public Builder header(String name, String... values) {
this.webClientBuilder.defaultHeader(name, values);
return this;
}
@Override
public Builder headers(Consumer<HttpHeaders> headersConsumer) {
this.webClientBuilder.defaultHeaders(headersConsumer);
return this;
}
@Override
public Builder codecConfigurer(Consumer<CodecConfigurer> codecConfigurerConsumer) {
this.webClientBuilder.codecs(codecConfigurerConsumer::accept);
return this;
}
@Override
public Builder webClient(Consumer<WebClient.Builder> configurer) {
configurer.accept(this.webClientBuilder);
return this;
}
@Override
public HttpGraphQlClient build() {
// Pass the codecs to the parent for response decoding
this.webClientBuilder.codecs(configurer ->
setJsonCodecs(
CodecDelegate.findJsonEncoder(configurer),
CodecDelegate.findJsonDecoder(configurer)));
WebClient webClient = this.webClientBuilder.build();
GraphQlClient graphQlClient = super.buildGraphQlClient(new HttpGraphQlTransport(webClient));
return new DefaultHttpGraphQlClient(graphQlClient, webClient, getBuilderInitializer());
}
}
}

View File

@@ -0,0 +1,150 @@
/*
* Copyright 2002-2022 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.client;
import java.net.URI;
import java.util.function.Consumer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.codec.CodecConfigurer;
import org.springframework.util.Assert;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.DefaultUriBuilderFactory;
import org.springframework.web.util.UriBuilderFactory;
import org.springframework.web.util.UriComponentsBuilder;
/**
* Default {@link HttpGraphQlClient.Builder} implementation, a simple wrapper
* around a {@link WebClient.Builder}.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class DefaultHttpGraphQlClientBuilder
extends AbstractGraphQlClientBuilder<DefaultHttpGraphQlClientBuilder>
implements HttpGraphQlClient.Builder<DefaultHttpGraphQlClientBuilder> {
private final WebClient.Builder webClientBuilder;
/**
* Constructor to start without a WebClient instance.
*/
DefaultHttpGraphQlClientBuilder() {
this(WebClient.builder());
}
/**
* Constructor to start with a pre-configured {@code WebClient}.
*/
DefaultHttpGraphQlClientBuilder(WebClient client) {
this(client.mutate());
}
/**
* Constructor to start with a pre-configured {@code WebClient}.
*/
DefaultHttpGraphQlClientBuilder(WebClient.Builder clientBuilder) {
this.webClientBuilder = clientBuilder;
}
@Override
public DefaultHttpGraphQlClientBuilder url(String url) {
this.webClientBuilder.baseUrl(url);
return this;
}
@Override
public DefaultHttpGraphQlClientBuilder url(URI url) {
UriBuilderFactory factory = new DefaultUriBuilderFactory(UriComponentsBuilder.fromUri(url));
this.webClientBuilder.uriBuilderFactory(factory);
return this;
}
@Override
public DefaultHttpGraphQlClientBuilder header(String name, String... values) {
this.webClientBuilder.defaultHeader(name, values);
return this;
}
@Override
public DefaultHttpGraphQlClientBuilder headers(Consumer<HttpHeaders> headersConsumer) {
this.webClientBuilder.defaultHeaders(headersConsumer);
return this;
}
@Override
public DefaultHttpGraphQlClientBuilder codecConfigurer(Consumer<CodecConfigurer> codecConfigurerConsumer) {
this.webClientBuilder.codecs(codecConfigurerConsumer::accept);
return this;
}
@Override
public DefaultHttpGraphQlClientBuilder webClient(Consumer<WebClient.Builder> configurer) {
configurer.accept(this.webClientBuilder);
return this;
}
@Override
public HttpGraphQlClient build() {
// Pass the codecs to the parent for response decoding
this.webClientBuilder.codecs(configurer ->
setJsonCodecs(
CodecDelegate.findJsonEncoder(configurer),
CodecDelegate.findJsonDecoder(configurer)));
WebClient webClient = this.webClientBuilder.build();
GraphQlClient graphQlClient = super.buildGraphQlClient(new HttpGraphQlTransport(webClient));
return new DefaultHttpGraphQlClient(graphQlClient, webClient, getBuilderInitializer());
}
/**
* Default {@link HttpGraphQlClient} implementation.
*/
private static class DefaultHttpGraphQlClient extends AbstractDelegatingGraphQlClient implements HttpGraphQlClient {
private final WebClient webClient;
private final Consumer<AbstractGraphQlClientBuilder<?>> builderInitializer;
DefaultHttpGraphQlClient(
GraphQlClient delegate, WebClient webClient,
Consumer<AbstractGraphQlClientBuilder<?>> builderInitializer) {
super(delegate);
Assert.notNull(webClient, "WebClient is required");
Assert.notNull(builderInitializer, "`builderInitializer` is required");
this.webClient = webClient;
this.builderInitializer = builderInitializer;
}
public DefaultHttpGraphQlClientBuilder mutate() {
DefaultHttpGraphQlClientBuilder builder = new DefaultHttpGraphQlClientBuilder(this.webClient);
this.builderInitializer.accept(builder);
return builder;
}
}
}

View File

@@ -1,166 +0,0 @@
/*
* Copyright 2002-2022 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.client;
import java.net.URI;
import java.util.function.Consumer;
import io.rsocket.transport.ClientTransport;
import io.rsocket.transport.netty.client.TcpClientTransport;
import io.rsocket.transport.netty.client.WebsocketClientTransport;
import org.springframework.lang.Nullable;
import org.springframework.messaging.rsocket.RSocketRequester;
import org.springframework.messaging.rsocket.RSocketStrategies;
import org.springframework.util.Assert;
import org.springframework.util.MimeType;
/**
* Default {@link RSocketGraphQlClient} implementation that builds the underlying
* {@code RSocketGraphQlTransport} to use.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class DefaultRSocketGraphQlClient extends AbstractDelegatingGraphQlClient implements RSocketGraphQlClient {
private final RSocketRequester.Builder requesterBuilder;
private final ClientTransport clientTransport;
private final String route;
private final Consumer<AbstractGraphQlClientBuilder<?>> builderInitializer;
DefaultRSocketGraphQlClient(
GraphQlClient graphQlClient, RSocketRequester.Builder requesterBuilder,
ClientTransport clientTransport, String route,
Consumer<AbstractGraphQlClientBuilder<?>> builderInitializer) {
super(graphQlClient);
this.requesterBuilder = requesterBuilder;
this.clientTransport = clientTransport;
this.route = route;
this.builderInitializer = builderInitializer;
}
@Override
public RSocketGraphQlClient.Builder<?> mutate() {
Builder builder = new Builder(this.requesterBuilder);
builder.clientTransport(this.clientTransport);
builder.route(this.route);
this.builderInitializer.accept(builder);
return builder;
}
/**
* Default {@link RSocketGraphQlClient.Builder} implementation.
*/
static final class Builder extends AbstractGraphQlClientBuilder<Builder> implements RSocketGraphQlClient.Builder<Builder> {
private final RSocketRequester.Builder requesterBuilder;
@Nullable
private ClientTransport clientTransport;
private String route;
Builder() {
this(initRSocketRequestBuilder());
}
Builder(RSocketRequester.Builder requesterBuilder) {
Assert.notNull(requesterBuilder, "RSocketRequester.Builder is required");
this.requesterBuilder = requesterBuilder;
this.route = "graphql";
}
private static RSocketRequester.Builder initRSocketRequestBuilder() {
MimeType mimeType = MimeType.valueOf("application/graphql+json");
RSocketRequester.Builder requesterBuilder = RSocketRequester.builder().dataMimeType(mimeType);
if (jackson2Present) {
requesterBuilder.rsocketStrategies(
RSocketStrategies.builder()
.encoder(DefaultJackson2Codecs.encoder())
.decoder(DefaultJackson2Codecs.decoder())
.build());
}
return requesterBuilder;
}
@Override
public Builder tcp(String host, int port) {
this.clientTransport = TcpClientTransport.create(host, port);
return this;
}
@Override
public Builder webSocket(URI uri) {
this.clientTransport = WebsocketClientTransport.create(uri);
return this;
}
@Override
public Builder clientTransport(ClientTransport clientTransport) {
this.clientTransport = clientTransport;
return this;
}
@Override
public Builder dataMimeType(MimeType dataMimeType) {
this.requesterBuilder.dataMimeType(dataMimeType);
return this;
}
@Override
public Builder route(String route) {
Assert.notNull(route, "'route' is required");
this.route = route;
return this;
}
@Override
public Builder rsocketRequester(Consumer<RSocketRequester.Builder> requesterConsumer) {
requesterConsumer.accept(this.requesterBuilder);
return this;
}
@Override
public RSocketGraphQlClient build() {
// Pass the codecs to the parent for response decoding
this.requesterBuilder.rsocketStrategies(builder -> {
builder.decoders(decoders -> setJsonDecoder(CodecDelegate.findJsonDecoder(decoders)));
builder.encoders(encoders -> setJsonEncoder(CodecDelegate.findJsonEncoder(encoders)));
});
Assert.state(this.clientTransport != null, "Neither WebSocket nor TCP networking configured");
RSocketRequester requester = this.requesterBuilder.transport(this.clientTransport);
RSocketGraphQlTransport graphQlTransport = new RSocketGraphQlTransport(this.route, requester, getJsonDecoder());
return new DefaultRSocketGraphQlClient(
super.buildGraphQlClient(graphQlTransport),
this.requesterBuilder, this.clientTransport, this.route, getBuilderInitializer());
}
}
}

View File

@@ -0,0 +1,168 @@
/*
* Copyright 2002-2022 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.client;
import java.net.URI;
import java.util.function.Consumer;
import io.rsocket.transport.ClientTransport;
import io.rsocket.transport.netty.client.TcpClientTransport;
import io.rsocket.transport.netty.client.WebsocketClientTransport;
import org.springframework.lang.Nullable;
import org.springframework.messaging.rsocket.RSocketRequester;
import org.springframework.messaging.rsocket.RSocketStrategies;
import org.springframework.util.Assert;
import org.springframework.util.MimeType;
/**
* Default {@link RSocketGraphQlClient.Builder} implementation that wraps
* a {@link RSocketRequester.Builder}.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class DefaultRSocketGraphQlClientBuilder
extends AbstractGraphQlClientBuilder<DefaultRSocketGraphQlClientBuilder>
implements RSocketGraphQlClient.Builder<DefaultRSocketGraphQlClientBuilder> {
private final RSocketRequester.Builder requesterBuilder;
@Nullable
private ClientTransport clientTransport;
private String route;
DefaultRSocketGraphQlClientBuilder() {
this(initRSocketRequestBuilder());
}
DefaultRSocketGraphQlClientBuilder(RSocketRequester.Builder requesterBuilder) {
Assert.notNull(requesterBuilder, "RSocketRequester.Builder is required");
this.requesterBuilder = requesterBuilder;
this.route = "graphql";
}
private static RSocketRequester.Builder initRSocketRequestBuilder() {
MimeType mimeType = MimeType.valueOf("application/graphql+json");
RSocketRequester.Builder requesterBuilder = RSocketRequester.builder().dataMimeType(mimeType);
if (jackson2Present) {
requesterBuilder.rsocketStrategies(
RSocketStrategies.builder()
.encoder(DefaultJackson2Codecs.encoder())
.decoder(DefaultJackson2Codecs.decoder())
.build());
}
return requesterBuilder;
}
@Override
public DefaultRSocketGraphQlClientBuilder tcp(String host, int port) {
this.clientTransport = TcpClientTransport.create(host, port);
return this;
}
@Override
public DefaultRSocketGraphQlClientBuilder webSocket(URI uri) {
this.clientTransport = WebsocketClientTransport.create(uri);
return this;
}
@Override
public DefaultRSocketGraphQlClientBuilder clientTransport(ClientTransport clientTransport) {
this.clientTransport = clientTransport;
return this;
}
@Override
public DefaultRSocketGraphQlClientBuilder dataMimeType(MimeType dataMimeType) {
this.requesterBuilder.dataMimeType(dataMimeType);
return this;
}
@Override
public DefaultRSocketGraphQlClientBuilder route(String route) {
Assert.notNull(route, "'route' is required");
this.route = route;
return this;
}
@Override
public DefaultRSocketGraphQlClientBuilder rsocketRequester(Consumer<RSocketRequester.Builder> requesterConsumer) {
requesterConsumer.accept(this.requesterBuilder);
return this;
}
@Override
public RSocketGraphQlClient build() {
// Pass the codecs to the parent for response decoding
this.requesterBuilder.rsocketStrategies(builder -> {
builder.decoders(decoders -> setJsonDecoder(CodecDelegate.findJsonDecoder(decoders)));
builder.encoders(encoders -> setJsonEncoder(CodecDelegate.findJsonEncoder(encoders)));
});
Assert.state(this.clientTransport != null, "Neither WebSocket nor TCP networking configured");
RSocketRequester requester = this.requesterBuilder.transport(this.clientTransport);
RSocketGraphQlTransport graphQlTransport = new RSocketGraphQlTransport(this.route, requester, getJsonDecoder());
return new DefaultRSocketGraphQlClient(
super.buildGraphQlClient(graphQlTransport),
this.requesterBuilder, this.clientTransport, this.route, getBuilderInitializer());
}
/**
* Default {@link RSocketGraphQlClient} implementation.
*/
private static class DefaultRSocketGraphQlClient extends AbstractDelegatingGraphQlClient implements RSocketGraphQlClient {
private final RSocketRequester.Builder requesterBuilder;
private final ClientTransport clientTransport;
private final String route;
private final Consumer<AbstractGraphQlClientBuilder<?>> builderInitializer;
DefaultRSocketGraphQlClient(
GraphQlClient graphQlClient, RSocketRequester.Builder requesterBuilder,
ClientTransport clientTransport, String route, Consumer<AbstractGraphQlClientBuilder<?>> builderInitializer) {
super(graphQlClient);
this.requesterBuilder = requesterBuilder;
this.clientTransport = clientTransport;
this.route = route;
this.builderInitializer = builderInitializer;
}
@Override
public RSocketGraphQlClient.Builder<?> mutate() {
DefaultRSocketGraphQlClientBuilder builder = new DefaultRSocketGraphQlClientBuilder(this.requesterBuilder);
builder.clientTransport(this.clientTransport);
builder.route(this.route);
this.builderInitializer.accept(builder);
return builder;
}
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright 2002-2022 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.client;
import java.util.function.Consumer;
import org.springframework.util.Assert;
/**
* Default {@link GraphQlClient.Builder} with a given, externally, prepared transport.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class DefaultTransportGraphQlClientBuilder
extends AbstractGraphQlClientBuilder<DefaultTransportGraphQlClientBuilder> {
private final GraphQlTransport transport;
DefaultTransportGraphQlClientBuilder(GraphQlTransport transport) {
Assert.notNull(transport, "GraphQlTransport is required");
this.transport = transport;
}
@Override
public GraphQlClient build() {
GraphQlClient client = buildGraphQlClient(this.transport);
return new DefaultTransportGraphQlClient(client, this.transport, getBuilderInitializer());
}
/**
* Default GraphQL client with a given transport.
*/
private static class DefaultTransportGraphQlClient extends AbstractDelegatingGraphQlClient {
private final GraphQlTransport transport;
private final Consumer<AbstractGraphQlClientBuilder<?>> builderInitializer;
DefaultTransportGraphQlClient(
GraphQlClient graphQlClient, GraphQlTransport transport,
Consumer<AbstractGraphQlClientBuilder<?>> builderInitializer) {
super(graphQlClient);
Assert.notNull(transport, "GraphQlTransport is required");
Assert.notNull(builderInitializer, "'builderInitializer' is required");
this.transport = transport;
this.builderInitializer = builderInitializer;
}
@Override
public DefaultTransportGraphQlClientBuilder mutate() {
DefaultTransportGraphQlClientBuilder builder = new DefaultTransportGraphQlClientBuilder(this.transport);
this.builderInitializer.accept(builder);
return builder;
}
}
}

View File

@@ -1,183 +0,0 @@
/*
* Copyright 2002-2022 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.client;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import reactor.core.publisher.Mono;
import org.springframework.http.HttpHeaders;
import org.springframework.http.codec.ClientCodecConfigurer;
import org.springframework.http.codec.CodecConfigurer;
import org.springframework.util.Assert;
import org.springframework.web.reactive.socket.client.WebSocketClient;
import org.springframework.web.util.DefaultUriBuilderFactory;
/**
* Default {@link WebSocketGraphQlClient} implementation that builds the underlying
* {@code WebSocketGraphQlTransport} to use.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class DefaultWebSocketGraphQlClient extends AbstractDelegatingGraphQlClient implements WebSocketGraphQlClient {
private final WebSocketGraphQlTransport transport;
private final Consumer<AbstractGraphQlClientBuilder<?>> builderInitializer;
DefaultWebSocketGraphQlClient(GraphQlClient delegate, WebSocketGraphQlTransport transport,
Consumer<AbstractGraphQlClientBuilder<?>> builderInitializer) {
super(delegate);
Assert.notNull(transport, "WebSocketGraphQlTransport is required");
Assert.notNull(builderInitializer, "`builderInitializer` is required");
this.transport = transport;
this.builderInitializer = builderInitializer;
}
@Override
public Mono<Void> start() {
return this.transport.start();
}
@Override
public Mono<Void> stop() {
return this.transport.stop();
}
@Override
public Builder mutate() {
Builder builder = new Builder(this.transport);
this.builderInitializer.accept(builder);
return builder;
}
/**
* Default {@link WebSocketGraphQlClient.Builder} implementation.
*/
static final class Builder extends AbstractGraphQlClientBuilder<Builder>
implements WebSocketGraphQlClient.Builder<Builder> {
private URI url;
private final HttpHeaders headers = new HttpHeaders();
private final WebSocketClient webSocketClient;
private final CodecConfigurer codecConfigurer;
/**
* Constructor to start via {@link WebSocketGraphQlClient#builder(String, WebSocketClient)}.
*/
Builder(String url, WebSocketClient client) {
this(toURI(url), client);
}
/**
* Constructor to start via {@link WebSocketGraphQlClient#builder(URI, WebSocketClient)}.
*/
Builder(URI url, WebSocketClient client) {
this.url = url;
this.webSocketClient = client;
this.codecConfigurer = ClientCodecConfigurer.create();
}
/**
* Constructor to mutate.
* @param transport the underlying transport with the current state
*/
Builder(WebSocketGraphQlTransport transport) {
this.url = transport.getUrl();
this.headers.putAll(transport.getHeaders());
this.webSocketClient = transport.getWebSocketClient();
this.codecConfigurer = transport.getCodecConfigurer();
}
@Override
public Builder url(String url) {
return url(toURI(url));
}
@Override
public Builder url(URI url) {
this.url = url;
return this;
}
private static URI toURI(String url) {
return new DefaultUriBuilderFactory().uriString(url).build();
}
@Override
public Builder header(String name, String... values) {
this.headers.put(name, Arrays.asList(values));
return this;
}
@Override
public Builder headers(Consumer<HttpHeaders> headersConsumer) {
headersConsumer.accept(this.headers);
return this;
}
@Override
public Builder codecConfigurer(Consumer<CodecConfigurer> codecConfigurerConsumer) {
codecConfigurerConsumer.accept(this.codecConfigurer);
return this;
}
@Override
public WebSocketGraphQlClient build() {
setJsonCodecs(
CodecDelegate.findJsonEncoder(this.codecConfigurer),
CodecDelegate.findJsonDecoder(this.codecConfigurer));
WebSocketGraphQlTransport transport = new WebSocketGraphQlTransport(
this.url, this.headers, this.webSocketClient, this.codecConfigurer, getInterceptor());
GraphQlClient graphQlClient = super.buildGraphQlClient(transport);
return new DefaultWebSocketGraphQlClient(graphQlClient, transport, getBuilderInitializer());
}
private WebSocketGraphQlClientInterceptor getInterceptor() {
List<WebSocketGraphQlClientInterceptor> interceptors = getInterceptors().stream()
.filter(interceptor -> interceptor instanceof WebSocketGraphQlClientInterceptor)
.map(interceptor -> (WebSocketGraphQlClientInterceptor) interceptor)
.collect(Collectors.toList());
Assert.state(interceptors.size() <= 1,
"Only a single interceptor of type WebSocketGraphQlClientInterceptor may be configured");
return (!interceptors.isEmpty() ? interceptors.get(0) : new WebSocketGraphQlClientInterceptor() {});
}
}
}

View File

@@ -0,0 +1,184 @@
/*
* Copyright 2002-2022 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.client;
import java.net.URI;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import reactor.core.publisher.Mono;
import org.springframework.http.HttpHeaders;
import org.springframework.http.codec.ClientCodecConfigurer;
import org.springframework.http.codec.CodecConfigurer;
import org.springframework.util.Assert;
import org.springframework.web.reactive.socket.client.WebSocketClient;
import org.springframework.web.util.DefaultUriBuilderFactory;
/**
* Default {@link WebSocketGraphQlClient.Builder} that builds the underlying
* {@code WebSocketGraphQlTransport}.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class DefaultWebSocketGraphQlClientBuilder
extends AbstractGraphQlClientBuilder<DefaultWebSocketGraphQlClientBuilder>
implements WebSocketGraphQlClient.Builder<DefaultWebSocketGraphQlClientBuilder> {
private URI url;
private final HttpHeaders headers = new HttpHeaders();
private final WebSocketClient webSocketClient;
private final CodecConfigurer codecConfigurer;
/**
* Constructor to start via {@link WebSocketGraphQlClient#builder(String, WebSocketClient)}.
*/
DefaultWebSocketGraphQlClientBuilder(String url, WebSocketClient client) {
this(toURI(url), client);
}
/**
* Constructor to start via {@link WebSocketGraphQlClient#builder(URI, WebSocketClient)}.
*/
DefaultWebSocketGraphQlClientBuilder(URI url, WebSocketClient client) {
this.url = url;
this.webSocketClient = client;
this.codecConfigurer = ClientCodecConfigurer.create();
}
/**
* Constructor to mutate.
* @param transport the underlying transport with the current state
*/
DefaultWebSocketGraphQlClientBuilder(WebSocketGraphQlTransport transport) {
this.url = transport.getUrl();
this.headers.putAll(transport.getHeaders());
this.webSocketClient = transport.getWebSocketClient();
this.codecConfigurer = transport.getCodecConfigurer();
}
@Override
public DefaultWebSocketGraphQlClientBuilder url(String url) {
return url(toURI(url));
}
@Override
public DefaultWebSocketGraphQlClientBuilder url(URI url) {
this.url = url;
return this;
}
private static URI toURI(String url) {
return new DefaultUriBuilderFactory().uriString(url).build();
}
@Override
public DefaultWebSocketGraphQlClientBuilder header(String name, String... values) {
this.headers.put(name, Arrays.asList(values));
return this;
}
@Override
public DefaultWebSocketGraphQlClientBuilder headers(Consumer<HttpHeaders> headersConsumer) {
headersConsumer.accept(this.headers);
return this;
}
@Override
public DefaultWebSocketGraphQlClientBuilder codecConfigurer(Consumer<CodecConfigurer> codecConfigurerConsumer) {
codecConfigurerConsumer.accept(this.codecConfigurer);
return this;
}
@Override
public WebSocketGraphQlClient build() {
setJsonCodecs(
CodecDelegate.findJsonEncoder(this.codecConfigurer),
CodecDelegate.findJsonDecoder(this.codecConfigurer));
WebSocketGraphQlTransport transport = new WebSocketGraphQlTransport(
this.url, this.headers, this.webSocketClient, this.codecConfigurer, getInterceptor());
GraphQlClient graphQlClient = super.buildGraphQlClient(transport);
return new DefaultWebSocketGraphQlClient(graphQlClient, transport, getBuilderInitializer());
}
private WebSocketGraphQlClientInterceptor getInterceptor() {
List<WebSocketGraphQlClientInterceptor> interceptors = getInterceptors().stream()
.filter(interceptor -> interceptor instanceof WebSocketGraphQlClientInterceptor)
.map(interceptor -> (WebSocketGraphQlClientInterceptor) interceptor)
.collect(Collectors.toList());
Assert.state(interceptors.size() <= 1,
"Only a single interceptor of type WebSocketGraphQlClientInterceptor may be configured");
return (!interceptors.isEmpty() ? interceptors.get(0) : new WebSocketGraphQlClientInterceptor() {});
}
/**
* Default {@link WebSocketGraphQlClient} implementation.
*/
private static class DefaultWebSocketGraphQlClient extends AbstractDelegatingGraphQlClient implements WebSocketGraphQlClient {
private final WebSocketGraphQlTransport transport;
private final Consumer<AbstractGraphQlClientBuilder<?>> builderInitializer;
DefaultWebSocketGraphQlClient(GraphQlClient delegate, WebSocketGraphQlTransport transport,
Consumer<AbstractGraphQlClientBuilder<?>> builderInitializer) {
super(delegate);
Assert.notNull(transport, "WebSocketGraphQlTransport is required");
Assert.notNull(builderInitializer, "`builderInitializer` is required");
this.transport = transport;
this.builderInitializer = builderInitializer;
}
@Override
public Mono<Void> start() {
return this.transport.start();
}
@Override
public Mono<Void> stop() {
return this.transport.stop();
}
@Override
public DefaultWebSocketGraphQlClientBuilder mutate() {
DefaultWebSocketGraphQlClientBuilder builder = new DefaultWebSocketGraphQlClientBuilder(this.transport);
this.builderInitializer.accept(builder);
return builder;
}
}
}

View File

@@ -1,78 +0,0 @@
/*
* Copyright 2002-2022 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.client;
import java.util.function.Consumer;
import org.springframework.util.Assert;
/**
* GraphQL client with a given, externally prepared transport.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class GenericGraphQlClient extends AbstractDelegatingGraphQlClient {
private final GraphQlTransport transport;
private final Consumer<AbstractGraphQlClientBuilder<?>> builderInitializer;
GenericGraphQlClient(
GraphQlClient graphQlClient, GraphQlTransport transport,
Consumer<AbstractGraphQlClientBuilder<?>> builderInitializer) {
super(graphQlClient);
Assert.notNull(transport, "GraphQlTransport is required");
Assert.notNull(builderInitializer, "'builderInitializer' is required");
this.transport = transport;
this.builderInitializer = builderInitializer;
}
@Override
public Builder mutate() {
Builder builder = new Builder(this.transport);
this.builderInitializer.accept(builder);
return builder;
}
/**
* Default {@link GraphQlClient.Builder} with a given transport.
*/
static final class Builder extends AbstractGraphQlClientBuilder<Builder> {
private final GraphQlTransport transport;
Builder(GraphQlTransport transport) {
Assert.notNull(transport, "GraphQlTransport is required");
this.transport = transport;
}
@Override
public GraphQlClient build() {
GraphQlClient client = buildGraphQlClient(this.transport);
return new GenericGraphQlClient(client, this.transport, getBuilderInitializer());
}
}
}

View File

@@ -81,7 +81,7 @@ public interface GraphQlClient {
* @return the builder for further initialization
*/
static Builder<?> builder(GraphQlTransport transport) {
return new GenericGraphQlClient.Builder(transport);
return new DefaultTransportGraphQlClientBuilder(transport);
}

View File

@@ -45,7 +45,7 @@ public interface HttpGraphQlClient extends WebGraphQlClient {
* Return a builder to initialize an {@link HttpGraphQlClient} with.
*/
static Builder<?> builder() {
return new DefaultHttpGraphQlClient.Builder();
return new DefaultHttpGraphQlClientBuilder();
}
/**
@@ -61,7 +61,7 @@ public interface HttpGraphQlClient extends WebGraphQlClient {
* to mutate and customize further through the returned builder.
*/
static Builder<?> builder(WebClient.Builder webClientBuilder) {
return new DefaultHttpGraphQlClient.Builder(webClientBuilder);
return new DefaultHttpGraphQlClientBuilder(webClientBuilder);
}

View File

@@ -44,14 +44,14 @@ public interface RSocketGraphQlClient extends GraphQlClient {
* and adding JSON codecs.
*/
static Builder<?> builder() {
return new DefaultRSocketGraphQlClient.Builder();
return new DefaultRSocketGraphQlClientBuilder();
}
/**
* Start with a given {@link #builder()}.
*/
static Builder<?> builder(RSocketRequester.Builder requesterBuilder) {
return new DefaultRSocketGraphQlClient.Builder(requesterBuilder);
return new DefaultRSocketGraphQlClientBuilder(requesterBuilder);
}

View File

@@ -70,7 +70,7 @@ public interface WebSocketGraphQlClient extends WebGraphQlClient {
* @param webSocketClient the underlying transport client to use
*/
static Builder<?> builder(String url, WebSocketClient webSocketClient) {
return new DefaultWebSocketGraphQlClient.Builder(url, webSocketClient);
return new DefaultWebSocketGraphQlClientBuilder(url, webSocketClient);
}
/**
@@ -79,7 +79,7 @@ public interface WebSocketGraphQlClient extends WebGraphQlClient {
* @param webSocketClient the underlying transport client to use
*/
static Builder<?> builder(URI url, WebSocketClient webSocketClient) {
return new DefaultWebSocketGraphQlClient.Builder(url, webSocketClient);
return new DefaultWebSocketGraphQlClientBuilder(url, webSocketClient);
}