Skip to content

Commit b22abfa

Browse files
smyrickShane Myrickdariuszkuc
authored
[generator] Support subscriptions of Publisher<DataFetcherResult<T>> (#786)
* Support subscriptions of Publisher<DataFetcherResult<T>> * Update javadocs and example * Update graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/utils/functionReturnTypes.kt Co-authored-by: Dariusz Kuc <[email protected]> Co-authored-by: Shane Myrick <[email protected]> Co-authored-by: Dariusz Kuc <[email protected]>
1 parent b4ea8eb commit b22abfa

File tree

3 files changed

+47
-21
lines changed

3 files changed

+47
-21
lines changed

examples/spring/src/main/kotlin/com/expediagroup/graphql/examples/subscriptions/SimpleSubscription.kt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ package com.expediagroup.graphql.examples.subscriptions
1818

1919
import com.expediagroup.graphql.annotations.GraphQLDescription
2020
import com.expediagroup.graphql.examples.context.MyGraphQLContext
21+
import com.expediagroup.graphql.spring.exception.SimpleKotlinGraphQLError
2122
import com.expediagroup.graphql.spring.operations.Subscription
23+
import graphql.execution.DataFetcherResult
2224
import kotlinx.coroutines.flow.flowOf
2325
import kotlinx.coroutines.reactive.asPublisher
2426
import org.reactivestreams.Publisher
@@ -57,9 +59,19 @@ class SimpleSubscription : Subscription {
5759
fun singleValueThenError(): Flux<Int> = Flux.just(1, 2)
5860
.map { if (it == 2) throw Exception("Second value") else it }
5961

60-
@GraphQLDescription("Returns list of values")
62+
@GraphQLDescription("Returns stream of values")
6163
fun flow(): Publisher<Int> = flowOf(1, 2, 4).asPublisher()
6264

65+
@GraphQLDescription("Returns stream of errors")
66+
fun flowOfErrors(): Publisher<DataFetcherResult<String?>> {
67+
val dfr: DataFetcherResult<String?> = DataFetcherResult.newResult<String?>()
68+
.data(null)
69+
.error(SimpleKotlinGraphQLError(Exception("error thrown")))
70+
.build()
71+
72+
return flowOf(dfr, dfr).asPublisher()
73+
}
74+
6375
@GraphQLDescription("Returns a value from the subscription context")
6476
fun subscriptionContext(myGraphQLContext: MyGraphQLContext): Publisher<String> =
6577
flowOf(myGraphQLContext.subscriptionValue ?: "", "value 2", "value3").asPublisher()

graphql-kotlin-schema-generator/src/main/kotlin/com/expediagroup/graphql/generator/types/utils/functionReturnTypes.kt

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,24 +33,35 @@ import kotlin.reflect.KType
3333
*
3434
* We can return the following combination of types:
3535
* Valid type T
36-
* Publisher<T>
3736
* DataFetcherResult<T>
37+
* Publisher<T>
38+
* Publisher<DataFetcherResult<T>>
3839
* CompletableFuture<T>
3940
* CompletableFuture<DataFetcherResult<T>>
4041
*/
4142
internal fun getWrappedReturnType(returnType: KType): KType {
4243
return when {
43-
returnType.isSubclassOf(Publisher::class) -> returnType.getTypeOfFirstArgument()
4444
returnType.isSubclassOf(DataFetcherResult::class) -> returnType.getTypeOfFirstArgument()
45-
returnType.isSubclassOf(CompletableFuture::class) -> {
46-
val wrappedType = returnType.getTypeOfFirstArgument()
45+
returnType.isSubclassOf(Publisher::class) -> { checkTypeForDataFetcherResult(returnType) }
46+
returnType.isSubclassOf(CompletableFuture::class) -> { checkTypeForDataFetcherResult(returnType) }
47+
else -> returnType
48+
}
49+
}
4750

48-
if (wrappedType.isSubclassOf(DataFetcherResult::class)) {
49-
return wrappedType.getTypeOfFirstArgument()
50-
}
51+
/**
52+
* Both Publisher and CompletableFuture can be optional wrapped internally with DataFetcherResult.
53+
*
54+
* Publisher<T>
55+
* Published<DataFetcherResult<T>>
56+
* CompletableFuture<T>
57+
* CompletableFuture<DataFetcherResult<T>>
58+
*/
59+
private fun checkTypeForDataFetcherResult(returnType: KType): KType {
60+
val wrappedType = returnType.getTypeOfFirstArgument()
5161

52-
wrappedType
53-
}
54-
else -> returnType
62+
return if (wrappedType.isSubclassOf(DataFetcherResult::class)) {
63+
wrappedType.getTypeOfFirstArgument()
64+
} else {
65+
wrappedType
5566
}
5667
}

graphql-kotlin-schema-generator/src/test/kotlin/com/expediagroup/graphql/generator/types/utils/FunctionReturnTypesKtTest.kt

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,38 +23,41 @@ import org.reactivestreams.Publisher
2323
import java.util.concurrent.CompletableFuture
2424
import kotlin.test.assertEquals
2525

26-
internal class FunctionReturnTypesKtTest {
26+
class FunctionReturnTypesKtTest {
2727

28-
internal class MyClass {
28+
class MyClass {
29+
// Valid Types
2930
val string = "my string"
30-
31+
val dataFetcherResult: DataFetcherResult<String> = DataFetcherResult.newResult<String>().data(string).build()
3132
val publisher: Publisher<String> = Flowable.just(string)
3233
val flowable: Flowable<String> = Flowable.just(string)
33-
val dataFetcherResult: DataFetcherResult<String> = DataFetcherResult.newResult<String>().data(string).build()
34+
val publisherDataFetcherResult: Publisher<DataFetcherResult<String>> = Flowable.just(dataFetcherResult)
3435
val completableFuture: CompletableFuture<String> = CompletableFuture.completedFuture(string)
36+
val completableFutureDataFetcher: CompletableFuture<DataFetcherResult<String>> = CompletableFuture.completedFuture(dataFetcherResult)
3537

36-
val invalidPublisher: Publisher<DataFetcherResult<String>> = Flowable.just(dataFetcherResult)
37-
val invalidDataFetcherResult: DataFetcherResult<CompletableFuture<String>> = DataFetcherResult.newResult<CompletableFuture<String>>().data(completableFuture).build()
38-
val validCompletableFutureDataFetcher: CompletableFuture<DataFetcherResult<String>> = CompletableFuture.completedFuture(dataFetcherResult)
38+
// Invalid types
39+
val invalidDataFetcherResultCompletableFuture: DataFetcherResult<CompletableFuture<String>> = DataFetcherResult.newResult<CompletableFuture<String>>().data(completableFuture).build()
40+
val invalidDataFetcherResultPublisher: DataFetcherResult<Publisher<String>> = DataFetcherResult.newResult<Publisher<String>>().data(publisher).build()
3941
val invalidCompletableFuture: CompletableFuture<Publisher<String>> = CompletableFuture.completedFuture(publisher)
4042
}
4143

4244
@Test
4345
fun `getWrappedReturnType of Publisher`() {
4446
assertEquals(MyClass::string.returnType, actual = getWrappedReturnType(MyClass::publisher.returnType))
45-
assertEquals(MyClass::dataFetcherResult.returnType, actual = getWrappedReturnType(MyClass::invalidPublisher.returnType))
47+
assertEquals(MyClass::string.returnType, actual = getWrappedReturnType(MyClass::publisherDataFetcherResult.returnType))
4648
}
4749

4850
@Test
4951
fun `getWrappedReturnType of DataFetcherResult`() {
5052
assertEquals(MyClass::string.returnType, actual = getWrappedReturnType(MyClass::dataFetcherResult.returnType))
51-
assertEquals(MyClass::completableFuture.returnType, actual = getWrappedReturnType(MyClass::invalidDataFetcherResult.returnType))
53+
assertEquals(MyClass::completableFuture.returnType, actual = getWrappedReturnType(MyClass::invalidDataFetcherResultCompletableFuture.returnType))
54+
assertEquals(MyClass::publisher.returnType, actual = getWrappedReturnType(MyClass::invalidDataFetcherResultPublisher.returnType))
5255
}
5356

5457
@Test
5558
fun `getWrappedReturnType of CompletableFuture`() {
5659
assertEquals(MyClass::string.returnType, actual = getWrappedReturnType(MyClass::completableFuture.returnType))
57-
assertEquals(MyClass::string.returnType, actual = getWrappedReturnType(MyClass::validCompletableFutureDataFetcher.returnType))
60+
assertEquals(MyClass::string.returnType, actual = getWrappedReturnType(MyClass::completableFutureDataFetcher.returnType))
5861
assertEquals(MyClass::publisher.returnType, actual = getWrappedReturnType(MyClass::invalidCompletableFuture.returnType))
5962
}
6063

0 commit comments

Comments
 (0)