Skip to content

Commit 0dfb6c3

Browse files
author
Dariusz Kuc
authored
[generator] avoid duplicate argument deserialization (ExpediaGroup#1379)
1 parent c1284dd commit 0dfb6c3

File tree

42 files changed

+485
-526
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+485
-526
lines changed

examples/server/spring-server/src/main/kotlin/com/expediagroup/graphql/examples/server/spring/Application.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2021 Expedia, Inc
2+
* Copyright 2022 Expedia, Inc
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -24,7 +24,6 @@ import com.expediagroup.graphql.examples.server.spring.execution.SpringDataFetch
2424
import com.expediagroup.graphql.examples.server.spring.hooks.CustomSchemaGeneratorHooks
2525
import com.expediagroup.graphql.generator.directives.KotlinDirectiveWiringFactory
2626
import com.expediagroup.graphql.server.spring.subscriptions.ApolloSubscriptionHooks
27-
import com.fasterxml.jackson.databind.ObjectMapper
2827
import graphql.execution.DataFetcherExceptionHandler
2928
import org.springframework.boot.autoconfigure.SpringBootApplication
3029
import org.springframework.boot.runApplication
@@ -43,9 +42,8 @@ class Application {
4342
@Bean
4443
fun dataFetcherFactoryProvider(
4544
springDataFetcherFactory: SpringDataFetcherFactory,
46-
objectMapper: ObjectMapper,
4745
applicationContext: ApplicationContext
48-
) = CustomDataFetcherFactoryProvider(springDataFetcherFactory, objectMapper, applicationContext)
46+
) = CustomDataFetcherFactoryProvider(springDataFetcherFactory, applicationContext)
4947

5048
@Bean
5149
fun dataFetcherExceptionHandler(): DataFetcherExceptionHandler = CustomDataFetcherExceptionHandler()

examples/server/spring-server/src/main/kotlin/com/expediagroup/graphql/examples/server/spring/execution/CustomDataFetcherFactoryProvider.kt

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2021 Expedia, Inc
2+
* Copyright 2022 Expedia, Inc
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,7 +17,6 @@
1717
package com.expediagroup.graphql.examples.server.spring.execution
1818

1919
import com.expediagroup.graphql.generator.execution.SimpleKotlinDataFetcherFactoryProvider
20-
import com.fasterxml.jackson.databind.ObjectMapper
2120
import graphql.schema.DataFetcherFactory
2221
import org.springframework.context.ApplicationContext
2322
import kotlin.reflect.KClass
@@ -29,15 +28,13 @@ import kotlin.reflect.KProperty
2928
*/
3029
class CustomDataFetcherFactoryProvider(
3130
private val springDataFetcherFactory: SpringDataFetcherFactory,
32-
private val objectMapper: ObjectMapper,
3331
private val applicationContext: ApplicationContext
34-
) : SimpleKotlinDataFetcherFactoryProvider(objectMapper) {
32+
) : SimpleKotlinDataFetcherFactoryProvider() {
3533

3634
override fun functionDataFetcherFactory(target: Any?, kFunction: KFunction<*>) = DataFetcherFactory {
3735
CustomFunctionDataFetcher(
3836
target = target,
3937
fn = kFunction,
40-
objectMapper = objectMapper,
4138
appContext = applicationContext
4239
)
4340
}

examples/server/spring-server/src/main/kotlin/com/expediagroup/graphql/examples/server/spring/execution/CustomFunctionDataFetcher.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2021 Expedia, Inc
2+
* Copyright 2022 Expedia, Inc
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,7 +17,6 @@
1717
package com.expediagroup.graphql.examples.server.spring.execution
1818

1919
import com.expediagroup.graphql.server.spring.execution.SpringDataFetcher
20-
import com.fasterxml.jackson.databind.ObjectMapper
2120
import graphql.schema.DataFetchingEnvironment
2221
import org.springframework.context.ApplicationContext
2322
import reactor.core.publisher.Mono
@@ -29,9 +28,8 @@ import kotlin.reflect.KFunction
2928
class CustomFunctionDataFetcher(
3029
target: Any?,
3130
fn: KFunction<*>,
32-
objectMapper: ObjectMapper,
3331
appContext: ApplicationContext
34-
) : SpringDataFetcher(target, fn, objectMapper, appContext) {
32+
) : SpringDataFetcher(target, fn, appContext) {
3533

3634
override fun get(environment: DataFetchingEnvironment): Any? = when (val result = super.get(environment)) {
3735
is Mono<*> -> result.toFuture()

examples/server/spring-server/src/main/kotlin/com/expediagroup/graphql/examples/server/spring/hooks/CustomSchemaGeneratorHooks.kt

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2021 Expedia, Inc
2+
* Copyright 2022 Expedia, Inc
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@ import graphql.schema.GraphQLScalarType
2727
import graphql.schema.GraphQLType
2828
import org.springframework.beans.factory.BeanFactoryAware
2929
import reactor.core.publisher.Mono
30+
import java.time.LocalDate
3031
import java.util.UUID
3132
import kotlin.reflect.KClass
3233
import kotlin.reflect.KType
@@ -43,6 +44,12 @@ class CustomSchemaGeneratorHooks(override val wiringFactory: KotlinDirectiveWiri
4344
*/
4445
override fun willGenerateGraphQLType(type: KType): GraphQLType? = when (type.classifier) {
4546
UUID::class -> graphqlUUIDType
47+
ClosedRange::class -> {
48+
when (type.arguments[0].type?.classifier as? KClass<*>) {
49+
LocalDate::class -> graphqlPeriodType
50+
else -> null
51+
}
52+
}
4653
else -> null
4754
}
4855

@@ -94,3 +101,36 @@ private object UUIDCoercing : Coercing<UUID, String> {
94101
throw CoercingSerializeException("Data fetcher result $dataFetcherResult cannot be serialized to a String")
95102
}
96103
}
104+
105+
internal val graphqlPeriodType: GraphQLScalarType = GraphQLScalarType.newScalar()
106+
.name("Period")
107+
.description("""A period of local date to local date, inclusive on both ends i.e. a closed range.""")
108+
.coercing(PeriodCoercing)
109+
.build()
110+
111+
typealias Period = ClosedRange<LocalDate>
112+
113+
private object PeriodCoercing : Coercing<Period, String> {
114+
override fun parseValue(input: Any): Period = runCatching {
115+
input.toString().parseAsPeriod()
116+
}.getOrElse {
117+
throw CoercingParseValueException("Expected valid Period but was $input")
118+
}
119+
120+
override fun parseLiteral(input: Any): Period = runCatching {
121+
(input as? StringValue)?.value?.parseAsPeriod() ?: throw CoercingParseLiteralException("Expected valid Period literal but was $input")
122+
}.getOrElse {
123+
throw CoercingParseLiteralException("Expected valid Period literal but was $input")
124+
}
125+
126+
override fun serialize(dataFetcherResult: Any): String = kotlin.runCatching {
127+
toString()
128+
}.getOrElse {
129+
throw CoercingSerializeException("Data fetcher result $dataFetcherResult cannot be serialized to a String")
130+
}
131+
132+
private fun String.parseAsPeriod(): Period = split("..").let {
133+
if (it.size != 2) error("Cannot parse input $this as Period")
134+
LocalDate.parse(it[0])..LocalDate.parse(it[1])
135+
}
136+
}

examples/server/spring-server/src/main/kotlin/com/expediagroup/graphql/examples/server/spring/model/Widget.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2021 Expedia, Inc
2+
* Copyright 2022 Expedia, Inc
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,6 +29,8 @@ data class Widget(
2929
@GraphQLDescription("The widget's deprecated value that shouldn't be used")
3030
val deprecatedValue: Int? = value,
3131

32+
val listOfValues: List<Int>? = null,
33+
3234
@GraphQLIgnore
3335
val ignoredField: String? = "ignored",
3436

examples/server/spring-server/src/main/kotlin/com/expediagroup/graphql/examples/server/spring/mutation/WidgetMutation.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2021 Expedia, Inc
2+
* Copyright 2022 Expedia, Inc
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -34,4 +34,14 @@ class WidgetMutation : Mutation {
3434
}
3535
return widget
3636
}
37+
38+
fun processWidgetList(widgets: List<Widget>): List<Widget> {
39+
widgets.forEach {
40+
if (null == it.value) {
41+
it.value = 42
42+
}
43+
}
44+
45+
return widgets
46+
}
3747
}

examples/server/spring-server/src/main/kotlin/com/expediagroup/graphql/examples/server/spring/query/ScalarQuery.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2021 Expedia, Inc
2+
* Copyright 2022 Expedia, Inc
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
1616

1717
package com.expediagroup.graphql.examples.server.spring.query
1818

19+
import com.expediagroup.graphql.examples.server.spring.hooks.Period
1920
import com.expediagroup.graphql.examples.server.spring.model.Person
2021
import com.expediagroup.graphql.generator.annotations.GraphQLDescription
2122
import com.expediagroup.graphql.generator.scalars.ID
@@ -32,11 +33,21 @@ class ScalarQuery : Query {
3233
@GraphQLDescription("generates random UUID")
3334
fun generateRandomUUID() = UUID.randomUUID()
3435

36+
@GraphQLDescription("Prints a string with a custom scalar as input")
37+
fun printUuid(uuid: UUID) = "You sent $uuid"
38+
3539
@GraphQLDescription("Prints a string with a custom scalar as input")
3640
fun printUuids(uuids: List<UUID>) = "You sent $uuids"
3741

3842
fun findPersonById(id: ID) = Person(id, "Nelson")
3943

4044
@GraphQLDescription("generates random GraphQL ID")
4145
fun generateRandomId() = ID(UUID.randomUUID().toString())
46+
47+
fun customScalarInput(input: CustomScalarInput): String = "foo is ${input.foo} and range is ${input.range}"
48+
49+
data class CustomScalarInput(
50+
val foo: String,
51+
val range: Period,
52+
)
4253
}

examples/server/spring-server/src/main/kotlin/com/expediagroup/graphql/examples/server/spring/query/SimpleQuery.kt

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2021 Expedia, Inc
2+
* Copyright 2022 Expedia, Inc
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -62,15 +62,6 @@ class SimpleQuery : Query {
6262
return (1..10).map { random.nextInt(100) }.toList()
6363
}
6464

65-
@GraphQLDescription("generates pseudo random array of ints")
66-
fun generatePrimitiveArray(): IntArray {
67-
val random = Random()
68-
return (1..10).map { random.nextInt(100) }.toIntArray()
69-
}
70-
71-
@GraphQLDescription("query with array input")
72-
fun doSomethingWithIntArray(ints: IntArray) = "received ints=[${ints.joinToString()}]"
73-
7465
@GraphQLDescription("query with optional input")
7566
fun doSomethingWithOptionalInput(
7667
@GraphQLDescription("this field is required") requiredValue: Int,

examples/server/spring-server/src/test/kotlin/com/expediagroup/graphql/examples/server/spring/mutation/ScalarMutationIT.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2021 Expedia, Inc
2+
* Copyright 2022 Expedia, Inc
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -42,7 +42,7 @@ class ScalarMutationIT(@Autowired private val testClient: WebTestClient) {
4242
.uri(GRAPHQL_ENDPOINT)
4343
.accept(APPLICATION_JSON)
4444
.contentType(GRAPHQL_MEDIA_TYPE)
45-
.bodyValue("mutation { $query(person: {id: 1, name: \"Alice\"}) { id, name } }")
45+
.bodyValue("mutation { $query(person: {id: \"1\", name: \"Alice\"}) { id, name } }")
4646
.exchange()
4747
.verifyOnlyDataExists(query)
4848
.jsonPath("$DATA_JSON_PATH.$query.id").isEqualTo(1)

examples/server/spring-server/src/test/kotlin/com/expediagroup/graphql/examples/server/spring/query/SimpleQueryIT.kt

Lines changed: 1 addition & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2021 Expedia, Inc
2+
* Copyright 2022 Expedia, Inc
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -162,36 +162,6 @@ class SimpleQueryIT(@Autowired private val testClient: WebTestClient) {
162162
.jsonPath("$DATA_JSON_PATH.$query").value(hasSize<Int>(10))
163163
}
164164

165-
@Test
166-
fun `verify generatePrimitiveArray query`() {
167-
val query = "generatePrimitiveArray"
168-
169-
testClient.post()
170-
.uri(GRAPHQL_ENDPOINT)
171-
.accept(APPLICATION_JSON)
172-
.contentType(GRAPHQL_MEDIA_TYPE)
173-
.bodyValue("query { $query }")
174-
.exchange()
175-
.expectStatus().isOk
176-
.verifyOnlyDataExists(query)
177-
.jsonPath("$DATA_JSON_PATH.$query").isArray
178-
.jsonPath("$DATA_JSON_PATH.$query").value(hasSize<Int>(10))
179-
}
180-
181-
@Test
182-
fun `verify doSomethingWithIntArray query`() {
183-
val query = "doSomethingWithIntArray"
184-
val expectedData = "received ints=[1, 2, 3, 4, 5]"
185-
186-
testClient.post()
187-
.uri(GRAPHQL_ENDPOINT)
188-
.accept(APPLICATION_JSON)
189-
.contentType(GRAPHQL_MEDIA_TYPE)
190-
.bodyValue("query { $query(ints: [1, 2, 3, 4, 5]) }")
191-
.exchange()
192-
.verifyData(query, expectedData)
193-
}
194-
195165
@Test
196166
fun `verify doSomethingWithOptionalInput query`() {
197167
val query = "doSomethingWithOptionalInput"

generator/graphql-kotlin-schema-generator/build.gradle.kts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ description = "Code-only GraphQL schema generation for Kotlin"
22

33
val classGraphVersion: String by project
44
val graphQLJavaVersion: String by project
5-
val jacksonVersion: String by project
65
val kotlinCoroutinesVersion: String by project
76
val rxjavaVersion: String by project
87
val junitVersion: String by project
@@ -11,7 +10,6 @@ val slf4jVersion: String by project
1110
dependencies {
1211
api("com.graphql-java:graphql-java:$graphQLJavaVersion")
1312
api("org.jetbrains.kotlinx:kotlinx-coroutines-reactive:$kotlinCoroutinesVersion")
14-
api("com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion")
1513
implementation("io.github.classgraph:classgraph:$classGraphVersion")
1614
implementation("org.slf4j:slf4j-api:$slf4jVersion")
1715
testImplementation("io.reactivex.rxjava3:rxjava:$rxjavaVersion")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright 2022 Expedia, Inc
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.expediagroup.graphql.generator.exceptions
18+
19+
import kotlin.reflect.KClass
20+
21+
/**
22+
* Thrown when unable to locate the public primary constructor of an input class.
23+
*/
24+
class PrimaryConstructorNotFound(klazz: KClass<*>) : GraphQLKotlinException("Invalid input object ${klazz.simpleName} - missing public primary constructor")

0 commit comments

Comments
 (0)