Skip to content

Commit 9f427ef

Browse files
smyrickdariuszkuc
authored andcommitted
feat: move the default handling of CompletableFuture to code (#166)
This will have no impact on existing clients. I just moved the handling of CompletableFutures into the code so that it will always happen. Right now, clients need to remember to still handle CompletableFutures if they override the hook. We now resolve the type from the hook first, which defaults to the same type. Then we check after if it is on of the default supported wrapped types which right now only include CompletableFuture, but will extend to Publisher when we support subscriptions
1 parent ff2778d commit 9f427ef

File tree

4 files changed

+30
-27
lines changed

4 files changed

+30
-27
lines changed

src/main/kotlin/com/expedia/graphql/generator/types/FunctionTypeBuilder.kt

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@ import com.expedia.graphql.generator.TypeBuilder
66
import com.expedia.graphql.generator.extensions.getDeprecationReason
77
import com.expedia.graphql.generator.extensions.getGraphQLDescription
88
import com.expedia.graphql.generator.extensions.getName
9+
import com.expedia.graphql.generator.extensions.getTypeOfFirstArgument
910
import com.expedia.graphql.generator.extensions.isGraphQLContext
1011
import com.expedia.graphql.generator.extensions.isInterface
1112
import graphql.schema.GraphQLArgument
1213
import graphql.schema.GraphQLFieldDefinition
1314
import graphql.schema.GraphQLInputType
1415
import graphql.schema.GraphQLOutputType
16+
import java.util.concurrent.CompletableFuture
1517
import kotlin.reflect.KFunction
1618
import kotlin.reflect.KParameter
19+
import kotlin.reflect.KType
1720
import kotlin.reflect.full.valueParameters
1821

1922
@Suppress("Detekt.UnsafeCast")
@@ -43,12 +46,20 @@ internal class FunctionTypeBuilder(generator: SchemaGenerator) : TypeBuilder(gen
4346
builder.dataFetcherFactory(config.dataFetcherFactoryProvider.functionDataFetcherFactory(target = target, kFunction = fn))
4447
}
4548

46-
val monadType = config.hooks.willResolveMonad(fn.returnType)
47-
builder.type(graphQLTypeOf(monadType) as GraphQLOutputType)
49+
val typeFromHooks = config.hooks.willResolveMonad(fn.returnType)
50+
val returnType = getWrappedReturnType(typeFromHooks)
51+
builder.type(graphQLTypeOf(returnType) as GraphQLOutputType)
4852
val graphQLType = builder.build()
49-
return config.hooks.onRewireGraphQLType(monadType, graphQLType) as GraphQLFieldDefinition
53+
54+
return config.hooks.onRewireGraphQLType(returnType, graphQLType) as GraphQLFieldDefinition
5055
}
5156

57+
private fun getWrappedReturnType(returnType: KType): KType =
58+
when (returnType.classifier) {
59+
CompletableFuture::class -> returnType.getTypeOfFirstArgument()
60+
else -> returnType
61+
}
62+
5263
@Throws(InvalidInputFieldTypeException::class)
5364
private fun argument(parameter: KParameter): GraphQLArgument {
5465

src/main/kotlin/com/expedia/graphql/hooks/SchemaGeneratorHooks.kt

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
package com.expedia.graphql.hooks
22

33
import com.expedia.graphql.execution.DataFetcherExecutionPredicate
4-
import com.expedia.graphql.generator.extensions.getTypeOfFirstArgument
54
import graphql.schema.GraphQLFieldDefinition
65
import graphql.schema.GraphQLSchema
76
import graphql.schema.GraphQLType
8-
import java.util.concurrent.CompletableFuture
97
import kotlin.reflect.KClass
108
import kotlin.reflect.KFunction
119
import kotlin.reflect.KProperty
@@ -31,15 +29,10 @@ interface SchemaGeneratorHooks {
3129
fun willGenerateGraphQLType(type: KType): GraphQLType? = null
3230

3331
/**
34-
* Called before resolving a Monad or Future type to its wrapped KType.
35-
* This allows for a custom resolver on how to extract the wrapped value.
32+
* Called before resolving a KType to the GraphQL type.
33+
* This allows for a custom resolver on how to extract wrapped values, like in a CompletableFuture.
3634
*/
37-
fun willResolveMonad(type: KType): KType =
38-
if (type.classifier == CompletableFuture::class) {
39-
type.getTypeOfFirstArgument()
40-
} else {
41-
type
42-
}
35+
fun willResolveMonad(type: KType): KType = type
4336

4437
/**
4538
* Called when looking at the KClass superclasses to determine if it valid for adding to the generated schema.

src/test/kotlin/com/expedia/graphql/generator/types/FunctionTypeBuilderTest.kt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
package com.expedia.graphql.generator.types
22

3-
import com.expedia.graphql.execution.FunctionDataFetcher
43
import com.expedia.graphql.annotations.GraphQLContext
54
import com.expedia.graphql.annotations.GraphQLDescription
65
import com.expedia.graphql.annotations.GraphQLDirective
6+
import com.expedia.graphql.execution.FunctionDataFetcher
77
import graphql.Scalars
88
import graphql.introspection.Introspection
99
import graphql.schema.GraphQLNonNull
1010
import org.junit.jupiter.api.Test
1111
import java.util.UUID
12+
import java.util.concurrent.CompletableFuture
1213
import kotlin.test.assertEquals
1314
import kotlin.test.assertFalse
1415
import kotlin.test.assertTrue
@@ -49,6 +50,8 @@ internal class FunctionTypeBuilderTest : TypeTestHelper() {
4950
fun saw(tree: String) = tree
5051

5152
fun context(@GraphQLContext context: String, string: String) = "$context and $string"
53+
54+
fun completableFuture(num: Int): CompletableFuture<Int> = CompletableFuture.completedFuture(num)
5255
}
5356

5457
@Test
@@ -148,4 +151,13 @@ internal class FunctionTypeBuilderTest : TypeTestHelper() {
148151
assertEquals(expected = 1, actual = result.arguments.size)
149152
assertFalse(result.dataFetcher is FunctionDataFetcher)
150153
}
154+
155+
@Test
156+
fun `completable future return type is valid`() {
157+
val kFunction = Happy::completableFuture
158+
val result = builder.function(fn = kFunction)
159+
160+
assertEquals(expected = 1, actual = result.arguments.size)
161+
assertEquals("Int", (result.type as? GraphQLNonNull)?.wrappedType?.name)
162+
}
151163
}

src/test/kotlin/com/expedia/graphql/hooks/SchemaGeneratorHooksTest.kt

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import graphql.schema.GraphQLFieldDefinition
99
import graphql.schema.GraphQLObjectType
1010
import graphql.schema.GraphQLSchema
1111
import graphql.schema.GraphQLType
12-
import java.util.concurrent.CompletableFuture
1312
import kotlin.reflect.KFunction
1413
import kotlin.reflect.KProperty
1514
import kotlin.reflect.KType
@@ -177,14 +176,6 @@ class SchemaGeneratorHooksTest {
177176
assertEquals("Hijacked Description", query.description)
178177
}
179178

180-
@Test
181-
fun `willResolveMonad returns CompletableFuture wrapped type`() {
182-
val hooks = NoopSchemaGeneratorHooks()
183-
val type = TestQueryFuture::comepletableFutre.returnType
184-
185-
assertEquals(expected = "SomeData", actual = hooks.willResolveMonad(type).getSimpleName())
186-
}
187-
188179
@Test
189180
fun `willResolveMonad returns basic type`() {
190181
val hooks = NoopSchemaGeneratorHooks()
@@ -197,9 +188,5 @@ class SchemaGeneratorHooksTest {
197188
fun query(): SomeData = SomeData(0)
198189
}
199190

200-
class TestQueryFuture {
201-
fun comepletableFutre(): CompletableFuture<SomeData> = CompletableFuture.completedFuture(SomeData(1))
202-
}
203-
204191
data class SomeData(val someNumber: Int)
205192
}

0 commit comments

Comments
 (0)