-
Notifications
You must be signed in to change notification settings - Fork 184
Handle Absinthe unsubscriptions #228
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
Handle Absinthe unsubscriptions #228
Conversation
You can run |
Thanks. Probably need to add a an aclose() call for Python 3.6 tests as well. |
I installed all the dev requirements and everything checks and passes now. However my version of the Python formatter black (21.7b0) reformatted 50 files! Should I commit them all, or install black version 19.10b0 and try again, or is there a way to skip the reformatting of files that I have not edited? |
It is explained in the CONTRIBUTING.md file. Once it is done, running |
Tests and checks now passed locally with |
Codecov Report
@@ Coverage Diff @@
## master #228 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 16 16
Lines 1067 1149 +82
=========================================
+ Hits 1067 1149 +82
Continue to review full report at Codecov.
|
I'm thinking perhaps I should create another exception class, TransportSubscriptionError, to handle the potential error that the CodeCov caught. It could happen if somehow the Phoenix Channel transport had removed a subscription listener, but then received an unsubscribe message still referring to it. That would be something wrong on the transport's part, but it's not one of the existing TransportErrors. I can write another test to cover it. Any comments? |
Now an unsubscribe reply will generate a 'complete' message to stop the async generator cleanly
…ql into fix_phoenix_subscriptions_close
My last commit is an attempt to handle more edge cases around the GraphQL and Phoenix Channel specs. I'm going hands off for a while. All tests are passing, and I'm testing against my Absinthe server as well. |
Thanks for your help! As you have seen I modified the code so that the I actually found the real bug you had at first which cause break to not work properly (even for the websockets transport). |
Also about your proposition of a TransportSubscriptionError exception, I suggest that instead we ignore this message and just log it as an error. |
In my commits I stayed with TransportProtocolErrors. No new error types.
|
The async generator will end properly and we will remove the listener inside the subscribe method
Note: you can see the full picture using
|
Coverage is now fixed but I really don't like the fact that now you need to use |
I don't know anything about the plain websocket protocol, but in Phoenix Channels, the replies from subscription docs are completely distinct from queries or mutations. We could let go of using get_operation_ast, and let the reply determine what we know about the request, but I think it's a stronger protocol to match the expected reply response content ("subscriptionId" for subscriptions, "data" and/or "errors" for queries and mutations) against the operation type. Since the "subscribe" code has already called "gql.gql" the hard work of building the AST is already paid for, "get_operation_ast" is just a quick traversal of the parsed nodes: https://github.com/graphql-python/graphql-core/blob/main/src/graphql/utilities/get_operation_ast.py You may have noticed that I split the tests into those that send queries and those that send subscriptions. One test I would like to add (but don't have time this week) is to create a session with a PhoenixChannelWebsocketsTransport, and run an async that has two separate queries in it (ideally one a mutation and the other a subscription to the mutation results). I have a working example of this in a separate client project. It seems to work now, removing the listeners appropriately as they are unsubscribed, etc. But it would be nice to have a test server that can handle both types of replies concurrently. In the vanilla websockets tests, there is "test_websocket_multiple_connections_in_parallel", but I don't see a test that does a single session connection with multiple queries in parallel. Is there an issue to create these types of tests already, or should I open one? Thanks for fixing the "unsubscribe" vs "complete" answer type. I'm just going to do some live testing on my Absinthe/Phoenix Channel server to send back some error responses to make sure they are parsed correctly. I will also spend some time looking at this: https://github.com/easco/absinthe_apollo_sockets, so I get a better understanding of how the vanilla websockets differ for my own edification. But I'll stay out of the code! |
Another way to distinguish between query/mutation and subscription queries is that with queries and mutation, the user is supposed to use the As for the tests with concurrent queries on the same connection, some of them already exists with other transports:
|
Yes. When I split the tests in test_phoenix_channel_query.py and test_phoenix_channel_exception.py, I used "execute" for queries and "subscribe" for subscriptions. As far as servers go, I took the absinthe_apollo_socket code, and built a Phoenix Channel server transport that has the same GraphQL schema and backend (simple counter with query, reset and increment mutations, and a subscription) as the Apollo websocket transport. Connecting to both backends with the code in the PR work as expected, when async running both a series of mutation executions and a long-running subscription, for both the PhoenixChannel and the plain Websocket transports. And I can test some error responses also. So I'm happy with things. |
Thanks for PR! |
Adds support for Absinthe GraphQL subscriptions, handling unsubscribe requests and replies.
Fixes Phoenix transport: breaking early from async generator loop requires canceling receive_data_task #225
a. Handles TransportClosed exception when the transport is closed before a subscription is unsubscribed.
b. Removes the listener when the unsubscribe reply is received from server.
Implements the Absinthe unsubscribe protocol in the mock servers in tests_phoenix_channel_subscription.py.