Skip to content

Commit d576bab

Browse files
authored
Add Missing LocalTime Coercing (#68)
* fix: clean up comments * fix: add LocalTime GraphQL coercing support
1 parent 3f313bd commit d576bab

File tree

3 files changed

+244
-130
lines changed

3 files changed

+244
-130
lines changed

graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/JavaScalars.java

Lines changed: 168 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@
2424
import java.time.Instant;
2525
import java.time.LocalDate;
2626
import java.time.LocalDateTime;
27+
import java.time.LocalTime;
28+
import java.time.ZoneOffset;
29+
import java.time.format.DateTimeFormatter;
2730
import java.time.format.DateTimeParseException;
2831
import java.util.Collections;
2932
import java.util.Date;
@@ -35,9 +38,6 @@
3538
import java.util.UUID;
3639
import java.util.stream.Collectors;
3740

38-
import org.slf4j.Logger;
39-
import org.slf4j.LoggerFactory;
40-
4141
import graphql.Assert;
4242
import graphql.Scalars;
4343
import graphql.language.ArrayValue;
@@ -51,7 +51,11 @@
5151
import graphql.language.StringValue;
5252
import graphql.language.Value;
5353
import graphql.schema.Coercing;
54+
import graphql.schema.CoercingParseValueException;
55+
import graphql.schema.CoercingSerializeException;
5456
import graphql.schema.GraphQLScalarType;
57+
import org.slf4j.Logger;
58+
import org.slf4j.LoggerFactory;
5559

5660
/**
5761
* Provides Registry to resolve GraphQL Query Java Scalar Types
@@ -100,6 +104,7 @@ public class JavaScalars {
100104

101105
scalarsRegistry.put(LocalDateTime.class, new GraphQLScalarType("LocalDateTime", "LocalDateTime type", new GraphQLLocalDateTimeCoercing()));
102106
scalarsRegistry.put(LocalDate.class, new GraphQLScalarType("LocalDate", "LocalDate type", new GraphQLLocalDateCoercing()));
107+
scalarsRegistry.put(LocalTime.class, new GraphQLScalarType("LocalTime", "LocalTime type", new GraphQLLocalTimeCoercing()));
103108
scalarsRegistry.put(Date.class, new GraphQLScalarType("Date", "Date type", new GraphQLDateCoercing()));
104109
scalarsRegistry.put(UUID.class, new GraphQLScalarType("UUID", "UUID type", new GraphQLUUIDCoercing()));
105110
scalarsRegistry.put(Object.class, new GraphQLScalarType("Object", "Object type", new GraphQLObjectCoercing()));
@@ -212,6 +217,51 @@ private LocalDate parseStringToLocalDate(String input) {
212217
}
213218
}
214219
};
220+
221+
public static class GraphQLLocalTimeCoercing implements Coercing<LocalTime, String> {
222+
223+
public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_LOCAL_TIME.withZone(ZoneOffset.UTC);
224+
225+
private LocalTime convertImpl(Object input) {
226+
if (input instanceof String) {
227+
try {
228+
return LocalTime.parse((String) input, FORMATTER);
229+
} catch (DateTimeParseException ignored) {
230+
}
231+
}
232+
return null;
233+
}
234+
235+
@Override
236+
public String serialize(Object input) {
237+
if (input instanceof LocalTime) {
238+
return DateTimeFormatter.ISO_LOCAL_TIME.format((LocalTime) input);
239+
} else {
240+
LocalTime result = convertImpl(input);
241+
if (result == null) {
242+
throw new CoercingSerializeException("Invalid value '" + input + "' for LocalTime");
243+
}
244+
return DateTimeFormatter.ISO_LOCAL_TIME.format(result);
245+
}
246+
}
247+
248+
@Override
249+
public LocalTime parseValue(Object input) {
250+
LocalTime result = convertImpl(input);
251+
if (result == null) {
252+
throw new CoercingParseValueException("Invalid value '" + input + "' for LocalTime");
253+
}
254+
return result;
255+
}
256+
257+
@Override
258+
public LocalTime parseLiteral(Object input) {
259+
if (!(input instanceof StringValue)) return null;
260+
String value = ((StringValue) input).getValue();
261+
LocalTime result = convertImpl(value);
262+
return result;
263+
}
264+
}
215265

216266
public static class GraphQLDateCoercing implements Coercing<Object, Object> {
217267
final DateFormat df;
@@ -328,111 +378,125 @@ public Object parseLiteral(Object input) {
328378
};
329379

330380
public static class GraphQLSqlDateCoercing implements Coercing<Object, Object> {
381+
331382
@Override
332-
public Object serialize(Object input) {
333-
if (input instanceof String) {
334-
return parseStringToDate((String) input);
335-
} else if (input instanceof Date) {
336-
return new java.sql.Date(((Date) input).getTime());
337-
} else if (input instanceof Long) {
338-
return new java.sql.Date(((Long) input).longValue());
339-
} else if (input instanceof Integer) {
340-
return new java.sql.Date(((Integer) input).longValue());
341-
}
342-
return null;
343-
}
383+
public Object serialize(Object input) {
384+
if (input instanceof String) {
385+
return parseStringToDate((String) input);
386+
} else if (input instanceof Date) {
387+
return new java.sql.Date(((Date) input).getTime());
388+
} else if (input instanceof Long) {
389+
return new java.sql.Date(((Long) input).longValue());
390+
} else if (input instanceof Integer) {
391+
return new java.sql.Date(((Integer) input).longValue());
392+
}
393+
return null;
394+
}
395+
344396
@Override
345-
public Object parseValue(Object input) {
346-
return serialize(input);
347-
}
397+
public Object parseValue(Object input) {
398+
return serialize(input);
399+
}
400+
348401
@Override
349-
public Object parseLiteral(Object input) {
350-
if (input instanceof StringValue) {
351-
return parseStringToDate(((StringValue) input).getValue());
352-
} else if (input instanceof IntValue) {
353-
BigInteger value = ((IntValue) input).getValue();
354-
return new java.sql.Date(value.longValue());
355-
}
356-
return null;
357-
}
402+
public Object parseLiteral(Object input) {
403+
if (input instanceof StringValue) {
404+
return parseStringToDate(((StringValue) input).getValue());
405+
} else if (input instanceof IntValue) {
406+
BigInteger value = ((IntValue) input).getValue();
407+
return new java.sql.Date(value.longValue());
408+
}
409+
return null;
410+
}
411+
358412
private java.sql.Date parseStringToDate(String input) {
359-
try {
360-
return new java.sql.Date(DateFormat.getInstance().parse(input).getTime());
361-
} catch (ParseException e) {
362-
log.warn("Failed to parse SQL Date from input: " + input, e);
363-
return null;
364-
}
365-
}
366-
}
413+
try {
414+
return new java.sql.Date(DateFormat.getInstance().parse(input).getTime());
415+
} catch (ParseException e) {
416+
log.warn("Failed to parse SQL Date from input: " + input, e);
417+
return null;
418+
}
419+
}
420+
}
421+
367422
public static class GraphQLSqlTimestampCoercing implements Coercing<Object, Object> {
423+
368424
@Override
369-
public Object serialize(Object input) {
370-
if (input instanceof String) {
371-
return parseStringToTimestamp((String) input);
372-
} else if (input instanceof Date) {
373-
return new java.sql.Timestamp(((Date) input).getTime());
374-
} else if (input instanceof Long) {
375-
return new java.sql.Timestamp(((Long) input).longValue());
376-
} else if (input instanceof Integer) {
377-
return new java.sql.Timestamp(((Integer) input).longValue());
378-
}
379-
return null;
380-
}
425+
public Object serialize(Object input) {
426+
if (input instanceof String) {
427+
return parseStringToTimestamp((String) input);
428+
} else if (input instanceof Date) {
429+
return new java.sql.Timestamp(((Date) input).getTime());
430+
} else if (input instanceof Long) {
431+
return new java.sql.Timestamp(((Long) input).longValue());
432+
} else if (input instanceof Integer) {
433+
return new java.sql.Timestamp(((Integer) input).longValue());
434+
}
435+
return null;
436+
}
437+
381438
@Override
382-
public Object parseValue(Object input) {
383-
return serialize(input);
384-
}
439+
public Object parseValue(Object input) {
440+
return serialize(input);
441+
}
442+
385443
@Override
386-
public Object parseLiteral(Object input) {
387-
if (input instanceof StringValue) {
388-
return parseStringToTimestamp(((StringValue) input).getValue());
389-
} else if (input instanceof IntValue) {
390-
BigInteger value = ((IntValue) input).getValue();
391-
return new java.sql.Date(value.longValue());
392-
}
393-
return null;
394-
}
444+
public Object parseLiteral(Object input) {
445+
if (input instanceof StringValue) {
446+
return parseStringToTimestamp(((StringValue) input).getValue());
447+
} else if (input instanceof IntValue) {
448+
BigInteger value = ((IntValue) input).getValue();
449+
return new java.sql.Date(value.longValue());
450+
}
451+
return null;
452+
}
453+
395454
private java.sql.Timestamp parseStringToTimestamp(String input) {
396-
try {
397-
return new java.sql.Timestamp(DateFormat.getInstance().parse(input).getTime());
398-
} catch (ParseException e) {
399-
log.warn("Failed to parse Timestamp from input: " + input, e);
400-
return null;
401-
}
402-
}
403-
}
455+
try {
456+
return new java.sql.Timestamp(DateFormat.getInstance().parse(input).getTime());
457+
} catch (ParseException e) {
458+
log.warn("Failed to parse Timestamp from input: " + input, e);
459+
return null;
460+
}
461+
}
462+
}
463+
404464
public static class GraphQLLOBCoercing implements Coercing<Object, Object> {
465+
405466
@Override
406-
public Object serialize(Object input) {
407-
if (input.getClass() == byte[].class) {
408-
return input;
409-
}
410-
return null;
411-
}
467+
public Object serialize(Object input) {
468+
if (input.getClass() == byte[].class) {
469+
return input;
470+
}
471+
return null;
472+
}
473+
412474
@Override
413-
public Object parseValue(Object input) {
414-
if (input instanceof String) {
415-
return parseStringToByteArray((String) input);
416-
}
417-
return null;
418-
}
475+
public Object parseValue(Object input) {
476+
if (input instanceof String) {
477+
return parseStringToByteArray((String) input);
478+
}
479+
return null;
480+
}
481+
419482
@Override
420-
public Object parseLiteral(Object input) {
421-
if (input instanceof StringValue) {
422-
return parseStringToByteArray(((StringValue) input).getValue());
423-
}
424-
return null;
425-
}
483+
public Object parseLiteral(Object input) {
484+
if (input instanceof StringValue) {
485+
return parseStringToByteArray(((StringValue) input).getValue());
486+
}
487+
return null;
488+
}
489+
426490
private byte[] parseStringToByteArray(String input) {
427-
try {
428-
return input.getBytes(StandardCharsets.UTF_8);
429-
} catch (IllegalArgumentException e) {
430-
log.warn("Failed to parse byte[] from input: " + input, e);
431-
return null;
432-
}
433-
}
434-
}
435-
491+
try {
492+
return input.getBytes(StandardCharsets.UTF_8);
493+
} catch (IllegalArgumentException e) {
494+
log.warn("Failed to parse byte[] from input: " + input, e);
495+
return null;
496+
}
497+
}
498+
}
499+
436500
public static class GraphQLObjectCoercing implements Coercing<Object, Object> {
437501

438502
@Override
@@ -447,17 +511,16 @@ public Object parseValue(Object input) {
447511

448512
@Override
449513
public Object parseLiteral(Object input) {
450-
return parseFieldValue((Value) input, Collections.emptyMap());
514+
return parseFieldValue((Value) input, Collections.emptyMap());
451515
}
452516

453517
//recursively parse the input into a Map
454518
private Object parseFieldValue(Object value, Map<String, Object> variables) {
455519
if (!(value instanceof Value)) {
456520
throw new IllegalArgumentException(
457-
"Expected AST type 'StringValue' but was '" + value + "'."
458-
);
459-
}
460-
521+
"Expected AST type 'StringValue' but was '" + value + "'.");
522+
}
523+
461524
if (value instanceof StringValue) {
462525
return ((StringValue) value).getValue();
463526
}
@@ -477,27 +540,26 @@ private Object parseFieldValue(Object value, Map<String, Object> variables) {
477540
return null;
478541
}
479542
if (value instanceof ArrayValue) {
480-
List<Value> values = ((ArrayValue) value).getValues();
543+
List<Value> values = ((ArrayValue) value).getValues();
481544
return values.stream()
482-
.map(v -> parseFieldValue(v, variables))
483-
.collect(Collectors.toList());
545+
.map(v -> parseFieldValue(v, variables))
546+
.collect(Collectors.toList());
484547
}
485548
if (value instanceof ObjectValue) {
486-
List<ObjectField> values = ((ObjectValue) value).getObjectFields();
549+
List<ObjectField> values = ((ObjectValue) value).getObjectFields();
487550
Map<String, Object> parsedValues = new LinkedHashMap<>();
488-
551+
489552
values.forEach(field -> {
490553
Object parsedValue = parseFieldValue(field.getValue(), variables);
491554
parsedValues.put(field.getName(), parsedValue);
492555
});
493556
return parsedValues;
494557
}
495-
558+
496559
//Should never happen, as it would mean the variable was not replaced by the parser
497560
throw new IllegalArgumentException("Unsupported scalar value type: " + value.getClass().getName());
498561
}
499562

500563
}
501-
502564

503565
}

graphql-jpa-query-schema/src/main/java/com/introproventures/graphql/jpa/query/schema/impl/GraphQLJpaQueryDataFetcher.java

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -195,22 +195,6 @@ private Page extractPageArgument(DataFetchingEnvironment environment, Field fiel
195195
Integer limit = pagex.get(GraphQLJpaSchemaBuilder.PAGE_LIMIT_PARAM_NAME);
196196

197197
return new Page(start, limit);
198-
199-
// ObjectValue paginationValues = (ObjectValue) paginationRequest.get().getValue();
200-
//
201-
// IntValue page = (IntValue) paginationValues.getObjectFields().stream()
202-
// .filter(it -> GraphQLJpaSchemaBuilder.PAGE_START_PARAM_NAME.equals(it.getName()))
203-
// .findFirst()
204-
// .get()
205-
// .getValue();
206-
//
207-
// IntValue size = (IntValue) paginationValues.getObjectFields().stream()
208-
// .filter(it -> GraphQLJpaSchemaBuilder.PAGE_LIMIT_PARAM_NAME.equals(it.getName()))
209-
// .findFirst()
210-
// .get()
211-
// .getValue();
212-
//
213-
// return new Page(page.getValue().intValue(), size.getValue().intValue());
214198
}
215199

216200
return new Page(1, Integer.MAX_VALUE);

0 commit comments

Comments
 (0)