diff --git a/lib/src/main/java/graphql/nadel/NextgenEngine.kt b/lib/src/main/java/graphql/nadel/NextgenEngine.kt index c5b065b8e..ee158d2a2 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 @@ -25,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 @@ -62,6 +64,8 @@ import kotlinx.coroutines.awaitAll 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 @@ -291,6 +295,29 @@ internal class NextgenEngine( executionHydrationDetails = executionContext.hydrationDetails, ) } + if (result is NadelIncrementalServiceExecutionResult) { + executionContext.incrementalResultSupport.defer( + result.incrementalItemPublisher + .asFlow() + .onEach {delayedIncrementalResult -> + // Transform + delayedIncrementalResult.incremental + ?.filterIsInstance() + ?.forEach {deferPayload -> + resultTransformer + .transform( + executionContext = executionContext, + serviceExecutionContext = serviceExecutionContext, + executionPlan = executionPlan, + artificialFields = queryTransform.artificialFields, + overallToUnderlyingFields = queryTransform.overallToUnderlyingFields, + service = service, + result = result, + deferPayload = deferPayload, + ) } + } + ) + } val transformedResult: ServiceExecutionResult = when { topLevelField.name.startsWith("__") -> result else -> timer.time(step = RootStep.ResultTransforming) { @@ -381,18 +408,15 @@ internal class NextgenEngine( ) } - if (serviceExecResult is NadelIncrementalServiceExecutionResult) { - executionContext.incrementalResultSupport.defer( - serviceExecResult.incrementalItemPublisher.asFlow() - ) + 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/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/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/query/NadelQueryPath.kt b/lib/src/main/java/graphql/nadel/engine/transform/query/NadelQueryPath.kt index 7843415d7..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 @@ -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 @@ -23,6 +25,21 @@ data class NadelQueryPath(val segments: List) { return segments.last() } + fun startsWith(prefix: List): Boolean { + if (prefix.size > segments.size) return false + for (i in 0..): NadelQueryPath { + if (this.startsWith(prefix)) { + return NadelQueryPath(segments.drop(prefix.size)) + } + return assertShouldNeverHappen("NadelQueryPath did not start with prefix") + } + 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 1fa73da9e..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 @@ -1,11 +1,15 @@ package graphql.nadel.engine.transform.result +import graphql.GraphQLError +import graphql.incremental.DeferPayload +import graphql.nadel.NadelIncrementalServiceExecutionResult import graphql.nadel.Service import graphql.nadel.ServiceExecutionResult 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.util.JsonMap import graphql.nadel.engine.util.MutableJsonMap @@ -24,22 +28,70 @@ internal class NadelResultTransformer(private val executionBlueprint: NadelOvera artificialFields: List, overallToUnderlyingFields: Map>, service: Service, - result: ServiceExecutionResult, + result: ServiceExecutionResult ): ServiceExecutionResult { val nodes = JsonNodes(result.data) + val instructions = getMutationInstructions( + executionContext, + serviceExecutionContext, + executionPlan, + artificialFields, + overallToUnderlyingFields, + service, + result, + nodes + ) + mutate(result, instructions) + return result + } - val deferredInstructions = ArrayList>>() + suspend fun transform( + executionContext: NadelExecutionContext, + serviceExecutionContext: NadelServiceExecutionContext, + executionPlan: NadelExecutionPlan, + artificialFields: List, + overallToUnderlyingFields: Map>, + service: Service, + 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 { 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) { - deferredInstructions.add( + asyncInstructions.add( async { step.transform.getResultInstructions( executionContext, @@ -50,29 +102,24 @@ internal class NadelResultTransformer(private val executionBlueprint: NadelOvera underlyingFields.first().parent, result, step.state, - nodes, + nodes ) - }, + } ) } } - deferredInstructions.add( + asyncInstructions.add( async { getRemoveArtificialFieldInstructions(artificialFields, nodes) - }, + } ) } - val instructions = deferredInstructions - .awaitAll() - .flatten() - - mutate(result, instructions) - - return result + return asyncInstructions.awaitAll().flatten() } + private fun mutate(result: ServiceExecutionResult, instructions: List) { instructions.forEach { transformation -> when (transformation) { @@ -83,6 +130,16 @@ internal class NadelResultTransformer(private val executionBlueprint: NadelOvera } } + 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 -> processGraphQLErrors(transformation, result.errors) + } + } + } + private fun process( instruction: NadelResultInstruction.Set, ) { @@ -110,6 +167,13 @@ internal class NadelResultTransformer(private val executionBlueprint: NadelOvera mutableErrors.add(newError) } + private fun processGraphQLErrors( + instruction: NadelResultInstruction.AddError, + errors: List?, + ) { + errors?.asMutable()?.add(instruction.error) + } + private fun getRemoveArtificialFieldInstructions( artificialFields: List, nodes: JsonNodes, 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..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 @@ -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 @@ -11,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 { /** @@ -19,12 +24,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) } } } @@ -34,18 +39,27 @@ interface JsonNodes { */ class NadelCachingJsonNodes( private val data: JsonMap, + 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>() 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.segments)) { + getNodesAt(rootNode, queryPath.removePrefix(pathPrefix.segments), flatten) + } else { + emptyList() + } } /** * Extracts the nodes at the given query selection path. */ private fun getNodesAt(rootNode: JsonNode, queryPath: NadelQueryPath, flatten: Boolean = false): List { + var queue = listOf(rootNode) // todo work backwards here instead of forwards 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..a19d3290f 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,14 +367,6 @@ fun ExecutionIdProvider.provide(executionInput: ExecutionInput): ExecutionId { return provide(executionInput.query, executionInput.operationName, executionInput.context) } -fun ServiceExecutionResult.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(), @@ -381,16 +375,6 @@ fun newServiceExecutionResult( return NadelServiceExecutionResultImpl(data, errors, extensions) } -fun newServiceExecutionResult( - error: GraphQLError, -): ServiceExecutionResult { - return newServiceExecutionResult( - errors = mutableListOf( - error.toSpecification(), - ), - ) -} - fun newExecutionResult( data: Any? = null, error: GraphQLError, 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..3f8fc8269 --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/DeferredFieldIsRenamedTestSnapshot.kt @@ -0,0 +1,109 @@ +// @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 + | ... @defer { + | rename__overallString__underlyingString: underlyingString + | __typename__rename__overallString: __typename + | } + | } + | } + """.trimMargin(), + variables = "{}", + result = """ + | { + | "data": { + | "defer": { + | "hello": "hello there" + | } + | }, + | "hasNext": true + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [ + | "defer" + | ], + | "data": { + | "overallString": "string for the deferred renamed field" + | } + | } + | ] + | } + """.trimMargin(), + ), + ), + ) + + /** + * ```json + * { + * "data": { + * "defer": { + * "hello": "hello there", + * "overallString": "string for the deferred renamed field" + * } + * } + * } + * ``` + */ + override val result: ExpectedNadelResult = ExpectedNadelResult( + result = """ + | { + | "data": { + | "defer": { + | "hello": "hello there" + | } + | }, + | "hasNext": true + | } + """.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/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/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..0367d8d9a --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenameTransformsInsideAndOutsideDeferSnapshot.kt @@ -0,0 +1,111 @@ +// @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 + | ... @defer { + | 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" + | } + | }, + | "hasNext": true + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [ + | "defer" + | ], + | "data": { + | "slowRenamedString": "this is the slow string (deferred)" + | } + | } + | ] + | } + """.trimMargin(), + ), + ), + ) + + /** + * ```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)" + | } + | }, + | "hasNext": true + | } + """.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/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..91e4c7451 --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleRenamedFieldsAreDeferredTogetherSnapshot.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 MultipleRenamedFieldsAreDeferredTogetherSnapshot : TestSnapshot() { + override val calls: List = listOf( + ExpectedServiceCall( + service = "defer", + query = """ + | { + | defer { + | hello + | ... @defer { + | rename__overallString__underlyingString: underlyingString + | __typename__rename__overallString: __typename + | rename__overallString2__underlyingString2: underlyingString2 + | __typename__rename__overallString2: __typename + | } + | } + | } + """.trimMargin(), + variables = "{}", + result = """ + | { + | "data": { + | "defer": { + | "hello": "hello there" + | } + | }, + | "hasNext": true + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [ + | "defer" + | ], + | "data": { + | "overallString": "deferred string 1", + | "overallString2": "deferred string 2" + | } + | } + | ] + | } + """.trimMargin(), + ), + ), + ) + + /** + * ```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" + | } + | }, + | "hasNext": true + | } + """.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/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..de6be970b --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/MultipleSeparateDefersWithTransformsSnapshot.kt @@ -0,0 +1,146 @@ +// @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 + | ... @defer { + | rename__slowRenamedString__slowString: slowString + | __typename__rename__slowRenamedString: __typename + | } + | ... @defer { + | 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" + | } + | }, + | "hasNext": true + | } + """.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(), + ), + ), + ) + + /** + * ```json + * { + * "data": { + * "defer": { + * "fastRenamedString": "this is the fast string (not deferred)", + * "anotherSlowRenamedString": "this is the other slow string (deferred)", + * "slowRenamedString": "this is the slow string (deferred)" + * } + * } + * } + * ``` + */ + override val result: ExpectedNadelResult = ExpectedNadelResult( + result = """ + | { + | "data": { + | "defer": { + | "fastRenamedString": "this is the fast string (not deferred)" + | } + | }, + | "hasNext": true + | } + """.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/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..99889e1ba --- /dev/null +++ b/test/src/test/kotlin/graphql/nadel/tests/next/fixtures/defer/transforms/RenameInnerFieldInsideDeferSnapshot.kt @@ -0,0 +1,111 @@ +// @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": { + | "firstName": "Steven" + | } + | } + | } + | ] + | } + """.trimMargin(), + ), + ), + ) + + /** + * ```json + * { + * "data": { + * "defer": { + * "user": { + * "firstName": "Steven" + * } + * } + * } + * } + * ``` + */ + override val result: ExpectedNadelResult = ExpectedNadelResult( + result = """ + | { + | "data": { + | "defer": {} + | }, + | "hasNext": true + | } + """.trimMargin(), + delayedResults = listOfJsonStrings( + """ + | { + | "hasNext": false, + | "incremental": [ + | { + | "path": [ + | "defer" + | ], + | "data": { + | "user": { + | "firstName": "Steven" + | } + | } + | } + | ] + | } + """.trimMargin(), + ), + ) +} 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(), + ), + ) +}