From 6f25a6019e6eb13dd9ddc0760191ce6313fb6b97 Mon Sep 17 00:00:00 2001 From: sbarker2 <109047597+sbarker2@users.noreply.github.com> Date: Mon, 19 Aug 2024 12:11:05 +1200 Subject: [PATCH 01/24] add error test for defer (#574) --- .../fixtures/defer/DeferReturnsErrorTest.kt | 62 ++++++++++ .../defer/DeferReturnsErrorTestSnapshot.kt | 110 ++++++++++++++++++ 2 files changed, 172 insertions(+) create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/DeferReturnsErrorTest.kt create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/DeferReturnsErrorTestSnapshot.kt diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/DeferReturnsErrorTest.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/DeferReturnsErrorTest.kt new file mode 100644 index 000000000..d258acaca --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/DeferReturnsErrorTest.kt @@ -0,0 +1,62 @@ +package graphql.nadel.tests.next.fixtures.defer + +import graphql.nadel.NadelExecutionHints +import graphql.nadel.tests.next.NadelIntegrationTest + +open class DeferReturnsErrorTest : NadelIntegrationTest( + query = """ + query { + defer { + hello + ... @defer(label: "slow-defer") { + slow + } + } + } + """.trimIndent(), + services = listOf( + Service( + name = "defer", + overallSchema = """ + directive @defer(if: Boolean, label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT + + type Query { + defer: DeferApi + } + type DeferApi { + hello: String + slow: String + } + + """.trimIndent(), + runtimeWiring = { wiring -> + data class DeferApi( + val hello: String, + val slow: String, + ) + + wiring + .type("Query") { type -> + type + .dataFetcher("defer") { env -> + Any() + } + } + .type("DeferApi") { type -> + type + .dataFetcher("hello") { env -> + "helloString" + } + .dataFetcher("slow") { env -> + RuntimeException("An error occurred while fetching 'slow'") + } + } + }, + ), + ), +) { + override fun makeExecutionHints(): NadelExecutionHints.Builder { + return super.makeExecutionHints() + .deferSupport { true } + } +} \ No newline at end of file diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/DeferReturnsErrorTestSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/DeferReturnsErrorTestSnapshot.kt new file mode 100644 index 000000000..b5190b3f3 --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/DeferReturnsErrorTestSnapshot.kt @@ -0,0 +1,110 @@ +// @formatter:off +package graphql.nadel.tests.next.fixtures.defer + +import graphql.nadel.tests.next.ExpectedNadelResult +import graphql.nadel.tests.next.ExpectedServiceCall +import graphql.nadel.tests.next.TestSnapshot +import graphql.nadel.tests.next.listOfJsonStrings +import kotlin.Suppress +import kotlin.collections.List +import kotlin.collections.listOf + +private suspend fun main() { + graphql.nadel.tests.next.update() +} + +/** + * This class is generated. Do NOT modify. + * + * Refer to [graphql.nadel.tests.next.UpdateTestSnapshots + */ +@Suppress("unused") +public class DeferReturnsErrorTestSnapshot : TestSnapshot() { + override val calls: List = listOf( + ExpectedServiceCall( + service = "defer", + query = """ + | { + | defer { + | hello + | ... @defer(label: "slow-defer") { + | slow + | } + | } + | } + """.trimMargin(), + variables = "{}", + result = """ + | { + | "data": { + | "defer": { + | "hello": "helloString" + | } + | }, + | "hasNext": true + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [ + | "defer" + | ], + | "label": "slow-defer", + | "data": { + | "slow": "java.lang.RuntimeException: An error occurred while fetching 'slow'" + | } + | } + | ] + | } + """.trimMargin(), + ), + ), + ) + + /** + * ```json + * { + * "data": { + * "defer": { + * "hello": "helloString", + * "slow": "java.lang.RuntimeException: An error occurred while fetching 'slow'" + * } + * } + * } + * ``` + */ + override val result: ExpectedNadelResult = ExpectedNadelResult( + result = """ + | { + | "data": { + | "defer": { + | "hello": "helloString" + | } + | }, + | "hasNext": true + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [ + | "defer" + | ], + | "label": "slow-defer", + | "data": { + | "slow": "java.lang.RuntimeException: An error occurred while fetching 'slow'" + | } + | } + | ] + | } + """.trimMargin(), + ), + ) +} From aa1248af82079d647ac6d3be7f99cfe2edba314f Mon Sep 17 00:00:00 2001 From: sbarker2 <109047597+sbarker2@users.noreply.github.com> Date: Mon, 19 Aug 2024 14:18:10 +1200 Subject: [PATCH 02/24] add initial defer transform tests (#576) --- .../transforms/DeferredFieldIsRenamedTest.kt | 69 +++++++++++ .../DeferredFieldIsRenamedTestSnapshot.kt | 78 ++++++++++++ ...leRenameTransformsInsideAndOutsideDefer.kt | 71 +++++++++++ ...TransformsInsideAndOutsideDeferSnapshot.kt | 80 ++++++++++++ ...ultipleRenamedFieldsAreDeferredTogether.kt | 75 ++++++++++++ ...enamedFieldsAreDeferredTogetherSnapshot.kt | 84 +++++++++++++ .../MultipleSeparateDefersWithTransforms.kt | 79 ++++++++++++ ...pleSeparateDefersWithTransformsSnapshot.kt | 86 +++++++++++++ ...ngDeferButNonDeferredFieldIsRenamedTest.kt | 69 +++++++++++ ...utNonDeferredFieldIsRenamedTestSnapshot.kt | 110 +++++++++++++++++ .../transforms/RenameInnerFieldInsideDefer.kt | 79 ++++++++++++ .../RenameInnerFieldInsideDeferSnapshot.kt | 114 ++++++++++++++++++ 12 files changed, 994 insertions(+) create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredFieldIsRenamedTest.kt create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredFieldIsRenamedTestSnapshot.kt create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenameTransformsInsideAndOutsideDefer.kt create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenameTransformsInsideAndOutsideDeferSnapshot.kt create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenamedFieldsAreDeferredTogether.kt create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenamedFieldsAreDeferredTogetherSnapshot.kt create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleSeparateDefersWithTransforms.kt create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleSeparateDefersWithTransformsSnapshot.kt create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/QueryContainingDeferButNonDeferredFieldIsRenamedTest.kt create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/QueryContainingDeferButNonDeferredFieldIsRenamedTestSnapshot.kt create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/RenameInnerFieldInsideDefer.kt create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/RenameInnerFieldInsideDeferSnapshot.kt diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredFieldIsRenamedTest.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredFieldIsRenamedTest.kt new file mode 100644 index 000000000..3c507a54c --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredFieldIsRenamedTest.kt @@ -0,0 +1,69 @@ +package graphql.nadel.tests.next.fixtures.defer.transforms + +import graphql.nadel.NadelExecutionHints +import graphql.nadel.tests.next.NadelIntegrationTest + +open class DeferredFieldIsRenamedTest : NadelIntegrationTest( + query = """ + query { + defer { + hello + ...@defer { + overallString + } + } + } + """.trimIndent(), + services = listOf( + Service( + name = "defer", + overallSchema = """ + directive @defer(if: Boolean, label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT + + type Query { + defer: DeferApi + } + type DeferApi { + hello: String + overallString: String @renamed(from: "underlyingString") + } + + """.trimIndent(), + underlyingSchema = """ + directive @defer(if: Boolean, label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT + + type Query { + defer: DeferApi + } + type DeferApi { + hello: String + underlyingString: String + } + + """.trimIndent(), + runtimeWiring = { wiring -> + wiring + .type("Query") { type -> + type + .dataFetcher("defer") { env -> + Any() + } + } + .type("DeferApi") { type -> + type + .dataFetcher("hello") { env -> + "hello there" + } + .dataFetcher("underlyingString") { env -> + "string for the deferred renamed field" + } + } + }, + ), + ), +) { + override fun makeExecutionHints(): NadelExecutionHints.Builder { + return super.makeExecutionHints() + .deferSupport { true } + } +} \ No newline at end of file diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredFieldIsRenamedTestSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredFieldIsRenamedTestSnapshot.kt new file mode 100644 index 000000000..25fd92375 --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredFieldIsRenamedTestSnapshot.kt @@ -0,0 +1,78 @@ +// @formatter:off +package graphql.nadel.tests.next.fixtures.defer.transforms + +import graphql.nadel.tests.next.ExpectedNadelResult +import graphql.nadel.tests.next.ExpectedServiceCall +import graphql.nadel.tests.next.TestSnapshot +import graphql.nadel.tests.next.listOfJsonStrings +import kotlin.Suppress +import kotlin.collections.List +import kotlin.collections.listOf + +private suspend fun main() { + graphql.nadel.tests.next.update() +} + +/** + * This class is generated. Do NOT modify. + * + * Refer to [graphql.nadel.tests.next.UpdateTestSnapshots + */ +@Suppress("unused") +public class DeferredFieldIsRenamedTestSnapshot : TestSnapshot() { + override val calls: List = listOf( + ExpectedServiceCall( + service = "defer", + query = """ + | { + | defer { + | hello + | rename__overallString__underlyingString: underlyingString + | __typename__rename__overallString: __typename + | } + | } + """.trimMargin(), + variables = "{}", + result = """ + | { + | "data": { + | "defer": { + | "hello": "hello there", + | "rename__overallString__underlyingString": "string for the deferred renamed field", + | "__typename__rename__overallString": "DeferApi" + | } + | } + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + ), + ), + ) + + /** + * ```json + * { + * "data": { + * "defer": { + * "hello": "hello there", + * "overallString": "string for the deferred renamed field" + * } + * } + * } + * ``` + */ + override val result: ExpectedNadelResult = ExpectedNadelResult( + result = """ + | { + | "data": { + | "defer": { + | "hello": "hello there", + | "overallString": "string for the deferred renamed field" + | } + | } + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + ), + ) +} diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenameTransformsInsideAndOutsideDefer.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenameTransformsInsideAndOutsideDefer.kt new file mode 100644 index 000000000..2d4546b7b --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenameTransformsInsideAndOutsideDefer.kt @@ -0,0 +1,71 @@ +package graphql.nadel.tests.next.fixtures.defer.transforms + +import graphql.nadel.NadelExecutionHints +import graphql.nadel.tests.next.NadelIntegrationTest + +open class MultipleRenameTransformsInsideAndOutsideDefer : NadelIntegrationTest( + query = """ + query { + defer { + fastRenamedString + ...@defer { + slowRenamedString + } + } + } + """.trimIndent(), + services = listOf( + Service( + name = "defer", + overallSchema = """ + directive @defer(if: Boolean, label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT + + type Query { + defer: DeferApi + } + type DeferApi { + hello: String + slowRenamedString: String @renamed(from: "slowString") + fastRenamedString: String @renamed(from: "fastString") + } + + """.trimIndent(), + underlyingSchema = """ + directive @defer(if: Boolean, label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT + + type Query { + defer: DeferApi + } + type DeferApi { + hello: String + slowString: String + fastString: String + } + + """.trimIndent(), + runtimeWiring = { wiring -> + wiring + .type("Query") { type -> + type + .dataFetcher("defer") { env -> + Any() + } + } + .type("DeferApi") { type -> + type + .dataFetcher("slowString") { env -> + "this is the slow string (deferred)" + } + .dataFetcher("fastString") { env -> + "this is the fast string (not deferred)" + } + } + }, + ), + ), +) { + override fun makeExecutionHints(): NadelExecutionHints.Builder { + return super.makeExecutionHints() + .deferSupport { true } + } +} \ No newline at end of file diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenameTransformsInsideAndOutsideDeferSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenameTransformsInsideAndOutsideDeferSnapshot.kt new file mode 100644 index 000000000..e85b6ce6f --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenameTransformsInsideAndOutsideDeferSnapshot.kt @@ -0,0 +1,80 @@ +// @formatter:off +package graphql.nadel.tests.next.fixtures.defer.transforms + +import graphql.nadel.tests.next.ExpectedNadelResult +import graphql.nadel.tests.next.ExpectedServiceCall +import graphql.nadel.tests.next.TestSnapshot +import graphql.nadel.tests.next.listOfJsonStrings +import kotlin.Suppress +import kotlin.collections.List +import kotlin.collections.listOf + +private suspend fun main() { + graphql.nadel.tests.next.update() +} + +/** + * This class is generated. Do NOT modify. + * + * Refer to [graphql.nadel.tests.next.UpdateTestSnapshots + */ +@Suppress("unused") +public class MultipleRenameTransformsInsideAndOutsideDeferSnapshot : TestSnapshot() { + override val calls: List = listOf( + ExpectedServiceCall( + service = "defer", + query = """ + | { + | defer { + | rename__fastRenamedString__fastString: fastString + | __typename__rename__fastRenamedString: __typename + | rename__slowRenamedString__slowString: slowString + | __typename__rename__slowRenamedString: __typename + | } + | } + """.trimMargin(), + variables = "{}", + result = """ + | { + | "data": { + | "defer": { + | "rename__fastRenamedString__fastString": "this is the fast string (not deferred)", + | "__typename__rename__fastRenamedString": "DeferApi", + | "rename__slowRenamedString__slowString": "this is the slow string (deferred)", + | "__typename__rename__slowRenamedString": "DeferApi" + | } + | } + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + ), + ), + ) + + /** + * ```json + * { + * "data": { + * "defer": { + * "fastRenamedString": "this is the fast string (not deferred)", + * "slowRenamedString": "this is the slow string (deferred)" + * } + * } + * } + * ``` + */ + override val result: ExpectedNadelResult = ExpectedNadelResult( + result = """ + | { + | "data": { + | "defer": { + | "fastRenamedString": "this is the fast string (not deferred)", + | "slowRenamedString": "this is the slow string (deferred)" + | } + | } + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + ), + ) +} diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenamedFieldsAreDeferredTogether.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenamedFieldsAreDeferredTogether.kt new file mode 100644 index 000000000..5f14a9b5e --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenamedFieldsAreDeferredTogether.kt @@ -0,0 +1,75 @@ +package graphql.nadel.tests.next.fixtures.defer.transforms + +import graphql.nadel.NadelExecutionHints +import graphql.nadel.tests.next.NadelIntegrationTest + +open class MultipleRenamedFieldsAreDeferredTogether : NadelIntegrationTest( + query = """ + query { + defer { + hello + ...@defer { + overallString + overallString2 + } + } + } + """.trimIndent(), + services = listOf( + Service( + name = "defer", + overallSchema = """ + directive @defer(if: Boolean, label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT + + type Query { + defer: DeferApi + } + type DeferApi { + hello: String + overallString: String @renamed(from: "underlyingString") + overallString2: String @renamed(from: "underlyingString2") + } + + """.trimIndent(), + underlyingSchema = """ + directive @defer(if: Boolean, label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT + + type Query { + defer: DeferApi + } + type DeferApi { + hello: String + underlyingString: String + underlyingString2: String + } + + """.trimIndent(), + runtimeWiring = { wiring -> + wiring + .type("Query") { type -> + type + .dataFetcher("defer") { env -> + Any() + } + } + .type("DeferApi") { type -> + type + .dataFetcher("hello") { env -> + "hello there" + } + .dataFetcher("underlyingString") { env -> + "deferred string 1" + } + .dataFetcher("underlyingString2") { env -> + "deferred string 2" + } + } + }, + ), + ), +) { + override fun makeExecutionHints(): NadelExecutionHints.Builder { + return super.makeExecutionHints() + .deferSupport { true } + } +} \ No newline at end of file diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenamedFieldsAreDeferredTogetherSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenamedFieldsAreDeferredTogetherSnapshot.kt new file mode 100644 index 000000000..d66fc0df9 --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenamedFieldsAreDeferredTogetherSnapshot.kt @@ -0,0 +1,84 @@ +// @formatter:off +package graphql.nadel.tests.next.fixtures.defer.transforms + +import graphql.nadel.tests.next.ExpectedNadelResult +import graphql.nadel.tests.next.ExpectedServiceCall +import graphql.nadel.tests.next.TestSnapshot +import graphql.nadel.tests.next.listOfJsonStrings +import kotlin.Suppress +import kotlin.collections.List +import kotlin.collections.listOf + +private suspend fun main() { + graphql.nadel.tests.next.update() +} + +/** + * This class is generated. Do NOT modify. + * + * Refer to [graphql.nadel.tests.next.UpdateTestSnapshots + */ +@Suppress("unused") +public class MultipleRenamedFieldsAreDeferredTogetherSnapshot : TestSnapshot() { + override val calls: List = listOf( + ExpectedServiceCall( + service = "defer", + query = """ + | { + | defer { + | hello + | rename__overallString__underlyingString: underlyingString + | __typename__rename__overallString: __typename + | rename__overallString2__underlyingString2: underlyingString2 + | __typename__rename__overallString2: __typename + | } + | } + """.trimMargin(), + variables = "{}", + result = """ + | { + | "data": { + | "defer": { + | "hello": "hello there", + | "rename__overallString__underlyingString": "deferred string 1", + | "__typename__rename__overallString": "DeferApi", + | "rename__overallString2__underlyingString2": "deferred string 2", + | "__typename__rename__overallString2": "DeferApi" + | } + | } + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + ), + ), + ) + + /** + * ```json + * { + * "data": { + * "defer": { + * "hello": "hello there", + * "overallString": "deferred string 1", + * "overallString2": "deferred string 2" + * } + * } + * } + * ``` + */ + override val result: ExpectedNadelResult = ExpectedNadelResult( + result = """ + | { + | "data": { + | "defer": { + | "hello": "hello there", + | "overallString": "deferred string 1", + | "overallString2": "deferred string 2" + | } + | } + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + ), + ) +} diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleSeparateDefersWithTransforms.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleSeparateDefersWithTransforms.kt new file mode 100644 index 000000000..78d8f05b2 --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleSeparateDefersWithTransforms.kt @@ -0,0 +1,79 @@ +package graphql.nadel.tests.next.fixtures.defer.transforms + +import graphql.nadel.NadelExecutionHints +import graphql.nadel.tests.next.NadelIntegrationTest + +open class MultipleSeparateDefersWithTransforms : NadelIntegrationTest( + query = """ + query { + defer { + fastRenamedString + ...@defer { + slowRenamedString + } + ...@defer { + anotherSlowRenamedString + } + } + } + """.trimIndent(), + services = listOf( + Service( + name = "defer", + overallSchema = """ + directive @defer(if: Boolean, label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT + + type Query { + defer: DeferApi + } + type DeferApi { + hello: String + slowRenamedString: String @renamed(from: "slowString") + anotherSlowRenamedString: String @renamed(from: "anotherSlowString") + fastRenamedString: String @renamed(from: "fastString") + } + + """.trimIndent(), + underlyingSchema = """ + directive @defer(if: Boolean, label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT + + type Query { + defer: DeferApi + } + type DeferApi { + hello: String + slowString: String + anotherSlowString: String + fastString: String + } + + """.trimIndent(), + runtimeWiring = { wiring -> + wiring + .type("Query") { type -> + type + .dataFetcher("defer") { env -> + Any() + } + } + .type("DeferApi") { type -> + type + .dataFetcher("slowString") { env -> + "this is the slow string (deferred)" + } + .dataFetcher("anotherSlowString") { env -> + "this is the other slow string (deferred)" + } + .dataFetcher("fastString") { env -> + "this is the fast string (not deferred)" + } + } + }, + ), + ), +) { + override fun makeExecutionHints(): NadelExecutionHints.Builder { + return super.makeExecutionHints() + .deferSupport { true } + } +} \ No newline at end of file diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleSeparateDefersWithTransformsSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleSeparateDefersWithTransformsSnapshot.kt new file mode 100644 index 000000000..ecc2c93da --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleSeparateDefersWithTransformsSnapshot.kt @@ -0,0 +1,86 @@ +// @formatter:off +package graphql.nadel.tests.next.fixtures.defer.transforms + +import graphql.nadel.tests.next.ExpectedNadelResult +import graphql.nadel.tests.next.ExpectedServiceCall +import graphql.nadel.tests.next.TestSnapshot +import graphql.nadel.tests.next.listOfJsonStrings +import kotlin.Suppress +import kotlin.collections.List +import kotlin.collections.listOf + +private suspend fun main() { + graphql.nadel.tests.next.update() +} + +/** + * This class is generated. Do NOT modify. + * + * Refer to [graphql.nadel.tests.next.UpdateTestSnapshots + */ +@Suppress("unused") +public class MultipleSeparateDefersWithTransformsSnapshot : TestSnapshot() { + override val calls: List = listOf( + ExpectedServiceCall( + service = "defer", + query = """ + | { + | defer { + | rename__fastRenamedString__fastString: fastString + | __typename__rename__fastRenamedString: __typename + | rename__slowRenamedString__slowString: slowString + | __typename__rename__slowRenamedString: __typename + | rename__anotherSlowRenamedString__anotherSlowString: anotherSlowString + | __typename__rename__anotherSlowRenamedString: __typename + | } + | } + """.trimMargin(), + variables = "{}", + result = """ + | { + | "data": { + | "defer": { + | "rename__fastRenamedString__fastString": "this is the fast string (not deferred)", + | "__typename__rename__fastRenamedString": "DeferApi", + | "rename__slowRenamedString__slowString": "this is the slow string (deferred)", + | "__typename__rename__slowRenamedString": "DeferApi", + | "rename__anotherSlowRenamedString__anotherSlowString": "this is the other slow string (deferred)", + | "__typename__rename__anotherSlowRenamedString": "DeferApi" + | } + | } + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + ), + ), + ) + + /** + * ```json + * { + * "data": { + * "defer": { + * "fastRenamedString": "this is the fast string (not deferred)", + * "slowRenamedString": "this is the slow string (deferred)", + * "anotherSlowRenamedString": "this is the other slow string (deferred)" + * } + * } + * } + * ``` + */ + override val result: ExpectedNadelResult = ExpectedNadelResult( + result = """ + | { + | "data": { + | "defer": { + | "fastRenamedString": "this is the fast string (not deferred)", + | "slowRenamedString": "this is the slow string (deferred)", + | "anotherSlowRenamedString": "this is the other slow string (deferred)" + | } + | } + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + ), + ) +} diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/QueryContainingDeferButNonDeferredFieldIsRenamedTest.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/QueryContainingDeferButNonDeferredFieldIsRenamedTest.kt new file mode 100644 index 000000000..7fcae6d7c --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/QueryContainingDeferButNonDeferredFieldIsRenamedTest.kt @@ -0,0 +1,69 @@ +package graphql.nadel.tests.next.fixtures.defer.transforms + +import graphql.nadel.NadelExecutionHints +import graphql.nadel.tests.next.NadelIntegrationTest + +open class QueryContainingDeferButNonDeferredFieldIsRenamedTest : NadelIntegrationTest( + query = """ + query { + defer { + overallString + ...@defer { + slow + } + } + } + """.trimIndent(), + services = listOf( + Service( + name = "defer", + overallSchema = """ + directive @defer(if: Boolean, label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT + + type Query { + defer: DeferApi + } + type DeferApi { + overallString: String @renamed(from: "underlyingString") + slow: String + } + + """.trimIndent(), + underlyingSchema = """ + directive @defer(if: Boolean, label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT + + type Query { + defer: DeferApi + } + type DeferApi { + underlyingString: String + slow: String + } + + """.trimIndent(), + runtimeWiring = { wiring -> + wiring + .type("Query") { type -> + type + .dataFetcher("defer") { env -> + Any() + } + } + .type("DeferApi") { type -> + type + .dataFetcher("underlyingString") { env -> + "this is the (non-deferred) renamed string" + } + .dataFetcher("slow") { env -> + "this is the deferred string" + } + } + }, + ), + ), +) { + override fun makeExecutionHints(): NadelExecutionHints.Builder { + return super.makeExecutionHints() + .deferSupport { true } + } +} \ No newline at end of file diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/QueryContainingDeferButNonDeferredFieldIsRenamedTestSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/QueryContainingDeferButNonDeferredFieldIsRenamedTestSnapshot.kt new file mode 100644 index 000000000..973c588fa --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/QueryContainingDeferButNonDeferredFieldIsRenamedTestSnapshot.kt @@ -0,0 +1,110 @@ +// @formatter:off +package graphql.nadel.tests.next.fixtures.defer.transforms + +import graphql.nadel.tests.next.ExpectedNadelResult +import graphql.nadel.tests.next.ExpectedServiceCall +import graphql.nadel.tests.next.TestSnapshot +import graphql.nadel.tests.next.listOfJsonStrings +import kotlin.Suppress +import kotlin.collections.List +import kotlin.collections.listOf + +private suspend fun main() { + graphql.nadel.tests.next.update() +} + +/** + * This class is generated. Do NOT modify. + * + * Refer to [graphql.nadel.tests.next.UpdateTestSnapshots + */ +@Suppress("unused") +public class QueryContainingDeferButNonDeferredFieldIsRenamedTestSnapshot : TestSnapshot() { + override val calls: List = listOf( + ExpectedServiceCall( + service = "defer", + query = """ + | { + | defer { + | rename__overallString__underlyingString: underlyingString + | __typename__rename__overallString: __typename + | ... @defer { + | slow + | } + | } + | } + """.trimMargin(), + variables = "{}", + result = """ + | { + | "data": { + | "defer": { + | "rename__overallString__underlyingString": "this is the (non-deferred) renamed string", + | "__typename__rename__overallString": "DeferApi" + | } + | }, + | "hasNext": true + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [ + | "defer" + | ], + | "data": { + | "slow": "this is the deferred string" + | } + | } + | ] + | } + """.trimMargin(), + ), + ), + ) + + /** + * ```json + * { + * "data": { + * "defer": { + * "overallString": "this is the (non-deferred) renamed string", + * "slow": "this is the deferred string" + * } + * } + * } + * ``` + */ + override val result: ExpectedNadelResult = ExpectedNadelResult( + result = """ + | { + | "data": { + | "defer": { + | "overallString": "this is the (non-deferred) renamed string" + | } + | }, + | "hasNext": true + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [ + | "defer" + | ], + | "data": { + | "slow": "this is the deferred string" + | } + | } + | ] + | } + """.trimMargin(), + ), + ) +} diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/RenameInnerFieldInsideDefer.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/RenameInnerFieldInsideDefer.kt new file mode 100644 index 000000000..c78bc24ab --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/RenameInnerFieldInsideDefer.kt @@ -0,0 +1,79 @@ +package graphql.nadel.tests.next.fixtures.defer.transforms + +import graphql.nadel.NadelExecutionHints +import graphql.nadel.tests.next.NadelIntegrationTest + +open class RenameInnerFieldInsideDefer : NadelIntegrationTest( + query = """ + query { + defer { + ...@defer(if: true) { + user { + firstName + } + } + } + } + """.trimIndent(), + services = listOf( + Service( + name = "defer", + overallSchema = """ + directive @defer(if: Boolean, label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT + + type Query { + defer: DeferApi + } + type DeferApi { + user: User + } + type User { + firstName: String @renamed(from: "name") + lastName: String + } + + """.trimIndent(), + underlyingSchema = """ + directive @defer(if: Boolean, label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT + + type Query { + defer: DeferApi + } + type DeferApi { + user: User + } + type User { + name: String + lastName: String + } + + """.trimIndent(), + runtimeWiring = { wiring -> + wiring + .type("Query") { type -> + type + .dataFetcher("defer") { env -> + Any() + } + } + .type("DeferApi") { type -> + type + .dataFetcher("user") { env -> + Any() + } + } + .type("User") { type -> + type + .dataFetcher("name") { env -> + "Steven" + } + } + }, + ), + ), +) { + override fun makeExecutionHints(): NadelExecutionHints.Builder { + return super.makeExecutionHints() + .deferSupport { true } + } +} \ No newline at end of file diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/RenameInnerFieldInsideDeferSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/RenameInnerFieldInsideDeferSnapshot.kt new file mode 100644 index 000000000..67893ce4d --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/RenameInnerFieldInsideDeferSnapshot.kt @@ -0,0 +1,114 @@ +// @formatter:off +package graphql.nadel.tests.next.fixtures.defer.transforms + +import graphql.nadel.tests.next.ExpectedNadelResult +import graphql.nadel.tests.next.ExpectedServiceCall +import graphql.nadel.tests.next.TestSnapshot +import graphql.nadel.tests.next.listOfJsonStrings +import kotlin.Suppress +import kotlin.collections.List +import kotlin.collections.listOf + +private suspend fun main() { + graphql.nadel.tests.next.update() +} + +/** + * This class is generated. Do NOT modify. + * + * Refer to [graphql.nadel.tests.next.UpdateTestSnapshots + */ +@Suppress("unused") +public class RenameInnerFieldInsideDeferSnapshot : TestSnapshot() { + override val calls: List = listOf( + ExpectedServiceCall( + service = "defer", + query = """ + | { + | defer { + | ... @defer { + | user { + | rename__firstName__name: name + | __typename__rename__firstName: __typename + | } + | } + | } + | } + """.trimMargin(), + variables = "{}", + result = """ + | { + | "data": { + | "defer": {} + | }, + | "hasNext": true + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [ + | "defer" + | ], + | "data": { + | "user": { + | "rename__firstName__name": "Steven", + | "__typename__rename__firstName": "User" + | } + | } + | } + | ] + | } + """.trimMargin(), + ), + ), + ) + + /** + * ```json + * { + * "data": { + * "defer": { + * "user": { + * "rename__firstName__name": "Steven", + * "__typename__rename__firstName": "User" + * } + * } + * } + * } + * ``` + */ + override val result: ExpectedNadelResult = ExpectedNadelResult( + result = """ + | { + | "data": { + | "defer": {} + | }, + | "hasNext": true + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [ + | "defer" + | ], + | "data": { + | "user": { + | "rename__firstName__name": "Steven", + | "__typename__rename__firstName": "User" + | } + | } + | } + | ] + | } + """.trimMargin(), + ), + ) +} From 96654b78aea20d5e1e3a05df6ba3e64a2c889272 Mon Sep 17 00:00:00 2001 From: sbarker2 <109047597+sbarker2@users.noreply.github.com> Date: Tue, 20 Aug 2024 08:49:18 +1200 Subject: [PATCH 03/24] initial defer logic (#577) --- .../main/java/graphql/nadel/NextgenEngine.kt | 25 +++++++- .../transform/NadelDeepRenameTransform.kt | 2 + .../engine/transform/NadelRenameTransform.kt | 2 + .../engine/transform/NadelTransformUtil.kt | 3 + .../hydration/NadelHydrationTransform.kt | 1 + .../batch/NadelBatchHydrationTransform.kt | 1 + .../nadel/engine/transform/query/NFUtil.kt | 11 ++++ .../result/NadelResultTransformer.kt | 26 ++++++-- .../DeferredFieldIsRenamedTestSnapshot.kt | 38 +++++++---- ...TransformsInsideAndOutsideDeferSnapshot.kt | 38 +++++++---- ...enamedFieldsAreDeferredTogetherSnapshot.kt | 48 +++++++++----- ...pleSeparateDefersWithTransformsSnapshot.kt | 64 ++++++++++++++----- 12 files changed, 198 insertions(+), 61 deletions(-) diff --git a/lib/src/main/java/graphql/nadel/NextgenEngine.kt b/lib/src/main/java/graphql/nadel/NextgenEngine.kt index c5b065b8e..89ec9cb03 100644 --- a/lib/src/main/java/graphql/nadel/NextgenEngine.kt +++ b/lib/src/main/java/graphql/nadel/NextgenEngine.kt @@ -62,6 +62,7 @@ import kotlinx.coroutines.awaitAll import kotlinx.coroutines.cancel import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.map import kotlinx.coroutines.future.asCompletableFuture import kotlinx.coroutines.future.asDeferred import kotlinx.coroutines.future.await @@ -287,8 +288,11 @@ internal class NextgenEngine( service = service, transformedQuery = transformedQuery, executionContext = executionContext, + executionPlan = executionPlan, serviceExecutionContext = serviceExecutionContext, executionHydrationDetails = executionContext.hydrationDetails, + artificialFields = queryTransform.artificialFields, + overallToUnderlyingFields = queryTransform.overallToUnderlyingFields, ) } val transformedResult: ServiceExecutionResult = when { @@ -313,8 +317,11 @@ internal class NextgenEngine( service: Service, transformedQuery: ExecutableNormalizedField, executionContext: NadelExecutionContext, + executionPlan: NadelExecutionPlan, serviceExecutionContext: NadelServiceExecutionContext, executionHydrationDetails: ServiceExecutionHydrationDetails? = null, + artificialFields: List, + overallToUnderlyingFields: Map>, ): ServiceExecutionResult { val timer = executionContext.timer @@ -383,7 +390,23 @@ internal class NextgenEngine( if (serviceExecResult is NadelIncrementalServiceExecutionResult) { executionContext.incrementalResultSupport.defer( - serviceExecResult.incrementalItemPublisher.asFlow() + serviceExecResult.incrementalItemPublisher + .asFlow() + .map {delayedIncrementalResult -> + // Transform + resultTransformer + .transform( + executionContext = executionContext, + serviceExecutionContext = serviceExecutionContext, + executionPlan = executionPlan, + artificialFields = artificialFields, + overallToUnderlyingFields = overallToUnderlyingFields, + service = service, + result = serviceExecResult, //NO LONGER USED + // dipr = delayedIncrementalResult + ) + delayedIncrementalResult + } ) } diff --git a/lib/src/main/java/graphql/nadel/engine/transform/NadelDeepRenameTransform.kt b/lib/src/main/java/graphql/nadel/engine/transform/NadelDeepRenameTransform.kt index 976c7c573..beb4a5886 100644 --- a/lib/src/main/java/graphql/nadel/engine/transform/NadelDeepRenameTransform.kt +++ b/lib/src/main/java/graphql/nadel/engine/transform/NadelDeepRenameTransform.kt @@ -216,6 +216,7 @@ internal class NadelDeepRenameTransform : NadelTransform { return NadelTransformUtil.makeTypeNameField( aliasHelper = state.aliasHelper, objectTypeNames = objectTypeNames, + deferredExecutions = field.deferredExecutions, ) } @@ -170,6 +171,7 @@ internal class NadelRenameTransform : NadelTransform { queryPathToField = NadelQueryPath(listOf(rename.underlyingName)), fieldArguments = field.normalizedArguments, fieldChildren = transformer.transform(field.children), + deferredExecutions = field.deferredExecutions, ) ) } diff --git a/lib/src/main/java/graphql/nadel/engine/transform/NadelTransformUtil.kt b/lib/src/main/java/graphql/nadel/engine/transform/NadelTransformUtil.kt index 52570b4ed..19f6ad532 100644 --- a/lib/src/main/java/graphql/nadel/engine/transform/NadelTransformUtil.kt +++ b/lib/src/main/java/graphql/nadel/engine/transform/NadelTransformUtil.kt @@ -11,6 +11,7 @@ import graphql.nadel.engine.util.getField import graphql.nadel.engine.util.makeFieldCoordinates import graphql.normalized.ExecutableNormalizedField import graphql.normalized.ExecutableNormalizedField.newNormalizedField +import graphql.normalized.incremental.NormalizedDeferredExecution import graphql.schema.GraphQLFieldDefinition object NadelTransformUtil { @@ -36,11 +37,13 @@ object NadelTransformUtil { fun makeTypeNameField( aliasHelper: NadelAliasHelper, objectTypeNames: List, + deferredExecutions: LinkedHashSet ): ExecutableNormalizedField { return newNormalizedField() .alias(aliasHelper.typeNameResultKey) .fieldName(TypeNameMetaFieldDef.name) .objectTypeNames(objectTypeNames) + .deferredExecutions(deferredExecutions) .build() } diff --git a/lib/src/main/java/graphql/nadel/engine/transform/hydration/NadelHydrationTransform.kt b/lib/src/main/java/graphql/nadel/engine/transform/hydration/NadelHydrationTransform.kt index b804b4b61..99cb7d2f8 100644 --- a/lib/src/main/java/graphql/nadel/engine/transform/hydration/NadelHydrationTransform.kt +++ b/lib/src/main/java/graphql/nadel/engine/transform/hydration/NadelHydrationTransform.kt @@ -145,6 +145,7 @@ internal class NadelHydrationTransform( return makeTypeNameField( aliasHelper = state.aliasHelper, objectTypeNames = objectTypeNames, + deferredExecutions = linkedSetOf(), ) } diff --git a/lib/src/main/java/graphql/nadel/engine/transform/hydration/batch/NadelBatchHydrationTransform.kt b/lib/src/main/java/graphql/nadel/engine/transform/hydration/batch/NadelBatchHydrationTransform.kt index ac2340dbe..012befe55 100644 --- a/lib/src/main/java/graphql/nadel/engine/transform/hydration/batch/NadelBatchHydrationTransform.kt +++ b/lib/src/main/java/graphql/nadel/engine/transform/hydration/batch/NadelBatchHydrationTransform.kt @@ -140,6 +140,7 @@ internal class NadelBatchHydrationTransform( return makeTypeNameField( aliasHelper = state.aliasHelper, objectTypeNames = objectTypeNames, + deferredExecutions = linkedSetOf(), ) } } diff --git a/lib/src/main/java/graphql/nadel/engine/transform/query/NFUtil.kt b/lib/src/main/java/graphql/nadel/engine/transform/query/NFUtil.kt index a945d530a..d5ad5f456 100644 --- a/lib/src/main/java/graphql/nadel/engine/transform/query/NFUtil.kt +++ b/lib/src/main/java/graphql/nadel/engine/transform/query/NFUtil.kt @@ -2,6 +2,7 @@ package graphql.nadel.engine.transform.query import graphql.normalized.ExecutableNormalizedField import graphql.normalized.NormalizedInputValue +import graphql.normalized.incremental.NormalizedDeferredExecution import graphql.schema.GraphQLInterfaceType import graphql.schema.GraphQLObjectType import graphql.schema.GraphQLOutputType @@ -18,6 +19,7 @@ object NFUtil { pathToField: NadelQueryPath, fieldArguments: Map, fieldChildren: List, + deferredExecutions: LinkedHashSet = LinkedHashSet(), ): List { return createFieldRecursively( schema, @@ -26,6 +28,7 @@ object NFUtil { fieldArguments, fieldChildren, pathToFieldIndex = 0, + deferredExecutions, ) } @@ -35,6 +38,7 @@ object NFUtil { queryPathToField: NadelQueryPath, fieldArguments: Map, fieldChildren: List, + deferredExecutions: LinkedHashSet = LinkedHashSet(), ): ExecutableNormalizedField { return createParticularField( schema, @@ -43,6 +47,7 @@ object NFUtil { fieldArguments, fieldChildren, pathToFieldIndex = 0, + deferredExecutions, ) } @@ -53,6 +58,7 @@ object NFUtil { fieldArguments: Map, fieldChildren: List, pathToFieldIndex: Int, + deferredExecutions: LinkedHashSet, ): List { // Note: remember that we are creating fields that do not exist in the original NF // Thus, we need to handle interfaces and object types @@ -65,6 +71,7 @@ object NFUtil { fieldArguments, fieldChildren, pathToFieldIndex, + deferredExecutions, ) } is GraphQLObjectType -> listOf( @@ -75,6 +82,7 @@ object NFUtil { fieldArguments, fieldChildren, pathToFieldIndex, + deferredExecutions, ) ) else -> error("Unsupported type '${parentType.javaClass.name}'") @@ -88,6 +96,7 @@ object NFUtil { fieldArguments: Map, fieldChildren: List, pathToFieldIndex: Int, + deferredExecutions: LinkedHashSet, ): ExecutableNormalizedField { val fieldName = queryPathToField.segments[pathToFieldIndex] val fieldDef = parentType.getFieldDefinition(fieldName) @@ -96,6 +105,7 @@ object NFUtil { return ExecutableNormalizedField.newNormalizedField() .objectTypeNames(listOf(parentType.name)) .fieldName(fieldName) + .deferredExecutions(deferredExecutions) .also { builder -> if (pathToFieldIndex == queryPathToField.segments.lastIndex) { builder.normalizedArguments(fieldArguments) @@ -112,6 +122,7 @@ object NFUtil { fieldArguments, fieldChildren, pathToFieldIndex = pathToFieldIndex + 1, + deferredExecutions, ) } ) diff --git a/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt b/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt index 1fa73da9e..011c5dab3 100644 --- a/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt +++ b/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt @@ -1,5 +1,7 @@ package graphql.nadel.engine.transform.result +import graphql.incremental.DelayedIncrementalPartialResultImpl +import graphql.nadel.NadelIncrementalServiceExecutionResult import graphql.nadel.Service import graphql.nadel.ServiceExecutionResult import graphql.nadel.engine.NadelExecutionContext @@ -15,6 +17,8 @@ import kotlinx.coroutines.Deferred import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.reactive.asFlow internal class NadelResultTransformer(private val executionBlueprint: NadelOverallExecutionBlueprint) { suspend fun transform( @@ -26,9 +30,23 @@ internal class NadelResultTransformer(private val executionBlueprint: NadelOvera service: Service, result: ServiceExecutionResult, ): ServiceExecutionResult { + if (result is NadelIncrementalServiceExecutionResult) { + result.incrementalItemPublisher.asFlow() + .map { + val incremental = it.incremental + ?.map { + + } + DelayedIncrementalPartialResultImpl.newIncrementalExecutionResult() + + .extensions(it.extensions) + .build() + } + } + val nodes = JsonNodes(result.data) - val deferredInstructions = ArrayList>>() + val asyncInstructions = ArrayList>>() coroutineScope { for ((field, steps) in executionPlan.transformationSteps) { @@ -39,7 +57,7 @@ internal class NadelResultTransformer(private val executionBlueprint: NadelOvera } for (step in steps) { - deferredInstructions.add( + asyncInstructions.add( async { step.transform.getResultInstructions( executionContext, @@ -57,14 +75,14 @@ internal class NadelResultTransformer(private val executionBlueprint: NadelOvera } } - deferredInstructions.add( + asyncInstructions.add( async { getRemoveArtificialFieldInstructions(artificialFields, nodes) }, ) } - val instructions = deferredInstructions + val instructions = asyncInstructions .awaitAll() .flatten() diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredFieldIsRenamedTestSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredFieldIsRenamedTestSnapshot.kt index 25fd92375..3159f4047 100644 --- a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredFieldIsRenamedTestSnapshot.kt +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredFieldIsRenamedTestSnapshot.kt @@ -27,8 +27,10 @@ public class DeferredFieldIsRenamedTestSnapshot : TestSnapshot() { | { | defer { | hello - | rename__overallString__underlyingString: underlyingString - | __typename__rename__overallString: __typename + | ... @defer { + | rename__overallString__underlyingString: underlyingString + | __typename__rename__overallString: __typename + | } | } | } """.trimMargin(), @@ -37,14 +39,29 @@ public class DeferredFieldIsRenamedTestSnapshot : TestSnapshot() { | { | "data": { | "defer": { - | "hello": "hello there", - | "rename__overallString__underlyingString": "string for the deferred renamed field", - | "__typename__rename__overallString": "DeferApi" + | "hello": "hello there" | } - | } + | }, + | "hasNext": true | } """.trimMargin(), delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [ + | "defer" + | ], + | "data": { + | "rename__overallString__underlyingString": "string for the deferred renamed field", + | "__typename__rename__overallString": "DeferApi" + | } + | } + | ] + | } + """.trimMargin(), ), ), ) @@ -54,8 +71,7 @@ public class DeferredFieldIsRenamedTestSnapshot : TestSnapshot() { * { * "data": { * "defer": { - * "hello": "hello there", - * "overallString": "string for the deferred renamed field" + * "hello": "hello there" * } * } * } @@ -66,10 +82,10 @@ public class DeferredFieldIsRenamedTestSnapshot : TestSnapshot() { | { | "data": { | "defer": { - | "hello": "hello there", - | "overallString": "string for the deferred renamed field" + | "hello": "hello there" | } - | } + | }, + | "hasNext": true | } """.trimMargin(), delayedResults = listOfJsonStrings( diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenameTransformsInsideAndOutsideDeferSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenameTransformsInsideAndOutsideDeferSnapshot.kt index e85b6ce6f..f28e8a5f5 100644 --- a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenameTransformsInsideAndOutsideDeferSnapshot.kt +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenameTransformsInsideAndOutsideDeferSnapshot.kt @@ -28,8 +28,10 @@ public class MultipleRenameTransformsInsideAndOutsideDeferSnapshot : TestSnapsho | defer { | rename__fastRenamedString__fastString: fastString | __typename__rename__fastRenamedString: __typename - | rename__slowRenamedString__slowString: slowString - | __typename__rename__slowRenamedString: __typename + | ... @defer { + | rename__slowRenamedString__slowString: slowString + | __typename__rename__slowRenamedString: __typename + | } | } | } """.trimMargin(), @@ -39,14 +41,29 @@ public class MultipleRenameTransformsInsideAndOutsideDeferSnapshot : TestSnapsho | "data": { | "defer": { | "rename__fastRenamedString__fastString": "this is the fast string (not deferred)", - | "__typename__rename__fastRenamedString": "DeferApi", - | "rename__slowRenamedString__slowString": "this is the slow string (deferred)", - | "__typename__rename__slowRenamedString": "DeferApi" + | "__typename__rename__fastRenamedString": "DeferApi" | } - | } + | }, + | "hasNext": true | } """.trimMargin(), delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [ + | "defer" + | ], + | "data": { + | "rename__slowRenamedString__slowString": "this is the slow string (deferred)", + | "__typename__rename__slowRenamedString": "DeferApi" + | } + | } + | ] + | } + """.trimMargin(), ), ), ) @@ -56,8 +73,7 @@ public class MultipleRenameTransformsInsideAndOutsideDeferSnapshot : TestSnapsho * { * "data": { * "defer": { - * "fastRenamedString": "this is the fast string (not deferred)", - * "slowRenamedString": "this is the slow string (deferred)" + * "fastRenamedString": "this is the fast string (not deferred)" * } * } * } @@ -68,10 +84,10 @@ public class MultipleRenameTransformsInsideAndOutsideDeferSnapshot : TestSnapsho | { | "data": { | "defer": { - | "fastRenamedString": "this is the fast string (not deferred)", - | "slowRenamedString": "this is the slow string (deferred)" + | "fastRenamedString": "this is the fast string (not deferred)" | } - | } + | }, + | "hasNext": true | } """.trimMargin(), delayedResults = listOfJsonStrings( diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenamedFieldsAreDeferredTogetherSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenamedFieldsAreDeferredTogetherSnapshot.kt index d66fc0df9..7f8352d46 100644 --- a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenamedFieldsAreDeferredTogetherSnapshot.kt +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenamedFieldsAreDeferredTogetherSnapshot.kt @@ -27,10 +27,12 @@ public class MultipleRenamedFieldsAreDeferredTogetherSnapshot : TestSnapshot() { | { | defer { | hello - | rename__overallString__underlyingString: underlyingString - | __typename__rename__overallString: __typename - | rename__overallString2__underlyingString2: underlyingString2 - | __typename__rename__overallString2: __typename + | ... @defer { + | rename__overallString__underlyingString: underlyingString + | __typename__rename__overallString: __typename + | rename__overallString2__underlyingString2: underlyingString2 + | __typename__rename__overallString2: __typename + | } | } | } """.trimMargin(), @@ -39,16 +41,31 @@ public class MultipleRenamedFieldsAreDeferredTogetherSnapshot : TestSnapshot() { | { | "data": { | "defer": { - | "hello": "hello there", - | "rename__overallString__underlyingString": "deferred string 1", - | "__typename__rename__overallString": "DeferApi", - | "rename__overallString2__underlyingString2": "deferred string 2", - | "__typename__rename__overallString2": "DeferApi" + | "hello": "hello there" | } - | } + | }, + | "hasNext": true | } """.trimMargin(), delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [ + | "defer" + | ], + | "data": { + | "__typename__rename__overallString2": "DeferApi", + | "rename__overallString2__underlyingString2": "deferred string 2", + | "rename__overallString__underlyingString": "deferred string 1", + | "__typename__rename__overallString": "DeferApi" + | } + | } + | ] + | } + """.trimMargin(), ), ), ) @@ -58,9 +75,7 @@ public class MultipleRenamedFieldsAreDeferredTogetherSnapshot : TestSnapshot() { * { * "data": { * "defer": { - * "hello": "hello there", - * "overallString": "deferred string 1", - * "overallString2": "deferred string 2" + * "hello": "hello there" * } * } * } @@ -71,11 +86,10 @@ public class MultipleRenamedFieldsAreDeferredTogetherSnapshot : TestSnapshot() { | { | "data": { | "defer": { - | "hello": "hello there", - | "overallString": "deferred string 1", - | "overallString2": "deferred string 2" + | "hello": "hello there" | } - | } + | }, + | "hasNext": true | } """.trimMargin(), delayedResults = listOfJsonStrings( diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleSeparateDefersWithTransformsSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleSeparateDefersWithTransformsSnapshot.kt index ecc2c93da..bd010bc70 100644 --- a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleSeparateDefersWithTransformsSnapshot.kt +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleSeparateDefersWithTransformsSnapshot.kt @@ -28,10 +28,14 @@ public class MultipleSeparateDefersWithTransformsSnapshot : TestSnapshot() { | defer { | rename__fastRenamedString__fastString: fastString | __typename__rename__fastRenamedString: __typename - | rename__slowRenamedString__slowString: slowString - | __typename__rename__slowRenamedString: __typename - | rename__anotherSlowRenamedString__anotherSlowString: anotherSlowString - | __typename__rename__anotherSlowRenamedString: __typename + | ... @defer { + | rename__slowRenamedString__slowString: slowString + | __typename__rename__slowRenamedString: __typename + | } + | ... @defer { + | rename__anotherSlowRenamedString__anotherSlowString: anotherSlowString + | __typename__rename__anotherSlowRenamedString: __typename + | } | } | } """.trimMargin(), @@ -41,16 +45,45 @@ public class MultipleSeparateDefersWithTransformsSnapshot : TestSnapshot() { | "data": { | "defer": { | "rename__fastRenamedString__fastString": "this is the fast string (not deferred)", - | "__typename__rename__fastRenamedString": "DeferApi", - | "rename__slowRenamedString__slowString": "this is the slow string (deferred)", - | "__typename__rename__slowRenamedString": "DeferApi", - | "rename__anotherSlowRenamedString__anotherSlowString": "this is the other slow string (deferred)", - | "__typename__rename__anotherSlowRenamedString": "DeferApi" + | "__typename__rename__fastRenamedString": "DeferApi" | } - | } + | }, + | "hasNext": true | } """.trimMargin(), delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [ + | "defer" + | ], + | "data": { + | "rename__slowRenamedString__slowString": "this is the slow string (deferred)", + | "__typename__rename__slowRenamedString": "DeferApi" + | } + | } + | ] + | } + """.trimMargin(), + """ + | { + | "hasNext": true, + | "incremental": [ + | { + | "path": [ + | "defer" + | ], + | "data": { + | "rename__anotherSlowRenamedString__anotherSlowString": "this is the other slow string (deferred)", + | "__typename__rename__anotherSlowRenamedString": "DeferApi" + | } + | } + | ] + | } + """.trimMargin(), ), ), ) @@ -60,9 +93,7 @@ public class MultipleSeparateDefersWithTransformsSnapshot : TestSnapshot() { * { * "data": { * "defer": { - * "fastRenamedString": "this is the fast string (not deferred)", - * "slowRenamedString": "this is the slow string (deferred)", - * "anotherSlowRenamedString": "this is the other slow string (deferred)" + * "fastRenamedString": "this is the fast string (not deferred)" * } * } * } @@ -73,11 +104,10 @@ public class MultipleSeparateDefersWithTransformsSnapshot : TestSnapshot() { | { | "data": { | "defer": { - | "fastRenamedString": "this is the fast string (not deferred)", - | "slowRenamedString": "this is the slow string (deferred)", - | "anotherSlowRenamedString": "this is the other slow string (deferred)" + | "fastRenamedString": "this is the fast string (not deferred)" | } - | } + | }, + | "hasNext": true | } """.trimMargin(), delayedResults = listOfJsonStrings( From 5a8e5056eef745bdcf8dba031ba77b64ceb5581c Mon Sep 17 00:00:00 2001 From: sbarker2 <109047597+sbarker2@users.noreply.github.com> Date: Tue, 20 Aug 2024 09:28:32 +1200 Subject: [PATCH 04/24] get transforms actually working for deferred payloads (#578) --- .../main/java/graphql/nadel/NextgenEngine.kt | 2 +- .../engine/transform/query/NadelQueryPath.kt | 11 +++ .../result/NadelResultTransformer.kt | 76 +++++++++++++++++++ .../engine/transform/result/json/JsonNodes.kt | 27 +++++++ .../DeferredFieldIsRenamedTestSnapshot.kt | 21 ++++- ...TransformsInsideAndOutsideDeferSnapshot.kt | 21 ++++- ...enamedFieldsAreDeferredTogetherSnapshot.kt | 26 +++++-- ...pleSeparateDefersWithTransformsSnapshot.kt | 40 ++++++++-- .../RenameInnerFieldInsideDeferSnapshot.kt | 9 +-- 9 files changed, 210 insertions(+), 23 deletions(-) diff --git a/lib/src/main/java/graphql/nadel/NextgenEngine.kt b/lib/src/main/java/graphql/nadel/NextgenEngine.kt index 89ec9cb03..ffcfa29d8 100644 --- a/lib/src/main/java/graphql/nadel/NextgenEngine.kt +++ b/lib/src/main/java/graphql/nadel/NextgenEngine.kt @@ -403,7 +403,7 @@ internal class NextgenEngine( overallToUnderlyingFields = overallToUnderlyingFields, service = service, result = serviceExecResult, //NO LONGER USED - // dipr = delayedIncrementalResult + delayedIncrementalPartialResult = delayedIncrementalResult, ) delayedIncrementalResult } diff --git a/lib/src/main/java/graphql/nadel/engine/transform/query/NadelQueryPath.kt b/lib/src/main/java/graphql/nadel/engine/transform/query/NadelQueryPath.kt index 7843415d7..97af4ba6e 100644 --- a/lib/src/main/java/graphql/nadel/engine/transform/query/NadelQueryPath.kt +++ b/lib/src/main/java/graphql/nadel/engine/transform/query/NadelQueryPath.kt @@ -23,6 +23,17 @@ data class NadelQueryPath(val segments: List) { return segments.last() } + fun startsWith(prefix: List): Boolean { + return segments.take(prefix.size) == prefix + } + + fun removePrefix(prefix: List): NadelQueryPath { + if (this.startsWith(prefix)) { + return NadelQueryPath(segments.drop(prefix.size)) + } + throw Error("NadelQueryPath did not start with prefix") //todo: make proper error + } + companion object { val root = NadelQueryPath(emptyList()) diff --git a/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt b/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt index 011c5dab3..ba0e35a7d 100644 --- a/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt +++ b/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt @@ -1,5 +1,7 @@ package graphql.nadel.engine.transform.result +import graphql.incremental.DeferPayload +import graphql.incremental.DelayedIncrementalPartialResult import graphql.incremental.DelayedIncrementalPartialResultImpl import graphql.nadel.NadelIncrementalServiceExecutionResult import graphql.nadel.Service @@ -9,6 +11,7 @@ import graphql.nadel.engine.NadelServiceExecutionContext import graphql.nadel.engine.blueprint.NadelOverallExecutionBlueprint import graphql.nadel.engine.plan.NadelExecutionPlan import graphql.nadel.engine.transform.result.json.JsonNodes +import graphql.nadel.engine.transform.result.json.NadelCachingJsonNodes import graphql.nadel.engine.util.JsonMap import graphql.nadel.engine.util.MutableJsonMap import graphql.nadel.engine.util.queryPath @@ -91,6 +94,69 @@ internal class NadelResultTransformer(private val executionBlueprint: NadelOvera return result } + suspend fun transform( + executionContext: NadelExecutionContext, + serviceExecutionContext: NadelServiceExecutionContext, + executionPlan: NadelExecutionPlan, + artificialFields: List, + overallToUnderlyingFields: Map>, + service: Service, + result: NadelIncrementalServiceExecutionResult, + delayedIncrementalPartialResult: DelayedIncrementalPartialResult // NadelIncrementalServiceExecutionResult, + ): DelayedIncrementalPartialResult { + val asyncInstructions = ArrayList>>() + + coroutineScope { + delayedIncrementalPartialResult.incremental + ?.filterIsInstance() // We need this filter because IncrementalPayloads could be stream or defer + ?.map { deferPayload -> + val nodes = NadelCachingJsonNodes( + deferPayload.getData() ?: emptyMap(), + prefix = deferPayload.path.filterIsInstance(), //converts resultPath to queryPath TODO: is it better for this to be NadelQueryPath? as that gives us better type safety + ) + + for ((field, steps) in executionPlan.transformationSteps) { + // This can be null if we did not end up sending the field e.g. for hydration + val underlyingFields = overallToUnderlyingFields[field] + if (underlyingFields.isNullOrEmpty()) { + continue + } + + for (step in steps) { + asyncInstructions.add( + async { + step.transform.getResultInstructions( + executionContext, + serviceExecutionContext, + executionBlueprint, + service, + field, + underlyingFields.first().parent, + result, + step.state, + nodes, + ) + }, + ) + } + + asyncInstructions.add( + async { + getRemoveArtificialFieldInstructions(artificialFields, nodes) + }, + ) + } + } + } + val instructions = asyncInstructions + .awaitAll() + .flatten() + + mutate(delayedIncrementalPartialResult, instructions) + + return delayedIncrementalPartialResult + } + private fun mutate(result: ServiceExecutionResult, instructions: List) { instructions.forEach { transformation -> when (transformation) { @@ -101,6 +167,16 @@ internal class NadelResultTransformer(private val executionBlueprint: NadelOvera } } + private fun mutate(result: DelayedIncrementalPartialResult, instructions: List) { + instructions.forEach { transformation -> + when (transformation) { + is NadelResultInstruction.Set -> process(transformation) + is NadelResultInstruction.Remove -> process(transformation) + is NadelResultInstruction.AddError -> process(transformation, emptyList()) // result.incremental?.first()?.errors) TODO: add errors properly on this line + } + } + } + private fun process( instruction: NadelResultInstruction.Set, ) { diff --git a/lib/src/main/java/graphql/nadel/engine/transform/result/json/JsonNodes.kt b/lib/src/main/java/graphql/nadel/engine/transform/result/json/JsonNodes.kt index b801c2461..da4442d14 100644 --- a/lib/src/main/java/graphql/nadel/engine/transform/result/json/JsonNodes.kt +++ b/lib/src/main/java/graphql/nadel/engine/transform/result/json/JsonNodes.kt @@ -34,8 +34,10 @@ interface JsonNodes { */ class NadelCachingJsonNodes( private val data: JsonMap, + private val prefix: List? = null, ) : JsonNodes { private val nodes = ConcurrentHashMap>() + private var removedPrefix: Boolean = false override fun getNodesAt(queryPath: NadelQueryPath, flatten: Boolean): List { val rootNode = JsonNode(data) @@ -46,6 +48,22 @@ class NadelCachingJsonNodes( * Extracts the nodes at the given query selection path. */ private fun getNodesAt(rootNode: JsonNode, queryPath: NadelQueryPath, flatten: Boolean = false): List { + // if incremental payload, remove prefix + if (prefix != null && !removedPrefix) { + if (queryPath.startsWith(prefix.toList())) { + removedPrefix = true + getNodesAt(queryPath.removePrefix(prefix), flatten) + } else { + emptyList() + } + } + removedPrefix = false + + // TODO: check if this is valid + // if (prefix != null && queryPath.startsWith(prefix.toList())) { + // getNodesAt(queryPath.removePrefix(prefix), flatten) + // } + var queue = listOf(rootNode) // todo work backwards here instead of forwards @@ -85,6 +103,15 @@ class NadelCachingJsonNodes( segment: String, flattenLists: Boolean, ): Sequence { + // if incremental and last item of prefix matches the segment, + // then return the map itself (instead of the value in the map) + // TODO: does this need the flattenLists logic? + if (prefix != null && prefix.last() == segment){ + return sequenceOf( + JsonNode(map), + ) + } + val value = map[segment] // We flatten lists as these nodes contribute to the BFS queue diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredFieldIsRenamedTestSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredFieldIsRenamedTestSnapshot.kt index 3159f4047..3f8fc8269 100644 --- a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredFieldIsRenamedTestSnapshot.kt +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredFieldIsRenamedTestSnapshot.kt @@ -55,8 +55,7 @@ public class DeferredFieldIsRenamedTestSnapshot : TestSnapshot() { | "defer" | ], | "data": { - | "rename__overallString__underlyingString": "string for the deferred renamed field", - | "__typename__rename__overallString": "DeferApi" + | "overallString": "string for the deferred renamed field" | } | } | ] @@ -71,7 +70,8 @@ public class DeferredFieldIsRenamedTestSnapshot : TestSnapshot() { * { * "data": { * "defer": { - * "hello": "hello there" + * "hello": "hello there", + * "overallString": "string for the deferred renamed field" * } * } * } @@ -89,6 +89,21 @@ public class DeferredFieldIsRenamedTestSnapshot : TestSnapshot() { | } """.trimMargin(), delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [ + | "defer" + | ], + | "data": { + | "overallString": "string for the deferred renamed field" + | } + | } + | ] + | } + """.trimMargin(), ), ) } diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenameTransformsInsideAndOutsideDeferSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenameTransformsInsideAndOutsideDeferSnapshot.kt index f28e8a5f5..0367d8d9a 100644 --- a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenameTransformsInsideAndOutsideDeferSnapshot.kt +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenameTransformsInsideAndOutsideDeferSnapshot.kt @@ -57,8 +57,7 @@ public class MultipleRenameTransformsInsideAndOutsideDeferSnapshot : TestSnapsho | "defer" | ], | "data": { - | "rename__slowRenamedString__slowString": "this is the slow string (deferred)", - | "__typename__rename__slowRenamedString": "DeferApi" + | "slowRenamedString": "this is the slow string (deferred)" | } | } | ] @@ -73,7 +72,8 @@ public class MultipleRenameTransformsInsideAndOutsideDeferSnapshot : TestSnapsho * { * "data": { * "defer": { - * "fastRenamedString": "this is the fast string (not deferred)" + * "fastRenamedString": "this is the fast string (not deferred)", + * "slowRenamedString": "this is the slow string (deferred)" * } * } * } @@ -91,6 +91,21 @@ public class MultipleRenameTransformsInsideAndOutsideDeferSnapshot : TestSnapsho | } """.trimMargin(), delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [ + | "defer" + | ], + | "data": { + | "slowRenamedString": "this is the slow string (deferred)" + | } + | } + | ] + | } + """.trimMargin(), ), ) } diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenamedFieldsAreDeferredTogetherSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenamedFieldsAreDeferredTogetherSnapshot.kt index 7f8352d46..91e4c7451 100644 --- a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenamedFieldsAreDeferredTogetherSnapshot.kt +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenamedFieldsAreDeferredTogetherSnapshot.kt @@ -57,10 +57,8 @@ public class MultipleRenamedFieldsAreDeferredTogetherSnapshot : TestSnapshot() { | "defer" | ], | "data": { - | "__typename__rename__overallString2": "DeferApi", - | "rename__overallString2__underlyingString2": "deferred string 2", - | "rename__overallString__underlyingString": "deferred string 1", - | "__typename__rename__overallString": "DeferApi" + | "overallString": "deferred string 1", + | "overallString2": "deferred string 2" | } | } | ] @@ -75,7 +73,9 @@ public class MultipleRenamedFieldsAreDeferredTogetherSnapshot : TestSnapshot() { * { * "data": { * "defer": { - * "hello": "hello there" + * "hello": "hello there", + * "overallString": "deferred string 1", + * "overallString2": "deferred string 2" * } * } * } @@ -93,6 +93,22 @@ public class MultipleRenamedFieldsAreDeferredTogetherSnapshot : TestSnapshot() { | } """.trimMargin(), delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [ + | "defer" + | ], + | "data": { + | "overallString": "deferred string 1", + | "overallString2": "deferred string 2" + | } + | } + | ] + | } + """.trimMargin(), ), ) } diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleSeparateDefersWithTransformsSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleSeparateDefersWithTransformsSnapshot.kt index bd010bc70..de6be970b 100644 --- a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleSeparateDefersWithTransformsSnapshot.kt +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleSeparateDefersWithTransformsSnapshot.kt @@ -61,8 +61,7 @@ public class MultipleSeparateDefersWithTransformsSnapshot : TestSnapshot() { | "defer" | ], | "data": { - | "rename__slowRenamedString__slowString": "this is the slow string (deferred)", - | "__typename__rename__slowRenamedString": "DeferApi" + | "slowRenamedString": "this is the slow string (deferred)" | } | } | ] @@ -77,8 +76,7 @@ public class MultipleSeparateDefersWithTransformsSnapshot : TestSnapshot() { | "defer" | ], | "data": { - | "rename__anotherSlowRenamedString__anotherSlowString": "this is the other slow string (deferred)", - | "__typename__rename__anotherSlowRenamedString": "DeferApi" + | "anotherSlowRenamedString": "this is the other slow string (deferred)" | } | } | ] @@ -93,7 +91,9 @@ public class MultipleSeparateDefersWithTransformsSnapshot : TestSnapshot() { * { * "data": { * "defer": { - * "fastRenamedString": "this is the fast string (not deferred)" + * "fastRenamedString": "this is the fast string (not deferred)", + * "anotherSlowRenamedString": "this is the other slow string (deferred)", + * "slowRenamedString": "this is the slow string (deferred)" * } * } * } @@ -111,6 +111,36 @@ public class MultipleSeparateDefersWithTransformsSnapshot : TestSnapshot() { | } """.trimMargin(), delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [ + | "defer" + | ], + | "data": { + | "slowRenamedString": "this is the slow string (deferred)" + | } + | } + | ] + | } + """.trimMargin(), + """ + | { + | "hasNext": true, + | "incremental": [ + | { + | "path": [ + | "defer" + | ], + | "data": { + | "anotherSlowRenamedString": "this is the other slow string (deferred)" + | } + | } + | ] + | } + """.trimMargin(), ), ) } diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/RenameInnerFieldInsideDeferSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/RenameInnerFieldInsideDeferSnapshot.kt index 67893ce4d..99889e1ba 100644 --- a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/RenameInnerFieldInsideDeferSnapshot.kt +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/RenameInnerFieldInsideDeferSnapshot.kt @@ -55,8 +55,7 @@ public class RenameInnerFieldInsideDeferSnapshot : TestSnapshot() { | ], | "data": { | "user": { - | "rename__firstName__name": "Steven", - | "__typename__rename__firstName": "User" + | "firstName": "Steven" | } | } | } @@ -73,8 +72,7 @@ public class RenameInnerFieldInsideDeferSnapshot : TestSnapshot() { * "data": { * "defer": { * "user": { - * "rename__firstName__name": "Steven", - * "__typename__rename__firstName": "User" + * "firstName": "Steven" * } * } * } @@ -101,8 +99,7 @@ public class RenameInnerFieldInsideDeferSnapshot : TestSnapshot() { | ], | "data": { | "user": { - | "rename__firstName__name": "Steven", - | "__typename__rename__firstName": "User" + | "firstName": "Steven" | } | } | } From 7ea4f4c51fc05db3525008d249a62ed7bd21943d Mon Sep 17 00:00:00 2001 From: Steven Barker Date: Tue, 20 Aug 2024 14:54:37 +1200 Subject: [PATCH 05/24] improve JsonNodes class --- .../result/NadelResultTransformer.kt | 2 +- .../engine/transform/result/json/JsonNodes.kt | 36 +++++-------------- 2 files changed, 10 insertions(+), 28 deletions(-) diff --git a/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt b/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt index ba0e35a7d..a33d623ca 100644 --- a/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt +++ b/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt @@ -112,7 +112,7 @@ internal class NadelResultTransformer(private val executionBlueprint: NadelOvera ?.map { deferPayload -> val nodes = NadelCachingJsonNodes( deferPayload.getData() ?: emptyMap(), - prefix = deferPayload.path.filterIsInstance(), //converts resultPath to queryPath TODO: is it better for this to be NadelQueryPath? as that gives us better type safety + pathPrefix = deferPayload.path.filterIsInstance(), //converts resultPath to queryPath TODO: is it better for this to be NadelQueryPath? as that gives us better type safety ) for ((field, steps) in executionPlan.transformationSteps) { diff --git a/lib/src/main/java/graphql/nadel/engine/transform/result/json/JsonNodes.kt b/lib/src/main/java/graphql/nadel/engine/transform/result/json/JsonNodes.kt index da4442d14..71f858e01 100644 --- a/lib/src/main/java/graphql/nadel/engine/transform/result/json/JsonNodes.kt +++ b/lib/src/main/java/graphql/nadel/engine/transform/result/json/JsonNodes.kt @@ -34,35 +34,26 @@ interface JsonNodes { */ class NadelCachingJsonNodes( private val data: JsonMap, - private val prefix: List? = null, + private val pathPrefix: List? = null, // for incremental (defer) payloads, we pass in the prefix we need to remove from path ) : JsonNodes { private val nodes = ConcurrentHashMap>() - private var removedPrefix: Boolean = false override fun getNodesAt(queryPath: NadelQueryPath, flatten: Boolean): List { val rootNode = JsonNode(data) - return getNodesAt(rootNode, queryPath, flatten) + + return if (pathPrefix == null) { + getNodesAt(rootNode, queryPath, flatten) + } else if (queryPath.startsWith(pathPrefix)) { + getNodesAt(rootNode, queryPath.removePrefix(pathPrefix), flatten) + } else { + emptyList() + } } /** * Extracts the nodes at the given query selection path. */ private fun getNodesAt(rootNode: JsonNode, queryPath: NadelQueryPath, flatten: Boolean = false): List { - // if incremental payload, remove prefix - if (prefix != null && !removedPrefix) { - if (queryPath.startsWith(prefix.toList())) { - removedPrefix = true - getNodesAt(queryPath.removePrefix(prefix), flatten) - } else { - emptyList() - } - } - removedPrefix = false - - // TODO: check if this is valid - // if (prefix != null && queryPath.startsWith(prefix.toList())) { - // getNodesAt(queryPath.removePrefix(prefix), flatten) - // } var queue = listOf(rootNode) @@ -103,15 +94,6 @@ class NadelCachingJsonNodes( segment: String, flattenLists: Boolean, ): Sequence { - // if incremental and last item of prefix matches the segment, - // then return the map itself (instead of the value in the map) - // TODO: does this need the flattenLists logic? - if (prefix != null && prefix.last() == segment){ - return sequenceOf( - JsonNode(map), - ) - } - val value = map[segment] // We flatten lists as these nodes contribute to the BFS queue From d64d395eb5bab08d4f56fd50f9b0743e724b266a Mon Sep 17 00:00:00 2001 From: Steven Barker Date: Mon, 26 Aug 2024 11:07:06 +1200 Subject: [PATCH 06/24] passing in NadelQueryPath instead of List to allow better type safety --- lib/src/main/java/graphql/nadel/NextgenEngine.kt | 2 +- .../nadel/engine/transform/result/NadelResultTransformer.kt | 3 ++- .../graphql/nadel/engine/transform/result/json/JsonNodes.kt | 6 +++--- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/src/main/java/graphql/nadel/NextgenEngine.kt b/lib/src/main/java/graphql/nadel/NextgenEngine.kt index ffcfa29d8..b9d10b83f 100644 --- a/lib/src/main/java/graphql/nadel/NextgenEngine.kt +++ b/lib/src/main/java/graphql/nadel/NextgenEngine.kt @@ -402,7 +402,7 @@ internal class NextgenEngine( artificialFields = artificialFields, overallToUnderlyingFields = overallToUnderlyingFields, service = service, - result = serviceExecResult, //NO LONGER USED + result = serviceExecResult, delayedIncrementalPartialResult = delayedIncrementalResult, ) delayedIncrementalResult diff --git a/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt b/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt index a33d623ca..714830b77 100644 --- a/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt +++ b/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt @@ -10,6 +10,7 @@ import graphql.nadel.engine.NadelExecutionContext import graphql.nadel.engine.NadelServiceExecutionContext import graphql.nadel.engine.blueprint.NadelOverallExecutionBlueprint import graphql.nadel.engine.plan.NadelExecutionPlan +import graphql.nadel.engine.transform.query.NadelQueryPath import graphql.nadel.engine.transform.result.json.JsonNodes import graphql.nadel.engine.transform.result.json.NadelCachingJsonNodes import graphql.nadel.engine.util.JsonMap @@ -112,7 +113,7 @@ internal class NadelResultTransformer(private val executionBlueprint: NadelOvera ?.map { deferPayload -> val nodes = NadelCachingJsonNodes( deferPayload.getData() ?: emptyMap(), - pathPrefix = deferPayload.path.filterIsInstance(), //converts resultPath to queryPath TODO: is it better for this to be NadelQueryPath? as that gives us better type safety + pathPrefix = NadelQueryPath(deferPayload.path.filterIsInstance()), ) for ((field, steps) in executionPlan.transformationSteps) { diff --git a/lib/src/main/java/graphql/nadel/engine/transform/result/json/JsonNodes.kt b/lib/src/main/java/graphql/nadel/engine/transform/result/json/JsonNodes.kt index 71f858e01..c80d5e2ef 100644 --- a/lib/src/main/java/graphql/nadel/engine/transform/result/json/JsonNodes.kt +++ b/lib/src/main/java/graphql/nadel/engine/transform/result/json/JsonNodes.kt @@ -34,7 +34,7 @@ interface JsonNodes { */ class NadelCachingJsonNodes( private val data: JsonMap, - private val pathPrefix: List? = null, // for incremental (defer) payloads, we pass in the prefix we need to remove from path + private val pathPrefix: NadelQueryPath? = null, // for incremental (defer) payloads, we pass in the prefix we need to remove from path ) : JsonNodes { private val nodes = ConcurrentHashMap>() @@ -43,8 +43,8 @@ class NadelCachingJsonNodes( return if (pathPrefix == null) { getNodesAt(rootNode, queryPath, flatten) - } else if (queryPath.startsWith(pathPrefix)) { - getNodesAt(rootNode, queryPath.removePrefix(pathPrefix), flatten) + } else if (queryPath.startsWith(pathPrefix.segments)) { + getNodesAt(rootNode, queryPath.removePrefix(pathPrefix.segments), flatten) } else { emptyList() } From 8d98b98822b729a4fb631e516d8411e72b50f34c Mon Sep 17 00:00:00 2001 From: Steven Barker Date: Mon, 26 Aug 2024 11:50:31 +1200 Subject: [PATCH 07/24] remove unused if block --- .../transform/result/NadelResultTransformer.kt | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt b/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt index 714830b77..8bc87bc65 100644 --- a/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt +++ b/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt @@ -34,20 +34,6 @@ internal class NadelResultTransformer(private val executionBlueprint: NadelOvera service: Service, result: ServiceExecutionResult, ): ServiceExecutionResult { - if (result is NadelIncrementalServiceExecutionResult) { - result.incrementalItemPublisher.asFlow() - .map { - val incremental = it.incremental - ?.map { - - } - DelayedIncrementalPartialResultImpl.newIncrementalExecutionResult() - - .extensions(it.extensions) - .build() - } - } - val nodes = JsonNodes(result.data) val asyncInstructions = ArrayList>>() From 876b302f94c4feffac19bf58fdf2a9ef8510cdaf Mon Sep 17 00:00:00 2001 From: Steven Barker Date: Mon, 26 Aug 2024 13:47:11 +1200 Subject: [PATCH 08/24] add ability to log errors for defer transforms --- .../main/java/graphql/nadel/NextgenEngine.kt | 27 +++--- .../result/NadelResultTransformer.kt | 88 ++++++++++--------- 2 files changed, 61 insertions(+), 54 deletions(-) diff --git a/lib/src/main/java/graphql/nadel/NextgenEngine.kt b/lib/src/main/java/graphql/nadel/NextgenEngine.kt index b9d10b83f..9d608f508 100644 --- a/lib/src/main/java/graphql/nadel/NextgenEngine.kt +++ b/lib/src/main/java/graphql/nadel/NextgenEngine.kt @@ -6,6 +6,7 @@ import graphql.ExecutionResult import graphql.GraphQLError import graphql.execution.ExecutionIdProvider import graphql.execution.instrumentation.InstrumentationState +import graphql.incremental.DeferPayload import graphql.incremental.IncrementalExecutionResultImpl import graphql.introspection.Introspection.TypeNameMetaFieldDef import graphql.language.Document @@ -394,17 +395,21 @@ internal class NextgenEngine( .asFlow() .map {delayedIncrementalResult -> // Transform - resultTransformer - .transform( - executionContext = executionContext, - serviceExecutionContext = serviceExecutionContext, - executionPlan = executionPlan, - artificialFields = artificialFields, - overallToUnderlyingFields = overallToUnderlyingFields, - service = service, - result = serviceExecResult, - delayedIncrementalPartialResult = delayedIncrementalResult, - ) + delayedIncrementalResult.incremental + ?.filterIsInstance() + ?.map {deferPayload -> + resultTransformer + .transform( + executionContext = executionContext, + serviceExecutionContext = serviceExecutionContext, + executionPlan = executionPlan, + artificialFields = artificialFields, + overallToUnderlyingFields = overallToUnderlyingFields, + service = service, + result = serviceExecResult, + deferPayload = deferPayload, + ) } + delayedIncrementalResult } ) diff --git a/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt b/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt index 8bc87bc65..c904a8121 100644 --- a/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt +++ b/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt @@ -3,6 +3,7 @@ package graphql.nadel.engine.transform.result import graphql.incremental.DeferPayload import graphql.incremental.DelayedIncrementalPartialResult import graphql.incremental.DelayedIncrementalPartialResultImpl +import graphql.incremental.IncrementalPayload import graphql.nadel.NadelIncrementalServiceExecutionResult import graphql.nadel.Service import graphql.nadel.ServiceExecutionResult @@ -89,59 +90,55 @@ internal class NadelResultTransformer(private val executionBlueprint: NadelOvera overallToUnderlyingFields: Map>, service: Service, result: NadelIncrementalServiceExecutionResult, - delayedIncrementalPartialResult: DelayedIncrementalPartialResult // NadelIncrementalServiceExecutionResult, - ): DelayedIncrementalPartialResult { + deferPayload: DeferPayload + ): DeferPayload { val asyncInstructions = ArrayList>>() coroutineScope { - delayedIncrementalPartialResult.incremental - ?.filterIsInstance() // We need this filter because IncrementalPayloads could be stream or defer - ?.map { deferPayload -> - val nodes = NadelCachingJsonNodes( - deferPayload.getData() ?: emptyMap(), - pathPrefix = NadelQueryPath(deferPayload.path.filterIsInstance()), - ) + val nodes = NadelCachingJsonNodes( + deferPayload.getData() ?: emptyMap(), + pathPrefix = NadelQueryPath(deferPayload.path.filterIsInstance()), + ) - for ((field, steps) in executionPlan.transformationSteps) { - // This can be null if we did not end up sending the field e.g. for hydration - val underlyingFields = overallToUnderlyingFields[field] - if (underlyingFields.isNullOrEmpty()) { - continue - } - - for (step in steps) { - asyncInstructions.add( - async { - step.transform.getResultInstructions( - executionContext, - serviceExecutionContext, - executionBlueprint, - service, - field, - underlyingFields.first().parent, - result, - step.state, - nodes, - ) - }, - ) - } + for ((field, steps) in executionPlan.transformationSteps) { + // This can be null if we did not end up sending the field e.g. for hydration + val underlyingFields = overallToUnderlyingFields[field] + if (underlyingFields.isNullOrEmpty()) { + continue + } - asyncInstructions.add( - async { - getRemoveArtificialFieldInstructions(artificialFields, nodes) - }, - ) - } + for (step in steps) { + asyncInstructions.add( + async { + step.transform.getResultInstructions( + executionContext, + serviceExecutionContext, + executionBlueprint, + service, + field, + underlyingFields.first().parent, + result, + step.state, + nodes, + ) + }, + ) } + + asyncInstructions.add( + async { + getRemoveArtificialFieldInstructions(artificialFields, nodes) + }, + ) + } } val instructions = asyncInstructions .awaitAll() .flatten() - mutate(delayedIncrementalPartialResult, instructions) + mutate(deferPayload, instructions) - return delayedIncrementalPartialResult + return deferPayload } private fun mutate(result: ServiceExecutionResult, instructions: List) { @@ -154,12 +151,17 @@ internal class NadelResultTransformer(private val executionBlueprint: NadelOvera } } - private fun mutate(result: DelayedIncrementalPartialResult, instructions: List) { + private fun mutate(result: DeferPayload, instructions: List) { instructions.forEach { transformation -> when (transformation) { is NadelResultInstruction.Set -> process(transformation) is NadelResultInstruction.Remove -> process(transformation) - is NadelResultInstruction.AddError -> process(transformation, emptyList()) // result.incremental?.first()?.errors) TODO: add errors properly on this line + is NadelResultInstruction.AddError -> process( + transformation, + result.errors?.map { graphQLError -> + mapOf("message" to graphQLError.message, "locations" to graphQLError.locations) + } ?: emptyList() + ) } } } From 76cfa2151207841a9ee3eeb13c799c65eb0f4758 Mon Sep 17 00:00:00 2001 From: Steven Barker Date: Tue, 27 Aug 2024 11:01:02 +1200 Subject: [PATCH 09/24] add extra defer tests for fields in lists and fields in hydrations --- .../DeferredListFieldIsRenamedTest.kt | 102 +++++++++ .../DeferredListFieldIsRenamedTestSnapshot.kt | 160 ++++++++++++++ .../DeferredfieldInsideListIsRenamed.kt | 1 + .../defer/HydrationDeferInRenamedField.kt | 36 ++++ ...ydrationRenamedFieldInDeferTestSnapshot.kt | 133 ++++++++++++ ...NestedHydrationsInsideDeferTestSnapshot.kt | 203 ++++++++++++++++++ 6 files changed, 635 insertions(+) create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredListFieldIsRenamedTest.kt create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredListFieldIsRenamedTestSnapshot.kt create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredfieldInsideListIsRenamed.kt create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/hydration/defer/HydrationRenamedFieldInDeferTestSnapshot.kt create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/hydration/defer/RenamedFieldInsideNestedHydrationsInsideDeferTestSnapshot.kt diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredListFieldIsRenamedTest.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredListFieldIsRenamedTest.kt new file mode 100644 index 000000000..e715e2aea --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredListFieldIsRenamedTest.kt @@ -0,0 +1,102 @@ +package graphql.nadel.tests.next.fixtures.defer.transforms + +import graphql.nadel.NadelExecutionHints +import graphql.nadel.tests.next.NadelIntegrationTest + +open class DeferredListFieldIsRenamedTest : NadelIntegrationTest( + query = """ + query { + ...@defer { + issues { + key + assigneeId + awesomeIssueName + } + } + } + """.trimIndent(), + services = listOf( + Service( + name = "defer", + overallSchema = """ + directive @defer(if: Boolean, label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT + + type Query { + issues: [Issue!] + } + + type Issue { + key: String! + assigneeId: ID! + awesomeIssueName: String @renamed(from: "title") + related: [Issue!] + parent: Issue + } + """.trimIndent(), + underlyingSchema = """ + directive @defer(if: Boolean, label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT + + type Query { + issues: [Issue!] + } + + type Issue { + key: String! + assigneeId: ID! + title: String + related: [Issue!] + parent: Issue + } + """.trimIndent(), + runtimeWiring = { wiring -> + data class Issue( + val key: String, + val assigneeId: String, + val title: String? = null, + val parentKey: String? = null, + val relatedKeys: List = emptyList(), + ) + val issues = listOf( + Issue( + key = "GQLGW-1", + assigneeId = "ari:cloud:identity::user/1", + title = "Issue 1", + ), + Issue( + key = "GQLGW-2", + assigneeId = "ari:cloud:identity::user/2", + parentKey = "GQLGW-1", + relatedKeys = listOf("GQLGW-1"), + title = "Issue 2", + ), + Issue( + key = "GQLGW-3", + assigneeId = "ari:cloud:identity::user/1", + parentKey = "GQLGW-1", + relatedKeys = listOf("GQLGW-1", "GQLGW-2"), + title = "Issue 3", + ), + Issue( + key = "GQLGW-4", + assigneeId = "ari:cloud:identity::user/3", + parentKey = "GQLGW-1", + relatedKeys = listOf("GQLGW-1", "GQLGW-2", "GQLGW-3"), + // no title here to test rename on null result + ), + ) + wiring + .type("Query") { type -> + type + .dataFetcher("issues") { env -> + issues + } + } + }, + ), + ), +) { + override fun makeExecutionHints(): NadelExecutionHints.Builder { + return super.makeExecutionHints() + .deferSupport { true } + } +} \ No newline at end of file diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredListFieldIsRenamedTestSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredListFieldIsRenamedTestSnapshot.kt new file mode 100644 index 000000000..d1da355ee --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredListFieldIsRenamedTestSnapshot.kt @@ -0,0 +1,160 @@ +// @formatter:off +package graphql.nadel.tests.next.fixtures.defer.transforms + +import graphql.nadel.tests.next.ExpectedNadelResult +import graphql.nadel.tests.next.ExpectedServiceCall +import graphql.nadel.tests.next.TestSnapshot +import graphql.nadel.tests.next.listOfJsonStrings +import kotlin.Suppress +import kotlin.collections.List +import kotlin.collections.listOf + +private suspend fun main() { + graphql.nadel.tests.next.update() +} + +/** + * This class is generated. Do NOT modify. + * + * Refer to [graphql.nadel.tests.next.UpdateTestSnapshots + */ +@Suppress("unused") +public class DeferredListFieldIsRenamedTestSnapshot : TestSnapshot() { + override val calls: List = listOf( + ExpectedServiceCall( + service = "defer", + query = """ + | { + | ... @defer { + | issues { + | key + | assigneeId + | rename__awesomeIssueName__title: title + | __typename__rename__awesomeIssueName: __typename + | } + | } + | } + """.trimMargin(), + variables = "{}", + result = """ + | { + | "data": {}, + | "hasNext": true + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [], + | "data": { + | "issues": [ + | { + | "key": "GQLGW-1", + | "assigneeId": "ari:cloud:identity::user/1", + | "awesomeIssueName": "Issue 1" + | }, + | { + | "key": "GQLGW-2", + | "assigneeId": "ari:cloud:identity::user/2", + | "awesomeIssueName": "Issue 2" + | }, + | { + | "key": "GQLGW-3", + | "assigneeId": "ari:cloud:identity::user/1", + | "awesomeIssueName": "Issue 3" + | }, + | { + | "key": "GQLGW-4", + | "assigneeId": "ari:cloud:identity::user/3", + | "awesomeIssueName": null + | } + | ] + | } + | } + | ] + | } + """.trimMargin(), + ), + ), + ) + + /** + * ```json + * { + * "data": { + * "issues": [ + * { + * "key": "GQLGW-1", + * "assigneeId": "ari:cloud:identity::user/1", + * "awesomeIssueName": "Issue 1" + * }, + * { + * "key": "GQLGW-2", + * "assigneeId": "ari:cloud:identity::user/2", + * "awesomeIssueName": "Issue 2" + * }, + * { + * "key": "GQLGW-3", + * "assigneeId": "ari:cloud:identity::user/1", + * "awesomeIssueName": "Issue 3" + * }, + * { + * "key": "GQLGW-4", + * "assigneeId": "ari:cloud:identity::user/3", + * "awesomeIssueName": null + * } + * ] + * } + * } + * ``` + */ + override val result: ExpectedNadelResult = ExpectedNadelResult( + result = """ + | { + | "data": { + | "issues": null + | }, + | "hasNext": true + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [], + | "data": { + | "issues": [ + | { + | "key": "GQLGW-1", + | "assigneeId": "ari:cloud:identity::user/1", + | "awesomeIssueName": "Issue 1" + | }, + | { + | "key": "GQLGW-2", + | "assigneeId": "ari:cloud:identity::user/2", + | "awesomeIssueName": "Issue 2" + | }, + | { + | "key": "GQLGW-3", + | "assigneeId": "ari:cloud:identity::user/1", + | "awesomeIssueName": "Issue 3" + | }, + | { + | "key": "GQLGW-4", + | "assigneeId": "ari:cloud:identity::user/3", + | "awesomeIssueName": null + | } + | ] + | } + | } + | ] + | } + """.trimMargin(), + ), + ) +} diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredfieldInsideListIsRenamed.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredfieldInsideListIsRenamed.kt new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredfieldInsideListIsRenamed.kt @@ -0,0 +1 @@ + diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/hydration/defer/HydrationDeferInRenamedField.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/hydration/defer/HydrationDeferInRenamedField.kt index 27a2335a0..97f95f60b 100644 --- a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/hydration/defer/HydrationDeferInRenamedField.kt +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/hydration/defer/HydrationDeferInRenamedField.kt @@ -20,6 +20,21 @@ class HydrationDeferInRenamedFieldTest : BaseHydrationDeferInRenamedFieldTest( """.trimIndent(), ) +class HydrationRenamedFieldInDeferTest : BaseHydrationDeferInRenamedFieldTest( + query = """ + query { + ... @defer { + issueByKey(key: "GQLGW-1") { # Renamed + key + assignee { + name + } + } + } + } + """.trimIndent(), +) + class HydrationDeferInRenamedFieldUsingRenamedFieldTest : BaseHydrationDeferInRenamedFieldTest( query = """ query { @@ -59,6 +74,27 @@ class HydrationDeferInRenamedFieldAndNestedHydrationsTest : BaseHydrationDeferIn """.trimIndent(), ) +class RenamedFieldInsideNestedHydrationsInsideDeferTest : BaseHydrationDeferInRenamedFieldTest( + query = """ + query { + issueByKey(key: "GQLGW-1") { # Renamed + key + ... @defer { + self { # Hydrate + self { # Hydrate + self { # Hydrate + assigneeV2 { # Renamed + name + } + } + } + } + } + } + } + """.trimIndent(), +) + abstract class BaseHydrationDeferInRenamedFieldTest( @Language("GraphQL") query: String, diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/hydration/defer/HydrationRenamedFieldInDeferTestSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/hydration/defer/HydrationRenamedFieldInDeferTestSnapshot.kt new file mode 100644 index 000000000..a1c9b8b80 --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/hydration/defer/HydrationRenamedFieldInDeferTestSnapshot.kt @@ -0,0 +1,133 @@ +// @formatter:off +package graphql.nadel.tests.next.fixtures.hydration.defer + +import graphql.nadel.tests.next.ExpectedNadelResult +import graphql.nadel.tests.next.ExpectedServiceCall +import graphql.nadel.tests.next.TestSnapshot +import graphql.nadel.tests.next.listOfJsonStrings +import kotlin.Suppress +import kotlin.collections.List +import kotlin.collections.listOf + +private suspend fun main() { + graphql.nadel.tests.next.update() +} + +/** + * This class is generated. Do NOT modify. + * + * Refer to [graphql.nadel.tests.next.UpdateTestSnapshots + */ +@Suppress("unused") +public class HydrationRenamedFieldInDeferTestSnapshot : TestSnapshot() { + override val calls: List = listOf( + ExpectedServiceCall( + service = "issues", + query = """ + | { + | ... @defer { + | rename__issueByKey__getIssueByKey: getIssueByKey(key: "GQLGW-1") { + | key + | hydration__assignee__assigneeId: assigneeId + | __typename__hydration__assignee: __typename + | } + | } + | } + """.trimMargin(), + variables = "{}", + result = """ + | { + | "data": {}, + | "hasNext": true + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [], + | "data": { + | "issueByKey": { + | "key": "GQLGW-1", + | "assignee": { + | "name": "Franklin" + | } + | } + | } + | } + | ] + | } + """.trimMargin(), + ), + ), + ExpectedServiceCall( + service = "users", + query = """ + | { + | userById(id: "ari:cloud:identity::user/1") { + | name + | } + | } + """.trimMargin(), + variables = "{}", + result = """ + | { + | "data": { + | "userById": { + | "name": "Franklin" + | } + | } + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + ), + ), + ) + + /** + * ```json + * { + * "data": { + * "issueByKey": { + * "key": "GQLGW-1", + * "assignee": { + * "name": "Franklin" + * } + * } + * } + * } + * ``` + */ + override val result: ExpectedNadelResult = ExpectedNadelResult( + result = """ + | { + | "data": { + | "issueByKey": null + | }, + | "hasNext": true + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [], + | "data": { + | "issueByKey": { + | "key": "GQLGW-1", + | "assignee": { + | "name": "Franklin" + | } + | } + | } + | } + | ] + | } + """.trimMargin(), + ), + ) +} diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/hydration/defer/RenamedFieldInsideNestedHydrationsInsideDeferTestSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/hydration/defer/RenamedFieldInsideNestedHydrationsInsideDeferTestSnapshot.kt new file mode 100644 index 000000000..731073a09 --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/hydration/defer/RenamedFieldInsideNestedHydrationsInsideDeferTestSnapshot.kt @@ -0,0 +1,203 @@ +// @formatter:off +package graphql.nadel.tests.next.fixtures.hydration.defer + +import graphql.nadel.tests.next.ExpectedNadelResult +import graphql.nadel.tests.next.ExpectedServiceCall +import graphql.nadel.tests.next.TestSnapshot +import graphql.nadel.tests.next.listOfJsonStrings +import kotlin.Suppress +import kotlin.collections.List +import kotlin.collections.listOf + +private suspend fun main() { + graphql.nadel.tests.next.update() +} + +/** + * This class is generated. Do NOT modify. + * + * Refer to [graphql.nadel.tests.next.UpdateTestSnapshots + */ +@Suppress("unused") +public class RenamedFieldInsideNestedHydrationsInsideDeferTestSnapshot : TestSnapshot() { + override val calls: List = listOf( + ExpectedServiceCall( + service = "issues", + query = """ + | { + | rename__issueById__getIssueById: getIssueById(id: "1") { + | hydration__assigneeV2__assigneeId: assigneeId + | __typename__hydration__assigneeV2: __typename + | } + | } + """.trimMargin(), + variables = "{}", + result = """ + | { + | "data": { + | "rename__issueById__getIssueById": { + | "hydration__assigneeV2__assigneeId": "ari:cloud:identity::user/1", + | "__typename__hydration__assigneeV2": "Issue" + | } + | } + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + ), + ), + ExpectedServiceCall( + service = "issues", + query = """ + | { + | rename__issueById__getIssueById: getIssueById(id: "1") { + | hydration__self__id: id + | __typename__hydration__self: __typename + | } + | } + """.trimMargin(), + variables = "{}", + result = """ + | { + | "data": { + | "rename__issueById__getIssueById": { + | "hydration__self__id": "1", + | "__typename__hydration__self": "Issue" + | } + | } + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + ), + ), + ExpectedServiceCall( + service = "issues", + query = """ + | { + | rename__issueById__getIssueById: getIssueById(id: "1") { + | hydration__self__id: id + | __typename__hydration__self: __typename + | } + | } + """.trimMargin(), + variables = "{}", + result = """ + | { + | "data": { + | "rename__issueById__getIssueById": { + | "hydration__self__id": "1", + | "__typename__hydration__self": "Issue" + | } + | } + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + ), + ), + ExpectedServiceCall( + service = "issues", + query = """ + | { + | rename__issueByKey__getIssueByKey: getIssueByKey(key: "GQLGW-1") { + | key + | hydration__self__id: id + | __typename__hydration__self: __typename + | } + | } + """.trimMargin(), + variables = "{}", + result = """ + | { + | "data": { + | "rename__issueByKey__getIssueByKey": { + | "key": "GQLGW-1", + | "hydration__self__id": "1", + | "__typename__hydration__self": "Issue" + | } + | } + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + ), + ), + ExpectedServiceCall( + service = "users", + query = """ + | { + | rename__quickUser__user_fast: user_fast(id: "ari:cloud:identity::user/1") { + | name + | } + | } + """.trimMargin(), + variables = "{}", + result = """ + | { + | "data": { + | "rename__quickUser__user_fast": { + | "name": "SPEED" + | } + | } + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + ), + ), + ) + + /** + * ```json + * { + * "data": { + * "issueByKey": { + * "key": "GQLGW-1", + * "self": { + * "self": { + * "self": { + * "assigneeV2": { + * "name": "SPEED" + * } + * } + * } + * } + * } + * } + * } + * ``` + */ + override val result: ExpectedNadelResult = ExpectedNadelResult( + result = """ + | { + | "data": { + | "issueByKey": { + | "key": "GQLGW-1" + | } + | }, + | "hasNext": true + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [ + | "issueByKey" + | ], + | "data": { + | "self": { + | "self": { + | "self": { + | "assigneeV2": { + | "name": "SPEED" + | } + | } + | } + | } + | } + | } + | ] + | } + """.trimMargin(), + ), + ) +} From 59de250498a4d4c1477d69e2d922d07d875adfcd Mon Sep 17 00:00:00 2001 From: Steven Barker Date: Tue, 27 Aug 2024 13:57:27 +1200 Subject: [PATCH 10/24] move processing of graphql errors --- .../result/NadelResultTransformer.kt | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt b/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt index c904a8121..7fe8bdfe8 100644 --- a/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt +++ b/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt @@ -1,5 +1,6 @@ package graphql.nadel.engine.transform.result +import graphql.GraphQLError import graphql.incremental.DeferPayload import graphql.incremental.DelayedIncrementalPartialResult import graphql.incremental.DelayedIncrementalPartialResultImpl @@ -156,12 +157,7 @@ internal class NadelResultTransformer(private val executionBlueprint: NadelOvera when (transformation) { is NadelResultInstruction.Set -> process(transformation) is NadelResultInstruction.Remove -> process(transformation) - is NadelResultInstruction.AddError -> process( - transformation, - result.errors?.map { graphQLError -> - mapOf("message" to graphQLError.message, "locations" to graphQLError.locations) - } ?: emptyList() - ) + is NadelResultInstruction.AddError -> processGraphQLErrors(transformation, result.errors) } } } @@ -193,6 +189,17 @@ internal class NadelResultTransformer(private val executionBlueprint: NadelOvera mutableErrors.add(newError) } + private fun processGraphQLErrors( + instruction: NadelResultInstruction.AddError, + errors: List?, + ) { + val errorsAsJsonMap = errors?.map { graphQLError -> + mapOf("message" to graphQLError.message, "locations" to graphQLError.locations) + } ?: emptyList() + + process(instruction, errorsAsJsonMap) + } + private fun getRemoveArtificialFieldInstructions( artificialFields: List, nodes: JsonNodes, From 8d1d18bdd22f3ab4b8fa98dd701b8f12e71a3cf4 Mon Sep 17 00:00:00 2001 From: Steven Barker Date: Tue, 27 Aug 2024 15:01:39 +1200 Subject: [PATCH 11/24] remove incorrect test --- .../fixtures/defer/DeferReturnsErrorTest.kt | 62 ------------ .../defer/DeferReturnsErrorTestSnapshot.kt | 95 ------------------- 2 files changed, 157 deletions(-) delete mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/DeferReturnsErrorTest.kt diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/DeferReturnsErrorTest.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/DeferReturnsErrorTest.kt deleted file mode 100644 index d258acaca..000000000 --- a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/DeferReturnsErrorTest.kt +++ /dev/null @@ -1,62 +0,0 @@ -package graphql.nadel.tests.next.fixtures.defer - -import graphql.nadel.NadelExecutionHints -import graphql.nadel.tests.next.NadelIntegrationTest - -open class DeferReturnsErrorTest : NadelIntegrationTest( - query = """ - query { - defer { - hello - ... @defer(label: "slow-defer") { - slow - } - } - } - """.trimIndent(), - services = listOf( - Service( - name = "defer", - overallSchema = """ - directive @defer(if: Boolean, label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT - - type Query { - defer: DeferApi - } - type DeferApi { - hello: String - slow: String - } - - """.trimIndent(), - runtimeWiring = { wiring -> - data class DeferApi( - val hello: String, - val slow: String, - ) - - wiring - .type("Query") { type -> - type - .dataFetcher("defer") { env -> - Any() - } - } - .type("DeferApi") { type -> - type - .dataFetcher("hello") { env -> - "helloString" - } - .dataFetcher("slow") { env -> - RuntimeException("An error occurred while fetching 'slow'") - } - } - }, - ), - ), -) { - override fun makeExecutionHints(): NadelExecutionHints.Builder { - return super.makeExecutionHints() - .deferSupport { true } - } -} \ No newline at end of file diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/DeferReturnsErrorTestSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/DeferReturnsErrorTestSnapshot.kt index b5190b3f3..836dfbe91 100644 --- a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/DeferReturnsErrorTestSnapshot.kt +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/DeferReturnsErrorTestSnapshot.kt @@ -13,98 +13,3 @@ private suspend fun main() { graphql.nadel.tests.next.update() } -/** - * This class is generated. Do NOT modify. - * - * Refer to [graphql.nadel.tests.next.UpdateTestSnapshots - */ -@Suppress("unused") -public class DeferReturnsErrorTestSnapshot : TestSnapshot() { - override val calls: List = listOf( - ExpectedServiceCall( - service = "defer", - query = """ - | { - | defer { - | hello - | ... @defer(label: "slow-defer") { - | slow - | } - | } - | } - """.trimMargin(), - variables = "{}", - result = """ - | { - | "data": { - | "defer": { - | "hello": "helloString" - | } - | }, - | "hasNext": true - | } - """.trimMargin(), - delayedResults = listOfJsonStrings( - """ - | { - | "hasNext": false, - | "incremental": [ - | { - | "path": [ - | "defer" - | ], - | "label": "slow-defer", - | "data": { - | "slow": "java.lang.RuntimeException: An error occurred while fetching 'slow'" - | } - | } - | ] - | } - """.trimMargin(), - ), - ), - ) - - /** - * ```json - * { - * "data": { - * "defer": { - * "hello": "helloString", - * "slow": "java.lang.RuntimeException: An error occurred while fetching 'slow'" - * } - * } - * } - * ``` - */ - override val result: ExpectedNadelResult = ExpectedNadelResult( - result = """ - | { - | "data": { - | "defer": { - | "hello": "helloString" - | } - | }, - | "hasNext": true - | } - """.trimMargin(), - delayedResults = listOfJsonStrings( - """ - | { - | "hasNext": false, - | "incremental": [ - | { - | "path": [ - | "defer" - | ], - | "label": "slow-defer", - | "data": { - | "slow": "java.lang.RuntimeException: An error occurred while fetching 'slow'" - | } - | } - | ] - | } - """.trimMargin(), - ), - ) -} From 83ceb7fc7bebb8ee371158c78fedd37b4676cd25 Mon Sep 17 00:00:00 2001 From: Steven Barker Date: Tue, 27 Aug 2024 15:02:51 +1200 Subject: [PATCH 12/24] remove unused test --- .../defer/DeferReturnsErrorTestSnapshot.kt | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/DeferReturnsErrorTestSnapshot.kt diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/DeferReturnsErrorTestSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/DeferReturnsErrorTestSnapshot.kt deleted file mode 100644 index 836dfbe91..000000000 --- a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/DeferReturnsErrorTestSnapshot.kt +++ /dev/null @@ -1,15 +0,0 @@ -// @formatter:off -package graphql.nadel.tests.next.fixtures.defer - -import graphql.nadel.tests.next.ExpectedNadelResult -import graphql.nadel.tests.next.ExpectedServiceCall -import graphql.nadel.tests.next.TestSnapshot -import graphql.nadel.tests.next.listOfJsonStrings -import kotlin.Suppress -import kotlin.collections.List -import kotlin.collections.listOf - -private suspend fun main() { - graphql.nadel.tests.next.update() -} - From fe5827dae06884a8b12486af35f6abea415cd676 Mon Sep 17 00:00:00 2001 From: Steven Barker Date: Tue, 27 Aug 2024 15:39:59 +1200 Subject: [PATCH 13/24] add logic to move transform functions next to each other --- .../main/java/graphql/nadel/NextgenEngine.kt | 63 ++++++++++--------- .../graphql/nadel/engine/util/GraphQLUtil.kt | 26 +++++++- 2 files changed, 58 insertions(+), 31 deletions(-) diff --git a/lib/src/main/java/graphql/nadel/NextgenEngine.kt b/lib/src/main/java/graphql/nadel/NextgenEngine.kt index 9d608f508..f175fc582 100644 --- a/lib/src/main/java/graphql/nadel/NextgenEngine.kt +++ b/lib/src/main/java/graphql/nadel/NextgenEngine.kt @@ -26,6 +26,7 @@ import graphql.nadel.engine.transform.query.DynamicServiceResolution import graphql.nadel.engine.transform.query.NadelFieldToService import graphql.nadel.engine.transform.query.NadelQueryTransformer import graphql.nadel.engine.transform.result.NadelResultTransformer +import graphql.nadel.engine.util.MutableJsonMap import graphql.nadel.engine.util.beginExecute import graphql.nadel.engine.util.compileToDocument import graphql.nadel.engine.util.copy @@ -296,6 +297,31 @@ internal class NextgenEngine( overallToUnderlyingFields = queryTransform.overallToUnderlyingFields, ) } + if (result is NadelIncrementalServiceExecutionResult) { + executionContext.incrementalResultSupport.defer( + result.incrementalItemPublisher + .asFlow() + .map {delayedIncrementalResult -> + // Transform + delayedIncrementalResult.incremental + ?.filterIsInstance() + ?.map {deferPayload -> + resultTransformer + .transform( + executionContext = executionContext, + serviceExecutionContext = serviceExecutionContext, + executionPlan = executionPlan, + artificialFields = queryTransform.artificialFields, + overallToUnderlyingFields = queryTransform.overallToUnderlyingFields, + service = service, + result = result, + deferPayload = deferPayload, + ) } + + delayedIncrementalResult + } + ) + } val transformedResult: ServiceExecutionResult = when { topLevelField.name.startsWith("__") -> result else -> timer.time(step = RootStep.ResultTransforming) { @@ -389,38 +415,15 @@ internal class NextgenEngine( ) } - if (serviceExecResult is NadelIncrementalServiceExecutionResult) { - executionContext.incrementalResultSupport.defer( - serviceExecResult.incrementalItemPublisher - .asFlow() - .map {delayedIncrementalResult -> - // Transform - delayedIncrementalResult.incremental - ?.filterIsInstance() - ?.map {deferPayload -> - resultTransformer - .transform( - executionContext = executionContext, - serviceExecutionContext = serviceExecutionContext, - executionPlan = executionPlan, - artificialFields = artificialFields, - overallToUnderlyingFields = overallToUnderlyingFields, - service = service, - result = serviceExecResult, - deferPayload = deferPayload, - ) } - - delayedIncrementalResult - } - ) + val transformedData: MutableJsonMap = serviceExecResult.data.let { data -> + data.takeIf { transformedQuery.resultKey in data } + ?: mutableMapOf(transformedQuery.resultKey to null) } - return serviceExecResult.copy( - data = serviceExecResult.data.let { data -> - data.takeIf { transformedQuery.resultKey in data } - ?: mutableMapOf(transformedQuery.resultKey to null) - }, - ) + return when(serviceExecResult) { + is NadelServiceExecutionResultImpl -> serviceExecResult.copy(data = transformedData) + is NadelIncrementalServiceExecutionResult -> serviceExecResult.copy(data = transformedData) + } } private fun chooseServiceExecution( diff --git a/lib/src/main/java/graphql/nadel/engine/util/GraphQLUtil.kt b/lib/src/main/java/graphql/nadel/engine/util/GraphQLUtil.kt index 5fa6f5742..509d01c98 100644 --- a/lib/src/main/java/graphql/nadel/engine/util/GraphQLUtil.kt +++ b/lib/src/main/java/graphql/nadel/engine/util/GraphQLUtil.kt @@ -40,6 +40,7 @@ import graphql.language.Type import graphql.language.TypeName import graphql.language.UnionTypeExtensionDefinition import graphql.language.Value +import graphql.nadel.NadelIncrementalServiceExecutionResult import graphql.nadel.NadelOperationKind import graphql.nadel.NadelServiceExecutionResultImpl import graphql.nadel.ServiceExecutionResult @@ -67,6 +68,7 @@ import graphql.schema.GraphQLUnionType import graphql.schema.GraphQLUnmodifiedType import graphql.schema.idl.TypeUtil import kotlinx.coroutines.future.asDeferred +import org.reactivestreams.Publisher internal typealias AnyAstValue = Value<*> internal typealias AnyAstNode = Node<*> @@ -365,7 +367,18 @@ fun ExecutionIdProvider.provide(executionInput: ExecutionInput): ExecutionId { return provide(executionInput.query, executionInput.operationName, executionInput.context) } -fun ServiceExecutionResult.copy( +fun NadelIncrementalServiceExecutionResult.copy( + data: MutableJsonMap = this.data, + errors: MutableList = this.errors, + extensions: MutableJsonMap = this.extensions, + incremental: List? = this.incremental, + incrementalItemPublisher: Publisher = this.incrementalItemPublisher, + hasNext: Boolean = this.hasNext, +): ServiceExecutionResult { + return newIncrementalServiceExecutionResult(data, errors, extensions, incremental, incrementalItemPublisher, hasNext) +} + +fun NadelServiceExecutionResultImpl.copy( data: MutableJsonMap = this.data, errors: MutableList = this.errors, extensions: MutableJsonMap = this.extensions, @@ -381,6 +394,17 @@ fun newServiceExecutionResult( return NadelServiceExecutionResultImpl(data, errors, extensions) } +fun newIncrementalServiceExecutionResult( + data: MutableJsonMap = mutableMapOf(), + errors: MutableList = mutableListOf(), + extensions: MutableJsonMap = mutableMapOf(), + incremental: List?, + incrementalItemPublisher: Publisher, + hasNext: Boolean, +): ServiceExecutionResult { + return NadelIncrementalServiceExecutionResult(data, errors, extensions, incremental, incrementalItemPublisher, hasNext) +} + fun newServiceExecutionResult( error: GraphQLError, ): ServiceExecutionResult { From ceb3c560b91ab0bc722cd844d44d34b15c704f21 Mon Sep 17 00:00:00 2001 From: Steven Barker Date: Wed, 28 Aug 2024 11:48:19 +1000 Subject: [PATCH 14/24] add level of abstraction for reused code in transform functions --- .../result/NadelResultTransformer.kt | 124 +++++++----------- .../engine/transform/result/json/JsonNodes.kt | 8 +- 2 files changed, 55 insertions(+), 77 deletions(-) diff --git a/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt b/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt index 7fe8bdfe8..af55e365f 100644 --- a/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt +++ b/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt @@ -2,9 +2,6 @@ package graphql.nadel.engine.transform.result import graphql.GraphQLError import graphql.incremental.DeferPayload -import graphql.incremental.DelayedIncrementalPartialResult -import graphql.incremental.DelayedIncrementalPartialResultImpl -import graphql.incremental.IncrementalPayload import graphql.nadel.NadelIncrementalServiceExecutionResult import graphql.nadel.Service import graphql.nadel.ServiceExecutionResult @@ -14,7 +11,6 @@ import graphql.nadel.engine.blueprint.NadelOverallExecutionBlueprint import graphql.nadel.engine.plan.NadelExecutionPlan import graphql.nadel.engine.transform.query.NadelQueryPath import graphql.nadel.engine.transform.result.json.JsonNodes -import graphql.nadel.engine.transform.result.json.NadelCachingJsonNodes import graphql.nadel.engine.util.JsonMap import graphql.nadel.engine.util.MutableJsonMap import graphql.nadel.engine.util.queryPath @@ -23,8 +19,6 @@ import kotlinx.coroutines.Deferred import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.reactive.asFlow internal class NadelResultTransformer(private val executionBlueprint: NadelOverallExecutionBlueprint) { suspend fun transform( @@ -34,52 +28,20 @@ internal class NadelResultTransformer(private val executionBlueprint: NadelOvera artificialFields: List, overallToUnderlyingFields: Map>, service: Service, - result: ServiceExecutionResult, + result: ServiceExecutionResult ): ServiceExecutionResult { val nodes = JsonNodes(result.data) - - val asyncInstructions = ArrayList>>() - - coroutineScope { - for ((field, steps) in executionPlan.transformationSteps) { - // This can be null if we did not end up sending the field e.g. for hydration - val underlyingFields = overallToUnderlyingFields[field] - if (underlyingFields.isNullOrEmpty()) { - continue - } - - for (step in steps) { - asyncInstructions.add( - async { - step.transform.getResultInstructions( - executionContext, - serviceExecutionContext, - executionBlueprint, - service, - field, - underlyingFields.first().parent, - result, - step.state, - nodes, - ) - }, - ) - } - } - - asyncInstructions.add( - async { - getRemoveArtificialFieldInstructions(artificialFields, nodes) - }, - ) - } - - val instructions = asyncInstructions - .awaitAll() - .flatten() - + val instructions = getMutationInstructions( + executionContext, + serviceExecutionContext, + executionPlan, + artificialFields, + overallToUnderlyingFields, + service, + result, + nodes + ) mutate(result, instructions) - return result } @@ -90,23 +52,43 @@ internal class NadelResultTransformer(private val executionBlueprint: NadelOvera artificialFields: List, overallToUnderlyingFields: Map>, service: Service, - result: NadelIncrementalServiceExecutionResult, + result: ServiceExecutionResult, deferPayload: DeferPayload ): DeferPayload { + val nodes = JsonNodes( + deferPayload.getData() ?: emptyMap(), + pathPrefix = NadelQueryPath(deferPayload.path.filterIsInstance()) + ) + val instructions = getMutationInstructions( + executionContext, + serviceExecutionContext, + executionPlan, + artificialFields, + overallToUnderlyingFields, + service, + result, + nodes + ) + mutate(deferPayload, instructions) + return deferPayload + } + + private suspend fun getMutationInstructions( + executionContext: NadelExecutionContext, + serviceExecutionContext: NadelServiceExecutionContext, + executionPlan: NadelExecutionPlan, + artificialFields: List, + overallToUnderlyingFields: Map>, + service: Service, + result: ServiceExecutionResult, + nodes: JsonNodes + ): List { val asyncInstructions = ArrayList>>() coroutineScope { - val nodes = NadelCachingJsonNodes( - deferPayload.getData() ?: emptyMap(), - pathPrefix = NadelQueryPath(deferPayload.path.filterIsInstance()), - ) - for ((field, steps) in executionPlan.transformationSteps) { - // This can be null if we did not end up sending the field e.g. for hydration val underlyingFields = overallToUnderlyingFields[field] - if (underlyingFields.isNullOrEmpty()) { - continue - } + if (underlyingFields.isNullOrEmpty()) continue for (step in steps) { asyncInstructions.add( @@ -120,28 +102,24 @@ internal class NadelResultTransformer(private val executionBlueprint: NadelOvera underlyingFields.first().parent, result, step.state, - nodes, + nodes ) - }, + } ) } - - asyncInstructions.add( - async { - getRemoveArtificialFieldInstructions(artificialFields, nodes) - }, - ) } - } - val instructions = asyncInstructions - .awaitAll() - .flatten() - mutate(deferPayload, instructions) + asyncInstructions.add( + async { + getRemoveArtificialFieldInstructions(artificialFields, nodes) + } + ) + } - return deferPayload + return asyncInstructions.awaitAll().flatten() } + private fun mutate(result: ServiceExecutionResult, instructions: List) { instructions.forEach { transformation -> when (transformation) { diff --git a/lib/src/main/java/graphql/nadel/engine/transform/result/json/JsonNodes.kt b/lib/src/main/java/graphql/nadel/engine/transform/result/json/JsonNodes.kt index c80d5e2ef..2b9c7e7a0 100644 --- a/lib/src/main/java/graphql/nadel/engine/transform/result/json/JsonNodes.kt +++ b/lib/src/main/java/graphql/nadel/engine/transform/result/json/JsonNodes.kt @@ -19,12 +19,12 @@ interface JsonNodes { fun getNodesAt(queryPath: NadelQueryPath, flatten: Boolean = false): List companion object { - internal var nodesFactory: (JsonMap) -> JsonNodes = { - NadelCachingJsonNodes(it) + internal var nodesFactory: (JsonMap, NadelQueryPath?) -> JsonNodes = { data, pathPrefix -> + NadelCachingJsonNodes(data, pathPrefix) } - operator fun invoke(data: JsonMap): JsonNodes { - return nodesFactory(data) + operator fun invoke(data: JsonMap, pathPrefix: NadelQueryPath? = null): JsonNodes { + return nodesFactory(data, pathPrefix) } } } From 35d74db591c27bcc2bb3f363dc4ab1ef27884d82 Mon Sep 17 00:00:00 2001 From: Steven Barker Date: Wed, 28 Aug 2024 11:54:34 +1000 Subject: [PATCH 15/24] add assertShouldNeverHappen when prefix does not match query path in JsonNodes --- .../graphql/nadel/engine/transform/query/NadelQueryPath.kt | 4 +++- .../graphql/nadel/engine/transform/result/json/JsonNodes.kt | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/src/main/java/graphql/nadel/engine/transform/query/NadelQueryPath.kt b/lib/src/main/java/graphql/nadel/engine/transform/query/NadelQueryPath.kt index 97af4ba6e..01e01e50c 100644 --- a/lib/src/main/java/graphql/nadel/engine/transform/query/NadelQueryPath.kt +++ b/lib/src/main/java/graphql/nadel/engine/transform/query/NadelQueryPath.kt @@ -1,5 +1,7 @@ package graphql.nadel.engine.transform.query +import graphql.Assert.assertShouldNeverHappen + data class NadelQueryPath(val segments: List) { val size: Int get() = segments.size @@ -31,7 +33,7 @@ data class NadelQueryPath(val segments: List) { if (this.startsWith(prefix)) { return NadelQueryPath(segments.drop(prefix.size)) } - throw Error("NadelQueryPath did not start with prefix") //todo: make proper error + return assertShouldNeverHappen("NadelQueryPath did not start with prefix") } companion object { diff --git a/lib/src/main/java/graphql/nadel/engine/transform/result/json/JsonNodes.kt b/lib/src/main/java/graphql/nadel/engine/transform/result/json/JsonNodes.kt index 2b9c7e7a0..3e91a3a8a 100644 --- a/lib/src/main/java/graphql/nadel/engine/transform/result/json/JsonNodes.kt +++ b/lib/src/main/java/graphql/nadel/engine/transform/result/json/JsonNodes.kt @@ -1,5 +1,7 @@ package graphql.nadel.engine.transform.result.json +import graphql.Assert +import graphql.Assert.assertShouldNeverHappen import graphql.nadel.engine.transform.query.NadelQueryPath import graphql.nadel.engine.util.AnyList import graphql.nadel.engine.util.AnyMap @@ -46,7 +48,7 @@ class NadelCachingJsonNodes( } else if (queryPath.startsWith(pathPrefix.segments)) { getNodesAt(rootNode, queryPath.removePrefix(pathPrefix.segments), flatten) } else { - emptyList() + assertShouldNeverHappen("The supplied path prefix did not match the start of the query path") } } From 75285f7aad84a05bf5ee778c6f9a8098a26b3439 Mon Sep 17 00:00:00 2001 From: Steven Barker Date: Wed, 28 Aug 2024 13:16:31 +1000 Subject: [PATCH 16/24] add deep deferred rename transform test --- .../transforms/DeferredDeepRenameTest.kt | 69 ++++++++++++++++ .../DeferredDeepRenameTestSnapshot.kt | 78 +++++++++++++++++++ 2 files changed, 147 insertions(+) create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredDeepRenameTest.kt create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredDeepRenameTestSnapshot.kt diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredDeepRenameTest.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredDeepRenameTest.kt new file mode 100644 index 000000000..3c507a54c --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredDeepRenameTest.kt @@ -0,0 +1,69 @@ +package graphql.nadel.tests.next.fixtures.defer.transforms + +import graphql.nadel.NadelExecutionHints +import graphql.nadel.tests.next.NadelIntegrationTest + +open class DeferredFieldIsRenamedTest : NadelIntegrationTest( + query = """ + query { + defer { + hello + ...@defer { + overallString + } + } + } + """.trimIndent(), + services = listOf( + Service( + name = "defer", + overallSchema = """ + directive @defer(if: Boolean, label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT + + type Query { + defer: DeferApi + } + type DeferApi { + hello: String + overallString: String @renamed(from: "underlyingString") + } + + """.trimIndent(), + underlyingSchema = """ + directive @defer(if: Boolean, label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT + + type Query { + defer: DeferApi + } + type DeferApi { + hello: String + underlyingString: String + } + + """.trimIndent(), + runtimeWiring = { wiring -> + wiring + .type("Query") { type -> + type + .dataFetcher("defer") { env -> + Any() + } + } + .type("DeferApi") { type -> + type + .dataFetcher("hello") { env -> + "hello there" + } + .dataFetcher("underlyingString") { env -> + "string for the deferred renamed field" + } + } + }, + ), + ), +) { + override fun makeExecutionHints(): NadelExecutionHints.Builder { + return super.makeExecutionHints() + .deferSupport { true } + } +} \ No newline at end of file diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredDeepRenameTestSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredDeepRenameTestSnapshot.kt new file mode 100644 index 000000000..518e8ff8b --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredDeepRenameTestSnapshot.kt @@ -0,0 +1,78 @@ +// @formatter:off +package graphql.nadel.tests.next.fixtures.defer.transforms + +import graphql.nadel.tests.next.ExpectedNadelResult +import graphql.nadel.tests.next.ExpectedServiceCall +import graphql.nadel.tests.next.TestSnapshot +import graphql.nadel.tests.next.listOfJsonStrings +import kotlin.Suppress +import kotlin.collections.List +import kotlin.collections.listOf + +private suspend fun main() { + graphql.nadel.tests.next.update() +} + +/** + * This class is generated. Do NOT modify. + * + * Refer to [graphql.nadel.tests.next.UpdateTestSnapshots + */ +@Suppress("unused") +public class DeferredDeepRenameTestSnapshot : TestSnapshot() { + override val calls: List = listOf( + ExpectedServiceCall( + service = "defer", + query = """ + | { + | details { + | deep_rename__name__issue: issue { + | name + | } + | __typename__deep_rename__name: __typename + | } + | } + """.trimMargin(), + variables = "{}", + result = """ + | { + | "data": { + | "details": { + | "deep_rename__name__issue": { + | "name": "Issue-1" + | }, + | "__typename__deep_rename__name": "IssueDetail" + | } + | } + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + ), + ), + ) + + /** + * ```json + * { + * "data": { + * "details": { + * "name": "Issue-1" + * } + * } + * } + * ``` + */ + override val result: ExpectedNadelResult = ExpectedNadelResult( + result = """ + | { + | "data": { + | "details": { + | "name": "Issue-1" + | } + | } + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + ), + ) +} From b3196a18e491cbb135710509e95185638705fa2a Mon Sep 17 00:00:00 2001 From: Steven Barker Date: Wed, 28 Aug 2024 13:28:26 +1000 Subject: [PATCH 17/24] remove empty file --- .../defer/transforms/DeferredfieldInsideListIsRenamed.kt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredfieldInsideListIsRenamed.kt diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredfieldInsideListIsRenamed.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredfieldInsideListIsRenamed.kt deleted file mode 100644 index 8b1378917..000000000 --- a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredfieldInsideListIsRenamed.kt +++ /dev/null @@ -1 +0,0 @@ - From 96cd51d632698fd28e09d6ef939e79af313fda99 Mon Sep 17 00:00:00 2001 From: Steven Barker Date: Wed, 28 Aug 2024 15:20:36 +1000 Subject: [PATCH 18/24] clean up defer transform code --- .../main/java/graphql/nadel/NextgenEngine.kt | 13 ++---- .../graphql/nadel/ServiceExecutionResult.kt | 4 +- .../engine/transform/query/NadelQueryPath.kt | 6 ++- .../result/NadelResultTransformer.kt | 6 +-- .../engine/transform/result/json/JsonNodes.kt | 5 ++- .../graphql/nadel/engine/util/GraphQLUtil.kt | 40 ------------------- 6 files changed, 15 insertions(+), 59 deletions(-) diff --git a/lib/src/main/java/graphql/nadel/NextgenEngine.kt b/lib/src/main/java/graphql/nadel/NextgenEngine.kt index f175fc582..ee158d2a2 100644 --- a/lib/src/main/java/graphql/nadel/NextgenEngine.kt +++ b/lib/src/main/java/graphql/nadel/NextgenEngine.kt @@ -65,6 +65,7 @@ import kotlinx.coroutines.cancel import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.future.asCompletableFuture import kotlinx.coroutines.future.asDeferred import kotlinx.coroutines.future.await @@ -290,22 +291,19 @@ internal class NextgenEngine( service = service, transformedQuery = transformedQuery, executionContext = executionContext, - executionPlan = executionPlan, serviceExecutionContext = serviceExecutionContext, executionHydrationDetails = executionContext.hydrationDetails, - artificialFields = queryTransform.artificialFields, - overallToUnderlyingFields = queryTransform.overallToUnderlyingFields, ) } if (result is NadelIncrementalServiceExecutionResult) { executionContext.incrementalResultSupport.defer( result.incrementalItemPublisher .asFlow() - .map {delayedIncrementalResult -> + .onEach {delayedIncrementalResult -> // Transform delayedIncrementalResult.incremental ?.filterIsInstance() - ?.map {deferPayload -> + ?.forEach {deferPayload -> resultTransformer .transform( executionContext = executionContext, @@ -317,8 +315,6 @@ internal class NextgenEngine( result = result, deferPayload = deferPayload, ) } - - delayedIncrementalResult } ) } @@ -344,11 +340,8 @@ internal class NextgenEngine( service: Service, transformedQuery: ExecutableNormalizedField, executionContext: NadelExecutionContext, - executionPlan: NadelExecutionPlan, serviceExecutionContext: NadelServiceExecutionContext, executionHydrationDetails: ServiceExecutionHydrationDetails? = null, - artificialFields: List, - overallToUnderlyingFields: Map>, ): ServiceExecutionResult { val timer = executionContext.timer diff --git a/lib/src/main/java/graphql/nadel/ServiceExecutionResult.kt b/lib/src/main/java/graphql/nadel/ServiceExecutionResult.kt index f7be68684..7323e169c 100644 --- a/lib/src/main/java/graphql/nadel/ServiceExecutionResult.kt +++ b/lib/src/main/java/graphql/nadel/ServiceExecutionResult.kt @@ -10,7 +10,7 @@ sealed class ServiceExecutionResult { abstract val extensions: MutableMap } -class NadelIncrementalServiceExecutionResult( +data class NadelIncrementalServiceExecutionResult( override val data: MutableMap = LinkedHashMap(), override val errors: MutableList> = ArrayList(), override val extensions: MutableMap = LinkedHashMap(), @@ -19,7 +19,7 @@ class NadelIncrementalServiceExecutionResult( val hasNext: Boolean, ) : ServiceExecutionResult() -class NadelServiceExecutionResultImpl @JvmOverloads constructor( +data class NadelServiceExecutionResultImpl @JvmOverloads constructor( override val data: MutableMap = LinkedHashMap(), override val errors: MutableList> = ArrayList(), override val extensions: MutableMap = LinkedHashMap(), diff --git a/lib/src/main/java/graphql/nadel/engine/transform/query/NadelQueryPath.kt b/lib/src/main/java/graphql/nadel/engine/transform/query/NadelQueryPath.kt index 01e01e50c..5d530f551 100644 --- a/lib/src/main/java/graphql/nadel/engine/transform/query/NadelQueryPath.kt +++ b/lib/src/main/java/graphql/nadel/engine/transform/query/NadelQueryPath.kt @@ -26,7 +26,11 @@ data class NadelQueryPath(val segments: List) { } fun startsWith(prefix: List): Boolean { - return segments.take(prefix.size) == prefix + if (prefix.size > segments.size) return false + for (i in 0..): NadelQueryPath { diff --git a/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt b/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt index af55e365f..2f0c2877c 100644 --- a/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt +++ b/lib/src/main/java/graphql/nadel/engine/transform/result/NadelResultTransformer.kt @@ -171,11 +171,7 @@ internal class NadelResultTransformer(private val executionBlueprint: NadelOvera instruction: NadelResultInstruction.AddError, errors: List?, ) { - val errorsAsJsonMap = errors?.map { graphQLError -> - mapOf("message" to graphQLError.message, "locations" to graphQLError.locations) - } ?: emptyList() - - process(instruction, errorsAsJsonMap) + errors?.asMutable()?.add(instruction.error) } private fun getRemoveArtificialFieldInstructions( diff --git a/lib/src/main/java/graphql/nadel/engine/transform/result/json/JsonNodes.kt b/lib/src/main/java/graphql/nadel/engine/transform/result/json/JsonNodes.kt index 3e91a3a8a..2a2e2569e 100644 --- a/lib/src/main/java/graphql/nadel/engine/transform/result/json/JsonNodes.kt +++ b/lib/src/main/java/graphql/nadel/engine/transform/result/json/JsonNodes.kt @@ -13,6 +13,9 @@ import java.util.concurrent.ConcurrentHashMap * * Use [NadelCachingJsonNodes] for the most part because that is faster. * It is the default implementation. + * + * @param data The JSON map data. + * @param pathPrefix For incremental (defer) payloads, this is the prefix that needs to be removed from the path. */ interface JsonNodes { /** @@ -48,7 +51,7 @@ class NadelCachingJsonNodes( } else if (queryPath.startsWith(pathPrefix.segments)) { getNodesAt(rootNode, queryPath.removePrefix(pathPrefix.segments), flatten) } else { - assertShouldNeverHappen("The supplied path prefix did not match the start of the query path") + emptyList() } } diff --git a/lib/src/main/java/graphql/nadel/engine/util/GraphQLUtil.kt b/lib/src/main/java/graphql/nadel/engine/util/GraphQLUtil.kt index 509d01c98..a19d3290f 100644 --- a/lib/src/main/java/graphql/nadel/engine/util/GraphQLUtil.kt +++ b/lib/src/main/java/graphql/nadel/engine/util/GraphQLUtil.kt @@ -367,25 +367,6 @@ fun ExecutionIdProvider.provide(executionInput: ExecutionInput): ExecutionId { return provide(executionInput.query, executionInput.operationName, executionInput.context) } -fun NadelIncrementalServiceExecutionResult.copy( - data: MutableJsonMap = this.data, - errors: MutableList = this.errors, - extensions: MutableJsonMap = this.extensions, - incremental: List? = this.incremental, - incrementalItemPublisher: Publisher = this.incrementalItemPublisher, - hasNext: Boolean = this.hasNext, -): ServiceExecutionResult { - return newIncrementalServiceExecutionResult(data, errors, extensions, incremental, incrementalItemPublisher, hasNext) -} - -fun NadelServiceExecutionResultImpl.copy( - data: MutableJsonMap = this.data, - errors: MutableList = this.errors, - extensions: MutableJsonMap = this.extensions, -): ServiceExecutionResult { - return newServiceExecutionResult(data, errors, extensions) -} - fun newServiceExecutionResult( data: MutableJsonMap = mutableMapOf(), errors: MutableList = mutableListOf(), @@ -394,27 +375,6 @@ fun newServiceExecutionResult( return NadelServiceExecutionResultImpl(data, errors, extensions) } -fun newIncrementalServiceExecutionResult( - data: MutableJsonMap = mutableMapOf(), - errors: MutableList = mutableListOf(), - extensions: MutableJsonMap = mutableMapOf(), - incremental: List?, - incrementalItemPublisher: Publisher, - hasNext: Boolean, -): ServiceExecutionResult { - return NadelIncrementalServiceExecutionResult(data, errors, extensions, incremental, incrementalItemPublisher, hasNext) -} - -fun newServiceExecutionResult( - error: GraphQLError, -): ServiceExecutionResult { - return newServiceExecutionResult( - errors = mutableListOf( - error.toSpecification(), - ), - ) -} - fun newExecutionResult( data: Any? = null, error: GraphQLError, From 92863a146348bd81b9c93793509866651389a57f Mon Sep 17 00:00:00 2001 From: Steven Barker Date: Wed, 28 Aug 2024 17:09:07 +1000 Subject: [PATCH 19/24] add tests for other transforms --- .../transforms/DeferredDeepRenameTest.kt | 47 ++--- .../DeferredDeepRenameTestSnapshot.kt | 2 +- .../DeferredFieldIsSkippedIfTrueTest.kt | 69 ++++++++ ...eferredFieldIsSkippedIfTrueTestSnapshot.kt | 102 +++++++++++ .../transforms/RenamedTypeIsDeferredTest.kt | 84 +++++++++ .../RenamedTypeIsDeferredTestSnapshot.kt | 164 ++++++++++++++++++ 6 files changed, 445 insertions(+), 23 deletions(-) create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredFieldIsSkippedIfTrueTest.kt create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredFieldIsSkippedIfTrueTestSnapshot.kt create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/RenamedTypeIsDeferredTest.kt create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/RenamedTypeIsDeferredTestSnapshot.kt diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredDeepRenameTest.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredDeepRenameTest.kt index 3c507a54c..ffe3bdddd 100644 --- a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredDeepRenameTest.kt +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredDeepRenameTest.kt @@ -3,14 +3,11 @@ package graphql.nadel.tests.next.fixtures.defer.transforms import graphql.nadel.NadelExecutionHints import graphql.nadel.tests.next.NadelIntegrationTest -open class DeferredFieldIsRenamedTest : NadelIntegrationTest( +open class DeferredDeepRenameTest : NadelIntegrationTest( query = """ query { - defer { - hello - ...@defer { - overallString - } + details { + name # Deep renamed from Issue.name } } """.trimIndent(), @@ -21,23 +18,26 @@ open class DeferredFieldIsRenamedTest : NadelIntegrationTest( directive @defer(if: Boolean, label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT type Query { - defer: DeferApi + details: IssueDetail } - type DeferApi { - hello: String - overallString: String @renamed(from: "underlyingString") + type IssueDetail { + name: String @renamed(from: "issue.name") } """.trimIndent(), underlyingSchema = """ directive @defer(if: Boolean, label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT - type Query { - defer: DeferApi + type Issue { + name: String + } + + type IssueDetail { + issue: Issue } - type DeferApi { - hello: String - underlyingString: String + + type Query { + details: IssueDetail } """.trimIndent(), @@ -45,17 +45,20 @@ open class DeferredFieldIsRenamedTest : NadelIntegrationTest( wiring .type("Query") { type -> type - .dataFetcher("defer") { env -> + .dataFetcher("details") { env -> Any() } } - .type("DeferApi") { type -> + .type("IssueDetail") { type -> type - .dataFetcher("hello") { env -> - "hello there" + .dataFetcher("issue") { env -> + Any() } - .dataFetcher("underlyingString") { env -> - "string for the deferred renamed field" + } + .type("Issue") { type -> + type + .dataFetcher("name") { env -> + "Issue-1" } } }, @@ -66,4 +69,4 @@ open class DeferredFieldIsRenamedTest : NadelIntegrationTest( return super.makeExecutionHints() .deferSupport { true } } -} \ No newline at end of file +} diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredDeepRenameTestSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredDeepRenameTestSnapshot.kt index 518e8ff8b..634604d33 100644 --- a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredDeepRenameTestSnapshot.kt +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredDeepRenameTestSnapshot.kt @@ -27,7 +27,7 @@ public class DeferredDeepRenameTestSnapshot : TestSnapshot() { | { | details { | deep_rename__name__issue: issue { - | name + | name | } | __typename__deep_rename__name: __typename | } diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredFieldIsSkippedIfTrueTest.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredFieldIsSkippedIfTrueTest.kt new file mode 100644 index 000000000..b44f24d15 --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredFieldIsSkippedIfTrueTest.kt @@ -0,0 +1,69 @@ +package graphql.nadel.tests.next.fixtures.defer.transforms + +import graphql.nadel.NadelExecutionHints +import graphql.nadel.tests.next.NadelIntegrationTest + +open class DeferredFieldIsSkippedIfTrueTest : NadelIntegrationTest( + query = """ + query { + defer { + ...@defer { + hello @skip(if: false) + overallString @skip (if: true) + } + } + } + """.trimIndent(), + services = listOf( + Service( + name = "defer", + overallSchema = """ + directive @defer(if: Boolean, label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT + + type Query { + defer: DeferApi + } + type DeferApi { + hello: String + overallString: String @renamed(from: "underlyingString") + } + + """.trimIndent(), + underlyingSchema = """ + directive @defer(if: Boolean, label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT + + type Query { + defer: DeferApi + } + type DeferApi { + hello: String + underlyingString: String + } + + """.trimIndent(), + runtimeWiring = { wiring -> + wiring + .type("Query") { type -> + type + .dataFetcher("defer") { env -> + Any() + } + } + .type("DeferApi") { type -> + type + .dataFetcher("hello") { env -> + "hello there" + } + .dataFetcher("underlyingString") { env -> + "string for the deferred renamed field" + } + } + }, + ), + ), +) { + override fun makeExecutionHints(): NadelExecutionHints.Builder { + return super.makeExecutionHints() + .deferSupport { true } + } +} \ No newline at end of file diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredFieldIsSkippedIfTrueTestSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredFieldIsSkippedIfTrueTestSnapshot.kt new file mode 100644 index 000000000..681e969c8 --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredFieldIsSkippedIfTrueTestSnapshot.kt @@ -0,0 +1,102 @@ +// @formatter:off +package graphql.nadel.tests.next.fixtures.defer.transforms + +import graphql.nadel.tests.next.ExpectedNadelResult +import graphql.nadel.tests.next.ExpectedServiceCall +import graphql.nadel.tests.next.TestSnapshot +import graphql.nadel.tests.next.listOfJsonStrings +import kotlin.Suppress +import kotlin.collections.List +import kotlin.collections.listOf + +private suspend fun main() { + graphql.nadel.tests.next.update() +} + +/** + * This class is generated. Do NOT modify. + * + * Refer to [graphql.nadel.tests.next.UpdateTestSnapshots + */ +@Suppress("unused") +public class DeferredFieldIsSkippedIfTrueTestSnapshot : TestSnapshot() { + override val calls: List = listOf( + ExpectedServiceCall( + service = "defer", + query = """ + | { + | defer { + | ... @defer { + | hello + | } + | } + | } + """.trimMargin(), + variables = "{}", + result = """ + | { + | "data": { + | "defer": {} + | }, + | "hasNext": true + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [ + | "defer" + | ], + | "data": { + | "hello": "hello there" + | } + | } + | ] + | } + """.trimMargin(), + ), + ), + ) + + /** + * ```json + * { + * "data": { + * "defer": { + * "hello": "hello there" + * } + * } + * } + * ``` + */ + override val result: ExpectedNadelResult = ExpectedNadelResult( + result = """ + | { + | "data": { + | "defer": {} + | }, + | "hasNext": true + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [ + | "defer" + | ], + | "data": { + | "hello": "hello there" + | } + | } + | ] + | } + """.trimMargin(), + ), + ) +} diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/RenamedTypeIsDeferredTest.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/RenamedTypeIsDeferredTest.kt new file mode 100644 index 000000000..272ec2996 --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/RenamedTypeIsDeferredTest.kt @@ -0,0 +1,84 @@ +package graphql.nadel.tests.next.fixtures.defer.transforms + +import graphql.nadel.NadelExecutionHints +import graphql.nadel.tests.next.NadelIntegrationTest + +open class RenamedTypeIsDeferredTest : NadelIntegrationTest( + query = """ + query { + zoo { + ...@defer { + monkey { + name + __typename + } + } + cat { + name + ...@defer { + __typename + } + } + } + } + """.trimIndent(), + services = listOf( + Service( + name = "defer", + overallSchema = """ + directive @defer(if: Boolean, label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT + + type Query { + zoo: ZooApi + } + type ZooApi { + monkey: Monkey + cat: Cat + } + type Monkey @renamed(from: "Donkey"){ + name: String + } + type Cat @renamed(from: "Rat"){ + name: String + } + + + """.trimIndent(), + runtimeWiring = { wiring -> + wiring + .type("Query") { type -> + type + .dataFetcher("zoo") { env -> + Any() + } + } + .type("ZooApi") { type -> + type + .dataFetcher("monkey") { env -> + Any() + } + .dataFetcher("cat") { env -> + Any() + } + } + .type("Donkey") { type -> + type + .dataFetcher("name") { env -> + "Harambe" + } + } + .type("Rat") { type -> + type + .dataFetcher("name") { env -> + "Garfield" + } + } + }, + ), + ), +) { + override fun makeExecutionHints(): NadelExecutionHints.Builder { + return super.makeExecutionHints() + .deferSupport { true } + } +} \ No newline at end of file diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/RenamedTypeIsDeferredTestSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/RenamedTypeIsDeferredTestSnapshot.kt new file mode 100644 index 000000000..4941a1150 --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/RenamedTypeIsDeferredTestSnapshot.kt @@ -0,0 +1,164 @@ +// @formatter:off +package graphql.nadel.tests.next.fixtures.defer.transforms + +import graphql.nadel.tests.next.ExpectedNadelResult +import graphql.nadel.tests.next.ExpectedServiceCall +import graphql.nadel.tests.next.TestSnapshot +import graphql.nadel.tests.next.listOfJsonStrings +import kotlin.Suppress +import kotlin.collections.List +import kotlin.collections.listOf + +private suspend fun main() { + graphql.nadel.tests.next.update() +} + +/** + * This class is generated. Do NOT modify. + * + * Refer to [graphql.nadel.tests.next.UpdateTestSnapshots + */ +@Suppress("unused") +public class RenamedTypeIsDeferredTestSnapshot : TestSnapshot() { + override val calls: List = listOf( + ExpectedServiceCall( + service = "defer", + query = """ + | { + | zoo { + | cat { + | name + | ... @defer { + | __typename + | } + | } + | ... @defer { + | monkey { + | name + | __typename + | } + | } + | } + | } + """.trimMargin(), + variables = "{}", + result = """ + | { + | "data": { + | "zoo": { + | "cat": { + | "name": "Garfield" + | } + | } + | }, + | "hasNext": true + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [ + | "zoo", + | "cat" + | ], + | "data": { + | "__typename": "Cat" + | } + | } + | ] + | } + """.trimMargin(), + """ + | { + | "hasNext": true, + | "incremental": [ + | { + | "path": [ + | "zoo" + | ], + | "data": { + | "monkey": { + | "name": "Harambe", + | "__typename": "Monkey" + | } + | } + | } + | ] + | } + """.trimMargin(), + ), + ), + ) + + /** + * ```json + * { + * "data": { + * "zoo": { + * "cat": { + * "name": "Garfield", + * "__typename": "Cat" + * }, + * "monkey": { + * "name": "Harambe", + * "__typename": "Monkey" + * } + * } + * } + * } + * ``` + */ + override val result: ExpectedNadelResult = ExpectedNadelResult( + result = """ + | { + | "data": { + | "zoo": { + | "cat": { + | "name": "Garfield" + | } + | } + | }, + | "hasNext": true + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [ + | "zoo", + | "cat" + | ], + | "data": { + | "__typename": "Cat" + | } + | } + | ] + | } + """.trimMargin(), + """ + | { + | "hasNext": true, + | "incremental": [ + | { + | "path": [ + | "zoo" + | ], + | "data": { + | "monkey": { + | "name": "Harambe", + | "__typename": "Monkey" + | } + | } + | } + | ] + | } + """.trimMargin(), + ), + ) +} From 691665f1c47532fbbfd2cfd17badd2bc7e581170 Mon Sep 17 00:00:00 2001 From: Steven Barker Date: Fri, 30 Aug 2024 22:25:06 +1000 Subject: [PATCH 20/24] add defer to deep rename transform test --- .../transforms/DeferredDeepRenameTest.kt | 6 +- .../DeferredDeepRenameTestSnapshot.kt | 57 +++++++++++++------ 2 files changed, 45 insertions(+), 18 deletions(-) diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredDeepRenameTest.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredDeepRenameTest.kt index ffe3bdddd..1eb11a0a8 100644 --- a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredDeepRenameTest.kt +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredDeepRenameTest.kt @@ -6,8 +6,10 @@ import graphql.nadel.tests.next.NadelIntegrationTest open class DeferredDeepRenameTest : NadelIntegrationTest( query = """ query { - details { - name # Deep renamed from Issue.name + ...@defer { + details { + name # Deep renamed from Issue.name + } } } """.trimIndent(), diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredDeepRenameTestSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredDeepRenameTestSnapshot.kt index 634604d33..868c74fc1 100644 --- a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredDeepRenameTestSnapshot.kt +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredDeepRenameTestSnapshot.kt @@ -25,28 +25,39 @@ public class DeferredDeepRenameTestSnapshot : TestSnapshot() { service = "defer", query = """ | { - | details { - | deep_rename__name__issue: issue { - | name + | ... @defer { + | details { + | deep_rename__name__issue: issue { + | name + | } + | __typename__deep_rename__name: __typename | } - | __typename__deep_rename__name: __typename | } | } """.trimMargin(), variables = "{}", result = """ | { - | "data": { - | "details": { - | "deep_rename__name__issue": { - | "name": "Issue-1" - | }, - | "__typename__deep_rename__name": "IssueDetail" - | } - | } + | "data": {}, + | "hasNext": true | } """.trimMargin(), delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [], + | "data": { + | "details": { + | "name": "Issue-1" + | } + | } + | } + | ] + | } + """.trimMargin(), ), ), ) @@ -66,13 +77,27 @@ public class DeferredDeepRenameTestSnapshot : TestSnapshot() { result = """ | { | "data": { - | "details": { - | "name": "Issue-1" - | } - | } + | "details": null + | }, + | "hasNext": true | } """.trimMargin(), delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [], + | "data": { + | "details": { + | "name": "Issue-1" + | } + | } + | } + | ] + | } + """.trimMargin(), ), ) } From 1037769029f16d135829deec575aea65152c4f8e Mon Sep 17 00:00:00 2001 From: Steven Barker Date: Fri, 13 Sep 2024 15:00:33 +1200 Subject: [PATCH 21/24] add tests for other transforms --- .../DeferWithTransformThrowsErrorTest.kt | 62 +++++++ ...ferWithTransformThrowsErrorTestSnapshot.kt | 163 ++++++++++++++++++ .../DeferredRenamedInputTypeTest.kt | 87 ++++++++++ .../DeferredRenamedInputTypeTestSnapshot.kt | 84 +++++++++ .../DeferredServiceTypeFilterTest.kt | 68 ++++++++ .../DeferredServiceTypeFilterTestSnapshot.kt | 69 ++++++++ 6 files changed, 533 insertions(+) create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferWithTransformThrowsErrorTest.kt create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferWithTransformThrowsErrorTestSnapshot.kt create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredRenamedInputTypeTest.kt create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredRenamedInputTypeTestSnapshot.kt create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredServiceTypeFilterTest.kt create mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredServiceTypeFilterTestSnapshot.kt diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferWithTransformThrowsErrorTest.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferWithTransformThrowsErrorTest.kt new file mode 100644 index 000000000..66d3dd191 --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferWithTransformThrowsErrorTest.kt @@ -0,0 +1,62 @@ +package graphql.nadel.tests.next.fixtures.defer + +import graphql.nadel.NadelExecutionHints +import graphql.nadel.tests.next.NadelIntegrationTest + +open class DeferWithTransformThrowsErrorTest : NadelIntegrationTest( + query = """ + query { + defer { + hello + ... @defer(label: "slow-defer") { + slow + } + } + } + """.trimIndent(), + services = listOf( + Service( + name = "defer", + overallSchema = """ + directive @defer(if: Boolean, label: String) on FRAGMENT_SPREAD | INLINE_FRAGMENT + + type Query { + defer: DeferApi + } + type DeferApi { + hello: String + slow: String @renamed(from: "underlyingSlow") + } + + """.trimIndent(), + runtimeWiring = { wiring -> + data class DeferApi( + val hello: String, + val slow: String, + ) + + wiring + .type("Query") { type -> + type + .dataFetcher("defer") { env -> + Any() + } + } + .type("DeferApi") { type -> + type + .dataFetcher("hello") { env -> + "helloString" + } + .dataFetcher("slow") { env -> + throw RuntimeException("An error occurred while fetching 'slow'") + } + } + }, + ), + ), +) { + override fun makeExecutionHints(): NadelExecutionHints.Builder { + return super.makeExecutionHints() + .deferSupport { true } + } +} diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferWithTransformThrowsErrorTestSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferWithTransformThrowsErrorTestSnapshot.kt new file mode 100644 index 000000000..9c02782cf --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferWithTransformThrowsErrorTestSnapshot.kt @@ -0,0 +1,163 @@ +package graphql.nadel.tests.next.fixtures.defer.transforms + +import graphql.nadel.tests.next.ExpectedNadelResult +import graphql.nadel.tests.next.ExpectedServiceCall +import graphql.nadel.tests.next.TestSnapshot +import graphql.nadel.tests.next.fixtures.defer.DeferThrowsErrorTest +import graphql.nadel.tests.next.fixtures.defer.DeferWithTransformThrowsErrorTest +import graphql.nadel.tests.next.listOfJsonStrings + +private suspend fun main() { + graphql.nadel.tests.next.update() +} + +/** + * This class is generated. Do NOT modify. + * + * Refer to [graphql.nadel.tests.next.UpdateTestSnapshots + */ +@Suppress("unused") +public class DeferWithTransformThrowsErrorTestSnapshot : TestSnapshot() { + override val calls: List = listOf( + ExpectedServiceCall( + service = "defer", + query = """ + | { + | defer { + | hello + | ... @defer(label: "slow-defer") { + | slow + | } + | } + | } + """.trimMargin(), + variables = " {}", + result = """ + | { + | "data": { + | "defer": { + | "hello": "helloString" + | } + | }, + | "hasNext": true + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [ + | "defer" + | ], + | "label": "slow-defer", + | "errors": [ + | { + | "message": "Exception while fetching data (/defer/slow) : An error occurred while fetching 'slow'", + | "locations": [ + | { + | "line": 5, + | "column": 7 + | } + | ], + | "path": [ + | "defer", + | "slow" + | ], + | "extensions": { + | "classification": "DataFetchingException" + | } + | } + | ], + | "data": { + | "slow": null + | } + | } + | ] + | } + """.trimMargin(), + ), + ), + ) + + /** + * ```json + * { + * "data": { + * "defer": { + * "hello": "helloString", + * "slow": null + * } + * }, + * "errors": [ + * { + * "message": "Exception while fetching data (/defer/slow) : An error occurred while + * fetching 'slow'", + * "locations": [ + * { + * "line": 5, + * "column": 7 + * } + * ], + * "path": [ + * "defer", + * "slow" + * ], + * "extensions": { + * "classification": "DataFetchingException" + * } + * } + * ] + * } + * ``` + */ + override val result: ExpectedNadelResult = ExpectedNadelResult( + result = """ + | { + | "data": { + | "defer": { + | "hello": "helloString" + | } + | }, + | "hasNext": true + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [ + | "defer" + | ], + | "label": "slow-defer", + | "errors": [ + | { + | "message": "Exception while fetching data (/defer/slow) : An error occurred while fetching 'slow'", + | "locations": [ + | { + | "line": 5, + | "column": 7 + | } + | ], + | "path": [ + | "defer", + | "slow" + | ], + | "extensions": { + | "classification": "DataFetchingException" + | } + | } + | ], + | "data": { + | "slow": null + | } + | } + | ] + | } + """.trimMargin(), + ), + ) +} \ No newline at end of file diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredRenamedInputTypeTest.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredRenamedInputTypeTest.kt new file mode 100644 index 000000000..c7093f80d --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredRenamedInputTypeTest.kt @@ -0,0 +1,87 @@ +package graphql.nadel.tests.next.fixtures.defer.transforms + +import graphql.nadel.NadelExecutionHints +import graphql.nadel.tests.next.NadelIntegrationTest + +/** + * The `ConfluenceLegacyPathType` input type was renamed. + * + * In the test snapshot we ensure the variable is defined as `PathType`. + * + * This tests the NadelRenameArgumentInputTypesTransform with defer + */ +class DeferredRenamedInputTypeTest : NadelIntegrationTest( + query = """ + query { + me { + profilePicture { + ...@defer { + path(type: ABSOLUTE) + } + } + } + } + """.trimIndent(), + services = listOf( + Service( + name = "confluence_legacy", + overallSchema = """ + type Query { + me: ConfluenceLegacyUser + } + type ConfluenceLegacyUser @renamed(from: "User") { + profilePicture: ConfluenceLegacyProfilePicture + } + type ConfluenceLegacyProfilePicture @renamed(from: "ProfilePicture") { + path(type: ConfluenceLegacyPathType!): String + } + enum ConfluenceLegacyPathType @renamed(from: "PathType") { + ABSOLUTE + RELATIVE + } + """.trimIndent(), + runtimeWiring = { wiring -> + data class ProfilePicture( + val absolutePath: String, + val relativePath: String, + ) + + data class User( + val profilePicture: ProfilePicture, + ) + + wiring + .type("Query") { type -> + type + .dataFetcher("me") { env -> + User( + profilePicture = ProfilePicture( + relativePath = "/wiki/aa-avatar/5ee0a4ef55749e0ab6e0fb70", + absolutePath = "https://atlassian.net/wiki/aa-avatar/5ee0a4ef55749e0ab6e0fb70", + ), + ) + } + } + .type("ProfilePicture") { type -> + type + .dataFetcher("path") { env -> + val pfp = env.getSource()!! + when (val urlType = env.getArgument("type")) { + "ABSOLUTE" -> pfp.absolutePath + "RELATIVE" -> pfp.relativePath + else -> throw IllegalArgumentException(urlType) + } + } + } + }, + ), + ), +) { + override fun makeExecutionHints(): NadelExecutionHints.Builder { + return super.makeExecutionHints() + // todo: this should be on by default + .allDocumentVariablesHint { + true + } + } +} diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredRenamedInputTypeTestSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredRenamedInputTypeTestSnapshot.kt new file mode 100644 index 000000000..ab3e38552 --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredRenamedInputTypeTestSnapshot.kt @@ -0,0 +1,84 @@ +// @formatter:off +package graphql.nadel.tests.next.fixtures.defer.transforms + +import graphql.nadel.tests.next.ExpectedNadelResult +import graphql.nadel.tests.next.ExpectedServiceCall +import graphql.nadel.tests.next.TestSnapshot +import graphql.nadel.tests.next.listOfJsonStrings +import kotlin.Suppress +import kotlin.collections.List +import kotlin.collections.listOf + +private suspend fun main() { + graphql.nadel.tests.next.update() +} + +/** + * This class is generated. Do NOT modify. + * + * Refer to [graphql.nadel.tests.next.UpdateTestSnapshots + */ +@Suppress("unused") +public class DeferredRenamedInputTypeTestSnapshot : TestSnapshot() { + override val calls: List = listOf( + ExpectedServiceCall( + service = "confluence_legacy", + query = """ + | query (${'$'}v0: PathType!) { + | me { + | profilePicture { + | path(type: ${'$'}v0) + | } + | } + | } + """.trimMargin(), + variables = """ + | { + | "v0": "ABSOLUTE" + | } + """.trimMargin(), + result = """ + | { + | "data": { + | "me": { + | "profilePicture": { + | "path": "https://atlassian.net/wiki/aa-avatar/5ee0a4ef55749e0ab6e0fb70" + | } + | } + | } + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + ), + ), + ) + + /** + * ```json + * { + * "data": { + * "me": { + * "profilePicture": { + * "path": "https://atlassian.net/wiki/aa-avatar/5ee0a4ef55749e0ab6e0fb70" + * } + * } + * } + * } + * ``` + */ + override val result: ExpectedNadelResult = ExpectedNadelResult( + result = """ + | { + | "data": { + | "me": { + | "profilePicture": { + | "path": "https://atlassian.net/wiki/aa-avatar/5ee0a4ef55749e0ab6e0fb70" + | } + | } + | } + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + ), + ) +} diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredServiceTypeFilterTest.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredServiceTypeFilterTest.kt new file mode 100644 index 000000000..c9995ce93 --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredServiceTypeFilterTest.kt @@ -0,0 +1,68 @@ +package graphql.nadel.tests.next.fixtures.defer.transforms + +import graphql.nadel.NadelExecutionHints +import graphql.nadel.tests.next.NadelIntegrationTest + +/* + * This tests the NadelServiceTypeFilterTransform with defer + */ +open class DeferredServiceTypeFilterTest : NadelIntegrationTest( + query = """ + query { + aErrors { + ... on BError { + id + } + } + } + """.trimIndent(), + services = listOf( + Service( + name = "defer", + overallSchema = """ + service shared { + interface Error { id: ID } + } + service A { + type Query { + aErrors: [Error] + } + type AError implements Error { id: ID } + } + service B { + type BError implements Error { + id: ID + b: String + } + } + + """.trimIndent(), + runtimeWiring = { wiring -> + wiring + .type("Query") { type -> + type + .dataFetcher("aErrors") { env -> + Any() + } + } + .type("AError") { type -> + type + .dataFetcher("id") { env -> + "A-ERROR-1" + } + } + .type("BError") { type -> + type + .dataFetcher("id") { env -> + "B-ERROR-1" + } + } + }, + ), + ), +) { + override fun makeExecutionHints(): NadelExecutionHints.Builder { + return super.makeExecutionHints() + .deferSupport { true } + } +} diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredServiceTypeFilterTestSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredServiceTypeFilterTestSnapshot.kt new file mode 100644 index 000000000..63d254a7b --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredServiceTypeFilterTestSnapshot.kt @@ -0,0 +1,69 @@ +// @formatter:off +package graphql.nadel.tests.next.fixtures.defer.transforms + +import graphql.nadel.tests.next.ExpectedNadelResult +import graphql.nadel.tests.next.ExpectedServiceCall +import graphql.nadel.tests.next.TestSnapshot +import graphql.nadel.tests.next.listOfJsonStrings +import kotlin.Suppress +import kotlin.collections.List +import kotlin.collections.listOf + +private suspend fun main() { + graphql.nadel.tests.next.update() +} + +/** + * This class is generated. Do NOT modify. + * + * Refer to [graphql.nadel.tests.next.UpdateTestSnapshots + */ +@Suppress("unused") +public class DeferredServiceTypeFilterTestSnapshot : TestSnapshot() { + override val calls: List = listOf( + ) + + /** + * ```json + * { + * "errors": [ + * { + * "message": "Validation error (FieldUndefined@[aErrors]) : Field 'aErrors' in type + * 'Query' is undefined", + * "locations": [ + * { + * "line": 2, + * "column": 3 + * } + * ], + * "extensions": { + * "classification": "ValidationError" + * } + * } + * ] + * } + * ``` + */ + override val result: ExpectedNadelResult = ExpectedNadelResult( + result = """ + | { + | "errors": [ + | { + | "message": "Validation error (FieldUndefined@[aErrors]) : Field 'aErrors' in type 'Query' is undefined", + | "locations": [ + | { + | "line": 2, + | "column": 3 + | } + | ], + | "extensions": { + | "classification": "ValidationError" + | } + | } + | ] + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + ), + ) +} From 1f57de1036e2d6f1d422125c91eac4c705ac1961 Mon Sep 17 00:00:00 2001 From: Steven Barker Date: Fri, 13 Sep 2024 15:27:00 +1200 Subject: [PATCH 22/24] remove unused test snapshot --- ...ferWithTransformThrowsErrorTestSnapshot.kt | 111 ------------------ 1 file changed, 111 deletions(-) delete mode 100644 test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/DeferWithTransformThrowsErrorTestSnapshot.kt diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/DeferWithTransformThrowsErrorTestSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/DeferWithTransformThrowsErrorTestSnapshot.kt deleted file mode 100644 index 92cfc7a91..000000000 --- a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/DeferWithTransformThrowsErrorTestSnapshot.kt +++ /dev/null @@ -1,111 +0,0 @@ -// @formatter:off -package graphql.nadel.tests.next.fixtures.defer - -import graphql.nadel.tests.next.ExpectedNadelResult -import graphql.nadel.tests.next.ExpectedServiceCall -import graphql.nadel.tests.next.TestSnapshot -import graphql.nadel.tests.next.listOfJsonStrings -import kotlin.Suppress -import kotlin.collections.List -import kotlin.collections.listOf - -private suspend fun main() { - graphql.nadel.tests.next.update() -} - -/** - * This class is generated. Do NOT modify. - * - * Refer to [graphql.nadel.tests.next.UpdateTestSnapshots - */ -@Suppress("unused") -public class DeferWithTransformThrowsErrorTestSnapshot : TestSnapshot() { - override val calls: List = listOf( - ExpectedServiceCall( - service = "defer", - query = """ - | { - | defer { - | hello - | ... @defer(label: "slow-defer") { - | rename__slow__underlyingSlow: underlyingSlow - | __typename__rename__slow: __typename - | } - | } - | } - """.trimMargin(), - variables = " {}", - result = """ - | { - | "data": { - | "defer": { - | "hello": "helloString" - | } - | }, - | "hasNext": true - | } - """.trimMargin(), - delayedResults = listOfJsonStrings( - """ - | { - | "hasNext": false, - | "incremental": [ - | { - | "path": [ - | "defer" - | ], - | "label": "slow-defer", - | "data": { - | "slow": null - | } - | } - | ] - | } - """.trimMargin(), - ), - ), - ) - - /** - * ```json - * { - * "data": { - * "defer": { - * "hello": "helloString", - * "slow": null - * } - * } - * } - * ``` - */ - override val result: ExpectedNadelResult = ExpectedNadelResult( - result = """ - | { - | "data": { - | "defer": { - | "hello": "helloString" - | } - | }, - | "hasNext": true - | } - """.trimMargin(), - delayedResults = listOfJsonStrings( - """ - | { - | "hasNext": false, - | "incremental": [ - | { - | "path": [ - | "defer" - | ], - | "label": "slow-defer", - | "data": { - | "slow": null - | } - | } - | ] - | } - """.trimMargin(), - ), - ) -} From 7a52d51c48e5704022c52c5494deccb4630923b7 Mon Sep 17 00:00:00 2001 From: Steven Barker Date: Mon, 16 Sep 2024 00:12:53 +1200 Subject: [PATCH 23/24] fix NadelServiceTypeFilterTransform test --- .../DeferredServiceTypeFilterTest.kt | 71 +++++++++++++------ .../DeferredServiceTypeFilterTestSnapshot.kt | 59 ++++++++------- 2 files changed, 79 insertions(+), 51 deletions(-) diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredServiceTypeFilterTest.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredServiceTypeFilterTest.kt index c9995ce93..3eb9203cf 100644 --- a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredServiceTypeFilterTest.kt +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredServiceTypeFilterTest.kt @@ -3,39 +3,37 @@ package graphql.nadel.tests.next.fixtures.defer.transforms import graphql.nadel.NadelExecutionHints import graphql.nadel.tests.next.NadelIntegrationTest -/* +/** * This tests the NadelServiceTypeFilterTransform with defer */ open class DeferredServiceTypeFilterTest : NadelIntegrationTest( query = """ query { aErrors { - ... on BError { - id + ...@defer { + ...on BError { + id + } } } } """.trimIndent(), services = listOf( Service( - name = "defer", + name = "A", overallSchema = """ - service shared { - interface Error { id: ID } + type Query { + aErrors: Error } - service A { - type Query { - aErrors: [Error] - } - type AError implements Error { id: ID } - } - service B { - type BError implements Error { - id: ID - b: String - } + type AError implements Error { id: ID } + interface Error { id: ID } + """.trimIndent(), + underlyingSchema = """ + type Query { + aErrors: Error } - + type AError implements Error { id: ID } + interface Error { id: ID } """.trimIndent(), runtimeWiring = { wiring -> wiring @@ -51,10 +49,41 @@ open class DeferredServiceTypeFilterTest : NadelIntegrationTest( "A-ERROR-1" } } - .type("BError") { type -> + .type("Error") { type -> type - .dataFetcher("id") { env -> - "B-ERROR-1" + .typeResolver { env -> + env.schema.getObjectType("AError") + } + } + }, + ), + Service( + name = "B", + overallSchema = """ + type Query { + echo: String + } + type BError implements Error { + id: ID + b: String + } + """.trimIndent(), + underlyingSchema = """ + type Query { + echo: String + } + type BError implements Error { + id: ID + b: String + } + interface Error { id: ID } + """.trimIndent(), + runtimeWiring = { wiring -> + wiring + .type("Error") { type -> + type + .typeResolver { env -> + env.schema.getObjectType("BError") } } }, diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredServiceTypeFilterTestSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredServiceTypeFilterTestSnapshot.kt index 63d254a7b..577f1db3e 100644 --- a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredServiceTypeFilterTestSnapshot.kt +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredServiceTypeFilterTestSnapshot.kt @@ -21,46 +21,45 @@ private suspend fun main() { @Suppress("unused") public class DeferredServiceTypeFilterTestSnapshot : TestSnapshot() { override val calls: List = listOf( - ) + ExpectedServiceCall( + service = "A", + query = """ + | { + | aErrors { + | __typename__type_filter__id: __typename + | } + | } + """.trimMargin(), + variables = " {}", + result = """ + | { + | "data": { + | "aErrors": { + | "__typename__type_filter__id": "AError" + | } + | } + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + ), + ), + ) /** * ```json * { - * "errors": [ - * { - * "message": "Validation error (FieldUndefined@[aErrors]) : Field 'aErrors' in type - * 'Query' is undefined", - * "locations": [ - * { - * "line": 2, - * "column": 3 - * } - * ], - * "extensions": { - * "classification": "ValidationError" - * } - * } - * ] + * "data": { + * "aErrors": {} + * } * } * ``` */ override val result: ExpectedNadelResult = ExpectedNadelResult( result = """ | { - | "errors": [ - | { - | "message": "Validation error (FieldUndefined@[aErrors]) : Field 'aErrors' in type 'Query' is undefined", - | "locations": [ - | { - | "line": 2, - | "column": 3 - | } - | ], - | "extensions": { - | "classification": "ValidationError" - | } - | } - | ] + | "data": { + | "aErrors": {} + | } | } """.trimMargin(), delayedResults = listOfJsonStrings( From 2d0625ec265523b7048d55e8572904e9522ca5a5 Mon Sep 17 00:00:00 2001 From: Steven Barker Date: Mon, 16 Sep 2024 01:07:46 +1200 Subject: [PATCH 24/24] update test package --- .../DeferWithTransformThrowsErrorTest.kt | 2 +- ...ferWithTransformThrowsErrorTestSnapshot.kt | 99 +++++-------------- 2 files changed, 25 insertions(+), 76 deletions(-) diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferWithTransformThrowsErrorTest.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferWithTransformThrowsErrorTest.kt index 66d3dd191..9780f12c9 100644 --- a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferWithTransformThrowsErrorTest.kt +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferWithTransformThrowsErrorTest.kt @@ -1,4 +1,4 @@ -package graphql.nadel.tests.next.fixtures.defer +package graphql.nadel.tests.next.fixtures.defer.transforms import graphql.nadel.NadelExecutionHints import graphql.nadel.tests.next.NadelIntegrationTest diff --git a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferWithTransformThrowsErrorTestSnapshot.kt b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferWithTransformThrowsErrorTestSnapshot.kt index 9c02782cf..e20d2f3fe 100644 --- a/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferWithTransformThrowsErrorTestSnapshot.kt +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferWithTransformThrowsErrorTestSnapshot.kt @@ -1,11 +1,13 @@ +// @formatter:off package graphql.nadel.tests.next.fixtures.defer.transforms import graphql.nadel.tests.next.ExpectedNadelResult import graphql.nadel.tests.next.ExpectedServiceCall import graphql.nadel.tests.next.TestSnapshot -import graphql.nadel.tests.next.fixtures.defer.DeferThrowsErrorTest -import graphql.nadel.tests.next.fixtures.defer.DeferWithTransformThrowsErrorTest import graphql.nadel.tests.next.listOfJsonStrings +import kotlin.Suppress +import kotlin.collections.List +import kotlin.collections.listOf private suspend fun main() { graphql.nadel.tests.next.update() @@ -19,20 +21,21 @@ private suspend fun main() { @Suppress("unused") public class DeferWithTransformThrowsErrorTestSnapshot : TestSnapshot() { override val calls: List = listOf( - ExpectedServiceCall( - service = "defer", - query = """ + ExpectedServiceCall( + service = "defer", + query = """ | { | defer { | hello | ... @defer(label: "slow-defer") { - | slow + | rename__slow__underlyingSlow: underlyingSlow + | __typename__rename__slow: __typename | } | } | } """.trimMargin(), - variables = " {}", - result = """ + variables = " {}", + result = """ | { | "data": { | "defer": { @@ -42,8 +45,8 @@ public class DeferWithTransformThrowsErrorTestSnapshot : TestSnapshot() { | "hasNext": true | } """.trimMargin(), - delayedResults = listOfJsonStrings( - """ + delayedResults = listOfJsonStrings( + """ | { | "hasNext": false, | "incremental": [ @@ -52,34 +55,17 @@ public class DeferWithTransformThrowsErrorTestSnapshot : TestSnapshot() { | "defer" | ], | "label": "slow-defer", - | "errors": [ - | { - | "message": "Exception while fetching data (/defer/slow) : An error occurred while fetching 'slow'", - | "locations": [ - | { - | "line": 5, - | "column": 7 - | } - | ], - | "path": [ - | "defer", - | "slow" - | ], - | "extensions": { - | "classification": "DataFetchingException" - | } - | } - | ], | "data": { - | "slow": null + | "rename__slow__underlyingSlow": null, + | "__typename__rename__slow": "DeferApi" | } | } | ] | } """.trimMargin(), + ), ), - ), - ) + ) /** * ```json @@ -89,31 +75,12 @@ public class DeferWithTransformThrowsErrorTestSnapshot : TestSnapshot() { * "hello": "helloString", * "slow": null * } - * }, - * "errors": [ - * { - * "message": "Exception while fetching data (/defer/slow) : An error occurred while - * fetching 'slow'", - * "locations": [ - * { - * "line": 5, - * "column": 7 - * } - * ], - * "path": [ - * "defer", - * "slow" - * ], - * "extensions": { - * "classification": "DataFetchingException" - * } - * } - * ] + * } * } * ``` */ override val result: ExpectedNadelResult = ExpectedNadelResult( - result = """ + result = """ | { | "data": { | "defer": { @@ -123,8 +90,8 @@ public class DeferWithTransformThrowsErrorTestSnapshot : TestSnapshot() { | "hasNext": true | } """.trimMargin(), - delayedResults = listOfJsonStrings( - """ + delayedResults = listOfJsonStrings( + """ | { | "hasNext": false, | "incremental": [ @@ -133,24 +100,6 @@ public class DeferWithTransformThrowsErrorTestSnapshot : TestSnapshot() { | "defer" | ], | "label": "slow-defer", - | "errors": [ - | { - | "message": "Exception while fetching data (/defer/slow) : An error occurred while fetching 'slow'", - | "locations": [ - | { - | "line": 5, - | "column": 7 - | } - | ], - | "path": [ - | "defer", - | "slow" - | ], - | "extensions": { - | "classification": "DataFetchingException" - | } - | } - | ], | "data": { | "slow": null | } @@ -158,6 +107,6 @@ public class DeferWithTransformThrowsErrorTestSnapshot : TestSnapshot() { | ] | } """.trimMargin(), - ), - ) -} \ No newline at end of file + ), + ) +}