Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/content/Reference/Type System/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ By default, every schema has following built in types:

### Scalars
* **String** - represents textual data, represented as UTF‐8 character sequences
* **Short** - represents a signed 16‐bit numeric non‐fractional value
* **Int** - represents a signed 32‐bit numeric non‐fractional value
* **Long** - represents a signed 64‐bit numeric non‐fractional value. Long type is not part of GraphQL specification, but it is built in primitive type in Kotlin language.
* **Float** - represents signed double‐precision fractional values as specified by IEEE 754. KGraphQL represents Kotlin primitive Double and Float values as GraphQL Float.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ import com.apurebase.kgraphql.schema.scalar.StringScalarCoercion
private const val STRING_DESCRIPTION =
"The String scalar type represents textual data, represented as UTF-8 character sequences"

private const val SHORT_DESCRIPTION =
"The Short scalar type represents a signed 16-bit numeric non-fractional value"

private const val INT_DESCRIPTION =
"The Int scalar type represents a signed 32-bit numeric non-fractional value"

Expand All @@ -34,6 +37,8 @@ object BUILT_IN_TYPE {

val STRING = TypeDef.Scalar(String::class.defaultKQLTypeName(), String::class, STRING_COERCION, STRING_DESCRIPTION)

val SHORT = TypeDef.Scalar(Short::class.defaultKQLTypeName(), Short::class, SHORT_COERCION, SHORT_DESCRIPTION)

val INT = TypeDef.Scalar(Int::class.defaultKQLTypeName(), Int::class, INT_COERCION, INT_DESCRIPTION)

//GraphQL does not differ float and double, treat double like float
Expand Down Expand Up @@ -116,6 +121,32 @@ object INT_COERCION : StringScalarCoercion<Int>{
}
}

object SHORT_COERCION : StringScalarCoercion<Short>{
override fun serialize(instance: Short): String = instance.toString()

override fun deserialize(raw: String, valueNode: ValueNode?) = when (valueNode) {
null -> {
if(!raw.isLiteral()) raw.toShort()
else throw GraphQLError("Cannot coerce string literal, expected numeric string constant")
}
is NumberValueNode -> when {
valueNode.value > Short.MAX_VALUE -> throw GraphQLError(
"Cannot coerce to type of Int as '${valueNode.value}' is greater than (2^-15)-1",
valueNode
)
valueNode.value < Short.MIN_VALUE -> throw GraphQLError(
"Cannot coerce to type of Int as '${valueNode.value}' is less than -(2^-15)",
valueNode
)
else -> valueNode.value.toShort()
}
else -> throw GraphQLError(
"Cannot coerce ${valueNode.valueNodeName} to numeric constant",
valueNode
)
}
}

object LONG_COERCION : StringScalarCoercion<Long> {
override fun serialize(instance: Long): String = instance.toString()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,16 @@ class SchemaBuilder internal constructor() {
stringScalar(T::class, block)
}

fun <T : Any> shortScalar(kClass: KClass<T>, block: ScalarDSL<T, Short>.() -> Unit) {
val scalar = ShortScalarDSL(kClass).apply(block)
configuration.appendMapper(scalar, kClass)
model.addScalar(TypeDef.Scalar(scalar.name, kClass, scalar.createCoercion(), scalar.description))
}

inline fun <reified T : Any> shortScalar(noinline block: ScalarDSL<T, Short>.() -> Unit) {
shortScalar(T::class, block)
}

fun <T : Any> intScalar(kClass: KClass<T>, block: ScalarDSL<T, Int>.() -> Unit) {
val scalar = IntScalarDSL(kClass).apply(block)
configuration.appendMapper(scalar, kClass)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.apurebase.kgraphql.schema.dsl.types


import com.apurebase.kgraphql.schema.SchemaException
import com.apurebase.kgraphql.schema.model.ast.ValueNode
import com.apurebase.kgraphql.schema.scalar.ShortScalarCoercion
import com.apurebase.kgraphql.schema.scalar.ScalarCoercion
import kotlin.reflect.KClass


class ShortScalarDSL<T : Any>(kClass: KClass<T>) : ScalarDSL<T, Short>(kClass) {

override fun createCoercionFromFunctions(): ScalarCoercion<T, Short> {
return object : ShortScalarCoercion<T> {

val serializeImpl = serialize ?: throw SchemaException(PLEASE_SPECIFY_COERCION)

val deserializeImpl = deserialize ?: throw SchemaException(PLEASE_SPECIFY_COERCION)

override fun serialize(instance: T): Short = serializeImpl(instance)

override fun deserialize(raw: Short, valueNode: ValueNode?): T = deserializeImpl(raw)
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ data class MutableSchemaDefinition (
BUILT_IN_TYPE.BOOLEAN,
BUILT_IN_TYPE.DOUBLE,
BUILT_IN_TYPE.FLOAT,
BUILT_IN_TYPE.SHORT,
BUILT_IN_TYPE.INT,
BUILT_IN_TYPE.LONG
BUILT_IN_TYPE.LONG,

),
private val mutations: ArrayList<MutationDef<*>> = arrayListOf(),
private val subscriptions: ArrayList<SubscriptionDef<*>> = arrayListOf(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,11 @@ package com.apurebase.kgraphql.schema.scalar
import com.apurebase.kgraphql.ExecutionException
import com.apurebase.kgraphql.dropQuotes
import com.fasterxml.jackson.databind.node.JsonNodeFactory
import com.apurebase.kgraphql.schema.builtin.BOOLEAN_COERCION
import com.apurebase.kgraphql.schema.builtin.DOUBLE_COERCION
import com.apurebase.kgraphql.schema.builtin.FLOAT_COERCION
import com.apurebase.kgraphql.schema.builtin.INT_COERCION
import com.apurebase.kgraphql.schema.builtin.LONG_COERCION
import com.apurebase.kgraphql.schema.builtin.STRING_COERCION
import com.apurebase.kgraphql.schema.execution.Execution
import com.apurebase.kgraphql.schema.model.ast.ValueNode
import com.apurebase.kgraphql.schema.model.ast.ValueNode.*
import com.apurebase.kgraphql.GraphQLError
import com.apurebase.kgraphql.schema.builtin.*
import com.apurebase.kgraphql.schema.structure.Type
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
Expand All @@ -28,11 +23,13 @@ fun <T : Any> deserializeScalar(scalar: Type.Scalar<T>, value : ValueNode): T {
STRING_COERCION -> STRING_COERCION.deserialize(value.valueNodeName, value as StringValueNode) as T
FLOAT_COERCION -> FLOAT_COERCION.deserialize(value.valueNodeName, value) as T
DOUBLE_COERCION -> DOUBLE_COERCION.deserialize(value.valueNodeName, value) as T
SHORT_COERCION -> SHORT_COERCION.deserialize(value.valueNodeName, value) as T
INT_COERCION -> INT_COERCION.deserialize(value.valueNodeName, value) as T
BOOLEAN_COERCION -> BOOLEAN_COERCION.deserialize(value.valueNodeName, value) as T
LONG_COERCION -> LONG_COERCION.deserialize(value.valueNodeName, value) as T

is StringScalarCoercion<T> -> scalar.coercion.deserialize(value.valueNodeName.dropQuotes(), value)
is ShortScalarCoercion<T> -> scalar.coercion.deserialize(value.valueNodeName.toShort(), value)
is IntScalarCoercion<T> -> scalar.coercion.deserialize(value.valueNodeName.toInt(), value)
is DoubleScalarCoercion<T> -> scalar.coercion.deserialize(value.valueNodeName.toDouble(), value)
is BooleanScalarCoercion<T> -> scalar.coercion.deserialize(value.valueNodeName.toBoolean(), value)
Expand All @@ -57,6 +54,9 @@ fun <T> serializeScalar(jsonNodeFactory: JsonNodeFactory, scalar: Type.Scalar<*>
is StringScalarCoercion<*> -> {
jsonNodeFactory.textNode((scalar.coercion as StringScalarCoercion<T>).serialize(value))
}
is ShortScalarCoercion<*> -> {
jsonNodeFactory.numberNode((scalar.coercion as ShortScalarCoercion<T>).serialize(value))
}
is IntScalarCoercion<*> -> {
jsonNodeFactory.numberNode((scalar.coercion as IntScalarCoercion<T>).serialize(value))
}
Expand All @@ -77,6 +77,9 @@ fun <T> serializeScalar(scalar: Type.Scalar<*>, value: T, executionNode: Executi
is StringScalarCoercion<*> -> {
JsonPrimitive((scalar.coercion as StringScalarCoercion<T>).serialize(value))
}
is ShortScalarCoercion<*> -> {
JsonPrimitive((scalar.coercion as ShortScalarCoercion<T>).serialize(value))
}
is IntScalarCoercion<*> -> {
JsonPrimitive((scalar.coercion as IntScalarCoercion<T>).serialize(value))
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.apurebase.kgraphql.schema.scalar

interface ShortScalarCoercion<T> : ScalarCoercion<T, Short>
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,25 @@ class SchemaBuilderTest {
assertThat(names, hasItem("TypeAsObject"))
}

@Test
fun `Short int types are mapped to Short Scalar`(){
val schema = defaultSchema {
query("shortQuery") {
resolver { -> 1 as Short }
}
}


val typesIntrospection = deserialize(schema.executeBlocking("{__schema{types{name}}}"))
val types = typesIntrospection.extract<List<Map<String,String>>>("data/__schema/types")
val names = types.map {it["name"]}
assertThat(names, hasItem("Short"))

val response = deserialize(schema.executeBlocking("{__schema{queryType{fields{ type { ofType { kind name }}}}}}"))
assertThat(response.extract("data/__schema/queryType/fields[0]/type/ofType/kind"), equalTo("SCALAR"))
assertThat(response.extract("data/__schema/queryType/fields[0]/type/ofType/name"), equalTo("Short"))
}

@Test
fun `Resolver cannot return an Unit value`(){
invoking {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ class ScalarsSpecificationTest {
}

data class Boo(val boolean: Boolean)
data class Sho(val short: Short)
data class Lon(val long: Long)
data class Dob(val double: Double)
data class Num(val int: Int)
Expand Down Expand Up @@ -192,6 +193,10 @@ class ScalarsSpecificationTest {
deserialize = ::Dob
serialize = { (double) -> double }
}
shortScalar<Sho> {
deserialize = ::Sho
serialize = { (short) -> short }
}
intScalar<Num> {
deserialize = ::Num
serialize = { (num) -> num }
Expand All @@ -203,6 +208,7 @@ class ScalarsSpecificationTest {

query("boo") { resolver { boo: Boo -> boo } }
query("lon") { resolver { lon: Lon -> lon } }
query("sho") { resolver { sho: Sho -> sho } }
query("dob") { resolver { dob: Dob -> dob } }
query("num") { resolver { num: Num -> num } }
query("str") { resolver { str: Str -> str } }
Expand All @@ -211,14 +217,16 @@ class ScalarsSpecificationTest {

val booValue = true
val lonValue = 124L
val shoValue: Short = 1
val dobValue = 2.5
val numValue = 155
val strValue = "Test"
val d = '$'

val req = """
query Query(${d}boo: Boo!, ${d}lon: Lon!, ${d}dob: Dob!, ${d}num: Num!, ${d}str: Str!){
query Query(${d}boo: Boo!, ${d}sho: Sho!, ${d}lon: Lon!, ${d}dob: Dob!, ${d}num: Num!, ${d}str: Str!){
boo(boo: ${d}boo)
sho(sho: ${d}sho)
lon(lon: ${d}lon)
dob(dob: ${d}dob)
num(num: ${d}num)
Expand All @@ -230,6 +238,7 @@ class ScalarsSpecificationTest {
val values = """
{
"boo": $booValue,
"sho": $shoValue,
"lon": $lonValue,
"dob": $dobValue,
"num": $numValue,
Expand All @@ -240,6 +249,7 @@ class ScalarsSpecificationTest {
try {
val response = deserialize(schema.executeBlocking(req, values))
assertThat(response.extract<Boolean>("data/boo"), equalTo(booValue))
assertThat(response.extract<Int>("data/sho"), equalTo(shoValue.toInt()))
assertThat(response.extract<Int>("data/lon"), equalTo(lonValue.toInt()))
assertThat(response.extract<Double>("data/dob"), equalTo(dobValue))
assertThat(response.extract<Int>("data/num"), equalTo(numValue))
Expand Down