Skip to content

Commit 58a48bf

Browse files
Merge pull request #378 from Netflix/input-value-serializer-graphql-java
Leverage graphql-java in InputValueSerializer
2 parents 4f71903 + e07eedd commit 58a48bf

File tree

8 files changed

+177
-66
lines changed

8 files changed

+177
-66
lines changed

graphql-dgs-codegen-client-core/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@ plugins {
2121
}
2222

2323
dependencies {
24+
api platform('com.netflix.graphql.dgs:graphql-dgs-platform-dependencies:latest.release')
2425
implementation platform('com.netflix.graphql.dgs:graphql-dgs-platform-dependencies:latest.release')
2526
compileOnly platform('com.netflix.graphql.dgs:graphql-dgs-platform-dependencies:latest.release')
2627
implementation "org.jetbrains.kotlin:kotlin-reflect"
27-
compileOnly 'com.graphql-java:graphql-java'
28+
api 'com.graphql-java:graphql-java'
2829
compileOnly 'com.fasterxml.jackson.core:jackson-databind'
2930

3031
testImplementation 'com.netflix.graphql.dgs:graphql-dgs-extended-scalars'
31-
testImplementation 'com.graphql-java:graphql-java'
3232
}

graphql-dgs-codegen-client-core/dependencies.lock

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
{
22
"apiDependenciesMetadata": {
3+
"com.graphql-java:graphql-java": {
4+
"locked": "18.0"
5+
},
6+
"com.netflix.graphql.dgs:graphql-dgs-platform-dependencies": {
7+
"locked": "4.10.1"
8+
},
39
"org.jetbrains.kotlin:kotlin-stdlib-jdk8": {
410
"locked": "1.6.21"
511
}
@@ -25,14 +31,14 @@
2531
"com.fasterxml.jackson.core:jackson-databind": {
2632
"locked": "2.13.2"
2733
},
28-
"com.graphql-java:graphql-java": {
29-
"locked": "18.0"
30-
},
3134
"com.netflix.graphql.dgs:graphql-dgs-platform-dependencies": {
3235
"locked": "4.10.1"
3336
}
3437
},
3538
"implementationDependenciesMetadata": {
39+
"com.graphql-java:graphql-java": {
40+
"locked": "18.0"
41+
},
3642
"com.netflix.graphql.dgs:graphql-dgs-platform-dependencies": {
3743
"locked": "4.10.1"
3844
},
@@ -64,6 +70,9 @@
6470
}
6571
},
6672
"runtimeClasspath": {
73+
"com.graphql-java:graphql-java": {
74+
"locked": "18.0"
75+
},
6776
"com.netflix.graphql.dgs:graphql-dgs-platform-dependencies": {
6877
"locked": "4.10.1"
6978
},

graphql-dgs-codegen-client-core/src/main/kotlin/com/netflix/graphql/dgs/client/codegen/InputValueSerializer.kt

Lines changed: 100 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,28 @@
1616

1717
package com.netflix.graphql.dgs.client.codegen
1818

19+
import graphql.language.ArrayValue
20+
import graphql.language.AstPrinter
21+
import graphql.language.BooleanValue
22+
import graphql.language.EnumValue
23+
import graphql.language.FloatValue
24+
import graphql.language.IntValue
25+
import graphql.language.NullValue
26+
import graphql.language.ObjectField
27+
import graphql.language.ObjectValue
28+
import graphql.language.StringValue
29+
import graphql.language.Value
1930
import graphql.schema.Coercing
20-
import java.time.*
21-
import java.util.*
22-
import kotlin.reflect.KProperty1
31+
import java.math.BigDecimal
32+
import java.math.BigInteger
33+
import java.time.Instant
34+
import java.time.LocalDate
35+
import java.time.LocalDateTime
36+
import java.time.LocalTime
37+
import java.time.OffsetDateTime
38+
import java.util.Currency
39+
import java.util.Date
40+
import java.util.TimeZone
2341
import kotlin.reflect.full.allSuperclasses
2442
import kotlin.reflect.full.hasAnnotation
2543
import kotlin.reflect.full.memberProperties
@@ -47,49 +65,90 @@ class InputValueSerializer(private val scalars: Map<Class<*>, Coercing<*, *>> =
4765
}
4866

4967
fun serialize(input: Any?): String {
68+
return AstPrinter.printAst(toValue(input))
69+
}
70+
71+
private fun toValue(input: Any?): Value<*> {
5072
if (input == null) {
51-
return "null"
73+
return NullValue.newNullValue().build()
5274
}
5375

54-
return if (input::class.java in scalars) {
55-
""""${scalars.getValue(input::class.java).serialize(input)}""""
56-
} else if (input::class.javaPrimitiveType != null) {
57-
input.toString()
58-
} else if (input::class.java.isEnum) {
59-
(input as Enum<*>).name
60-
} else if (input::class in toStringClasses) {
61-
// Call toString for known types, in case no scalar is found. This is for backward compatibility.
62-
""""${input.toString().replace("\\", "\\\\").replace("\"", "\\\"")}""""
63-
} else if (input is Collection<*>) {
64-
"""[${input.filterNotNull().joinToString(", ") { listItem -> serialize(listItem) }}]"""
65-
} else if (input is Map<*, *>) {
66-
input.entries.joinToString(", ", "{ ", " }") { (key, value) ->
67-
if (value != null) {
68-
"""$key: ${serialize(value)}"""
69-
} else {
70-
"""$key: null"""
71-
}
72-
}
73-
} else {
74-
val classes = sequenceOf(input::class) + input::class.allSuperclasses.asSequence() - Any::class
75-
val properties = mutableMapOf<String, KProperty1<*, *>>()
76-
77-
for (klass in classes) {
78-
for (property in klass.memberProperties) {
79-
if (property.name in properties || property.isAbstract || property.hasAnnotation<Transient>()) {
80-
continue
81-
}
82-
83-
property.isAccessible = true
84-
properties[property.name] = property
76+
if (input::class.java in scalars) {
77+
return scalars.getValue(input::class.java).valueToLiteral(input)
78+
}
79+
80+
if (input::class in toStringClasses) {
81+
return StringValue.of(input.toString())
82+
}
83+
84+
if (input is String) {
85+
return StringValue.of(input)
86+
}
87+
88+
if (input is Float) {
89+
return FloatValue.of(input.toDouble())
90+
}
91+
92+
if (input is Double) {
93+
return FloatValue.of(input)
94+
}
95+
96+
if (input is BigDecimal) {
97+
return FloatValue.newFloatValue(input).build()
98+
}
99+
100+
if (input is BigInteger) {
101+
return IntValue.newIntValue(input).build()
102+
}
103+
104+
if (input is Int) {
105+
return IntValue.of(input)
106+
}
107+
108+
if (input is Number) {
109+
return IntValue.newIntValue(BigInteger.valueOf(input.toLong())).build()
110+
}
111+
112+
if (input is Boolean) {
113+
return BooleanValue.of(input)
114+
}
115+
116+
if (input is Enum<*>) {
117+
return EnumValue.newEnumValue(input.name).build()
118+
}
119+
120+
if (input is Collection<*>) {
121+
return ArrayValue.newArrayValue()
122+
.values(input.map { toValue(it) })
123+
.build()
124+
}
125+
126+
if (input is Map<*, *>) {
127+
return ObjectValue.newObjectValue()
128+
.objectFields(input.map { (key, value) -> ObjectField(key.toString(), toValue(value)) })
129+
.build()
130+
}
131+
132+
val classes = sequenceOf(input::class) + input::class.allSuperclasses.asSequence() - Any::class
133+
val propertyValues = mutableMapOf<String, Any?>()
134+
135+
for (klass in classes) {
136+
for (property in klass.memberProperties) {
137+
if (property.name in propertyValues || property.isAbstract || property.hasAnnotation<Transient>()) {
138+
continue
85139
}
86-
}
87140

88-
properties.values.asSequence()
89-
.mapNotNull { property ->
90-
val value = property.call(input)
91-
value?.let { """${property.name}:${serialize(value)}""" }
92-
}.joinToString(", ", "{", "}")
141+
property.isAccessible = true
142+
propertyValues[property.name] = property.call(input)
143+
}
93144
}
145+
146+
val objectFields = propertyValues.asSequence()
147+
.filter { (_, value) -> value != null }
148+
.map { (name, value) -> ObjectField(name, toValue(value)) }
149+
.toList()
150+
return ObjectValue.newObjectValue()
151+
.objectFields(objectFields)
152+
.build()
94153
}
95154
}

graphql-dgs-codegen-client-core/src/test/kotlin/com/netflix/graphql/dgs/client/codegen/DateRangeScalar.kt

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.netflix.graphql.dgs.client.codegen
1818

1919
import graphql.language.StringValue
20+
import graphql.language.Value
2021
import graphql.schema.Coercing
2122
import graphql.schema.CoercingParseLiteralException
2223
import graphql.schema.CoercingParseValueException
@@ -25,7 +26,7 @@ import java.time.LocalDate
2526
import java.time.format.DateTimeFormatter
2627

2728
class DateRangeScalar : Coercing<DateRange, String> {
28-
private var formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy")
29+
private val formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy")
2930

3031
@Throws(CoercingSerializeException::class)
3132
override fun serialize(dataFetcherResult: Any): String {
@@ -37,17 +38,21 @@ class DateRangeScalar : Coercing<DateRange, String> {
3738
override fun parseValue(input: Any): DateRange {
3839
val split = (input as String).split("-").toTypedArray()
3940
val from = LocalDate.parse(split[0], formatter)
40-
val to = LocalDate.parse(split[0], formatter)
41+
val to = LocalDate.parse(split[1], formatter)
4142
return DateRange(from, to)
4243
}
4344

4445
@Throws(CoercingParseLiteralException::class)
4546
override fun parseLiteral(input: Any): DateRange {
4647
val split = (input as StringValue).value.split("-").toTypedArray()
4748
val from = LocalDate.parse(split[0], formatter)
48-
val to = LocalDate.parse(split[0], formatter)
49+
val to = LocalDate.parse(split[1], formatter)
4950
return DateRange(from, to)
5051
}
52+
53+
override fun valueToLiteral(input: Any): Value<*> {
54+
return StringValue.of(serialize(input))
55+
}
5156
}
5257

5358
class DateRange(val from: LocalDate, val to: LocalDate)

graphql-dgs-codegen-client-core/src/test/kotlin/com/netflix/graphql/dgs/client/codegen/GraphQLQueryRequestTest.kt

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,13 @@
1818

1919
package com.netflix.graphql.dgs.client.codegen
2020

21+
import graphql.language.StringValue
22+
import graphql.language.Value
2123
import graphql.schema.Coercing
2224
import org.assertj.core.api.Assertions.assertThat
2325
import org.junit.jupiter.api.Test
2426
import java.time.LocalDate
25-
import java.util.*
27+
import java.util.UUID
2628

2729
class GraphQLQueryRequestTest {
2830
@Test
@@ -64,7 +66,7 @@ class GraphQLQueryRequestTest {
6466
}
6567
val request = GraphQLQueryRequest(query)
6668
val result = request.serialize()
67-
assertThat(result).isEqualTo("""query {test(movie: {movieId:1234, name:"testMovie"}) }""")
69+
assertThat(result).isEqualTo("""query {test(movie: {movieId : 1234, name : "testMovie"}) }""")
6870
}
6971

7072
@Test
@@ -74,7 +76,7 @@ class GraphQLQueryRequestTest {
7476
}
7577
val request = GraphQLQueryRequest(query, MovieProjection().name().movieId())
7678
val result = request.serialize()
77-
assertThat(result).isEqualTo("""query {test(movie: {movieId:1234, name:"testMovie"}){ name movieId } }""")
79+
assertThat(result).isEqualTo("""query {test(movie: {movieId : 1234, name : "testMovie"}){ name movieId } }""")
7880
}
7981

8082
@Test
@@ -84,7 +86,7 @@ class GraphQLQueryRequestTest {
8486
}
8587
val request = GraphQLQueryRequest(query, MovieProjection().name().movieId())
8688
val result = request.serialize()
87-
assertThat(result).isEqualTo("""mutation {testMutation(movie: {movieId:1234, name:"testMovie"}){ name movieId } }""")
89+
assertThat(result).isEqualTo("""mutation {testMutation(movie: {movieId : 1234, name : "testMovie"}){ name movieId } }""")
8890
}
8991

9092
@Test
@@ -94,7 +96,7 @@ class GraphQLQueryRequestTest {
9496
}
9597
val request = GraphQLQueryRequest(query, MovieProjection().name().movieId())
9698
val result = request.serialize()
97-
assertThat(result).isEqualTo("""query TestNamedQuery {test(movie: {movieId:123, name:"greatMovie"}){ name movieId } }""")
99+
assertThat(result).isEqualTo("""query TestNamedQuery {test(movie: {movieId : 123, name : "greatMovie"}){ name movieId } }""")
98100
}
99101

100102
@Test
@@ -107,7 +109,7 @@ class GraphQLQueryRequestTest {
107109
GraphQLQueryRequest(query, MovieProjection(), mapOf(DateRange::class.java to DateRangeScalar()))
108110

109111
val result = request.serialize()
110-
assertThat(result).isEqualTo("""query TestNamedQuery {test(movie: {movieId:123, name:"greatMovie"}, dateRange: "01/01/2020-05/11/2021") }""")
112+
assertThat(result).isEqualTo("""query TestNamedQuery {test(movie: {movieId : 123, name : "greatMovie"}, dateRange: "01/01/2020-05/11/2021") }""")
111113
}
112114

113115
@Test
@@ -121,7 +123,7 @@ class GraphQLQueryRequestTest {
121123
GraphQLQueryRequest(query, MovieProjection(), mapOf(UUID::class.java to UUIDScalar))
122124

123125
val result = request.serialize()
124-
assertThat(result).isEqualTo("query TestNamedQuery {test(id: \"$randomUUID\") }")
126+
assertThat(result).isEqualTo("""query TestNamedQuery {test(id: "$randomUUID") }""")
125127
}
126128

127129
object UUIDScalar : Coercing<UUID, String> {
@@ -136,6 +138,10 @@ class GraphQLQueryRequestTest {
136138
override fun parseLiteral(input: Any): UUID {
137139
return UUID.fromString(input.toString())
138140
}
141+
142+
override fun valueToLiteral(input: Any): Value<*> {
143+
return StringValue.of(serialize(input))
144+
}
139145
}
140146

141147
@Test
@@ -147,7 +153,7 @@ class GraphQLQueryRequestTest {
147153
GraphQLQueryRequest(query, MovieProjection(), mapOf(DateRange::class.java to DateRangeScalar()))
148154

149155
val result = request.serialize()
150-
assertThat(result).isEqualTo("""query TestNamedQuery {test(movie: {movieId:123, name:"greatMovie", window:"01/01/2020-05/11/2021"}) }""")
156+
assertThat(result).isEqualTo("""query TestNamedQuery {test(movie: {movieId : 123, name : "greatMovie", window : "01/01/2020-05/11/2021"}) }""")
151157
}
152158

153159
@Test
@@ -158,7 +164,7 @@ class GraphQLQueryRequestTest {
158164
}
159165
val request = GraphQLQueryRequest(query, MovieProjection(), mapOf(DateRange::class.java to DateRangeScalar()))
160166
val result = request.serialize()
161-
assertThat(result).isEqualTo("""query {test(actors: { name: "actorA", movies: ["movie1", "movie2"] }, movie: {movieId:123, name:"greatMovie", window:"01/01/2020-05/11/2021"}) }""")
167+
assertThat(result).isEqualTo("""query {test(actors: {name : "actorA", movies : ["movie1", "movie2"]}, movie: {movieId : 123, name : "greatMovie", window : "01/01/2020-05/11/2021"}) }""")
162168
}
163169
}
164170

0 commit comments

Comments
 (0)