From 562d39a9a2e21d7b1936eb89a31a51fdd0dd5bc4 Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Wed, 1 Feb 2023 09:46:14 +0000 Subject: [PATCH] Revert "Remove ArgumentMapMethodArgumentResolver" This reverts commit 7b301591093479f1ce8496a34ad8c83ee00803fa. --- .../src/docs/asciidoc/index.adoc | 33 +----- .../data/method/annotation/Argument.java | 13 ++- .../AnnotatedControllerConfigurer.java | 1 + .../ArgumentMapMethodArgumentResolver.java | 66 ++++++++++++ .../ArgumentMethodArgumentResolver.java | 9 +- ...rgumentMapMethodArgumentResolverTests.java | 101 ++++++++++++++++++ .../ArgumentMethodArgumentResolverTests.java | 22 +--- .../ArgumentsMethodArgumentResolverTests.java | 22 +--- 8 files changed, 185 insertions(+), 82 deletions(-) create mode 100644 spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentMapMethodArgumentResolver.java create mode 100644 spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/ArgumentMapMethodArgumentResolverTests.java diff --git a/spring-graphql-docs/src/docs/asciidoc/index.adoc b/spring-graphql-docs/src/docs/asciidoc/index.adoc index 4c41e86d..316bf49a 100644 --- a/spring-graphql-docs/src/docs/asciidoc/index.adoc +++ b/spring-graphql-docs/src/docs/asciidoc/index.adoc @@ -1183,23 +1183,19 @@ Schema mapping handler methods can have any of the following method arguments: | `@Argument` | For access to a named field argument bound to a higher-level, typed Object. - See <>. | `@Argument Map` -| For access to the raw argument value. - -See <>. +| For access to the raw map of arguments, where `@Argument` does not have a +`name` attribute. | `ArgumentValue` | For access to a named field argument bound to a higher-level, typed Object along with a flag to indicate if the input argument was omitted vs set to `null`. - See <>. | `@Arguments` | For access to all field arguments bound to a higher-level, typed Object. - See <>. | `@Arguments Map` @@ -1207,17 +1203,14 @@ See <>. | `@ProjectedPayload` Interface | For access to field arguments through a project interface. - See <>. | "Source" | For access to the source (i.e. parent/container) instance of the field. - See <>. | `DataLoader` | For access to a `DataLoader` in the `DataLoaderRegistry`. - See <>. | `@ContextValue` @@ -1297,26 +1290,8 @@ are enforced by GraphQL Java. If binding fails, a `BindException` is raised with binding issues accumulated as field errors where the `field` of each error is the argument path where the issue occurred. -You can use `@Argument` with a `Map` argument, to obtain the raw value of -the argument. For example: - -[source,java,indent=0,subs="verbatim,quotes"] ----- - @Controller - public class BookController { - - @MutationMapping - public Book addBook(@Argument Map bookInput) { - // ... - } - } ----- - -NOTE: Prior to 1.2, `@Argument Map` returned the full arguments map if -the annotation did not specify a name. After 1.2, `@Argument` with -`Map` always returns the raw argument value, matching either to the name -specified in the annotation, or to the parameter name. For access to the full arguments -map, please use <> instead. +You can use `@Argument` with a `Map` argument, to obtain the raw map of +all argument values. The name attribute on `@Argument` must not be set. [[controllers.schema-mapping.argument-value]] diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/Argument.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/Argument.java index 6e378f82..a2c0561e 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/Argument.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/Argument.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2023 the original author or authors. + * 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. @@ -40,13 +40,12 @@ import org.springframework.validation.BindException; * {@code field} of each error is the argument path where the issue occurred. * *

If the method parameter is {@link java.util.Map Map<String, Object>} - * then the raw argument value for the named argument is used. For access to the - * full {@link graphql.schema.DataFetchingEnvironment#getArguments() arguments} - * map, use {@link Arguments @Arguments} instead. + * and a parameter name is not specified, then the resolves value is the raw + * {@link graphql.schema.DataFetchingEnvironment#getArguments() arguments} map. * - *

This annotation has neither a "required" flag nor the option to specify a - * default value, both of which can be specified at the GraphQL schema level - * and are enforced by the GraphQL Java engine. + *

Note that this annotation has neither a "required" flag nor the option to + * specify a default value, both of which can be specified at the GraphQL schema + * level and are enforced by the GraphQL Java engine. * * @author Rossen Stoyanchev * @since 1.0.0 diff --git a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java index 81142a41..da700d77 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java @@ -189,6 +189,7 @@ public class AnnotatedControllerConfigurer // Must be ahead of ArgumentMethodArgumentResolver resolvers.addResolver(new ProjectedPayloadMethodArgumentResolver(obtainApplicationContext())); } + resolvers.addResolver(new ArgumentMapMethodArgumentResolver()); GraphQlArgumentBinder argumentBinder = new GraphQlArgumentBinder(this.conversionService); resolvers.addResolver(new ArgumentMethodArgumentResolver(argumentBinder)); resolvers.addResolver(new ArgumentsMethodArgumentResolver(argumentBinder)); 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..8699cd04 --- /dev/null +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentMapMethodArgumentResolver.java @@ -0,0 +1,66 @@ +/* + * Copyright 2002-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.graphql.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.graphql.data.method.annotation.Arguments; +import org.springframework.util.StringUtils; + + +/** + * Resolves a {@link Map} method parameter for access to the raw arguments map. + * Supported with the following: + *

    + *
  • {@link Map} argument annotated with {@link Argument @Argument} where the + * annotation does not explicitly specify a name. + *
  • {@link Map} argument annotated with {@link Arguments @Arguments}. + *
+ * + * @author Rossen Stoyanchev + * @since 1.0.0 + */ +public class ArgumentMapMethodArgumentResolver implements HandlerMethodArgumentResolver { + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return (checkArgumentMap(parameter) || checkArgumentsMap(parameter)); + } + + private static boolean checkArgumentMap(MethodParameter parameter) { + Argument argument = parameter.getParameterAnnotation(Argument.class); + return (argument != null && + Map.class.isAssignableFrom(parameter.getParameterType()) && + !StringUtils.hasText(argument.name())); + } + + private static boolean checkArgumentsMap(MethodParameter parameter) { + Arguments argument = parameter.getParameterAnnotation(Arguments.class); + return (argument != null && Map.class.isAssignableFrom(parameter.getParameterType())); + } + + @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/ArgumentMethodArgumentResolver.java b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentMethodArgumentResolver.java index 854591d4..87f49bf7 100644 --- a/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentMethodArgumentResolver.java +++ b/spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentMethodArgumentResolver.java @@ -30,7 +30,7 @@ import org.springframework.util.StringUtils; * Resolver for a method parameter that is annotated with * {@link Argument @Argument}. The specified raw argument value is obtained via * {@link DataFetchingEnvironment#getArgument(String)} and bound to a higher - * level object via {@link GraphQlArgumentBinder} to match the target method + * level object, via {@link GraphQlArgumentBinder}, to match the target method * parameter type. * *

This resolver also supports wrapping the target object with @@ -45,7 +45,9 @@ import org.springframework.util.StringUtils; * @author Rossen Stoyanchev * @author Brian Clozel * @since 1.0.0 - * @see org.springframework.graphql.data.method.annotation.support.ArgumentsMethodArgumentResolver + * @see org.springframework.graphql.data.method.annotation.Argument + * @see org.springframework.graphql.data.method.annotation.Arguments + * @see org.springframework.graphql.data.GraphQlArgumentBinder */ public class ArgumentMethodArgumentResolver implements HandlerMethodArgumentResolver { @@ -79,8 +81,7 @@ public class ArgumentMethodArgumentResolver implements HandlerMethodArgumentReso } } else if (parameter.getParameterType() != ArgumentValue.class) { - throw new IllegalStateException( - "Expected either @Argument or a method parameter of type ArgumentValue"); + throw new IllegalStateException("Expected @Argument annotation"); } String parameterName = parameter.getParameterName(); diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/ArgumentMapMethodArgumentResolverTests.java b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/ArgumentMapMethodArgumentResolverTests.java new file mode 100644 index 00000000..fbd8575f --- /dev/null +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/ArgumentMapMethodArgumentResolverTests.java @@ -0,0 +1,101 @@ +/* + * Copyright 2020-2022 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.graphql.data.method.annotation.support; + + +import java.util.Collections; +import java.util.Map; + +import org.junit.jupiter.api.Test; + +import org.springframework.core.MethodParameter; +import org.springframework.graphql.Book; +import org.springframework.graphql.data.method.HandlerMethodArgumentResolver; +import org.springframework.graphql.data.method.annotation.Argument; +import org.springframework.graphql.data.method.annotation.Arguments; +import org.springframework.graphql.data.method.annotation.QueryMapping; +import org.springframework.stereotype.Controller; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Unit tests for {@link ArgumentMethodArgumentResolver}. + * @author Rossen Stoyanchev + */ +class ArgumentMapMethodArgumentResolverTests extends ArgumentResolverTestSupport { + + private final HandlerMethodArgumentResolver resolver = new ArgumentMapMethodArgumentResolver(); + + + @Test + void shouldSupportAnnotatedParameters() { + MethodParameter param = methodParam(BookController.class, "argumentMap", Map.class); + assertThat(this.resolver.supportsParameter(param)).isTrue(); + + param = methodParam(BookController.class, "argumentsMap", Map.class); + assertThat(this.resolver.supportsParameter(param)).isTrue(); + + param = methodParam(BookController.class, "argument", Long.class); + assertThat(this.resolver.supportsParameter(param)).isFalse(); + + param = methodParam(BookController.class, "namedArgumentMap", Map.class); + assertThat(this.resolver.supportsParameter(param)).isFalse(); + + param = methodParam(BookController.class, "notAnnotated", String.class); + assertThat(this.resolver.supportsParameter(param)).isFalse(); + } + + @Test + void shouldResolveRawArgumentsMap() throws Exception { + Object result = this.resolver.resolveArgument( + methodParam(BookController.class, "argumentMap", Map.class), + environment("{\"id\": 42 }")); + + assertThat(result).isNotNull().isInstanceOf(Map.class).isEqualTo(Collections.singletonMap("id", 42)); + } + + + @SuppressWarnings({"ConstantConditions", "unused"}) + @Controller + static class BookController { + + @QueryMapping + public Book argumentMap(@Argument Map args) { + return null; + } + + @QueryMapping + public Book argumentsMap(@Arguments Map args) { + return null; + } + + @QueryMapping + public Book argument(@Argument Long id) { + return null; + } + + @QueryMapping + public Book namedArgumentMap(@Argument(name = "book") Map book) { + return null; + } + + public void notAnnotated(String param) { + } + + } + +} \ No newline at end of file diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/ArgumentMethodArgumentResolverTests.java b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/ArgumentMethodArgumentResolverTests.java index 10483d77..0aba8a85 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/ArgumentMethodArgumentResolverTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/ArgumentMethodArgumentResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 the original author or authors. + * Copyright 2020-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,6 @@ package org.springframework.graphql.data.method.annotation.support; import java.util.List; -import java.util.Map; import org.junit.jupiter.api.Test; @@ -53,9 +52,6 @@ class ArgumentMethodArgumentResolverTests extends ArgumentResolverTestSupport { param = methodParam(BookController.class, "addBook", ArgumentValue.class); assertThat(this.resolver.supportsParameter(param)).isTrue(); - param = methodParam(BookController.class, "rawArgumentValue", Map.class); - assertThat(this.resolver.supportsParameter(param)).isTrue(); - param = methodParam(BookController.class, "notSupported", String.class); assertThat(this.resolver.supportsParameter(param)).isFalse(); } @@ -117,17 +113,6 @@ class ArgumentMethodArgumentResolverTests extends ArgumentResolverTestSupport { assertThat(result).isNotNull().isInstanceOf(Keyword.class).hasFieldOrPropertyWithValue("term", "test"); } - @Test - void shouldResolveRawArgumentValue() throws Exception { - Map result = (Map) this.resolver.resolveArgument( - methodParam(BookController.class, "rawArgumentValue", Map.class), - environment("{\"bookInput\": { \"name\": \"test name\", \"authorId\": 42} }")); - - assertThat(result) - .containsEntry("name", "test name") - .containsEntry("authorId", 42); - } - @SuppressWarnings({"ConstantConditions", "unused"}) @Controller @@ -162,13 +147,8 @@ class ArgumentMethodArgumentResolverTests extends ArgumentResolverTestSupport { return null; } - @MutationMapping - public Book rawArgumentValue(@Argument Map bookInput) { - return null; - } } - @SuppressWarnings({"NotNullFieldNotInitialized", "unused"}) static class BookInput { diff --git a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/ArgumentsMethodArgumentResolverTests.java b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/ArgumentsMethodArgumentResolverTests.java index a231425d..f05d69c3 100644 --- a/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/ArgumentsMethodArgumentResolverTests.java +++ b/spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/ArgumentsMethodArgumentResolverTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 the original author or authors. + * Copyright 2020-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,9 +17,6 @@ package org.springframework.graphql.data.method.annotation.support; -import java.util.Collections; -import java.util.Map; - import org.junit.jupiter.api.Test; import org.springframework.core.MethodParameter; @@ -30,7 +27,6 @@ import org.springframework.graphql.data.GraphQlArgumentBinder; import org.springframework.graphql.data.method.HandlerMethodArgumentResolver; import org.springframework.graphql.data.method.annotation.Arguments; import org.springframework.graphql.data.method.annotation.MutationMapping; -import org.springframework.graphql.data.method.annotation.QueryMapping; import org.springframework.stereotype.Controller; import static org.assertj.core.api.Assertions.assertThat; @@ -49,9 +45,6 @@ class ArgumentsMethodArgumentResolverTests extends ArgumentResolverTestSupport { void shouldSupportAnnotatedParameters() { MethodParameter methodParameter = methodParam(BookController.class, "addBook", BookInput.class); assertThat(resolver.supportsParameter(methodParameter)).isTrue(); - - methodParameter = methodParam(BookController.class, "argumentsMap", Map.class); - assertThat(resolver.supportsParameter(methodParameter)).isTrue(); } @Test @@ -78,15 +71,6 @@ class ArgumentsMethodArgumentResolverTests extends ArgumentResolverTestSupport { }); } - @Test - void shouldResolveRawArgumentsMap() throws Exception { - Object result = this.resolver.resolveArgument( - methodParam(BookController.class, "argumentsMap", Map.class), - environment("{\"id\": 42 }")); - - assertThat(result).isNotNull().isEqualTo(Collections.singletonMap("id", 42)); - } - @SuppressWarnings({"ConstantConditions", "unused"}) @Controller @@ -100,10 +84,6 @@ class ArgumentsMethodArgumentResolverTests extends ArgumentResolverTestSupport { return null; } - @QueryMapping - public Book argumentsMap(@Arguments Map map) { - return null; - } } @SuppressWarnings({"NotNullFieldNotInitialized", "unused"})