diff --git a/kgraphql/src/main/kotlin/com/apurebase/kgraphql/schema/execution/ParallelRequestExecutor.kt b/kgraphql/src/main/kotlin/com/apurebase/kgraphql/schema/execution/ParallelRequestExecutor.kt index 5c11a48a..3694c5fa 100644 --- a/kgraphql/src/main/kotlin/com/apurebase/kgraphql/schema/execution/ParallelRequestExecutor.kt +++ b/kgraphql/src/main/kotlin/com/apurebase/kgraphql/schema/execution/ParallelRequestExecutor.kt @@ -182,7 +182,7 @@ class ParallelRequestExecutor(val schema: DefaultSchema) : RequestExecutor { when (child) { is Execution.Fragment -> objectNode.setAll(handleFragment(ctx, value, child)) else -> { - val (key, jsonNode) = handleProperty(ctx, value, child, type, node.children.size) + val (key, jsonNode) = handleProperty(ctx, value, child, type, node.children.size) ?: continue objectNode.merge(key, jsonNode) } } @@ -190,7 +190,7 @@ class ParallelRequestExecutor(val schema: DefaultSchema) : RequestExecutor { return objectNode } - private suspend fun handleProperty(ctx: ExecutionContext, value: T, child: Execution, type: Type, childrenSize: Int): Pair { + private suspend fun handleProperty(ctx: ExecutionContext, value: T, child: Execution, type: Type, childrenSize: Int): Pair? { when (child) { //Union is subclass of Node so check it first is Execution.Union -> { @@ -205,7 +205,7 @@ class ParallelRequestExecutor(val schema: DefaultSchema) : RequestExecutor { is Execution.Node -> { val field = type.unwrapped()[child.key] ?: throw IllegalStateException("Execution unit ${child.key} is not contained by operation return type") - return child.aliasOrKey to createPropertyNode(ctx, value, child, field, childrenSize) + return child.aliasOrKey to (createPropertyNode(ctx, value, child, field, childrenSize) ?: return null) } else -> { throw UnsupportedOperationException("Handling containers is not implemented yet") @@ -224,7 +224,7 @@ class ParallelRequestExecutor(val schema: DefaultSchema) : RequestExecutor { when (child) { is Execution.Fragment -> handleFragment(ctx, value, child).toList() // TODO: Should not be 1 - else -> listOf(handleProperty(ctx, value, child, expectedType, 1)) + else -> listOfNotNull(handleProperty(ctx, value, child, expectedType, 1)) } }.fold(mutableMapOf()) { map, entry -> map.merge(entry.first, entry.second) } } diff --git a/kgraphql/src/test/kotlin/com/apurebase/kgraphql/DataLoaderTest.kt b/kgraphql/src/test/kotlin/com/apurebase/kgraphql/DataLoaderTest.kt index 12bf63c1..95e710c9 100644 --- a/kgraphql/src/test/kotlin/com/apurebase/kgraphql/DataLoaderTest.kt +++ b/kgraphql/src/test/kotlin/com/apurebase/kgraphql/DataLoaderTest.kt @@ -382,7 +382,7 @@ class DataLoaderTest { val result = schema.executeBlocking(query).also(::println).deserialize() result.extract("data/abc[0]/person/fullName") shouldBeEqualTo "${jogvan.firstName} ${jogvan.lastName}" - extractOrNull(result, "data/abc[1]/person") shouldBeEqualTo null + result.extract("data/abc[1]/person") shouldBeEqualTo null result.extract("data/abc[2]/person/fullName") shouldBeEqualTo "${juul.firstName} ${juul.lastName}" } } diff --git a/kgraphql/src/test/kotlin/com/apurebase/kgraphql/TestUtils.kt b/kgraphql/src/test/kotlin/com/apurebase/kgraphql/TestUtils.kt index b5185b49..07b641ac 100644 --- a/kgraphql/src/test/kotlin/com/apurebase/kgraphql/TestUtils.kt +++ b/kgraphql/src/test/kotlin/com/apurebase/kgraphql/TestUtils.kt @@ -5,7 +5,6 @@ import com.apurebase.kgraphql.schema.Schema import com.apurebase.kgraphql.schema.dsl.SchemaBuilder import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper import org.amshove.kluent.shouldBeEqualTo -import org.amshove.kluent.shouldEqual import org.hamcrest.CoreMatchers import org.hamcrest.FeatureMatcher import org.hamcrest.MatcherAssert.assertThat @@ -31,23 +30,17 @@ fun Map<*, *>.extract(path: String) : T { try { return tokens.fold(this as Any?) { workingMap, token -> if(token.contains('[')){ - val list = (workingMap as Map<*,*>)[token.substringBefore('[')] + if (!(workingMap as Map<*, *>).containsKey(token.substringBefore('['))) throw IllegalArgumentException() + val list = workingMap[token.substringBefore('[')] val index = token.substring(token.indexOf('[')+1, token.length -1).toInt() (list as List<*>)[index] } else { - (workingMap as Map<*,*>)[token] + if (!(workingMap as Map<*, *>).containsKey(token)) throw IllegalArgumentException() + workingMap[token] } } as T } catch (e : Exception){ - throw IllegalArgumentException("Path: $path does not exist in map: ${this}", e) - } -} - -fun extractOrNull(map: Map<*,*>, path : String) : T? { - try { - return map.extract(path) - } catch (e: IllegalArgumentException){ - return null + throw IllegalArgumentException("Path: $path does not exist in map: $this", e) } } diff --git a/kgraphql/src/test/kotlin/com/apurebase/kgraphql/integration/BaseSchemaTest.kt b/kgraphql/src/test/kotlin/com/apurebase/kgraphql/integration/BaseSchemaTest.kt index bf0fc705..66d66df3 100644 --- a/kgraphql/src/test/kotlin/com/apurebase/kgraphql/integration/BaseSchemaTest.kt +++ b/kgraphql/src/test/kotlin/com/apurebase/kgraphql/integration/BaseSchemaTest.kt @@ -1,8 +1,8 @@ package com.apurebase.kgraphql.integration import com.apurebase.kgraphql.* +import com.apurebase.kgraphql.schema.execution.ExecutionOptions import org.junit.jupiter.api.AfterEach -import java.io.ByteArrayInputStream abstract class BaseSchemaTest { @@ -317,8 +317,14 @@ abstract class BaseSchemaTest { @AfterEach fun cleanup() = createdActors.clear() - fun execute(query: String, variables : String? = null) = testedSchema - .executeBlocking(query, variables) + fun execute( + query: String, + variables: String? = null, + context: Context = Context(emptyMap()), + options: ExecutionOptions = ExecutionOptions(), + operationName: String? = null, + ) = testedSchema + .executeBlocking(query, variables, context, options, operationName) .also(::println) .deserialize() diff --git a/kgraphql/src/test/kotlin/com/apurebase/kgraphql/specification/language/FragmentsSpecificationTest.kt b/kgraphql/src/test/kotlin/com/apurebase/kgraphql/specification/language/FragmentsSpecificationTest.kt index b752d4d7..d9c164fe 100644 --- a/kgraphql/src/test/kotlin/com/apurebase/kgraphql/specification/language/FragmentsSpecificationTest.kt +++ b/kgraphql/src/test/kotlin/com/apurebase/kgraphql/specification/language/FragmentsSpecificationTest.kt @@ -6,6 +6,7 @@ import com.apurebase.kgraphql.integration.BaseSchemaTest import com.apurebase.kgraphql.integration.BaseSchemaTest.Companion.INTROSPECTION_QUERY import com.apurebase.kgraphql.GraphQLError import org.amshove.kluent.invoking +import org.amshove.kluent.shouldBeEqualTo import org.amshove.kluent.shouldThrow import org.amshove.kluent.withMessage import org.hamcrest.CoreMatchers @@ -13,6 +14,7 @@ import org.hamcrest.CoreMatchers.* import org.hamcrest.MatcherAssert import org.hamcrest.MatcherAssert.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows @Specification("2.8 Fragments") class FragmentsSpecificationTest { @@ -70,8 +72,8 @@ class FragmentsSpecificationTest { "{\"expandedInfo\":false}" )) assertNoErrors(response) - assertThat(extractOrNull(response, "data/actor/actualActor/name"), equalTo("Boguś Linda")) - assertThat(extractOrNull(response, "data/actor/actualActor/age"), nullValue()) + assertThat(response.extract("data/actor/actualActor/name"), equalTo("Boguś Linda")) + assertThrows { response.extract("data/actor/actualActor/age") } } @Test @@ -83,11 +85,11 @@ class FragmentsSpecificationTest { when (name) { "David Fincher" /* director */ -> { MatcherAssert.assertThat(map.extract>("data/people[$i]/favActors"), CoreMatchers.notNullValue()) - MatcherAssert.assertThat(extractOrNull(map, "data/people[$i]/isOld"), CoreMatchers.nullValue()) + assertThrows { map.extract("data/people[$i]/isOld") } } "Brad Pitt" /* actor */ -> { MatcherAssert.assertThat(map.extract("data/people[$i]/isOld"), CoreMatchers.notNullValue()) - MatcherAssert.assertThat(extractOrNull>(map, "data/people[$i]/favActors"), CoreMatchers.nullValue()) + assertThrows { map.extract("data/people[$i]/favActors") } } } } @@ -102,11 +104,11 @@ class FragmentsSpecificationTest { when (name) { "David Fincher" /* director */ -> { MatcherAssert.assertThat(map.extract>("data/people[$i]/favActors"), CoreMatchers.notNullValue()) - MatcherAssert.assertThat(extractOrNull(map, "data/people[$i]/isOld"), CoreMatchers.nullValue()) + assertThrows { map.extract("data/people[$i]/isOld") } } "Brad Pitt" /* actor */ -> { MatcherAssert.assertThat(map.extract("data/people[$i]/isOld"), CoreMatchers.notNullValue()) - MatcherAssert.assertThat(extractOrNull>(map, "data/people[$i]/favActors"), CoreMatchers.nullValue()) + assertThrows { map.extract("data/people[$i]/favActors") } } } } diff --git a/kgraphql/src/test/kotlin/com/apurebase/kgraphql/specification/typesystem/DirectivesSpecificationTest.kt b/kgraphql/src/test/kotlin/com/apurebase/kgraphql/specification/typesystem/DirectivesSpecificationTest.kt index c152c447..55d9ae0d 100644 --- a/kgraphql/src/test/kotlin/com/apurebase/kgraphql/specification/typesystem/DirectivesSpecificationTest.kt +++ b/kgraphql/src/test/kotlin/com/apurebase/kgraphql/specification/typesystem/DirectivesSpecificationTest.kt @@ -2,12 +2,11 @@ package com.apurebase.kgraphql.specification.typesystem import com.apurebase.kgraphql.* import com.apurebase.kgraphql.integration.BaseSchemaTest +import com.apurebase.kgraphql.schema.execution.ExecutionOptions +import com.apurebase.kgraphql.schema.execution.Executor import org.amshove.kluent.shouldBeEqualTo -import org.amshove.kluent.shouldEqual -import org.hamcrest.CoreMatchers.notNullValue -import org.hamcrest.CoreMatchers.nullValue -import org.hamcrest.MatcherAssert.assertThat import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows @Specification("3.2 Directives") class DirectivesSpecificationTest : BaseSchemaTest() { @@ -15,43 +14,43 @@ class DirectivesSpecificationTest : BaseSchemaTest() { @Test fun `query with @include directive on field`(){ val map = execute("{film{title, year @include(if: false)}}") - assertThat(extractOrNull(map, "data/film/year"), nullValue()) + assertThrows { map.extract("data/film/year") } } @Test fun `query with @skip directive on field`(){ val map = execute("{film{title, year @skip(if: true)}}") - assertThat(extractOrNull(map, "data/film/year"), nullValue()) + assertThrows { map.extract("data/film/year") } } @Test fun `query with @include and @skip directive on field`(){ val mapBothSkip = execute("{film{title, year @include(if: false) @skip(if: true)}}") - assertThat(extractOrNull(mapBothSkip, "data/film/year"), nullValue()) + assertThrows { mapBothSkip.extract("data/film/year") } val mapOnlySkip = execute("{film{title, year @include(if: true) @skip(if: true)}}") - assertThat(extractOrNull(mapOnlySkip, "data/film/year"), nullValue()) + assertThrows { mapOnlySkip.extract("data/film/year") } val mapOnlyInclude = execute("{film{title, year @include(if: false) @skip(if: false)}}") - assertThat(extractOrNull(mapOnlyInclude, "data/film/year"), nullValue()) + assertThrows { mapOnlyInclude.extract("data/film/year") } val mapNeither = execute("{film{title, year @include(if: true) @skip(if: false)}}") - assertThat(extractOrNull(mapNeither, "data/film/year"), notNullValue()) + mapNeither.extract("data/film/year") shouldBeEqualTo 2006 } @Test fun `query with @include and @skip directive on field object`() { val mapWithSkip = execute("{ number(big: true), film @skip(if: true) { title } }") - mapWithSkip.extract("data/film") shouldBeEqualTo null + assertThrows { mapWithSkip.extract("data/film") } val mapWithoutSkip = execute("{ number(big: true), film @skip(if: false) { title } }") mapWithoutSkip.extract("data/film/title") shouldBeEqualTo "Prestige" val mapWithInclude = execute("{ number(big: true), film @include(if: true) { title } }") - mapWithInclude.extract("data/film/title") shouldBeEqualTo "Prestige" + mapWithInclude.extract("data/film/title") shouldBeEqualTo "Prestige" val mapWithoutInclude = execute("{ number(big: true), film @include(if: false) { title } }") - mapWithoutInclude.extract("data/film") shouldBeEqualTo null + assertThrows { mapWithoutInclude.extract("data/film") } } @Test @@ -60,6 +59,88 @@ class DirectivesSpecificationTest : BaseSchemaTest() { "query film (\$include: Boolean!) {film{title, year @include(if: \$include)}}", "{\"include\":\"false\"}" ) - assertThat(extractOrNull(map, "data/film/year"), nullValue()) + assertThrows { map.extract("data/film/year") } + } + + @Test + fun `query with @include directive on field (using DataLoaderPrepared executor)`() { + val map = execute( + "{film{title, year @include(if: false)}}", + options = ExecutionOptions(executor = Executor.DataLoaderPrepared) + ) + assertThrows { map.extract("data/film/year") } + } + + @Test + fun `query with @skip directive on field (using DataLoaderPrepared executor)`() { + val map = execute( + "{film{title, year @skip(if: true)}}", + options = ExecutionOptions(executor = Executor.DataLoaderPrepared) + ) + assertThrows { map.extract("data/film/year") } + } + + @Test + fun `query with @include and @skip directive on field (using DataLoaderPrepared executor)`() { + val mapBothSkip = execute( + "{film{title, year @include(if: false) @skip(if: true)}}", + options = ExecutionOptions(executor = Executor.DataLoaderPrepared) + ) + assertThrows { mapBothSkip.extract("data/film/year") } + + val mapOnlySkip = execute( + "{film{title, year @include(if: true) @skip(if: true)}}", + options = ExecutionOptions(executor = Executor.DataLoaderPrepared) + ) + assertThrows { mapOnlySkip.extract("data/film/year") } + + val mapOnlyInclude = execute( + "{film{title, year @include(if: false) @skip(if: false)}}", + options = ExecutionOptions(executor = Executor.DataLoaderPrepared) + ) + assertThrows { mapOnlyInclude.extract("data/film/year") } + + val mapNeither = execute( + "{film{title, year @include(if: true) @skip(if: false)}}", + options = ExecutionOptions(executor = Executor.DataLoaderPrepared) + ) + mapNeither.extract("data/film/year") shouldBeEqualTo 2006 + } + + @Test + fun `query with @include and @skip directive on field object (using DataLoaderPrepared executor)`() { + val mapWithSkip = execute( + "{ number(big: true), film @skip(if: true) { title } }", + options = ExecutionOptions(executor = Executor.DataLoaderPrepared) + ) + assertThrows { mapWithSkip.extract("data/film") } + + val mapWithoutSkip = execute( + "{ number(big: true), film @skip(if: false) { title } }", + options = ExecutionOptions(executor = Executor.DataLoaderPrepared) + ) + mapWithoutSkip.extract("data/film/title") shouldBeEqualTo "Prestige" + + val mapWithInclude = execute( + "{ number(big: true), film @include(if: true) { title } }", + options = ExecutionOptions(executor = Executor.DataLoaderPrepared) + ) + mapWithInclude.extract("data/film/title") shouldBeEqualTo "Prestige" + + val mapWithoutInclude = execute( + "{ number(big: true), film @include(if: false) { title } }", + options = ExecutionOptions(executor = Executor.DataLoaderPrepared) + ) + assertThrows { mapWithoutInclude.extract("data/film") } + } + + @Test + fun `query with @include directive on field with variable (using DataLoaderPrepared executor)`() { + val map = execute( + "query film (\$include: Boolean!) {film{title, year @include(if: \$include)}}", + "{\"include\":\"false\"}", + options = ExecutionOptions(executor = Executor.DataLoaderPrepared) + ) + assertThrows { map.extract("data/film/year") } } } diff --git a/kgraphql/src/test/kotlin/com/apurebase/kgraphql/specification/typesystem/UnionsSpecificationTest.kt b/kgraphql/src/test/kotlin/com/apurebase/kgraphql/specification/typesystem/UnionsSpecificationTest.kt index 15d22864..01be8e9b 100644 --- a/kgraphql/src/test/kotlin/com/apurebase/kgraphql/specification/typesystem/UnionsSpecificationTest.kt +++ b/kgraphql/src/test/kotlin/com/apurebase/kgraphql/specification/typesystem/UnionsSpecificationTest.kt @@ -1,15 +1,15 @@ package com.apurebase.kgraphql.specification.typesystem import com.apurebase.kgraphql.* +import com.apurebase.kgraphql.helpers.getFields import com.apurebase.kgraphql.integration.BaseSchemaTest import com.apurebase.kgraphql.schema.SchemaException -import com.apurebase.kgraphql.GraphQLError -import com.apurebase.kgraphql.helpers.getFields import com.apurebase.kgraphql.schema.execution.Execution import org.amshove.kluent.* import org.hamcrest.CoreMatchers import org.hamcrest.MatcherAssert import org.junit.jupiter.api.Test +import org.junit.jupiter.api.assertThrows @Specification("3.1.4 Unions") @@ -236,7 +236,7 @@ class UnionsSpecificationTest : BaseSchemaTest() { } """.trimIndent()).also(::println).deserialize().run { extract("data/returnUnion/i") shouldBeEqualTo 1 - extract("data/returnUnion/s") shouldBeEqualTo null + assertThrows { extract("data/returnUnion/s") } extract>("data/returnUnion/fields") shouldBeEqualTo listOf("i", "fields", "s") } }