Skip to content

Commit 34dc055

Browse files
authored
Add ApolloInterceptor.InsertionPoint to control where the interceptors are added (#6761)
* fix typo * Add ApolloInterceptor.InsertionPoint * update ApiDump * Apply suggestion from @martinbonnin
1 parent 42855c9 commit 34dc055

File tree

8 files changed

+149
-17
lines changed

8 files changed

+149
-17
lines changed

libraries/apollo-compiler/src/main/kotlin/com/apollographql/apollo/compiler/codegen/LayoutImpl.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ internal class LayoutImpl(
4646
* Make it possible to support several types with different cases. Example:
4747
*
4848
* ```graphql
49-
* type URL @targetName(newName: "Url1")
49+
* type URL @targetName(name: "Url1")
5050
* type Url
5151
* type url
5252
* ```

libraries/apollo-runtime/api/android/apollo-runtime.api

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ public final class com/apollographql/apollo/ApolloClient$Builder : com/apollogra
7373
public synthetic fun addHttpHeader (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
7474
public final fun addHttpInterceptor (Lcom/apollographql/apollo/network/http/HttpInterceptor;)Lcom/apollographql/apollo/ApolloClient$Builder;
7575
public final fun addInterceptor (Lcom/apollographql/apollo/interceptor/ApolloInterceptor;)Lcom/apollographql/apollo/ApolloClient$Builder;
76+
public final fun addInterceptor (Lcom/apollographql/apollo/interceptor/ApolloInterceptor;Lcom/apollographql/apollo/interceptor/ApolloInterceptor$InsertionPoint;)Lcom/apollographql/apollo/ApolloClient$Builder;
77+
public static synthetic fun addInterceptor$default (Lcom/apollographql/apollo/ApolloClient$Builder;Lcom/apollographql/apollo/interceptor/ApolloInterceptor;Lcom/apollographql/apollo/interceptor/ApolloInterceptor$InsertionPoint;ILjava/lang/Object;)Lcom/apollographql/apollo/ApolloClient$Builder;
7678
public final fun addInterceptors (Ljava/util/List;)Lcom/apollographql/apollo/ApolloClient$Builder;
7779
public final fun autoPersistedQueries ()Lcom/apollographql/apollo/ApolloClient$Builder;
7880
public final fun autoPersistedQueries (Lcom/apollographql/apollo/api/http/HttpMethod;)Lcom/apollographql/apollo/ApolloClient$Builder;
@@ -203,6 +205,16 @@ public abstract interface class com/apollographql/apollo/interceptor/ApolloInter
203205
public abstract fun intercept (Lcom/apollographql/apollo/api/ApolloRequest;Lcom/apollographql/apollo/interceptor/ApolloInterceptorChain;)Lkotlinx/coroutines/flow/Flow;
204206
}
205207

208+
public final class com/apollographql/apollo/interceptor/ApolloInterceptor$InsertionPoint : java/lang/Enum {
209+
public static final field BeforeAutoPersistedQueries Lcom/apollographql/apollo/interceptor/ApolloInterceptor$InsertionPoint;
210+
public static final field BeforeCache Lcom/apollographql/apollo/interceptor/ApolloInterceptor$InsertionPoint;
211+
public static final field BeforeNetwork Lcom/apollographql/apollo/interceptor/ApolloInterceptor$InsertionPoint;
212+
public static final field BeforeRetryOnError Lcom/apollographql/apollo/interceptor/ApolloInterceptor$InsertionPoint;
213+
public static fun getEntries ()Lkotlin/enums/EnumEntries;
214+
public static fun valueOf (Ljava/lang/String;)Lcom/apollographql/apollo/interceptor/ApolloInterceptor$InsertionPoint;
215+
public static fun values ()[Lcom/apollographql/apollo/interceptor/ApolloInterceptor$InsertionPoint;
216+
}
217+
206218
public abstract interface class com/apollographql/apollo/interceptor/ApolloInterceptorChain {
207219
public abstract fun proceed (Lcom/apollographql/apollo/api/ApolloRequest;)Lkotlinx/coroutines/flow/Flow;
208220
}

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,19 @@ abstract interface <#A: com.apollographql.apollo.api/Operation.Data> com.apollog
3838

3939
abstract interface com.apollographql.apollo.interceptor/ApolloInterceptor { // com.apollographql.apollo.interceptor/ApolloInterceptor|null[0]
4040
abstract fun <#A1: com.apollographql.apollo.api/Operation.Data> intercept(com.apollographql.apollo.api/ApolloRequest<#A1>, com.apollographql.apollo.interceptor/ApolloInterceptorChain): kotlinx.coroutines.flow/Flow<com.apollographql.apollo.api/ApolloResponse<#A1>> // com.apollographql.apollo.interceptor/ApolloInterceptor.intercept|intercept(com.apollographql.apollo.api.ApolloRequest<0:0>;com.apollographql.apollo.interceptor.ApolloInterceptorChain){0§<com.apollographql.apollo.api.Operation.Data>}[0]
41+
42+
final enum class InsertionPoint : kotlin/Enum<com.apollographql.apollo.interceptor/ApolloInterceptor.InsertionPoint> { // com.apollographql.apollo.interceptor/ApolloInterceptor.InsertionPoint|null[0]
43+
enum entry BeforeAutoPersistedQueries // com.apollographql.apollo.interceptor/ApolloInterceptor.InsertionPoint.BeforeAutoPersistedQueries|null[0]
44+
enum entry BeforeCache // com.apollographql.apollo.interceptor/ApolloInterceptor.InsertionPoint.BeforeCache|null[0]
45+
enum entry BeforeNetwork // com.apollographql.apollo.interceptor/ApolloInterceptor.InsertionPoint.BeforeNetwork|null[0]
46+
enum entry BeforeRetryOnError // com.apollographql.apollo.interceptor/ApolloInterceptor.InsertionPoint.BeforeRetryOnError|null[0]
47+
48+
final val entries // com.apollographql.apollo.interceptor/ApolloInterceptor.InsertionPoint.entries|#static{}entries[0]
49+
final fun <get-entries>(): kotlin.enums/EnumEntries<com.apollographql.apollo.interceptor/ApolloInterceptor.InsertionPoint> // com.apollographql.apollo.interceptor/ApolloInterceptor.InsertionPoint.entries.<get-entries>|<get-entries>#static(){}[0]
50+
51+
final fun valueOf(kotlin/String): com.apollographql.apollo.interceptor/ApolloInterceptor.InsertionPoint // com.apollographql.apollo.interceptor/ApolloInterceptor.InsertionPoint.valueOf|valueOf#static(kotlin.String){}[0]
52+
final fun values(): kotlin/Array<com.apollographql.apollo.interceptor/ApolloInterceptor.InsertionPoint> // com.apollographql.apollo.interceptor/ApolloInterceptor.InsertionPoint.values|values#static(){}[0]
53+
}
4154
}
4255

4356
abstract interface com.apollographql.apollo.interceptor/ApolloInterceptorChain { // com.apollographql.apollo.interceptor/ApolloInterceptorChain|null[0]
@@ -663,7 +676,7 @@ final class com.apollographql.apollo/ApolloClient : com.apollographql.apollo.api
663676
final fun addExecutionContext(com.apollographql.apollo.api/ExecutionContext): com.apollographql.apollo/ApolloClient.Builder // com.apollographql.apollo/ApolloClient.Builder.addExecutionContext|addExecutionContext(com.apollographql.apollo.api.ExecutionContext){}[0]
664677
final fun addHttpHeader(kotlin/String, kotlin/String): com.apollographql.apollo/ApolloClient.Builder // com.apollographql.apollo/ApolloClient.Builder.addHttpHeader|addHttpHeader(kotlin.String;kotlin.String){}[0]
665678
final fun addHttpInterceptor(com.apollographql.apollo.network.http/HttpInterceptor): com.apollographql.apollo/ApolloClient.Builder // com.apollographql.apollo/ApolloClient.Builder.addHttpInterceptor|addHttpInterceptor(com.apollographql.apollo.network.http.HttpInterceptor){}[0]
666-
final fun addInterceptor(com.apollographql.apollo.interceptor/ApolloInterceptor): com.apollographql.apollo/ApolloClient.Builder // com.apollographql.apollo/ApolloClient.Builder.addInterceptor|addInterceptor(com.apollographql.apollo.interceptor.ApolloInterceptor){}[0]
679+
final fun addInterceptor(com.apollographql.apollo.interceptor/ApolloInterceptor, com.apollographql.apollo.interceptor/ApolloInterceptor.InsertionPoint = ...): com.apollographql.apollo/ApolloClient.Builder // com.apollographql.apollo/ApolloClient.Builder.addInterceptor|addInterceptor(com.apollographql.apollo.interceptor.ApolloInterceptor;com.apollographql.apollo.interceptor.ApolloInterceptor.InsertionPoint){}[0]
667680
final fun addInterceptors(kotlin.collections/List<com.apollographql.apollo.interceptor/ApolloInterceptor>): com.apollographql.apollo/ApolloClient.Builder // com.apollographql.apollo/ApolloClient.Builder.addInterceptors|addInterceptors(kotlin.collections.List<com.apollographql.apollo.interceptor.ApolloInterceptor>){}[0]
668681
final fun autoPersistedQueries(com.apollographql.apollo.api.http/HttpMethod = ..., com.apollographql.apollo.api.http/HttpMethod = ..., kotlin/Boolean = ...): com.apollographql.apollo/ApolloClient.Builder // com.apollographql.apollo/ApolloClient.Builder.autoPersistedQueries|autoPersistedQueries(com.apollographql.apollo.api.http.HttpMethod;com.apollographql.apollo.api.http.HttpMethod;kotlin.Boolean){}[0]
669682
final fun autoPersistedQueriesInterceptor(com.apollographql.apollo.interceptor/ApolloInterceptor?): com.apollographql.apollo/ApolloClient.Builder // com.apollographql.apollo/ApolloClient.Builder.autoPersistedQueriesInterceptor|autoPersistedQueriesInterceptor(com.apollographql.apollo.interceptor.ApolloInterceptor?){}[0]

libraries/apollo-runtime/api/jvm/apollo-runtime.api

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ public final class com/apollographql/apollo/ApolloClient$Builder : com/apollogra
7373
public synthetic fun addHttpHeader (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
7474
public final fun addHttpInterceptor (Lcom/apollographql/apollo/network/http/HttpInterceptor;)Lcom/apollographql/apollo/ApolloClient$Builder;
7575
public final fun addInterceptor (Lcom/apollographql/apollo/interceptor/ApolloInterceptor;)Lcom/apollographql/apollo/ApolloClient$Builder;
76+
public final fun addInterceptor (Lcom/apollographql/apollo/interceptor/ApolloInterceptor;Lcom/apollographql/apollo/interceptor/ApolloInterceptor$InsertionPoint;)Lcom/apollographql/apollo/ApolloClient$Builder;
77+
public static synthetic fun addInterceptor$default (Lcom/apollographql/apollo/ApolloClient$Builder;Lcom/apollographql/apollo/interceptor/ApolloInterceptor;Lcom/apollographql/apollo/interceptor/ApolloInterceptor$InsertionPoint;ILjava/lang/Object;)Lcom/apollographql/apollo/ApolloClient$Builder;
7678
public final fun addInterceptors (Ljava/util/List;)Lcom/apollographql/apollo/ApolloClient$Builder;
7779
public final fun autoPersistedQueries ()Lcom/apollographql/apollo/ApolloClient$Builder;
7880
public final fun autoPersistedQueries (Lcom/apollographql/apollo/api/http/HttpMethod;)Lcom/apollographql/apollo/ApolloClient$Builder;
@@ -203,6 +205,16 @@ public abstract interface class com/apollographql/apollo/interceptor/ApolloInter
203205
public abstract fun intercept (Lcom/apollographql/apollo/api/ApolloRequest;Lcom/apollographql/apollo/interceptor/ApolloInterceptorChain;)Lkotlinx/coroutines/flow/Flow;
204206
}
205207

208+
public final class com/apollographql/apollo/interceptor/ApolloInterceptor$InsertionPoint : java/lang/Enum {
209+
public static final field BeforeAutoPersistedQueries Lcom/apollographql/apollo/interceptor/ApolloInterceptor$InsertionPoint;
210+
public static final field BeforeCache Lcom/apollographql/apollo/interceptor/ApolloInterceptor$InsertionPoint;
211+
public static final field BeforeNetwork Lcom/apollographql/apollo/interceptor/ApolloInterceptor$InsertionPoint;
212+
public static final field BeforeRetryOnError Lcom/apollographql/apollo/interceptor/ApolloInterceptor$InsertionPoint;
213+
public static fun getEntries ()Lkotlin/enums/EnumEntries;
214+
public static fun valueOf (Ljava/lang/String;)Lcom/apollographql/apollo/interceptor/ApolloInterceptor$InsertionPoint;
215+
public static fun values ()[Lcom/apollographql/apollo/interceptor/ApolloInterceptor$InsertionPoint;
216+
}
217+
206218
public abstract interface class com/apollographql/apollo/interceptor/ApolloInterceptorChain {
207219
public abstract fun proceed (Lcom/apollographql/apollo/api/ApolloRequest;)Lkotlinx/coroutines/flow/Flow;
208220
}

libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/ApolloClient.kt

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ import kotlinx.coroutines.flow.channelFlow
4141
import kotlinx.coroutines.flow.onEach
4242
import kotlinx.coroutines.withContext
4343
import okio.Closeable
44+
import kotlin.collections.mutableListOf
45+
import kotlin.collections.plusAssign
4446
import kotlin.jvm.JvmOverloads
4547

4648
/**
@@ -288,14 +290,20 @@ private constructor(
288290
}.build()
289291

290292
val allInterceptors = buildList {
291-
addAll(interceptors)
293+
addAll(builder._beforeCacheInterceptors)
292294
if (cacheInterceptor != null) {
293295
add(cacheInterceptor)
294296
}
297+
298+
addAll(builder._beforeAutoPersistedQueriesInterceptors)
295299
if (autoPersistedQueryInterceptor != null) {
296300
add(autoPersistedQueryInterceptor)
297301
}
302+
303+
addAll(builder._beforeRetryOnErrorInterceptors)
298304
add(retryOnErrorInterceptor ?: RetryOnErrorInterceptor())
305+
306+
addAll(builder._beforeNetworkInterceptors)
299307
add(networkInterceptor)
300308
}
301309
return DefaultInterceptorChain(allInterceptors, 0)
@@ -327,8 +335,12 @@ private constructor(
327335
private val _customScalarAdaptersBuilder = CustomScalarAdapters.Builder()
328336
val customScalarAdapters: CustomScalarAdapters get() = _customScalarAdaptersBuilder.build()
329337

330-
private val _interceptors: MutableList<ApolloInterceptor> = mutableListOf()
331-
val interceptors: List<ApolloInterceptor> = _interceptors
338+
internal val _beforeCacheInterceptors: MutableList<ApolloInterceptor> = mutableListOf()
339+
internal val _beforeAutoPersistedQueriesInterceptors: MutableList<ApolloInterceptor> = mutableListOf()
340+
internal val _beforeRetryOnErrorInterceptors: MutableList<ApolloInterceptor> = mutableListOf()
341+
internal val _beforeNetworkInterceptors: MutableList<ApolloInterceptor> = mutableListOf()
342+
343+
val interceptors: List<ApolloInterceptor> = _beforeCacheInterceptors + _beforeAutoPersistedQueriesInterceptors + _beforeRetryOnErrorInterceptors + _beforeNetworkInterceptors
332344

333345
private val _httpInterceptors: MutableList<HttpInterceptor> = mutableListOf()
334346
val httpInterceptors: List<HttpInterceptor> = _httpInterceptors
@@ -819,25 +831,31 @@ private constructor(
819831
/**
820832
* Adds an [ApolloInterceptor] to this [ApolloClient].
821833
*
822-
* [ApolloInterceptor]s monitor, rewrite and retry an [ApolloCall]. Internally, [ApolloInterceptor] is used for features
834+
* An [ApolloInterceptor] may monitor, rewrite, or retry an [ApolloRequest]. Internally, [ApolloInterceptor] is used for features
823835
* such as normalized cache and auto persisted queries. [ApolloClient] also inserts a terminating [ApolloInterceptor] that
824836
* executes the request.
825837
*
826-
* **The order is important**. The [ApolloInterceptor]s are executed in the order they are added and are always added before
827-
* the built-in interceptors:
838+
* **The order is important**. The built-in interceptors are always called in the following order:
828839
*
829-
* - user interceptors
830840
* - cacheInterceptor
831841
* - autoPersistedQueriesInterceptor
832842
* - retryOnErrorInterceptor
833843
* - networkInterceptor
834844
*
845+
* Use [ApolloInterceptor.InsertionPoint] to control where to insert a specific interceptor.
846+
*
835847
* @see cacheInterceptor
836848
* @see autoPersistedQueriesInterceptor
837849
* @see retryOnErrorInterceptor
838850
*/
839-
fun addInterceptor(interceptor: ApolloInterceptor) = apply {
840-
_interceptors.add(interceptor)
851+
@JvmOverloads
852+
fun addInterceptor(interceptor: ApolloInterceptor, insertionPoint: ApolloInterceptor.InsertionPoint = ApolloInterceptor.InsertionPoint.BeforeCache) = apply {
853+
when(insertionPoint) {
854+
ApolloInterceptor.InsertionPoint.BeforeCache -> _beforeCacheInterceptors
855+
ApolloInterceptor.InsertionPoint.BeforeAutoPersistedQueries -> _beforeAutoPersistedQueriesInterceptors
856+
ApolloInterceptor.InsertionPoint.BeforeRetryOnError -> _beforeRetryOnErrorInterceptors
857+
ApolloInterceptor.InsertionPoint.BeforeNetwork -> _beforeNetworkInterceptors
858+
}.add(interceptor)
841859
}
842860

843861
/**
@@ -846,7 +864,7 @@ private constructor(
846864
* **The order is important**. This method removes the first occurrence of the [ApolloInterceptor] in the list.
847865
*/
848866
fun removeInterceptor(interceptor: ApolloInterceptor) = apply {
849-
_interceptors.remove(interceptor)
867+
_beforeCacheInterceptors.remove(interceptor)
850868
}
851869

852870
/**
@@ -862,7 +880,7 @@ private constructor(
862880
@Deprecated("Use addInterceptor() or interceptors()", level = DeprecationLevel.ERROR)
863881
@ApolloDeprecatedSince(ApolloDeprecatedSince.Version.v4_0_0)
864882
fun addInterceptors(interceptors: List<ApolloInterceptor>) = apply {
865-
this._interceptors += interceptors
883+
error("This function is not supported anymore, please use addInterceptor() instead")
866884
}
867885

868886
/**
@@ -876,8 +894,8 @@ private constructor(
876894
* use interceptors, the order of the cache/APQs configuration also influences the final interceptor list.
877895
*/
878896
fun interceptors(interceptors: List<ApolloInterceptor>) = apply {
879-
this._interceptors.clear()
880-
this._interceptors += interceptors
897+
this._beforeCacheInterceptors.clear()
898+
this._beforeCacheInterceptors += interceptors
881899
}
882900

883901
/**
@@ -964,7 +982,12 @@ private constructor(
964982
fun copy(): Builder {
965983
return Builder()
966984
.customScalarAdapters(_customScalarAdaptersBuilder.build())
967-
.interceptors(interceptors)
985+
.also {
986+
it._beforeCacheInterceptors.addAll(_beforeCacheInterceptors)
987+
it._beforeAutoPersistedQueriesInterceptors.addAll(_beforeAutoPersistedQueriesInterceptors)
988+
it._beforeRetryOnErrorInterceptors.addAll(_beforeRetryOnErrorInterceptors)
989+
it._beforeNetworkInterceptors.addAll(_beforeNetworkInterceptors)
990+
}
968991
.dispatcher(dispatcher)
969992
.executionContext(executionContext)
970993
.url(url)

libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/interceptor/ApolloInterceptor.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,18 @@ interface ApolloInterceptor {
3737
* @see ApolloResponse.Builder
3838
*/
3939
fun <D : Operation.Data> intercept(request: ApolloRequest<D>, chain: ApolloInterceptorChain): Flow<ApolloResponse<D>>
40+
41+
/**
42+
* The place where the interceptor must be inserted in the interceptor chain.
43+
*
44+
* If two interceptors are added at the same insertion point, their relative order is the order in which they have been added.
45+
*/
46+
enum class InsertionPoint {
47+
BeforeCache,
48+
BeforeAutoPersistedQueries,
49+
BeforeRetryOnError,
50+
BeforeNetwork
51+
}
4052
}
4153

4254
/**

libraries/apollo-runtime/src/commonMain/kotlin/com/apollographql/apollo/network/websocket/WebSocketEngine.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ interface WebSocketEngine: Closeable {
1818
* - [WebSocketListener.onError] has been called
1919
* - [WebSocketListener.onClosed] has been called
2020
*
21-
* @param url: an url starting with one of:
21+
* @param url the url, starting with one of:
2222
* - ws://
2323
* - wss://
2424
* - http://
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package test
2+
3+
import com.apollographql.apollo.ApolloClient
4+
import com.apollographql.apollo.api.ApolloRequest
5+
import com.apollographql.apollo.api.ApolloResponse
6+
import com.apollographql.apollo.api.ExecutionContext
7+
import com.apollographql.apollo.api.Operation
8+
import com.apollographql.apollo.exception.DefaultApolloException
9+
import com.apollographql.apollo.interceptor.ApolloInterceptor
10+
import com.apollographql.apollo.interceptor.ApolloInterceptorChain
11+
import com.apollographql.apollo.network.NetworkTransport
12+
import com.apollographql.apollo.testing.internal.runTest
13+
import kotlinx.coroutines.flow.Flow
14+
import kotlinx.coroutines.flow.flowOf
15+
import okio.use
16+
import kotlin.test.Test
17+
import kotlin.test.assertNotNull
18+
19+
class InterceptorTest {
20+
@Test
21+
fun interceptorsCanBeAddedAfterCache() = runTest {
22+
ApolloClient.Builder()
23+
.networkTransport(object : NetworkTransport {
24+
override fun <D : Operation.Data> execute(request: ApolloRequest<D>): Flow<ApolloResponse<D>> {
25+
return flowOf(ApolloResponse.Builder(request.operation, request.requestUuid).exception(DefaultApolloException("unused")).build())
26+
}
27+
28+
override fun dispose() {}
29+
})
30+
.cacheInterceptor(object : ApolloInterceptor {
31+
override fun <D : Operation.Data> intercept(
32+
request: ApolloRequest<D>,
33+
chain: ApolloInterceptorChain,
34+
): Flow<ApolloResponse<D>> {
35+
return chain.proceed(request.newBuilder().addExecutionContext(CacheExecutionContext()).build())
36+
}
37+
})
38+
.addInterceptor(object : ApolloInterceptor {
39+
override fun <D : Operation.Data> intercept(
40+
request: ApolloRequest<D>,
41+
chain: ApolloInterceptorChain,
42+
): Flow<ApolloResponse<D>> {
43+
// check that we are called after the cache
44+
assertNotNull(request.executionContext[CacheExecutionContext])
45+
return chain.proceed(request)
46+
}
47+
}, ApolloInterceptor.InsertionPoint.BeforeNetwork)
48+
.build()
49+
.use {
50+
it.query(FooQuery()).execute()
51+
}
52+
}
53+
}
54+
55+
class CacheExecutionContext: ExecutionContext.Element {
56+
override val key: ExecutionContext.Key<*>
57+
get() = Key
58+
59+
companion object Key: ExecutionContext.Key<CacheExecutionContext>
60+
}

0 commit comments

Comments
 (0)