1717
1818import java .math .BigDecimal ;
1919import java .math .BigInteger ;
20+ import java .nio .charset .StandardCharsets ;
2021import java .text .DateFormat ;
2122import java .text .ParseException ;
23+ import java .text .SimpleDateFormat ;
2224import java .time .Instant ;
2325import java .time .LocalDate ;
2426import java .time .LocalDateTime ;
2527import java .time .format .DateTimeParseException ;
28+ import java .util .Collections ;
2629import java .util .Date ;
2730import java .util .HashMap ;
31+ import java .util .LinkedHashMap ;
32+ import java .util .List ;
33+ import java .util .Map ;
2834import java .util .TimeZone ;
2935import java .util .UUID ;
36+ import java .util .stream .Collectors ;
3037
3138import org .slf4j .Logger ;
3239import org .slf4j .LoggerFactory ;
3340
3441import graphql .Assert ;
3542import graphql .Scalars ;
43+ import graphql .language .ArrayValue ;
44+ import graphql .language .BooleanValue ;
45+ import graphql .language .EnumValue ;
46+ import graphql .language .FloatValue ;
3647import graphql .language .IntValue ;
48+ import graphql .language .NullValue ;
49+ import graphql .language .ObjectField ;
50+ import graphql .language .ObjectValue ;
3751import graphql .language .StringValue ;
52+ import graphql .language .Value ;
3853import graphql .schema .Coercing ;
3954import graphql .schema .GraphQLScalarType ;
4055
@@ -49,6 +64,8 @@ public class JavaScalars {
4964 static final Logger log = LoggerFactory .getLogger (JavaScalars .class );
5065
5166 private static HashMap <Class <?>, GraphQLScalarType > scalarsRegistry = new HashMap <Class <?>, GraphQLScalarType >();
67+
68+ private static JavaScalars instance = new JavaScalars ();
5269
5370 static {
5471 scalarsRegistry .put (String .class , Scalars .GraphQLString );
@@ -86,21 +103,24 @@ public class JavaScalars {
86103 scalarsRegistry .put (Date .class , new GraphQLScalarType ("Date" , "Date type" , new GraphQLDateCoercing ()));
87104 scalarsRegistry .put (UUID .class , new GraphQLScalarType ("UUID" , "UUID type" , new GraphQLUUIDCoercing ()));
88105 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 ()));
89109 }
90110
91111 public static GraphQLScalarType of (Class <?> key ) {
92112 return scalarsRegistry .get (key );
93113 }
94114
95- public JavaScalars register (Class <?> key , GraphQLScalarType value ) {
115+ public static JavaScalars register (Class <?> key , GraphQLScalarType value ) {
96116 Assert .assertNotNull (key , "key parameter cannot be null." );
97117 Assert .assertNotNull (value , "value parameter cannot be null." );
98118
99119 scalarsRegistry .put (key , value );
100120
101- return this ;
121+ return instance ;
102122 }
103-
123+
104124 public static class GraphQLLocalDateTimeCoercing implements Coercing <Object , Object > {
105125
106126 @ Override
@@ -227,7 +247,7 @@ public Object parseLiteral(Object input) {
227247
228248 private Date parseStringToDate (String input ) {
229249 try {
230- return DateFormat . getInstance ( ).parse (input );
250+ return new SimpleDateFormat ( "yyyy-MM-dd" ).parse (input );
231251 } catch (ParseException e ) {
232252 log .warn ("Failed to parse Date from input: " + input , e );
233253 return null ;
@@ -271,7 +291,7 @@ private UUID parseStringToUUID(String input) {
271291 }
272292 };
273293
274- public static class GraphQLObjectCoercing implements Coercing <Object , Object > {
294+ public static class GraphQLRawObjectCoercing implements Coercing <Object , Object > {
275295
276296 @ Override
277297 public Object serialize (Object input ) {
@@ -288,5 +308,178 @@ public Object parseLiteral(Object input) {
288308 return input ;
289309 }
290310 };
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+
291484
292485}
0 commit comments