diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLSchema.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLSchema.kt index c3dedfd9..35e3a5a1 100644 --- a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLSchema.kt +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/TachideskGraphQLSchema.kt @@ -46,6 +46,7 @@ import suwayomi.tachidesk.graphql.queries.UpdateQuery import suwayomi.tachidesk.graphql.server.primitives.Cursor import suwayomi.tachidesk.graphql.server.primitives.GraphQLCursor import suwayomi.tachidesk.graphql.server.primitives.GraphQLDurationAsString +import suwayomi.tachidesk.graphql.server.primitives.GraphQLFloatAsDouble import suwayomi.tachidesk.graphql.server.primitives.GraphQLLongAsString import suwayomi.tachidesk.graphql.server.primitives.GraphQLUpload import suwayomi.tachidesk.graphql.subscriptions.DownloadSubscription @@ -64,6 +65,7 @@ class CustomSchemaGeneratorHooks : FlowSubscriptionSchemaGeneratorHooks() { override fun willGenerateGraphQLType(type: KType): GraphQLType? = when (type.classifier as? KClass<*>) { Long::class -> GraphQLLongAsString // encode to string for JS + Float::class -> GraphQLFloatAsDouble // encode float as double Duration::class -> GraphQLDurationAsString // encode Duration as ISO-8601 string Cursor::class -> GraphQLCursor UploadedFile::class -> GraphQLUpload diff --git a/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/FloatAsDouble.kt b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/FloatAsDouble.kt new file mode 100644 index 00000000..02474fe7 --- /dev/null +++ b/server/src/main/kotlin/suwayomi/tachidesk/graphql/server/primitives/FloatAsDouble.kt @@ -0,0 +1,132 @@ +package suwayomi.tachidesk.graphql.server.primitives + +import graphql.GraphQLContext +import graphql.execution.CoercedVariables +import graphql.language.FloatValue +import graphql.language.Value +import graphql.scalar.CoercingUtil +import graphql.schema.Coercing +import graphql.schema.CoercingParseLiteralException +import graphql.schema.CoercingParseValueException +import graphql.schema.CoercingSerializeException +import graphql.schema.GraphQLScalarType +import java.util.Locale + +val GraphQLFloatAsDouble: GraphQLScalarType = + GraphQLScalarType + .newScalar() + .name( + "Float32", + ).description("32-bit float aka kotlin.lang.Float") + .coercing(GraphqlActualFloatCoercing()) + .build() + +private class GraphqlActualFloatCoercing : Coercing { + private fun toDoubleImpl(input: Any): Double? = + when (input) { + is Float -> input.toDouble() + is Double -> input + is Int -> input.toDouble() + else -> null + } + + private fun parseValueImpl( + input: Any, + locale: Locale, + ): Float = + when (input) { + is Int -> input.toFloat() + is Double -> input.toFloat() + else -> throw CoercingParseValueException( + CoercingUtil.i18nMsg( + locale, + "Float.unexpectedRawValueType", + CoercingUtil.typeName(input), + ), + ) + } + + private fun parseLiteralImpl( + input: Any, + locale: Locale, + ): Float { + if (input !is FloatValue) { + throw CoercingParseLiteralException( + CoercingUtil.i18nMsg( + locale, + "Scalar.unexpectedAstType", + "FloatValue", + CoercingUtil.typeName(input), + ), + ) + } + return input.value.toFloat() + } + + private fun valueToLiteralImpl(input: Any): FloatValue = + FloatValue + .newFloatValue() + .value( + toDoubleImpl(input) ?: throw CoercingSerializeException( + CoercingUtil.i18nMsg( + Locale.getDefault(), + "Float.unexpectedRawValueType", + CoercingUtil.typeName(input), + ), + ), + ).build() + + @Deprecated("") + override fun serialize(dataFetcherResult: Any): Double = + toDoubleImpl(dataFetcherResult) ?: throw CoercingSerializeException( + CoercingUtil.i18nMsg( + Locale.getDefault(), + "Float.unexpectedRawValueType", + CoercingUtil.typeName(dataFetcherResult), + ), + ) + + @Throws(CoercingSerializeException::class) + override fun serialize( + dataFetcherResult: Any, + graphQLContext: GraphQLContext, + locale: Locale, + ): Double = + toDoubleImpl(dataFetcherResult) ?: throw CoercingSerializeException( + CoercingUtil.i18nMsg( + locale, + "Float.unexpectedRawValueType", + CoercingUtil.typeName(dataFetcherResult), + ), + ) + + @Deprecated("") + override fun parseValue(input: Any): Float = parseValueImpl(input, Locale.getDefault()) + + @Throws(CoercingParseValueException::class) + override fun parseValue( + input: Any, + graphQLContext: GraphQLContext, + locale: Locale, + ): Float = parseValueImpl(input, locale) + + @Deprecated("") + override fun parseLiteral(input: Any): Float = parseLiteralImpl(input, Locale.getDefault()) + + @Throws(CoercingParseLiteralException::class) + override fun parseLiteral( + input: Value<*>, + variables: CoercedVariables, + graphQLContext: GraphQLContext, + locale: Locale, + ): Float = parseLiteralImpl(input, locale) + + @Deprecated("") + override fun valueToLiteral(input: Any): Value<*> = valueToLiteralImpl(input) + + override fun valueToLiteral( + input: Any, + graphQLContext: GraphQLContext, + locale: Locale, + ): Value<*> = valueToLiteralImpl(input) +}