Skip to content

Commit 7e43223

Browse files
committed
Polishing
See gh-280
1 parent 0ecf921 commit 7e43223

File tree

12 files changed

+105
-81
lines changed

12 files changed

+105
-81
lines changed

spring-graphql-docs/src/docs/asciidoc/index.adoc

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,11 +1118,11 @@ Schema mapping handler methods can have any of the following method arguments:
11181118
| Method Argument | Description
11191119

11201120
| `@Argument`
1121-
| For access to a named field argument converted to a higher-level, typed Object.
1121+
| For access to a named field argument bound to a higher-level, typed Object.
11221122
See <<controllers-schema-mapping-argument>>.
11231123

11241124
| `@Arguments`
1125-
| For access to all field arguments converted to a higher-level, typed Object.
1125+
| For access to all field arguments bound to a higher-level, typed Object.
11261126
See <<controllers-schema-mapping-arguments>>.
11271127

11281128
| `@ProjectedPayload` Interface
@@ -1173,11 +1173,12 @@ In GraphQL Java, `DataFetchingEnvironment` provides access to a map of field-spe
11731173
argument values. The values can be simple scalar values (e.g. String, Long), a `Map` of
11741174
values for more complex input, or a `List` of values.
11751175

1176-
Use the `@Argument` annotation to inject a named field argument into a handler method.
1177-
The method parameter can be a higher-level, typed Object of any type. It is created and
1178-
initialized from the named field argument value(s), either matching them to single data
1179-
constructor parameters, or using the default constructor and then matching keys onto
1180-
Object properties through a `org.springframework.validation.DataBinder`:
1176+
Use the `@Argument` annotation to have an argument bound to a target object and
1177+
injected into the handler method. Binding is performed by mapping argument values to a
1178+
primary data constructor of the expected method parameter type, or by using a default
1179+
constructor to create the object and then map argument values to its properties. This is
1180+
repeated recursively, using all nested argument values and creating nested target objects
1181+
accordingly. For example:
11811182

11821183
[source,java,indent=0,subs="verbatim,quotes"]
11831184
----
@@ -1204,6 +1205,9 @@ TIP: The `@Argument` annotation does not have a "required" flag, nor the option
12041205
specify a default value. Both of these can be specified at the GraphQL schema level and
12051206
are enforced by GraphQL Java.
12061207

1208+
If binding fails, a `BindException` is raised with binding issues accumulated as field
1209+
errors where the `field` of each error is the argument path where the issue occurred.
1210+
12071211
You can use `@Argument` on a `Map<String, Object>` argument, to obtain all argument
12081212
values. The name attribute on `@Argument` must not be set.
12091213

spring-graphql/src/main/java/org/springframework/graphql/data/GraphQlArgumentInitializer.java renamed to spring-graphql/src/main/java/org/springframework/graphql/data/GraphQlArgumentBinder.java

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -48,23 +48,29 @@
4848

4949

5050
/**
51-
* Bind GraphQL arguments to higher level objects.
52-
*
53-
* <p>The target object may have
51+
* Bind a GraphQL argument values to higher level objects.
52+
*
53+
* <p>Binding is performed by mapping argument values to a primary data
54+
* constructor of the target Object, or by using a default constructor
55+
* and mapping argument values to properties. This is applied recursively.
5456
*
5557
* @author Brian Clozel
5658
* @author Rossen Stoyanchev
5759
* @since 1.0.0
5860
*/
59-
public class GraphQlArgumentInitializer {
61+
public class GraphQlArgumentBinder {
6062

6163
@Nullable
6264
private final SimpleTypeConverter typeConverter;
6365

6466
private final BindingErrorProcessor bindingErrorProcessor = new DefaultBindingErrorProcessor();
6567

6668

67-
public GraphQlArgumentInitializer(@Nullable ConversionService conversionService) {
69+
public GraphQlArgumentBinder() {
70+
this(null);
71+
}
72+
73+
public GraphQlArgumentBinder(@Nullable ConversionService conversionService) {
6874
if (conversionService != null) {
6975
this.typeConverter = new SimpleTypeConverter();
7076
this.typeConverter.setConversionService(conversionService);
@@ -87,24 +93,27 @@ private ConversionService getConversionService() {
8793

8894

8995
/**
90-
* Initialize an Object of the given {@code targetType}, either from a named
91-
* {@link DataFetchingEnvironment#getArgument(String) argument value}, or from all
92-
* {@link DataFetchingEnvironment#getArguments() values} as the source.
93-
* @param environment the environment with the argument values
94-
* @param name optionally, the name of an argument to initialize from,
95-
* or if {@code null}, the full map of arguments is used.
96-
* @param targetType the type of Object to initialize
97-
* @return the initialized Object, or {@code null}
98-
* @throws BindException raised in case of issues with binding argument values
99-
* such as conversion errors, type mismatches between the source values and
100-
* the target type structure, etc.
96+
* Bind a single argument or the full arguments map onto an object of the
97+
* given target type.
98+
* @param environment to obtain the argument value(s) from
99+
* @param argumentName the name of the argument to bind, or {@code null} to
100+
* use the full arguments map
101+
* @param targetType the type of Object to create
102+
* @return the created Object, possibly {@code null}
103+
* @throws BindException in case of binding issues such as conversion errors,
104+
* mismatches between the source and the target object structure, and so on.
105+
* Binding issues are accumulated as {@link BindException#getFieldErrors()
106+
* field errors} where the {@code field} of each error is the argument path
107+
* where the issue occurred.
101108
*/
102109
@Nullable
103110
@SuppressWarnings("unchecked")
104-
public Object initializeArgument(
105-
DataFetchingEnvironment environment, @Nullable String name, ResolvableType targetType) throws BindException {
111+
public Object bind(
112+
DataFetchingEnvironment environment, @Nullable String argumentName, ResolvableType targetType)
113+
throws BindException {
106114

107-
Object rawValue = (name != null ? environment.getArgument(name) : environment.getArguments());
115+
Object rawValue = (argumentName != null ?
116+
environment.getArgument(argumentName) : environment.getArguments());
108117

109118
if (rawValue == null) {
110119
return wrapAsOptionalIfNecessary(null, targetType);
@@ -113,15 +122,15 @@ public Object initializeArgument(
113122
Class<?> targetClass = targetType.resolve();
114123
Assert.notNull(targetClass, "Could not determine target type from " + targetType);
115124

116-
DataBinder binder = new DataBinder(null, name != null ? name : "arguments");
125+
DataBinder binder = new DataBinder(null, argumentName != null ? argumentName : "arguments");
117126
BindingResult bindingResult = binder.getBindingResult();
118127
Stack<String> segments = new Stack<>();
119128

120129
try {
121130
// From Collection
122131

123132
if (CollectionFactory.isApproximableCollectionType(rawValue.getClass())) {
124-
segments.push(name);
133+
segments.push(argumentName);
125134
return createCollection((Collection<Object>) rawValue, targetType, bindingResult, segments);
126135
}
127136

spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/Argument.java

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,26 @@
2222
import java.lang.annotation.Target;
2323

2424
import org.springframework.core.annotation.AliasFor;
25+
import org.springframework.validation.BindException;
2526

2627
/**
27-
* Annotation to bind a method parameter to a GraphQL input
28-
* {@link graphql.schema.DataFetchingEnvironment#getArgument(String) argument}.
28+
* Annotation to bind a named GraphQL
29+
* {@link graphql.schema.DataFetchingEnvironment#getArgument(String) argument}
30+
* onto a method parameter.
2931
*
30-
* <p>If the method parameter is {@link java.util.Map Map&lt;String, Object&gt;}
31-
* and a parameter name is not specified, then the map parameter is populated
32-
* via {@link graphql.schema.DataFetchingEnvironment#getArguments()}.
32+
* <p>Binding is performed by mapping argument values to a primary data
33+
* constructor of the expected method parameter type, or by using a default
34+
* constructor to create it and then map values to its properties. This is
35+
* applied recursively, using all nested values and creating nested target
36+
* objects.
37+
*
38+
* <p>If binding fails, a {@link BindException} is raised with binding issues
39+
* accumulated as {@link BindException#getFieldErrors() field errors} where the
40+
* {@code field} of each error is the argument path where the issue occurred.
3341
*
34-
* <p>The target method parameter can be a higher-level, typed Object of any
35-
* type. It is created and initialized from the named field argument value(s),
36-
* either matching them to single data constructor parameters, or using the
37-
* default constructor and then matching keys onto Object properties through
38-
* a {@link org.springframework.validation.DataBinder}.
42+
* <p>If the method parameter is {@link java.util.Map Map&lt;String, Object&gt;}
43+
* and a parameter name is not specified, then the resolves value is the raw
44+
* {@link graphql.schema.DataFetchingEnvironment#getArguments() arguments} map.
3945
*
4046
* <p>Note that this annotation has neither a "required" flag nor the option to
4147
* specify a default value, both of which can be specified at the GraphQL schema

spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/Arguments.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,14 @@
2121
import java.lang.annotation.RetentionPolicy;
2222
import java.lang.annotation.Target;
2323

24+
import graphql.schema.DataFetchingEnvironment;
25+
2426
/**
25-
* Similar to {@link Argument @Argument} but using the full map of argument
26-
* values as the source of values to bind to the target Object.
27+
* Analogous to {@link Argument} but binding with the full
28+
* {@link DataFetchingEnvironment#getArgument(String) arguments} map.
2729
*
2830
* @author Rossen Stoyanchev
2931
* @since 1.0.0
30-
* @see Argument
3132
*/
3233
@Target(ElementType.PARAMETER)
3334
@Retention(RetentionPolicy.RUNTIME)

spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/AnnotatedControllerConfigurer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
import org.springframework.format.FormatterRegistrar;
5454
import org.springframework.format.support.DefaultFormattingConversionService;
5555
import org.springframework.format.support.FormattingConversionService;
56-
import org.springframework.graphql.data.GraphQlArgumentInitializer;
56+
import org.springframework.graphql.data.GraphQlArgumentBinder;
5757
import org.springframework.graphql.data.method.HandlerMethod;
5858
import org.springframework.graphql.data.method.HandlerMethodArgumentResolver;
5959
import org.springframework.graphql.data.method.HandlerMethodArgumentResolverComposite;
@@ -159,7 +159,7 @@ public void afterPropertiesSet() {
159159
this.argumentResolvers.addResolver(new ProjectedPayloadMethodArgumentResolver(obtainApplicationContext()));
160160
}
161161
this.argumentResolvers.addResolver(new ArgumentMapMethodArgumentResolver());
162-
GraphQlArgumentInitializer initializer = new GraphQlArgumentInitializer(this.conversionService);
162+
GraphQlArgumentBinder initializer = new GraphQlArgumentBinder(this.conversionService);
163163
this.argumentResolvers.addResolver(new ArgumentMethodArgumentResolver(initializer));
164164
this.argumentResolvers.addResolver(new ArgumentsMethodArgumentResolver(initializer));
165165
this.argumentResolvers.addResolver(new ContextValueMethodArgumentResolver());

spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentMapMethodArgumentResolver.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2021 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16+
1617
package org.springframework.graphql.data.method.annotation.support;
1718

1819
import java.util.Map;
@@ -24,9 +25,11 @@
2425
import org.springframework.graphql.data.method.annotation.Argument;
2526
import org.springframework.util.StringUtils;
2627

28+
2729
/**
28-
* Resolves {@link Map} method arguments annotated with an @{@link Argument}
29-
* where the annotation does not specify an argument name.
30+
* Resolves a {@link Map} method parameter annotated with an
31+
* {@link Argument @Argument} by returning the GraphQL
32+
* {@link DataFetchingEnvironment#getArguments() arguments} map.
3033
*
3134
* @author Rossen Stoyanchev
3235
* @since 1.0.0

spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentMethodArgumentResolver.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
import org.springframework.core.MethodParameter;
2121
import org.springframework.core.ResolvableType;
22-
import org.springframework.graphql.data.GraphQlArgumentInitializer;
22+
import org.springframework.graphql.data.GraphQlArgumentBinder;
2323
import org.springframework.graphql.data.method.HandlerMethodArgumentResolver;
2424
import org.springframework.graphql.data.method.annotation.Argument;
2525
import org.springframework.util.Assert;
@@ -33,28 +33,29 @@
3333
* @author Rossen Stoyanchev
3434
* @author Brian Clozel
3535
* @since 1.0.0
36+
* @see Argument
3637
*/
3738
public class ArgumentMethodArgumentResolver implements HandlerMethodArgumentResolver {
3839

39-
private final GraphQlArgumentInitializer argumentInitializer;
40+
private final GraphQlArgumentBinder argumentInitializer;
4041

4142

42-
public ArgumentMethodArgumentResolver(GraphQlArgumentInitializer initializer) {
43+
public ArgumentMethodArgumentResolver(GraphQlArgumentBinder initializer) {
4344
Assert.notNull(initializer, "GraphQlArgumentInitializer is required");
4445
this.argumentInitializer = initializer;
4546
}
4647

4748

4849
@Override
4950
public boolean supportsParameter(MethodParameter parameter) {
50-
return parameter.getParameterAnnotation(Argument.class) != null;
51+
return (parameter.getParameterAnnotation(Argument.class) != null);
5152
}
5253

5354
@Override
5455
public Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) throws Exception {
5556
String name = getArgumentName(parameter);
5657
ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
57-
return this.argumentInitializer.initializeArgument(environment, name, resolvableType);
58+
return this.argumentInitializer.bind(environment, name, resolvableType);
5859
}
5960

6061
static String getArgumentName(MethodParameter parameter) {

spring-graphql/src/main/java/org/springframework/graphql/data/method/annotation/support/ArgumentsMethodArgumentResolver.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,25 +19,25 @@
1919

2020
import org.springframework.core.MethodParameter;
2121
import org.springframework.core.ResolvableType;
22-
import org.springframework.graphql.data.GraphQlArgumentInitializer;
22+
import org.springframework.graphql.data.GraphQlArgumentBinder;
2323
import org.springframework.graphql.data.method.HandlerMethodArgumentResolver;
2424
import org.springframework.graphql.data.method.annotation.Arguments;
2525
import org.springframework.util.Assert;
2626

2727
/**
28-
* Resolver for {@link Arguments @Arguments} annotated method parameters,
29-
* obtained via {@link DataFetchingEnvironment#getArgument(String)} and
30-
* converted to the declared type of the method parameter.
28+
* Analogous to {@link ArgumentMethodArgumentResolver} but resolving method
29+
* parameters annotated with {@link Arguments @Arguments} and binding with the
30+
* full {@link DataFetchingEnvironment#getArgument(String) arguments} map.
3131
*
3232
* @author Rossen Stoyanchev
3333
* @since 1.0.0
3434
*/
3535
public class ArgumentsMethodArgumentResolver implements HandlerMethodArgumentResolver {
3636

37-
private final GraphQlArgumentInitializer argumentInitializer;
37+
private final GraphQlArgumentBinder argumentInitializer;
3838

3939

40-
public ArgumentsMethodArgumentResolver(GraphQlArgumentInitializer initializer) {
40+
public ArgumentsMethodArgumentResolver(GraphQlArgumentBinder initializer) {
4141
Assert.notNull(initializer, "GraphQlArgumentInitializer is required");
4242
this.argumentInitializer = initializer;
4343
}
@@ -51,7 +51,7 @@ public boolean supportsParameter(MethodParameter parameter) {
5151
@Override
5252
public Object resolveArgument(MethodParameter parameter, DataFetchingEnvironment environment) throws Exception {
5353
ResolvableType resolvableType = ResolvableType.forMethodParameter(parameter);
54-
return this.argumentInitializer.initializeArgument(environment, null, resolvableType);
54+
return this.argumentInitializer.bind(environment, null, resolvableType);
5555
}
5656

5757
}

spring-graphql/src/main/java/org/springframework/graphql/data/query/QueryByExampleDataFetcher.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
import org.springframework.data.repository.query.ReactiveQueryByExampleExecutor;
3939
import org.springframework.data.util.ClassTypeInformation;
4040
import org.springframework.data.util.TypeInformation;
41-
import org.springframework.graphql.data.GraphQlArgumentInitializer;
41+
import org.springframework.graphql.data.GraphQlArgumentBinder;
4242
import org.springframework.graphql.data.GraphQlRepository;
4343
import org.springframework.graphql.execution.RuntimeWiringConfigurer;
4444
import org.springframework.util.Assert;
@@ -92,12 +92,12 @@ public abstract class QueryByExampleDataFetcher<T> {
9292

9393
private final TypeInformation<T> domainType;
9494

95-
private final GraphQlArgumentInitializer argumentInitializer;
95+
private final GraphQlArgumentBinder argumentInitializer;
9696

9797

9898
QueryByExampleDataFetcher(TypeInformation<T> domainType) {
9999
this.domainType = domainType;
100-
this.argumentInitializer = new GraphQlArgumentInitializer(null);
100+
this.argumentInitializer = new GraphQlArgumentBinder();
101101
}
102102

103103

@@ -109,7 +109,7 @@ public abstract class QueryByExampleDataFetcher<T> {
109109
@SuppressWarnings({"ConstantConditions", "unchecked"})
110110
protected Example<T> buildExample(DataFetchingEnvironment env) throws BindException {
111111
ResolvableType targetType = ResolvableType.forClass(this.domainType.getType());
112-
return (Example<T>) Example.of(this.argumentInitializer.initializeArgument(env, null, targetType));
112+
return (Example<T>) Example.of(this.argumentInitializer.bind(env, null, targetType));
113113
}
114114

115115
protected boolean requiresProjection(Class<?> resultType) {

spring-graphql/src/test/java/org/springframework/graphql/data/method/annotation/support/ArgumentMethodArgumentResolverTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
import org.springframework.core.MethodParameter;
2525
import org.springframework.format.support.DefaultFormattingConversionService;
2626
import org.springframework.graphql.Book;
27-
import org.springframework.graphql.data.GraphQlArgumentInitializer;
27+
import org.springframework.graphql.data.GraphQlArgumentBinder;
2828
import org.springframework.graphql.data.method.HandlerMethodArgumentResolver;
2929
import org.springframework.graphql.data.method.annotation.Argument;
3030
import org.springframework.graphql.data.method.annotation.MutationMapping;
@@ -40,7 +40,7 @@
4040
class ArgumentMethodArgumentResolverTests extends ArgumentResolverTestSupport {
4141

4242
private final HandlerMethodArgumentResolver resolver = new ArgumentMethodArgumentResolver(
43-
new GraphQlArgumentInitializer(new DefaultFormattingConversionService()));
43+
new GraphQlArgumentBinder(new DefaultFormattingConversionService()));
4444

4545

4646
@Test

0 commit comments

Comments
 (0)