@@ -93,6 +93,25 @@ class NestedGenericWrapperTest {
9393 @Test
9494 fun `unwraps wrappers in a list in an Optional` () = assertResolves(" optionalListOfWrapped" , ab)
9595
96+ @Test
97+ fun `resolves a DataFetcherResult in an Optional and keeps its errors` () {
98+ val result = execute(" optionalResult" )
99+
100+ assertEquals(result.errors.map { it.message }, listOf (" partial" ))
101+ assertEquals(result.getData(), mapOf (" optionalResult" to mapOf (" data" to " a" )))
102+ }
103+
104+ @Test
105+ fun `resolves an empty Optional of a DataFetcherResult to null` () = assertResolves(" emptyOptionalResult" , null )
106+
107+ @Test
108+ fun `unwraps wrappers in a list in a DataFetcherResult in an Optional and keeps its errors` () {
109+ val result = execute(" optionalResultOfListOfWrapped" )
110+
111+ assertEquals(result.errors.map { it.message }, listOf (" partial" ))
112+ assertEquals(result.getData(), mapOf (" optionalResultOfListOfWrapped" to ab))
113+ }
114+
96115 @Test
97116 fun `unwraps wrappers in a list from a suspend function` () = assertResolves(" suspendListOfWrapped" , ab)
98117
@@ -178,6 +197,7 @@ class NestedGenericWrapperTest {
178197 assertSame(fetch(schema, " plainList" ), query.plainList)
179198 assertSame(fetch(schema, " futureOfList" ), query.futureOfList)
180199 assertSame(fetch(schema, " wrappedFuture" ), query.wrappedFuture.get())
200+ assertSame(fetch(schema, " optionalPayload" ), query.optionalPayload)
181201 }
182202
183203 @Test
@@ -202,18 +222,22 @@ class NestedGenericWrapperTest {
202222 val shapes = (1 .. 4 ).fold(listOf (listOf<Class <* >>())) { shapes, _ -> shapes + shapes.filter { it.size == shapes.last().size }.flatMap { shape -> containers.map { shape + it } } }
203223
204224 val accepted = graphQLTypes.flatMap { graphQLType ->
205- shapes.flatMap { shape -> listOf (false , true ).map { shape to javaType(shape, leaf(graphQLType).javaClass, wildcardElements = it) } }.mapNotNull { (shape, javaType) ->
225+ val shapesOfTheSameListDepth = shapes.filter { shape -> shape.count { it == List ::class .java || it == Page ::class .java } == graphQLType.listDepth() }
226+ shapesOfTheSameListDepth.flatMap { shape -> listOf (false , true ).map { shape to javaType(shape, leaf(graphQLType).javaClass, wildcardElements = it) } }.mapNotNull { (shape, javaType) ->
206227 val potentialMatch = TypeClassMatcher .PotentialMatch .returnValue(graphQLType, javaType, typeResolver, ScanDictionary .ReturnValueReference (method))
207- val match = try {
228+ try {
208229 matcher.match(potentialMatch)
230+ shape to potentialMatch
209231 } catch (e: SchemaClassScannerError ) {
210232 null
211233 }
212- (shape to potentialMatch).takeIf { match != null && potentialMatch.unwrapping.javaType == leaf(graphQLType).javaClass }
213234 }
214235 }
215236 val failures = accepted.mapNotNull { (shape, potentialMatch) ->
216237 val graphQLType = potentialMatch.graphQLType
238+ if (potentialMatch.unwrapping.javaType != leaf(graphQLType).javaClass) {
239+ return @mapNotNull " ${TypeUtils .toString(potentialMatch.javaType)} as ${AstPrinter .printAst(graphQLType)} : bound to ${TypeUtils .toString(potentialMatch.unwrapping.javaType)} "
240+ }
217241 val unwrapper = ReturnValueUnwrapper .create(
218242 method, potentialMatch.unwrapping, TypeUtils .getRawType(typeResolver.resolve(potentialMatch.javaType), null ), isSuspendFunction = false , isSubscription = false , options.genericWrappers
219243 )
@@ -232,6 +256,38 @@ class NestedGenericWrapperTest {
232256 assertEquals(failures, listOf<String >())
233257 }
234258
259+ @Test
260+ fun `rejects a DataFetcherResult or an Optional twice in one value` () {
261+ val typeResolver = GenericTypeResolver (Query ::class .java, SchemaParserOptions .newOptions().build())
262+ val matcher = TypeClassMatcher (mapOf (" Payload" to ObjectTypeDefinition (" Payload" )))
263+ val method = Query ::class .java.getMethod(" listOfWrapped" )
264+ val result = DataFetcherResult ::class .java
265+ val optional = Optional ::class .java
266+ val rejections = listOf (
267+ listOf (optional, result, result) to " ${result.name} cannot be nested within itself" ,
268+ listOf (result, optional, result) to " ${result.name} cannot be nested within itself" ,
269+ listOf (optional, result, optional) to " ${optional.name} cannot be nested within itself" ,
270+ )
271+
272+ val messages = rejections.map { (shape, _) ->
273+ val potentialMatch = TypeClassMatcher .PotentialMatch .returnValue(TypeName (" Payload" ), javaType(shape, Payload ::class .java, wildcardElements = false ), typeResolver, ScanDictionary .ReturnValueReference (method))
274+ try {
275+ matcher.match(potentialMatch)
276+ " accepted"
277+ } catch (e: SchemaClassScannerError ) {
278+ e.message.orEmpty().substringAfterLast(" ): " )
279+ }
280+ }
281+
282+ assertEquals(messages, rejections.map { it.second })
283+ }
284+
285+ private fun Type <* >.listDepth (): Int = when (this ) {
286+ is ListType -> 1 + type.listDepth()
287+ is NonNullType -> type.listDepth()
288+ else -> 0
289+ }
290+
235291 private fun leaf (graphQLType : Type <* >): Any = if (graphQLType.unwrap().name == " Leaf" ) FutureLeaf (" a" ) else Payload (" a" )
236292
237293 private fun javaType (shape : List <Class <* >>, leafClass : Class <* >, wildcardElements : Boolean ): java.lang.reflect.Type =
@@ -284,6 +340,10 @@ class NestedGenericWrapperTest {
284340 pageOfWrapped: [Payload!]!
285341 resultOfListOfWrapped: [Payload!]!
286342 listOfResultsOfWrapped: [Payload!]!
343+ optionalResult: Payload
344+ emptyOptionalResult: Payload
345+ optionalResultOfListOfWrapped: [Payload!]
346+ optionalPayload: Payload
287347 optionalListOfWrapped: [Payload!]
288348 suspendListOfWrapped: [Payload!]!
289349 suspendListOfFutures: [Payload!]!
@@ -387,6 +447,7 @@ class NestedGenericWrapperTest {
387447 val plainList = listOf (Payload (" a" ))
388448 val futureOfList: CompletableFuture <List <Payload >> = completedFuture(plainList)
389449 val wrappedFuture: Try <CompletableFuture <Payload >> = Try .success(completedFuture(Payload (" a" )))
450+ val optionalPayload: Optional <Payload > = Optional .of(Payload (" a" ))
390451
391452 private fun <T : Any > result (data : T , vararg errors : GraphQLError ): DataFetcherResult <T > = DataFetcherResult .newResult<T >().data(data).errors(errors.toList()).build()
392453
@@ -406,6 +467,9 @@ class NestedGenericWrapperTest {
406467 fun resultOfListOfWrapped (): DataFetcherResult <List <Try <Payload >>> = result(listOfWrapped(), GraphQLError .newError().message(" partial" ).build())
407468 fun listOfResultsOfWrapped (): List <DataFetcherResult <Try <Payload >>> = listOfWrapped().map { result(it) }
408469 fun optionalListOfWrapped (): Optional <List <Try <Payload >>> = Optional .of(listOfWrapped())
470+ fun optionalResult (): Optional <DataFetcherResult <Payload >> = Optional .of(result(Payload (" a" ), GraphQLError .newError().message(" partial" ).build()))
471+ fun emptyOptionalResult (): Optional <DataFetcherResult <Payload >> = Optional .empty()
472+ fun optionalResultOfListOfWrapped (): Optional <DataFetcherResult <List <Try <Payload >>>> = Optional .of(resultOfListOfWrapped())
409473 suspend fun suspendListOfWrapped (): List <Try <Payload >> = listOfWrapped()
410474 suspend fun suspendListOfFutures (): List <CompletableFuture <Payload >> = listOfFutures()
411475 suspend fun suspendNullableListOfFutures (): List <CompletableFuture <Payload >> = listOfFutures()
0 commit comments