DjangoChannelsGraphqlWs icon indicating copy to clipboard operation
DjangoChannelsGraphqlWs copied to clipboard

It is not possible to access the `operation_id` from the `subscribe` or `unsubscribed` methods (or it very seriously lacks documentation).

Open ygbourhis opened this issue 3 years ago • 1 comments

It is not possible to access the operation_id from the subscribe or unsubscribed methods (or it very seriously lacks documentation).

If you create a service which you send you some logs, and test it like this with the wscat shell :

wscat --slash -s graphql-ws -c ws://localhost:8000/graphql -H "Authorization:Bearer some_token"
{"type": "connection_init", "payload": {}}

You then subscribe to a subscription like this like this (example):

{"id": "1", "type": "start", "payload": {"query": "subscription { logs(build_id: 97770) { logs { timestamp message } } }", "variables": null}}

And to another like this:

{"id": "2", "type": "start", "payload": {"query": "subscription { logs(build_id: 97769) { logs { timestamp message } } }", "variables": null}}

These subscriptions launch threads which will be referenced via the id. Each thread send there responses in a specific group created specifically in the subscribe method.

To unsubscribe to the first subscription you would send:

{"id": "1", "type": "stop"}

and.or tho the second one via

{"id": "2", "type": "stop"}

however, I found strictly no way to access this "id": "1" in the info parameter of the subscribe and unsubscribed methods. And therefore to terminate the desired thread and subscription instead of all.

Currently I subclassed channels_graphql_ws.GraphqlWsConsumer with these methods:

    async def _on_gql_start(self, operation_id, payload):
        self.scope['on_gql_start_operation_id'] = operation_id
        self.scope['on_gql_start_payload'] = payload
        return await super()._on_gql_start(operation_id, payload)

    async def _on_gql_stop(self, operation_id):
        self.scope['on_gql_stop_operation_id'] = operation_id
        return await super()._on_gql_stop(operation_id)

And I can find the operation_id in my channels_graphql_ws.Subscription.subscribe method via info.context.on_gql_start_operation_id and in the channels_graphql_ws.Subscription.unsubscribed method via info.context.on_gql_stop_operation_id. The payload is also interesting because I hash it, and if someone else subscribes to the same thing I reuse the existing thread (and join the existing subscription group).

is there a a proper way to access this operation_id? because it's in my opinion compulsory to be able to access it. Otherwise you need to unsubscribe all threads and not only one.

It is to note that the angular apollo graphql client launches multiple subscriptions in a unique websocket (channels_graphql_ws.GraphqlWsConsumer.on_connect). The Apollo client forges this operaion_id itself. And when you create a web app with angular which subscribes to multiple subscriptions in the same page, without the hack I did it was not possible to select multiple subscriptions on the same page and to start stop only the desired one on demand.

Let me know if anything was unclear.

Regards,

ygbourhis avatar Jan 12 '22 15:01 ygbourhis

The major issue with the workaround described above is that it only works with channels_graphql_ws.GraphqlWsConsumer.strict_ordering = True

Without this, the information is totally mixed up

ygbourhis avatar Feb 01 '22 16:02 ygbourhis

The version 1.0.0rc1 is available in PyPI, it has info.context.graphql_operation_id field to address this issue. Closing this, feel free to reopen or file a new one in case of problems.

prokher avatar Apr 27 '23 21:04 prokher