diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 68e5a93a..e28102c0 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -202,6 +202,7 @@ Again, the most checks can be performed by `make check` command, though some add 400: {"description": "Bad data is supplied", "schema": ErrorSchema}, # exception raised by this method 401: {"description": "Authorization required", "schema": ErrorSchema}, # should be always presented 403: {"description": "Access is forbidden", "schema": ErrorSchema}, # should be always presented + 404: {"description": "Repository is unknown", "schema": ErrorSchema}, # include if BaseView.service() method is called 500: {"description": "Internal server error", "schema": ErrorSchema}, # should be always presented }, security=[{"token": [POST_PERMISSION]}], diff --git a/src/ahriman/application/lock.py b/src/ahriman/application/lock.py index 706e60fd..0143d2c9 100644 --- a/src/ahriman/application/lock.py +++ b/src/ahriman/application/lock.py @@ -70,9 +70,8 @@ class Lock(LazyLogging): repository_id(RepositoryId): repository unique identifier configuration(Configuration): configuration instance """ - lock_suffix = f"{repository_id.name}_{repository_id.architecture}" self.path: Path | None = \ - args.lock.with_stem(f"{args.lock.stem}_{lock_suffix}") if args.lock is not None else None + args.lock.with_stem(f"{args.lock.stem}_{repository_id.id}") if args.lock is not None else None self.force: bool = args.force self.unsafe: bool = args.unsafe diff --git a/src/ahriman/web/views/base.py b/src/ahriman/web/views/base.py index 8778d114..75d3239b 100644 --- a/src/ahriman/web/views/base.py +++ b/src/ahriman/web/views/base.py @@ -18,7 +18,7 @@ # along with this program. If not, see . # from aiohttp_cors import CorsViewMixin # type: ignore[import-untyped] -from aiohttp.web import HTTPBadRequest, Request, StreamResponse, View +from aiohttp.web import HTTPBadRequest, HTTPNotFound, Request, StreamResponse, View from collections.abc import Awaitable, Callable from typing import Any, TypeVar @@ -245,10 +245,16 @@ class BaseView(View, CorsViewMixin): Returns: Watcher: build status watcher instance. If no repository provided, it will return the first one + + Raises: + HTTPNotFound: if no repository found """ if repository_id is None: repository_id = self.repository_id() - return self.services[repository_id] + try: + return self.services[repository_id] + except KeyError: + raise HTTPNotFound(reason=f"Repository {repository_id.id} is unknown") async def username(self) -> str | None: """ diff --git a/src/ahriman/web/views/v1/service/add.py b/src/ahriman/web/views/v1/service/add.py index d61df72e..776c7d72 100644 --- a/src/ahriman/web/views/v1/service/add.py +++ b/src/ahriman/web/views/v1/service/add.py @@ -46,6 +46,7 @@ class AddView(BaseView): 400: {"description": "Bad data is supplied", "schema": ErrorSchema}, 401: {"description": "Authorization required", "schema": ErrorSchema}, 403: {"description": "Access is forbidden", "schema": ErrorSchema}, + 404: {"description": "Repository is unknown", "schema": ErrorSchema}, 500: {"description": "Internal server error", "schema": ErrorSchema}, }, security=[{"token": [POST_PERMISSION]}], diff --git a/src/ahriman/web/views/v1/service/pgp.py b/src/ahriman/web/views/v1/service/pgp.py index 8f366c7b..8ab94c31 100644 --- a/src/ahriman/web/views/v1/service/pgp.py +++ b/src/ahriman/web/views/v1/service/pgp.py @@ -48,7 +48,7 @@ class PGPView(BaseView): 400: {"description": "Bad data is supplied", "schema": ErrorSchema}, 401: {"description": "Authorization required", "schema": ErrorSchema}, 403: {"description": "Access is forbidden", "schema": ErrorSchema}, - 404: {"description": "Package base is unknown", "schema": ErrorSchema}, + 404: {"description": "PGP key is unknown", "schema": ErrorSchema}, 500: {"description": "Internal server error", "schema": ErrorSchema}, }, security=[{"token": [GET_PERMISSION]}], @@ -75,7 +75,7 @@ class PGPView(BaseView): try: key = self.sign.key_download(server, key) except Exception: - raise HTTPNotFound + raise HTTPNotFound(reason=f"Key {key} is unknown") return json_response({"key": key}) diff --git a/src/ahriman/web/views/v1/service/process.py b/src/ahriman/web/views/v1/service/process.py index 0ed53d81..c364f886 100644 --- a/src/ahriman/web/views/v1/service/process.py +++ b/src/ahriman/web/views/v1/service/process.py @@ -45,7 +45,7 @@ class ProcessView(BaseView): 200: {"description": "Success response", "schema": ProcessSchema}, 401: {"description": "Authorization required", "schema": ErrorSchema}, 403: {"description": "Access is forbidden", "schema": ErrorSchema}, - 404: {"description": "Not found", "schema": ErrorSchema}, + 404: {"description": "Process is unknown", "schema": ErrorSchema}, 500: {"description": "Internal server error", "schema": ErrorSchema}, }, security=[{"token": [GET_PERMISSION]}], diff --git a/src/ahriman/web/views/v1/service/rebuild.py b/src/ahriman/web/views/v1/service/rebuild.py index 367b0037..cd41a974 100644 --- a/src/ahriman/web/views/v1/service/rebuild.py +++ b/src/ahriman/web/views/v1/service/rebuild.py @@ -46,6 +46,7 @@ class RebuildView(BaseView): 400: {"description": "Bad data is supplied", "schema": ErrorSchema}, 401: {"description": "Authorization required", "schema": ErrorSchema}, 403: {"description": "Access is forbidden", "schema": ErrorSchema}, + 404: {"description": "Repository is unknown", "schema": ErrorSchema}, 500: {"description": "Internal server error", "schema": ErrorSchema}, }, security=[{"token": [POST_PERMISSION]}], diff --git a/src/ahriman/web/views/v1/service/remove.py b/src/ahriman/web/views/v1/service/remove.py index c4d1a633..ccbee7ac 100644 --- a/src/ahriman/web/views/v1/service/remove.py +++ b/src/ahriman/web/views/v1/service/remove.py @@ -46,6 +46,7 @@ class RemoveView(BaseView): 400: {"description": "Bad data is supplied", "schema": ErrorSchema}, 401: {"description": "Authorization required", "schema": ErrorSchema}, 403: {"description": "Access is forbidden", "schema": ErrorSchema}, + 404: {"description": "Repository is unknown", "schema": ErrorSchema}, 500: {"description": "Internal server error", "schema": ErrorSchema}, }, security=[{"token": [POST_PERMISSION]}], diff --git a/src/ahriman/web/views/v1/service/request.py b/src/ahriman/web/views/v1/service/request.py index 8f629985..aa7eebf3 100644 --- a/src/ahriman/web/views/v1/service/request.py +++ b/src/ahriman/web/views/v1/service/request.py @@ -46,6 +46,7 @@ class RequestView(BaseView): 400: {"description": "Bad data is supplied", "schema": ErrorSchema}, 401: {"description": "Authorization required", "schema": ErrorSchema}, 403: {"description": "Access is forbidden", "schema": ErrorSchema}, + 404: {"description": "Repository is unknown", "schema": ErrorSchema}, 500: {"description": "Internal server error", "schema": ErrorSchema}, }, security=[{"token": [POST_PERMISSION]}], diff --git a/src/ahriman/web/views/v1/service/update.py b/src/ahriman/web/views/v1/service/update.py index 3bf355bd..b7187eb8 100644 --- a/src/ahriman/web/views/v1/service/update.py +++ b/src/ahriman/web/views/v1/service/update.py @@ -46,6 +46,7 @@ class UpdateView(BaseView): 400: {"description": "Bad data is supplied", "schema": ErrorSchema}, 401: {"description": "Authorization required", "schema": ErrorSchema}, 403: {"description": "Access is forbidden", "schema": ErrorSchema}, + 404: {"description": "Repository is unknown", "schema": ErrorSchema}, 500: {"description": "Internal server error", "schema": ErrorSchema}, }, security=[{"token": [POST_PERMISSION]}], diff --git a/src/ahriman/web/views/v1/service/upload.py b/src/ahriman/web/views/v1/service/upload.py index 89d88e2b..001957a6 100644 --- a/src/ahriman/web/views/v1/service/upload.py +++ b/src/ahriman/web/views/v1/service/upload.py @@ -101,7 +101,7 @@ class UploadView(BaseView): 400: {"description": "Bad data is supplied", "schema": ErrorSchema}, 401: {"description": "Authorization required", "schema": ErrorSchema}, 403: {"description": "Access is forbidden", "schema": ErrorSchema}, - 404: {"description": "Not found", "schema": ErrorSchema}, + 404: {"description": "Repository is unknown or endpoint is disabled", "schema": ErrorSchema}, 500: {"description": "Internal server error", "schema": ErrorSchema}, }, security=[{"token": [POST_PERMISSION]}], diff --git a/src/ahriman/web/views/v1/status/logs.py b/src/ahriman/web/views/v1/status/logs.py index 7c4d3ad3..0f6ce43c 100644 --- a/src/ahriman/web/views/v1/status/logs.py +++ b/src/ahriman/web/views/v1/status/logs.py @@ -51,6 +51,7 @@ class LogsView(BaseView): 204: {"description": "Success response"}, 401: {"description": "Authorization required", "schema": ErrorSchema}, 403: {"description": "Access is forbidden", "schema": ErrorSchema}, + 404: {"description": "Repository is unknown", "schema": ErrorSchema}, 500: {"description": "Internal server error", "schema": ErrorSchema}, }, security=[{"token": [DELETE_PERMISSION]}], @@ -78,7 +79,7 @@ class LogsView(BaseView): 200: {"description": "Success response", "schema": LogsSchema}, 401: {"description": "Authorization required", "schema": ErrorSchema}, 403: {"description": "Access is forbidden", "schema": ErrorSchema}, - 404: {"description": "Package base is unknown", "schema": ErrorSchema}, + 404: {"description": "Package base and/or repository are unknown", "schema": ErrorSchema}, 500: {"description": "Internal server error", "schema": ErrorSchema}, }, security=[{"token": [GET_PERMISSION]}], @@ -101,7 +102,7 @@ class LogsView(BaseView): try: _, status = self.service().package_get(package_base) except UnknownPackageError: - raise HTTPNotFound + raise HTTPNotFound(reason=f"Package {package_base} is unknown") logs = self.service().logs_get(package_base) response = { @@ -120,6 +121,7 @@ class LogsView(BaseView): 400: {"description": "Bad data is supplied", "schema": ErrorSchema}, 401: {"description": "Authorization required", "schema": ErrorSchema}, 403: {"description": "Access is forbidden", "schema": ErrorSchema}, + 404: {"description": "Repository is unknown", "schema": ErrorSchema}, 500: {"description": "Internal server error", "schema": ErrorSchema}, }, security=[{"token": [POST_PERMISSION]}], diff --git a/src/ahriman/web/views/v1/status/package.py b/src/ahriman/web/views/v1/status/package.py index 88e4e713..b7084331 100644 --- a/src/ahriman/web/views/v1/status/package.py +++ b/src/ahriman/web/views/v1/status/package.py @@ -52,6 +52,7 @@ class PackageView(BaseView): 204: {"description": "Success response"}, 401: {"description": "Authorization required", "schema": ErrorSchema}, 403: {"description": "Access is forbidden", "schema": ErrorSchema}, + 404: {"description": "Repository is unknown", "schema": ErrorSchema}, 500: {"description": "Internal server error", "schema": ErrorSchema}, }, security=[{"token": [DELETE_PERMISSION]}], @@ -79,7 +80,7 @@ class PackageView(BaseView): 200: {"description": "Success response", "schema": PackageStatusSchema(many=True)}, 401: {"description": "Authorization required", "schema": ErrorSchema}, 403: {"description": "Access is forbidden", "schema": ErrorSchema}, - 404: {"description": "Package base is unknown", "schema": ErrorSchema}, + 404: {"description": "Package base and/or repository are unknown", "schema": ErrorSchema}, 500: {"description": "Internal server error", "schema": ErrorSchema}, }, security=[{"token": [GET_PERMISSION]}], @@ -103,7 +104,7 @@ class PackageView(BaseView): try: package, status = self.service(repository_id).package_get(package_base) except UnknownPackageError: - raise HTTPNotFound + raise HTTPNotFound(reason=f"Package {package_base} is unknown") response = [ { @@ -123,6 +124,7 @@ class PackageView(BaseView): 400: {"description": "Bad data is supplied", "schema": ErrorSchema}, 401: {"description": "Authorization required", "schema": ErrorSchema}, 403: {"description": "Access is forbidden", "schema": ErrorSchema}, + 404: {"description": "Repository is unknown", "schema": ErrorSchema}, 500: {"description": "Internal server error", "schema": ErrorSchema}, }, security=[{"token": [POST_PERMISSION]}], diff --git a/src/ahriman/web/views/v1/status/packages.py b/src/ahriman/web/views/v1/status/packages.py index afa4a1ce..c620846f 100644 --- a/src/ahriman/web/views/v1/status/packages.py +++ b/src/ahriman/web/views/v1/status/packages.py @@ -52,6 +52,7 @@ class PackagesView(BaseView): 400: {"description": "Bad data is supplied", "schema": ErrorSchema}, 401: {"description": "Authorization required", "schema": ErrorSchema}, 403: {"description": "Access is forbidden", "schema": ErrorSchema}, + 404: {"description": "Repository is unknown", "schema": ErrorSchema}, 500: {"description": "Internal server error", "schema": ErrorSchema}, }, security=[{"token": [GET_PERMISSION]}], @@ -90,6 +91,7 @@ class PackagesView(BaseView): 204: {"description": "Success response"}, 401: {"description": "Authorization required", "schema": ErrorSchema}, 403: {"description": "Access is forbidden", "schema": ErrorSchema}, + 404: {"description": "Repository is unknown", "schema": ErrorSchema}, 500: {"description": "Internal server error", "schema": ErrorSchema}, }, security=[{"token": [POST_PERMISSION]}], diff --git a/src/ahriman/web/views/v1/status/status.py b/src/ahriman/web/views/v1/status/status.py index 7039699b..d16ae49e 100644 --- a/src/ahriman/web/views/v1/status/status.py +++ b/src/ahriman/web/views/v1/status/status.py @@ -51,6 +51,7 @@ class StatusView(BaseView): 200: {"description": "Success response", "schema": InternalStatusSchema}, 401: {"description": "Authorization required", "schema": ErrorSchema}, 403: {"description": "Access is forbidden", "schema": ErrorSchema}, + 404: {"description": "Repository is unknown", "schema": ErrorSchema}, 500: {"description": "Internal server error", "schema": ErrorSchema}, }, security=[{"token": [GET_PERMISSION]}], @@ -84,6 +85,7 @@ class StatusView(BaseView): 400: {"description": "Bad data is supplied", "schema": ErrorSchema}, 401: {"description": "Authorization required", "schema": ErrorSchema}, 403: {"description": "Access is forbidden", "schema": ErrorSchema}, + 404: {"description": "Repository is unknown", "schema": ErrorSchema}, 500: {"description": "Internal server error", "schema": ErrorSchema}, }, security=[{"token": [POST_PERMISSION]}], diff --git a/src/ahriman/web/views/v2/status/logs.py b/src/ahriman/web/views/v2/status/logs.py index ed391e44..c9e4a065 100644 --- a/src/ahriman/web/views/v2/status/logs.py +++ b/src/ahriman/web/views/v2/status/logs.py @@ -47,7 +47,7 @@ class LogsView(BaseView): 400: {"description": "Bad data is supplied", "schema": ErrorSchema}, 401: {"description": "Authorization required", "schema": ErrorSchema}, 403: {"description": "Access is forbidden", "schema": ErrorSchema}, - 404: {"description": "Package base is unknown", "schema": ErrorSchema}, + 404: {"description": "Package base and/or repository are unknown", "schema": ErrorSchema}, 500: {"description": "Internal server error", "schema": ErrorSchema}, }, security=[{"token": [GET_PERMISSION]}], @@ -71,7 +71,7 @@ class LogsView(BaseView): try: _, status = self.service().package_get(package_base) except UnknownPackageError: - raise HTTPNotFound + raise HTTPNotFound(reason=f"Package {package_base} is unknown") logs = self.service().logs_get(package_base, limit, offset) response = { diff --git a/tests/ahriman/application/test_lock.py b/tests/ahriman/application/test_lock.py index 23851685..1f787c4d 100644 --- a/tests/ahriman/application/test_lock.py +++ b/tests/ahriman/application/test_lock.py @@ -23,7 +23,7 @@ def test_path(args: argparse.Namespace, configuration: Configuration) -> None: assert Lock(args, repository_id, configuration).path is None args.lock = Path("/run/ahriman.lock") - assert Lock(args, repository_id, configuration).path == Path("/run/ahriman_aur-clone_x86_64.lock") + assert Lock(args, repository_id, configuration).path == Path("/run/ahriman_x86_64-aur-clone.lock") with pytest.raises(ValueError): args.lock = Path("/") diff --git a/tests/ahriman/web/views/test_view_base.py b/tests/ahriman/web/views/test_view_base.py index 6ec31af0..3faff16f 100644 --- a/tests/ahriman/web/views/test_view_base.py +++ b/tests/ahriman/web/views/test_view_base.py @@ -2,7 +2,7 @@ import pytest from multidict import MultiDict from aiohttp.test_utils import TestClient -from aiohttp.web import HTTPBadRequest +from aiohttp.web import HTTPBadRequest, HTTPNotFound from pytest_mock import MockerFixture from unittest.mock import AsyncMock @@ -240,6 +240,14 @@ def test_service_auto(base: BaseView, repository_id: RepositoryId, mocker: Mocke assert base.service() == base.services[repository_id] +def test_service_not_found(base: BaseView) -> None: + """ + must raise HTTPNotFound if no repository found + """ + with pytest.raises(HTTPNotFound): + base.service(RepositoryId("", "")) + + async def test_username(base: BaseView, mocker: MockerFixture) -> None: """ must return identity of logged-in user