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

Improve extensions error handling #3217

Merged

Conversation

DoctorJohn
Copy link
Member

@DoctorJohn DoctorJohn commented Nov 10, 2023

This PR brings schema extension error handling in line with field extension and resolver error handling.

Description

This PR alters schema extension error handling so that any error raised from within schema
extensions will abort the operation and is added to the execution result.

Before, most errors (including Strawberry/graphql-core GraphQLErrors) would cause an internal server error.

Furthermore, these changes enable extensions (e.g. the one described in #3204) to abort operations early in a requests lifecycle which was not easily possible before.

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).

Copy link

codecov bot commented Nov 10, 2023

Codecov Report

Merging #3217 (55d82c1) into main (87f6b47) will increase coverage by 0.01%.
Report is 5 commits behind head on main.
The diff coverage is 100.00%.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3217      +/-   ##
==========================================
+ Coverage   96.48%   96.49%   +0.01%     
==========================================
  Files         509      509              
  Lines       32703    32862     +159     
  Branches     5404     5421      +17     
==========================================
+ Hits        31552    31711     +159     
  Misses        940      940              
  Partials      211      211              

Copy link

codspeed-hq bot commented Nov 10, 2023

CodSpeed Performance Report

Merging #3217 will improve performances by 13.87%

Comparing DoctorJohn:improve-extensions-error-handling (55d82c1) with main (861cc0d)

Summary

⚡ 1 improvements
✅ 12 untouched benchmarks

Benchmarks breakdown

Benchmark main DoctorJohn:improve-extensions-error-handling Change
test_execute_basic 11.2 ms 9.9 ms +13.87%

@erikwrede
Copy link
Member

I am in favor of addressing #3204 in the same go, as the changing of the current mechanism might create some breakage for existing web views which we could mitigate by integrating the changes required from #3204. Should only be a few lines after all.

@DoctorJohn
Copy link
Member Author

DoctorJohn commented Nov 10, 2023

I am in favor of addressing #3204 in the same go, as the changing of the current mechanism might create some breakage for existing web views which we could mitigate by integrating the changes required from #3204. Should only be a few lines after all.

Just to make sure we're on the same page: In its current state this PR does not change the current mechanism, since it's only removing an unnecessary "fallback" except block wrapping a single call to the graphql-core parse function.

Should only be a few lines after all

Yup, pretty straightforward changes. Making sure they're covered by tests this time.

@DoctorJohn DoctorJohn force-pushed the improve-extensions-error-handling branch 3 times, most recently from 32331a0 to cbebf5e Compare November 14, 2023 01:36
@DoctorJohn DoctorJohn force-pushed the improve-extensions-error-handling branch from cbebf5e to 0379a33 Compare November 14, 2023 01:39
@DoctorJohn DoctorJohn marked this pull request as ready for review November 15, 2023 01:30
@botberry
Copy link
Member

botberry commented Nov 15, 2023

Thanks for adding the RELEASE.md file!

Here's a preview of the changelog:


Starting with this release, any error raised from within schema
extensions will abort the operation and is returned to the client.

This corresponds to the way we already handle field extension errors
and resolver errors.

This is particular useful for schema extensions performing checks early
in the request lifecycle, for example:

class MaxQueryLengthExtension(SchemaExtension):
    MAX_QUERY_LENGTH = 8192

    async def on_operation(self):
        if len(self.execution_context.query) > self.MAX_QUERY_LENGTH:
            raise StrawberryGraphQLError(message="Query too large")
        yield

Here's the tweet text:

🆕 Release (next) is out! Thanks to @NucleonJohn for the PR 👏

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

# only return a sanitised version to the client.
process_errors(result.errors, execution_context)

except (MissingQueryError, InvalidOperationTypeError) as e:
Copy link
Member Author

Choose a reason for hiding this comment

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

These two exceptions get special handling by our view integrations and enables them to return appropriate status codes. For context:

try:
result = self.execute_operation(
request=request,
context=context,
root_value=root_value,
)
except InvalidOperationTypeError as e:
raise HTTPException(
400, e.as_http_error_reason(request_adapter.method)
) from e
except MissingQueryError as e:
raise HTTPException(400, "No GraphQL query found in the request") from e

Copy link
Member

Choose a reason for hiding this comment

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

I understand that this solves the problem for now. However, I doubt that this is a long term solution due to the specificity of re-raising only specific exceptions. We should try to abstract that into an exception -> status code or modified response or graphQL error "middleware/exception filter" as a follow up to this PR.

Copy link
Member

Choose a reason for hiding this comment

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

something like an OperationError? 😊

Copy link
Member

Choose a reason for hiding this comment

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

let's do in another PR/refactoring 😊

@patrick91 patrick91 mentioned this pull request Nov 27, 2023
11 tasks
@patrick91
Copy link
Member

/pre-release

@botberry
Copy link
Member

botberry commented Nov 27, 2023

Pre-release

👋

Pre-release 0.220.0.dev.1709543239 [e3b40c0] has been released on PyPi! 🚀
You can try it by doing:

poetry add strawberry-graphql==0.220.0.dev.1709543239

@imconnorngl
Copy link
Contributor

After testing this compared to the functionality of #3256. It seems like this PR is more verbose than I'd like in best case, as seen below

Before:
image
After:
image

@DoctorJohn
Copy link
Member Author

After testing this compared to the functionality of #3256. It seems like this PR is more verbose than I'd like in best case

Thanks for testing the pre-release! This PR currently wraps all raised errors in a GraphQLError, even when the raised error aleady is a GraphQLError. Currently testing whether not doing that yields the expected result :)

@DoctorJohn
Copy link
Member Author

/pre-release

@strawberry-graphql strawberry-graphql deleted a comment from botberry Nov 30, 2023
@patrick91
Copy link
Member

/pre-release

@patrick91
Copy link
Member

/pre-release

@patrick91
Copy link
Member

I've merged main into this, but something has changed 😊 I'll take a proper look at this in the following days

@@ -721,7 +725,7 @@ def ping(self) -> str:
@pytest.mark.parametrize(
("failing_hook", "expected_hooks"),
(
("on_operation_start", set()),
("on_operation_start", {2, 3, 4, 5, 6, 7}),
Copy link
Member

Choose a reason for hiding this comment

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

@erikwrede do you think this makes sense?

we run all the hooks even if the execution stops?

Copy link
Member

Choose a reason for hiding this comment

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

mmh, looking at the current failure it looks like what we were trying to do is not working as I though

we can't stop an operation from an extension if we catch all exceptions, unless I'm missing something

Copy link
Member

@patrick91 patrick91 left a comment

Choose a reason for hiding this comment

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

Thank you so much for this, sorry it took so long to do this 😊

@patrick91 patrick91 merged commit 63dfc89 into strawberry-graphql:main Apr 17, 2024
64 checks passed
@DoctorJohn DoctorJohn deleted the improve-extensions-error-handling branch November 20, 2024 16:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

StrawberryGraphQLError in Custom Extension causes 500 error response instead of formatted error
5 participants