diff --git a/src/ahriman/web/routes.py b/src/ahriman/web/routes.py index 8be56b90..f2faa601 100644 --- a/src/ahriman/web/routes.py +++ b/src/ahriman/web/routes.py @@ -17,6 +17,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . # +import re + from aiohttp.web import Application, View from collections.abc import Generator @@ -45,6 +47,23 @@ def _dynamic_routes(configuration: Configuration) -> Generator[tuple[str, type[V yield route, view +def _identifier(route: str) -> str: + """ + extract valid route identifier (aka name) for the route. This method replaces curly brackets by single colon + and replaces other special symbols (including slashes) by underscore + + Args: + route(str): source route + + Returns: + str: route with special symbols being replaced + """ + # replace special symbols + alphanum = re.sub(r"[^A-Za-z\d\-{}]", "_", route) + # finally replace curly brackets + return alphanum.replace("{", ":").replace("}", "") + + def setup_routes(application: Application, configuration: Configuration) -> None: """ setup all defined routes @@ -53,7 +72,8 @@ def setup_routes(application: Application, configuration: Configuration) -> None application(Application): web application instance configuration(Configuration): configuration instance """ - application.router.add_static("/static", configuration.getpath("web", "static_path"), follow_symlinks=True) + application.router.add_static("/static", configuration.getpath("web", "static_path"), name="_static", + follow_symlinks=True) for route, view in _dynamic_routes(configuration): - application.router.add_view(route, view) + application.router.add_view(route, view, name=_identifier(route)) diff --git a/src/ahriman/web/views/v1/packages/packages.py b/src/ahriman/web/views/v1/packages/packages.py index 779591fe..818addec 100644 --- a/src/ahriman/web/views/v1/packages/packages.py +++ b/src/ahriman/web/views/v1/packages/packages.py @@ -46,7 +46,7 @@ class PackagesView(StatusViewGuard, BaseView): ROUTES = ["/api/v1/packages"] @apidocs( - tags=["packages"], + tags=["Packages"], summary="Get packages list", description="Retrieve packages and their descriptors", permission=GET_PERMISSION, diff --git a/tests/ahriman/web/middlewares/test_metrics_handler.py b/tests/ahriman/web/middlewares/test_metrics_handler.py index ed6d976d..8bff58a6 100644 --- a/tests/ahriman/web/middlewares/test_metrics_handler.py +++ b/tests/ahriman/web/middlewares/test_metrics_handler.py @@ -2,12 +2,12 @@ import importlib import pytest import sys -import ahriman.web.middlewares.metrics_handler as metrics_handler - from aiohttp.web import HTTPNotFound from pytest_mock import MockerFixture from unittest.mock import AsyncMock +import ahriman.web.middlewares.metrics_handler as metrics_handler + async def test_metrics(mocker: MockerFixture) -> None: """ diff --git a/tests/ahriman/web/test_routes.py b/tests/ahriman/web/test_routes.py index bba6bf5e..4a07a569 100644 --- a/tests/ahriman/web/test_routes.py +++ b/tests/ahriman/web/test_routes.py @@ -3,7 +3,7 @@ from pathlib import Path from ahriman.core.configuration import Configuration from ahriman.core.utils import walk -from ahriman.web.routes import _dynamic_routes, setup_routes +from ahriman.web.routes import _dynamic_routes, _identifier, setup_routes def test_dynamic_routes(resource_path_root: Path, configuration: Configuration) -> None: @@ -22,9 +22,19 @@ def test_dynamic_routes(resource_path_root: Path, configuration: Configuration) assert len(set(routes.values())) == len(expected_views) +def test_identifier() -> None: + """ + must correctly extract route identifiers + """ + assert _identifier("/") == "_" + assert _identifier("/api/v1/status") == "_api_v1_status" + assert _identifier("/api/v1/packages/{package}") == "_api_v1_packages_:package" + + def test_setup_routes(application: Application, configuration: Configuration) -> None: """ must generate non-empty list of routes """ + application.router._named_resources = {} setup_routes(application, configuration) assert application.router.routes()