From 16951e417699215a8a0d03bf64e8de75030e5023 Mon Sep 17 00:00:00 2001 From: John Chilton Date: Wed, 16 Dec 2020 15:34:28 -0500 Subject: [PATCH] Add FastAPI roles controller, de-duplicate app initialization for FastAPI. --- lib/galaxy/webapps/galaxy/api/roles.py | 51 +++++++++++++++++++++++++- lib/galaxy/webapps/galaxy/fast_app.py | 14 +++++++ lib/galaxy_test/driver/driver_util.py | 8 +--- scripts/fapi.py | 12 +----- 4 files changed, 68 insertions(+), 17 deletions(-) create mode 100644 lib/galaxy/webapps/galaxy/fast_app.py diff --git a/lib/galaxy/webapps/galaxy/api/roles.py b/lib/galaxy/webapps/galaxy/api/roles.py index f7d132180cf..a780b51fa4e 100644 --- a/lib/galaxy/webapps/galaxy/api/roles.py +++ b/lib/galaxy/webapps/galaxy/api/roles.py @@ -5,21 +5,41 @@ import json import logging from typing import List +from fastapi import ( + Body, + Depends, +) +from fastapi_utils.cbv import cbv +from fastapi_utils.inferring_router import InferringRouter as APIRouter from pydantic import ( BaseModel, ) from galaxy import web +from galaxy.app import UniverseApplication from galaxy.managers.base import decode_id from galaxy.managers.roles import ( + RoleManager, RoleDefeinitionModel, RoleModel, ) +from galaxy.schema.fields import EncodedDatabaseIdField from galaxy.webapps.base.controller import BaseAPIController, url_for +from . import ( + get_admin_user, + get_app, + get_trans, +) +from galaxy.work.context import ( + SessionRequestContext, +) log = logging.getLogger(__name__) +router = APIRouter(tags=["roles"]) + + class RoleListModel(BaseModel): __root__: List[RoleModel] @@ -27,10 +47,39 @@ class RoleListModel(BaseModel): def role_to_model(trans, role): item = role.to_dict(view='element', value_mapper={'id': trans.security.encode_id}) role_id = trans.security.encode_id(role.id) - item['url'] = url_for('role', id=role_id) + try: + item['url'] = url_for('role', id=role_id) + except AttributeError: + item['url'] = "*deprecated attribute not filled in by FastAPI server*" return RoleModel(**item) +def get_role_manager(app: UniverseApplication = Depends(get_app)) -> RoleManager: + return app.role_manager + + + +@cbv(router) +class FastAPIRoles: + role_manager: RoleManager = Depends(get_role_manager) + + @router.get('/') + def index(self, trans: SessionRequestContext = Depends(get_trans)) -> RoleListModel: + roles = self.role_manager.list_displayable_roles(trans) + return RoleListModel(__root__=[role_to_model(trans, r) for r in roles]) + + @router.get('/{id}') + def show(self, id: EncodedDatabaseIdField, trans: SessionRequestContext = Depends(get_trans)) -> RoleModel: + role_id = trans.app.security.decode_id(id) + role = self.role_manager.get(trans, role_id) + return role_to_model(trans, role) + + @router.put("/") + def create(self, trans: SessionRequestContext = Depends(get_trans), admin_user=Depends(get_admin_user), role_definition_model: RoleDefeinitionModel = Body(...)) -> RoleModel: + role = self.role_manager.create(trans, role_definition_model) + return role_to_model(trans, role) + + class RoleAPIController(BaseAPIController): @web.expose_api diff --git a/lib/galaxy/webapps/galaxy/fast_app.py b/lib/galaxy/webapps/galaxy/fast_app.py new file mode 100644 index 00000000000..90ef4830997 --- /dev/null +++ b/lib/galaxy/webapps/galaxy/fast_app.py @@ -0,0 +1,14 @@ +from fastapi import FastAPI +from fastapi.middleware.wsgi import WSGIMiddleware + + +def initialize_fast_app(gx_app, app=None): + if app is None: + app = FastAPI() + wsgi_handler = WSGIMiddleware(gx_app) + from galaxy.webapps.galaxy.api import jobs + from galaxy.webapps.galaxy.api import roles + app.include_router(jobs.router, prefix='/api/jobs') + app.include_router(roles.router, prefix='/api/roles') + app.mount('/', wsgi_handler) + return app diff --git a/lib/galaxy_test/driver/driver_util.py b/lib/galaxy_test/driver/driver_util.py index c93c743adea..321eb6ad4f2 100644 --- a/lib/galaxy_test/driver/driver_util.py +++ b/lib/galaxy_test/driver/driver_util.py @@ -869,12 +869,8 @@ def launch_uvicorn(gx_app, webapp_factory, kwargs, prefix=DEFAULT_CONFIG_PREFIX, static_enabled=True, register_shutdown_at_exit=False ) - wsgi_handler = WSGIMiddleware(gx) - from galaxy.webapps.galaxy.api import jobs - from fastapi import FastAPI - app = FastAPI() - app.include_router(jobs.router, prefix='/api/jobs') - app.mount('/', wsgi_handler) + from galaxy.webapps.galaxy.fast_app import initialize_fast_app + app = initialize_fast_app(gx) server, port = uvicorn_serve(app, host=host, port=port) set_and_wait_for_http_target(prefix, host, port) log.info(f"Embedded uvicorn web server for {name} started at {host}:{port}") diff --git a/scripts/fapi.py b/scripts/fapi.py index fcf5390e547..28dac203008 100644 --- a/scripts/fapi.py +++ b/scripts/fapi.py @@ -1,6 +1,4 @@ import uvicorn -from fastapi import FastAPI -from fastapi.middleware.wsgi import WSGIMiddleware from galaxy_main import ( GalaxyConfigBuilder, main, @@ -8,6 +6,7 @@ from galaxy_main import ( from galaxy.util.properties import load_app_properties from galaxy.webapps.galaxy.buildapp import app_factory +from galaxy.webapps.galaxy.fast_app import initialize_fast_app app = FastAPI() @@ -17,14 +16,7 @@ def wsgiloop(args, log): kwds = config_builder.app_kwds() kwds = load_app_properties(**kwds) gx = app_factory(global_conf=config_builder.global_conf(), **kwds) - wsgi_handler = WSGIMiddleware(gx) - - # app factory will import api controllers while passing in app object, - # this needs to happen before we can import the router from individual api modules - # (until we've replaced everything with FastAPI, at which point we can do normal imports). - from galaxy.webapps.galaxy.api import jobs - app.include_router(jobs.router, prefix='/api/jobs') - app.mount('/', wsgi_handler) + initialize_fast_app(gx, app=app) uvicorn.run(app)