Skip to content

Commit acec214

Browse files
authored
Merge pull request #156 from Bertrand/support_for_short_scalar
feat(native scalars): Add support for `Short` native kotlin type
2 parents 41446a7 + cb0ca7c commit acec214

File tree

9 files changed

+113
-8
lines changed

9 files changed

+113
-8
lines changed

docs/content/Reference/Type System/overview.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ By default, every schema has following built in types:
1919

2020
### Scalars
2121
* **String** - represents textual data, represented as UTF‐8 character sequences
22+
* **Short** - represents a signed 16‐bit numeric non‐fractional value
2223
* **Int** - represents a signed 32‐bit numeric non‐fractional value
2324
* **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.
2425
* **Float** - represents signed double‐precision fractional values as specified by IEEE 754. KGraphQL represents Kotlin primitive Double and Float values as GraphQL Float.

kgraphql/src/main/kotlin/com/apurebase/kgraphql/schema/builtin/BUILT_IN_TYPE.kt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ import com.apurebase.kgraphql.schema.scalar.StringScalarCoercion
1515
private const val STRING_DESCRIPTION =
1616
"The String scalar type represents textual data, represented as UTF-8 character sequences"
1717

18+
private const val SHORT_DESCRIPTION =
19+
"The Short scalar type represents a signed 16-bit numeric non-fractional value"
20+
1821
private const val INT_DESCRIPTION =
1922
"The Int scalar type represents a signed 32-bit numeric non-fractional value"
2023

@@ -34,6 +37,8 @@ object BUILT_IN_TYPE {
3437

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

40+
val SHORT = TypeDef.Scalar(Short::class.defaultKQLTypeName(), Short::class, SHORT_COERCION, SHORT_DESCRIPTION)
41+
3742
val INT = TypeDef.Scalar(Int::class.defaultKQLTypeName(), Int::class, INT_COERCION, INT_DESCRIPTION)
3843

3944
//GraphQL does not differ float and double, treat double like float
@@ -116,6 +121,32 @@ object INT_COERCION : StringScalarCoercion<Int>{
116121
}
117122
}
118123

124+
object SHORT_COERCION : StringScalarCoercion<Short>{
125+
override fun serialize(instance: Short): String = instance.toString()
126+
127+
override fun deserialize(raw: String, valueNode: ValueNode?) = when (valueNode) {
128+
null -> {
129+
if(!raw.isLiteral()) raw.toShort()
130+
else throw GraphQLError("Cannot coerce string literal, expected numeric string constant")
131+
}
132+
is NumberValueNode -> when {
133+
valueNode.value > Short.MAX_VALUE -> throw GraphQLError(
134+
"Cannot coerce to type of Int as '${valueNode.value}' is greater than (2^-15)-1",
135+
valueNode
136+
)
137+
valueNode.value < Short.MIN_VALUE -> throw GraphQLError(
138+
"Cannot coerce to type of Int as '${valueNode.value}' is less than -(2^-15)",
139+
valueNode
140+
)
141+
else -> valueNode.value.toShort()
142+
}
143+
else -> throw GraphQLError(
144+
"Cannot coerce ${valueNode.valueNodeName} to numeric constant",
145+
valueNode
146+
)
147+
}
148+
}
149+
119150
object LONG_COERCION : StringScalarCoercion<Long> {
120151
override fun serialize(instance: Long): String = instance.toString()
121152

kgraphql/src/main/kotlin/com/apurebase/kgraphql/schema/dsl/SchemaBuilder.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,16 @@ class SchemaBuilder internal constructor() {
8080
stringScalar(T::class, block)
8181
}
8282

83+
fun <T : Any> shortScalar(kClass: KClass<T>, block: ScalarDSL<T, Short>.() -> Unit) {
84+
val scalar = ShortScalarDSL(kClass).apply(block)
85+
configuration.appendMapper(scalar, kClass)
86+
model.addScalar(TypeDef.Scalar(scalar.name, kClass, scalar.createCoercion(), scalar.description))
87+
}
88+
89+
inline fun <reified T : Any> shortScalar(noinline block: ScalarDSL<T, Short>.() -> Unit) {
90+
shortScalar(T::class, block)
91+
}
92+
8393
fun <T : Any> intScalar(kClass: KClass<T>, block: ScalarDSL<T, Int>.() -> Unit) {
8494
val scalar = IntScalarDSL(kClass).apply(block)
8595
configuration.appendMapper(scalar, kClass)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.apurebase.kgraphql.schema.dsl.types
2+
3+
4+
import com.apurebase.kgraphql.schema.SchemaException
5+
import com.apurebase.kgraphql.schema.model.ast.ValueNode
6+
import com.apurebase.kgraphql.schema.scalar.ShortScalarCoercion
7+
import com.apurebase.kgraphql.schema.scalar.ScalarCoercion
8+
import kotlin.reflect.KClass
9+
10+
11+
class ShortScalarDSL<T : Any>(kClass: KClass<T>) : ScalarDSL<T, Short>(kClass) {
12+
13+
override fun createCoercionFromFunctions(): ScalarCoercion<T, Short> {
14+
return object : ShortScalarCoercion<T> {
15+
16+
val serializeImpl = serialize ?: throw SchemaException(PLEASE_SPECIFY_COERCION)
17+
18+
val deserializeImpl = deserialize ?: throw SchemaException(PLEASE_SPECIFY_COERCION)
19+
20+
override fun serialize(instance: T): Short = serializeImpl(instance)
21+
22+
override fun deserialize(raw: Short, valueNode: ValueNode?): T = deserializeImpl(raw)
23+
}
24+
}
25+
26+
}

kgraphql/src/main/kotlin/com/apurebase/kgraphql/schema/model/MutableSchemaDefinition.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ data class MutableSchemaDefinition (
2626
BUILT_IN_TYPE.BOOLEAN,
2727
BUILT_IN_TYPE.DOUBLE,
2828
BUILT_IN_TYPE.FLOAT,
29+
BUILT_IN_TYPE.SHORT,
2930
BUILT_IN_TYPE.INT,
30-
BUILT_IN_TYPE.LONG
31+
BUILT_IN_TYPE.LONG,
32+
3133
),
3234
private val mutations: ArrayList<MutationDef<*>> = arrayListOf(),
3335
private val subscriptions: ArrayList<SubscriptionDef<*>> = arrayListOf(),

kgraphql/src/main/kotlin/com/apurebase/kgraphql/schema/scalar/Coercion.kt

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,11 @@ package com.apurebase.kgraphql.schema.scalar
33
import com.apurebase.kgraphql.ExecutionException
44
import com.apurebase.kgraphql.dropQuotes
55
import com.fasterxml.jackson.databind.node.JsonNodeFactory
6-
import com.apurebase.kgraphql.schema.builtin.BOOLEAN_COERCION
7-
import com.apurebase.kgraphql.schema.builtin.DOUBLE_COERCION
8-
import com.apurebase.kgraphql.schema.builtin.FLOAT_COERCION
9-
import com.apurebase.kgraphql.schema.builtin.INT_COERCION
10-
import com.apurebase.kgraphql.schema.builtin.LONG_COERCION
11-
import com.apurebase.kgraphql.schema.builtin.STRING_COERCION
126
import com.apurebase.kgraphql.schema.execution.Execution
137
import com.apurebase.kgraphql.schema.model.ast.ValueNode
148
import com.apurebase.kgraphql.schema.model.ast.ValueNode.*
159
import com.apurebase.kgraphql.GraphQLError
10+
import com.apurebase.kgraphql.schema.builtin.*
1611
import com.apurebase.kgraphql.schema.structure.Type
1712
import kotlinx.serialization.json.JsonElement
1813
import kotlinx.serialization.json.JsonPrimitive
@@ -28,11 +23,13 @@ fun <T : Any> deserializeScalar(scalar: Type.Scalar<T>, value : ValueNode): T {
2823
STRING_COERCION -> STRING_COERCION.deserialize(value.valueNodeName, value as StringValueNode) as T
2924
FLOAT_COERCION -> FLOAT_COERCION.deserialize(value.valueNodeName, value) as T
3025
DOUBLE_COERCION -> DOUBLE_COERCION.deserialize(value.valueNodeName, value) as T
26+
SHORT_COERCION -> SHORT_COERCION.deserialize(value.valueNodeName, value) as T
3127
INT_COERCION -> INT_COERCION.deserialize(value.valueNodeName, value) as T
3228
BOOLEAN_COERCION -> BOOLEAN_COERCION.deserialize(value.valueNodeName, value) as T
3329
LONG_COERCION -> LONG_COERCION.deserialize(value.valueNodeName, value) as T
3430

3531
is StringScalarCoercion<T> -> scalar.coercion.deserialize(value.valueNodeName.dropQuotes(), value)
32+
is ShortScalarCoercion<T> -> scalar.coercion.deserialize(value.valueNodeName.toShort(), value)
3633
is IntScalarCoercion<T> -> scalar.coercion.deserialize(value.valueNodeName.toInt(), value)
3734
is DoubleScalarCoercion<T> -> scalar.coercion.deserialize(value.valueNodeName.toDouble(), value)
3835
is BooleanScalarCoercion<T> -> scalar.coercion.deserialize(value.valueNodeName.toBoolean(), value)
@@ -57,6 +54,9 @@ fun <T> serializeScalar(jsonNodeFactory: JsonNodeFactory, scalar: Type.Scalar<*>
5754
is StringScalarCoercion<*> -> {
5855
jsonNodeFactory.textNode((scalar.coercion as StringScalarCoercion<T>).serialize(value))
5956
}
57+
is ShortScalarCoercion<*> -> {
58+
jsonNodeFactory.numberNode((scalar.coercion as ShortScalarCoercion<T>).serialize(value))
59+
}
6060
is IntScalarCoercion<*> -> {
6161
jsonNodeFactory.numberNode((scalar.coercion as IntScalarCoercion<T>).serialize(value))
6262
}
@@ -77,6 +77,9 @@ fun <T> serializeScalar(scalar: Type.Scalar<*>, value: T, executionNode: Executi
7777
is StringScalarCoercion<*> -> {
7878
JsonPrimitive((scalar.coercion as StringScalarCoercion<T>).serialize(value))
7979
}
80+
is ShortScalarCoercion<*> -> {
81+
JsonPrimitive((scalar.coercion as ShortScalarCoercion<T>).serialize(value))
82+
}
8083
is IntScalarCoercion<*> -> {
8184
JsonPrimitive((scalar.coercion as IntScalarCoercion<T>).serialize(value))
8285
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
package com.apurebase.kgraphql.schema.scalar
2+
3+
interface ShortScalarCoercion<T> : ScalarCoercion<T, Short>

kgraphql/src/test/kotlin/com/apurebase/kgraphql/schema/SchemaBuilderTest.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,25 @@ class SchemaBuilderTest {
592592
assertThat(names, hasItem("TypeAsObject"))
593593
}
594594

595+
@Test
596+
fun `Short int types are mapped to Short Scalar`(){
597+
val schema = defaultSchema {
598+
query("shortQuery") {
599+
resolver { -> 1 as Short }
600+
}
601+
}
602+
603+
604+
val typesIntrospection = deserialize(schema.executeBlocking("{__schema{types{name}}}"))
605+
val types = typesIntrospection.extract<List<Map<String,String>>>("data/__schema/types")
606+
val names = types.map {it["name"]}
607+
assertThat(names, hasItem("Short"))
608+
609+
val response = deserialize(schema.executeBlocking("{__schema{queryType{fields{ type { ofType { kind name }}}}}}"))
610+
assertThat(response.extract("data/__schema/queryType/fields[0]/type/ofType/kind"), equalTo("SCALAR"))
611+
assertThat(response.extract("data/__schema/queryType/fields[0]/type/ofType/name"), equalTo("Short"))
612+
}
613+
595614
@Test
596615
fun `Resolver cannot return an Unit value`(){
597616
invoking {

kgraphql/src/test/kotlin/com/apurebase/kgraphql/specification/typesystem/ScalarsSpecificationTest.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ class ScalarsSpecificationTest {
152152
}
153153

154154
data class Boo(val boolean: Boolean)
155+
data class Sho(val short: Short)
155156
data class Lon(val long: Long)
156157
data class Dob(val double: Double)
157158
data class Num(val int: Int)
@@ -192,6 +193,10 @@ class ScalarsSpecificationTest {
192193
deserialize = ::Dob
193194
serialize = { (double) -> double }
194195
}
196+
shortScalar<Sho> {
197+
deserialize = ::Sho
198+
serialize = { (short) -> short }
199+
}
195200
intScalar<Num> {
196201
deserialize = ::Num
197202
serialize = { (num) -> num }
@@ -203,6 +208,7 @@ class ScalarsSpecificationTest {
203208

204209
query("boo") { resolver { boo: Boo -> boo } }
205210
query("lon") { resolver { lon: Lon -> lon } }
211+
query("sho") { resolver { sho: Sho -> sho } }
206212
query("dob") { resolver { dob: Dob -> dob } }
207213
query("num") { resolver { num: Num -> num } }
208214
query("str") { resolver { str: Str -> str } }
@@ -211,14 +217,16 @@ class ScalarsSpecificationTest {
211217

212218
val booValue = true
213219
val lonValue = 124L
220+
val shoValue: Short = 1
214221
val dobValue = 2.5
215222
val numValue = 155
216223
val strValue = "Test"
217224
val d = '$'
218225

219226
val req = """
220-
query Query(${d}boo: Boo!, ${d}lon: Lon!, ${d}dob: Dob!, ${d}num: Num!, ${d}str: Str!){
227+
query Query(${d}boo: Boo!, ${d}sho: Sho!, ${d}lon: Lon!, ${d}dob: Dob!, ${d}num: Num!, ${d}str: Str!){
221228
boo(boo: ${d}boo)
229+
sho(sho: ${d}sho)
222230
lon(lon: ${d}lon)
223231
dob(dob: ${d}dob)
224232
num(num: ${d}num)
@@ -230,6 +238,7 @@ class ScalarsSpecificationTest {
230238
val values = """
231239
{
232240
"boo": $booValue,
241+
"sho": $shoValue,
233242
"lon": $lonValue,
234243
"dob": $dobValue,
235244
"num": $numValue,
@@ -240,6 +249,7 @@ class ScalarsSpecificationTest {
240249
try {
241250
val response = deserialize(schema.executeBlocking(req, values))
242251
assertThat(response.extract<Boolean>("data/boo"), equalTo(booValue))
252+
assertThat(response.extract<Int>("data/sho"), equalTo(shoValue.toInt()))
243253
assertThat(response.extract<Int>("data/lon"), equalTo(lonValue.toInt()))
244254
assertThat(response.extract<Double>("data/dob"), equalTo(dobValue))
245255
assertThat(response.extract<Int>("data/num"), equalTo(numValue))

0 commit comments

Comments
 (0)