Skip to content

Commit aec5f22

Browse files
committed
Introduce GraphQlArgumentBinder.Options
Container for the growing list of configuration options to avoid having to declare and pass them individually from AnnotatedControllerConfigurer and FederationSchemaFactory. See gh-1146
1 parent 289ddd2 commit aec5f22

File tree

7 files changed

+130
-22
lines changed

7 files changed

+130
-22
lines changed

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

Lines changed: 79 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -82,31 +82,51 @@ public class GraphQlArgumentBinder {
8282
private final boolean fallBackOnDirectFieldAccess;
8383

8484

85+
/**
86+
* Default constructor.
87+
*/
8588
public GraphQlArgumentBinder() {
86-
this(null);
89+
this((Options) null);
8790
}
8891

92+
/**
93+
* Constructor with additional flag for direct field access support.
94+
* @param conversionService the service to use
95+
* @deprecated in favor of {@link #GraphQlArgumentBinder(Options)}
96+
*/
97+
@Deprecated(since = "2.0", forRemoval = true)
8998
public GraphQlArgumentBinder(@Nullable ConversionService conversionService) {
9099
this(conversionService, false);
91100
}
92101

93-
public GraphQlArgumentBinder(@Nullable ConversionService conversionService, boolean fallBackOnDirectFieldAccess) {
94-
this.typeConverter = initTypeConverter(conversionService);
102+
/**
103+
* Constructor with additional flag for direct field access support.
104+
* @param service the service to use
105+
* @param fallBackOnDirectFieldAccess whether to fall back on direct field access
106+
* @deprecated in favor of {@link #GraphQlArgumentBinder(Options)}
107+
*/
108+
@Deprecated(since = "2.0", forRemoval = true)
109+
public GraphQlArgumentBinder(@Nullable ConversionService service, boolean fallBackOnDirectFieldAccess) {
110+
this.typeConverter = initTypeConverter(service);
95111
this.fallBackOnDirectFieldAccess = fallBackOnDirectFieldAccess;
96112
}
97113

98-
private static @Nullable SimpleTypeConverter initTypeConverter(@Nullable ConversionService conversionService) {
99-
if (conversionService == null) {
114+
public GraphQlArgumentBinder(@Nullable Options options) {
115+
this.typeConverter = ((options != null) ? initTypeConverter(options.conversionService()) : null);
116+
this.fallBackOnDirectFieldAccess = (options != null && options.fallBackOnDirectFieldAccess());
117+
}
118+
119+
private static @Nullable SimpleTypeConverter initTypeConverter(@Nullable ConversionService service) {
120+
if (service == null) {
100121
// Not thread-safe when using PropertyEditors
101122
return null;
102123
}
103124
SimpleTypeConverter typeConverter = new SimpleTypeConverter();
104-
typeConverter.setConversionService(conversionService);
125+
typeConverter.setConversionService(service);
105126
return typeConverter;
106127
}
107128

108129

109-
110130
/**
111131
* Create and populate an Object of the given target type, from a single
112132
* GraphQL argument, or from the full GraphQL arguments map.
@@ -375,6 +395,58 @@ private void bindViaSetters(Object target,
375395
}
376396

377397

398+
/**
399+
* Container of configuration settings for {@link GraphQlArgumentBinder}.
400+
* @since 2.0.0
401+
*/
402+
public static final class Options {
403+
404+
private final @Nullable ConversionService conversionService;
405+
406+
private final boolean fallBackOnDirectFieldAccess;
407+
408+
private Options(@Nullable ConversionService conversionService, boolean fallBackOnDirectFieldAccess) {
409+
this.conversionService = conversionService;
410+
this.fallBackOnDirectFieldAccess = fallBackOnDirectFieldAccess;
411+
}
412+
413+
/**
414+
* Add a {@link ConversionService} to apply type conversion to argument
415+
* values where needed.
416+
* @param service the service to use
417+
*/
418+
public Options conversionService(@Nullable ConversionService service) {
419+
return new Options(service, this.fallBackOnDirectFieldAccess);
420+
}
421+
422+
/**
423+
* Whether binding GraphQL arguments onto
424+
* {@link org.springframework.graphql.data.method.annotation.Argument @Argument}
425+
* should falls back to direct field access in case the target object does
426+
* not use accessor methods.
427+
* @param fallBackOnDirectFieldAccess whether to fall back on direct field access
428+
*/
429+
public Options fallBackOnDirectFieldAccess(boolean fallBackOnDirectFieldAccess) {
430+
return new Options(this.conversionService, fallBackOnDirectFieldAccess);
431+
}
432+
433+
public @Nullable ConversionService conversionService() {
434+
return this.conversionService;
435+
}
436+
437+
public boolean fallBackOnDirectFieldAccess() {
438+
return this.fallBackOnDirectFieldAccess;
439+
}
440+
441+
/**
442+
* Create an instance without any options set.
443+
*/
444+
public static Options create() {
445+
return new Options(null, false);
446+
}
447+
}
448+
449+
378450
/**
379451
* Subclass of {@link AbstractBindingResult} that doesn't have a target Object,
380452
* and takes the raw value as input when recording errors.

spring-graphql/src/main/java/org/springframework/graphql/data/federation/FederationSchemaFactory.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,7 @@ protected HandlerMethodArgumentResolverComposite initArgumentResolvers() {
117117

118118
HandlerMethodArgumentResolverComposite resolvers = new HandlerMethodArgumentResolverComposite();
119119

120-
GraphQlArgumentBinder argumentBinder =
121-
new GraphQlArgumentBinder(getConversionService(), isFallBackOnDirectFieldAccess());
120+
GraphQlArgumentBinder argumentBinder = new GraphQlArgumentBinder(getBinderOptions());
122121

123122
// Annotation based
124123
resolvers.addResolver(new ContextValueMethodArgumentResolver());

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,7 @@ protected HandlerMethodArgumentResolverComposite initArgumentResolvers() {
169169
resolvers.addResolver(new ProjectedPayloadMethodArgumentResolver(obtainApplicationContext()));
170170
}
171171

172-
GraphQlArgumentBinder argumentBinder =
173-
new GraphQlArgumentBinder(getConversionService(), isFallBackOnDirectFieldAccess());
172+
GraphQlArgumentBinder argumentBinder = new GraphQlArgumentBinder(getBinderOptions());
174173

175174
resolvers.addResolver(new ArgumentMethodArgumentResolver(argumentBinder));
176175
resolvers.addResolver(new ArgumentsMethodArgumentResolver(argumentBinder));

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

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import java.util.Set;
2626
import java.util.concurrent.Callable;
2727
import java.util.concurrent.Executor;
28+
import java.util.function.Consumer;
2829
import java.util.function.Predicate;
2930

3031
import graphql.schema.DataFetcher;
@@ -44,6 +45,7 @@
4445
import org.springframework.format.FormatterRegistrar;
4546
import org.springframework.format.support.DefaultFormattingConversionService;
4647
import org.springframework.format.support.FormattingConversionService;
48+
import org.springframework.graphql.data.GraphQlArgumentBinder;
4749
import org.springframework.graphql.data.method.HandlerMethod;
4850
import org.springframework.graphql.data.method.HandlerMethodArgumentResolverComposite;
4951
import org.springframework.graphql.execution.DataFetcherExceptionResolver;
@@ -86,9 +88,8 @@ public abstract class AnnotatedControllerDetectionSupport<M> implements Applicat
8688
protected final Log logger = LogFactory.getLog(getClass());
8789

8890

89-
private final FormattingConversionService conversionService = new DefaultFormattingConversionService();
90-
91-
private boolean fallBackOnDirectFieldAccess;
91+
private GraphQlArgumentBinder.Options binderOptions = GraphQlArgumentBinder.Options.create()
92+
.conversionService(new DefaultFormattingConversionService());
9293

9394
private @Nullable AnnotatedControllerExceptionResolver exceptionResolver;
9495

@@ -102,19 +103,51 @@ public abstract class AnnotatedControllerDetectionSupport<M> implements Applicat
102103
private @Nullable ApplicationContext applicationContext;
103104

104105

106+
/**
107+
* Callback to configure options for binding GraphQL arguments to target objects.
108+
* @param binderOptionsConsumer consumer to customize options with
109+
* @since 2.0.0
110+
*/
111+
public void configureBinder(Consumer<GraphQlArgumentBinder.Options> binderOptionsConsumer) {
112+
binderOptionsConsumer.accept(this.binderOptions);
113+
}
114+
115+
/**
116+
* Set the options to use for binding GraphQL arguments to target objects.
117+
* @param binderOptions the options to use
118+
* @since 2.0.0
119+
*/
120+
public void setBinderOptions(GraphQlArgumentBinder.Options binderOptions) {
121+
this.binderOptions = binderOptions;
122+
}
123+
124+
protected GraphQlArgumentBinder.Options getBinderOptions() {
125+
return this.binderOptions;
126+
}
127+
105128
/**
106129
* Add a {@code FormatterRegistrar} to customize the {@link ConversionService}
107130
* that assists in binding GraphQL arguments onto
108131
* {@link org.springframework.graphql.data.method.annotation.Argument @Argument}
109132
* annotated method parameters.
110133
* @param registrar the formatter registrar
134+
* @deprecated in favor of {@link #configureBinder(Consumer)}
111135
*/
136+
@Deprecated(since = "2.0", forRemoval = true)
112137
public void addFormatterRegistrar(FormatterRegistrar registrar) {
113-
registrar.registerFormatters(this.conversionService);
138+
if (this.binderOptions.conversionService() instanceof FormattingConversionService fcs) {
139+
registrar.registerFormatters(fcs);
140+
return;
141+
}
142+
throw new IllegalStateException("Expected FormattingConversionService");
114143
}
115144

145+
@Deprecated(since = "2.0", forRemoval = true)
116146
protected FormattingConversionService getConversionService() {
117-
return this.conversionService;
147+
if (this.binderOptions.conversionService() instanceof FormattingConversionService fcs) {
148+
return fcs;
149+
}
150+
throw new IllegalStateException("Expected FormattingConversionService");
118151
}
119152

120153
/**
@@ -123,14 +156,16 @@ protected FormattingConversionService getConversionService() {
123156
* should falls back to direct field access in case the target object does
124157
* not use accessor methods.
125158
* @param fallBackOnDirectFieldAccess whether binding should fall back on direct field access
126-
* @since 1.2.0
159+
* @deprecated in favor of {@link #configureBinder(Consumer)}
127160
*/
161+
@Deprecated(since = "2.0", forRemoval = true)
128162
public void setFallBackOnDirectFieldAccess(boolean fallBackOnDirectFieldAccess) {
129-
this.fallBackOnDirectFieldAccess = fallBackOnDirectFieldAccess;
163+
this.binderOptions = this.binderOptions.fallBackOnDirectFieldAccess(fallBackOnDirectFieldAccess);
130164
}
131165

166+
@Deprecated(since = "2.0", forRemoval = true)
132167
protected boolean isFallBackOnDirectFieldAccess() {
133-
return this.fallBackOnDirectFieldAccess;
168+
return this.binderOptions.fallBackOnDirectFieldAccess();
134169
}
135170

136171
/**

spring-graphql/src/test/java/org/springframework/graphql/data/GraphQlArgumentBinderTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ class GraphQlArgumentBinderTests {
5757

5858
private final ObjectMapper mapper = new ObjectMapper();
5959

60-
private final GraphQlArgumentBinder binder = new GraphQlArgumentBinder(new DefaultFormattingConversionService());
60+
private final GraphQlArgumentBinder binder = new GraphQlArgumentBinder(
61+
GraphQlArgumentBinder.Options.create().conversionService(new DefaultFormattingConversionService()));
6162

6263

6364
@Test

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@
4242
class ArgumentMethodArgumentResolverTests extends ArgumentResolverTestSupport {
4343

4444
private final HandlerMethodArgumentResolver resolver = new ArgumentMethodArgumentResolver(
45-
new GraphQlArgumentBinder(new DefaultFormattingConversionService()));
45+
new GraphQlArgumentBinder(GraphQlArgumentBinder.Options.create()
46+
.conversionService(new DefaultFormattingConversionService())));
4647

4748

4849
@Test

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@
4242
class ArgumentsMethodArgumentResolverTests extends ArgumentResolverTestSupport {
4343

4444
private final HandlerMethodArgumentResolver resolver = new ArgumentsMethodArgumentResolver(
45-
new GraphQlArgumentBinder(new DefaultFormattingConversionService()));
45+
new GraphQlArgumentBinder(GraphQlArgumentBinder.Options.create()
46+
.conversionService(new DefaultFormattingConversionService())));
4647

4748

4849
@Test

0 commit comments

Comments
 (0)