Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds RequestContext to the RequestChain/Interceptor pipeline #3198

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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?,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only concern I have is that this is technically a minor breaking change. Anyone who is implementing the ApolloClientProtocol (which I would think is usually just for mock objects in unit tests), will have to add the new parameter. It's not a huge change, but it will not be backwards compatible.

I'm not really sure that there is a great method for deprecation of the old signatures though. So I think this falls under acceptable minor breaking changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I couldn't think of a way to do it without creating a breaking change. Glad to hear you're likely ok with it!

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