From e91dff62b0b515ad1c8977a1d70c4ed83bbda76d Mon Sep 17 00:00:00 2001 From: pavan-traceable <73101820+pavan-traceable@users.noreply.github.com> Date: Wed, 1 Dec 2021 19:37:48 +0530 Subject: [PATCH] Add offsetTime Scalar to Typefunction (#84) Add offsetTime Scalar to Typefunction --- .../common/schema/CommonSchemaFragment.java | 9 ++- .../schema/typefunctions/DateTimeScalar.java | 2 +- .../typefunctions/OffsetTimeScalar.java | 78 +++++++++++++++++++ .../schema/scalars/DateTimeScalarTest.java | 9 --- .../schema/scalars/OffsetTimeScalarTest.java | 72 +++++++++++++++++ 5 files changed, 158 insertions(+), 12 deletions(-) create mode 100644 hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/typefunctions/OffsetTimeScalar.java create mode 100644 hypertrace-core-graphql-common-schema/src/test/java/org/hypertrace/core/graphql/common/schema/scalars/OffsetTimeScalarTest.java diff --git a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/CommonSchemaFragment.java b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/CommonSchemaFragment.java index cca0b551..82b2dc31 100644 --- a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/CommonSchemaFragment.java +++ b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/CommonSchemaFragment.java @@ -7,6 +7,7 @@ import org.hypertrace.core.graphql.common.schema.typefunctions.AttributeScopeDynamicEnum; import org.hypertrace.core.graphql.common.schema.typefunctions.DateTimeScalar; import org.hypertrace.core.graphql.common.schema.typefunctions.DurationScalar; +import org.hypertrace.core.graphql.common.schema.typefunctions.OffsetTimeScalar; import org.hypertrace.core.graphql.common.schema.typefunctions.UnknownScalar; import org.hypertrace.core.graphql.spi.schema.GraphQlSchemaFragment; @@ -16,17 +17,20 @@ class CommonSchemaFragment implements GraphQlSchemaFragment { private final UnknownScalar unknownScalar; private final AttributeScopeDynamicEnum attributeScopeDynamicEnum; private final DurationScalar durationScalar; + private final OffsetTimeScalar offsetTimeScalar; @Inject CommonSchemaFragment( DateTimeScalar dateTimeScalar, UnknownScalar unknownScalar, AttributeScopeDynamicEnum attributeScopeDynamicEnum, - DurationScalar durationScalar) { + DurationScalar durationScalar, + OffsetTimeScalar offsetTimeScalar) { this.dateTimeScalar = dateTimeScalar; this.unknownScalar = unknownScalar; this.attributeScopeDynamicEnum = attributeScopeDynamicEnum; this.durationScalar = durationScalar; + this.offsetTimeScalar = offsetTimeScalar; } @Override @@ -41,6 +45,7 @@ public List typeFunctions() { this.unknownScalar, this.dateTimeScalar, this.attributeScopeDynamicEnum, - this.durationScalar); + this.durationScalar, + this.offsetTimeScalar); } } diff --git a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/typefunctions/DateTimeScalar.java b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/typefunctions/DateTimeScalar.java index b14b72c5..c3429f8d 100644 --- a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/typefunctions/DateTimeScalar.java +++ b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/typefunctions/DateTimeScalar.java @@ -64,7 +64,7 @@ private Instant toInstant( @Override public boolean canBuildType(Class aClass, AnnotatedType annotatedType) { - return TemporalAccessor.class.isAssignableFrom(aClass); + return Instant.class.isAssignableFrom(aClass); } @Override diff --git a/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/typefunctions/OffsetTimeScalar.java b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/typefunctions/OffsetTimeScalar.java new file mode 100644 index 00000000..cd035920 --- /dev/null +++ b/hypertrace-core-graphql-common-schema/src/main/java/org/hypertrace/core/graphql/common/schema/typefunctions/OffsetTimeScalar.java @@ -0,0 +1,78 @@ +package org.hypertrace.core.graphql.common.schema.typefunctions; + +import graphql.GraphqlErrorException; +import graphql.annotations.processor.ProcessingElementsContainer; +import graphql.annotations.processor.typeFunctions.TypeFunction; +import graphql.language.StringValue; +import graphql.schema.Coercing; +import graphql.schema.CoercingParseLiteralException; +import graphql.schema.CoercingParseValueException; +import graphql.schema.CoercingSerializeException; +import graphql.schema.GraphQLScalarType; +import java.lang.reflect.AnnotatedType; +import java.time.DateTimeException; +import java.time.OffsetTime; +import java.time.temporal.TemporalAccessor; +import java.util.function.Function; + +public class OffsetTimeScalar implements TypeFunction { + + private static final GraphQLScalarType OFFSET_TIME_SCALAR = + GraphQLScalarType.newScalar() + .name("OffsetTime") + .description("An ISO-8601 formatted OffsetTime Scalar") + .coercing( + new Coercing() { + @Override + public String serialize(Object fetcherResult) throws CoercingSerializeException { + return toOffsetTime(fetcherResult, CoercingSerializeException::new).toString(); + } + + @Override + public OffsetTime parseValue(Object input) throws CoercingParseValueException { + return toOffsetTime(input, CoercingParseValueException::new); + } + + @Override + public OffsetTime parseLiteral(Object input) throws CoercingParseLiteralException { + return toOffsetTime(input, CoercingParseLiteralException::new); + } + + private OffsetTime toOffsetTime( + Object offsetInput, Function errorWrapper) throws E { + try { + if (offsetInput instanceof TemporalAccessor) { + return OffsetTime.from((TemporalAccessor) offsetInput); + } + if (offsetInput instanceof CharSequence) { + return OffsetTime.parse((CharSequence) offsetInput); + } + if (offsetInput instanceof StringValue) { + return OffsetTime.parse(((StringValue) offsetInput).getValue()); + } + } catch (DateTimeException exception) { + throw errorWrapper.apply(exception); + } + throw errorWrapper.apply( + new DateTimeException( + String.format( + "Cannot convert provided format '%s' to OffsetTime", + offsetInput.getClass().getCanonicalName()))); + } + }) + .build(); + + @Override + public boolean canBuildType(Class aClass, AnnotatedType annotatedType) { + return OffsetTime.class.isAssignableFrom(aClass); + } + + @Override + public GraphQLScalarType buildType( + boolean input, + Class aClass, + AnnotatedType annotatedType, + ProcessingElementsContainer container) { + return OFFSET_TIME_SCALAR; + } +} diff --git a/hypertrace-core-graphql-common-schema/src/test/java/org/hypertrace/core/graphql/common/schema/scalars/DateTimeScalarTest.java b/hypertrace-core-graphql-common-schema/src/test/java/org/hypertrace/core/graphql/common/schema/scalars/DateTimeScalarTest.java index 4a71c9b5..5f5953c5 100644 --- a/hypertrace-core-graphql-common-schema/src/test/java/org/hypertrace/core/graphql/common/schema/scalars/DateTimeScalarTest.java +++ b/hypertrace-core-graphql-common-schema/src/test/java/org/hypertrace/core/graphql/common/schema/scalars/DateTimeScalarTest.java @@ -8,11 +8,7 @@ import graphql.schema.GraphQLScalarType; import java.lang.reflect.AnnotatedType; import java.time.Instant; -import java.time.LocalDateTime; -import java.time.OffsetDateTime; import java.time.ZoneOffset; -import java.time.ZonedDateTime; -import java.time.chrono.ChronoLocalDateTime; import org.hypertrace.core.graphql.common.schema.typefunctions.DateTimeScalar; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -44,11 +40,6 @@ void beforeEach() { @Test void canDetermineIfConvertible() { assertTrue(this.dateTimeFunction.canBuildType(Instant.class, this.mockAnnotatedType)); - assertTrue(this.dateTimeFunction.canBuildType(OffsetDateTime.class, this.mockAnnotatedType)); - assertTrue(this.dateTimeFunction.canBuildType(LocalDateTime.class, this.mockAnnotatedType)); - assertTrue( - this.dateTimeFunction.canBuildType(ChronoLocalDateTime.class, this.mockAnnotatedType)); - assertTrue(this.dateTimeFunction.canBuildType(ZonedDateTime.class, this.mockAnnotatedType)); } @Test diff --git a/hypertrace-core-graphql-common-schema/src/test/java/org/hypertrace/core/graphql/common/schema/scalars/OffsetTimeScalarTest.java b/hypertrace-core-graphql-common-schema/src/test/java/org/hypertrace/core/graphql/common/schema/scalars/OffsetTimeScalarTest.java new file mode 100644 index 00000000..ed8382ae --- /dev/null +++ b/hypertrace-core-graphql-common-schema/src/test/java/org/hypertrace/core/graphql/common/schema/scalars/OffsetTimeScalarTest.java @@ -0,0 +1,72 @@ +package org.hypertrace.core.graphql.common.schema.scalars; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import graphql.annotations.processor.ProcessingElementsContainer; +import graphql.language.StringValue; +import graphql.schema.GraphQLScalarType; +import java.lang.reflect.AnnotatedType; +import java.time.OffsetTime; +import java.time.ZoneOffset; +import org.hypertrace.core.graphql.common.schema.typefunctions.OffsetTimeScalar; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +@ExtendWith(MockitoExtension.class) +class OffsetTimeScalarTest { + + private static final String TEST_SCALAR_TIME_STRING = "21:30:12.748+12:30"; + private static final OffsetTime TEST_OFFSET_TIME = OffsetTime.parse(TEST_SCALAR_TIME_STRING); + private OffsetTimeScalar offsetTimeFunction; + private GraphQLScalarType offsetTimeType; + @Mock AnnotatedType mockAnnotatedType; + // Can't actually mock class, but it's not used so to convey intent using the Mock class. + private final Class mockAnnotatedClass = Mock.class; + @Mock ProcessingElementsContainer mockProcessingElementsContainer; + + @BeforeEach + void beforeEach() { + this.offsetTimeFunction = new OffsetTimeScalar(); + // Can't actually mock class, but it's not used so to convey intent using + this.offsetTimeType = + this.offsetTimeFunction.buildType( + false, mockAnnotatedClass, mockAnnotatedType, mockProcessingElementsContainer); + } + + @Test + void canDetermineIfConvertible() { + assertTrue(this.offsetTimeFunction.canBuildType(OffsetTime.class, this.mockAnnotatedType)); + } + + @Test + void canConvertFromLiteral() { + assertEquals( + TEST_OFFSET_TIME, offsetTimeType.getCoercing().parseLiteral(TEST_SCALAR_TIME_STRING)); + } + + @Test + void canSerialize() { + assertEquals(TEST_SCALAR_TIME_STRING, offsetTimeType.getCoercing().serialize(TEST_OFFSET_TIME)); + assertEquals( + TEST_SCALAR_TIME_STRING, offsetTimeType.getCoercing().serialize(TEST_SCALAR_TIME_STRING)); + + assertEquals( + TEST_SCALAR_TIME_STRING, + offsetTimeType + .getCoercing() + .serialize(TEST_OFFSET_TIME.withOffsetSameLocal(ZoneOffset.ofHoursMinutes(12, 30)))); + } + + @Test + void canConvertFromValue() { + assertEquals( + TEST_OFFSET_TIME, + offsetTimeType + .getCoercing() + .parseValue(StringValue.newStringValue().value(TEST_SCALAR_TIME_STRING).build())); + } +}