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 ecd4ee1a4..e5688dbd1 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 @@ -34,6 +34,8 @@ import graphql.nadel.engine.util.toBuilder import graphql.nadel.engine.util.toGraphQLError import graphql.nadel.engine.util.unwrapNonNull import graphql.nadel.hooks.NadelExecutionHooks +import graphql.nadel.result.NadelResultPath +import graphql.nadel.result.NadelResultPathSegment import graphql.normalized.ExecutableNormalizedField import graphql.schema.FieldCoordinates import kotlinx.coroutines.async @@ -263,13 +265,13 @@ internal class NadelHydrationTransform( overallField.resultKey to data?.value, ), ) - .path(parentPath) + .path(parentPath.toRawPath()) .errors( hydration.errors .map { toGraphQLError( raw = it, - path = path, + path = path.toRawPath(), ) }, ) diff --git a/lib/src/main/java/graphql/nadel/engine/transform/result/json/NadelJsonNodeIterator.kt b/lib/src/main/java/graphql/nadel/engine/transform/result/json/NadelJsonNodeIterator.kt index 912ed9e49..d9984c4c8 100644 --- a/lib/src/main/java/graphql/nadel/engine/transform/result/json/NadelJsonNodeIterator.kt +++ b/lib/src/main/java/graphql/nadel/engine/transform/result/json/NadelJsonNodeIterator.kt @@ -6,6 +6,7 @@ import graphql.nadel.engine.transform.result.json.NadelEphemeralJsonNode.Compani import graphql.nadel.engine.transform.result.json.NadelEphemeralJsonNode.Companion.component3 import graphql.nadel.engine.util.AnyList import graphql.nadel.engine.util.AnyMap +import graphql.nadel.result.NadelResultPath import graphql.nadel.result.NadelResultPathSegment /** @@ -56,12 +57,12 @@ internal class NadelIteratingJsonNodes( */ internal abstract class NadelEphemeralJsonNode { abstract val queryPath: List - abstract val resultPath: List + abstract val resultPath: NadelResultPath abstract val value: Any? companion object { operator fun NadelEphemeralJsonNode.component1(): List = queryPath - operator fun NadelEphemeralJsonNode.component2(): List = resultPath + operator fun NadelEphemeralJsonNode.component2(): NadelResultPath = resultPath operator fun NadelEphemeralJsonNode.component3(): Any? = value } } @@ -113,7 +114,7 @@ internal class NadelJsonNodeIterator( private val ephemeralJsonNode = object : NadelEphemeralJsonNode() { override val queryPath get() = currentQueryPathSegments - override val resultPath get() = currentResultPathSegments + override val resultPath get() = NadelResultPath(currentResultPathSegments) override var value: Any? = NONE } diff --git a/lib/src/main/java/graphql/nadel/result/NadelResultPath.kt b/lib/src/main/java/graphql/nadel/result/NadelResultPath.kt new file mode 100644 index 000000000..f9115bcc8 --- /dev/null +++ b/lib/src/main/java/graphql/nadel/result/NadelResultPath.kt @@ -0,0 +1,31 @@ +package graphql.nadel.result + +@JvmInline +internal value class NadelResultPath( + val value: List, +) { + operator fun plus(key: String): NadelResultPath { + return NadelResultPath(value + NadelResultPathSegment.Object(key)) + } + + operator fun plus(key: Int): NadelResultPath { + return NadelResultPath(value + NadelResultPathSegment.Array(key)) + } + + fun clone(): NadelResultPath { + return NadelResultPath(value = value.toList()) + } + + fun toRawPath(): List { + return value.map { + when (it) { + is NadelResultPathSegment.Array -> it.index + is NadelResultPathSegment.Object -> it.key + } + } + } + + companion object { + val empty = NadelResultPath(value = emptyList()) + } +} diff --git a/lib/src/main/java/graphql/nadel/result/NadelResultPathBuilder.kt b/lib/src/main/java/graphql/nadel/result/NadelResultPathBuilder.kt index 7ffaebdbd..b05db228e 100644 --- a/lib/src/main/java/graphql/nadel/result/NadelResultPathBuilder.kt +++ b/lib/src/main/java/graphql/nadel/result/NadelResultPathBuilder.kt @@ -3,7 +3,7 @@ package graphql.nadel.result internal interface NadelResultPathBuilder { fun add(key: String): NadelResultPathBuilder fun add(index: Int): NadelResultPathBuilder - fun build(): List + fun build(): NadelResultPath companion object { operator fun invoke( @@ -22,14 +22,18 @@ internal interface NadelResultPathBuilder { return this } - override fun build(): List { - return segments + override fun build(): NadelResultPath { + return NadelResultPath(segments) } } } } } +internal fun NadelResultPath.toBuilder(): NadelResultPathBuilder { + return NadelResultPathBuilder(value) +} + internal fun List.toBuilder(): NadelResultPathBuilder { return NadelResultPathBuilder(this) } diff --git a/lib/src/main/java/graphql/nadel/result/NadelResultTracker.kt b/lib/src/main/java/graphql/nadel/result/NadelResultTracker.kt index a593fc88a..3026387bb 100644 --- a/lib/src/main/java/graphql/nadel/result/NadelResultTracker.kt +++ b/lib/src/main/java/graphql/nadel/result/NadelResultTracker.kt @@ -30,7 +30,7 @@ internal class NadelResultTracker { suspend fun getResultPath( queryPath: NadelQueryPath, node: JsonNode, - ): List? { + ): NadelResultPath? { val result = result.await() val data = result.toSpecification()["data"] @@ -38,7 +38,7 @@ internal class NadelResultTracker { for (ephemeralNode in jsonNodeIterator) { if (ephemeralNode.queryPath.size == queryPath.segments.size && ephemeralNode.value === node.value) { // Clone because underlying values are ephemeral too - return ephemeralNode.resultPath.toList() + return ephemeralNode.resultPath.clone() } } diff --git a/lib/src/test/kotlin/graphql/nadel/engine/transform/result/json/NadelJsonNodeIteratorTest.kt b/lib/src/test/kotlin/graphql/nadel/engine/transform/result/json/NadelJsonNodeIteratorTest.kt index c0b2adff7..f80de6610 100644 --- a/lib/src/test/kotlin/graphql/nadel/engine/transform/result/json/NadelJsonNodeIteratorTest.kt +++ b/lib/src/test/kotlin/graphql/nadel/engine/transform/result/json/NadelJsonNodeIteratorTest.kt @@ -4,20 +4,20 @@ import com.fasterxml.jackson.module.kotlin.readValue import graphql.nadel.engine.transform.query.NadelQueryPath import graphql.nadel.engine.util.JsonMap import graphql.nadel.jsonObjectMapper +import graphql.nadel.result.NadelResultPath import graphql.nadel.result.NadelResultPathBuilder -import graphql.nadel.result.NadelResultPathSegment import org.junit.jupiter.api.Test import kotlin.test.assertTrue class NadelJsonNodeIteratorTest { private data class TraversedJsonNode( override val queryPath: List, - override val resultPath: List, + override val resultPath: NadelResultPath, override val value: Any?, ) : NadelEphemeralJsonNode() { constructor(other: NadelEphemeralJsonNode) : this( queryPath = other.queryPath.toList(), - resultPath = other.resultPath.toList(), + resultPath = other.resultPath.clone(), value = other.value, ) } @@ -45,7 +45,7 @@ class NadelJsonNodeIteratorTest { val expectedTraversals = listOf( TraversedJsonNode( queryPath = emptyList(), - resultPath = emptyList(), + resultPath = NadelResultPath.empty, value = root, ), TraversedJsonNode( @@ -123,7 +123,7 @@ class NadelJsonNodeIteratorTest { val expectedTraversals = listOf( TraversedJsonNode( queryPath = emptyList(), - resultPath = emptyList(), + resultPath = NadelResultPath.empty, value = root, ), TraversedJsonNode( diff --git a/lib/src/test/kotlin/graphql/nadel/result/NadelResultTrackerTest.kt b/lib/src/test/kotlin/graphql/nadel/result/NadelResultTrackerTest.kt index a25c1deae..fb4745683 100644 --- a/lib/src/test/kotlin/graphql/nadel/result/NadelResultTrackerTest.kt +++ b/lib/src/test/kotlin/graphql/nadel/result/NadelResultTrackerTest.kt @@ -188,6 +188,7 @@ class NadelResultTrackerTest { if (value is AnyList || value is AnyMap) { val queryPath = NadelQueryPath( path + .value .filterIsInstance() .map(NadelResultPathSegment.Object::key), ) @@ -379,6 +380,7 @@ class NadelResultTrackerTest { if (value is AnyList || value is AnyMap) { val queryPath = NadelQueryPath( path + .value .filterIsInstance() .map(NadelResultPathSegment.Object::key), ) @@ -439,7 +441,7 @@ class NadelResultTrackerTest { val data = result.getData() // When - val visited = mutableListOf, Any?>>() + val visited = mutableListOf>() JsonNode(data).dfs { path, value -> visited.add(path to value) } @@ -529,8 +531,8 @@ class NadelResultTrackerTest { * It's much easier to reason the logic in this recursive function. */ private suspend fun JsonNode.dfs( - path: List = emptyList(), - onConsume: suspend (List, Any?) -> Unit, + path: NadelResultPath = NadelResultPath.empty, + onConsume: suspend (NadelResultPath, Any?) -> Unit, ) { onConsume(path, value) @@ -545,6 +547,12 @@ class NadelResultTrackerTest { } } + private fun JsonNode.getAt( + path: NadelResultPath, + ): Any? { + return getAt(path.value) + } + private fun JsonNode.getAt( path: List, ): Any? {