Skip to content

Commit

Permalink
Use graphql-java-extended-scalars implementations [BREAKING CHANGE]
Browse files Browse the repository at this point in the history
For UUID and date/time/datetime types, use available upstream
implementations instead of maintaining our own.  Note that
the `OffsetDateTime` GraphQL type is renamed to `DateTime`,
and the `LocalDate` GraphQL type is renamed to `Date`.  Serialized
datetimes will also better conform to ISO/RFC formats.

Also update our scalar implementations to throw the appropriate
exception type, resulting in queries that execute and return
error results instead of failing to execute.  This is compatible
with the graphql-java-extended-scalars implementations and more
in line with how GraphQL failures should be handled anyway.

Bump to version 1.31.x due to these behavioral changes.
  • Loading branch information
josephlbarnett committed Apr 25, 2022
1 parent 8731569 commit b39111f
Show file tree
Hide file tree
Showing 11 changed files with 65 additions and 268 deletions.
2 changes: 1 addition & 1 deletion build-resources/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

<groupId>com.trib3</groupId>
<artifactId>build-resources</artifactId>
<version>1.30-SNAPSHOT</version>
<version>1.31-SNAPSHOT</version>

<name>Build Resources</name>
<description>Resources for use during the build process</description>
Expand Down
2 changes: 1 addition & 1 deletion config/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<parent>
<groupId>com.trib3</groupId>
<artifactId>leakycauldron</artifactId>
<version>1.30-SNAPSHOT</version>
<version>1.31-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion db/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<parent>
<groupId>com.trib3</groupId>
<artifactId>leakycauldron</artifactId>
<version>1.30-SNAPSHOT</version>
<version>1.31-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
2 changes: 1 addition & 1 deletion graphql/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<parent>
<groupId>com.trib3</groupId>
<artifactId>leakycauldron</artifactId>
<version>1.30-SNAPSHOT</version>
<version>1.31-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import com.expediagroup.graphql.generator.hooks.FlowSubscriptionSchemaGeneratorH
import graphql.language.StringValue
import graphql.scalars.ExtendedScalars
import graphql.schema.Coercing
import graphql.schema.CoercingParseLiteralException
import graphql.schema.CoercingParseValueException
import graphql.schema.CoercingSerializeException
import graphql.schema.GraphQLScalarType
import graphql.schema.GraphQLType
Expand All @@ -19,6 +21,7 @@ import java.time.LocalTime
import java.time.OffsetDateTime
import java.time.Year
import java.time.YearMonth
import java.time.format.DateTimeFormatter
import java.util.UUID
import javax.annotation.Nullable
import javax.inject.Inject
Expand All @@ -29,22 +32,22 @@ internal val YEAR_SCALAR = GraphQLScalarType.newScalar()
.description("Year, for example 2019")
.coercing(
object : Coercing<Year, String> {
private fun parse(input: String): Year {
private fun parse(input: String, exceptionConstructor: (String, Throwable) -> Exception): Year {
return try {
Year.parse(input)
} catch (e: Exception) {
throw CoercingSerializeException("can't parse $input", e)
throw exceptionConstructor("can't parse $input", e)
}
}

override fun parseValue(input: Any): Year {
return parse(input.toString())
return parse(input.toString(), ::CoercingParseValueException)
}

override fun parseLiteral(input: Any): Year {
return when (input) {
is StringValue -> parse(input.value)
else -> throw CoercingSerializeException("can't parse $input")
is StringValue -> parse(input.value, ::CoercingParseLiteralException)
else -> throw CoercingParseLiteralException("can't parse $input")
}
}

Expand All @@ -63,22 +66,22 @@ internal val YEAR_MONTH_SCALAR = GraphQLScalarType.newScalar()
.description("Year + Month, for example 2019-01")
.coercing(
object : Coercing<YearMonth, String> {
private fun parse(input: String): YearMonth {
private fun parse(input: String, exceptionConstructor: (String, Throwable) -> Exception): YearMonth {
return try {
YearMonth.parse(input)
} catch (e: Exception) {
throw CoercingSerializeException("can't parse $input", e)
throw exceptionConstructor("can't parse $input", e)
}
}

override fun parseValue(input: Any): YearMonth {
return parse(input.toString())
return parse(input.toString(), ::CoercingParseValueException)
}

override fun parseLiteral(input: Any): YearMonth {
return when (input) {
is StringValue -> parse(input.value)
else -> throw CoercingSerializeException("can't parse $input")
is StringValue -> parse(input.value, ::CoercingParseLiteralException)
else -> throw CoercingParseLiteralException("can't parse $input")
}
}

Expand All @@ -97,22 +100,22 @@ internal val YEAR_QUARTER_SCALAR = GraphQLScalarType.newScalar()
.description("Year + Quarter, for example 2019-Q1")
.coercing(
object : Coercing<YearQuarter, String> {
private fun parse(input: String): YearQuarter {
private fun parse(input: String, exceptionConstructor: (String, Throwable) -> Exception): YearQuarter {
return try {
YearQuarter.parse(input)
} catch (e: Exception) {
throw CoercingSerializeException("can't parse $input", e)
throw exceptionConstructor("can't parse $input", e)
}
}

override fun parseValue(input: Any): YearQuarter {
return parse(input.toString())
return parse(input.toString(), ::CoercingParseValueException)
}

override fun parseLiteral(input: Any): YearQuarter {
return when (input) {
is StringValue -> parse(input.value)
else -> throw CoercingSerializeException("can't parse $input")
is StringValue -> parse(input.value, ::CoercingParseLiteralException)
else -> throw CoercingParseLiteralException("can't parse $input")
}
}

Expand All @@ -134,167 +137,28 @@ internal val LOCAL_DATETIME_SCALAR = GraphQLScalarType.newScalar()
)
.coercing(
object : Coercing<LocalDateTime, String> {
private fun parse(input: String): LocalDateTime {
private fun parse(input: String, exceptionConstructor: (String, Throwable) -> Exception): LocalDateTime {
return try {
LocalDateTime.parse(input)
} catch (e: Exception) {
throw CoercingSerializeException("can't parse $input", e)
throw exceptionConstructor("can't parse $input", e)
}
}

override fun parseValue(input: Any): LocalDateTime {
return parse(input.toString())
return parse(input.toString(), ::CoercingParseValueException)
}

override fun parseLiteral(input: Any): LocalDateTime {
return when (input) {
is StringValue -> parse(input.value)
else -> throw CoercingSerializeException("can't parse $input")
is StringValue -> parse(input.value, ::CoercingParseLiteralException)
else -> throw CoercingParseLiteralException("can't parse $input")
}
}

override fun serialize(dataFetcherResult: Any): String {
return when (dataFetcherResult) {
is LocalDateTime -> dataFetcherResult.toString()
else -> throw CoercingSerializeException("can't serialize ${dataFetcherResult::class}")
}
}
}
)
.build()

internal val LOCAL_DATE_SCALAR = GraphQLScalarType.newScalar()
.name("LocalDate")
.description("Year + Month + Day Of Month, for example 2019-10-31")
.coercing(
object : Coercing<LocalDate, String> {
private fun parse(input: String): LocalDate {
return try {
LocalDate.parse(input)
} catch (e: Exception) {
throw CoercingSerializeException("can't parse $input", e)
}
}

override fun parseValue(input: Any): LocalDate {
return parse(input.toString())
}

override fun parseLiteral(input: Any): LocalDate {
return when (input) {
is StringValue -> parse(input.value)
else -> throw CoercingSerializeException("can't parse $input")
}
}

override fun serialize(dataFetcherResult: Any): String {
return when (dataFetcherResult) {
is LocalDate -> dataFetcherResult.toString()
else -> throw CoercingSerializeException("can't serialize ${dataFetcherResult::class}")
}
}
}
)
.build()

internal val LOCAL_TIME_SCALAR = GraphQLScalarType.newScalar()
.name("LocalTime")
.description("Hour + Minute + Optional (Second + Millisecond) , for example 12:31:45.129")
.coercing(
object : Coercing<LocalTime, String> {
private fun parse(input: String): LocalTime {
return try {
LocalTime.parse(input)
} catch (e: Exception) {
throw CoercingSerializeException("can't parse $input", e)
}
}

override fun parseValue(input: Any): LocalTime {
return parse(input.toString())
}

override fun parseLiteral(input: Any): LocalTime {
return when (input) {
is StringValue -> parse(input.value)
else -> throw CoercingSerializeException("can't parse $input")
}
}

override fun serialize(dataFetcherResult: Any): String {
return when (dataFetcherResult) {
is LocalTime -> dataFetcherResult.toString()
else -> throw CoercingSerializeException("can't serialize ${dataFetcherResult::class}")
}
}
}
)
.build()

internal val OFFSET_DATETIME_SCALAR = GraphQLScalarType.newScalar()
.name("OffsetDateTime")
.description(
"Year + Month + Day Of Month + Time (Hour:Minute + Optional(Second.Milliseconds)) " +
"+ Offset, for example 2019-10-31T12:31:45.129-07:00"
)
.coercing(
object : Coercing<OffsetDateTime, String> {
private fun parse(input: String): OffsetDateTime {
return try {
OffsetDateTime.parse(input)
} catch (e: Exception) {
throw CoercingSerializeException("can't parse $input", e)
}
}

override fun parseValue(input: Any): OffsetDateTime {
return parse(input.toString())
}

override fun parseLiteral(input: Any): OffsetDateTime {
return when (input) {
is StringValue -> parse(input.value)
else -> throw CoercingSerializeException("can't parse $input")
}
}

override fun serialize(dataFetcherResult: Any): String {
return when (dataFetcherResult) {
is OffsetDateTime -> dataFetcherResult.toString()
else -> throw CoercingSerializeException("can't serialize ${dataFetcherResult::class}")
}
}
}
)
.build()

internal val UUID_SCALAR = GraphQLScalarType.newScalar()
.name("UUID")
.description("String representation of a UUID")
.coercing(
object : Coercing<UUID, String> {
private fun parse(input: String): UUID {
try {
return UUID.fromString(input)
} catch (e: Exception) {
throw CoercingSerializeException("can't parse $input", e)
}
}

override fun parseValue(input: Any): UUID {
return parse(input.toString())
}

override fun parseLiteral(input: Any): UUID {
return when (input) {
is StringValue -> parse(input.value)
else -> throw CoercingSerializeException("can't parse $input")
}
}

override fun serialize(dataFetcherResult: Any): String {
return when (dataFetcherResult) {
is UUID -> dataFetcherResult.toString()
is LocalDateTime -> DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(dataFetcherResult)
else -> throw CoercingSerializeException("can't serialize ${dataFetcherResult::class}")
}
}
Expand All @@ -321,16 +185,15 @@ class LeakyCauldronHooks @Inject constructor(

override fun willGenerateGraphQLType(type: KType): GraphQLType? {
// TODO: include more from ExtendedScalars?
// Use impls from ExtendedScalars instead of our own for date/time/datetimes?
return when (type.classifier) {
Year::class -> YEAR_SCALAR
YearMonth::class -> YEAR_MONTH_SCALAR
YearQuarter::class -> YEAR_QUARTER_SCALAR
LocalDateTime::class -> LOCAL_DATETIME_SCALAR
LocalDate::class -> LOCAL_DATE_SCALAR
LocalTime::class -> LOCAL_TIME_SCALAR
OffsetDateTime::class -> OFFSET_DATETIME_SCALAR
UUID::class -> UUID_SCALAR
LocalDate::class -> ExtendedScalars.Date
LocalTime::class -> ExtendedScalars.LocalTime
OffsetDateTime::class -> ExtendedScalars.DateTime
UUID::class -> ExtendedScalars.UUID
BigDecimal::class -> ExtendedScalars.GraphQLBigDecimal
else -> null
}
Expand Down
Loading

0 comments on commit b39111f

Please sign in to comment.