From 32373f0f0081ac1325bf6aeb894c714d5602b608 Mon Sep 17 00:00:00 2001 From: Oliver Sanders Date: Fri, 1 Nov 2024 11:55:36 +0000 Subject: [PATCH] add jupyter extensions to userprofile handler --- cylc/uiserver/handlers.py | 16 +++++++++++++++- cylc/uiserver/tests/test_handlers.py | 17 +++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/cylc/uiserver/handlers.py b/cylc/uiserver/handlers.py index c4e50fcc..f238f2e8 100644 --- a/cylc/uiserver/handlers.py +++ b/cylc/uiserver/handlers.py @@ -18,6 +18,7 @@ import json import getpass import os +import re from typing import TYPE_CHECKING, Callable, Dict from graphene_tornado.tornado_graphql_handler import TornadoGraphQLHandler @@ -44,6 +45,7 @@ ME = getpass.getuser() +RE_SLASH = x = re.compile(r'\/+') def authorised(fun: Callable) -> Callable: @@ -258,7 +260,6 @@ def set_default_headers(self) -> None: self.set_header("Content-Type", 'application/json') @web.authenticated - # @authorised TODO: I can't think why we would want to authorise this def get(self): user_info = { **self.current_user.__dict__, @@ -281,6 +282,19 @@ def get(self): user_info['mode'] = 'single user' else: user_info['mode'] = 'multi user' + + user_info['extensions'] = { + app.name: RE_SLASH.sub( + '/', f'{self.serverapp.base_url}/{app.default_url}' + ) + for extension_apps + in self.serverapp.extension_manager.extension_apps.values() + # filter out extensions that do not provide a default_url OR + # set it to the root endpoint. + for app in extension_apps + if getattr(app, 'default_url', '/') != '/' + } + self.write(json.dumps(user_info)) diff --git a/cylc/uiserver/tests/test_handlers.py b/cylc/uiserver/tests/test_handlers.py index 5f10676d..e07d6066 100644 --- a/cylc/uiserver/tests/test_handlers.py +++ b/cylc/uiserver/tests/test_handlers.py @@ -15,6 +15,7 @@ from functools import partial from getpass import getuser +import json from unittest import mock from unittest.mock import MagicMock import pytest @@ -120,3 +121,19 @@ def test_assert_callback_handler_gets_called(self): self.io_loop.run_sync(handler.open, get_async_test_timeout()) handler.subscription_server.handle.assert_called_once() + + +@pytest.mark.integration +async def test_userprofile( + jp_fetch, cylc_uis, jp_serverapp, +): + """Test the userprofile endpoint.""" + # patch the default_url back to how it is set in cylc.uiserver.app + cylc_uis.default_url = '/cylc' + + response = await jp_fetch('cylc', 'userprofile') + user_profile = json.loads(response.body.decode()) + assert user_profile['username'] == getuser() + assert user_profile['owner'] == getuser() + assert 'read' in user_profile['permissions'] + assert 'cylc' in user_profile['extensions']