From a98ca42981828c8b4320411629dd621ce4a7260f Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Tue, 31 Aug 2021 14:44:47 +0100 Subject: [PATCH] Add resolver for @Argument Map Closes gh-113 --- .../src/docs/asciidoc/index.adoc | 3 ++ .../AnnotatedDataFetcherConfigurer.java | 12 +++-- .../ArgumentMapMethodArgumentResolver.java | 49 +++++++++++++++++++ ...va => ArgumentMethodArgumentResolver.java} | 8 +-- .../AnnotatedDataFetcherConfigurerTests.java | 4 +- 5 files changed, 66 insertions(+), 10 deletions(-) create mode 100644 spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentMapMethodArgumentResolver.java rename spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/{InputArgumentMethodArgumentResolver.java => ArgumentMethodArgumentResolver.java} (95%) diff --git a/spring-graphql-docs/src/docs/asciidoc/index.adoc b/spring-graphql-docs/src/docs/asciidoc/index.adoc index 5722ff7b..50129e7d 100644 --- a/spring-graphql-docs/src/docs/asciidoc/index.adoc +++ b/spring-graphql-docs/src/docs/asciidoc/index.adoc @@ -566,6 +566,9 @@ it not specified, it defaults to the method parameter name, but this requires th By default, an `@Argument` is required, but you can make it optional by setting the `required` flag to false or by declaring the argument with `java.util.Optional`. +You can use `@Argument` on a `Map` argument, to obtain all argument +values. The name attribute on `@Argument` must not be set. + [[controllers-source]] ==== Source diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/AnnotatedDataFetcherConfigurer.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/AnnotatedDataFetcherConfigurer.java index 8c95658a..2db5542e 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/AnnotatedDataFetcherConfigurer.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/AnnotatedDataFetcherConfigurer.java @@ -45,8 +45,9 @@ import org.springframework.graphql.data.method.annotation.MutationMapping; import org.springframework.graphql.data.method.annotation.QueryMapping; import org.springframework.graphql.data.method.annotation.SchemaMapping; import org.springframework.graphql.data.method.annotation.SubscriptionMapping; +import org.springframework.graphql.data.method.annotation.support.ArgumentMapMethodArgumentResolver; import org.springframework.graphql.data.method.annotation.support.DataFetchingEnvironmentMethodArgumentResolver; -import org.springframework.graphql.data.method.annotation.support.InputArgumentMethodArgumentResolver; +import org.springframework.graphql.data.method.annotation.support.ArgumentMethodArgumentResolver; import org.springframework.graphql.data.method.annotation.support.SourceMethodArgumentResolver; import org.springframework.graphql.execution.RuntimeWiringConfigurer; import org.springframework.http.MediaType; @@ -159,17 +160,18 @@ public class AnnotatedDataFetcherConfigurer public void afterPropertiesSet() { this.argumentResolvers = new HandlerMethodArgumentResolverComposite(); this.argumentResolvers.addResolver(initInputArgumentMethodArgumentResolver()); + this.argumentResolvers.addResolver(new ArgumentMapMethodArgumentResolver()); this.argumentResolvers.addResolver(new DataFetchingEnvironmentMethodArgumentResolver()); this.argumentResolvers.addResolver(new SourceMethodArgumentResolver()); } - private InputArgumentMethodArgumentResolver initInputArgumentMethodArgumentResolver() { - InputArgumentMethodArgumentResolver argumentResolver; + private ArgumentMethodArgumentResolver initInputArgumentMethodArgumentResolver() { + ArgumentMethodArgumentResolver argumentResolver; if (this.jsonMessageConverter != null) { - argumentResolver = new InputArgumentMethodArgumentResolver(this.jsonMessageConverter); + argumentResolver = new ArgumentMethodArgumentResolver(this.jsonMessageConverter); } else if (this.jsonEncoder != null && this.jsonDecoder != null) { - argumentResolver = new InputArgumentMethodArgumentResolver(this.jsonDecoder, this.jsonEncoder); + argumentResolver = new ArgumentMethodArgumentResolver(this.jsonDecoder, this.jsonEncoder); } else { throw new IllegalArgumentException( diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentMapMethodArgumentResolver.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentMapMethodArgumentResolver.java new file mode 100644 index 00000000..3a80d0f5 --- /dev/null +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentMapMethodArgumentResolver.java @@ -0,0 +1,49 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.graphql.data.method.annotation.support; + +import java.util.Map; + +import graphql.schema.DataFetchingEnvironment; + +import org.springframework.core.MethodParameter; +import org.springframework.graphql.data.method.HandlerMethodArgumentResolver; +import org.springframework.graphql.data.method.annotation.Argument; +import org.springframework.util.StringUtils; + +/** + * Resolves {@link Map} method arguments annotated with an @{@link Argument} + * where the annotation does not specify an argument name. + * + * @author Rossen Stoyanchev + * @since 1.0.0 + */ +public class ArgumentMapMethodArgumentResolver implements HandlerMethodArgumentResolver { + + @Override + public boolean supportsParameter(MethodParameter parameter) { + Argument argument = parameter.getParameterAnnotation(Argument.class); + return (argument != null && + Map.class.isAssignableFrom(parameter.getParameterType()) && + !StringUtils.hasText(argument.name())); + } + + @Override + public Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) { + return environment.getArguments(); + } + +} diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/InputArgumentMethodArgumentResolver.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentMethodArgumentResolver.java similarity index 95% rename from spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/InputArgumentMethodArgumentResolver.java rename to spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentMethodArgumentResolver.java index 17912de9..4ed637e8 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/InputArgumentMethodArgumentResolver.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentMethodArgumentResolver.java @@ -53,7 +53,7 @@ import org.springframework.util.StringUtils; * @author Rossen Stoyanchev * @since 1.0.0 */ -public class InputArgumentMethodArgumentResolver implements HandlerMethodArgumentResolver { +public class ArgumentMethodArgumentResolver implements HandlerMethodArgumentResolver { private final ArgumentConverter argumentConverter; @@ -62,16 +62,16 @@ public class InputArgumentMethodArgumentResolver implements HandlerMethodArgumen * {@link org.springframework.http.converter.HttpMessageConverter} to convert * Map-based input arguments to higher level Objects. */ - public InputArgumentMethodArgumentResolver(GenericHttpMessageConverter converter) { + public ArgumentMethodArgumentResolver(GenericHttpMessageConverter converter) { this.argumentConverter = new MessageConverterArgumentConverter(converter); } /** * Variant of - * {@link #InputArgumentMethodArgumentResolver(GenericHttpMessageConverter)} + * {@link #ArgumentMethodArgumentResolver(GenericHttpMessageConverter)} * to use an {@link Encoder} and {@link Decoder} to convert input arguments. */ - public InputArgumentMethodArgumentResolver(Decoder decoder, Encoder encoder) { + public ArgumentMethodArgumentResolver(Decoder decoder, Encoder encoder) { this.argumentConverter = new CodecArgumentConverter(decoder, encoder); } diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/method/AnnotatedDataFetcherConfigurerTests.java b/spring-graphql/src/test/java/org/springframework/graphql/data/method/AnnotatedDataFetcherConfigurerTests.java index 291cc88d..0b7bc2be 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/method/AnnotatedDataFetcherConfigurerTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/method/AnnotatedDataFetcherConfigurerTests.java @@ -37,7 +37,9 @@ import org.springframework.stereotype.Controller; import static org.assertj.core.api.Assertions.assertThat; /** - * Unit tests for {@link AnnotatedDataFetcherConfigurer}. + * Unit tests for {@link AnnotatedDataFetcherConfigurer}, focusing on detection + * and mapping of handler methods to schema fields. + * * @author Rossen Stoyanchev */ public class AnnotatedDataFetcherConfigurerTests {