Skip to content

Commit

Permalink
Adds RequestContext to the RequestChain/Interceptor pipeline (#3198)
Browse files Browse the repository at this point in the history
Co-authored-by: Arik Devens <[email protected]>
  • Loading branch information
danieltiger and danieltiger authored Sep 5, 2023
1 parent e10dc84 commit 888461d
Show file tree
Hide file tree
Showing 19 changed files with 183 additions and 20 deletions.
8 changes: 8 additions & 0 deletions Apollo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
objects = {

/* Begin PBXBuildFile section */
07236D332A992498009BFF7B /* RequestContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07236D322A992498009BFF7B /* RequestContextTests.swift */; };
073D39F52A8AD1AF001BD34A /* RequestContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 073D39F42A8AD1AF001BD34A /* RequestContext.swift */; };
19E9F6AC26D58A9A003AB80E /* OperationMessageIdCreatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19E9F6AA26D58A92003AB80E /* OperationMessageIdCreatorTests.swift */; };
19E9F6B526D6BF25003AB80E /* OperationMessageIdCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19E9F6A826D5867E003AB80E /* OperationMessageIdCreator.swift */; };
2EE7FFD0276802E30035DC39 /* CacheKeyConstructionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2EE7FFCF276802E30035DC39 /* CacheKeyConstructionTests.swift */; };
Expand Down Expand Up @@ -1134,6 +1136,8 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
07236D322A992498009BFF7B /* RequestContextTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestContextTests.swift; sourceTree = "<group>"; };
073D39F42A8AD1AF001BD34A /* RequestContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestContext.swift; sourceTree = "<group>"; };
19E9F6A826D5867E003AB80E /* OperationMessageIdCreator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationMessageIdCreator.swift; sourceTree = "<group>"; };
19E9F6AA26D58A92003AB80E /* OperationMessageIdCreatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationMessageIdCreatorTests.swift; sourceTree = "<group>"; };
2EE7FFCF276802E30035DC39 /* CacheKeyConstructionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CacheKeyConstructionTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2360,6 +2364,7 @@
9B260BF4245A028D00562176 /* HTTPResponse.swift */,
E69F436B29B81182006FF548 /* InterceptorRequestChain.swift */,
9B260BF2245A026F00562176 /* RequestChain.swift */,
073D39F42A8AD1AF001BD34A /* RequestContext.swift */,
9B260C03245A090600562176 /* RequestChainNetworkTransport.swift */,
);
name = RequestChain;
Expand Down Expand Up @@ -2758,6 +2763,7 @@
F16D083B21EF6F7300C458B8 /* SelectionSet_JSONInitializerTests.swift */,
C338DF1622DD9DE9006AF33E /* RequestBodyCreatorTests.swift */,
9B96500824BE6201003C29C0 /* RequestChainTests.swift */,
07236D322A992498009BFF7B /* RequestContextTests.swift */,
E61DD76426D60C1800C41614 /* SQLiteDotSwiftDatabaseBehaviorTests.swift */,
9B9BBB1A24DB75E60021C30F /* UploadRequestTests.swift */,
5BB2C0222380836100774170 /* VersionNumberTests.swift */,
Expand Down Expand Up @@ -5502,6 +5508,7 @@
9F55347B1DE1DB2100E54264 /* ApolloStore.swift in Sources */,
9BDE43D122C6655300FD7C7F /* Cancellable.swift in Sources */,
9F69FFA91D42855900E000B1 /* NetworkTransport.swift in Sources */,
073D39F52A8AD1AF001BD34A /* RequestContext.swift in Sources */,
9F8E0BD325668552000D9FA5 /* DataLoader.swift in Sources */,
9FCDFD291E33D0CE007519DC /* GraphQLQueryWatcher.swift in Sources */,
DE56DC232683B2020090D6E4 /* DefaultInterceptorProvider.swift in Sources */,
Expand Down Expand Up @@ -5600,6 +5607,7 @@
DED45C2A2615319E0086EF63 /* DefaultInterceptorProviderTests.swift in Sources */,
E6EE6F1229C141A600BDE7A5 /* HTTPURLResponseExtensionTests.swift in Sources */,
9F21730E2567E6F000566121 /* DataLoaderTests.swift in Sources */,
07236D332A992498009BFF7B /* RequestContextTests.swift in Sources */,
DED45DEC261B96B70086EF63 /* CacheDependentInterceptorTests.swift in Sources */,
DED45DEB261B96B70086EF63 /* SQLiteCacheTests.swift in Sources */,
C338DF1722DD9DE9006AF33E /* RequestBodyCreatorTests.swift in Sources */,
Expand Down
10 changes: 10 additions & 0 deletions Sources/Apollo/ApolloClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,22 +80,26 @@ extension ApolloClient: ApolloClientProtocol {
@discardableResult public func fetch<Query: GraphQLQuery>(query: Query,
cachePolicy: CachePolicy = .default,
contextIdentifier: UUID? = nil,
context: RequestContext? = nil,
queue: DispatchQueue = .main,
resultHandler: GraphQLResultHandler<Query.Data>? = nil) -> Cancellable {
return self.networkTransport.send(operation: query,
cachePolicy: cachePolicy,
contextIdentifier: contextIdentifier,
context: context,
callbackQueue: queue) { result in
resultHandler?(result)
}
}

public func watch<Query: GraphQLQuery>(query: Query,
cachePolicy: CachePolicy = .default,
context: RequestContext? = nil,
callbackQueue: DispatchQueue = .main,
resultHandler: @escaping GraphQLResultHandler<Query.Data>) -> GraphQLQueryWatcher<Query> {
let watcher = GraphQLQueryWatcher(client: self,
query: query,
context: context,
callbackQueue: callbackQueue,
resultHandler: resultHandler)
watcher.fetch(cachePolicy: cachePolicy)
Expand All @@ -105,12 +109,14 @@ extension ApolloClient: ApolloClientProtocol {
@discardableResult
public func perform<Mutation: GraphQLMutation>(mutation: Mutation,
publishResultToStore: Bool = true,
context: RequestContext? = nil,
queue: DispatchQueue = .main,
resultHandler: GraphQLResultHandler<Mutation.Data>? = nil) -> Cancellable {
return self.networkTransport.send(
operation: mutation,
cachePolicy: publishResultToStore ? .default : .fetchIgnoringCacheCompletely,
contextIdentifier: nil,
context: context,
callbackQueue: queue,
completionHandler: { result in
resultHandler?(result)
Expand All @@ -121,6 +127,7 @@ extension ApolloClient: ApolloClientProtocol {
@discardableResult
public func upload<Operation: GraphQLOperation>(operation: Operation,
files: [GraphQLFile],
context: RequestContext? = nil,
queue: DispatchQueue = .main,
resultHandler: GraphQLResultHandler<Operation.Data>? = nil) -> Cancellable {
guard let uploadingTransport = self.networkTransport as? UploadingNetworkTransport else {
Expand All @@ -133,17 +140,20 @@ extension ApolloClient: ApolloClientProtocol {

return uploadingTransport.upload(operation: operation,
files: files,
context: context,
callbackQueue: queue) { result in
resultHandler?(result)
}
}

public func subscribe<Subscription: GraphQLSubscription>(subscription: Subscription,
context: RequestContext? = nil,
queue: DispatchQueue = .main,
resultHandler: @escaping GraphQLResultHandler<Subscription.Data>) -> Cancellable {
return self.networkTransport.send(operation: subscription,
cachePolicy: .default,
contextIdentifier: nil,
context: context,
callbackQueue: queue,
completionHandler: resultHandler)
}
Expand Down
10 changes: 10 additions & 0 deletions Sources/Apollo/ApolloClientProtocol.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ public protocol ApolloClientProtocol: AnyObject {
/// - cachePolicy: A cache policy that specifies when results should be fetched from the server and when data should be loaded from the local cache.
/// - queue: A dispatch queue on which the result handler will be called. Should default to the main queue.
/// - contextIdentifier: [optional] A unique identifier for this request, to help with deduping cache hits for watchers. Should default to `nil`.
/// - context: [optional] A context that is being passed through the request chain. Should default to `nil`.
/// - resultHandler: [optional] A closure that is called when query results are available or when an error occurs.
/// - Returns: An object that can be used to cancel an in progress fetch.
func fetch<Query: GraphQLQuery>(query: Query,
cachePolicy: CachePolicy,
contextIdentifier: UUID?,
context: RequestContext?,
queue: DispatchQueue,
resultHandler: GraphQLResultHandler<Query.Data>?) -> Cancellable

Expand All @@ -37,11 +39,13 @@ public protocol ApolloClientProtocol: AnyObject {
/// - Parameters:
/// - query: The query to fetch.
/// - cachePolicy: A cache policy that specifies when results should be fetched from the server or from the local cache.
/// - context: [optional] A context that is being passed through the request chain. Should default to `nil`.
/// - callbackQueue: A dispatch queue on which the result handler will be called. Should default to the main queue.
/// - resultHandler: [optional] A closure that is called when query results are available or when an error occurs.
/// - Returns: A query watcher object that can be used to control the watching behavior.
func watch<Query: GraphQLQuery>(query: Query,
cachePolicy: CachePolicy,
context: RequestContext?,
callbackQueue: DispatchQueue,
resultHandler: @escaping GraphQLResultHandler<Query.Data>) -> GraphQLQueryWatcher<Query>

Expand All @@ -50,11 +54,13 @@ public protocol ApolloClientProtocol: AnyObject {
/// - Parameters:
/// - mutation: The mutation to perform.
/// - publishResultToStore: If `true`, this will publish the result returned from the operation to the cache store. Default is `true`.
/// - context: [optional] A context that is being passed through the request chain. Should default to `nil`.
/// - queue: A dispatch queue on which the result handler will be called. Should default to the main queue.
/// - resultHandler: An optional closure that is called when mutation results are available or when an error occurs.
/// - Returns: An object that can be used to cancel an in progress mutation.
func perform<Mutation: GraphQLMutation>(mutation: Mutation,
publishResultToStore: Bool,
context: RequestContext?,
queue: DispatchQueue,
resultHandler: GraphQLResultHandler<Mutation.Data>?) -> Cancellable

Expand All @@ -63,23 +69,27 @@ public protocol ApolloClientProtocol: AnyObject {
/// - Parameters:
/// - operation: The operation to send
/// - files: An array of `GraphQLFile` objects to send.
/// - context: [optional] A context that is being passed through the request chain. Should default to `nil`.
/// - queue: A dispatch queue on which the result handler will be called. Should default to the main queue.
/// - completionHandler: The completion handler to execute when the request completes or errors. Note that an error will be returned If your `networkTransport` does not also conform to `UploadingNetworkTransport`.
/// - Returns: An object that can be used to cancel an in progress request.
func upload<Operation: GraphQLOperation>(operation: Operation,
files: [GraphQLFile],
context: RequestContext?,
queue: DispatchQueue,
resultHandler: GraphQLResultHandler<Operation.Data>?) -> Cancellable

/// Subscribe to a subscription
///
/// - Parameters:
/// - subscription: The subscription to subscribe to.
/// - context: [optional] A context that is being passed through the request chain. Should default to `nil`.
/// - fetchHTTPMethod: The HTTP Method to be used.
/// - queue: A dispatch queue on which the result handler will be called. Should default to the main queue.
/// - resultHandler: An optional closure that is called when mutation results are available or when an error occurs.
/// - Returns: An object that can be used to cancel an in progress subscription.
func subscribe<Subscription: GraphQLSubscription>(subscription: Subscription,
context: RequestContext?,
queue: DispatchQueue,
resultHandler: @escaping GraphQLResultHandler<Subscription.Data>) -> Cancellable
}
6 changes: 5 additions & 1 deletion Sources/Apollo/GraphQLQueryWatcher.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public final class GraphQLQueryWatcher<Query: GraphQLQuery>: Cancellable, Apollo
private let callbackQueue: DispatchQueue

private let contextIdentifier = UUID()
private let context: RequestContext?

private class WeakFetchTaskContainer {
weak var cancellable: Cancellable?
Expand All @@ -34,16 +35,19 @@ public final class GraphQLQueryWatcher<Query: GraphQLQuery>: Cancellable, Apollo
/// - Parameters:
/// - client: The client protocol to pass in.
/// - query: The query to watch.
/// - context: [optional] A context that is being passed through the request chain. Defaults to `nil`.
/// - callbackQueue: The queue for the result handler. Defaults to the main queue.
/// - resultHandler: The result handler to call with changes.
public init(client: ApolloClientProtocol,
query: Query,
context: RequestContext? = nil,
callbackQueue: DispatchQueue = .main,
resultHandler: @escaping GraphQLResultHandler<Query.Data>) {
self.client = client
self.query = query
self.resultHandler = resultHandler
self.callbackQueue = callbackQueue
self.context = context

client.store.subscribe(self)
}
Expand All @@ -58,7 +62,7 @@ public final class GraphQLQueryWatcher<Query: GraphQLQuery>: Cancellable, Apollo
// Cancel anything already in flight before starting a new fetch
$0.cancellable?.cancel()
$0.cachePolicy = cachePolicy
$0.cancellable = client?.fetch(query: query, cachePolicy: cachePolicy, contextIdentifier: self.contextIdentifier, queue: callbackQueue) { [weak self] result in
$0.cancellable = client?.fetch(query: query, cachePolicy: cachePolicy, contextIdentifier: self.contextIdentifier, context: self.context, queue: callbackQueue) { [weak self] result in
guard let self = self else { return }

switch result {
Expand Down
8 changes: 7 additions & 1 deletion Sources/Apollo/HTTPRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ open class HTTPRequest<Operation: GraphQLOperation>: Hashable {

/// [optional] A unique identifier for this request, to help with deduping cache hits for watchers.
public let contextIdentifier: UUID?

/// [optional] A context that is being passed through the request chain.
public let context: RequestContext?

/// Designated Initializer
///
Expand All @@ -32,19 +35,22 @@ open class HTTPRequest<Operation: GraphQLOperation>: Hashable {
/// - clientVersion: The version of the client to send with the `"apollographql-client-version"` header
/// - additionalHeaders: Any additional headers you wish to add by default to this request.
/// - cachePolicy: The `CachePolicy` to use for this request. Defaults to the `.default` policy
/// - context: [optional] A context that is being passed through the request chain. Defaults to `nil`.
public init(graphQLEndpoint: URL,
operation: Operation,
contextIdentifier: UUID? = nil,
contentType: String,
clientName: String,
clientVersion: String,
additionalHeaders: [String: String],
cachePolicy: CachePolicy = .default) {
cachePolicy: CachePolicy = .default,
context: RequestContext? = nil) {
self.graphQLEndpoint = graphQLEndpoint
self.operation = operation
self.contextIdentifier = contextIdentifier
self.additionalHeaders = additionalHeaders
self.cachePolicy = cachePolicy
self.context = context

self.addHeader(name: "Content-Type", value: contentType)
// Note: in addition to this being a generally useful header to send, Apollo
Expand Down
5 changes: 4 additions & 1 deletion Sources/Apollo/JSONRequest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ open class JSONRequest<Operation: GraphQLOperation>: HTTPRequest<Operation> {
/// - clientVersion: The version of the client to send with the `"apollographql-client-version"` header
/// - additionalHeaders: Any additional headers you wish to add by default to this request
/// - cachePolicy: The `CachePolicy` to use for this request.
/// - context: [optional] A context that is being passed through the request chain. Defaults to `nil`.
/// - autoPersistQueries: `true` if Auto-Persisted Queries should be used. Defaults to `false`.
/// - useGETForQueries: `true` if Queries should use `GET` instead of `POST` for HTTP requests. Defaults to `false`.
/// - useGETForPersistedQueryRetry: `true` if when an Auto-Persisted query is retried, it should use `GET` instead of `POST` to send the query. Defaults to `false`.
Expand All @@ -49,6 +50,7 @@ open class JSONRequest<Operation: GraphQLOperation>: HTTPRequest<Operation> {
clientVersion: String,
additionalHeaders: [String: String] = [:],
cachePolicy: CachePolicy = .default,
context: RequestContext? = nil,
autoPersistQueries: Bool = false,
useGETForQueries: Bool = false,
useGETForPersistedQueryRetry: Bool = false,
Expand All @@ -67,7 +69,8 @@ open class JSONRequest<Operation: GraphQLOperation>: HTTPRequest<Operation> {
clientName: clientName,
clientVersion: clientVersion,
additionalHeaders: additionalHeaders,
cachePolicy: cachePolicy
cachePolicy: cachePolicy,
context: context
)
}

Expand Down
Loading

0 comments on commit 888461d

Please sign in to comment.