@@ -79,14 +79,16 @@ public class GraphQlArgumentBinder {
79
79
80
80
private final @ Nullable SimpleTypeConverter typeConverter ;
81
81
82
+ private final @ Nullable NameResolver nameResolver ;
83
+
82
84
private final boolean fallBackOnDirectFieldAccess ;
83
85
84
86
85
87
/**
86
88
* Default constructor.
87
89
*/
88
90
public GraphQlArgumentBinder () {
89
- this (( Options ) null );
91
+ this (Options . create () );
90
92
}
91
93
92
94
/**
@@ -96,7 +98,7 @@ public GraphQlArgumentBinder() {
96
98
*/
97
99
@ Deprecated (since = "2.0" , forRemoval = true )
98
100
public GraphQlArgumentBinder (@ Nullable ConversionService conversionService ) {
99
- this (conversionService , false );
101
+ this (Options . create (). conversionService ( conversionService ) );
100
102
}
101
103
102
104
/**
@@ -107,13 +109,13 @@ public GraphQlArgumentBinder(@Nullable ConversionService conversionService) {
107
109
*/
108
110
@ Deprecated (since = "2.0" , forRemoval = true )
109
111
public GraphQlArgumentBinder (@ Nullable ConversionService service , boolean fallBackOnDirectFieldAccess ) {
110
- this .typeConverter = initTypeConverter (service );
111
- this .fallBackOnDirectFieldAccess = fallBackOnDirectFieldAccess ;
112
+ this (Options .create ().conversionService (service ).fallBackOnDirectFieldAccess (fallBackOnDirectFieldAccess ));
112
113
}
113
114
114
- public GraphQlArgumentBinder (@ Nullable Options options ) {
115
- this .typeConverter = ((options != null ) ? initTypeConverter (options .conversionService ()) : null );
116
- this .fallBackOnDirectFieldAccess = (options != null && options .fallBackOnDirectFieldAccess ());
115
+ public GraphQlArgumentBinder (Options options ) {
116
+ this .typeConverter = initTypeConverter (options .conversionService ());
117
+ this .nameResolver = options .nameResolver ();
118
+ this .fallBackOnDirectFieldAccess = options .fallBackOnDirectFieldAccess ();
117
119
}
118
120
119
121
private static @ Nullable SimpleTypeConverter initTypeConverter (@ Nullable ConversionService service ) {
@@ -198,6 +200,10 @@ public GraphQlArgumentBinder(@Nullable Options options) {
198
200
Assert .state (targetClass != null , "Could not resolve target type for: " + targetType );
199
201
}
200
202
203
+ if (this .nameResolver != null ) {
204
+ name = this .nameResolver .resolveName (name );
205
+ }
206
+
201
207
Object value ;
202
208
if (rawValue == null || targetClass == Object .class ) {
203
209
value = rawValue ;
@@ -302,8 +308,21 @@ private Map<?, Object> bindMapToMap(
302
308
ResolvableType targetType = ResolvableType .forType (
303
309
ResolvableType .forConstructorParameter (constructor , i ).getType (), ownerType );
304
310
311
+ Object rawValue = rawMap .get (name );
312
+ boolean isNotPresent = !rawMap .containsKey (name );
313
+
314
+ if (rawValue == null && this .nameResolver != null ) {
315
+ for (String key : rawMap .keySet ()) {
316
+ if (this .nameResolver .resolveName (key ).equals (name )) {
317
+ rawValue = rawMap .get (key );
318
+ isNotPresent = false ;
319
+ break ;
320
+ }
321
+ }
322
+ }
323
+
305
324
constructorArguments [i ] = bindRawValue (
306
- name , rawMap . get ( name ), ! rawMap . containsKey ( name ) , targetType , paramTypes [i ], bindingResult );
325
+ name , rawValue , isNotPresent , targetType , paramTypes [i ], bindingResult );
307
326
}
308
327
309
328
Object target ;
@@ -342,6 +361,9 @@ private void bindViaSetters(Object target,
342
361
343
362
for (Map .Entry <String , Object > entry : rawMap .entrySet ()) {
344
363
String key = entry .getKey ();
364
+ if (this .nameResolver != null ) {
365
+ key = this .nameResolver .resolveName (key );
366
+ }
345
367
TypeDescriptor typeDescriptor = beanWrapper .getPropertyTypeDescriptor (key );
346
368
if (typeDescriptor == null && this .fallBackOnDirectFieldAccess ) {
347
369
Field field = ReflectionUtils .findField (beanWrapper .getWrappedClass (), key );
@@ -403,10 +425,15 @@ public static final class Options {
403
425
404
426
private final @ Nullable ConversionService conversionService ;
405
427
428
+ private final @ Nullable NameResolver nameResolver ;
429
+
406
430
private final boolean fallBackOnDirectFieldAccess ;
407
431
408
- private Options (@ Nullable ConversionService conversionService , boolean fallBackOnDirectFieldAccess ) {
432
+ private Options (@ Nullable ConversionService conversionService , @ Nullable NameResolver nameResolver ,
433
+ boolean fallBackOnDirectFieldAccess ) {
434
+
409
435
this .conversionService = conversionService ;
436
+ this .nameResolver = nameResolver ;
410
437
this .fallBackOnDirectFieldAccess = fallBackOnDirectFieldAccess ;
411
438
}
412
439
@@ -416,7 +443,16 @@ private Options(@Nullable ConversionService conversionService, boolean fallBackO
416
443
* @param service the service to use
417
444
*/
418
445
public Options conversionService (@ Nullable ConversionService service ) {
419
- return new Options (service , this .fallBackOnDirectFieldAccess );
446
+ return new Options (service , this .nameResolver , this .fallBackOnDirectFieldAccess );
447
+ }
448
+
449
+ /**
450
+ * Add a resolver to help to map GraphQL argument names to Object property names.
451
+ * @param resolver the resolver to add
452
+ */
453
+ public Options nameResolver (NameResolver resolver ) {
454
+ resolver = ((this .nameResolver != null ) ? this .nameResolver .andThen (resolver ) : resolver );
455
+ return new Options (this .conversionService , resolver , this .fallBackOnDirectFieldAccess );
420
456
}
421
457
422
458
/**
@@ -427,13 +463,17 @@ public Options conversionService(@Nullable ConversionService service) {
427
463
* @param fallBackOnDirectFieldAccess whether to fall back on direct field access
428
464
*/
429
465
public Options fallBackOnDirectFieldAccess (boolean fallBackOnDirectFieldAccess ) {
430
- return new Options (this .conversionService , fallBackOnDirectFieldAccess );
466
+ return new Options (this .conversionService , this . nameResolver , fallBackOnDirectFieldAccess );
431
467
}
432
468
433
469
public @ Nullable ConversionService conversionService () {
434
470
return this .conversionService ;
435
471
}
436
472
473
+ public @ Nullable NameResolver nameResolver () {
474
+ return this .nameResolver ;
475
+ }
476
+
437
477
public boolean fallBackOnDirectFieldAccess () {
438
478
return this .fallBackOnDirectFieldAccess ;
439
479
}
@@ -442,7 +482,33 @@ public boolean fallBackOnDirectFieldAccess() {
442
482
* Create an instance without any options set.
443
483
*/
444
484
public static Options create () {
445
- return new Options (null , false );
485
+ return new Options (null , (name ) -> name , false );
486
+ }
487
+ }
488
+
489
+
490
+ /**
491
+ * Contract to customize the mapping of GraphQL argument names to Object
492
+ * properties. This can be useful for dealing with naming conventions like
493
+ * the use of "-" that cannot be used in Java property names.
494
+ * @since 2.0.0
495
+ */
496
+ public interface NameResolver {
497
+
498
+ /**
499
+ * Resolve the given GraphQL argument name to an Object property name.
500
+ * @param name the argument name
501
+ * @return the resolved name to use
502
+ */
503
+ String resolveName (String name );
504
+
505
+ /**
506
+ * Append another resolver to be invoked after the current one.
507
+ * @param resolver the resolver to invoked
508
+ * @return a new composite resolver
509
+ */
510
+ default NameResolver andThen (NameResolver resolver ) {
511
+ return (name ) -> resolver .resolveName (resolveName (name ));
446
512
}
447
513
}
448
514
0 commit comments