mirror of
https://github.com/arcan1s/ahriman.git
synced 2026-04-01 06:03:39 +00:00
feat: implement local reporter mode (#126)
* implement local reporter mode * simplify watcher class * review changes * do not update unknown status * allow empty key patches via api * fix some pylint warnings in tests
This commit is contained in:
@@ -25,6 +25,7 @@ from typing import TypeVar
|
||||
from ahriman.core.auth import Auth
|
||||
from ahriman.core.configuration import Configuration
|
||||
from ahriman.core.distributed import WorkersCache
|
||||
from ahriman.core.exceptions import UnknownPackageError
|
||||
from ahriman.core.sign.gpg import GPG
|
||||
from ahriman.core.spawn import Spawn
|
||||
from ahriman.core.status.watcher import Watcher
|
||||
@@ -218,12 +219,13 @@ class BaseView(View, CorsViewMixin):
|
||||
return RepositoryId(architecture, name)
|
||||
return next(iter(sorted(self.services.keys())))
|
||||
|
||||
def service(self, repository_id: RepositoryId | None = None) -> Watcher:
|
||||
def service(self, repository_id: RepositoryId | None = None, package_base: str | None = None) -> Watcher:
|
||||
"""
|
||||
get status watcher instance
|
||||
|
||||
Args:
|
||||
repository_id(RepositoryId | None, optional): repository unique identifier (Default value = None)
|
||||
package_base(str | None, optional): package base to validate if exists (Default value = None)
|
||||
|
||||
Returns:
|
||||
Watcher: build status watcher instance. If no repository provided, it will return the first one
|
||||
@@ -234,9 +236,11 @@ class BaseView(View, CorsViewMixin):
|
||||
if repository_id is None:
|
||||
repository_id = self.repository_id()
|
||||
try:
|
||||
return self.services[repository_id]
|
||||
return self.services[repository_id](package_base)
|
||||
except KeyError:
|
||||
raise HTTPNotFound(reason=f"Repository {repository_id.id} is unknown")
|
||||
except UnknownPackageError:
|
||||
raise HTTPNotFound(reason=f"Package {package_base} is unknown")
|
||||
|
||||
async def username(self) -> str | None:
|
||||
"""
|
||||
|
||||
@@ -19,9 +19,8 @@
|
||||
#
|
||||
import aiohttp_apispec # type: ignore[import-untyped]
|
||||
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, HTTPNotFound, Response, json_response
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, Response, json_response
|
||||
|
||||
from ahriman.core.exceptions import UnknownPackageError
|
||||
from ahriman.models.changes import Changes
|
||||
from ahriman.models.user_access import UserAccess
|
||||
from ahriman.web.schemas import AuthSchema, ChangesSchema, ErrorSchema, PackageNameSchema, RepositoryIdSchema
|
||||
@@ -70,10 +69,7 @@ class ChangesView(StatusViewGuard, BaseView):
|
||||
"""
|
||||
package_base = self.request.match_info["package"]
|
||||
|
||||
try:
|
||||
changes = self.service().package_changes_get(package_base)
|
||||
except UnknownPackageError:
|
||||
raise HTTPNotFound(reason=f"Package {package_base} is unknown")
|
||||
changes = self.service(package_base=package_base).package_changes_get(package_base)
|
||||
|
||||
return json_response(changes.view())
|
||||
|
||||
@@ -113,7 +109,6 @@ class ChangesView(StatusViewGuard, BaseView):
|
||||
raise HTTPBadRequest(reason=str(ex))
|
||||
|
||||
changes = Changes(last_commit_sha, change)
|
||||
repository_id = self.repository_id()
|
||||
self.service(repository_id).database.changes_insert(package_base, changes, repository_id)
|
||||
self.service().package_changes_update(package_base, changes)
|
||||
|
||||
raise HTTPNoContent
|
||||
|
||||
113
src/ahriman/web/views/v1/packages/dependencies.py
Normal file
113
src/ahriman/web/views/v1/packages/dependencies.py
Normal file
@@ -0,0 +1,113 @@
|
||||
#
|
||||
# Copyright (c) 2021-2024 ahriman team.
|
||||
#
|
||||
# This file is part of ahriman
|
||||
# (see https://github.com/arcan1s/ahriman).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import aiohttp_apispec # type: ignore[import-untyped]
|
||||
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, Response, json_response
|
||||
|
||||
from ahriman.models.dependencies import Dependencies
|
||||
from ahriman.models.user_access import UserAccess
|
||||
from ahriman.web.schemas import AuthSchema, DependenciesSchema, ErrorSchema, PackageNameSchema, RepositoryIdSchema
|
||||
from ahriman.web.views.base import BaseView
|
||||
from ahriman.web.views.status_view_guard import StatusViewGuard
|
||||
|
||||
|
||||
class DependenciesView(StatusViewGuard, BaseView):
|
||||
"""
|
||||
package dependencies web view
|
||||
|
||||
Attributes:
|
||||
GET_PERMISSION(UserAccess): (class attribute) get permissions of self
|
||||
POST_PERMISSION(UserAccess): (class attribute) post permissions of self
|
||||
"""
|
||||
|
||||
GET_PERMISSION = UserAccess.Reporter
|
||||
POST_PERMISSION = UserAccess.Full
|
||||
ROUTES = ["/api/v1/packages/{package}/dependencies"]
|
||||
|
||||
@aiohttp_apispec.docs(
|
||||
tags=["Build"],
|
||||
summary="Get package dependencies",
|
||||
description="Retrieve package implicit dependencies",
|
||||
responses={
|
||||
200: {"description": "Success response", "schema": DependenciesSchema},
|
||||
401: {"description": "Authorization required", "schema": ErrorSchema},
|
||||
403: {"description": "Access is forbidden", "schema": ErrorSchema},
|
||||
404: {"description": "Package base and/or repository are unknown", "schema": ErrorSchema},
|
||||
500: {"description": "Internal server error", "schema": ErrorSchema},
|
||||
},
|
||||
security=[{"token": [GET_PERMISSION]}],
|
||||
)
|
||||
@aiohttp_apispec.cookies_schema(AuthSchema)
|
||||
@aiohttp_apispec.match_info_schema(PackageNameSchema)
|
||||
@aiohttp_apispec.querystring_schema(RepositoryIdSchema)
|
||||
async def get(self) -> Response:
|
||||
"""
|
||||
get package dependencies
|
||||
|
||||
Returns:
|
||||
Response: 200 with package implicit dependencies on success
|
||||
|
||||
Raises:
|
||||
HTTPNotFound: if package base is unknown
|
||||
"""
|
||||
package_base = self.request.match_info["package"]
|
||||
|
||||
dependencies = self.service(package_base=package_base).package_dependencies_get(package_base)
|
||||
|
||||
return json_response(dependencies.view())
|
||||
|
||||
@aiohttp_apispec.docs(
|
||||
tags=["Build"],
|
||||
summary="Update package dependencies",
|
||||
description="Set package implicit dependencies",
|
||||
responses={
|
||||
204: {"description": "Success response"},
|
||||
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]}],
|
||||
)
|
||||
@aiohttp_apispec.cookies_schema(AuthSchema)
|
||||
@aiohttp_apispec.match_info_schema(PackageNameSchema)
|
||||
@aiohttp_apispec.querystring_schema(RepositoryIdSchema)
|
||||
@aiohttp_apispec.json_schema(DependenciesSchema)
|
||||
async def post(self) -> None:
|
||||
"""
|
||||
insert new package dependencies
|
||||
|
||||
Raises:
|
||||
HTTPBadRequest: if bad data is supplied
|
||||
HTTPNoContent: in case of success response
|
||||
"""
|
||||
package_base = self.request.match_info["package"]
|
||||
|
||||
try:
|
||||
data = await self.request.json()
|
||||
data["package_base"] = package_base # read from path instead of object
|
||||
dependencies = Dependencies.from_json(data)
|
||||
except Exception as ex:
|
||||
raise HTTPBadRequest(reason=str(ex))
|
||||
|
||||
self.service(package_base=package_base).package_dependencies_update(package_base, dependencies)
|
||||
|
||||
raise HTTPNoContent
|
||||
@@ -25,8 +25,8 @@ from ahriman.core.exceptions import UnknownPackageError
|
||||
from ahriman.core.util import pretty_datetime
|
||||
from ahriman.models.log_record_id import LogRecordId
|
||||
from ahriman.models.user_access import UserAccess
|
||||
from ahriman.web.schemas import AuthSchema, ErrorSchema, LogsSchema, PackageNameSchema, RepositoryIdSchema, \
|
||||
VersionedLogSchema
|
||||
from ahriman.web.schemas import AuthSchema, ErrorSchema, LogsSchema, PackageNameSchema, PackageVersionSchema, \
|
||||
RepositoryIdSchema, VersionedLogSchema
|
||||
from ahriman.web.views.base import BaseView
|
||||
from ahriman.web.views.status_view_guard import StatusViewGuard
|
||||
|
||||
@@ -60,7 +60,7 @@ class LogsView(StatusViewGuard, BaseView):
|
||||
)
|
||||
@aiohttp_apispec.cookies_schema(AuthSchema)
|
||||
@aiohttp_apispec.match_info_schema(PackageNameSchema)
|
||||
@aiohttp_apispec.querystring_schema(RepositoryIdSchema)
|
||||
@aiohttp_apispec.querystring_schema(PackageVersionSchema)
|
||||
async def delete(self) -> None:
|
||||
"""
|
||||
delete package logs
|
||||
@@ -69,7 +69,8 @@ class LogsView(StatusViewGuard, BaseView):
|
||||
HTTPNoContent: on success response
|
||||
"""
|
||||
package_base = self.request.match_info["package"]
|
||||
self.service().logs_remove(package_base, None)
|
||||
version = self.request.query.get("version")
|
||||
self.service().package_logs_remove(package_base, version)
|
||||
|
||||
raise HTTPNoContent
|
||||
|
||||
@@ -103,7 +104,7 @@ class LogsView(StatusViewGuard, BaseView):
|
||||
|
||||
try:
|
||||
_, status = self.service().package_get(package_base)
|
||||
logs = self.service().logs_get(package_base)
|
||||
logs = self.service(package_base=package_base).package_logs_get(package_base, -1, 0)
|
||||
except UnknownPackageError:
|
||||
raise HTTPNotFound(reason=f"Package {package_base} is unknown")
|
||||
|
||||
@@ -149,6 +150,6 @@ class LogsView(StatusViewGuard, BaseView):
|
||||
except Exception as ex:
|
||||
raise HTTPBadRequest(reason=str(ex))
|
||||
|
||||
self.service().logs_update(LogRecordId(package_base, version), created, record)
|
||||
self.service().package_logs_add(LogRecordId(package_base, version), created, record)
|
||||
|
||||
raise HTTPNoContent
|
||||
|
||||
@@ -152,7 +152,10 @@ class PackageView(StatusViewGuard, BaseView):
|
||||
raise HTTPBadRequest(reason=str(ex))
|
||||
|
||||
try:
|
||||
self.service().package_update(package_base, status, package)
|
||||
if package is None:
|
||||
self.service().package_status_update(package_base, status)
|
||||
else:
|
||||
self.service().package_update(package, status)
|
||||
except UnknownPackageError:
|
||||
raise HTTPBadRequest(reason=f"Package {package_base} is unknown, but no package body set")
|
||||
|
||||
|
||||
@@ -63,7 +63,8 @@ class PatchView(StatusViewGuard, BaseView):
|
||||
"""
|
||||
package_base = self.request.match_info["package"]
|
||||
variable = self.request.match_info["patch"]
|
||||
self.service().patches_remove(package_base, variable)
|
||||
|
||||
self.service().package_patches_remove(package_base, variable)
|
||||
|
||||
raise HTTPNoContent
|
||||
|
||||
@@ -95,7 +96,7 @@ class PatchView(StatusViewGuard, BaseView):
|
||||
package_base = self.request.match_info["package"]
|
||||
variable = self.request.match_info["patch"]
|
||||
|
||||
patches = self.service().patches_get(package_base, variable)
|
||||
patches = self.service().package_patches_get(package_base, variable)
|
||||
|
||||
selected = next((patch for patch in patches if patch.key == variable), None)
|
||||
if selected is None:
|
||||
|
||||
@@ -63,7 +63,7 @@ class PatchesView(StatusViewGuard, BaseView):
|
||||
Response: 200 with package patches on success
|
||||
"""
|
||||
package_base = self.request.match_info["package"]
|
||||
patches = self.service().patches_get(package_base, None)
|
||||
patches = self.service().package_patches_get(package_base, None)
|
||||
|
||||
response = [patch.view() for patch in patches]
|
||||
return json_response(response)
|
||||
@@ -96,11 +96,11 @@ class PatchesView(StatusViewGuard, BaseView):
|
||||
|
||||
try:
|
||||
data = await self.request.json()
|
||||
key = data["key"]
|
||||
key = data.get("key")
|
||||
value = data["value"]
|
||||
except Exception as ex:
|
||||
raise HTTPBadRequest(reason=str(ex))
|
||||
|
||||
self.service().patches_update(package_base, PkgbuildPatch(key, value))
|
||||
self.service().package_patches_update(package_base, PkgbuildPatch(key, value))
|
||||
|
||||
raise HTTPNoContent
|
||||
|
||||
@@ -19,9 +19,8 @@
|
||||
#
|
||||
import aiohttp_apispec # type: ignore[import-untyped]
|
||||
|
||||
from aiohttp.web import HTTPNotFound, Response, json_response
|
||||
from aiohttp.web import Response, json_response
|
||||
|
||||
from ahriman.core.exceptions import UnknownPackageError
|
||||
from ahriman.models.user_access import UserAccess
|
||||
from ahriman.web.schemas import AuthSchema, ErrorSchema, LogSchema, PackageNameSchema, PaginationSchema
|
||||
from ahriman.web.views.base import BaseView
|
||||
@@ -68,10 +67,8 @@ class LogsView(StatusViewGuard, BaseView):
|
||||
"""
|
||||
package_base = self.request.match_info["package"]
|
||||
limit, offset = self.page()
|
||||
try:
|
||||
logs = self.service().logs_get(package_base, limit, offset)
|
||||
except UnknownPackageError:
|
||||
raise HTTPNotFound(reason=f"Package {package_base} is unknown")
|
||||
|
||||
logs = self.service(package_base=package_base).package_logs_get(package_base, limit, offset)
|
||||
|
||||
response = [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user