From dfed05a82278642105c088a9f6994ee5eaaed2dc Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Tue, 20 Sep 2022 12:29:00 +0100 Subject: [PATCH] Consistent access to raw arguments map See gh-493 --- .../src/docs/asciidoc/index.adoc | 15 ++- .../ArgumentMapMethodArgumentResolver.java | 20 +++- ...rgumentMapMethodArgumentResolverTests.java | 101 ++++++++++++++++++ 3 files changed, 130 insertions(+), 6 deletions(-) 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 b766f405..426ef265 100644 --- a/spring-graphql-docs/src/docs/asciidoc/index.adoc +++ b/spring-graphql-docs/src/docs/asciidoc/index.adoc @@ -1275,11 +1275,18 @@ See <>. | For access to all field arguments bound to a higher-level, typed Object. See <>. +| `@Argument Map` +| For access to the raw map of arguments, where `@Argument` does not have a +`name` attribute. + +| `@Arguments Map` +| For access to the raw map of arguments. + | `@ProjectedPayload` Interface | For access to field arguments through a project interface. See <>. -| Source +| "Source" | For access to the source (i.e. parent/container) instance of the field. See <>. @@ -1365,8 +1372,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` on a `Map` argument, to obtain all argument -values. The name attribute on `@Argument` must not be set. +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. @@ -1380,6 +1387,8 @@ For example, `@Argument BookInput bookInput` uses the value of the argument "boo to initialize `BookInput`, while `@Arguments` uses the full arguments map and in that case, top-level arguments are bound to `BookInput` properties. +You can use `@Arguments` with a `Map` argument, to obtain the raw map of +all argument values. [[controllers-schema-mapping-projectedpayload-argument]] 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 index 9dc997f8..8699cd04 100644 --- 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 @@ -23,13 +23,18 @@ 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 annotated with an - * {@link Argument @Argument} by returning the GraphQL - * {@link DataFetchingEnvironment#getArguments() arguments} map. + * 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 @@ -38,12 +43,21 @@ public class ArgumentMapMethodArgumentResolver implements HandlerMethodArgumentR @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/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