Merge branch '1.2.x'

This commit is contained in:
Brian Clozel
2024-04-05 09:51:43 +02:00
282 changed files with 2537 additions and 2166 deletions

View File

@@ -18,16 +18,16 @@ ext {
}
dependencies {
checkstyle "io.spring.javaformat:spring-javaformat-checkstyle:${javaFormatVersion}"
checkstyle("com.puppycrawl.tools:checkstyle:${checkstyle.toolVersion}")
checkstyle("io.spring.javaformat:spring-javaformat-checkstyle:${javaFormatVersion}")
implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
implementation("org.jetbrains.kotlin:kotlin-compiler-embeddable:${kotlinVersion}")
implementation("io.spring.javaformat:spring-javaformat-gradle-plugin:${javaFormatVersion}")
}
checkstyle {
def archive = configurations.checkstyle.filter { it.name.startsWith("spring-javaformat-checkstyle")}
config = resources.text.fromArchiveEntry(archive, "io/spring/javaformat/checkstyle/checkstyle.xml")
toolVersion = 8.11
toolVersion = "10.12.4"
}
gradlePlugin {

View File

@@ -1 +1 @@
javaFormatVersion=0.0.28
javaFormatVersion=0.0.41

View File

@@ -23,6 +23,7 @@ import org.gradle.api.plugins.JavaBasePlugin;
import org.gradle.api.publish.maven.plugins.MavenPublishPlugin;
import org.springframework.graphql.build.conventions.DeploymentConventions;
import org.springframework.graphql.build.conventions.FormattingConventions;
import org.springframework.graphql.build.conventions.JavaConventions;
import org.springframework.graphql.build.conventions.KotlinConventions;
@@ -42,6 +43,7 @@ public class ConventionsPlugin implements Plugin<Project> {
@Override
public void apply(Project project) {
new FormattingConventions().apply(project);
new JavaConventions().apply(project);
new KotlinConventions().apply(project);
new DeploymentConventions().apply(project);

View File

@@ -16,7 +16,6 @@
package org.springframework.graphql.build.conventions;
import io.spring.javaformat.gradle.FormatTask;
import io.spring.javaformat.gradle.SpringJavaFormatPlugin;
import org.gradle.api.Project;
import org.gradle.api.artifacts.DependencySet;
@@ -26,8 +25,7 @@ import org.gradle.api.plugins.quality.CheckstylePlugin;
/**
* Conventions that are applied in the presence of the {@link JavaBasePlugin}. When the
* plugin is applied, the {@link SpringJavaFormatPlugin Spring Java Format} and
* {@link CheckstylePlugin Checkstyle}.
* plugin is applied, {@link CheckstylePlugin Checkstyle} is applied and configured.
*
* @author Brian Clozel
*/
@@ -38,14 +36,14 @@ public class FormattingConventions {
}
private void applySpringJavaFormat(Project project) {
project.getPlugins().apply(SpringJavaFormatPlugin.class);
project.getTasks().withType(FormatTask.class, (formatTask) -> formatTask.setEncoding("UTF-8"));
project.getPlugins().apply(CheckstylePlugin.class);
CheckstyleExtension checkstyle = project.getExtensions().getByType(CheckstyleExtension.class);
checkstyle.setToolVersion("8.43");
checkstyle.setToolVersion("10.12.4");
checkstyle.getConfigDirectory().set(project.getRootProject().file("src/checkstyle"));
String version = SpringJavaFormatPlugin.class.getPackage().getImplementationVersion();
DependencySet checkstyleDependencies = project.getConfigurations().getByName("checkstyle").getDependencies();
checkstyleDependencies
.add(project.getDependencies().create("com.puppycrawl.tools:checkstyle:" + checkstyle.getToolVersion()));
checkstyleDependencies
.add(project.getDependencies().create("io.spring.javaformat:spring-javaformat-checkstyle:" + version));
}

View File

@@ -31,8 +31,8 @@ public class GraphQlConfiguration {
@Bean
RuntimeWiringConfigurer customWiringConfigurer(BookRepository bookRepository) { // <1>
DataFetcher<Book> dataFetcher = QuerydslDataFetcher.builder(bookRepository).single();
return wiringBuilder -> wiringBuilder
.type("Query", builder -> builder.dataFetcher("book", dataFetcher)); // <2>
return (wiringBuilder) -> wiringBuilder
.type("Query", (builder) -> builder.dataFetcher("book", dataFetcher)); // <2>
}
}

View File

@@ -28,13 +28,13 @@ import org.springframework.web.servlet.function.ServerResponse;
@Configuration
public class GraphiQlConfiguration {
@Bean
@Order(0)
public RouterFunction<ServerResponse> graphiQlRouterFunction() {
RouterFunctions.Builder builder = RouterFunctions.route();
ClassPathResource graphiQlPage = new ClassPathResource("graphiql/index.html"); // <1>
GraphiQlHandler graphiQLHandler = new GraphiQlHandler("/graphql", "", graphiQlPage); // <2>
builder = builder.GET("/graphiql", graphiQLHandler::handleRequest); // <3>
return builder.build(); // <4>
}
@Bean
@Order(0)
public RouterFunction<ServerResponse> graphiQlRouterFunction() {
RouterFunctions.Builder builder = RouterFunctions.route();
ClassPathResource graphiQlPage = new ClassPathResource("graphiql/index.html"); // <1>
GraphiQlHandler graphiQLHandler = new GraphiQlHandler("/graphql", "", graphiQlPage); // <2>
builder = builder.GET("/graphiql", graphiQLHandler::handleRequest); // <3>
return builder.build(); // <4>
}
}

View File

@@ -30,20 +30,20 @@ class RequestErrorInterceptor implements WebGraphQlInterceptor {
@Override
public Mono<WebGraphQlResponse> intercept(WebGraphQlRequest request, Chain chain) {
return chain.next(request).map(response -> {
return chain.next(request).map((response) -> {
if (response.isValid()) {
return response; // <1>
}
List<GraphQLError> errors = response.getErrors().stream() // <2>
.map(error -> {
.map((error) -> {
GraphqlErrorBuilder<?> builder = GraphqlErrorBuilder.newError();
// ...
return builder.build();
})
.toList();
return response.transform(builder -> builder.errors(errors).build()); // <3>
return response.transform((builder) -> builder.errors(errors).build()); // <3>
});
}
}
}

View File

@@ -33,7 +33,7 @@ class ResponseHeaderInterceptor implements WebGraphQlInterceptor {
@Override
public Mono<WebGraphQlResponse> intercept(WebGraphQlRequest request, Chain chain) { // <2>
return chain.next(request).doOnNext(response -> {
return chain.next(request).doOnNext((response) -> {
String value = response.getExecutionInput().getGraphQLContext().get("cookieName");
ResponseCookie cookie = ResponseCookie.from("cookieName", value).build();
response.getResponseHeaders().add(HttpHeaders.SET_COOKIE, cookie.toString());

View File

@@ -43,4 +43,4 @@ public class GraphQlRSocketController {
public Flux<Map<String, Object>> handleSubscription(Map<String, Object> payload) {
return this.handler.handleSubscription(payload);
}
}
}

View File

@@ -41,7 +41,6 @@ import org.springframework.util.IdGenerator;
* to a server-side GraphQL handler or service.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
abstract class AbstractDirectGraphQlTransport implements GraphQlTransport {
@@ -56,7 +55,7 @@ abstract class AbstractDirectGraphQlTransport implements GraphQlTransport {
@SuppressWarnings({"ConstantConditions", "unchecked"})
@Override
public Flux<GraphQlResponse> executeSubscription(GraphQlRequest request) {
return executeInternal(toExecutionRequest(request)).flatMapMany(response -> {
return executeInternal(toExecutionRequest(request)).flatMapMany((response) -> {
try {
Object data = response.getData();
AssertionErrors.assertTrue("Not a Publisher: " + data, data instanceof Publisher);
@@ -64,7 +63,7 @@ abstract class AbstractDirectGraphQlTransport implements GraphQlTransport {
List<ResponseError> errors = response.getErrors();
AssertionErrors.assertTrue("Subscription errors: " + errors, CollectionUtils.isEmpty(errors));
return Flux.from((Publisher<ExecutionResult>) data).map(executionResult ->
return Flux.from((Publisher<ExecutionResult>) data).map((executionResult) ->
new DefaultExecutionGraphQlResponse(response.getExecutionInput(), executionResult));
}
catch (AssertionError ex) {

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.test.tester;
import java.time.Duration;
@@ -35,7 +36,6 @@ import org.springframework.graphql.ResponseError;
import org.springframework.graphql.client.AbstractGraphQlClientBuilder;
import org.springframework.graphql.client.GraphQlClient;
import org.springframework.graphql.client.GraphQlTransport;
import org.springframework.graphql.support.CachingDocumentSource;
import org.springframework.graphql.support.DocumentSource;
import org.springframework.graphql.support.ResourceDocumentSource;
import org.springframework.lang.Nullable;
@@ -51,6 +51,7 @@ import org.springframework.util.ClassUtils;
* agnostic {@code GraphQlTester}. A transport specific extension can then wrap
* this default tester by extending {@link AbstractDelegatingGraphQlTester}.
*
* @param <B> the type of builder
* @author Rossen Stoyanchev
* @since 1.0.0
* @see AbstractDelegatingGraphQlTester
@@ -86,7 +87,7 @@ public abstract class AbstractGraphQlTesterBuilder<B extends AbstractGraphQlTest
@Override
public B errorFilter(Predicate<ResponseError> predicate) {
this.errorFilter = (this.errorFilter != null ? errorFilter.and(predicate) : predicate);
this.errorFilter = (this.errorFilter != null) ? this.errorFilter.and(predicate) : predicate;
return self();
}
@@ -115,6 +116,7 @@ public abstract class AbstractGraphQlTesterBuilder<B extends AbstractGraphQlTest
/**
* Allow transport-specific subclass builders to register a JSON Path
* {@link MappingProvider} that matches the JSON encoding/decoding they use.
* @param configurer a function applied to the JSON Path configuration
*/
protected void configureJsonPathConfig(Function<Configuration, Configuration> configurer) {
this.jsonPathConfig = configurer.apply(this.jsonPathConfig);
@@ -123,6 +125,7 @@ public abstract class AbstractGraphQlTesterBuilder<B extends AbstractGraphQlTest
/**
* Build the default transport-agnostic client that subclasses can then wrap
* with {@link AbstractDelegatingGraphQlTester}.
* @param transport the graphql transport to use
*/
protected GraphQlTester buildGraphQlTester(GraphQlTransport transport) {
@@ -139,12 +142,12 @@ public abstract class AbstractGraphQlTesterBuilder<B extends AbstractGraphQlTest
* initialize new builder instances with, based on "this" builder.
*/
protected Consumer<AbstractGraphQlTesterBuilder<?>> getBuilderInitializer() {
return builder -> {
return (builder) -> {
if (this.errorFilter != null) {
builder.errorFilter(this.errorFilter);
}
builder.documentSource(this.documentSource);
builder.configureJsonPathConfig(config -> this.jsonPathConfig);
builder.configureJsonPathConfig((config) -> this.jsonPathConfig);
builder.responseTimeout(this.responseTimeout);
};
}
@@ -153,6 +156,7 @@ public abstract class AbstractGraphQlTesterBuilder<B extends AbstractGraphQlTest
* For cases where the Tester needs the {@link GraphQlTransport}, we can't use
* transports directly since they are package private, but we can adapt the corresponding
* {@link GraphQlClient} and adapt it to {@code GraphQlTransport}.
* @param client the graphql client to use for extracting the transport
*/
protected static GraphQlTransport asTransport(GraphQlClient client) {
return new GraphQlTransport() {
@@ -180,7 +184,7 @@ public abstract class AbstractGraphQlTesterBuilder<B extends AbstractGraphQlTest
}
private static class Jackson2Configurer {
private static final class Jackson2Configurer {
private static final Class<?> defaultJsonProviderType;

View File

@@ -37,7 +37,6 @@ import org.springframework.util.Assert;
* wraps an {@code ExecutionGraphQlService}.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class DefaultExecutionGraphQlServiceTesterBuilder
extends AbstractGraphQlTesterBuilder<DefaultExecutionGraphQlServiceTesterBuilder>
@@ -95,7 +94,7 @@ final class DefaultExecutionGraphQlServiceTesterBuilder
private void registerJsonPathMappingProvider() {
if (this.encoder != null && this.decoder != null) {
configureJsonPathConfig(config -> {
configureJsonPathConfig((config) -> {
EncoderDecoderMappingProvider provider = new EncoderDecoderMappingProvider(
Collections.singletonList(this.encoder), Collections.singletonList(this.decoder));
return config.mappingProvider(provider);
@@ -111,7 +110,7 @@ final class DefaultExecutionGraphQlServiceTesterBuilder
/**
* Default {@link ExecutionGraphQlServiceTester} implementation.
*/
private static class DefaultExecutionGraphQlServiceTester
private static final class DefaultExecutionGraphQlServiceTester
extends AbstractDelegatingGraphQlTester implements ExecutionGraphQlServiceTester {
private final GraphQlServiceGraphQlTransport transport;

View File

@@ -153,7 +153,7 @@ final class DefaultGraphQlTester implements GraphQlTester {
@SuppressWarnings("ConstantConditions")
@Override
public Response execute() {
return transport.execute(request()).map(response -> mapResponse(response, request())).block(responseTimeout);
return DefaultGraphQlTester.this.transport.execute(request()).map((response) -> mapResponse(response, request())).block(DefaultGraphQlTester.this.responseTimeout);
}
@Override
@@ -163,7 +163,7 @@ final class DefaultGraphQlTester implements GraphQlTester {
@Override
public Subscription executeSubscription() {
return () -> transport.executeSubscription(request()).map(result -> mapResponse(result, request()));
return () -> DefaultGraphQlTester.this.transport.executeSubscription(request()).map((result) -> mapResponse(result, request()));
}
private GraphQlRequest request() {
@@ -171,7 +171,7 @@ final class DefaultGraphQlTester implements GraphQlTester {
}
private DefaultResponse mapResponse(GraphQlResponse response, GraphQlRequest request) {
return new DefaultResponse(response, errorFilter, assertDecorator(request), jsonPathConfig);
return new DefaultResponse(response, DefaultGraphQlTester.this.errorFilter, assertDecorator(request), DefaultGraphQlTester.this.jsonPathConfig);
}
private Consumer<Runnable> assertDecorator(GraphQlRequest request) {
@@ -191,7 +191,7 @@ final class DefaultGraphQlTester implements GraphQlTester {
/**
* Container for GraphQL response data and errors along with convenience methods.
*/
private final static class ResponseDelegate {
private static final class ResponseDelegate {
private final DocumentContext jsonDoc;
@@ -258,7 +258,7 @@ final class DefaultGraphQlTester implements GraphQlTester {
}
void consumeErrors(Consumer<List<ResponseError>> consumer) {
filterErrors(error -> true);
filterErrors((error) -> true);
consumer.accept(this.errors);
}
@@ -392,7 +392,7 @@ final class DefaultGraphQlTester implements GraphQlTester {
this.delegate.doAssert(() -> {
Object value = this.pathHelper.evaluateJsonPath(this.delegate.jsonContent());
AssertionErrors.assertNull(
"Expected null value at JSON path \"" + path + "\" but found " + value, value);
"Expected null value at JSON path \"" + this.path + "\" but found " + value, value);
});
return this;
}
@@ -464,7 +464,7 @@ final class DefaultGraphQlTester implements GraphQlTester {
}
private static String joinPaths(@Nullable String basePath, String path) {
return (basePath != null ? basePath + "." + path : path);
return (basePath != null) ? basePath + "." + path : path;
}
@@ -476,7 +476,7 @@ final class DefaultGraphQlTester implements GraphQlTester {
private final D entity;
protected DefaultEntity(TypeRefAdapter<D> typeAdapter) {
this.entity = delegate.read(jsonPath, typeAdapter);
this.entity = DefaultPath.this.delegate.read(DefaultPath.this.jsonPath, typeAdapter);
}
protected D getEntity() {
@@ -484,57 +484,57 @@ final class DefaultGraphQlTester implements GraphQlTester {
}
protected void doAssert(Runnable task) {
delegate.doAssert(task);
DefaultPath.this.delegate.doAssert(task);
}
protected String getPath() {
return path;
return DefaultPath.this.path;
}
@Override
public Path path(String path) {
return forPath(basePath, path, delegate);
return forPath(DefaultPath.this.basePath, path, DefaultPath.this.delegate);
}
@Override
public Path path(String path, Consumer<Path> pathConsumer) {
return forNestedPath(basePath, path, delegate, pathConsumer);
return forNestedPath(DefaultPath.this.basePath, path, DefaultPath.this.delegate, pathConsumer);
}
@Override
public <T extends S> T isEqualTo(Object expected) {
delegate.doAssert(() -> AssertionErrors.assertEquals(path, expected, this.entity));
DefaultPath.this.delegate.doAssert(() -> AssertionErrors.assertEquals(DefaultPath.this.path, expected, this.entity));
return self();
}
@Override
public <T extends S> T isNotEqualTo(Object other) {
delegate.doAssert(() -> AssertionErrors.assertNotEquals(path, other, this.entity));
DefaultPath.this.delegate.doAssert(() -> AssertionErrors.assertNotEquals(DefaultPath.this.path, other, this.entity));
return self();
}
@Override
public <T extends S> T isSameAs(Object expected) {
delegate.doAssert(() -> AssertionErrors.assertTrue(path, expected == this.entity));
DefaultPath.this.delegate.doAssert(() -> AssertionErrors.assertTrue(DefaultPath.this.path, expected == this.entity));
return self();
}
@Override
public <T extends S> T isNotSameAs(Object other) {
delegate.doAssert(() -> AssertionErrors.assertTrue(path, other != this.entity));
DefaultPath.this.delegate.doAssert(() -> AssertionErrors.assertTrue(DefaultPath.this.path, other != this.entity));
return self();
}
@Override
public <T extends S> T matches(Predicate<D> predicate) {
delegate
.doAssert(() -> AssertionErrors.assertTrue(path, predicate.test(this.entity)));
DefaultPath.this.delegate
.doAssert(() -> AssertionErrors.assertTrue(DefaultPath.this.path, predicate.test(this.entity)));
return self();
}
@Override
public <T extends S> T satisfies(Consumer<D> consumer) {
delegate.doAssert(() -> consumer.accept(this.entity));
DefaultPath.this.delegate.doAssert(() -> consumer.accept(this.entity));
return self();
}
@@ -557,7 +557,7 @@ final class DefaultGraphQlTester implements GraphQlTester {
private final class DefaultEntityList<E>
extends DefaultEntity<List<E>, EntityList<E>> implements EntityList<E> {
public DefaultEntityList(TypeRefAdapter<List<E>> typeAdapter) {
DefaultEntityList(TypeRefAdapter<List<E>> typeAdapter) {
super(typeAdapter);
}

View File

@@ -33,7 +33,6 @@ import org.springframework.web.util.UriComponentsBuilder;
* {@link WebTestClient.Builder}.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class DefaultHttpGraphQlTesterBuilder
extends AbstractGraphQlTesterBuilder<DefaultHttpGraphQlTesterBuilder>
@@ -93,8 +92,8 @@ final class DefaultHttpGraphQlTesterBuilder
}
private void registerJsonPathMappingProvider() {
this.webTestClientBuilder.codecs(codecConfigurer ->
configureJsonPathConfig(config -> {
this.webTestClientBuilder.codecs((codecConfigurer) ->
configureJsonPathConfig((config) -> {
EncoderDecoderMappingProvider provider = new EncoderDecoderMappingProvider(codecConfigurer);
return config.mappingProvider(provider);
}));
@@ -105,7 +104,7 @@ final class DefaultHttpGraphQlTesterBuilder
* Default {@link HttpGraphQlTester} that builds and uses a {@link WebTestClient}
* for request execution.
*/
private static class DefaultHttpGraphQlTester extends AbstractDelegatingGraphQlTester implements HttpGraphQlTester {
private static final class DefaultHttpGraphQlTester extends AbstractDelegatingGraphQlTester implements HttpGraphQlTester {
private final WebTestClient webTestClient;

View File

@@ -113,9 +113,9 @@ public class DefaultRSocketGraphQlTesterBuilder
}
private void registerJsonPathMappingProvider() {
this.rsocketGraphQlClientBuilder.rsocketRequester(builder ->
builder.rsocketStrategies(strategiesBuilder ->
configureJsonPathConfig(config -> {
this.rsocketGraphQlClientBuilder.rsocketRequester((builder) ->
builder.rsocketStrategies((strategiesBuilder) ->
configureJsonPathConfig((config) -> {
RSocketStrategies strategies = strategiesBuilder.build();
List<Encoder<?>> encoders = strategies.encoders();
List<Decoder<?>> decoders = strategies.decoders();

View File

@@ -27,7 +27,6 @@ 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> {
@@ -50,7 +49,7 @@ final class DefaultTransportGraphQlTesterBuilder
/**
* {@link GraphQlTester} with a given transport.
*/
private static class DefaultTransportGraphQlTester extends AbstractDelegatingGraphQlTester {
private static final class DefaultTransportGraphQlTester extends AbstractDelegatingGraphQlTester {
private final GraphQlTransport transport;

View File

@@ -34,7 +34,6 @@ import org.springframework.web.util.DefaultUriBuilderFactory;
* {@link WebGraphQlHandler} for request execution.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class DefaultWebGraphQlTesterBuilder
extends AbstractGraphQlTesterBuilder<DefaultWebGraphQlTesterBuilder>
@@ -104,7 +103,7 @@ final class DefaultWebGraphQlTesterBuilder
}
private void registerJsonPathMappingProvider() {
configureJsonPathConfig(jsonPathConfig -> {
configureJsonPathConfig((jsonPathConfig) -> {
EncoderDecoderMappingProvider provider = new EncoderDecoderMappingProvider(this.codecConfigurer);
return jsonPathConfig.mappingProvider(provider);
});
@@ -114,7 +113,7 @@ final class DefaultWebGraphQlTesterBuilder
/**
* Default {@link WebGraphQlTester} implementation.
*/
private static class DefaultWebGraphQlTester extends AbstractDelegatingGraphQlTester implements WebGraphQlTester {
private static final class DefaultWebGraphQlTester extends AbstractDelegatingGraphQlTester implements WebGraphQlTester {
private final WebGraphQlHandlerGraphQlTransport transport;

View File

@@ -34,7 +34,6 @@ import org.springframework.web.reactive.socket.client.WebSocketClient;
* {@link WebSocketGraphQlClient.Builder}.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class DefaultWebSocketGraphQlTesterBuilder
extends AbstractGraphQlTesterBuilder<DefaultWebSocketGraphQlTesterBuilder>
@@ -108,8 +107,8 @@ final class DefaultWebSocketGraphQlTesterBuilder
}
private void registerJsonPathMappingProvider() {
this.graphQlClientBuilder.codecConfigurer(codecConfigurer -> {
configureJsonPathConfig(jsonPathConfig -> {
this.graphQlClientBuilder.codecConfigurer((codecConfigurer) -> {
configureJsonPathConfig((jsonPathConfig) -> {
EncoderDecoderMappingProvider provider = new EncoderDecoderMappingProvider(codecConfigurer);
return jsonPathConfig.mappingProvider(provider);
});
@@ -120,7 +119,7 @@ final class DefaultWebSocketGraphQlTesterBuilder
/**
* Default {@link WebSocketGraphQlTester} implementation.
*/
private static class DefaultWebSocketGraphQlTester extends AbstractDelegatingGraphQlTester implements WebSocketGraphQlTester {
private static final class DefaultWebSocketGraphQlTester extends AbstractDelegatingGraphQlTester implements WebSocketGraphQlTester {
private final WebSocketGraphQlClient client;

View File

@@ -45,7 +45,6 @@ import org.springframework.util.MimeTypeUtils;
* JSON Path {@link MappingProvider} that uses {@link Encoder} and {@link Decoder}.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class EncoderDecoderMappingProvider implements MappingProvider {
@@ -60,29 +59,29 @@ final class EncoderDecoderMappingProvider implements MappingProvider {
/**
* Create an instance with a {@link CodecConfigurer}.
*/
public EncoderDecoderMappingProvider(CodecConfigurer configurer) {
EncoderDecoderMappingProvider(CodecConfigurer configurer) {
this.encoder = findJsonEncoder(configurer);
this.decoder = findJsonDecoder(configurer);
}
/**
* Create an instance with a List of encoders and decoders>
* Create an instance with a List of encoders and decoders.
*/
public EncoderDecoderMappingProvider(List<Encoder<?>> encoders, List<Decoder<?>> decoders) {
EncoderDecoderMappingProvider(List<Encoder<?>> encoders, List<Decoder<?>> decoders) {
this.encoder = findJsonEncoder(encoders);
this.decoder = findJsonDecoder(decoders);
}
private static Encoder<?> findJsonEncoder(CodecConfigurer configurer) {
return findJsonEncoder(configurer.getWriters().stream()
.filter(writer -> writer instanceof EncoderHttpMessageWriter)
.map(writer -> ((EncoderHttpMessageWriter<?>) writer).getEncoder()));
.filter((writer) -> writer instanceof EncoderHttpMessageWriter)
.map((writer) -> ((EncoderHttpMessageWriter<?>) writer).getEncoder()));
}
private static Decoder<?> findJsonDecoder(CodecConfigurer configurer) {
return findJsonDecoder(configurer.getReaders().stream()
.filter(reader -> reader instanceof DecoderHttpMessageReader)
.map(reader -> ((DecoderHttpMessageReader<?>) reader).getDecoder()));
.filter((reader) -> reader instanceof DecoderHttpMessageReader)
.map((reader) -> ((DecoderHttpMessageReader<?>) reader).getDecoder()));
}
private static Encoder<?> findJsonEncoder(List<Encoder<?>> encoders) {
@@ -95,14 +94,14 @@ final class EncoderDecoderMappingProvider implements MappingProvider {
private static Encoder<?> findJsonEncoder(Stream<Encoder<?>> stream) {
return stream
.filter(encoder -> encoder.canEncode(MAP_TYPE, MediaType.APPLICATION_JSON))
.filter((encoder) -> encoder.canEncode(MAP_TYPE, MediaType.APPLICATION_JSON))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("No JSON Encoder"));
}
private static Decoder<?> findJsonDecoder(Stream<Decoder<?>> decoderStream) {
return decoderStream
.filter(decoder -> decoder.canDecode(MAP_TYPE, MediaType.APPLICATION_JSON))
.filter((decoder) -> decoder.canDecode(MAP_TYPE, MediaType.APPLICATION_JSON))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("No JSON Decoder"));
}

View File

@@ -40,6 +40,7 @@ public interface ExecutionGraphQlServiceTester extends GraphQlTester {
/**
* Create a {@link ExecutionGraphQlServiceTester} instance.
* @param service the GraphQL service to use
*/
static ExecutionGraphQlServiceTester create(ExecutionGraphQlService service) {
return builder(service).build();
@@ -47,6 +48,7 @@ public interface ExecutionGraphQlServiceTester extends GraphQlTester {
/**
* Return a builder for {@link ExecutionGraphQlServiceTester}.
* @param service the GraphQL service to use
*/
static ExecutionGraphQlServiceTester.Builder<?> builder(ExecutionGraphQlService service) {
return new DefaultExecutionGraphQlServiceTesterBuilder(service);
@@ -55,12 +57,14 @@ public interface ExecutionGraphQlServiceTester extends GraphQlTester {
/**
* Default {@link ExecutionGraphQlServiceTester.Builder} implementation.
* @param <B> the type of builder
*/
interface Builder<B extends Builder<B>> extends GraphQlTester.Builder<B> {
/**
* Provide a {@code BiFunction} to help initialize the
* {@link ExecutionInput} with.
* @param configurer the function that initializes the execution input
* @since 1.1.2
* @see org.springframework.graphql.ExecutionGraphQlRequest#configureExecutionInput(BiFunction)
*/
@@ -69,12 +73,14 @@ public interface ExecutionGraphQlServiceTester extends GraphQlTester {
/**
* Configure the JSON encoder to use for mapping response data to
* higher level objects.
* @param encoder the JSON encoder to use
*/
B encoder(Encoder<?> encoder);
/**
* Configure the JSON decoder to use for mapping response data to
* higher level objects.
* @param decoder the JSON decoder to use
*/
B decoder(Decoder<?> decoder);

View File

@@ -34,7 +34,6 @@ import org.springframework.util.Assert;
* {@code GraphQlTransport} that calls directly a {@link ExecutionGraphQlService}.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class GraphQlServiceGraphQlTransport extends AbstractDirectGraphQlTransport {
@@ -53,11 +52,11 @@ final class GraphQlServiceGraphQlTransport extends AbstractDirectGraphQlTranspor
}
public ExecutionGraphQlService getGraphQlService() {
ExecutionGraphQlService getGraphQlService() {
return this.graphQlService;
}
public List<BiFunction<ExecutionInput, ExecutionInput.Builder, ExecutionInput>> getExecutionInputConfigurers() {
List<BiFunction<ExecutionInput, ExecutionInput.Builder, ExecutionInput>> getExecutionInputConfigurers() {
return this.executionInputConfigurers;
}

View File

@@ -66,6 +66,7 @@ public interface GraphQlTester {
* Variant of {@link #document(String)} that uses the given key to resolve
* the GraphQL document from a file with the help of the configured
* {@link Builder#documentSource(DocumentSource) DocumentSource}.
* @param documentName the name of the document to send
* @return spec for response assertions
* @throws IllegalArgumentException if the documentName cannot be resolved
* @throws AssertionError if the response status is not 200 (OK)
@@ -94,6 +95,7 @@ public interface GraphQlTester {
/**
* A builder to create a {@link GraphQlTester} instance.
* @param <B> the type of builder
*/
interface Builder<B extends Builder<B>> {
@@ -111,6 +113,7 @@ public interface GraphQlTester {
* <p>By default, this is set to {@link ResourceDocumentSource} with
* classpath location {@code "graphql-test/"} and
* {@link ResourceDocumentSource#FILE_EXTENSIONS} as extensions.
* @param contentLoader the document content loader
*/
B documentSource(DocumentSource contentLoader);
@@ -130,6 +133,7 @@ public interface GraphQlTester {
/**
* Declare options to gather input for a GraphQL request and execute it.
* @param <T> the type of request
*/
interface Request<T extends Request<T>> {
@@ -311,7 +315,7 @@ public interface GraphQlTester {
}
/**
* Contains a decoded entity and provides options to assert it
* Contains a decoded entity and provides options to assert it.
*
* @param <D> the entity type
* @param <S> the {@code Entity} spec type
@@ -320,6 +324,7 @@ public interface GraphQlTester {
/**
* Verify the decoded entity is equal to the given value.
* @param <T> the {@code Entity} spec type
* @param expected the expected value
* @return the {@code Entity} spec for further assertions
*/
@@ -327,6 +332,7 @@ public interface GraphQlTester {
/**
* Verify the decoded entity is not equal to the given value.
* @param <T> the {@code Entity} spec type
* @param other the value to check against
* @return the {@code Entity} spec for further assertions
*/
@@ -334,6 +340,7 @@ public interface GraphQlTester {
/**
* Verify the decoded entity is the same instance as the given value.
* @param <T> the {@code Entity} spec type
* @param expected the expected value
* @return the {@code Entity} spec for further assertions
*/
@@ -341,6 +348,7 @@ public interface GraphQlTester {
/**
* Verify the decoded entity is not the same instance as the given value.
* @param <T> the {@code Entity} spec type
* @param other the value to check against
* @return the {@code Entity} spec for further assertions
*/
@@ -348,6 +356,7 @@ public interface GraphQlTester {
/**
* Verify the decoded entity matches the given predicate.
* @param <T> the {@code Entity} spec type
* @param predicate the predicate to apply
* @return the {@code Entity} spec for further assertions
*/
@@ -355,6 +364,7 @@ public interface GraphQlTester {
/**
* Verify the entity with the given {@link Consumer}.
* @param <T> the {@code Entity} spec type
* @param consumer the consumer to apply
* @return the {@code Entity} spec for further assertions
*/

View File

@@ -37,6 +37,7 @@ public interface HttpGraphQlTester extends WebGraphQlTester {
/**
* Create an {@link HttpGraphQlTester} that uses the given {@link WebTestClient}.
* @param webTestClient the {@code WebTestClient} to use
*/
static HttpGraphQlTester create(WebTestClient webTestClient) {
return builder(webTestClient.mutate()).build();
@@ -45,6 +46,7 @@ public interface HttpGraphQlTester extends WebGraphQlTester {
/**
* Return a builder to initialize an {@link HttpGraphQlTester} by creating
* the underlying {@link WebTestClient} through the given builder.
* @param webTestClientBuilder the {@code WebTestClient} builder to use
*/
static HttpGraphQlTester.Builder<?> builder(WebTestClient.Builder webTestClientBuilder) {
return new DefaultHttpGraphQlTesterBuilder(webTestClientBuilder);
@@ -53,6 +55,7 @@ public interface HttpGraphQlTester extends WebGraphQlTester {
/**
* Builder for the GraphQL over HTTP tester.
* @param <B> the type of builder
*/
interface Builder<B extends Builder<B>> extends WebGraphQlTester.Builder<B> {
@@ -60,6 +63,7 @@ public interface HttpGraphQlTester extends WebGraphQlTester {
* Customize the {@code WebTestClient} to use.
* <p>Note that some properties of {@code WebTestClient.Builder} like the
* base URL, headers, and codecs can be customized through this builder.
* @param webClient a consumer that customizes the {@code WebClient} builder
* @see #url(String)
* @see #header(String, String...)
* @see #codecConfigurer(Consumer)

View File

@@ -64,6 +64,7 @@ public interface RSocketGraphQlTester extends GraphQlTester {
/**
* Start with a given {@link #builder()}.
* @param requesterBuilder the builder to use as a baseline
*/
static RSocketGraphQlTester.Builder<?> builder(RSocketRequester.Builder requesterBuilder) {
return new DefaultRSocketGraphQlTesterBuilder(requesterBuilder);
@@ -72,6 +73,7 @@ public interface RSocketGraphQlTester extends GraphQlTester {
/**
* Builder for a GraphQL over RSocket tester.
* @param <B> the type of builder
*/
interface Builder<B extends Builder<B>> extends GraphQlTester.Builder<B> {
@@ -119,11 +121,12 @@ public interface RSocketGraphQlTester extends GraphQlTester {
* <p>Note that some properties of {@code RSocketRequester.Builder} like the
* data MimeType, and the underlying RSocket transport can be customized
* through this builder.
* @param requester a consumer that customizes the {@code RSocketRequester} through its builder
* @return the same builder instance
* @see #dataMimeType(MimeType)
* @see #tcp(String, int)
* @see #webSocket(URI)
* @see #clientTransport(ClientTransport)
* @return the same builder instance
*/
B rsocketRequester(Consumer<RSocketRequester.Builder> requester);

View File

@@ -35,7 +35,6 @@ import org.springframework.lang.Nullable;
* {@code GraphQlTransport} that calls directly a {@link WebGraphQlHandler}.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class WebGraphQlHandlerGraphQlTransport extends AbstractDirectGraphQlTransport {
@@ -51,26 +50,26 @@ final class WebGraphQlHandlerGraphQlTransport extends AbstractDirectGraphQlTrans
WebGraphQlHandlerGraphQlTransport(
@Nullable URI url, HttpHeaders headers, WebGraphQlHandler handler, CodecConfigurer configurer) {
this.url = (url != null ? url : URI.create(""));
this.url = (url != null) ? url : URI.create("");
this.headers.addAll(headers);
this.graphQlHandler = handler;
this.codecConfigurer = configurer;
}
public URI getUrl() {
URI getUrl() {
return this.url;
}
public HttpHeaders getHeaders() {
HttpHeaders getHeaders() {
return this.headers;
}
public WebGraphQlHandler getGraphQlHandler() {
WebGraphQlHandler getGraphQlHandler() {
return this.graphQlHandler;
}
public CodecConfigurer getCodecConfigurer() {
CodecConfigurer getCodecConfigurer() {
return this.codecConfigurer;
}

View File

@@ -43,6 +43,7 @@ public interface WebGraphQlTester extends GraphQlTester {
/**
* Create a {@link WebGraphQlTester} instance.
* @param graphQlHandler the web GraphQL handler to be tested
*/
static WebGraphQlTester create(WebGraphQlHandler graphQlHandler) {
return builder(graphQlHandler).build();
@@ -59,6 +60,7 @@ public interface WebGraphQlTester extends GraphQlTester {
/**
* Common builder for Web {@code GraphQlTester} extensions.
* @param <B> the type of builder
*/
interface Builder<B extends Builder<B>> extends GraphQlTester.Builder<B> {
@@ -91,6 +93,7 @@ public interface WebGraphQlTester extends GraphQlTester {
/**
* Configure the underlying {@code CodecConfigurer} to use for all JSON
* encoding and decoding needs.
* @param codecsConsumer a consumer that customizes the configured codecs
*/
B codecConfigurer(Consumer<CodecConfigurer> codecsConsumer);

View File

@@ -77,6 +77,7 @@ public interface WebSocketGraphQlTester extends WebGraphQlTester {
/**
* Builder for a GraphQL over WebSocket tester.
* @param <B> the type of builder
*/
interface Builder<B extends Builder<B>> extends WebGraphQlTester.Builder<B> {

View File

@@ -34,12 +34,11 @@ import org.springframework.util.Assert;
* {@code GraphQlTransport} for GraphQL over HTTP via {@link WebTestClient}.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class WebTestClientTransport implements GraphQlTransport {
private static final ParameterizedTypeReference<Map<String, Object>> MAP_TYPE =
new ParameterizedTypeReference<Map<String, Object>>() {};
new ParameterizedTypeReference<Map<String, Object>>() { };
private final WebTestClient webTestClient;
@@ -65,7 +64,7 @@ final class WebTestClientTransport implements GraphQlTransport {
.returnResult()
.getResponseBody();
responseMap = (responseMap != null ? responseMap : Collections.emptyMap());
responseMap = (responseMap != null) ? responseMap : Collections.emptyMap();
GraphQlResponse response = GraphQlTransport.createResponse(responseMap);
return Mono.just(response);
}

View File

@@ -139,7 +139,7 @@ public class GraphQlTesterTests extends GraphQlTesterTestSupport {
assertThat(actual.getName()).isEqualTo("Luke Skywalker");
response.path("")
.entity(new ParameterizedTypeReference<Map<String, MovieCharacter>>() {})
.entity(new ParameterizedTypeReference<Map<String, MovieCharacter>>() { })
.isEqualTo(Collections.singletonMap("me", luke));
assertThat(getActualRequestDocument()).contains(document);
@@ -186,7 +186,7 @@ public class GraphQlTesterTests extends GraphQlTesterTestSupport {
"Request: document='{me {name, friends}}'");
response.path("me.friends")
.entityList(new ParameterizedTypeReference<MovieCharacter>() {})
.entityList(new ParameterizedTypeReference<MovieCharacter>() { })
.containsExactly(han, leia);
assertThat(getActualRequestDocument()).contains(document);

View File

@@ -117,7 +117,7 @@ public class RSocketGraphQlTesterBuilderTests {
assertThat(testDecoder.getLastValue()).isEqualTo(character);
}
private static class BuilderSetup {
private final MockExecutionGraphQlService graphQlService = new MockExecutionGraphQlService();

View File

@@ -61,7 +61,7 @@ public interface GraphQlRequest {
/**
* Convert the request to a {@link Map} as defined in
* <a href="https://github.com/graphql/graphql-over-http/blob/main/spec/GraphQLOverHTTP.md">GraphQL over HTTP</a> and
* <a href="https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md">GraphQL over WebSocket</a>:
* <a href="https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md">GraphQL over WebSocket</a>.
* <table>
* <tr><th>Key</th><th>Value</th></tr>
* <tr><td>query</td><td>{@link #getDocument() document}</td></tr>

View File

@@ -1,3 +1,19 @@
/*
* 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;
import java.util.List;

View File

@@ -47,6 +47,7 @@ import org.springframework.util.ClassUtils;
* agnostic {@code GraphQlClient}. A transport specific extension can then wrap
* this default tester by extending {@link AbstractDelegatingGraphQlClient}.
*
* @param <B> the type of builder
* @author Rossen Stoyanchev
* @since 1.0.0
* @see AbstractDelegatingGraphQlClient
@@ -123,6 +124,8 @@ public abstract class AbstractGraphQlClientBuilder<B extends AbstractGraphQlClie
* Transport-specific subclasses can provide their JSON {@code Encoder} and
* {@code Decoder} for use at the client level, for mapping response data
* to some target entity type.
* @param encoder the JSON encoder
* @param decoder the JSON decoder
*/
protected void setJsonCodecs(Encoder<?> encoder, Decoder<?> decoder) {
this.jsonEncoder = encoder;
@@ -131,6 +134,7 @@ public abstract class AbstractGraphQlClientBuilder<B extends AbstractGraphQlClie
/**
* Variant of {@link #setJsonCodecs} for setting each codec individually.
* @param encoder the JSON encoder
*/
protected void setJsonEncoder(Encoder<?> encoder) {
this.jsonEncoder = encoder;
@@ -146,6 +150,7 @@ public abstract class AbstractGraphQlClientBuilder<B extends AbstractGraphQlClie
/**
* Variant of {@link #setJsonCodecs} for setting each codec individually.
* @param decoder the JSON decoder
*/
protected void setJsonDecoder(Decoder<?> decoder) {
this.jsonDecoder = decoder;
@@ -170,12 +175,13 @@ public abstract class AbstractGraphQlClientBuilder<B extends AbstractGraphQlClie
/**
* Build the default transport-agnostic client that subclasses can then wrap
* with {@link AbstractDelegatingGraphQlClient}.
* @param transport the GraphQL transport to be used by the client
*/
protected GraphQlClient buildGraphQlClient(GraphQlTransport transport) {
if (jackson2Present) {
this.jsonEncoder = (this.jsonEncoder == null ? DefaultJackson2Codecs.encoder() : this.jsonEncoder);
this.jsonDecoder = (this.jsonDecoder == null ? DefaultJackson2Codecs.decoder() : this.jsonDecoder);
this.jsonEncoder = (this.jsonEncoder == null) ? DefaultJackson2Codecs.encoder() : this.jsonEncoder;
this.jsonDecoder = (this.jsonDecoder == null) ? DefaultJackson2Codecs.decoder() : this.jsonDecoder;
}
return new DefaultGraphQlClient(this.documentSource,
@@ -186,33 +192,33 @@ public abstract class AbstractGraphQlClientBuilder<B extends AbstractGraphQlClie
* Return a {@code Consumer} to initialize new builders from "this" builder.
*/
protected Consumer<AbstractGraphQlClientBuilder<?>> getBuilderInitializer() {
return builder -> {
builder.interceptors(interceptorList -> interceptorList.addAll(interceptors));
builder.documentSource(documentSource);
return (builder) -> {
builder.interceptors((interceptorList) -> interceptorList.addAll(this.interceptors));
builder.documentSource(this.documentSource);
builder.setJsonCodecs(getEncoder(), getDecoder());
};
}
private Chain createExecuteChain(GraphQlTransport transport) {
Chain chain = request -> transport.execute(request)
.map(response -> new DefaultClientGraphQlResponse(request, response, getEncoder(), getDecoder()));
Chain chain = (request) -> transport.execute(request)
.map((response) -> new DefaultClientGraphQlResponse(request, response, getEncoder(), getDecoder()));
return this.interceptors.stream()
.reduce(GraphQlClientInterceptor::andThen)
.map(i -> (Chain) (request) -> i.intercept(request, chain))
.map((i) -> (Chain) (request) -> i.intercept(request, chain))
.orElse(chain);
}
private SubscriptionChain createSubscriptionChain(GraphQlTransport transport) {
SubscriptionChain chain = request -> transport
SubscriptionChain chain = (request) -> transport
.executeSubscription(request)
.map(response -> new DefaultClientGraphQlResponse(request, response, getEncoder(), getDecoder()));
.map((response) -> new DefaultClientGraphQlResponse(request, response, getEncoder(), getDecoder()));
return this.interceptors.stream()
.reduce(GraphQlClientInterceptor::andThen)
.map(i -> (SubscriptionChain) (request) -> i.interceptSubscription(request, chain))
.map((i) -> (SubscriptionChain) (request) -> i.interceptSubscription(request, chain))
.orElse(chain);
}

View File

@@ -49,6 +49,7 @@ import org.springframework.util.ClassUtils;
* agnostic {@code GraphQlClient}. A transport specific extension can then wrap
* this default tester by extending {@link AbstractDelegatingGraphQlClient}.
*
* @param <B> the type of builder
* @author Rossen Stoyanchev
* @since 1.3
* @see AbstractDelegatingGraphQlClient
@@ -131,6 +132,7 @@ public abstract class AbstractGraphQlClientSyncBuilder<B extends AbstractGraphQl
* Transport-specific subclasses can provide their JSON {@code Encoder} and
* {@code Decoder} for use at the client level, for mapping response data
* to some target entity type.
* @param converter the message converter for JSON payloads
*/
protected void setJsonConverter(HttpMessageConverter<Object> converter) {
this.jsonConverter = converter;
@@ -140,12 +142,13 @@ public abstract class AbstractGraphQlClientSyncBuilder<B extends AbstractGraphQl
/**
* Build the default transport-agnostic client that subclasses can then wrap
* with {@link AbstractDelegatingGraphQlClient}.
* @param transport the GraphQL transport to be used by the client
*/
protected GraphQlClient buildGraphQlClient(SyncGraphQlTransport transport) {
if (jackson2Present) {
this.jsonConverter = (this.jsonConverter == null ?
DefaultJacksonConverter.initialize() : this.jsonConverter);
this.jsonConverter = (this.jsonConverter == null) ?
DefaultJacksonConverter.initialize() : this.jsonConverter;
}
return new DefaultGraphQlClient(
@@ -156,9 +159,9 @@ public abstract class AbstractGraphQlClientSyncBuilder<B extends AbstractGraphQl
* Return a {@code Consumer} to initialize new builders from "this" builder.
*/
protected Consumer<AbstractGraphQlClientSyncBuilder<?>> getBuilderInitializer() {
return builder -> {
builder.interceptors(interceptorList -> interceptorList.addAll(interceptors));
builder.documentSource(documentSource);
return (builder) -> {
builder.interceptors((interceptorList) -> interceptorList.addAll(this.interceptors));
builder.documentSource(this.documentSource);
builder.setJsonConverter(getJsonConverter());
};
}
@@ -168,14 +171,14 @@ public abstract class AbstractGraphQlClientSyncBuilder<B extends AbstractGraphQl
Encoder<?> encoder = HttpMessageConverterDelegate.asEncoder(getJsonConverter());
Decoder<?> decoder = HttpMessageConverterDelegate.asDecoder(getJsonConverter());
Chain chain = request -> {
Chain chain = (request) -> {
GraphQlResponse response = transport.execute(request);
return new DefaultClientGraphQlResponse(request, response, encoder, decoder);
};
return this.interceptors.stream()
.reduce(SyncGraphQlClientInterceptor::andThen)
.map(i -> (Chain) (request) -> i.intercept(request, chain))
.map((i) -> (Chain) (request) -> i.intercept(request, chain))
.orElse(chain);
}
@@ -185,7 +188,7 @@ public abstract class AbstractGraphQlClientSyncBuilder<B extends AbstractGraphQl
}
private static class DefaultJacksonConverter {
private static final class DefaultJacksonConverter {
static HttpMessageConverter<Object> initialize() {
return new MappingJackson2HttpMessageConverter();

View File

@@ -32,10 +32,12 @@ public interface ClientGraphQlResponse extends GraphQlResponse {
/**
* {@inheritDoc}
*/
@Override
ClientResponseField field(String path);
/**
* Decode the full response map to the given target type.
* @param <D> the target type
* @param type the target class
* @return the decoded value, or never {@code null}
* @throws FieldAccessException if the response is not {@link #isValid() valid}
@@ -44,7 +46,8 @@ public interface ClientGraphQlResponse extends GraphQlResponse {
/**
* Variant of {@link #toEntity(Class)} with a {@link ParameterizedTypeReference}.
* @param type the target type
* @param <D> the target type
* @param type the target parameterized type
* @return the decoded value, or never {@code null}
* @throws FieldAccessException if the response is not {@link #isValid() valid}
*/

View File

@@ -34,6 +34,7 @@ public interface ClientResponseField extends ResponseField {
/**
* Decode the field to an entity of the given type.
* @param <D> the entity type
* @param entityType the type to convert to
* @return the decoded entity, or {@code null} if the field is {@code null}
* but otherwise there are no errors
@@ -46,12 +47,15 @@ public interface ClientResponseField extends ResponseField {
/**
* Variant of {@link #toEntity(Class)} with a {@link ParameterizedTypeReference}.
* @param <D> the entity type
* @param entityType the type to convert to
*/
@Nullable
<D> D toEntity(ParameterizedTypeReference<D> entityType);
/**
* Variant of {@link #toEntity(Class)} to decode to a list of entities.
* @param <D> the entity type
* @param elementType the type of elements in the list
* @return the list of decoded entities, or an empty list if the field is
* {@code null} but otherwise there are no errors
@@ -61,15 +65,16 @@ public interface ClientResponseField extends ResponseField {
*/
<D> List<D> toEntityList(Class<D> elementType);
/**
* Variant of {@link #toEntity(Class)} to decode to a list of entities.
* @param elementType the type of elements in the list
* @return the list of decoded entities, or an empty list if the field is
* {@code null} but otherwise there are no errors
* @throws FieldAccessException if the target field is {@code null} and the
* response is not {@link GraphQlResponse#isValid() valid} or the field has
* {@link ResponseField#getErrors() errors}.
*/
/**
* Variant of {@link #toEntity(Class)} to decode to a list of entities.
* @param <D> the entity type
* @param elementType the type of elements in the list
* @return the list of decoded entities, or an empty list if the field is
* {@code null} but otherwise there are no errors
* @throws FieldAccessException if the target field is {@code null} and the
* response is not {@link GraphQlResponse#isValid() valid} or the field has
* {@link ResponseField#getErrors() errors}.
*/
<D> List<D> toEntityList(ParameterizedTypeReference<D> elementType);
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.client;
import java.util.List;
@@ -37,7 +38,6 @@ import org.springframework.web.reactive.socket.WebSocketSession;
* Helper class for encoding and decoding GraphQL messages.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class CodecDelegate {
@@ -60,14 +60,14 @@ final class CodecDelegate {
static Encoder<?> findJsonEncoder(CodecConfigurer configurer) {
return findJsonEncoder(configurer.getWriters().stream()
.filter(writer -> writer instanceof EncoderHttpMessageWriter)
.map(writer -> ((EncoderHttpMessageWriter<?>) writer).getEncoder()));
.filter((writer) -> writer instanceof EncoderHttpMessageWriter)
.map((writer) -> ((EncoderHttpMessageWriter<?>) writer).getEncoder()));
}
static Decoder<?> findJsonDecoder(CodecConfigurer configurer) {
return findJsonDecoder(configurer.getReaders().stream()
.filter(reader -> reader instanceof DecoderHttpMessageReader)
.map(reader -> ((DecoderHttpMessageReader<?>) reader).getDecoder()));
.filter((reader) -> reader instanceof DecoderHttpMessageReader)
.map((reader) -> ((DecoderHttpMessageReader<?>) reader).getDecoder()));
}
static Encoder<?> findJsonEncoder(List<Encoder<?>> encoders) {
@@ -80,26 +80,26 @@ final class CodecDelegate {
private static Encoder<?> findJsonEncoder(Stream<Encoder<?>> stream) {
return stream
.filter(encoder -> encoder.canEncode(MESSAGE_TYPE, MediaType.APPLICATION_JSON))
.filter((encoder) -> encoder.canEncode(MESSAGE_TYPE, MediaType.APPLICATION_JSON))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("No JSON Encoder"));
}
private static Decoder<?> findJsonDecoder(Stream<Decoder<?>> decoderStream) {
return decoderStream
.filter(decoder -> decoder.canDecode(MESSAGE_TYPE, MediaType.APPLICATION_JSON))
.filter((decoder) -> decoder.canDecode(MESSAGE_TYPE, MediaType.APPLICATION_JSON))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("No JSON Decoder"));
}
public CodecConfigurer getCodecConfigurer() {
CodecConfigurer getCodecConfigurer() {
return this.codecConfigurer;
}
@SuppressWarnings("unchecked")
public <T> WebSocketMessage encode(WebSocketSession session, GraphQlWebSocketMessage message) {
<T> WebSocketMessage encode(WebSocketSession session, GraphQlWebSocketMessage message) {
DataBuffer buffer = ((Encoder<T>) this.encoder).encodeValue(
(T) message, session.bufferFactory(), MESSAGE_TYPE, MimeTypeUtils.APPLICATION_JSON, null);
@@ -108,7 +108,7 @@ final class CodecDelegate {
}
@SuppressWarnings("ConstantConditions")
public GraphQlWebSocketMessage decode(WebSocketMessage webSocketMessage) {
GraphQlWebSocketMessage decode(WebSocketMessage webSocketMessage) {
DataBuffer buffer = DataBufferUtils.retain(webSocketMessage.getPayload());
return (GraphQlWebSocketMessage) this.decoder.decode(buffer, MESSAGE_TYPE, null, null);
}

View File

@@ -27,7 +27,6 @@ import org.springframework.lang.Nullable;
* Default implementation of {@link ClientGraphQlRequest}.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class DefaultClientGraphQlRequest extends DefaultGraphQlRequest implements ClientGraphQlRequest {

View File

@@ -26,7 +26,6 @@ import org.springframework.graphql.GraphQlResponse;
* Default implementation of {@link ClientGraphQlResponse}.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class DefaultClientGraphQlResponse extends ResponseMapGraphQlResponse implements ClientGraphQlResponse {

View File

@@ -40,7 +40,6 @@ import org.springframework.util.MimeTypeUtils;
* support for decoding.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class DefaultClientResponseField implements ClientResponseField {
@@ -88,13 +87,13 @@ final class DefaultClientResponseField implements ClientResponseField {
@Override
public <D> List<D> toEntityList(Class<D> elementType) {
List<D> list = toEntity(ResolvableType.forClassWithGenerics(List.class, elementType));
return (list != null ? list : Collections.emptyList());
return (list != null) ? list : Collections.emptyList();
}
@Override
public <D> List<D> toEntityList(ParameterizedTypeReference<D> elementType) {
List<D> list = toEntity(ResolvableType.forClassWithGenerics(List.class, ResolvableType.forType(elementType)));
return (list != null ? list : Collections.emptyList());
return (list != null) ? list : Collections.emptyList();
}
@SuppressWarnings("unchecked")

View File

@@ -36,7 +36,6 @@ import org.springframework.util.Assert;
* Default, final {@link GraphQlClient} implementation for use with any transport.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class DefaultGraphQlClient implements GraphQlClient {
@@ -63,7 +62,7 @@ final class DefaultGraphQlClient implements GraphQlClient {
this.documentSource = documentSource;
this.blockingChain = blockingChain;
this.nonBlockingChain = adaptToNonBlockingChain(blockingChain, scheduler);
this.subscriptionChain = request -> Flux.error(new IllegalStateException("Subscriptions on supported"));
this.subscriptionChain = (request) -> Flux.error(new IllegalStateException("Subscriptions on supported"));
this.blockingTimeout = blockingTimeout;
}
@@ -87,14 +86,14 @@ final class DefaultGraphQlClient implements GraphQlClient {
private static GraphQlClientInterceptor.Chain adaptToNonBlockingChain(
SyncGraphQlClientInterceptor.Chain blockingChain, Scheduler scheduler) {
return request -> Mono.fromCallable(() -> blockingChain.next(request)).subscribeOn(scheduler);
return (request) -> Mono.fromCallable(() -> blockingChain.next(request)).subscribeOn(scheduler);
}
@SuppressWarnings("DataFlowIssue")
private static SyncGraphQlClientInterceptor.Chain adaptToBlockingChain(
GraphQlClientInterceptor.Chain executeChain, @Nullable Duration blockingTimeout) {
return (request -> blockingTimeout != null ?
return ((request) -> (blockingTimeout != null) ?
executeChain.next(request).block(blockingTimeout) : executeChain.next(request).block());
}
@@ -203,28 +202,28 @@ final class DefaultGraphQlClient implements GraphQlClient {
@Override
public ClientGraphQlResponse executeSync() {
Mono<ClientGraphQlRequest> mono = initRequest();
ClientGraphQlRequest request = (blockingTimeout != null ? mono.block(blockingTimeout) : mono.block());
return blockingChain.next(request);
ClientGraphQlRequest request = (DefaultGraphQlClient.this.blockingTimeout != null) ? mono.block(DefaultGraphQlClient.this.blockingTimeout) : mono.block();
return DefaultGraphQlClient.this.blockingChain.next(request);
}
@Override
public Mono<ClientGraphQlResponse> execute() {
return initRequest().flatMap(request -> nonBlockingChain.next(request)
return initRequest().flatMap((request) -> DefaultGraphQlClient.this.nonBlockingChain.next(request)
.onErrorResume(
ex -> !(ex instanceof GraphQlClientException),
ex -> Mono.error(new GraphQlTransportException(ex, request))));
(ex) -> !(ex instanceof GraphQlClientException),
(ex) -> Mono.error(new GraphQlTransportException(ex, request))));
}
@Override
public Flux<ClientGraphQlResponse> executeSubscription() {
return initRequest().flatMapMany(request -> subscriptionChain.next(request)
return initRequest().flatMapMany((request) -> DefaultGraphQlClient.this.subscriptionChain.next(request)
.onErrorResume(
ex -> !(ex instanceof GraphQlClientException),
ex -> Mono.error(new GraphQlTransportException(ex, request))));
(ex) -> !(ex instanceof GraphQlClientException),
(ex) -> Mono.error(new GraphQlTransportException(ex, request))));
}
private Mono<ClientGraphQlRequest> initRequest() {
return this.documentMono.map(document -> new DefaultClientGraphQlRequest(
return this.documentMono.map((document) -> new DefaultClientGraphQlRequest(
document, this.operationName, this.variables, this.extensions, this.attributes));
}
@@ -252,7 +251,7 @@ final class DefaultGraphQlClient implements GraphQlClient {
throw new FieldAccessException(
((DefaultClientGraphQlResponse) response).getRequest(), response, field);
}
return (field.getValue() != null ? field : null);
return (field.getValue() != null) ? field : null;
}
}
@@ -270,25 +269,25 @@ final class DefaultGraphQlClient implements GraphQlClient {
@Override
public <D> D toEntity(Class<D> entityType) {
ClientResponseField field = getValidField(this.response);
return (field != null ? field.toEntity(entityType) : null);
return (field != null) ? field.toEntity(entityType) : null;
}
@Override
public <D> D toEntity(ParameterizedTypeReference<D> entityType) {
ClientResponseField field = getValidField(this.response);
return (field != null ? field.toEntity(entityType) : null);
return (field != null) ? field.toEntity(entityType) : null;
}
@Override
public <D> List<D> toEntityList(Class<D> elementType) {
ClientResponseField field = getValidField(this.response);
return (field != null ? field.toEntityList(elementType) : Collections.emptyList());
return (field != null) ? field.toEntityList(elementType) : Collections.emptyList();
}
@Override
public <D> List<D> toEntityList(ParameterizedTypeReference<D> elementType) {
ClientResponseField field = getValidField(this.response);
return (field != null ? field.toEntityList(elementType) : Collections.emptyList());
return (field != null) ? field.toEntityList(elementType) : Collections.emptyList();
}
}
@@ -305,27 +304,27 @@ final class DefaultGraphQlClient implements GraphQlClient {
@Override
public <D> Mono<D> toEntity(Class<D> entityType) {
return this.responseMono.mapNotNull(this::getValidField).mapNotNull(field -> field.toEntity(entityType));
return this.responseMono.mapNotNull(this::getValidField).mapNotNull((field) -> field.toEntity(entityType));
}
@Override
public <D> Mono<D> toEntity(ParameterizedTypeReference<D> entityType) {
return this.responseMono.mapNotNull(this::getValidField).mapNotNull(field -> field.toEntity(entityType));
return this.responseMono.mapNotNull(this::getValidField).mapNotNull((field) -> field.toEntity(entityType));
}
@Override
public <D> Mono<List<D>> toEntityList(Class<D> elementType) {
return this.responseMono.map(response -> {
return this.responseMono.map((response) -> {
ClientResponseField field = getValidField(response);
return (field != null ? field.toEntityList(elementType) : Collections.emptyList());
return (field != null) ? field.toEntityList(elementType) : Collections.emptyList();
});
}
@Override
public <D> Mono<List<D>> toEntityList(ParameterizedTypeReference<D> elementType) {
return this.responseMono.map(response -> {
return this.responseMono.map((response) -> {
ClientResponseField field = getValidField(response);
return (field != null ? field.toEntityList(elementType) : Collections.emptyList());
return (field != null) ? field.toEntityList(elementType) : Collections.emptyList();
});
}
@@ -343,27 +342,27 @@ final class DefaultGraphQlClient implements GraphQlClient {
@Override
public <D> Flux<D> toEntity(Class<D> entityType) {
return this.responseFlux.mapNotNull(this::getValidField).mapNotNull(field -> field.toEntity(entityType));
return this.responseFlux.mapNotNull(this::getValidField).mapNotNull((field) -> field.toEntity(entityType));
}
@Override
public <D> Flux<D> toEntity(ParameterizedTypeReference<D> entityType) {
return this.responseFlux.mapNotNull(this::getValidField).mapNotNull(field -> field.toEntity(entityType));
return this.responseFlux.mapNotNull(this::getValidField).mapNotNull((field) -> field.toEntity(entityType));
}
@Override
public <D> Flux<List<D>> toEntityList(Class<D> elementType) {
return this.responseFlux.map(response -> {
return this.responseFlux.map((response) -> {
ClientResponseField field = getValidField(response);
return (field != null ? field.toEntityList(elementType) : Collections.emptyList());
return (field != null) ? field.toEntityList(elementType) : Collections.emptyList();
});
}
@Override
public <D> Flux<List<D>> toEntityList(ParameterizedTypeReference<D> elementType) {
return this.responseFlux.map(response -> {
return this.responseFlux.map((response) -> {
ClientResponseField field = getValidField(response);
return (field != null ? field.toEntityList(elementType) : Collections.emptyList());
return (field != null) ? field.toEntityList(elementType) : Collections.emptyList();
});
}

View File

@@ -33,7 +33,6 @@ import org.springframework.web.util.UriComponentsBuilder;
* around a {@link WebClient.Builder}.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class DefaultHttpGraphQlClientBuilder
extends AbstractGraphQlClientBuilder<DefaultHttpGraphQlClientBuilder>
@@ -105,7 +104,7 @@ final class DefaultHttpGraphQlClientBuilder
public HttpGraphQlClient build() {
// Pass the codecs to the parent for response decoding
this.webClientBuilder.codecs(configurer ->
this.webClientBuilder.codecs((configurer) ->
setJsonCodecs(
CodecDelegate.findJsonEncoder(configurer),
CodecDelegate.findJsonDecoder(configurer)));
@@ -139,6 +138,7 @@ final class DefaultHttpGraphQlClientBuilder
this.builderInitializer = builderInitializer;
}
@Override
public DefaultHttpGraphQlClientBuilder mutate() {
DefaultHttpGraphQlClientBuilder builder = new DefaultHttpGraphQlClientBuilder(this.webClient);
this.builderInitializer.accept(builder);

View File

@@ -41,7 +41,6 @@ import org.springframework.util.MimeTypeUtils;
* a {@link RSocketRequester.Builder}.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class DefaultRSocketGraphQlClientBuilder
extends AbstractGraphQlClientBuilder<DefaultRSocketGraphQlClientBuilder>
@@ -134,9 +133,9 @@ final class DefaultRSocketGraphQlClientBuilder
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)));
this.requesterBuilder.rsocketStrategies((builder) -> {
builder.decoders((decoders) -> setJsonDecoder(CodecDelegate.findJsonDecoder(decoders)));
builder.encoders((encoders) -> setJsonEncoder(CodecDelegate.findJsonEncoder(encoders)));
});
RSocketRequester requester;

View File

@@ -34,7 +34,6 @@ import org.springframework.web.util.UriComponentsBuilder;
* around a {@link RestClient.Builder}.
*
* @author Rossen Stoyanchev
* @since 1.3
*/
final class DefaultSyncHttpGraphQlClientBuilder
extends AbstractGraphQlClientSyncBuilder<DefaultSyncHttpGraphQlClientBuilder>
@@ -105,7 +104,7 @@ final class DefaultSyncHttpGraphQlClientBuilder
@Override
public HttpSyncGraphQlClient build() {
this.restClientBuilder.messageConverters(converters -> {
this.restClientBuilder.messageConverters((converters) -> {
HttpMessageConverter<Object> converter = HttpMessageConverterDelegate.findJsonConverter(converters);
setJsonConverter(converter);
});
@@ -141,6 +140,7 @@ final class DefaultSyncHttpGraphQlClientBuilder
this.builderInitializer = builderInitializer;
}
@Override
public DefaultSyncHttpGraphQlClientBuilder mutate() {
DefaultSyncHttpGraphQlClientBuilder builder = new DefaultSyncHttpGraphQlClientBuilder(this.restClient);
this.builderInitializer.accept(builder);

View File

@@ -26,7 +26,6 @@ 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> {

View File

@@ -20,7 +20,6 @@ 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;
@@ -37,7 +36,6 @@ import org.springframework.web.util.DefaultUriBuilderFactory;
* {@code WebSocketGraphQlTransport}.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class DefaultWebSocketGraphQlClientBuilder
extends AbstractGraphQlClientBuilder<DefaultWebSocketGraphQlClientBuilder>
@@ -130,14 +128,14 @@ final class DefaultWebSocketGraphQlClientBuilder
private WebSocketGraphQlClientInterceptor getInterceptor() {
List<WebSocketGraphQlClientInterceptor> interceptors = getInterceptors().stream()
.filter(interceptor -> interceptor instanceof WebSocketGraphQlClientInterceptor)
.map(interceptor -> (WebSocketGraphQlClientInterceptor) interceptor)
.filter((interceptor) -> interceptor instanceof WebSocketGraphQlClientInterceptor)
.map((interceptor) -> (WebSocketGraphQlClientInterceptor) interceptor)
.toList();
Assert.state(interceptors.size() <= 1,
"Only a single interceptor of type WebSocketGraphQlClientInterceptor may be configured");
return (!interceptors.isEmpty() ? interceptors.get(0) : new WebSocketGraphQlClientInterceptor() {});
return (!interceptors.isEmpty() ? interceptors.get(0) : new WebSocketGraphQlClientInterceptor() { });
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.client;
import java.util.HashMap;
@@ -39,8 +40,8 @@ import org.springframework.util.Assert;
* GraphQlClient client = ... ;
* DgsGraphQlClient dgsClient = DgsGraphQlClient.create(client);
*
* List<Book> books = dgsClient.request(new BooksGraphQLQuery())
* .projection(new BooksProjectionRoot<>().id().name())
* List&lt;Book&gt; books = dgsClient.request(new BooksGraphQLQuery())
* .projection(new BooksProjectionRoot&lt;&gt;().id().name())
* .retrieveSync()
* .toEntityList(Book.class);
* </pre>
@@ -67,6 +68,7 @@ public final class DgsGraphQlClient {
/**
* Start defining a GraphQL request for the given {@link GraphQLQuery}.
* @param query the GraphQL query
*/
public RequestSpec request(GraphQLQuery query) {
return new RequestSpec(query);
@@ -80,7 +82,7 @@ public final class DgsGraphQlClient {
public static DgsGraphQlClient create(GraphQlClient client) {
return new DgsGraphQlClient(client);
}
/**
* Declare options to gather input for a GraphQL request and execute it.
@@ -105,6 +107,7 @@ public final class DgsGraphQlClient {
/**
* Provide a {@link BaseProjectionNode} that defines the response selection set.
* @param projectionNode the response selection set
* @return ths same builder instance
*/
public RequestSpec projection(BaseProjectionNode projectionNode) {
@@ -114,20 +117,23 @@ public final class DgsGraphQlClient {
/**
* Configure {@link Coercing} for serialization of scalar types.
* @param scalarType the scalar type
* @param coercing the coercing function for this scalar
* @return ths same builder instance
*/
public RequestSpec coercing(Class<?> scalarType, Coercing<?, ?> coercing) {
this.coercingMap = (this.coercingMap != null ? this.coercingMap : new LinkedHashMap<>());
this.coercingMap = (this.coercingMap != null) ? this.coercingMap : new LinkedHashMap<>();
this.coercingMap.put(scalarType, coercing);
return this;
}
/**
* Configure {@link Coercing} for serialization of scalar types.
* @param coercingMap the map of coercing function
* @return ths same builder instance
*/
public RequestSpec coercing(Map<Class<?>, Coercing<?, ?>> coercingMap) {
this.coercingMap = (this.coercingMap != null ? this.coercingMap : new LinkedHashMap<>());
this.coercingMap = (this.coercingMap != null) ? this.coercingMap : new LinkedHashMap<>();
this.coercingMap.putAll(coercingMap);
return this;
}
@@ -136,10 +142,12 @@ public final class DgsGraphQlClient {
* Set a client request attribute.
* <p>This is purely for client side request processing, i.e. available
* throughout the {@link GraphQlClientInterceptor} chain but not sent.
* @param name the attribute name
* @param value the attribute value
* @return ths same builder instance
*/
public RequestSpec attribute(String name, Object value) {
this.attributes = (this.attributes != null ? this.attributes : new HashMap<>());
this.attributes = (this.attributes != null) ? this.attributes : new HashMap<>();
this.attributes.put(name, value);
return this;
}
@@ -147,10 +155,11 @@ public final class DgsGraphQlClient {
/**
* Manipulate the client request attributes. The map provided to the consumer
* is "live", so the consumer can inspect and modify attributes accordingly.
* @param attributesConsumer the consumer that will manipulate request attributes
* @return ths same builder instance
*/
public RequestSpec attributes(Consumer<Map<String, Object>> attributesConsumer) {
this.attributes = (this.attributes != null ? this.attributes : new HashMap<>());
this.attributes = (this.attributes != null) ? this.attributes : new HashMap<>();
attributesConsumer.accept(this.attributes);
return this;
}
@@ -168,6 +177,7 @@ public final class DgsGraphQlClient {
/**
* Variant of {@link #executeSync()} with explicit path relative to the "data" key.
* @param path the JSON path relative to the "data" key
*/
public GraphQlClient.RetrieveSyncSpec retrieveSync(String path) {
return initRequestSpec().retrieveSync(path);
@@ -186,6 +196,7 @@ public final class DgsGraphQlClient {
/**
* Variant of {@link #retrieve()} with explicit path relative to the "data" key.
* @param path the JSON path relative to the "data" key
*/
public GraphQlClient.RetrieveSpec retrieve(String path) {
return initRequestSpec().retrieve(path);
@@ -237,15 +248,15 @@ public final class DgsGraphQlClient {
Assert.state(this.projectionNode != null || this.coercingMap == null,
"Coercing map provided without projection");
GraphQLQueryRequest request = (this.coercingMap != null ?
GraphQLQueryRequest request = (this.coercingMap != null) ?
new GraphQLQueryRequest(this.query, this.projectionNode, this.coercingMap) :
new GraphQLQueryRequest(this.query, this.projectionNode));
new GraphQLQueryRequest(this.query, this.projectionNode);
String operationName = (this.query.getName() != null ? this.query.getName() : null);
String operationName = (this.query.getName() != null) ? this.query.getName() : null;
return graphQlClient.document(request.serialize())
return DgsGraphQlClient.this.graphQlClient.document(request.serialize())
.operationName(operationName)
.attributes(map -> {
.attributes((map) -> {
if (this.attributes != null) {
map.putAll(this.attributes);
}

View File

@@ -38,6 +38,9 @@ public class FieldAccessException extends GraphQlClientException {
/**
* Constructor with the request and response, and the accessed field.
* @param request the client request
* @param response the client response
* @param field the accessed field that caused the error
*/
public FieldAccessException(
ClientGraphQlRequest request, ClientGraphQlResponse response, ClientResponseField field) {

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.client;
import java.time.Duration;
@@ -64,6 +65,7 @@ public interface GraphQlClient {
* Variant of {@link #document(String)} that uses the given key to resolve
* the GraphQL document from a file with the help of the configured
* {@link Builder#documentSource(DocumentSource) DocumentSource}.
* @param name the document name
* @throws IllegalArgumentException if the content could not be loaded
*/
RequestSpec documentName(String name);
@@ -90,7 +92,7 @@ public interface GraphQlClient {
/**
* Base builder for creating and initializing a {@link GraphQlClient}.
* @since 1.3
* @param <B> the type of builder
*/
interface BaseBuilder<B extends BaseBuilder<B>> {
@@ -100,6 +102,7 @@ public interface GraphQlClient {
* <p>By default, this is set to {@link ResourceDocumentSource} with
* classpath location {@code "graphql-documents/"} and
* {@link ResourceDocumentSource#FILE_EXTENSIONS} as extensions.
* @param contentLoader the strategy for resolving documents by their names
*/
B documentSource(DocumentSource contentLoader);
@@ -125,7 +128,7 @@ public interface GraphQlClient {
/**
* Builder to create a {@link GraphQlClient} instance with a
* synchronous execution chain and transport.
* @since 1.3
* @param <B> the type of builder
* @see SyncGraphQlTransport
*/
interface SyncBuilder<B extends SyncBuilder<B>> extends BaseBuilder<B> {
@@ -159,6 +162,7 @@ public interface GraphQlClient {
/**
* Builder to create a {@link GraphQlClient} with a non-blocking execution
* chain and transport.
* @param <B> the type of builder
*/
interface Builder<B extends Builder<B>> extends BaseBuilder<B> {
@@ -248,6 +252,7 @@ public interface GraphQlClient {
* <pre>
* client.document("..").executeSync()
* </pre>
* @param path the field path
* @return a spec with decoding options
* @throws FieldAccessException if the field has any field errors,
* including errors at, above or below the field path.
@@ -261,6 +266,7 @@ public interface GraphQlClient {
* <pre>
* client.document("..").execute().map(response -> ...)
* </pre>
* @param path the field path
* @return a spec with decoding options
* @throws FieldAccessException if the field has any field errors,
* including errors at, above or below the field path.
@@ -274,6 +280,7 @@ public interface GraphQlClient {
* <pre>
* client.document("..").executeSubscription().map(response -> ...)
* </pre>
* @param path the field path
* @return a spec with decoding options
*/
RetrieveSubscriptionSpec retrieveSubscription(String path);
@@ -318,12 +325,12 @@ public interface GraphQlClient {
/**
* Declares options to decode a field in a single response.
* @since 1.3
*/
interface RetrieveSyncSpec {
/**
* Decode the field to an entity of the given type.
* @param <D> the type to convert to
* @param entityType the type to convert to
* @return the entity or null if the field is {@code null} and has no errors.
* @throws FieldAccessException in case of {@link ResponseField field
@@ -335,18 +342,23 @@ public interface GraphQlClient {
/**
* Variant of {@link #toEntity(Class)} with a {@link ParameterizedTypeReference}.
* @param <D> the type to convert to
* @param entityType the type to convert to
*/
@Nullable
<D> D toEntity(ParameterizedTypeReference<D> entityType);
/**
* Variant of {@link #toEntity(Class)} to decode to a List of entities.
* @param <D> the type to convert to
* @param elementType the type of elements in the list
*/
<D> List<D> toEntityList(Class<D> elementType);
/**
* Variant of {@link #toEntityList(Class)} with a {@link ParameterizedTypeReference}.
* @param <D> the type to convert to
* @param elementType the type of elements in the list
*/
<D> List<D> toEntityList(ParameterizedTypeReference<D> elementType);
@@ -360,6 +372,7 @@ public interface GraphQlClient {
/**
* Decode the field to an entity of the given type.
* @param <D> the type to convert to
* @param entityType the type to convert to
* @return {@code Mono} with the decoded entity; completes with
* {@link FieldAccessException} in case of {@link ResponseField field
@@ -371,17 +384,22 @@ public interface GraphQlClient {
/**
* Variant of {@link #toEntity(Class)} with a {@link ParameterizedTypeReference}.
* @param <D> the entity type
* @param entityType the type to convert to
*/
<D> Mono<D> toEntity(ParameterizedTypeReference<D> entityType);
/**
* Variant of {@link #toEntity(Class)} to decode to a List of entities.
* @param <D> the type to convert to
* @param elementType the type of elements in the list
*/
<D> Mono<List<D>> toEntityList(Class<D> elementType);
/**
* Variant of {@link #toEntityList(Class)} with a {@link ParameterizedTypeReference}.
* @param <D> the type to convert to
* @param elementType the type of elements in the list
*/
<D> Mono<List<D>> toEntityList(ParameterizedTypeReference<D> elementType);
@@ -395,6 +413,7 @@ public interface GraphQlClient {
/**
* Decode the field to an entity of the given type.
* @param <D> the type to convert to
* @param entityType the type to convert to
* @return {@code Mono} with the decoded entity; completes with
* {@link FieldAccessException} in case of {@link ResponseField field
@@ -406,20 +425,25 @@ public interface GraphQlClient {
/**
* Variant of {@link #toEntity(Class)} with a {@link ParameterizedTypeReference}.
* @param <D> the type to convert to
* @param entityType the type to convert to
*/
<D> Flux<D> toEntity(ParameterizedTypeReference<D> entityType);
/**
* Variant of {@link #toEntity(Class)} to decode each response to a List of entities.
* @param <D> the type to convert to
* @param elementType the type of elements in the list
*/
<D> Flux<List<D>> toEntityList(Class<D> elementType);
/**
* Variant of {@link #toEntity(Class)} to decode each response to a List of entities.
* @param <D> the type to convert to
* @param elementType the type of elements in the list
*/
<D> Flux<List<D>> toEntityList(ParameterizedTypeReference<D> elementType);
}
}
}

View File

@@ -36,6 +36,9 @@ public class GraphQlClientException extends NestedRuntimeException {
/**
* Constructor with a message, optional cause, and the request details.
* @param message the exception message to use
* @param cause the original cause for the client exception
* @param request the request that failed
*/
public GraphQlClientException(String message, @Nullable Throwable cause, GraphQlRequest request) {
super(message, cause);

View File

@@ -67,13 +67,13 @@ public interface GraphQlClientInterceptor {
@Override
public Mono<ClientGraphQlResponse> intercept(ClientGraphQlRequest request, Chain chain) {
return GraphQlClientInterceptor.this.intercept(
request, nextRequest -> interceptor.intercept(nextRequest, chain));
request, (nextRequest) -> interceptor.intercept(nextRequest, chain));
}
@Override
public Flux<ClientGraphQlResponse> interceptSubscription(ClientGraphQlRequest request, SubscriptionChain chain) {
return GraphQlClientInterceptor.this.interceptSubscription(
request, nextRequest -> interceptor.interceptSubscription(nextRequest, chain));
request, (nextRequest) -> interceptor.interceptSubscription(nextRequest, chain));
}
};
}

View File

@@ -63,6 +63,7 @@ public interface GraphQlTransport {
/**
* Factory method to create {@link GraphQlResponse} from a GraphQL response
* map for use in transport implementations.
* @param responseMap the GraphQL response map
*/
static GraphQlResponse createResponse(Map<String, Object> responseMap) {
return new ResponseMapGraphQlResponse(responseMap);

View File

@@ -32,6 +32,8 @@ public class GraphQlTransportException extends GraphQlClientException {
/**
* Constructor with a default message.
* @param cause the original cause of the transport error
* @param request the request that failed at the transport level
*/
public GraphQlTransportException(@Nullable Throwable cause, GraphQlRequest request) {
super("GraphQlTransport error: " + cause.getMessage(), cause, request);
@@ -39,6 +41,9 @@ public class GraphQlTransportException extends GraphQlClientException {
/**
* Constructor with a given message.
* @param message the exception message to use
* @param cause the original cause of the transport error
* @param request the request that failed at the transport level
*/
public GraphQlTransportException(String message, @Nullable Throwable cause, GraphQlRequest request) {
super(message, cause, request);

View File

@@ -36,6 +36,7 @@ public interface HttpGraphQlClient extends WebGraphQlClient {
/**
* Create an {@link HttpGraphQlClient} that uses the given {@link WebClient}.
* @param webClient the {@code WebClient} to use for sending HTTP requests
*/
static HttpGraphQlClient create(WebClient webClient) {
return builder(webClient.mutate()).build();
@@ -51,6 +52,7 @@ public interface HttpGraphQlClient extends WebGraphQlClient {
/**
* Variant of {@link #builder()} with a pre-configured {@code WebClient}
* to mutate and customize further through the returned builder.
* @param webClient the {@code WebClient} to use for sending HTTP requests
*/
static Builder<?> builder(WebClient webClient) {
return builder(webClient.mutate());
@@ -59,6 +61,7 @@ public interface HttpGraphQlClient extends WebGraphQlClient {
/**
* Variant of {@link #builder()} with a pre-configured {@code WebClient}
* to mutate and customize further through the returned builder.
* @param webClientBuilder the {@code WebClient.Builder} to use for building the HTTP client
*/
static Builder<?> builder(WebClient.Builder webClientBuilder) {
return new DefaultHttpGraphQlClientBuilder(webClientBuilder);
@@ -67,6 +70,7 @@ public interface HttpGraphQlClient extends WebGraphQlClient {
/**
* Builder for the GraphQL over HTTP client.
* @param <B> the builder type
*/
interface Builder<B extends Builder<B>> extends WebGraphQlClient.Builder<B> {
@@ -74,6 +78,7 @@ public interface HttpGraphQlClient extends WebGraphQlClient {
* Customize the {@code WebClient} to use.
* <p>Note that some properties of {@code WebClient.Builder} like the
* base URL, headers, and codecs can be customized through this builder.
* @param webClient the function for customizing the {@code WebClient.Builder} that's used to build the HTTP client
* @see #url(String)
* @see #header(String, String...)
* @see #codecConfigurer(Consumer)

View File

@@ -39,15 +39,14 @@ import org.springframework.web.reactive.function.client.WebClient;
*
* @author Rossen Stoyanchev
* @author Brian Clozel
* @since 1.0.0
*/
final class HttpGraphQlTransport implements GraphQlTransport {
private static final ParameterizedTypeReference<Map<String, Object>> MAP_TYPE =
new ParameterizedTypeReference<Map<String, Object>>() {};
new ParameterizedTypeReference<Map<String, Object>>() { };
private static final ParameterizedTypeReference<ServerSentEvent<Map<String, Object>>> SSE_TYPE =
new ParameterizedTypeReference<ServerSentEvent<Map<String, Object>>>() {};
new ParameterizedTypeReference<ServerSentEvent<Map<String, Object>>>() { };
// To be removed in favor of Framework's MediaType.APPLICATION_GRAPHQL_RESPONSE
private static final MediaType APPLICATION_GRAPHQL_RESPONSE =
@@ -69,7 +68,7 @@ final class HttpGraphQlTransport implements GraphQlTransport {
HttpHeaders headers = new HttpHeaders();
webClient.mutate().defaultHeaders(headers::putAll);
MediaType contentType = headers.getContentType();
return (contentType != null ? contentType : MediaType.APPLICATION_JSON);
return (contentType != null) ? contentType : MediaType.APPLICATION_JSON;
}
@@ -80,7 +79,7 @@ final class HttpGraphQlTransport implements GraphQlTransport {
.contentType(this.contentType)
.accept(MediaType.APPLICATION_JSON, APPLICATION_GRAPHQL_RESPONSE, MediaType.APPLICATION_GRAPHQL)
.bodyValue(request.toMap())
.attributes(attributes -> {
.attributes((attributes) -> {
if (request instanceof ClientGraphQlRequest clientRequest) {
attributes.putAll(clientRequest.getAttributes());
}
@@ -96,15 +95,15 @@ final class HttpGraphQlTransport implements GraphQlTransport {
.contentType(this.contentType)
.accept(MediaType.TEXT_EVENT_STREAM)
.bodyValue(request.toMap())
.attributes(attributes -> {
.attributes((attributes) -> {
if (request instanceof ClientGraphQlRequest clientRequest) {
attributes.putAll(clientRequest.getAttributes());
}
})
.retrieve()
.bodyToFlux(SSE_TYPE)
.takeWhile(event -> "next".equals(event.event()))
.map(event -> new ResponseMapGraphQlResponse(event.data()));
.takeWhile((event) -> "next".equals(event.event()))
.map((event) -> new ResponseMapGraphQlResponse(event.data()));
}
}

View File

@@ -54,14 +54,17 @@ import org.springframework.util.MimeType;
* for JSON and adapt it to {@link Encoder} and {@link Decoder}.
*
* @author Rossen Stoyanchev
* @since 1.3
*/
final class HttpMessageConverterDelegate {
private HttpMessageConverterDelegate() {
}
@SuppressWarnings("unchecked")
static HttpMessageConverter<Object> findJsonConverter(List<HttpMessageConverter<?>> converters) {
return (HttpMessageConverter<Object>) converters.stream()
.filter(converter -> converter.canRead(Map.class, MediaType.APPLICATION_JSON))
.filter((converter) -> converter.canRead(Map.class, MediaType.APPLICATION_JSON))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("No JSON HttpMessageConverter"));
}
@@ -79,14 +82,14 @@ final class HttpMessageConverterDelegate {
if (mimeType instanceof MediaType mediaType) {
return mediaType;
}
return (mimeType != null ? new MediaType(mimeType) : null);
return (mimeType != null) ? new MediaType(mimeType) : null;
}
/**
* Partial Encoder implementation to encode a single value through an HttpMessageConverter.
*/
private static class HttpMessageConverterEncoder implements Encoder<Object> {
private static final class HttpMessageConverterEncoder implements Encoder<Object> {
private final HttpMessageConverter<Object> converter;
@@ -140,7 +143,7 @@ final class HttpMessageConverterDelegate {
/**
* Partial Decoder implementation to decode a single buffer through an HttpMessageConverter.
*/
private static class HttpMessageConverterDecoder implements Decoder<Object> {
private static final class HttpMessageConverterDecoder implements Decoder<Object> {
private final HttpMessageConverter<Object> converter;
@@ -196,7 +199,7 @@ final class HttpMessageConverterDelegate {
}
private static class HttpInputMessageAdapter extends ByteArrayInputStream implements HttpInputMessage {
private static final class HttpInputMessageAdapter extends ByteArrayInputStream implements HttpInputMessage {
HttpInputMessageAdapter(DataBuffer buffer) {
super(toBytes(buffer));
@@ -222,7 +225,7 @@ final class HttpMessageConverterDelegate {
}
private static class HttpOutputMessageAdapter extends ByteArrayOutputStream implements HttpOutputMessage {
private static final class HttpOutputMessageAdapter extends ByteArrayOutputStream implements HttpOutputMessage {
private static final HttpHeaders noOpHeaders = new HttpHeaders();

View File

@@ -42,6 +42,7 @@ public interface HttpSyncGraphQlClient extends GraphQlClient {
/**
* Create an {@link HttpSyncGraphQlClient} that uses the given {@link RestClient}.
* @param client the {@code RestClient} to use for HTTP requests
*/
static HttpSyncGraphQlClient create(RestClient client) {
return builder(client.mutate()).build();
@@ -57,6 +58,7 @@ public interface HttpSyncGraphQlClient extends GraphQlClient {
/**
* Variant of {@link #builder()} with a pre-configured {@code RestClient}
* to mutate and customize further through the returned builder.
* @param client the {@code RestClient} to use for HTTP requests
*/
static Builder<?> builder(RestClient client) {
return builder(client.mutate());
@@ -65,6 +67,7 @@ public interface HttpSyncGraphQlClient extends GraphQlClient {
/**
* Variant of {@link #builder()} with a pre-configured {@code RestClient}
* to mutate and customize further through the returned builder.
* @param builder the {@code RestClient} builder to use for HTTP requests
*/
static Builder<?> builder(RestClient.Builder builder) {
return new DefaultSyncHttpGraphQlClientBuilder(builder);
@@ -73,6 +76,7 @@ public interface HttpSyncGraphQlClient extends GraphQlClient {
/**
* Builder for the GraphQL over HTTP client with a blocking execution chain.
* @param <B> the type of builder
*/
interface Builder<B extends Builder<B>> extends GraphQlClient.SyncBuilder<B> {
@@ -115,6 +119,7 @@ public interface HttpSyncGraphQlClient extends GraphQlClient {
* Customize the underlying {@code RestClient}.
* <p>Note that some properties of {@code RestClient.Builder} like the base URL,
* headers, and message converters can be customized through this builder.
* @param builderConsumer a consumer that customizes the {@code RestClient}.
* @see #url(String)
* @see #header(String, String...)
* @see #messageConverters(Consumer)

View File

@@ -32,11 +32,10 @@ import org.springframework.web.client.RestClient;
* Transport for GraphQL over HTTP requests executed with {@link RestClient}.
*
* @author Rossen Stoyanchev
* @since 1.3
*/
final class HttpSyncGraphQlTransport implements SyncGraphQlTransport {
private static final ParameterizedTypeReference<Map<String, Object>> MAP_TYPE = new ParameterizedTypeReference<>() {};
private static final ParameterizedTypeReference<Map<String, Object>> MAP_TYPE = new ParameterizedTypeReference<>() { };
private final RestClient restClient;
@@ -54,7 +53,7 @@ final class HttpSyncGraphQlTransport implements SyncGraphQlTransport {
HttpHeaders headers = new HttpHeaders();
webClient.mutate().defaultHeaders(headers::putAll);
MediaType contentType = headers.getContentType();
return (contentType != null ? contentType : MediaType.APPLICATION_JSON);
return (contentType != null) ? contentType : MediaType.APPLICATION_JSON;
}
@@ -68,7 +67,7 @@ final class HttpSyncGraphQlTransport implements SyncGraphQlTransport {
.retrieve()
.body(MAP_TYPE);
return new ResponseMapGraphQlResponse(body != null ? body : Collections.emptyMap());
return new ResponseMapGraphQlResponse((body != null) ? body : Collections.emptyMap());
}
}

View File

@@ -70,6 +70,7 @@ public interface RSocketGraphQlClient extends GraphQlClient {
/**
* Start with a given {@link #builder()}.
* @param requesterBuilder the existing request builder
*/
static Builder<?> builder(RSocketRequester.Builder requesterBuilder) {
return new DefaultRSocketGraphQlClientBuilder(requesterBuilder);
@@ -78,6 +79,7 @@ public interface RSocketGraphQlClient extends GraphQlClient {
/**
* Builder for the GraphQL over HTTP client.
* @param <B> the builder type
*/
interface Builder<B extends Builder<B>> extends GraphQlClient.Builder<B> {
@@ -146,11 +148,12 @@ public interface RSocketGraphQlClient extends GraphQlClient {
* <p>Note that some properties of {@code RSocketRequester.Builder} like the
* data MimeType, and the underlying RSocket transport can be customized
* through this builder.
* @param requester the requester to be customized
* @return the same builder instance
* @see #dataMimeType(MimeType)
* @see #tcp(String, int)
* @see #webSocket(URI)
* @see #clientTransport(ClientTransport)
* @return the same builder instance
*/
B rsocketRequester(Consumer<RSocketRequester.Builder> requester);

View File

@@ -45,12 +45,11 @@ import org.springframework.util.Assert;
* metadata extension.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
final class RSocketGraphQlTransport implements GraphQlTransport {
private static final ParameterizedTypeReference<Map<String, Object>> MAP_TYPE =
new ParameterizedTypeReference<Map<String, Object>>() {};
new ParameterizedTypeReference<Map<String, Object>>() { };
private static final ResolvableType LIST_TYPE = ResolvableType.forClass(List.class);
@@ -83,7 +82,7 @@ final class RSocketGraphQlTransport implements GraphQlTransport {
public Flux<GraphQlResponse> executeSubscription(GraphQlRequest request) {
return this.rsocketRequester.route(this.route).data(request.toMap())
.retrieveFlux(MAP_TYPE)
.onErrorResume(RejectedException.class, ex -> Flux.error(decodeErrors(request, ex)))
.onErrorResume(RejectedException.class, (ex) -> Flux.error(decodeErrors(request, ex)))
.map(ResponseMapGraphQlResponse::new);
}

View File

@@ -36,7 +36,6 @@ import org.springframework.util.ObjectUtils;
* {@link GraphQlResponse} that wraps a deserialized the GraphQL response map.
*
* @author Rossen Stoyanchev
* @since 1.0.0
*/
class ResponseMapGraphQlResponse extends AbstractGraphQlResponse {
@@ -60,7 +59,7 @@ class ResponseMapGraphQlResponse extends AbstractGraphQlResponse {
@SuppressWarnings("unchecked")
private static List<ResponseError> wrapErrors(Map<String, Object> map) {
List<Map<String, Object>> errors = (List<Map<String, Object>>) map.get("errors");
errors = (errors != null ? errors : Collections.emptyList());
errors = (errors != null) ? errors : Collections.emptyList();
return errors.stream().map(MapResponseError::new).collect(Collectors.toList());
}
@@ -134,7 +133,7 @@ class ResponseMapGraphQlResponse extends AbstractGraphQlResponse {
return Collections.emptyList();
}
return locations.stream()
.map(m -> new SourceLocation(getInt(m, "line"), getInt(m, "column"), (String) m.get("sourceName")))
.map((m) -> new SourceLocation(getInt(m, "line"), getInt(m, "column"), (String) m.get("sourceName")))
.collect(Collectors.toList());
}
@@ -155,7 +154,7 @@ class ResponseMapGraphQlResponse extends AbstractGraphQlResponse {
return "";
}
return path.stream().reduce("",
(s, o) -> s + (o instanceof Integer ? "[" + o + "]" : (s.isEmpty() ? o : "." + o)),
(s, o) -> s + ((o instanceof Integer) ? "[" + o + "]" : (s.isEmpty() ? o : "." + o)),
(s, s2) -> null);
}
@@ -163,7 +162,7 @@ class ResponseMapGraphQlResponse extends AbstractGraphQlResponse {
@Override
@Nullable
public String getMessage() {
return (String) errorMap.get("message");
return (String) this.errorMap.get("message");
}
@Override

View File

@@ -38,6 +38,8 @@ public class SubscriptionErrorException extends GraphQlTransportException {
/**
* Constructor with the request details and the errors listed in the payload
* of the {@code "errors"} message.
* @param request the request details
* @param errors the errors listed in the payload
*/
public SubscriptionErrorException(GraphQlRequest request, List<ResponseError> errors) {
super("GraphQL subscription completed with an \"error\" message, " +

View File

@@ -50,7 +50,7 @@ public interface SyncGraphQlClientInterceptor {
@Override
public ClientGraphQlResponse intercept(ClientGraphQlRequest request, Chain chain) {
return SyncGraphQlClientInterceptor.this.intercept(
request, nextRequest -> interceptor.intercept(nextRequest, chain));
request, (nextRequest) -> interceptor.intercept(nextRequest, chain));
}
};
}

View File

@@ -39,6 +39,7 @@ public interface WebGraphQlClient extends GraphQlClient {
/**
* Base builder for GraphQL clients over a Web transport.
* @param <B> the type of builder
*/
interface Builder<B extends Builder<B>> extends GraphQlClient.Builder<B> {
@@ -72,6 +73,7 @@ public interface WebGraphQlClient extends GraphQlClient {
* Configure JSON encoders and decoders for use in the
* {@link org.springframework.graphql.GraphQlResponse} to convert response
* data to higher level objects.
* @param codecsConsumer a callback that customizes the configured codecs
*/
B codecConfigurer(Consumer<CodecConfigurer> codecsConsumer);

View File

@@ -35,6 +35,9 @@ public class WebSocketDisconnectedException extends GraphQlTransportException {
/**
* Constructor with an explanation about the closure, along with the request
* details and the status used to close the WebSocket session.
* @param closeStatusMessage the message received when the connection was closed
* @param request the ongoing request when the connection was closed
* @param status the received close status
*/
public WebSocketDisconnectedException(String closeStatusMessage, GraphQlRequest request, CloseStatus status) {
super(closeStatusMessage, null, request);

View File

@@ -85,6 +85,7 @@ public interface WebSocketGraphQlClient extends WebGraphQlClient {
/**
* Builder for a GraphQL over WebSocket client.
* @param <B> the builder type
*/
interface Builder<B extends Builder<B>> extends WebGraphQlClient.Builder<B> {

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.client;
import java.net.URI;
@@ -50,7 +51,6 @@ import org.springframework.web.reactive.socket.client.WebSocketClient;
* {@link GraphQlTransport} for GraphQL over WebSocket via {@link WebSocketClient}.
*
* @author Rossen Stoyanchev
* @since 1.0.0
* @see <a href="https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md">GraphQL over WebSocket protocol</a>
*/
final class WebSocketGraphQlTransport implements GraphQlTransport {
@@ -78,7 +78,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
Assert.notNull(interceptor, "WebSocketGraphQlClientInterceptor is required");
this.url = url;
this.headers.putAll(headers != null ? headers : HttpHeaders.EMPTY);
this.headers.putAll((headers != null) ? headers : HttpHeaders.EMPTY);
this.webSocketClient = client;
this.graphQlSessionHandler = new GraphQlSessionHandler(codecConfigurer, interceptor);
@@ -100,7 +100,9 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
Mono<GraphQlSession> sessionMono = handler.getGraphQlSession();
client.execute(uri, headers, handler)
.subscribe(aVoid -> {},
.subscribe((aVoid) -> {
},
handler::handleWebSocketSessionError,
handler::handleWebSocketSessionClosed);
@@ -109,19 +111,19 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
}
public URI getUrl() {
URI getUrl() {
return this.url;
}
public HttpHeaders getHeaders() {
HttpHeaders getHeaders() {
return this.headers;
}
public WebSocketClient getWebSocketClient() {
WebSocketClient getWebSocketClient() {
return this.webSocketClient;
}
public CodecConfigurer getCodecConfigurer() {
CodecConfigurer getCodecConfigurer() {
return this.graphQlSessionHandler.getCodecConfigurer();
}
@@ -132,7 +134,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
* @return {@code Mono} that completes when the WebSocket is connected and
* ready to begin sending GraphQL requests
*/
public Mono<Void> start() {
Mono<Void> start() {
this.graphQlSessionHandler.setStopped(false);
return this.graphQlSessionMono.then();
}
@@ -145,19 +147,19 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
* call {@link #start()} to allow requests again.
* @return {@code Mono} that completes when the underlying session is closed
*/
public Mono<Void> stop() {
Mono<Void> stop() {
this.graphQlSessionHandler.setStopped(true);
return this.graphQlSessionMono.flatMap(GraphQlSession::close).onErrorResume(ex -> Mono.empty());
return this.graphQlSessionMono.flatMap(GraphQlSession::close).onErrorResume((ex) -> Mono.empty());
}
@Override
public Mono<GraphQlResponse> execute(GraphQlRequest request) {
return this.graphQlSessionMono.flatMap(session -> session.execute(request));
return this.graphQlSessionMono.flatMap((session) -> session.execute(request));
}
@Override
public Flux<GraphQlResponse> executeSubscription(GraphQlRequest request) {
return this.graphQlSessionMono.flatMapMany(session -> session.executeSubscription(request));
return this.graphQlSessionMono.flatMapMany((session) -> session.executeSubscription(request));
}
@@ -189,7 +191,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
}
public CodecConfigurer getCodecConfigurer() {
CodecConfigurer getCodecConfigurer() {
return this.codecDelegate.getCodecConfigurer();
}
@@ -205,7 +207,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
* the "connection_init" and "connection_ack" messages are exchanged or
* returns an error if it fails for any reason.
*/
public Mono<GraphQlSession> getGraphQlSession() {
Mono<GraphQlSession> getGraphQlSession() {
return this.graphQlSessionSink.asMono();
}
@@ -213,14 +215,14 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
* When the handler is marked "stopped", i.e. set to {@code true}, new
* requests are rejected. When set to {@code true} they are allowed.
*/
public void setStopped(boolean stopped) {
void setStopped(boolean stopped) {
this.stopped.set(stopped);
}
/**
* Whether the handler is marked {@link #setStopped(boolean) "stopped"}.
*/
public boolean isStopped() {
boolean isStopped() {
return this.stopped.get();
}
@@ -241,10 +243,10 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
Mono<Void> sendCompletion =
session.send(connectionInitMono.concatWith(graphQlSession.getRequestFlux())
.map(message -> this.codecDelegate.encode(session, message)));
.map((message) -> this.codecDelegate.encode(session, message)));
Mono<Void> receiveCompletion = session.receive()
.flatMap(webSocketMessage -> {
.flatMap((webSocketMessage) -> {
if (sessionNotInitialized()) {
try {
GraphQlWebSocketMessage message = this.codecDelegate.decode(webSocketMessage);
@@ -301,14 +303,14 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
private void registerCloseStatusHandling(GraphQlSession graphQlSession, WebSocketSession session) {
session.closeStatus()
.defaultIfEmpty(CloseStatus.NO_STATUS_CODE)
.doOnNext(closeStatus -> {
.doOnNext((closeStatus) -> {
String closeStatusMessage = initCloseStatusMessage(closeStatus, null, graphQlSession);
if (logger.isDebugEnabled()) {
logger.debug(closeStatusMessage);
}
graphQlSession.terminateRequests(closeStatusMessage, closeStatus);
})
.doOnError(cause -> {
.doOnError((cause) -> {
CloseStatus closeStatus = CloseStatus.NO_STATUS_CODE;
String closeStatusMessage = initCloseStatusMessage(closeStatus, cause, graphQlSession);
if (logger.isErrorEnabled()) {
@@ -347,7 +349,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
* with an error. The error is routed to subscribers of
* {@link #getGraphQlSession()} which is necessary for connection issues.
*/
public void handleWebSocketSessionError(Throwable ex) {
void handleWebSocketSessionError(Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Session handling error: " + ex.getMessage(), ex);
@@ -364,7 +366,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
* This must be called from code that calls the {@code WebSocketClient}
* when execution completes.
*/
public void handleWebSocketSessionClosed() {
void handleWebSocketSessionClosed() {
this.graphQlSessionSink = Sinks.unsafe().one();
}
@@ -396,16 +398,16 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
/**
* Return the {@code Flux} of GraphQL requests to send as WebSocket messages.
*/
public Flux<GraphQlWebSocketMessage> getRequestFlux() {
Flux<GraphQlWebSocketMessage> getRequestFlux() {
return this.requestSink.getRequestFlux();
}
// Outbound messages
public Mono<GraphQlResponse> execute(GraphQlRequest request) {
Mono<GraphQlResponse> execute(GraphQlRequest request) {
String id = String.valueOf(this.requestIndex.incrementAndGet());
return Mono.<GraphQlResponse>create(sink -> {
return Mono.<GraphQlResponse>create((sink) -> {
SingleResponseRequestState state = new SingleResponseRequestState(request, sink);
this.requestStateMap.put(id, state);
try {
@@ -419,9 +421,9 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
}).doOnCancel(() -> this.requestStateMap.remove(id));
}
public Flux<GraphQlResponse> executeSubscription(GraphQlRequest request) {
Flux<GraphQlResponse> executeSubscription(GraphQlRequest request) {
String id = String.valueOf(this.requestIndex.incrementAndGet());
return Flux.<GraphQlResponse>create(sink -> {
return Flux.<GraphQlResponse>create((sink) -> {
SubscriptionRequestState state = new SubscriptionRequestState(request, sink);
this.requestStateMap.put(id, state);
try {
@@ -452,7 +454,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
}
}
public void sendPong(@Nullable Map<String, Object> payload) {
void sendPong(@Nullable Map<String, Object> payload) {
GraphQlWebSocketMessage message = GraphQlWebSocketMessage.pong(payload);
this.requestSink.sendRequest(message);
}
@@ -463,7 +465,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
/**
* Handle a "next" message and route to its recipient.
*/
public void handleNext(GraphQlWebSocketMessage message) {
void handleNext(GraphQlWebSocketMessage message) {
String id = message.getId();
RequestState requestState = this.requestStateMap.get(id);
if (requestState == null) {
@@ -486,7 +488,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
* Handle an "error" message, turning it into an {@link GraphQlResponse}
* for single responses, or signaling an error for streams.
*/
public void handleError(GraphQlWebSocketMessage message) {
void handleError(GraphQlWebSocketMessage message) {
String id = message.getId();
RequestState requestState = this.requestStateMap.remove(id);
if (requestState == null) {
@@ -512,7 +514,7 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
/**
* Handle a "complete" message.
*/
public void handleComplete(GraphQlWebSocketMessage message) {
void handleComplete(GraphQlWebSocketMessage message) {
String id = message.getId();
RequestState requestState = this.requestStateMap.remove(id);
if (requestState == null) {
@@ -528,22 +530,22 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
* Return a {@code Mono} that completes when the connection is closed
* for any reason.
*/
public Mono<Void> notifyWhenClosed() {
Mono<Void> notifyWhenClosed() {
return this.connection.notifyWhenClosed();
}
/**
* Close the underlying connection.
*/
public Mono<Void> close() {
Mono<Void> close() {
return this.connection.close(CloseStatus.GOING_AWAY);
}
/**
* Terminate and clean all in-progress requests with the given error.
*/
public void terminateRequests(String message, CloseStatus status) {
this.requestStateMap.values().forEach(info -> info.emitDisconnectError(message, status));
void terminateRequests(String message, CloseStatus status) {
this.requestStateMap.values().forEach((info) -> info.emitDisconnectError(message, status));
this.requestStateMap.clear();
}
@@ -595,21 +597,21 @@ final class WebSocketGraphQlTransport implements GraphQlTransport {
/**
* Holds the request {@code Flux} and associated {@link FluxSink}.
*/
private static class RequestSink {
private static final class RequestSink {
@Nullable
private FluxSink<GraphQlWebSocketMessage> requestSink;
private final Flux<GraphQlWebSocketMessage> requestFlux = Flux.create(sink -> {
private final Flux<GraphQlWebSocketMessage> requestFlux = Flux.create((sink) -> {
Assert.state(this.requestSink == null, "Expected single subscriber only for outbound messages");
this.requestSink = sink;
});
public Flux<GraphQlWebSocketMessage> getRequestFlux() {
Flux<GraphQlWebSocketMessage> getRequestFlux() {
return this.requestFlux;
}
public void sendRequest(GraphQlWebSocketMessage message) {
void sendRequest(GraphQlWebSocketMessage message) {
Assert.state(this.requestSink != null, "Unexpected request before Flux is subscribed to");
this.requestSink.next(message);
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.data;
@@ -39,8 +40,8 @@ import org.springframework.util.ObjectUtils;
* object.
* </ul>
*
* @author Rossen Stoyanchev
* @param <T> the type of value contained
* @author Rossen Stoyanchev
* @since 1.1.0
* @see <a href="http://spec.graphql.org/October2021/#sec-Non-Null.Nullable-vs-Optional">Nullable vs Optional</a>
*/
@@ -115,6 +116,7 @@ public final class ArgumentValue<T> {
/**
* Static factory method for an argument value that was provided, even if
* it was set to {@literal "null}.
* @param <T> the type of value
* @param value the value to hold in the instance
*/
public static <T> ArgumentValue<T> ofNullable(@Nullable T value) {
@@ -123,6 +125,7 @@ public final class ArgumentValue<T> {
/**
* Static factory method for an argument value that was omitted.
* @param <T> the type of value
*/
@SuppressWarnings("unchecked")
public static <T> ArgumentValue<T> omitted() {

View File

@@ -139,7 +139,7 @@ public class GraphQlArgumentBinder {
DataFetchingEnvironment environment, @Nullable String name, ResolvableType targetType)
throws BindException {
Object rawValue = (name != null ? environment.getArgument(name) : environment.getArguments());
Object rawValue = (name != null) ? environment.getArgument(name) : environment.getArguments();
boolean isOmitted = (name != null && !environment.getArguments().containsKey(name));
return bind(name, rawValue, isOmitted, targetType);
@@ -148,6 +148,11 @@ public class GraphQlArgumentBinder {
/**
* Variant of {@link #bind(DataFetchingEnvironment, String, ResolvableType)}
* with a pre-extracted raw value to bind from.
* @param name the name of an argument, or {@code null} to use the full map
* @param rawValue the raw argument value (Collection, Map, or scalar)
* @param isOmitted {@code true} if the argument was omitted from the input
* and {@code false} if it was provided, but possibly {@code null}
* @param targetType the type of Object to create
* @since 1.3
*/
@Nullable
@@ -259,9 +264,9 @@ public class GraphQlArgumentBinder {
Constructor<?> constructor = BeanUtils.getResolvableConstructor(targetClass);
Object value = (constructor.getParameterCount() > 0 ?
Object value = (constructor.getParameterCount() > 0) ?
bindMapToObjectViaConstructor(rawMap, constructor, targetType, bindingResult) :
bindMapToObjectViaSetters(rawMap, constructor, targetType, bindingResult));
bindMapToObjectViaSetters(rawMap, constructor, targetType, bindingResult);
bindingResult.popNestedPath();
@@ -373,7 +378,7 @@ public class GraphQlArgumentBinder {
Object value = null;
try {
TypeConverter converter =
(this.typeConverter != null ? this.typeConverter : new SimpleTypeConverter());
(this.typeConverter != null) ? this.typeConverter : new SimpleTypeConverter();
value = converter.convertIfNecessary(
rawValue, (Class<?>) clazz, new TypeDescriptor(type, null, null));
@@ -398,9 +403,9 @@ public class GraphQlArgumentBinder {
}
private static String initObjectName(ResolvableType targetType) {
return (targetType.getSource() instanceof MethodParameter methodParameter ?
return (targetType.getSource() instanceof MethodParameter methodParameter) ?
Conventions.getVariableNameForParameter(methodParameter) :
ClassUtils.getShortNameAsProperty(targetType.resolve(Object.class)));
ClassUtils.getShortNameAsProperty(targetType.resolve(Object.class));
}
@Override
@@ -413,7 +418,7 @@ public class GraphQlArgumentBinder {
return null;
}
public void rejectArgumentValue(
void rejectArgumentValue(
String field, @Nullable Object rawValue, String code, String defaultMessage) {
addError(new FieldError(

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.data;
import java.lang.annotation.Documented;

View File

@@ -44,7 +44,6 @@ import org.springframework.lang.Nullable;
* {@link EntityHandlerMethod}s.
*
* @author Rossen Stoyanchev
* @since 1.3
* @see com.apollographql.federation.graphqljava.SchemaTransformer#fetchEntities(DataFetcher)
*/
final class EntitiesDataFetcher implements DataFetcher<Mono<DataFetcherResult<List<Object>>>> {
@@ -54,7 +53,7 @@ final class EntitiesDataFetcher implements DataFetcher<Mono<DataFetcherResult<Li
private final HandlerDataFetcherExceptionResolver exceptionResolver;
public EntitiesDataFetcher(
EntitiesDataFetcher(
Map<String, EntityHandlerMethod> handlerMethods, HandlerDataFetcherExceptionResolver resolver) {
this.handlerMethods = new LinkedHashMap<>(handlerMethods);
@@ -90,15 +89,15 @@ final class EntitiesDataFetcher implements DataFetcher<Mono<DataFetcherResult<Li
return handlerMethod.getEntity(env, map, index)
.switchIfEmpty(Mono.error(new RepresentationNotResolvedException(map, handlerMethod)))
.onErrorResume(ex -> resolveException(ex, env, handlerMethod, index));
.onErrorResume((ex) -> resolveException(ex, env, handlerMethod, index));
}
private Mono<Object> resolveException(
Throwable ex, DataFetchingEnvironment env, @Nullable EntityHandlerMethod handlerMethod, int index) {
Throwable theEx = (ex instanceof CompletionException ? ex.getCause() : ex);
Throwable theEx = (ex instanceof CompletionException) ? ex.getCause() : ex;
DataFetchingEnvironment theEnv = new EntityDataFetchingEnvironment(env, index);
Object handler = (handlerMethod != null ? handlerMethod.getBean() : null);
Object handler = (handlerMethod != null) ? handlerMethod.getBean() : null;
return this.exceptionResolver.resolveException(theEx, theEnv, handler)
.map(ErrorContainer::new)
@@ -108,8 +107,8 @@ final class EntitiesDataFetcher implements DataFetcher<Mono<DataFetcherResult<Li
private ErrorContainer createDefaultError(Throwable ex, DataFetchingEnvironment env) {
ErrorType errorType = (ex instanceof RepresentationException representationEx ?
representationEx.getErrorType() : ErrorType.INTERNAL_ERROR);
ErrorType errorType = (ex instanceof RepresentationException representationEx) ?
representationEx.getErrorType() : ErrorType.INTERNAL_ERROR;
return new ErrorContainer(GraphqlErrorBuilder.newError(env)
.errorType(errorType)
@@ -134,7 +133,7 @@ final class EntitiesDataFetcher implements DataFetcher<Mono<DataFetcherResult<Li
private final ExecutionStepInfo executionStepInfo;
public EntityDataFetchingEnvironment(DataFetchingEnvironment env, int index) {
EntityDataFetchingEnvironment(DataFetchingEnvironment env, int index) {
super(env);
this.executionStepInfo = ExecutionStepInfo.newExecutionStepInfo(env.getExecutionStepInfo())
.path(env.getExecutionStepInfo().getPath().segment(index))

View File

@@ -34,7 +34,6 @@ import org.springframework.validation.BindException;
* the entity uniquely.
*
* @author Rossen Stoyanchev
* @since 1.3
*/
final class EntityArgumentMethodArgumentResolver extends ArgumentMethodArgumentResolver {
@@ -61,7 +60,7 @@ final class EntityArgumentMethodArgumentResolver extends ArgumentMethodArgumentR
/**
* Wrap the environment in order to also expose the entity representation map.
*/
public static DataFetchingEnvironment wrap(DataFetchingEnvironment env, Map<String, Object> representation) {
static DataFetchingEnvironment wrap(DataFetchingEnvironment env, Map<String, Object> representation) {
return new EntityDataFetchingEnvironment(env, representation);
}
@@ -75,7 +74,7 @@ final class EntityArgumentMethodArgumentResolver extends ArgumentMethodArgumentR
this.representation = representation;
}
public Map<String, Object> getRepresentation() {
Map<String, Object> getRepresentation() {
return this.representation;
}
}

View File

@@ -32,11 +32,10 @@ import org.springframework.lang.Nullable;
* Invokable controller method to fetch a federated entity.
*
* @author Rossen Stoyanchev
* @since 1.3
*/
final class EntityHandlerMethod extends DataFetcherHandlerMethodSupport {
public EntityHandlerMethod(
EntityHandlerMethod(
HandlerMethod handlerMethod, HandlerMethodArgumentResolverComposite resolvers,
@Nullable Executor executor) {
@@ -44,7 +43,7 @@ final class EntityHandlerMethod extends DataFetcherHandlerMethodSupport {
}
public Mono<Object> getEntity(
Mono<Object> getEntity(
DataFetchingEnvironment environment, Map<String, Object> representation, int index) {
Object[] args;

View File

@@ -75,6 +75,7 @@ public final class FederationSchemaFactory
/**
* Configure a resolver that helps to map Java to entity schema type names.
* <p>By default this is {@link ClassNameTypeResolver}.
* @param typeResolver the custom type resolver to use
* @see SchemaTransformer#resolveEntityType(TypeResolver)
*/
public void setTypeResolver(@Nullable TypeResolver typeResolver) {
@@ -86,7 +87,7 @@ public final class FederationSchemaFactory
public void afterPropertiesSet() {
super.afterPropertiesSet();
detectHandlerMethods().forEach(info ->
detectHandlerMethods().forEach((info) ->
this.handlerMethods.put(info.typeName(),
new EntityHandlerMethod(info.handlerMethod(), getArgumentResolvers(), getExecutor())));
@@ -149,6 +150,8 @@ public final class FederationSchemaFactory
* Create {@link GraphQLSchema} via {@link SchemaTransformer}, setting up
* the "_entities" {@link DataFetcher} and {@link TypeResolver} for federated types.
* <p>Use this to supply a {@link SchemaResourceBuilder#schemaFactory(BiFunction) schemaFactory}.
* @param registry the existing type definition registry
* @param wiring the existing runtime wiring
*/
public GraphQLSchema createGraphQLSchema(TypeDefinitionRegistry registry, RuntimeWiring wiring) {
return createSchemaTransformer(registry, wiring).build();
@@ -157,6 +160,8 @@ public final class FederationSchemaFactory
/**
* Alternative to {@link #createGraphQLSchema(TypeDefinitionRegistry, RuntimeWiring)}
* that allows calling additional methods on {@link SchemaTransformer}.
* @param registry the existing type definition registry
* @param wiring the existing runtime wiring
*/
public SchemaTransformer createSchemaTransformer(TypeDefinitionRegistry registry, RuntimeWiring wiring) {
Assert.state(this.typeResolver != null, "afterPropertiesSet not called");

View File

@@ -55,7 +55,7 @@ public class RepresentationException extends RuntimeException {
super(msg);
this.representation = representation;
this.handlerMethod = hm;
this.errorType = (representation.get("__typename") == null ? ErrorType.BAD_REQUEST : ErrorType.INTERNAL_ERROR);
this.errorType = (representation.get("__typename") == null) ? ErrorType.BAD_REQUEST : ErrorType.INTERNAL_ERROR;
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.data.method;
import java.lang.annotation.Annotation;
@@ -77,6 +78,8 @@ public class HandlerMethod {
/**
* Constructor with a handler instance and a method.
* @param bean the handler instance
* @param method the handler method
*/
public HandlerMethod(Object bean, Method method) {
Assert.notNull(bean, "Bean is required");
@@ -94,6 +97,9 @@ public class HandlerMethod {
* Constructor with a bean name for the handler along with a {@code BeanFactory}
* to allow {@link #createWithResolvedBean() resolving} the handler instance
* later.
* @param beanName the bean name
* @param beanFactory the bean factory to use for bean resolution
* @param method the handler method
*/
public HandlerMethod(String beanName, BeanFactory beanFactory, Method method) {
Assert.hasText(beanName, "Bean name is required");
@@ -115,6 +121,7 @@ public class HandlerMethod {
/**
* Copy constructor for use from subclasses that accept more arguments.
* @param handlerMethod the handler method
*/
protected HandlerMethod(HandlerMethod handlerMethod) {
this(handlerMethod, handlerMethod.bean);
@@ -191,6 +198,7 @@ public class HandlerMethod {
/**
* Return the actual return value type.
* @param returnValue the return value instance, can be {@code null}
*/
public MethodParameter getReturnValueType(@Nullable Object returnValue) {
return new ReturnValueMethodParameter(returnValue);
@@ -208,6 +216,7 @@ public class HandlerMethod {
* if no annotation can be found on the given method itself.
* <p>Also supports <em>merged</em> composed annotations with attribute
* overrides as of Spring Framework 4.3.
* @param <A> the annotation type
* @param annotationType the type of annotation to introspect the method for
* @return the annotation, or {@code null} if none found
* @see AnnotatedElementUtils#findMergedAnnotation
@@ -219,6 +228,7 @@ public class HandlerMethod {
/**
* Return whether the parameter is declared with the given annotation type.
* @param <A> the annotation type
* @param annotationType the annotation type to look for
* @see AnnotatedElementUtils#hasAnnotation
*/
@@ -331,6 +341,9 @@ public class HandlerMethod {
* processing time may be a JDK dynamic proxy (lazy initialization, prototype
* beans, and others). Endpoint classes that require proxying should prefer
* class-based proxy mechanisms.
* @param method the handler method
* @param targetBean the bean instance
* @param args the method arguments
*/
protected void assertTargetBean(Method method, Object targetBean, Object[] args) {
Class<?> methodDeclaringClass = method.getDeclaringClass();
@@ -347,9 +360,9 @@ public class HandlerMethod {
protected String formatInvokeError(String text, Object[] args) {
String formattedArgs = IntStream.range(0, args.length)
.mapToObj(i -> (args[i] != null ?
.mapToObj((i) -> (args[i] != null) ?
"[" + i + "] [type=" + args[i].getClass().getName() + "] [value=" + args[i] + "]" :
"[" + i + "] [null]"))
"[" + i + "] [null]")
.collect(Collectors.joining(",\n", " ", " "));
return text + "\n" +
@@ -401,21 +414,7 @@ public class HandlerMethod {
if (index < ifcAnns.length) {
Annotation[] paramAnns = ifcAnns[index];
if (paramAnns.length > 0) {
List<Annotation> merged = new ArrayList<>(anns.length + paramAnns.length);
merged.addAll(Arrays.asList(anns));
for (Annotation paramAnn : paramAnns) {
boolean existingType = false;
for (Annotation ann : anns) {
if (ann.annotationType() == paramAnn.annotationType()) {
existingType = true;
break;
}
}
if (!existingType) {
merged.add(adaptAnnotation(paramAnn));
}
}
anns = merged.toArray(new Annotation[0]);
anns = mergeAnnotations(anns, paramAnns);
}
}
}
@@ -424,6 +423,25 @@ public class HandlerMethod {
}
return anns;
}
private Annotation[] mergeAnnotations(Annotation[] anns, Annotation[] paramAnns) {
List<Annotation> merged = new ArrayList<>(anns.length + paramAnns.length);
merged.addAll(Arrays.asList(anns));
for (Annotation paramAnn : paramAnns) {
boolean existingType = false;
for (Annotation ann : anns) {
if (ann.annotationType() == paramAnn.annotationType()) {
existingType = true;
break;
}
}
if (!existingType) {
merged.add(adaptAnnotation(paramAnn));
}
}
anns = merged.toArray(new Annotation[0]);
return anns;
}
}
@@ -435,7 +453,7 @@ public class HandlerMethod {
@Nullable
private final Object returnValue;
public ReturnValueMethodParameter(@Nullable Object returnValue) {
ReturnValueMethodParameter(@Nullable Object returnValue) {
super(-1);
this.returnValue = returnValue;
}
@@ -447,7 +465,7 @@ public class HandlerMethod {
@Override
public Class<?> getParameterType() {
return (this.returnValue != null ? this.returnValue.getClass() : super.getParameterType());
return (this.returnValue != null) ? this.returnValue.getClass() : super.getParameterType();
}
@Override

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.data.method;
import graphql.schema.DataFetchingEnvironment;
@@ -35,20 +36,18 @@ public interface HandlerMethodArgumentResolver {
/**
* Whether this resolver supports the given {@link MethodParameter}.
* @param parameter the method parameter to check for support
*/
boolean supportsParameter(MethodParameter parameter);
/**
* Resolve a method parameter to a value.
*
* @param parameter the method parameter to resolve. This parameter must
* have previously checked via {@link #supportsParameter}.
* @param environment the environment to use to resolve the value
*
* @return the resolved value, which may be {@code null} if not resolved;
* the value may also be a {@link reactor.core.publisher.Mono} if it
* requires asynchronous resolution.
*
* @throws Exception in case of errors with the preparation of argument values
*/
@Nullable

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.data.method;
import java.util.ArrayList;
@@ -43,6 +44,7 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu
/**
* Add the given {@link HandlerMethodArgumentResolver}.
* @param resolver the argument resolver
*/
public void addResolver(HandlerMethodArgumentResolver resolver) {
this.argumentResolvers.add(resolver);
@@ -84,10 +86,11 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu
/**
* Find a registered {@link HandlerMethodArgumentResolver} that supports
* the given method parameter.
* @param parameter the method parameter
*/
@Nullable
public HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
return this.argumentResolverCache.computeIfAbsent(parameter, p -> {
return this.argumentResolverCache.computeIfAbsent(parameter, (p) -> {
for (HandlerMethodArgumentResolver resolver : this.argumentResolvers) {
if (resolver.supportsParameter(parameter)) {
return resolver;
@@ -97,4 +100,4 @@ public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgu
});
}
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.data.method;
import java.lang.reflect.InvocationTargetException;
@@ -25,7 +26,6 @@ import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import graphql.GraphQLContext;
import io.micrometer.context.ContextSnapshot;
import io.micrometer.context.ContextSnapshotFactory;
import reactor.core.publisher.Mono;
@@ -72,6 +72,7 @@ public abstract class InvocableHandlerMethodSupport extends HandlerMethod {
/**
* Invoke the handler method with the given argument values.
* @param graphQLContext the GraphQL context for this data fetching operation
* @param argValues the values to use to invoke the method
* @return the value returned from the method or a {@code Mono<Throwable>}
* if the invocation fails.
@@ -92,7 +93,7 @@ public abstract class InvocableHandlerMethodSupport extends HandlerMethod {
}
catch (IllegalArgumentException ex) {
assertTargetBean(method, getBean(), argValues);
String text = (ex.getMessage() != null ? ex.getMessage() : "Illegal argument");
String text = (ex.getMessage() != null) ? ex.getMessage() : "Illegal argument";
return Mono.error(new IllegalStateException(formatInvokeError(text, argValues), ex));
}
catch (InvocationTargetException ex) {
@@ -145,15 +146,16 @@ public abstract class InvocableHandlerMethodSupport extends HandlerMethod {
/**
* Use this method to resolve the arguments asynchronously. This is only
* useful when at least one of the values is a {@link Mono}
* @param args the arguments to be resolved asynchronously
*/
@SuppressWarnings("unchecked")
protected Mono<Object[]> toArgsMono(Object[] args) {
List<Mono<Object>> monoList = new ArrayList<>();
for (Object arg : args) {
Mono<Object> argMono = (arg instanceof Mono ? (Mono<Object>) arg : Mono.justOrEmpty(arg));
Mono<Object> argMono = ((arg instanceof Mono) ? (Mono<Object>) arg : Mono.justOrEmpty(arg));
monoList.add(argMono.defaultIfEmpty(NO_VALUE));
}
return Mono.zip(monoList, values -> {
return Mono.zip(monoList, (values) -> {
for (int i = 0; i < values.length; i++) {
if (values[i] == NO_VALUE) {
values[i] = null;

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.data.method.annotation;
import java.lang.annotation.Documented;

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.data.method.annotation;
import java.lang.annotation.Documented;

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.data.method.annotation;
import java.lang.annotation.Documented;

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.data.method.annotation;
import java.lang.annotation.Documented;

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.data.method.annotation;
import java.lang.annotation.Documented;

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.data.method.annotation;
import java.lang.annotation.Documented;

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.data.method.annotation;
import java.lang.annotation.Documented;

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.data.method.annotation;
import java.lang.annotation.Documented;

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.data.method.annotation;
import java.lang.annotation.Documented;

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.data.method.annotation;
import java.lang.annotation.Documented;

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.data.method.annotation.support;
import java.lang.annotation.Annotation;
@@ -105,13 +106,13 @@ public class AnnotatedControllerConfigurer
private static final ClassLoader classLoader = AnnotatedControllerConfigurer.class.getClassLoader();
private final static boolean springDataPresent = ClassUtils.isPresent(
private static final boolean springDataPresent = ClassUtils.isPresent(
"org.springframework.data.projection.SpelAwareProxyProjectionFactory", classLoader);
private final static boolean springSecurityPresent = ClassUtils.isPresent(
private static final boolean springSecurityPresent = ClassUtils.isPresent(
"org.springframework.security.core.context.SecurityContext", classLoader);
private final static boolean beanValidationPresent = ClassUtils.isPresent(
private static final boolean beanValidationPresent = ClassUtils.isPresent(
"jakarta.validation.executable.ExecutableValidator", classLoader);
@@ -125,7 +126,6 @@ public class AnnotatedControllerConfigurer
* Add a {@link HandlerMethodArgumentResolver} for custom controller method
* arguments. Such custom resolvers are ordered after built-in resolvers
* except for {@link SourceMethodArgumentResolver}, which is always last.
*
* @param resolver the resolver to add.
* @since 1.2.0
*/
@@ -228,7 +228,7 @@ public class AnnotatedControllerConfigurer
@Override
public void configure(RuntimeWiring.Builder runtimeWiringBuilder) {
detectHandlerMethods().forEach(info -> {
detectHandlerMethods().forEach((info) -> {
DataFetcher<?> dataFetcher;
if (!info.isBatchMapping()) {
dataFetcher = new SchemaMappingDataFetcher(
@@ -238,7 +238,7 @@ public class AnnotatedControllerConfigurer
dataFetcher = registerBatchLoader(info);
}
FieldCoordinates coordinates = info.getCoordinates();
runtimeWiringBuilder.type(coordinates.getTypeName(), typeBuilder ->
runtimeWiringBuilder.type(coordinates.getTypeName(), (typeBuilder) ->
typeBuilder.dataFetcher(coordinates.getFieldName(), dataFetcher));
});
}
@@ -322,7 +322,7 @@ public class AnnotatedControllerConfigurer
BatchLoaderRegistry registry = obtainApplicationContext().getBean(BatchLoaderRegistry.class);
BatchLoaderRegistry.RegistrationSpec<Object, Object> registration = registry.forName(dataLoaderKey);
if (info.getMaxBatchSize() > 0) {
registration.withOptions(options -> options.setMaxBatchSize(info.getMaxBatchSize()));
registration.withOptions((options) -> options.setMaxBatchSize(info.getMaxBatchSize()));
}
HandlerMethod handlerMethod = info.getHandlerMethod();
@@ -362,6 +362,7 @@ public class AnnotatedControllerConfigurer
* Alternative to {@link #configure(RuntimeWiring.Builder)} that registers
* data fetchers in a {@link GraphQLCodeRegistry.Builder}. This could be
* used with programmatic creation of {@link graphql.schema.GraphQLSchema}.
* @param codeRegistryBuilder the code registry
*/
@SuppressWarnings("rawtypes")
public void configure(GraphQLCodeRegistry.Builder codeRegistryBuilder) {
@@ -408,7 +409,7 @@ public class AnnotatedControllerConfigurer
this.argumentResolvers = argumentResolvers;
this.methodValidationHelper =
(helper != null ? helper.getValidationHelperFor(info.getHandlerMethod()) : null);
(helper != null) ? helper.getValidationHelperFor(info.getHandlerMethod()) : null;
this.exceptionResolver = exceptionResolver;
@@ -429,12 +430,12 @@ public class AnnotatedControllerConfigurer
@Override
public Map<String, ResolvableType> getArguments() {
Predicate<MethodParameter> argumentPredicate = p ->
Predicate<MethodParameter> argumentPredicate = (p) ->
(p.getParameterAnnotation(Argument.class) != null || p.getParameterType() == ArgumentValue.class);
return Arrays.stream(this.mappingInfo.getHandlerMethod().getMethodParameters())
.filter(argumentPredicate)
.peek(p -> p.initParameterNameDiscovery(parameterNameDiscoverer))
.peek((p) -> p.initParameterNameDiscovery(parameterNameDiscoverer))
.collect(Collectors.toMap(
ArgumentMethodArgumentResolver::getArgumentName,
ResolvableType::forMethodParameter));
@@ -443,7 +444,7 @@ public class AnnotatedControllerConfigurer
/**
* Return the {@link HandlerMethod} used to fetch data.
*/
public HandlerMethod getHandlerMethod() {
HandlerMethod getHandlerMethod() {
return this.mappingInfo.getHandlerMethod();
}
@@ -469,13 +470,13 @@ public class AnnotatedControllerConfigurer
DataFetchingEnvironment env, DataFetcherHandlerMethod handlerMethod, Object result) {
if (this.subscription && result instanceof Publisher<?> publisher) {
result = Flux.from(publisher).onErrorResume(ex -> handleSubscriptionError(ex, env, handlerMethod));
result = Flux.from(publisher).onErrorResume((ex) -> handleSubscriptionError(ex, env, handlerMethod));
}
else if (result instanceof Mono) {
result = ((Mono<T>) result).onErrorResume(ex -> (Mono<T>) handleException(ex, env, handlerMethod));
result = ((Mono<T>) result).onErrorResume((ex) -> (Mono<T>) handleException(ex, env, handlerMethod));
}
else if (result instanceof Flux<?>) {
result = ((Flux<T>) result).onErrorResume(ex -> (Mono<T>) handleException(ex, env, handlerMethod));
result = ((Flux<T>) result).onErrorResume((ex) -> (Mono<T>) handleException(ex, env, handlerMethod));
}
return result;
}
@@ -484,7 +485,7 @@ public class AnnotatedControllerConfigurer
Throwable ex, DataFetchingEnvironment env, DataFetcherHandlerMethod handlerMethod) {
return this.exceptionResolver.resolveException(ex, env, handlerMethod.getBean())
.map(errors -> DataFetcherResult.newResult().errors(errors).build())
.map((errors) -> DataFetcherResult.newResult().errors(errors).build())
.switchIfEmpty(Mono.error(ex));
}
@@ -493,7 +494,7 @@ public class AnnotatedControllerConfigurer
Throwable ex, DataFetchingEnvironment env, DataFetcherHandlerMethod handlerMethod) {
return (Publisher<T>) this.exceptionResolver.resolveException(ex, env, handlerMethod.getBean())
.flatMap(errors -> Mono.error(new SubscriptionPublisherException(errors, ex)))
.flatMap((errors) -> Mono.error(new SubscriptionPublisherException(errors, ex)))
.switchIfEmpty(Mono.error(ex));
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.data.method.annotation.support;
import java.lang.reflect.Method;
@@ -54,13 +55,13 @@ import org.springframework.util.ClassUtils;
* Convenient base for classes that find annotated controller method with argument
* values resolved from a {@link graphql.schema.DataFetchingEnvironment}.
*
* @param <M> the type of mapping info prepared from a controller method
* @author Rossen Stoyanchev
* @since 1.3
* @param <M> the type of mapping info prepared from a controller method
*/
public abstract class AnnotatedControllerDetectionSupport<M> implements ApplicationContextAware, InitializingBean {
protected final static boolean springSecurityPresent = ClassUtils.isPresent(
protected static final boolean springSecurityPresent = ClassUtils.isPresent(
"org.springframework.security.core.context.SecurityContext",
AnnotatedControllerDetectionSupport.class.getClassLoader());
@@ -102,6 +103,7 @@ public abstract class AnnotatedControllerDetectionSupport<M> implements Applicat
* that assists in binding GraphQL arguments onto
* {@link org.springframework.graphql.data.method.annotation.Argument @Argument}
* annotated method parameters.
* @param registrar the formatter registrar
*/
public void addFormatterRegistrar(FormatterRegistrar registrar) {
registrar.registerFormatters(this.conversionService);
@@ -116,6 +118,7 @@ public abstract class AnnotatedControllerDetectionSupport<M> implements Applicat
* {@link org.springframework.graphql.data.method.annotation.Argument @Argument}
* should falls back to direct field access in case the target object does
* not use accessor methods.
* @param fallBackOnDirectFieldAccess whether binding should fall back on direct field access
* @since 1.2.0
*/
public void setFallBackOnDirectFieldAccess(boolean fallBackOnDirectFieldAccess) {
@@ -133,11 +136,9 @@ public abstract class AnnotatedControllerDetectionSupport<M> implements Applicat
* exceptions from non-controller {@link DataFetcher}s since exceptions from
* {@code @SchemaMapping} controller methods are handled automatically at
* the point of invocation.
*
* @return a resolver instance that can be plugged into
* {@link org.springframework.graphql.execution.GraphQlSource.Builder#exceptionResolvers(List)
* GraphQlSource.Builder}
*
* @since 1.2.0
*/
public HandlerDataFetcherExceptionResolver getExceptionResolver() {
@@ -214,15 +215,15 @@ public abstract class AnnotatedControllerDetectionSupport<M> implements Applicat
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isTraceEnabled()) {
logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Could not resolve type for bean '" + beanName + "'", ex);
}
}
if (beanType == null || !AnnotatedElementUtils.hasAnnotation(beanType, Controller.class)) {
continue;
}
Class<?> beanClass = context.getType(beanName);
findHandlerMethods(beanName, beanClass).forEach(info -> registerHandlerMethod(info, results));
findHandlerMethods(beanName, beanClass).forEach((info) -> registerHandlerMethod(info, results));
}
return results;
}
@@ -240,8 +241,8 @@ public abstract class AnnotatedControllerDetectionSupport<M> implements Applicat
Collection<M> mappingInfos = map.values();
if (logger.isTraceEnabled() && !mappingInfos.isEmpty()) {
logger.trace(formatMappings(userClass, mappingInfos));
if (this.logger.isTraceEnabled() && !mappingInfos.isEmpty()) {
this.logger.trace(formatMappings(userClass, mappingInfos));
}
return mappingInfos;
@@ -252,10 +253,10 @@ public abstract class AnnotatedControllerDetectionSupport<M> implements Applicat
private String formatMappings(Class<?> handlerType, Collection<M> infos) {
String formattedType = Arrays.stream(ClassUtils.getPackageName(handlerType).split("\\."))
.map(p -> p.substring(0, 1))
.map((p) -> p.substring(0, 1))
.collect(Collectors.joining(".", "", "." + handlerType.getSimpleName()));
return infos.stream()
.map(info -> {
.map((info) -> {
Method method = getHandlerMethod(info).getMethod();
String methodParameters = Arrays.stream(method.getGenericParameterTypes())
.map(Type::getTypeName)
@@ -268,7 +269,7 @@ public abstract class AnnotatedControllerDetectionSupport<M> implements Applicat
private void registerHandlerMethod(M info, Set<M> results) {
Assert.state(this.exceptionResolver != null, "afterPropertiesSet not called");
HandlerMethod handlerMethod = getHandlerMethod(info);
M existing = results.stream().filter(o -> o.equals(info)).findFirst().orElse(null);
M existing = results.stream().filter((o) -> o.equals(info)).findFirst().orElse(null);
if (existing != null && !getHandlerMethod(existing).equals(handlerMethod)) {
throw new IllegalStateException(
"Ambiguous mapping. Cannot map '" + handlerMethod.getBean() + "' method \n" +
@@ -281,9 +282,9 @@ public abstract class AnnotatedControllerDetectionSupport<M> implements Applicat
protected HandlerMethod createHandlerMethod(Method originalMethod, Object handler, Class<?> handlerType) {
Method method = AopUtils.selectInvocableMethod(originalMethod, handlerType);
return (handler instanceof String beanName ?
return (handler instanceof String beanName) ?
new HandlerMethod(beanName, obtainApplicationContext().getAutowireCapableBeanFactory(), method) :
new HandlerMethod(handler, method));
new HandlerMethod(handler, method);
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.data.method.annotation.support;
import java.lang.reflect.Method;
@@ -72,7 +73,6 @@ import org.springframework.web.method.ControllerAdviceBean;
*
* @author Rossen Stoyanchev
* @author Brian Clozel
* @since 1.2.0
*/
final class AnnotatedControllerExceptionResolver implements HandlerDataFetcherExceptionResolver {
@@ -98,9 +98,9 @@ final class AnnotatedControllerExceptionResolver implements HandlerDataFetcherEx
* are validated to ensure they are within a range of supported types.
* @param controllerType the controller type to register
*/
public void registerController(Class<?> controllerType) {
void registerController(Class<?> controllerType) {
this.controllerCache.computeIfAbsent(
controllerType, type -> new MethodResolver(findExceptionHandlers(controllerType)));
controllerType, (type) -> new MethodResolver(findExceptionHandlers(controllerType)));
}
/**
@@ -110,7 +110,7 @@ final class AnnotatedControllerExceptionResolver implements HandlerDataFetcherEx
* for use at runtime.
* @param context the context to look into
*/
public void registerControllerAdvice(ApplicationContext context) {
void registerControllerAdvice(ApplicationContext context) {
Map<ControllerAdviceBean, MethodResolver> detectedControllerAdvice = new HashMap<>();
for (ControllerAdviceBean bean : ControllerAdviceBean.findAnnotatedBeans(context)) {
Class<?> beanType = bean.getBeanType();
@@ -121,9 +121,8 @@ final class AnnotatedControllerExceptionResolver implements HandlerDataFetcherEx
}
}
}
detectedControllerAdvice.keySet().stream().sorted(OrderComparator.INSTANCE).forEach(bean -> {
this.controllerAdviceCache.put(bean, detectedControllerAdvice.get(bean));
});
detectedControllerAdvice.keySet().stream().sorted(OrderComparator.INSTANCE)
.forEach((bean) -> this.controllerAdviceCache.put(bean, detectedControllerAdvice.get(bean)));
if (logger.isDebugEnabled()) {
logger.debug("@GraphQlException methods in ControllerAdvice beans: " +
(this.controllerAdviceCache.isEmpty() ? "none" : this.controllerAdviceCache.size()));
@@ -134,7 +133,7 @@ final class AnnotatedControllerExceptionResolver implements HandlerDataFetcherEx
private static Map<Class<? extends Throwable>, Method> findExceptionHandlers(Class<?> handlerType) {
Map<Method, GraphQlExceptionHandler> handlerMap = MethodIntrospector.selectMethods(
handlerType, (MethodIntrospector.MetadataLookup<GraphQlExceptionHandler>) method ->
handlerType, (MethodIntrospector.MetadataLookup<GraphQlExceptionHandler>) (method) ->
AnnotatedElementUtils.findMergedAnnotation(method, GraphQlExceptionHandler.class));
Map<Class<? extends Throwable>, Method> mappings = new HashMap<>(handlerMap.size());
@@ -230,7 +229,7 @@ final class AnnotatedControllerExceptionResolver implements HandlerDataFetcherEx
while (exToExpose != null) {
exceptions.add(exToExpose);
Throwable cause = exToExpose.getCause();
exToExpose = (cause != exToExpose ? cause : null);
exToExpose = (cause != exToExpose) ? cause : null;
}
Object[] arguments = new Object[exceptions.size() + 1];
exceptions.toArray(arguments); // efficient arraycopy call in ArrayList
@@ -278,7 +277,7 @@ final class AnnotatedControllerExceptionResolver implements HandlerDataFetcherEx
* @return the exception handler to use, or {@code null} if no match
*/
@Nullable
public MethodHolder resolveMethod(Throwable exception) {
MethodHolder resolveMethod(Throwable exception) {
MethodHolder method = resolveMethodByExceptionType(exception.getClass());
if (method == null) {
Throwable cause = exception.getCause();
@@ -296,7 +295,7 @@ final class AnnotatedControllerExceptionResolver implements HandlerDataFetcherEx
method = getMappedMethod(exceptionType);
this.resolvedExceptionCache.put(exceptionType, method);
}
return (method != NO_MATCH ? method : null);
return (method != NO_MATCH) ? method : null;
}
private MethodHolder getMappedMethod(Class<? extends Throwable> exceptionType) {
@@ -342,11 +341,11 @@ final class AnnotatedControllerExceptionResolver implements HandlerDataFetcherEx
this.adapter = ReturnValueAdapter.createFor(this.returnType);
}
public Method getMethod() {
Method getMethod() {
return this.method;
}
public Mono<List<GraphQLError>> adapt(@Nullable Object result, Throwable ex) {
Mono<List<GraphQLError>> adapt(@Nullable Object result, Throwable ex) {
return this.adapter.adapt(result, this.returnType, ex);
}
@@ -421,24 +420,24 @@ final class AnnotatedControllerExceptionResolver implements HandlerDataFetcherEx
}
/** Adapter for void */
/* Adapter for void */
ReturnValueAdapter forVoid = (result, returnType, ex) -> Mono.just(Collections.emptyList());
/** Adapter for a single GraphQLError */
/* Adapter for a single GraphQLError */
ReturnValueAdapter forSingleError = (result, returnType, ex) ->
(result == null ?
Mono.empty() :
Mono.just(Collections.singletonList((GraphQLError) result)));
(result != null) ?
Mono.just(Collections.singletonList((GraphQLError) result)) :
Mono.empty();
/** Adapter for a collection of GraphQLError's */
/* Adapter for a collection of GraphQLError's */
ReturnValueAdapter forCollection = (result, returnType, ex) ->
(result == null ?
Mono.empty() :
Mono.just((result instanceof List ?
(result != null) ?
Mono.just((result instanceof List) ?
(List<GraphQLError>) result :
new ArrayList<>((Collection<GraphQLError>) result))));
new ArrayList<>((Collection<GraphQLError>) result)) :
Mono.empty();
/** Adapter for Object */
/* Adapter for Object */
ReturnValueAdapter forObject = (result, returnType, ex) -> {
if (result == null) {
return Mono.empty();
@@ -458,15 +457,15 @@ final class AnnotatedControllerExceptionResolver implements HandlerDataFetcherEx
}
};
/** Adapter for {@code Mono<Void>} */
/* Adapter for {@code Mono<Void>} */
ReturnValueAdapter forMonoVoid = (result, returnType, ex) ->
(result == null ? Mono.empty() : Mono.just(Collections.emptyList()));
(result != null) ? Mono.just(Collections.emptyList()) : Mono.empty();
/** Adapter for a {@code Mono} wrapping any of the other synchronous return value types */
/* Adapter for a {@code Mono} wrapping any of the other synchronous return value types */
ReturnValueAdapter forMono = (result, returnType, ex) ->
(result == null ?
Mono.empty() :
((Mono<?>) result).flatMap(o -> forObject.adapt(o, returnType, ex)).switchIfEmpty(Mono.error(ex)));
(result != null) ?
((Mono<?>) result).flatMap((o) -> forObject.adapt(o, returnType, ex)).switchIfEmpty(Mono.error(ex)) :
Mono.empty();
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.data.method.annotation.support;
import graphql.schema.DataFetchingEnvironment;
@@ -83,6 +84,9 @@ public class ArgumentMethodArgumentResolver implements HandlerMethodArgumentReso
/**
* Perform the binding with the configured {@link #getArgumentBinder() binder}.
* @param environment for access to the arguments
* @param name the name of an argument, or {@code null} to use the full map
* @param targetType the type of Object to create
* @since 1.3
*/
@Nullable

View File

@@ -19,22 +19,24 @@ package org.springframework.graphql.data.method.annotation.support;
import jakarta.validation.valueextraction.ExtractedValue;
import jakarta.validation.valueextraction.UnwrapByDefault;
import jakarta.validation.valueextraction.ValueExtractor;
import org.springframework.graphql.data.ArgumentValue;
/**
* {@link ValueExtractor} that enables {@code @Valid} with {@link ArgumentValue},
* and helps to extract the value from it.
*
* @author Rossen Stoyanchev
* @since 1.2.2
*/
@UnwrapByDefault
public final class ArgumentValueValueExtractor implements ValueExtractor<ArgumentValue<@ExtractedValue ?>> {
@Override
public void extractValues(ArgumentValue<?> argumentValue, ValueReceiver receiver) {
if (!argumentValue.isOmitted()) {
receiver.value(null, argumentValue.value());
}
}
@Override
public void extractValues(ArgumentValue<?> argumentValue, ValueReceiver receiver) {
if (!argumentValue.isOmitted()) {
receiver.value(null, argumentValue.value());
}
}
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.data.method.annotation.support;
import graphql.schema.DataFetchingEnvironment;

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.data.method.annotation.support;
import java.lang.annotation.Annotation;
@@ -97,7 +98,7 @@ public class AuthenticationPrincipalArgumentResolver implements HandlerMethodArg
@Override
public Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) throws Exception {
return getCurrentAuthentication(parameter)
.mapNotNull(auth -> resolvePrincipal(parameter, auth.getPrincipal()))
.mapNotNull((auth) -> resolvePrincipal(parameter, auth.getPrincipal()))
.transform((argument) -> isPublisherOrMono(parameter) ? Mono.just(argument) : argument);
}
@@ -109,7 +110,7 @@ public class AuthenticationPrincipalArgumentResolver implements HandlerMethodArg
@SuppressWarnings("unchecked")
private Mono<Authentication> getCurrentAuthentication(MethodParameter parameter) {
Object value = PrincipalMethodArgumentResolver.resolveAuthentication(parameter);
return (value instanceof Authentication auth ? Mono.just(auth) : (Mono<Authentication>) value);
return (value instanceof Authentication auth) ? Mono.just(auth) : (Mono<Authentication>) value;
}
@Nullable

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.data.method.annotation.support;
import java.security.Principal;
@@ -50,7 +51,7 @@ import org.springframework.util.ClassUtils;
*/
public class BatchLoaderHandlerMethod extends InvocableHandlerMethodSupport {
private final static boolean springSecurityPresent = ClassUtils.isPresent(
private static final boolean springSecurityPresent = ClassUtils.isPresent(
"org.springframework.security.core.context.SecurityContext",
AnnotatedControllerConfigurer.class.getClassLoader());
@@ -66,7 +67,6 @@ public class BatchLoaderHandlerMethod extends InvocableHandlerMethodSupport {
/**
* Invoke the underlying batch loader method with a collection of keys to
* return a Map of key-value pairs.
*
* @param keys the keys for which to load values
* @param environment the environment available to batch loaders
* @param <K> the type of keys in the map
@@ -80,7 +80,7 @@ public class BatchLoaderHandlerMethod extends InvocableHandlerMethodSupport {
Object result = doInvoke(environment.getContext(), args);
return toMonoMap(result);
}
return toArgsMono(args).flatMap(argValues -> {
return toArgsMono(args).flatMap((argValues) -> {
Object result = doInvoke(environment.getContext(), argValues);
return toMonoMap(result);
});
@@ -89,7 +89,6 @@ public class BatchLoaderHandlerMethod extends InvocableHandlerMethodSupport {
/**
* Invoke the underlying batch loader method with a collection of input keys
* to return a collection of matching values.
*
* @param keys the keys for which to load values
* @param environment the environment available to batch loaders
* @param <V> the type of values returned
@@ -101,7 +100,7 @@ public class BatchLoaderHandlerMethod extends InvocableHandlerMethodSupport {
Object result = doInvoke(environment.getContext(), args);
return toFlux(result);
}
return toArgsMono(args).flatMapMany(resolvedArgs -> {
return toArgsMono(args).flatMapMany((resolvedArgs) -> {
Object result = doInvoke(environment.getContext(), resolvedArgs);
return toFlux(result);
});
@@ -164,7 +163,7 @@ public class BatchLoaderHandlerMethod extends InvocableHandlerMethodSupport {
}
private boolean doesNotHaveAsyncArgs(Object[] args) {
return Arrays.stream(args).noneMatch(arg -> arg instanceof Mono);
return Arrays.stream(args).noneMatch((arg) -> arg instanceof Mono);
}
@SuppressWarnings("unchecked")
@@ -176,7 +175,7 @@ public class BatchLoaderHandlerMethod extends InvocableHandlerMethodSupport {
return (Mono<Map<K, V>>) result;
}
else if (result instanceof CompletableFuture) {
return Mono.fromFuture((CompletableFuture<? extends Map<K,V>>) result);
return Mono.fromFuture((CompletableFuture<? extends Map<K, V>>) result);
}
return Mono.error(new IllegalStateException("Unexpected return value: " + result));
}

View File

@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.data.method.annotation.support;
import java.lang.annotation.Annotation;
@@ -72,7 +73,7 @@ public class ContextValueMethodArgumentResolver implements HandlerMethodArgument
@Nullable GraphQLContext graphQlContext) {
Class<?> parameterType = parameter.getParameterType();
Object value = (graphQlContext != null ? graphQlContext.get(contextValueName) : null);
Object value = (graphQlContext != null) ? graphQlContext.get(contextValueName) : null;
boolean isOptional = parameterType.equals(Optional.class);
boolean isMono = parameterType.equals(Mono.class);
@@ -85,14 +86,14 @@ public class ContextValueMethodArgumentResolver implements HandlerMethodArgument
if (value == null) {
value = Mono.empty();
}
else if (!( value instanceof Mono)) {
else if (!(value instanceof Mono)) {
value = Mono.just(value);
}
return Mono.just(value);
}
if (isOptional) {
return (value instanceof Optional ? value : Optional.ofNullable(value));
return (value instanceof Optional) ? value : Optional.ofNullable(value);
}
return value;

View File

@@ -13,9 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.data.method.annotation.support;
import graphql.schema.DataFetchingEnvironment;
import org.springframework.core.MethodParameter;
import org.springframework.graphql.data.method.HandlerMethodArgumentResolver;
@@ -27,14 +29,14 @@ import org.springframework.graphql.data.method.HandlerMethodArgumentResolver;
*/
public class ContinuationHandlerMethodArgumentResolver implements HandlerMethodArgumentResolver {
@Override
public boolean supportsParameter(MethodParameter parameter) {
return "kotlin.coroutines.Continuation".equals(parameter.getParameterType().getName());
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
return "kotlin.coroutines.Continuation".equals(parameter.getParameterType().getName());
}
@Override
public Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) {
return null;
}
@Override
public Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) {
return null;
}
}

View File

@@ -13,9 +13,11 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.graphql.data.method.annotation.support;
import java.util.Arrays;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.function.BiConsumer;
@@ -49,6 +51,7 @@ public class DataFetcherHandlerMethod extends DataFetcherHandlerMethodSupport {
* @param handlerMethod the handler method
* @param resolvers the argument resolvers
* @param validationHelper to apply bean validation with
* @param executor an {@link Executor} to use for {@link Callable} return values
* @param subscription whether the field being fetched is of subscription type
*/
public DataFetcherHandlerMethod(
@@ -58,7 +61,7 @@ public class DataFetcherHandlerMethod extends DataFetcherHandlerMethodSupport {
super(handlerMethod, resolvers, executor);
Assert.isTrue(!resolvers.getResolvers().isEmpty(), "No argument resolvers");
this.validationHelper = (validationHelper != null ? validationHelper : (controller, args) -> {});
this.validationHelper = (validationHelper != null) ? validationHelper : (controller, args) -> { };
this.subscription = subscription;
}
@@ -72,9 +75,7 @@ public class DataFetcherHandlerMethod extends DataFetcherHandlerMethodSupport {
* The {@code providedArgs} parameter however may supply argument values to
* be used directly, i.e. without argument resolution. Provided argument
* values are checked before argument resolvers.
*
* @param environment the environment to resolve arguments from
*
* @return the raw value returned by the invoked method, possibly a
* {@code Mono} in case a method argument requires asynchronous resolution;
* {@code Mono<Throwable>} is returned if invocation fails.
@@ -87,6 +88,8 @@ public class DataFetcherHandlerMethod extends DataFetcherHandlerMethodSupport {
/**
* Variant of {@link #invoke(DataFetchingEnvironment)} that also accepts
* "given" arguments, which are matched by type.
* @param environment the data fetching environment
* @param providedArgs additional arguments to be matched by their type
* @since 1.2.0
*/
@Nullable
@@ -99,17 +102,17 @@ public class DataFetcherHandlerMethod extends DataFetcherHandlerMethodSupport {
return Mono.error(ex);
}
if (Arrays.stream(args).noneMatch(arg -> arg instanceof Mono)) {
if (Arrays.stream(args).noneMatch((arg) -> arg instanceof Mono)) {
return validateAndInvoke(args, environment);
}
return this.subscription ?
toArgsMono(args).flatMapMany(argValues -> {
toArgsMono(args).flatMapMany((argValues) -> {
Object result = validateAndInvoke(argValues, environment);
Assert.state(result instanceof Publisher, "Expected a Publisher from a Subscription response");
return Flux.from((Publisher<?>) result);
}) :
toArgsMono(args).flatMap(argValues -> {
toArgsMono(args).flatMap((argValues) -> {
Object result = validateAndInvoke(argValues, environment);
if (result instanceof Mono<?> mono) {
return mono;

Some files were not shown because too many files have changed in this diff Show More