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

Add support for extensions to the HTTP protocol #3461

Open
wants to merge 17 commits into
base: main
Choose a base branch
from

Conversation

omarzouk
Copy link

@omarzouk omarzouk commented Apr 19, 2024

Description

The GQL spec specifies a field called extensions as part of the request parameters spec in the HTTP protocol. We are missing support for this

Adding the field to be parsed and propagating it as part of the Info object. This is done by wrapping the entire context object in a temporary container, and then unwrapping it inside the Info context getter. A new function is also added to the Info class called input_extensions. It exposes the extensions data.

Example usage by the end-user:

@strawberry.field
def value_from_extensions(self, key: str, info: strawberry.Info) -> str:
    return info.input_extensions[key]

Types of Changes

  • Core
  • Bugfix
  • New feature
  • Enhancement/optimization
  • Documentation

Issues Fixed or Closed by This PR

Checklist

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the CONTRIBUTING document.
  • I have added tests to cover my changes.
  • I have tested the changes and verified that they work and don't break anything (as well as I can manage).

Summary by Sourcery

This pull request introduces support for the 'extensions' field in HTTP requests, allowing it to be parsed and included in the ExecutionContext. This change is accompanied by updates to the documentation and new tests to ensure proper functionality.

  • New Features:
    • Added support for the 'extensions' field in HTTP requests, allowing it to be parsed and propagated as part of the ExecutionContext.
  • Documentation:
    • Updated documentation to reflect the new support for the 'extensions' field in HTTP requests.
  • Tests:
    • Added tests to verify the correct handling and propagation of the 'extensions' field in both GET and POST HTTP requests.

@omarzouk omarzouk marked this pull request as draft April 19, 2024 17:38
@botberry
Copy link
Member

botberry commented Apr 19, 2024

Hi, thanks for contributing to Strawberry 🍓!

We noticed that this PR is missing a RELEASE.md file. We use that to automatically do releases here on GitHub and, most importantly, to PyPI!

So as soon as this PR is merged, a release will be made 🚀.

Here's an example of RELEASE.md:

Release type: patch

Description of the changes, ideally with some examples, if adding a new feature.

Release type can be one of patch, minor or major. We use semver, so make sure to pick the appropriate type. If in doubt feel free to ask :)

Here's the tweet text:

🆕 Release (next) is out! Thanks to Omar Marzouk for the PR 👏

Get it here 👉 https://strawberry.rocks/release/(next)

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey @omarzouk - I've reviewed your changes and they look great!

Here's what I looked at during the review
  • 🟡 General issues: 1 issue found
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟢 Complexity: all looks good
  • 🟢 Docstrings: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment to tell me if it was helpful.

strawberry/http/__init__.py Show resolved Hide resolved
Copy link

codecov bot commented Apr 19, 2024

Codecov Report

Attention: Patch coverage is 92.30769% with 3 lines in your changes missing coverage. Please review.

Project coverage is 96.21%. Comparing base (581f508) to head (82708d7).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3461      +/-   ##
==========================================
- Coverage   96.22%   96.21%   -0.01%     
==========================================
  Files         526      527       +1     
  Lines       33999    34037      +38     
  Branches     5610     5621      +11     
==========================================
+ Hits        32714    32750      +36     
  Misses       1048     1048              
- Partials      237      239       +2     

Copy link

codspeed-hq bot commented Apr 19, 2024

CodSpeed Performance Report

Merging #3461 will not alter performance

Comparing omarzouk:add_http_extensions (82708d7) with main (581f508)

Summary

✅ 13 untouched benchmarks

@strawberry-graphql strawberry-graphql deleted a comment from botberry Jun 22, 2024
Copy link
Member

@erikwrede erikwrede left a comment

Choose a reason for hiding this comment

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

Thanks for the PR! Can you add a test to ensure the exension dict is actually correctly passed down the chain? 🙂

Additionally, we can investigate merging the http protocol extensions with subscription extensions (need to double check if we support this already in strawberry). It might also make sense to add the protocol extensions to Info

@omarzouk
Copy link
Author

omarzouk commented Jun 24, 2024

@erikwrede Hi! yea I think I need to do a fresh take on this, I think I hit a dead end with my current approach, I've been investigating how to do it, and I think we have a couple options:

1- Wrap the context field in another object that holds the extensions and the context, i.e. wrap the field before this line, then unwrap it inside the Info class. We would then introduce an input_extensions or protocol_extensions field to Info that reads the extensions from the wrapper.

2- Extend the definition of the get_context function, add a 3rd parameter called extensions. However, this would require moving the body parsing and data fetching to happen before the get_context is called, i.e. this operation would have to move above this line. It will also require some refactoring to get a similar pattern to work with subscriptions.

The benefit of doing Option 1 would be that we can add this right away to http, without having to modify other protocols, and just include a type check for the wrapper in the info.context getter. It can then be added to other protocols over time, in other PRs.

What do u think? should I go ahead and do Option 1?

@omarzouk omarzouk requested a review from erikwrede June 24, 2024 14:16
@omarzouk
Copy link
Author

hi @erikwrede, I went ahead and implemented Option 1 mentioned above, which is to wrap the context object and unwrap it in the Info getter. I also added a new test that is passing successfully. Please let me know what you think. 🙏

@omarzouk omarzouk marked this pull request as ready for review June 24, 2024 14:24
Copy link
Contributor

sourcery-ai bot commented Jun 24, 2024

Reviewer's Guide by Sourcery

This pull request adds support for the 'extensions' field in the HTTP protocol for GraphQL requests. The changes involve parsing the 'extensions' field from the request, propagating it through the ExecutionContext, and making it accessible to applications. Additionally, tests have been added to ensure the new functionality works correctly.

File-Level Changes

Files Changes
tests/http/clients/base.py
tests/http/clients/channels.py
tests/http/clients/aiohttp.py
tests/http/clients/asgi.py
tests/http/clients/chalice.py
tests/http/clients/django.py
tests/http/clients/fastapi.py
tests/http/clients/flask.py
tests/http/clients/litestar.py
tests/http/clients/quart.py
tests/http/clients/sanic.py
tests/http/clients/starlite.py
Added 'extensions' parameter to various HTTP client request functions and updated request body construction to include 'extensions'.
strawberry/http/async_base_view.py
strawberry/http/sync_base_view.py
Wrapped context in ContextWrapper to include 'extensions' in execute_operation and parse_http_body functions.

Tips
  • Trigger a new Sourcery review by commenting @sourcery-ai review on the pull request.
  • You can change your review settings at any time by accessing your dashboard:
    • Enable or disable the Sourcery-generated pull request summary or reviewer's guide;
    • Change the review language;
  • You can always contact us if you have any questions or feedback.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey @omarzouk - I've reviewed your changes and they look great!

Here's what I looked at during the review
  • 🟡 General issues: 3 issues found
  • 🟢 Security: all looks good
  • 🟡 Testing: 1 issue found
  • 🟡 Complexity: 2 issues found
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment to tell me if it was helpful.

strawberry/types/info.py Outdated Show resolved Hide resolved
strawberry/http/__init__.py Show resolved Hide resolved
strawberry/types/context_wrapper.py Show resolved Hide resolved
tests/http/test_query.py Show resolved Hide resolved
strawberry/http/async_base_view.py Show resolved Hide resolved
strawberry/http/sync_base_view.py Show resolved Hide resolved
@omarzouk
Copy link
Author

pinging @patrick91 as well, since we discussed this originally in #3224

Copy link
Member

@bellini666 bellini666 left a comment

Choose a reason for hiding this comment

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

Some comments, but I'm not totally aware of this, so other @strawberry-graphql/core will be able to do a better review :)

strawberry/http/__init__.py Show resolved Hide resolved
strawberry/http/async_base_view.py Outdated Show resolved Hide resolved
strawberry/http/sync_base_view.py Outdated Show resolved Hide resolved
strawberry/types/info.py Outdated Show resolved Hide resolved
strawberry/types/info.py Outdated Show resolved Hide resolved
@dataclass
class ContextWrapper:
context: Optional[Any]
extensions: Optional[Dict[str, Any]]
Copy link
Member

Choose a reason for hiding this comment

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

Same suggestion as above

Suggested change
extensions: Optional[Dict[str, Any]]
extensions: Optional[Dict[str, Any]] = None

tests/views/schema.py Outdated Show resolved Hide resolved
strawberry/http/base.py Outdated Show resolved Hide resolved
omarzouk and others added 5 commits July 6, 2024 21:18
@DoctorJohn DoctorJohn mentioned this pull request Jul 7, 2024
11 tasks
@omarzouk
Copy link
Author

omarzouk commented Jul 7, 2024

Hello! apart from the extra check for list in the parameter parsing which is potentially redundant and might be removed, as I understand it, that change is also not really blocking for this PR, right? #3558 could be updated to remove the list parsing for extensions after this one is merged?

Is there any other comments regarding this PR? the approach in general around wrapping context and exposing the extra field in the Info object? how would you like me to proceed?

@bellini666 @DoctorJohn @erikwrede @patrick91

@omarzouk
Copy link
Author

Hi! I have merged main to include #3558 and removed the extra list type check for the extensions param. Anything else you guys would like me to change about this PR? if not I can should I create a release file?

@bellini666 @DoctorJohn @erikwrede @patrick91

Comment on lines 87 to 91
@property
def input_extensions(self) -> Dict[str, Any]:
if isinstance(self._raw_info.context, ContextWrapper):
return self._raw_info.context.extensions
return {}
Copy link
Member

Choose a reason for hiding this comment

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

how come this is called input_extensions? 😊

Copy link
Author

Choose a reason for hiding this comment

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

honestly, I just didn't have a better name 😅 I just felt extensions is used in so many contexts in strawberry and people might be confused as to which one this is. But I'm happy to change it to whatever you think makes most sense

Comment on lines +116 to +118
context_wrapper = ContextWrapper(
context=context, extensions=request_data.extensions
)
Copy link
Member

Choose a reason for hiding this comment

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

is this context wrapper only to pass request data?

Copy link
Author

Choose a reason for hiding this comment

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

yes exactly. Since the context data comes from get_context and that can be any type of object the user decides to return, this felt like a safe way to attach more data

Copy link
Member

Choose a reason for hiding this comment

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

I'll try to review and merge this this week 😊 thanks for the patience!

Copy link
Author

Choose a reason for hiding this comment

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

thank you so much! 🙏

@omarzouk omarzouk requested a review from patrick91 July 24, 2024 09:04
@omarzouk
Copy link
Author

hello! sorry to keep pinging, we are really eager to use this in our APIs, do u think its possible to get some traction on it? I'm happy to help any way I can @patrick91 🙏

@patrick91
Copy link
Member

@omarzouk sure, I think I'll have time this week(end) (I'm currently travelling) 😊

And please consider sponsoring us, so we can prioritise issues better ;)

@patrick91
Copy link
Member

@omarzouk an update on this, I think I found a nice way to pass the request extensions to info, but it is quite different from this, so I might try it in another PR

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.

Support the extensions Request Parameter for the HTTP protocol
6 participants