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

Conversation

danieltiger
Copy link
Contributor

There is sometimes a need to do things in an interceptor that relies on information not contained in the operation that is passed in. An example of this might be wanting to do custom headers on a per-request or pre-request-type basis.

As a solution, this PR adds a new marker protocol called RequestContext, that can be used to pass anything that a consumer might want/need through the request chain, making it available in any custom interceptor.

This was intentionally left as only a marker protocol, not any kind of specific context implementation. That's to allow the maximum flexibility for users of the chain, and to not try to solve for the probably endless needs they might have.

@netlify
Copy link

netlify bot commented Aug 17, 2023

👷 Deploy request for apollo-ios-docs pending review.

Visit the deploys page to approve it

Name Link
🔨 Latest commit 28311c7

Copy link
Contributor

@AnthonyMDev AnthonyMDev left a comment

Choose a reason for hiding this comment

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

Thanks so much for the contribution! I don't have a problem with this approach at all, though I would like to get input from @calvincestari on this before merging in. He works on the networking layer a lot more than I do. He is out until the 27th, so I'll get his input when he's back.

/// - 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!

@AnthonyMDev
Copy link
Contributor

Oh! This PR also needs a couple simple unit tests that just ensure that the RequestContext does get passed all the way through from the client functions to the interceptors.

@danieltiger
Copy link
Contributor Author

Added the tests that were requested :)

Copy link
Contributor

@AnthonyMDev AnthonyMDev left a comment

Choose a reason for hiding this comment

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

I had thought about suggesting that we attach the requestContext to the GraphQLOperation instead of passing it through the client functions, but that would require it to be added as a field on all the generated operations, which I don't think is a better solution.

With the added tests, I'm good with merging this PR in! @calvincestari, do you have any other opinions?

@@ -0,0 +1,82 @@
import XCTest
Copy link
Contributor

Choose a reason for hiding this comment

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

These tests are awesome! Nice work.

@calvincestari
Copy link
Member

What about if we make the request context part of HTTPRequest instead? Interceptors already have access to that through the request property that is passed along in all the ApolloInterceptor functions. That would bind the context to the actual request instead of having to link them through the additional ApolloInterceptor function parameter.

My original thought was that two requests with different contexts should be recognized as different requests, i.e.: not equatable. I don't know yet where that comparison would be useful but I feel it's still technically correct.

I'm unable to create a branch on your fork so I've put the changes into a patch file which you can apply and see the difference in implementation. I think it creates less of a breaking change too.

@calvincestari
Copy link
Member

My original thought was that two requests with different contexts should be recognized as different requests, i.e.: not equatable. I don't know yet where that comparison would be useful but I feel it's still technically correct.

Unfortunately my original goal of the difference in equality is still not possible due to RequestContext being a protocol whereas a concrete type is needed for conformance to Hashable.

@AnthonyMDev
Copy link
Contributor

I actually really like that idea @calvincestari! The RequestContext would be a parameter in the ApolloClient functions and the NetworkTransport, but once the RequestChainNetworkTransport constructs the request (in this case a JSONRequest or UploadRequest which are both subclasses of HTTPRequest, then the RequestContext would be passed into it, making it easier to access throughout the request chain without having to continue to pass it as a parameter.

This also feels like a logical place to store it and ensures it is retained until the request is complete and deallocated.

@danieltiger Do you mind making that change?

@calvincestari
Copy link
Member

The RequestContext would be a parameter in the ApolloClient functions and the NetworkTransport, but once the RequestChainNetworkTransport constructs the request (in this case a JSONRequest or UploadRequest which are both subclasses of HTTPRequest, then the RequestContext would be passed into it, making it easier to access throughout the request chain without having to continue to pass it as a parameter.

Correct.

@calvincestari
Copy link
Member

@danieltiger Do you mind making that change?

Simply applying the patch should give you exactly that.

@danieltiger
Copy link
Contributor Author

Sorry, I haven't had a chance to take a look yet, but I will tomorrow morning!

@danieltiger
Copy link
Contributor Author

Ok. PR is updated. This approach is great. I had briefly considered it when I started, but pivoted for reasons I no longer remember ;)

Anyway, I updated all the comments everywhere and made sure the context is being passed into the upload request as well. We should be good to go now!

Copy link
Member

@calvincestari calvincestari left a comment

Choose a reason for hiding this comment

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

Looks good, thanks for the contribution @danieltiger.

@danieltiger
Copy link
Contributor Author

@AnthonyMDev are we ready to merge this? :D

@AnthonyMDev AnthonyMDev merged commit 888461d into apollographql:main Sep 5, 2023
@AnthonyMDev
Copy link
Contributor

Merged! Thanks so much @danieltiger!

We're going to get a minor release out to include this today.

@danieltiger
Copy link
Contributor Author

Amazing! Thanks so much for helping this get done!

@danieltiger danieltiger deleted the dev-apollo-request-context branch September 5, 2023 18:31
@petarbelokonski
Copy link

I have achieved the same by subclassing query classes. Then in the interceptor I'd downcast to check whether I need what I am after. Since we are forced to use downcasting the RequestContext as well I wonder if this approach has more merits ?

Also worth mentioning that I am using query subclasses to differentiate operations in the InterceptorProvider level as described here #3213. We have no way to use the RequestContext in the InterceptorProvider as of now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants