From 9d915641a346406daeb88e6e2c005c16bc4e798d Mon Sep 17 00:00:00 2001 From: Oliver Sanders Date: Wed, 9 Aug 2023 16:47:25 +0100 Subject: [PATCH] support the graphql-ws subprotocol * The protocol for message exchange employed over a websocket connection is referred as the subprotocol. * We currently use the `graphql-ws` subprotocol for serving GraphQL subscriptions over websockets. * The JavaScript library which implements the `grahpql-ws` subprotocol is no longer maintained. * This PR adds support for the `graphql-transport-ws` subprotocol, allowing us to migrate our client away from this unsupported library. * This requires https://github.com/graphql-python/graphql-ws/pull/65 and a new release of the Python graphql-ws library to work. --- cylc/uiserver/handlers.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/cylc/uiserver/handlers.py b/cylc/uiserver/handlers.py index aecef402..9aa693d6 100644 --- a/cylc/uiserver/handlers.py +++ b/cylc/uiserver/handlers.py @@ -23,7 +23,7 @@ from graphene_tornado.tornado_graphql_handler import TornadoGraphQLHandler from graphql import get_default_backend -from graphql_ws.constants import GRAPHQL_WS +from graphql_ws.constants import GRAPHQL_WS, TRANSPORT_WS_PROTOCOL from jupyter_server.base.handlers import JupyterHandler from tornado import web, websocket from tornado.ioloop import IOLoop @@ -36,9 +36,10 @@ from cylc.uiserver.authorise import Authorization, AuthorizationMiddleware from cylc.uiserver.resolvers import Resolvers from cylc.uiserver.websockets import authenticated as websockets_authenticated -from cylc.uiserver.websockets.tornado import TornadoSubscriptionServer + if TYPE_CHECKING: from graphql.execution import ExecutionResult + from cylc.uiserver.websockets.tornado import TornadoSubscriptionServer ME = getpass.getuser() @@ -367,11 +368,15 @@ class SubscriptionHandler(CylcAppHandler, websocket.WebSocketHandler): # No authorization decorators here, auth handled in AuthorizationMiddleware def initialize(self, sub_server, resolvers, sub_statuses=None): self.queue: Queue = Queue(100) - self.subscription_server: TornadoSubscriptionServer = sub_server + self.subscription_server: 'TornadoSubscriptionServer' = sub_server self.resolvers: Resolvers = resolvers self.sub_statuses: Dict = sub_statuses def select_subprotocol(self, subprotocols): + if TRANSPORT_WS_PROTOCOL in subprotocols: + # use graphql-transport-ws out of preference + return TRANSPORT_WS_PROTOCOL + # fallback to graphql-ws if required return GRAPHQL_WS @websockets_authenticated @@ -415,5 +420,6 @@ def context(self): self.get_current_user() ).get('name'), 'ops_queue': {}, - 'sub_statuses': self.sub_statuses + 'sub_statuses': self.sub_statuses, + 'subprotocols': [self.selected_subprotocol], }