Skip to content

Commit 8dfc4b3

Browse files
committed
Support both legacy and modern incremental delivery protocols, with a configuration. Defaults to the legacy one.
1 parent f05a860 commit 8dfc4b3

31 files changed

+2784
-610
lines changed

.github/workflows/defer-with-router-tests.yml renamed to .github/workflows/defer-integration-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: defer-router-tests
1+
name: defer-integration-tests
22

33
on:
44
schedule:

libraries/apollo-api/api/apollo-api.api

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,6 @@ public final class com/apollographql/apollo/api/CustomScalarAdapters$Builder {
426426
public final fun deferredFragmentIdentifiers (Ljava/util/Set;)Lcom/apollographql/apollo/api/CustomScalarAdapters$Builder;
427427
public final fun errors (Ljava/util/List;)Lcom/apollographql/apollo/api/CustomScalarAdapters$Builder;
428428
public final fun falseVariables (Ljava/util/Set;)Lcom/apollographql/apollo/api/CustomScalarAdapters$Builder;
429-
public final fun pendingResultIds (Ljava/util/Set;)Lcom/apollographql/apollo/api/CustomScalarAdapters$Builder;
430429
}
431430

432431
public final class com/apollographql/apollo/api/CustomScalarAdapters$Key : com/apollographql/apollo/api/ExecutionContext$Key {
@@ -714,6 +713,12 @@ public final class com/apollographql/apollo/api/ImmutableMapBuilder {
714713
public final fun put (Ljava/lang/Object;Ljava/lang/Object;)Lcom/apollographql/apollo/api/ImmutableMapBuilder;
715714
}
716715

716+
public final class com/apollographql/apollo/api/IncrementalResultIdentifierKt {
717+
public static final fun isPending (Ljava/util/Set;)Z
718+
public static final fun nonPending (Ljava/util/Set;)Ljava/util/Set;
719+
public static final fun pending (Ljava/util/Set;)Ljava/util/Set;
720+
}
721+
717722
public final class com/apollographql/apollo/api/InputObjectType : com/apollographql/apollo/api/CompiledNamedType {
718723
public fun <init> (Ljava/lang/String;)V
719724
}
@@ -928,7 +933,12 @@ public final class com/apollographql/apollo/api/http/ByteStringHttpBody : com/ap
928933

929934
public final class com/apollographql/apollo/api/http/DefaultHttpRequestComposer : com/apollographql/apollo/api/http/HttpRequestComposer {
930935
public static final field Companion Lcom/apollographql/apollo/api/http/DefaultHttpRequestComposer$Companion;
936+
public static final field HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20220824 Ljava/lang/String;
937+
public static final field HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20230621 Ljava/lang/String;
938+
public static final field HEADER_ACCEPT_VALUE_SUBSCRIPTIONS_1_0 Ljava/lang/String;
931939
public fun <init> (Ljava/lang/String;)V
940+
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
941+
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
932942
public fun compose (Lcom/apollographql/apollo/api/ApolloRequest;)Lcom/apollographql/apollo/api/http/HttpRequest;
933943
}
934944

libraries/apollo-api/api/apollo-api.klib.api

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -493,10 +493,18 @@ final class com.apollographql.apollo.api.http/ByteStringHttpBody : com.apollogra
493493

494494
final class com.apollographql.apollo.api.http/DefaultHttpRequestComposer : com.apollographql.apollo.api.http/HttpRequestComposer { // com.apollographql.apollo.api.http/DefaultHttpRequestComposer|null[0]
495495
constructor <init>(kotlin/String) // com.apollographql.apollo.api.http/DefaultHttpRequestComposer.<init>|<init>(kotlin.String){}[0]
496+
constructor <init>(kotlin/String, kotlin/String = ..., kotlin/String = ...) // com.apollographql.apollo.api.http/DefaultHttpRequestComposer.<init>|<init>(kotlin.String;kotlin.String;kotlin.String){}[0]
496497

497498
final fun <#A1: com.apollographql.apollo.api/Operation.Data> compose(com.apollographql.apollo.api/ApolloRequest<#A1>): com.apollographql.apollo.api.http/HttpRequest // com.apollographql.apollo.api.http/DefaultHttpRequestComposer.compose|compose(com.apollographql.apollo.api.ApolloRequest<0:0>){0§<com.apollographql.apollo.api.Operation.Data>}[0]
498499

499500
final object Companion { // com.apollographql.apollo.api.http/DefaultHttpRequestComposer.Companion|null[0]
501+
final const val HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20220824 // com.apollographql.apollo.api.http/DefaultHttpRequestComposer.Companion.HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20220824|{}HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20220824[0]
502+
final fun <get-HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20220824>(): kotlin/String // com.apollographql.apollo.api.http/DefaultHttpRequestComposer.Companion.HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20220824.<get-HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20220824>|<get-HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20220824>(){}[0]
503+
final const val HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20230621 // com.apollographql.apollo.api.http/DefaultHttpRequestComposer.Companion.HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20230621|{}HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20230621[0]
504+
final fun <get-HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20230621>(): kotlin/String // com.apollographql.apollo.api.http/DefaultHttpRequestComposer.Companion.HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20230621.<get-HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20230621>|<get-HEADER_ACCEPT_VALUE_QUERIES_AND_MUTATIONS_20230621>(){}[0]
505+
final const val HEADER_ACCEPT_VALUE_SUBSCRIPTIONS_1_0 // com.apollographql.apollo.api.http/DefaultHttpRequestComposer.Companion.HEADER_ACCEPT_VALUE_SUBSCRIPTIONS_1_0|{}HEADER_ACCEPT_VALUE_SUBSCRIPTIONS_1_0[0]
506+
final fun <get-HEADER_ACCEPT_VALUE_SUBSCRIPTIONS_1_0>(): kotlin/String // com.apollographql.apollo.api.http/DefaultHttpRequestComposer.Companion.HEADER_ACCEPT_VALUE_SUBSCRIPTIONS_1_0.<get-HEADER_ACCEPT_VALUE_SUBSCRIPTIONS_1_0>|<get-HEADER_ACCEPT_VALUE_SUBSCRIPTIONS_1_0>(){}[0]
507+
500508
final val HEADER_ACCEPT_NAME // com.apollographql.apollo.api.http/DefaultHttpRequestComposer.Companion.HEADER_ACCEPT_NAME|{}HEADER_ACCEPT_NAME[0]
501509
final fun <get-HEADER_ACCEPT_NAME>(): kotlin/String // com.apollographql.apollo.api.http/DefaultHttpRequestComposer.Companion.HEADER_ACCEPT_NAME.<get-HEADER_ACCEPT_NAME>|<get-HEADER_ACCEPT_NAME>(){}[0]
502510
final val HEADER_ACCEPT_VALUE_DEFER // com.apollographql.apollo.api.http/DefaultHttpRequestComposer.Companion.HEADER_ACCEPT_VALUE_DEFER|{}HEADER_ACCEPT_VALUE_DEFER[0]
@@ -938,7 +946,6 @@ final class com.apollographql.apollo.api/CustomScalarAdapters : com.apollographq
938946
final fun deferredFragmentIdentifiers(kotlin.collections/Set<com.apollographql.apollo.api/DeferredFragmentIdentifier>?): com.apollographql.apollo.api/CustomScalarAdapters.Builder // com.apollographql.apollo.api/CustomScalarAdapters.Builder.deferredFragmentIdentifiers|deferredFragmentIdentifiers(kotlin.collections.Set<com.apollographql.apollo.api.DeferredFragmentIdentifier>?){}[0]
939947
final fun errors(kotlin.collections/List<com.apollographql.apollo.api/Error>?): com.apollographql.apollo.api/CustomScalarAdapters.Builder // com.apollographql.apollo.api/CustomScalarAdapters.Builder.errors|errors(kotlin.collections.List<com.apollographql.apollo.api.Error>?){}[0]
940948
final fun falseVariables(kotlin.collections/Set<kotlin/String>?): com.apollographql.apollo.api/CustomScalarAdapters.Builder // com.apollographql.apollo.api/CustomScalarAdapters.Builder.falseVariables|falseVariables(kotlin.collections.Set<kotlin.String>?){}[0]
941-
final fun pendingResultIds(kotlin.collections/Set<com.apollographql.apollo.api/DeferredFragmentIdentifier>?): com.apollographql.apollo.api/CustomScalarAdapters.Builder // com.apollographql.apollo.api/CustomScalarAdapters.Builder.pendingResultIds|pendingResultIds(kotlin.collections.Set<com.apollographql.apollo.api.DeferredFragmentIdentifier>?){}[0]
942949
}
943950

944951
final object Key : com.apollographql.apollo.api/ExecutionContext.Key<com.apollographql.apollo.api/CustomScalarAdapters> { // com.apollographql.apollo.api/CustomScalarAdapters.Key|null[0]
@@ -1430,6 +1437,9 @@ final fun (com.apollographql.apollo.api/Operation.Data).com.apollographql.apollo
14301437
final fun (kotlin.collections/List<com.apollographql.apollo.api.http/HttpHeader>).com.apollographql.apollo.api.http/get(kotlin/String): kotlin/String? // com.apollographql.apollo.api.http/get|get@kotlin.collections.List<com.apollographql.apollo.api.http.HttpHeader>(kotlin.String){}[0]
14311438
final fun (kotlin.collections/List<com.apollographql.apollo.api.http/HttpHeader>).com.apollographql.apollo.api.http/valueOf(kotlin/String): kotlin/String? // com.apollographql.apollo.api.http/valueOf|valueOf@kotlin.collections.List<com.apollographql.apollo.api.http.HttpHeader>(kotlin.String){}[0]
14321439
final fun (kotlin.collections/Map<kotlin/String, kotlin/Any?>).com.apollographql.apollo.api.json/jsonReader(): com.apollographql.apollo.api.json/JsonReader // com.apollographql.apollo.api.json/jsonReader|jsonReader@kotlin.collections.Map<kotlin.String,kotlin.Any?>(){}[0]
1440+
final fun (kotlin.collections/Set<com.apollographql.apollo.api/DeferredFragmentIdentifier>).com.apollographql.apollo.api/isPending(): kotlin/Boolean // com.apollographql.apollo.api/isPending|isPending@kotlin.collections.Set<com.apollographql.apollo.api.DeferredFragmentIdentifier>(){}[0]
1441+
final fun (kotlin.collections/Set<com.apollographql.apollo.api/DeferredFragmentIdentifier>).com.apollographql.apollo.api/nonPending(): kotlin.collections/Set<com.apollographql.apollo.api/DeferredFragmentIdentifier> // com.apollographql.apollo.api/nonPending|nonPending@kotlin.collections.Set<com.apollographql.apollo.api.DeferredFragmentIdentifier>(){}[0]
1442+
final fun (kotlin.collections/Set<com.apollographql.apollo.api/DeferredFragmentIdentifier>).com.apollographql.apollo.api/pending(): kotlin.collections/Set<com.apollographql.apollo.api/DeferredFragmentIdentifier> // com.apollographql.apollo.api/pending|pending@kotlin.collections.Set<com.apollographql.apollo.api.DeferredFragmentIdentifier>(){}[0]
14331443
final fun (kotlin/String).com.apollographql.apollo.api.http.internal/urlDecode(): kotlin/String // com.apollographql.apollo.api.http.internal/urlDecode|urlDecode@kotlin.String(){}[0]
14341444
final fun (kotlin/String).com.apollographql.apollo.api.http.internal/urlEncode(): kotlin/String // com.apollographql.apollo.api.http.internal/urlEncode|urlEncode@kotlin.String(){}[0]
14351445
final fun (okio/BufferedSource).com.apollographql.apollo.api.json/jsonReader(): com.apollographql.apollo.api.json/JsonReader // com.apollographql.apollo.api.json/jsonReader|jsonReader@okio.BufferedSource(){}[0]

libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/BooleanExpression.kt

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -67,30 +67,37 @@ internal fun <T : Any> BooleanExpression<T>.evaluate(block: (T) -> Boolean): Boo
6767
fun BooleanExpression<BTerm>.evaluate(
6868
variables: Set<String>?,
6969
typename: String?,
70-
pendingResultIds: Set<IncrementalResultIdentifier>?,
70+
deferredFragmentIdentifiers: IncrementalResultIdentifiers?,
7171
path: List<Any>?,
7272
): Boolean {
7373
// Remove "data" from the path
7474
val croppedPath = path?.drop(1)
7575
return evaluate {
7676
when (it) {
7777
is BVariable -> !(variables?.contains(it.name) ?: false)
78-
is BLabel -> !isDeferredFragmentPending(pendingResultIds, croppedPath!!, it.label)
78+
is BLabel -> shouldParseFragment(deferredFragmentIdentifiers = deferredFragmentIdentifiers, path = croppedPath!!, label = it.label)
7979
is BPossibleTypes -> it.possibleTypes.contains(typename)
8080
}
8181
}
8282
}
8383

84-
private fun isDeferredFragmentPending(
85-
pendingResultIds: Set<IncrementalResultIdentifier>?,
84+
private fun shouldParseFragment(
85+
deferredFragmentIdentifiers: IncrementalResultIdentifiers?,
8686
path: List<Any>,
8787
label: String?,
8888
): Boolean {
89-
if (pendingResultIds == null) {
89+
if (deferredFragmentIdentifiers == null) {
9090
// By default, parse all deferred fragments - this is the case when parsing from the normalized cache.
91-
return false
91+
return true
92+
}
93+
val identifier = IncrementalResultIdentifier(path, label)
94+
return if (deferredFragmentIdentifiers.isPending()) {
95+
// Modern protocol: parse fragments that are _not_ pending
96+
!deferredFragmentIdentifiers.contains(identifier)
97+
} else {
98+
// Legacy 20220824 protocol: parse fragments that have been merged
99+
deferredFragmentIdentifiers.contains(identifier)
92100
}
93-
return pendingResultIds.contains(IncrementalResultIdentifier(path, label))
94101
}
95102

96103
/**

libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/CustomScalarAdapters.kt

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ class CustomScalarAdapters private constructor(
1919
@JvmField
2020
val falseVariables: Set<String>?,
2121
/**
22-
* Pending incremental result identifiers used to determine whether the parser must parse deferred fragments
22+
* Incremental result identifiers used to determine whether the parser must parse deferred fragments
2323
*/
2424
@JvmField
25-
val deferredFragmentIdentifiers: Set<IncrementalResultIdentifier>?,
25+
val deferredFragmentIdentifiers: IncrementalResultIdentifiers?,
2626
/**
2727
* Errors to use with @catch
2828
*/
@@ -125,26 +125,21 @@ class CustomScalarAdapters private constructor(
125125
fun newBuilder(): Builder {
126126
return Builder().addAll(this)
127127
.falseVariables(falseVariables)
128-
.pendingResultIds(deferredFragmentIdentifiers)
128+
.deferredFragmentIdentifiers(deferredFragmentIdentifiers)
129129
}
130130

131131
class Builder {
132132
private val adaptersMap: MutableMap<String, Adapter<*>> = mutableMapOf()
133133
private var falseVariables: Set<String>? = null
134-
private var pendingResultIds: Set<IncrementalResultIdentifier>? = null
134+
private var deferredFragmentIdentifiers: IncrementalResultIdentifiers? = null
135135
private var errors: List<Error>? = null
136136

137137
fun falseVariables(falseVariables: Set<String>?) = apply {
138138
this.falseVariables = falseVariables
139139
}
140140

141-
@Deprecated("Use pendingResultIds instead", ReplaceWith("pendingResultIds(pendingResultIds = deferredFragmentIdentifiers)"))
142-
fun deferredFragmentIdentifiers(deferredFragmentIdentifiers: Set<IncrementalResultIdentifier>?) = apply {
143-
this.pendingResultIds = deferredFragmentIdentifiers
144-
}
145-
146-
fun pendingResultIds(pendingResultIds: Set<IncrementalResultIdentifier>?) = apply {
147-
this.pendingResultIds = pendingResultIds
141+
fun deferredFragmentIdentifiers(deferredFragmentIdentifiers: IncrementalResultIdentifiers?) = apply {
142+
this.deferredFragmentIdentifiers = deferredFragmentIdentifiers
148143
}
149144

150145
fun errors(errors: List<Error>?) = apply {
@@ -178,7 +173,7 @@ class CustomScalarAdapters private constructor(
178173
return CustomScalarAdapters(
179174
adaptersMap,
180175
falseVariables,
181-
pendingResultIds,
176+
deferredFragmentIdentifiers,
182177
errors,
183178
)
184179
}

libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Executables.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,12 @@ fun <D : Executable.Data> Executable<D>.parseData(
7171
jsonReader: JsonReader,
7272
customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty,
7373
falseVariables: Set<String>? = null,
74-
deferredFragmentIds: Set<IncrementalResultIdentifier>? = null,
74+
deferredFragmentIds: IncrementalResultIdentifiers? = null,
7575
errors: List<Error>? = null,
7676
): D? {
7777
val customScalarAdapters1 = customScalarAdapters.newBuilder()
7878
.falseVariables(falseVariables)
79-
.pendingResultIds(pendingResultIds = deferredFragmentIds)
79+
.deferredFragmentIdentifiers(deferredFragmentIds)
8080
.errors(errors)
8181
.build()
8282
return adapter().nullable().fromJson(jsonReader, customScalarAdapters1)
Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,41 @@
11
package com.apollographql.apollo.api
22

3+
import com.apollographql.apollo.annotations.ApolloInternal
4+
35
data class DeferredFragmentIdentifier(
46
/**
57
* Path of the fragment in the overall JSON response. The elements can either be Strings (names) or Integers (array indices).
68
*/
79
val path: List<Any>,
810
val label: String?,
9-
)
11+
) {
12+
internal companion object {
13+
/**
14+
* Special identifier to signal that the identifiers are pending, as in the modern version of the protocol.
15+
*/
16+
internal val Pending = DeferredFragmentIdentifier(emptyList(), "__pending")
17+
}
18+
}
1019

1120
/**
1221
* Identifies an incremental result.
13-
* [DeferredFragmentIdentifier] is kept to not break the API/ABI, but this alias is more descriptive of its purpose.
22+
* [DeferredFragmentIdentifier] is kept to preserve the API/ABI, but this alias is more descriptive of its purpose.
1423
*/
1524
typealias IncrementalResultIdentifier = DeferredFragmentIdentifier
25+
26+
typealias IncrementalResultIdentifiers = Set<IncrementalResultIdentifier>
27+
28+
@ApolloInternal
29+
fun IncrementalResultIdentifiers.isPending(): Boolean {
30+
return any { it === DeferredFragmentIdentifier.Pending }
31+
}
32+
33+
@ApolloInternal
34+
fun IncrementalResultIdentifiers.pending(): IncrementalResultIdentifiers {
35+
return this + DeferredFragmentIdentifier.Pending
36+
}
37+
38+
@ApolloInternal
39+
fun IncrementalResultIdentifiers.nonPending(): IncrementalResultIdentifiers {
40+
return filter { it !== DeferredFragmentIdentifier.Pending }.toSet()
41+
}

libraries/apollo-api/src/commonMain/kotlin/com/apollographql/apollo/api/Operations.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ fun <D : Operation.Data> Operation<D>.composeJsonRequest(
7070
fun <D : Operation.Data> Operation<D>.parseJsonResponse(
7171
jsonReader: JsonReader,
7272
customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty,
73-
deferredFragmentIdentifiers: Set<IncrementalResultIdentifier>? = null,
73+
deferredFragmentIdentifiers: IncrementalResultIdentifiers? = null,
7474
): ApolloResponse<D> {
7575
return jsonReader.use {
7676
ResponseParser.parse(
@@ -103,7 +103,7 @@ fun <D : Operation.Data> Operation<D>.parseResponse(
103103
jsonReader: JsonReader,
104104
requestUuid: Uuid? = null,
105105
customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty,
106-
deferredFragmentIdentifiers: Set<IncrementalResultIdentifier>? = null,
106+
deferredFragmentIdentifiers: IncrementalResultIdentifiers? = null,
107107
): ApolloResponse<D> {
108108
return try {
109109
ResponseParser.parse(
@@ -177,7 +177,7 @@ fun <D : Operation.Data> JsonReader.toApolloResponse(
177177
operation: Operation<D>,
178178
requestUuid: Uuid? = null,
179179
customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty,
180-
deferredFragmentIdentifiers: Set<IncrementalResultIdentifier>? = null,
180+
deferredFragmentIdentifiers: IncrementalResultIdentifiers? = null,
181181
): ApolloResponse<D> {
182182
return use {
183183
try {
@@ -213,7 +213,7 @@ fun <D : Operation.Data> JsonReader.parseResponse(
213213
operation: Operation<D>,
214214
requestUuid: Uuid? = null,
215215
customScalarAdapters: CustomScalarAdapters = CustomScalarAdapters.Empty,
216-
deferredFragmentIdentifiers: Set<IncrementalResultIdentifier>? = null,
216+
deferredFragmentIdentifiers: IncrementalResultIdentifiers? = null,
217217
): ApolloResponse<D> {
218218
return try {
219219
ResponseParser.parse(

0 commit comments

Comments
 (0)