17
17
18
18
import java .math .BigDecimal ;
19
19
import java .math .BigInteger ;
20
+ import java .nio .charset .StandardCharsets ;
20
21
import java .text .DateFormat ;
21
22
import java .text .ParseException ;
23
+ import java .text .SimpleDateFormat ;
22
24
import java .time .Instant ;
23
25
import java .time .LocalDate ;
24
26
import java .time .LocalDateTime ;
25
27
import java .time .format .DateTimeParseException ;
28
+ import java .util .Collections ;
26
29
import java .util .Date ;
27
30
import java .util .HashMap ;
31
+ import java .util .LinkedHashMap ;
32
+ import java .util .List ;
33
+ import java .util .Map ;
28
34
import java .util .TimeZone ;
29
35
import java .util .UUID ;
36
+ import java .util .stream .Collectors ;
30
37
31
38
import org .slf4j .Logger ;
32
39
import org .slf4j .LoggerFactory ;
33
40
34
41
import graphql .Assert ;
35
42
import graphql .Scalars ;
43
+ import graphql .language .ArrayValue ;
44
+ import graphql .language .BooleanValue ;
45
+ import graphql .language .EnumValue ;
46
+ import graphql .language .FloatValue ;
36
47
import graphql .language .IntValue ;
48
+ import graphql .language .NullValue ;
49
+ import graphql .language .ObjectField ;
50
+ import graphql .language .ObjectValue ;
37
51
import graphql .language .StringValue ;
52
+ import graphql .language .Value ;
38
53
import graphql .schema .Coercing ;
39
54
import graphql .schema .GraphQLScalarType ;
40
55
@@ -49,6 +64,8 @@ public class JavaScalars {
49
64
static final Logger log = LoggerFactory .getLogger (JavaScalars .class );
50
65
51
66
private static HashMap <Class <?>, GraphQLScalarType > scalarsRegistry = new HashMap <Class <?>, GraphQLScalarType >();
67
+
68
+ private static JavaScalars instance = new JavaScalars ();
52
69
53
70
static {
54
71
scalarsRegistry .put (String .class , Scalars .GraphQLString );
@@ -86,21 +103,24 @@ public class JavaScalars {
86
103
scalarsRegistry .put (Date .class , new GraphQLScalarType ("Date" , "Date type" , new GraphQLDateCoercing ()));
87
104
scalarsRegistry .put (UUID .class , new GraphQLScalarType ("UUID" , "UUID type" , new GraphQLUUIDCoercing ()));
88
105
scalarsRegistry .put (Object .class , new GraphQLScalarType ("Object" , "Object type" , new GraphQLObjectCoercing ()));
106
+ scalarsRegistry .put (java .sql .Date .class , new GraphQLScalarType ("SqlDate" , "SQL Date type" , new GraphQLSqlDateCoercing ()));
107
+ scalarsRegistry .put (java .sql .Timestamp .class , new GraphQLScalarType ("SqlTimestamp" , "SQL Timestamp type" , new GraphQLSqlTimestampCoercing ()));
108
+ scalarsRegistry .put (Byte [].class , new GraphQLScalarType ("ByteArray" , "ByteArray type" , new GraphQLLOBCoercing ()));
89
109
}
90
110
91
111
public static GraphQLScalarType of (Class <?> key ) {
92
112
return scalarsRegistry .get (key );
93
113
}
94
114
95
- public JavaScalars register (Class <?> key , GraphQLScalarType value ) {
115
+ public static JavaScalars register (Class <?> key , GraphQLScalarType value ) {
96
116
Assert .assertNotNull (key , "key parameter cannot be null." );
97
117
Assert .assertNotNull (value , "value parameter cannot be null." );
98
118
99
119
scalarsRegistry .put (key , value );
100
120
101
- return this ;
121
+ return instance ;
102
122
}
103
-
123
+
104
124
public static class GraphQLLocalDateTimeCoercing implements Coercing <Object , Object > {
105
125
106
126
@ Override
@@ -227,7 +247,7 @@ public Object parseLiteral(Object input) {
227
247
228
248
private Date parseStringToDate (String input ) {
229
249
try {
230
- return DateFormat . getInstance ( ).parse (input );
250
+ return new SimpleDateFormat ( "yyyy-MM-dd" ).parse (input );
231
251
} catch (ParseException e ) {
232
252
log .warn ("Failed to parse Date from input: " + input , e );
233
253
return null ;
@@ -271,7 +291,7 @@ private UUID parseStringToUUID(String input) {
271
291
}
272
292
};
273
293
274
- public static class GraphQLObjectCoercing implements Coercing <Object , Object > {
294
+ public static class GraphQLRawObjectCoercing implements Coercing <Object , Object > {
275
295
276
296
@ Override
277
297
public Object serialize (Object input ) {
@@ -288,5 +308,178 @@ public Object parseLiteral(Object input) {
288
308
return input ;
289
309
}
290
310
};
311
+
312
+ public static class GraphQLSqlDateCoercing implements Coercing <Object , Object > {
313
+ @ Override
314
+ public Object serialize (Object input ) {
315
+ if (input instanceof String ) {
316
+ return parseStringToDate ((String ) input );
317
+ } else if (input instanceof Date ) {
318
+ return new java .sql .Date (((Date ) input ).getTime ());
319
+ } else if (input instanceof Long ) {
320
+ return new java .sql .Date (((Long ) input ).longValue ());
321
+ } else if (input instanceof Integer ) {
322
+ return new java .sql .Date (((Integer ) input ).longValue ());
323
+ }
324
+ return null ;
325
+ }
326
+ @ Override
327
+ public Object parseValue (Object input ) {
328
+ return serialize (input );
329
+ }
330
+ @ Override
331
+ public Object parseLiteral (Object input ) {
332
+ if (input instanceof StringValue ) {
333
+ return parseStringToDate (((StringValue ) input ).getValue ());
334
+ } else if (input instanceof IntValue ) {
335
+ BigInteger value = ((IntValue ) input ).getValue ();
336
+ return new java .sql .Date (value .longValue ());
337
+ }
338
+ return null ;
339
+ }
340
+ private java .sql .Date parseStringToDate (String input ) {
341
+ try {
342
+ return new java .sql .Date (DateFormat .getInstance ().parse (input ).getTime ());
343
+ } catch (ParseException e ) {
344
+ log .warn ("Failed to parse SQL Date from input: " + input , e );
345
+ return null ;
346
+ }
347
+ }
348
+ }
349
+ public static class GraphQLSqlTimestampCoercing implements Coercing <Object , Object > {
350
+ @ Override
351
+ public Object serialize (Object input ) {
352
+ if (input instanceof String ) {
353
+ return parseStringToTimestamp ((String ) input );
354
+ } else if (input instanceof Date ) {
355
+ return new java .sql .Timestamp (((Date ) input ).getTime ());
356
+ } else if (input instanceof Long ) {
357
+ return new java .sql .Timestamp (((Long ) input ).longValue ());
358
+ } else if (input instanceof Integer ) {
359
+ return new java .sql .Timestamp (((Integer ) input ).longValue ());
360
+ }
361
+ return null ;
362
+ }
363
+ @ Override
364
+ public Object parseValue (Object input ) {
365
+ return serialize (input );
366
+ }
367
+ @ Override
368
+ public Object parseLiteral (Object input ) {
369
+ if (input instanceof StringValue ) {
370
+ return parseStringToTimestamp (((StringValue ) input ).getValue ());
371
+ } else if (input instanceof IntValue ) {
372
+ BigInteger value = ((IntValue ) input ).getValue ();
373
+ return new java .sql .Date (value .longValue ());
374
+ }
375
+ return null ;
376
+ }
377
+ private java .sql .Timestamp parseStringToTimestamp (String input ) {
378
+ try {
379
+ return new java .sql .Timestamp (DateFormat .getInstance ().parse (input ).getTime ());
380
+ } catch (ParseException e ) {
381
+ log .warn ("Failed to parse Timestamp from input: " + input , e );
382
+ return null ;
383
+ }
384
+ }
385
+ }
386
+ public static class GraphQLLOBCoercing implements Coercing <Object , Object > {
387
+ @ Override
388
+ public Object serialize (Object input ) {
389
+ if (input .getClass () == byte [].class ) {
390
+ return input ;
391
+ }
392
+ return null ;
393
+ }
394
+ @ Override
395
+ public Object parseValue (Object input ) {
396
+ if (input instanceof String ) {
397
+ return parseStringToByteArray ((String ) input );
398
+ }
399
+ return null ;
400
+ }
401
+ @ Override
402
+ public Object parseLiteral (Object input ) {
403
+ if (input instanceof StringValue ) {
404
+ return parseStringToByteArray (((StringValue ) input ).getValue ());
405
+ }
406
+ return null ;
407
+ }
408
+ private byte [] parseStringToByteArray (String input ) {
409
+ try {
410
+ return input .getBytes (StandardCharsets .UTF_8 );
411
+ } catch (IllegalArgumentException e ) {
412
+ log .warn ("Failed to parse byte[] from input: " + input , e );
413
+ return null ;
414
+ }
415
+ }
416
+ }
417
+
418
+ public static class GraphQLObjectCoercing implements Coercing <Object , Object > {
419
+
420
+ @ Override
421
+ public Object serialize (Object dataFetcherResult ) {
422
+ return dataFetcherResult ;
423
+ }
424
+
425
+ @ Override
426
+ public Object parseValue (Object input ) {
427
+ return input ;
428
+ }
429
+
430
+ @ Override
431
+ public Object parseLiteral (Object input ) {
432
+ return parseFieldValue ((Value ) input , Collections .emptyMap ());
433
+ }
434
+
435
+ //recursively parse the input into a Map
436
+ private Object parseFieldValue (Object value , Map <String , Object > variables ) {
437
+ if (!(value instanceof Value )) {
438
+ throw new IllegalArgumentException (
439
+ "Expected AST type 'StringValue' but was '" + value + "'."
440
+ );
441
+ }
442
+
443
+ if (value instanceof StringValue ) {
444
+ return ((StringValue ) value ).getValue ();
445
+ }
446
+ if (value instanceof IntValue ) {
447
+ return ((IntValue ) value ).getValue ();
448
+ }
449
+ if (value instanceof FloatValue ) {
450
+ return ((FloatValue ) value ).getValue ();
451
+ }
452
+ if (value instanceof BooleanValue ) {
453
+ return ((BooleanValue ) value ).isValue ();
454
+ }
455
+ if (value instanceof EnumValue ) {
456
+ return ((EnumValue ) value ).getName ();
457
+ }
458
+ if (value instanceof NullValue ) {
459
+ return null ;
460
+ }
461
+ if (value instanceof ArrayValue ) {
462
+ List <Value > values = ((ArrayValue ) value ).getValues ();
463
+ return values .stream ()
464
+ .map (v -> parseFieldValue (v , variables ))
465
+ .collect (Collectors .toList ());
466
+ }
467
+ if (value instanceof ObjectValue ) {
468
+ List <ObjectField > values = ((ObjectValue ) value ).getObjectFields ();
469
+ Map <String , Object > parsedValues = new LinkedHashMap <>();
470
+
471
+ values .forEach (field -> {
472
+ Object parsedValue = parseFieldValue (field .getValue (), variables );
473
+ parsedValues .put (field .getName (), parsedValue );
474
+ });
475
+ return parsedValues ;
476
+ }
477
+
478
+ //Should never happen, as it would mean the variable was not replaced by the parser
479
+ throw new IllegalArgumentException ("Unsupported scalar value type: " + value .getClass ().getName ());
480
+ }
481
+
482
+ }
483
+
291
484
292
485
}
0 commit comments