|
20 | 20 | import java.math.BigDecimal;
|
21 | 21 | import java.math.BigInteger;
|
22 | 22 | import java.nio.charset.StandardCharsets;
|
| 23 | +import java.sql.Timestamp; |
23 | 24 | import java.text.DateFormat;
|
24 | 25 | import java.text.ParseException;
|
25 | 26 | import java.text.SimpleDateFormat;
|
|
39 | 40 | import java.util.LinkedHashMap;
|
40 | 41 | import java.util.List;
|
41 | 42 | import java.util.Map;
|
| 43 | +import java.util.Objects; |
42 | 44 | import java.util.TimeZone;
|
43 | 45 | import java.util.UUID;
|
| 46 | +import java.util.concurrent.CopyOnWriteArrayList; |
| 47 | +import java.util.function.Function; |
44 | 48 | import java.util.stream.Collectors;
|
45 | 49 |
|
46 | 50 | import org.slf4j.Logger;
|
|
60 | 64 | import graphql.language.Value;
|
61 | 65 | import graphql.language.VariableReference;
|
62 | 66 | import graphql.schema.Coercing;
|
| 67 | +import graphql.schema.CoercingParseLiteralException; |
63 | 68 | import graphql.schema.CoercingParseValueException;
|
64 | 69 | import graphql.schema.CoercingSerializeException;
|
65 | 70 | import graphql.schema.GraphQLScalarType;
|
@@ -577,45 +582,57 @@ private java.sql.Date parseStringToDate(String input) {
|
577 | 582 | }
|
578 | 583 | }
|
579 | 584 |
|
580 |
| - public static class GraphQLSqlTimestampCoercing implements Coercing<Object, Object> { |
| 585 | + public static class GraphQLSqlTimestampCoercing implements Coercing<Timestamp, Object> { |
581 | 586 |
|
582 |
| - @Override |
583 |
| - public Object serialize(Object input) { |
584 |
| - if (input instanceof String) { |
585 |
| - return parseStringToTimestamp((String) input); |
586 |
| - } else if (input instanceof Date) { |
587 |
| - return new java.sql.Timestamp(((Date) input).getTime()); |
588 |
| - } else if (input instanceof Long) { |
589 |
| - return new java.sql.Timestamp(((Long) input).longValue()); |
590 |
| - } else if (input instanceof Integer) { |
591 |
| - return new java.sql.Timestamp(((Integer) input).longValue()); |
| 587 | + private Timestamp doConvert(Object input) { |
| 588 | + if (input instanceof Long) { |
| 589 | + return new Timestamp(Long.class.cast(input)); |
| 590 | + } else if (input instanceof String) { |
| 591 | + Instant instant = DateTimeHelper.parseDate(String.class.cast(input)); |
| 592 | + |
| 593 | + return Timestamp.from(instant); |
| 594 | + } else if (input instanceof Timestamp) { |
| 595 | + return Timestamp.class.cast(input); |
592 | 596 | }
|
| 597 | + |
593 | 598 | return null;
|
594 | 599 | }
|
595 |
| - |
| 600 | + |
596 | 601 | @Override
|
597 |
| - public Object parseValue(Object input) { |
598 |
| - return serialize(input); |
| 602 | + public Object serialize(Object input) { |
| 603 | + Timestamp result = doConvert(input); |
| 604 | + |
| 605 | + if (result == null) { |
| 606 | + throw new CoercingSerializeException("Invalid value '" + input + "' for Timestamp"); |
| 607 | + } |
| 608 | + |
| 609 | + return DateTimeFormatter.ISO_INSTANT.format(result.toInstant()); |
599 | 610 | }
|
600 | 611 |
|
601 | 612 | @Override
|
602 |
| - public Object parseLiteral(Object input) { |
603 |
| - if (input instanceof StringValue) { |
604 |
| - return parseStringToTimestamp(((StringValue) input).getValue()); |
605 |
| - } else if (input instanceof IntValue) { |
606 |
| - BigInteger value = ((IntValue) input).getValue(); |
607 |
| - return new java.sql.Date(value.longValue()); |
| 613 | + public Timestamp parseValue(Object input) { |
| 614 | + Timestamp result = doConvert(input); |
| 615 | + |
| 616 | + if (result == null) { |
| 617 | + throw new CoercingParseValueException("Invalid value '" + input + "' for Timestamp"); |
608 | 618 | }
|
609 |
| - return null; |
| 619 | + return result; |
610 | 620 | }
|
611 | 621 |
|
612 |
| - private java.sql.Timestamp parseStringToTimestamp(String input) { |
613 |
| - try { |
614 |
| - return new java.sql.Timestamp(DateFormat.getInstance().parse(input).getTime()); |
615 |
| - } catch (ParseException e) { |
616 |
| - log.warn("Failed to parse Timestamp from input: " + input, e); |
617 |
| - return null; |
| 622 | + @Override |
| 623 | + public Timestamp parseLiteral(Object input) { |
| 624 | + Object value = null; |
| 625 | + |
| 626 | + if (IntValue.class.isInstance(input)) { |
| 627 | + value = IntValue.class.cast(input).getValue().longValue(); |
| 628 | + } |
| 629 | + else if (StringValue.class.isInstance(input)) { |
| 630 | + value = StringValue.class.cast(input).getValue(); |
| 631 | + } else { |
| 632 | + throw new CoercingParseLiteralException("Invalid value '" + input + "' for Timestamp"); |
618 | 633 | }
|
| 634 | + |
| 635 | + return doConvert(value); |
619 | 636 | }
|
620 | 637 | }
|
621 | 638 |
|
@@ -724,5 +741,88 @@ public Object parseLiteral(Object value, Map<String, Object> variables) {
|
724 | 741 | }
|
725 | 742 |
|
726 | 743 | }
|
| 744 | + |
| 745 | + public final static class DateTimeHelper { |
| 746 | + |
| 747 | + static final List<Function<String, Instant>> CONVERTERS = new CopyOnWriteArrayList<>(); |
| 748 | + |
| 749 | + static { |
| 750 | + CONVERTERS.add(new InstantConverter()); |
| 751 | + CONVERTERS.add(new OffsetDateTimeConverter()); |
| 752 | + CONVERTERS.add(new ZonedDateTimeConverter()); |
| 753 | + CONVERTERS.add(new LocalDateTimeConverter()); |
| 754 | + CONVERTERS.add(new LocalDateConverter()); |
| 755 | + } |
| 756 | + |
| 757 | + public static Instant parseDate(String date) { |
| 758 | + Objects.requireNonNull(date, "date"); |
| 759 | + |
| 760 | + for (Function<String, Instant> converter : CONVERTERS) { |
| 761 | + try { |
| 762 | + return converter.apply(date); |
| 763 | + } catch (java.time.format.DateTimeParseException ignored) { |
| 764 | + } |
| 765 | + } |
| 766 | + |
| 767 | + return null; |
| 768 | + } |
| 769 | + |
| 770 | + static class InstantConverter implements Function<String, Instant> { |
| 771 | + |
| 772 | + @Override |
| 773 | + public Instant apply(String date) { |
| 774 | + return Instant.parse(date); |
| 775 | + } |
| 776 | + } |
| 777 | + |
| 778 | + static class OffsetDateTimeConverter implements Function<String, Instant> { |
| 779 | + |
| 780 | + static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_OFFSET_DATE_TIME.withZone(ZoneOffset.UTC); |
| 781 | + |
| 782 | + @Override |
| 783 | + public Instant apply(String date) { |
| 784 | + return OffsetDateTime.parse(date, FORMATTER) |
| 785 | + .toInstant(); |
| 786 | + } |
| 787 | + } |
| 788 | + |
| 789 | + static class ZonedDateTimeConverter implements Function<String, Instant> { |
| 790 | + |
| 791 | + static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_ZONED_DATE_TIME.withZone(ZoneOffset.UTC); |
727 | 792 |
|
| 793 | + @Override |
| 794 | + public Instant apply(String date) { |
| 795 | + return ZonedDateTime.parse(date, FORMATTER) |
| 796 | + .toInstant(); |
| 797 | + } |
| 798 | + } |
| 799 | + |
| 800 | + |
| 801 | + static class LocalDateTimeConverter implements Function<String, Instant> { |
| 802 | + |
| 803 | + static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE_TIME.withZone(ZoneOffset.UTC); |
| 804 | + |
| 805 | + @Override |
| 806 | + public Instant apply(String date) { |
| 807 | + return LocalDateTime.parse(date, FORMATTER) |
| 808 | + .toInstant(ZoneOffset.UTC); |
| 809 | + } |
| 810 | + } |
| 811 | + |
| 812 | + static class LocalDateConverter implements Function<String, Instant> { |
| 813 | + |
| 814 | + static final DateTimeFormatter FORMATTER = DateTimeFormatter.ISO_LOCAL_DATE.withZone(ZoneOffset.UTC); |
| 815 | + |
| 816 | + @Override |
| 817 | + public Instant apply(String date) { |
| 818 | + LocalDate localDate = LocalDate.parse(date, FORMATTER); |
| 819 | + |
| 820 | + return localDate.atStartOfDay() |
| 821 | + .toInstant(ZoneOffset.UTC); |
| 822 | + } |
| 823 | + } |
| 824 | + } |
| 825 | + |
| 826 | + |
| 827 | + |
728 | 828 | }
|
0 commit comments