Compare commits

...

2 Commits

Author SHA1 Message Date
06653f815b make apispec dependency optional 2024-12-21 17:40:48 +02:00
6738f9206d type: remove unused typeguard 2024-12-21 17:02:09 +02:00
71 changed files with 666 additions and 582 deletions

View File

@ -72,8 +72,9 @@ package_ahriman-triggers() {
package_ahriman-web() {
pkgname='ahriman-web'
pkgdesc="ArcH linux ReposItory MANager, web server"
depends=("$pkgbase-core=$pkgver" 'python-aiohttp-apispec>=3.0.0' 'python-aiohttp-cors' 'python-aiohttp-jinja2')
depends=("$pkgbase-core=$pkgver" 'python-aiohttp-cors' 'python-aiohttp-jinja2')
optdepends=('python-aioauth-client: OAuth2 authorization support'
'python-aiohttp-apispec>=3.0.0: autogenerated API documentation'
'python-aiohttp-security: authorization support'
'python-aiohttp-session: authorization support'
'python-cryptography: authorization support')

View File

@ -21,7 +21,6 @@ import configparser
import shlex
import sys
from collections.abc import Callable
from pathlib import Path
from typing import Any, Self
@ -68,7 +67,6 @@ class Configuration(configparser.RawConfigParser):
_LEGACY_ARCHITECTURE_SPECIFIC_SECTIONS = ["web"]
ARCHITECTURE_SPECIFIC_SECTIONS = ["alpm", "build", "sign"]
SYSTEM_CONFIGURATION_PATH = Path(sys.prefix) / "share" / "ahriman" / "settings" / "ahriman.ini"
converters: dict[str, Callable[[str], Any]] # typing guard
def __init__(self, allow_no_value: bool = False, allow_multi_key: bool = True) -> None:
"""

View File

@ -0,0 +1,32 @@
#
# 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/>.
#
try:
import aiohttp_apispec # type: ignore[import-untyped]
from marshmallow import Schema, fields
except ImportError:
from unittest.mock import Mock
Schema = object # type: ignore[assignment,misc]
aiohttp_apispec = None
fields = Mock()
__all__ = ["Schema", "aiohttp_apispec", "fields"]

View File

@ -0,0 +1,135 @@
#
# 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/>.
#
from typing import Any, Callable
from ahriman.models.user_access import UserAccess
from ahriman.web.apispec import Schema, aiohttp_apispec
from ahriman.web.schemas import AuthSchema, ErrorSchema
__all__ = ["apidocs"]
def _response_schema(response: Schema | type[Schema] | None, code: int | None = None,
error_400_enabled: bool = False, error_403_enabled: bool = True,
error_404_description: str | None = None) -> dict[int, Any]:
"""
render response schema specification
Args:
response(Schema | type[Schema] | None): response schema type, set ``None`` for empty responses
code(int | None, optional): code for the success response. If none set it will be defined automatically
(Default value = None)
error_400_enabled(bool, optional): include response for 404 codes (Default value = False)
error_403_enabled(bool, optional): include response for 403 codes (Default value = False)
error_404_description(str | None, optional): description for 404 codes if available (Default value = None)
Returns:
dict[int, Any]: response schema in apispec format
"""
schema = {
401: {"description": "Authorization required", "schema": ErrorSchema},
500: {"description": "Internal server error", "schema": ErrorSchema},
}
if code is None:
code = 200 if response is not None else 204
schema[code] = {"description": "Success response"}
if response is not None:
schema[code]["schema"] = response
if error_400_enabled:
schema[400] = {"description": "Bad request", "schema": ErrorSchema}
if error_403_enabled:
schema[403] = {"description": "Access is forbidden", "schema": ErrorSchema}
if error_404_description is not None:
schema[404] = {"description": error_404_description, "schema": ErrorSchema}
return schema
def apidocs(*,
tags: list[str],
summary: str,
description: str,
permission: UserAccess,
code: int | None = None,
error_400_enabled: bool = False,
error_404_description: str | None = None,
schema: Schema | type[Schema] | None = None,
match_schema: Schema | type[Schema] | None = None,
query_schema: Schema | type[Schema] | None = None,
body_schema: Schema | type[Schema] | None = None,
body_location: str = "json",
) -> Callable[..., Any]:
"""
wrapper around :mod:`aiohttp_apispec` to decorate HTTP methods
Args:
tags(list[str]): list of tags for the endpoint
summary(str): summary for the endpoint
description(str): long description for the endpoint
code(int | None, optional): code for the success response. If none set it will be defined automatically
(Default value = None)
error_400_enabled(bool, optional): include response for 404 codes (Default value = False)
error_404_description(str | None, optional): description for 404 codes if available (Default value = None)
permission(UserAccess, optional): permission to access endpoint (Default value = UserAccess.Unauthorized)
schema(Schema | type[Schema] | None): response schema type, set ``None`` for empty responses
(Default value = None)
match_schema(Schema | type[Schema] | None): schema for uri matcher if used (Default value = None)
query_schema(Schema | type[Schema] | None): query string schema type, set ``None`` if not applicable
(Default value = None)
body_schema(Schema | type[Schema] | None): body schema type, set ``None`` if not applicable
(Default value = None)
body_location(str, optional): body location name (Default value = "json")
Returns:
Callable[..., Any]: decorated function
"""
authorization_required = permission != UserAccess.Unauthorized
def wrapper(handler: Callable[..., Any]) -> Callable[..., Any]:
if aiohttp_apispec is None:
return handler # apispec is disabled
handler = aiohttp_apispec.docs(
tags=tags,
summary=summary,
description=description,
responses=_response_schema(schema, code, error_400_enabled, authorization_required, error_404_description),
security=[{"token": [permission]}],
)(handler)
# request schemas
if authorization_required:
handler = aiohttp_apispec.cookies_schema(AuthSchema)(handler)
if match_schema is not None:
handler = aiohttp_apispec.match_info_schema(match_schema)(handler)
if query_schema is not None:
handler = aiohttp_apispec.querystring_schema(query_schema)(handler)
if body_schema is not None:
handler = aiohttp_apispec.request_schema(
body_schema, locations=[body_location], put_into=body_location)(handler)
return handler
return wrapper

View File

@ -17,12 +17,11 @@
# 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 Application
from typing import Any
from ahriman import __version__
from ahriman.web.apispec import aiohttp_apispec
from ahriman.web.keys import ConfigurationKey
@ -101,7 +100,7 @@ def _servers(application: Application) -> list[dict[str, Any]]:
}]
def setup_apispec(application: Application) -> aiohttp_apispec.AiohttpApiSpec:
def setup_apispec(application: Application) -> Any:
"""
setup swagger api specification
@ -109,8 +108,11 @@ def setup_apispec(application: Application) -> aiohttp_apispec.AiohttpApiSpec:
application(Application): web application instance
Returns:
aiohttp_apispec.AiohttpApiSpec: created specification instance
Any: created specification instance if module is available
"""
if aiohttp_apispec is None:
return None
return aiohttp_apispec.setup_aiohttp_apispec(
application,
url="/api-docs/swagger.json",

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman.web.apispec import Schema, fields
class AURPackageSchema(Schema):

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman.web.apispec import Schema, fields
class AuthSchema(Schema):

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman.web.apispec import Schema, fields
class BuildOptionsSchema(Schema):

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman.web.apispec import Schema, fields
class ChangesSchema(Schema):

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman.web.apispec import Schema, fields
class CountersSchema(Schema):

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman.web.apispec import Schema, fields
class DependenciesSchema(Schema):

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman.web.apispec import Schema, fields
class ErrorSchema(Schema):

View File

@ -17,9 +17,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman.models.event import EventType
from ahriman.web.apispec import Schema, fields
class EventSchema(Schema):

View File

@ -17,9 +17,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import fields
from ahriman.models.event import EventType
from ahriman.web.apispec import fields
from ahriman.web.schemas.pagination_schema import PaginationSchema

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman.web.apispec import Schema, fields
class FileSchema(Schema):

View File

@ -17,9 +17,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman import __version__
from ahriman.web.apispec import Schema, fields
from ahriman.web.schemas.repository_id_schema import RepositoryIdSchema

View File

@ -17,9 +17,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import fields
from ahriman import __version__
from ahriman.web.apispec import fields
from ahriman.web.schemas.counters_schema import CountersSchema
from ahriman.web.schemas.repository_id_schema import RepositoryIdSchema
from ahriman.web.schemas.status_schema import StatusSchema

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman.web.apispec import Schema, fields
class LogSchema(Schema):

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman.web.apispec import Schema, fields
class LoginSchema(Schema):

View File

@ -17,8 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman.web.apispec import Schema, fields
from ahriman.web.schemas.status_schema import StatusSchema

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman.web.apispec import Schema, fields
class OAuth2Schema(Schema):

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman.web.apispec import Schema, fields
class PackageNameSchema(Schema):

View File

@ -17,8 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import fields
from ahriman.web.apispec import fields
from ahriman.web.schemas.build_options_schema import BuildOptionsSchema

View File

@ -17,8 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import fields
from ahriman.web.apispec import fields
from ahriman.web.schemas.package_names_schema import PackageNamesSchema
from ahriman.web.schemas.patch_schema import PatchSchema

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman.web.apispec import Schema, fields
class PackagePropertiesSchema(Schema):

View File

@ -17,9 +17,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman import __version__
from ahriman.web.apispec import Schema, fields
from ahriman.web.schemas.package_properties_schema import PackagePropertiesSchema
from ahriman.web.schemas.remote_schema import RemoteSchema

View File

@ -17,9 +17,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman.models.build_status import BuildStatusEnum
from ahriman.web.apispec import Schema, fields
from ahriman.web.schemas.package_schema import PackageSchema
from ahriman.web.schemas.repository_id_schema import RepositoryIdSchema
from ahriman.web.schemas.status_schema import StatusSchema

View File

@ -17,9 +17,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import fields
from ahriman import __version__
from ahriman.web.apispec import fields
from ahriman.web.schemas.repository_id_schema import RepositoryIdSchema

View File

@ -17,8 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import fields
from ahriman.web.apispec import fields
from ahriman.web.schemas.repository_id_schema import RepositoryIdSchema

View File

@ -17,8 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import fields
from ahriman.web.apispec import fields
from ahriman.web.schemas.package_name_schema import PackageNameSchema

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman.web.apispec import Schema, fields
class PatchSchema(Schema):

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman.web.apispec import Schema, fields
class PGPKeyIdSchema(Schema):

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman.web.apispec import Schema, fields
class PGPKeySchema(Schema):

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman.web.apispec import Schema, fields
class ProcessIdSchema(Schema):

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman.web.apispec import Schema, fields
class ProcessSchema(Schema):

View File

@ -17,9 +17,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman.models.package_source import PackageSource
from ahriman.web.apispec import Schema, fields
class RemoteSchema(Schema):

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman.web.apispec import Schema, fields
class RepositoryIdSchema(Schema):

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman.web.apispec import Schema, fields
class SearchSchema(Schema):

View File

@ -17,9 +17,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman.models.build_status import BuildStatusEnum
from ahriman.web.apispec import Schema, fields
class StatusSchema(Schema):

View File

@ -17,8 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import fields
from ahriman.web.apispec import fields
from ahriman.web.schemas.build_options_schema import BuildOptionsSchema

View File

@ -17,9 +17,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import fields
from ahriman import __version__
from ahriman.web.apispec import fields
from ahriman.web.schemas.log_schema import LogSchema
from ahriman.web.schemas.repository_id_schema import RepositoryIdSchema

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from marshmallow import Schema, fields
from ahriman.web.apispec import Schema, fields
class WorkerSchema(Schema):

View File

@ -17,13 +17,12 @@
# 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.event import Event
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, EventSchema, EventSearchSchema
from ahriman.web.apispec.decorators import apidocs
from ahriman.web.schemas import EventSchema, EventSearchSchema
from ahriman.web.views.base import BaseView
@ -39,21 +38,15 @@ class EventsView(BaseView):
GET_PERMISSION = POST_PERMISSION = UserAccess.Full
ROUTES = ["/api/v1/events"]
@aiohttp_apispec.docs(
@apidocs(
tags=["Audit log"],
summary="Get events",
description="Retrieve events from audit log",
responses={
200: {"description": "Success response", "schema": EventSchema(many=True)},
400: {"description": "Bad data is supplied", "schema": ErrorSchema},
401: {"description": "Authorization required", "schema": ErrorSchema},
403: {"description": "Access is forbidden", "schema": ErrorSchema},
500: {"description": "Internal server error", "schema": ErrorSchema},
},
security=[{"token": [GET_PERMISSION]}],
permission=GET_PERMISSION,
error_400_enabled=True,
schema=EventSchema(many=True),
query_schema=EventSearchSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.querystring_schema(EventSearchSchema)
async def get(self) -> Response:
"""
get events list
@ -78,21 +71,14 @@ class EventsView(BaseView):
return json_response(response)
@aiohttp_apispec.docs(
@apidocs(
tags=["Audit log"],
summary="Create event",
description="Add new event to the audit log",
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},
500: {"description": "Internal server error", "schema": ErrorSchema},
},
security=[{"token": [POST_PERMISSION]}],
permission=POST_PERMISSION,
error_400_enabled=True,
body_schema=EventSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.json_schema(EventSchema)
async def post(self) -> None:
"""
add new audit log event

View File

@ -17,14 +17,13 @@
# 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 collections.abc import Callable
from ahriman.models.user_access import UserAccess
from ahriman.models.worker import Worker
from ahriman.web.schemas import AuthSchema, ErrorSchema, WorkerSchema
from ahriman.web.apispec.decorators import apidocs
from ahriman.web.schemas import WorkerSchema
from ahriman.web.views.base import BaseView
@ -41,19 +40,12 @@ class WorkersView(BaseView):
DELETE_PERMISSION = GET_PERMISSION = POST_PERMISSION = UserAccess.Full
ROUTES = ["/api/v1/distributed"]
@aiohttp_apispec.docs(
@apidocs(
tags=["Distributed"],
summary="Unregister all workers",
description="Unregister and remove all known workers from the service",
responses={
204: {"description": "Success response"},
401: {"description": "Authorization required", "schema": ErrorSchema},
403: {"description": "Access is forbidden", "schema": ErrorSchema},
500: {"description": "Internal server error", "schema": ErrorSchema},
},
security=[{"token": [DELETE_PERMISSION]}],
permission=DELETE_PERMISSION,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
async def delete(self) -> None:
"""
unregister worker
@ -65,19 +57,13 @@ class WorkersView(BaseView):
raise HTTPNoContent
@aiohttp_apispec.docs(
@apidocs(
tags=["Distributed"],
summary="Get workers",
description="Retrieve registered workers",
responses={
200: {"description": "Success response", "schema": WorkerSchema(many=True)},
401: {"description": "Authorization required", "schema": ErrorSchema},
403: {"description": "Access is forbidden", "schema": ErrorSchema},
500: {"description": "Internal server error", "schema": ErrorSchema},
},
security=[{"token": [GET_PERMISSION]}],
permission=GET_PERMISSION,
schema=WorkerSchema(many=True),
)
@aiohttp_apispec.cookies_schema(AuthSchema)
async def get(self) -> Response:
"""
get workers list
@ -92,21 +78,14 @@ class WorkersView(BaseView):
return json_response(response)
@aiohttp_apispec.docs(
@apidocs(
tags=["Distributed"],
summary="Register worker",
description="Register or update remote worker",
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},
500: {"description": "Internal server error", "schema": ErrorSchema},
},
security=[{"token": [POST_PERMISSION]}],
permission=POST_PERMISSION,
error_400_enabled=True,
body_schema=WorkerSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.json_schema(WorkerSchema)
async def post(self) -> None:
"""
register remote worker

View File

@ -17,13 +17,12 @@
# 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.changes import Changes
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ChangesSchema, ErrorSchema, PackageNameSchema, RepositoryIdSchema
from ahriman.web.apispec.decorators import apidocs
from ahriman.web.schemas import ChangesSchema, PackageNameSchema, RepositoryIdSchema
from ahriman.web.views.base import BaseView
from ahriman.web.views.status_view_guard import StatusViewGuard
@ -41,22 +40,16 @@ class ChangesView(StatusViewGuard, BaseView):
POST_PERMISSION = UserAccess.Full
ROUTES = ["/api/v1/packages/{package}/changes"]
@aiohttp_apispec.docs(
@apidocs(
tags=["Packages"],
summary="Get package changes",
description="Retrieve package changes since the last build",
responses={
200: {"description": "Success response", "schema": ChangesSchema},
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]}],
permission=GET_PERMISSION,
error_404_description="Package base and/or repository are unknown",
schema=ChangesSchema,
match_schema=PackageNameSchema,
query_schema=RepositoryIdSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.match_info_schema(PackageNameSchema)
@aiohttp_apispec.querystring_schema(RepositoryIdSchema)
async def get(self) -> Response:
"""
get package changes
@ -73,24 +66,17 @@ class ChangesView(StatusViewGuard, BaseView):
return json_response(changes.view())
@aiohttp_apispec.docs(
@apidocs(
tags=["Packages"],
summary="Update package changes",
description="Update package changes to the new ones",
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]}],
permission=POST_PERMISSION,
error_400_enabled=True,
error_404_description="Repository is unknown",
match_schema=PackageNameSchema,
query_schema=RepositoryIdSchema,
body_schema=ChangesSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.match_info_schema(PackageNameSchema)
@aiohttp_apispec.querystring_schema(RepositoryIdSchema)
@aiohttp_apispec.json_schema(ChangesSchema)
async def post(self) -> None:
"""
insert new package changes

View File

@ -17,13 +17,12 @@
# 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.apispec.decorators import apidocs
from ahriman.web.schemas import DependenciesSchema, PackageNameSchema, RepositoryIdSchema
from ahriman.web.views.base import BaseView
from ahriman.web.views.status_view_guard import StatusViewGuard
@ -41,22 +40,16 @@ class DependenciesView(StatusViewGuard, BaseView):
POST_PERMISSION = UserAccess.Full
ROUTES = ["/api/v1/packages/{package}/dependencies"]
@aiohttp_apispec.docs(
@apidocs(
tags=["Packages"],
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]}],
permission=GET_PERMISSION,
error_404_description="Package base and/or repository are unknown",
schema=DependenciesSchema,
match_schema=PackageNameSchema,
query_schema=RepositoryIdSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.match_info_schema(PackageNameSchema)
@aiohttp_apispec.querystring_schema(RepositoryIdSchema)
async def get(self) -> Response:
"""
get package dependencies
@ -73,24 +66,17 @@ class DependenciesView(StatusViewGuard, BaseView):
return json_response(dependencies.view())
@aiohttp_apispec.docs(
@apidocs(
tags=["Packages"],
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]}],
permission=POST_PERMISSION,
error_400_enabled=True,
error_404_description="Repository is unknown",
match_schema=PackageNameSchema,
query_schema=RepositoryIdSchema,
body_schema=DependenciesSchema,
)
@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

View File

@ -17,16 +17,15 @@
# 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, HTTPNotFound, Response, json_response
from ahriman.core.exceptions import UnknownPackageError
from ahriman.core.utils 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, PackageVersionSchema, \
RepositoryIdSchema, VersionedLogSchema
from ahriman.web.apispec.decorators import apidocs
from ahriman.web.schemas import LogsSchema, PackageNameSchema, PackageVersionSchema, RepositoryIdSchema, \
VersionedLogSchema
from ahriman.web.views.base import BaseView
from ahriman.web.views.status_view_guard import StatusViewGuard
@ -45,22 +44,15 @@ class LogsView(StatusViewGuard, BaseView):
GET_PERMISSION = UserAccess.Reporter
ROUTES = ["/api/v1/packages/{package}/logs"]
@aiohttp_apispec.docs(
@apidocs(
tags=["Packages"],
summary="Delete package logs",
description="Delete all logs which belong to the specified package",
responses={
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]}],
permission=DELETE_PERMISSION,
error_404_description="Repository is unknown",
match_schema=PackageNameSchema,
query_schema=PackageVersionSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.match_info_schema(PackageNameSchema)
@aiohttp_apispec.querystring_schema(PackageVersionSchema)
async def delete(self) -> None:
"""
delete package logs
@ -74,22 +66,16 @@ class LogsView(StatusViewGuard, BaseView):
raise HTTPNoContent
@aiohttp_apispec.docs(
@apidocs(
tags=["Packages"],
summary="Get package logs",
description="Retrieve all package logs and the last package status",
responses={
200: {"description": "Success response", "schema": LogsSchema},
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]}],
permission=GET_PERMISSION,
error_404_description="Package base and/or repository are unknown",
schema=LogsSchema,
match_schema=PackageNameSchema,
query_schema=RepositoryIdSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.match_info_schema(PackageNameSchema)
@aiohttp_apispec.querystring_schema(RepositoryIdSchema)
async def get(self) -> Response:
"""
get last package logs
@ -115,23 +101,16 @@ class LogsView(StatusViewGuard, BaseView):
}
return json_response(response)
@aiohttp_apispec.docs(
@apidocs(
tags=["Packages"],
summary="Add package logs",
description="Insert new package log record",
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]}],
permission=POST_PERMISSION,
error_400_enabled=True,
error_404_description="Repository is unknown",
match_schema=PackageNameSchema,
body_schema=VersionedLogSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.match_info_schema(PackageNameSchema)
@aiohttp_apispec.json_schema(VersionedLogSchema)
async def post(self) -> None:
"""
create new package log record

View File

@ -17,16 +17,15 @@
# 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, HTTPNotFound, Response, json_response
from ahriman.core.exceptions import UnknownPackageError
from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.package import Package
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, PackageNameSchema, PackageStatusSchema, \
PackageStatusSimplifiedSchema, RepositoryIdSchema
from ahriman.web.apispec.decorators import apidocs
from ahriman.web.schemas import PackageNameSchema, PackageStatusSchema, PackageStatusSimplifiedSchema, \
RepositoryIdSchema
from ahriman.web.views.base import BaseView
from ahriman.web.views.status_view_guard import StatusViewGuard
@ -45,22 +44,15 @@ class PackageView(StatusViewGuard, BaseView):
GET_PERMISSION = UserAccess.Read
ROUTES = ["/api/v1/packages/{package}"]
@aiohttp_apispec.docs(
@apidocs(
tags=["Packages"],
summary="Delete package",
description="Delete package and its status from service",
responses={
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]}],
permission=DELETE_PERMISSION,
error_404_description="Repository is unknown",
match_schema=PackageNameSchema,
query_schema=RepositoryIdSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.match_info_schema(PackageNameSchema)
@aiohttp_apispec.querystring_schema(RepositoryIdSchema)
async def delete(self) -> None:
"""
delete package base from status page
@ -73,22 +65,16 @@ class PackageView(StatusViewGuard, BaseView):
raise HTTPNoContent
@aiohttp_apispec.docs(
@apidocs(
tags=["Packages"],
summary="Get package",
description="Retrieve packages and its descriptor",
responses={
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 and/or repository are unknown", "schema": ErrorSchema},
500: {"description": "Internal server error", "schema": ErrorSchema},
},
security=[{"token": [GET_PERMISSION]}],
permission=GET_PERMISSION,
error_404_description="Package base and/or repository are unknown",
schema=PackageStatusSchema(many=True),
match_schema=PackageNameSchema,
query_schema=RepositoryIdSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.match_info_schema(PackageNameSchema)
@aiohttp_apispec.querystring_schema(RepositoryIdSchema)
async def get(self) -> Response:
"""
get current package base status
@ -116,24 +102,17 @@ class PackageView(StatusViewGuard, BaseView):
]
return json_response(response)
@aiohttp_apispec.docs(
@apidocs(
tags=["Packages"],
summary="Update package",
description="Update package status and set its descriptior optionally",
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]}],
permission=POST_PERMISSION,
error_400_enabled=True,
error_404_description="Repository is unknown",
match_schema=PackageNameSchema,
query_schema=RepositoryIdSchema,
body_schema=PackageStatusSimplifiedSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.match_info_schema(PackageNameSchema)
@aiohttp_apispec.querystring_schema(RepositoryIdSchema)
@aiohttp_apispec.json_schema(PackageStatusSimplifiedSchema)
async def post(self) -> None:
"""
update package build status

View File

@ -17,7 +17,6 @@
# 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]
import itertools
from aiohttp.web import HTTPNoContent, Response, json_response
@ -26,7 +25,8 @@ from collections.abc import Callable
from ahriman.models.build_status import BuildStatus
from ahriman.models.package import Package
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, PackageStatusSchema, PaginationSchema, RepositoryIdSchema
from ahriman.web.apispec.decorators import apidocs
from ahriman.web.schemas import PackageStatusSchema, PaginationSchema, RepositoryIdSchema
from ahriman.web.views.base import BaseView
from ahriman.web.views.status_view_guard import StatusViewGuard
@ -44,22 +44,16 @@ class PackagesView(StatusViewGuard, BaseView):
POST_PERMISSION = UserAccess.Full
ROUTES = ["/api/v1/packages"]
@aiohttp_apispec.docs(
tags=["Packages"],
@apidocs(
tags=["packages"],
summary="Get packages list",
description="Retrieve packages and their descriptors",
responses={
200: {"description": "Success response", "schema": PackageStatusSchema(many=True)},
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]}],
permission=GET_PERMISSION,
error_400_enabled=True,
error_404_description="Repository is unknown",
schema=PackageStatusSchema(many=True),
query_schema=PaginationSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.querystring_schema(PaginationSchema)
async def get(self) -> Response:
"""
get current packages status
@ -84,21 +78,14 @@ class PackagesView(StatusViewGuard, BaseView):
return json_response(response)
@aiohttp_apispec.docs(
@apidocs(
tags=["Packages"],
summary="Load packages",
description="Load packages from cache",
responses={
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]}],
permission=POST_PERMISSION,
error_404_description="Repository is unknown",
query_schema=RepositoryIdSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.querystring_schema(RepositoryIdSchema)
async def post(self) -> None:
"""
reload all packages from repository

View File

@ -17,12 +17,11 @@
# 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 HTTPNoContent, HTTPNotFound, Response, json_response
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, PatchNameSchema, PatchSchema
from ahriman.web.apispec.decorators import apidocs
from ahriman.web.schemas import PatchNameSchema, PatchSchema
from ahriman.web.views.base import BaseView
from ahriman.web.views.status_view_guard import StatusViewGuard
@ -40,20 +39,13 @@ class PatchView(StatusViewGuard, BaseView):
GET_PERMISSION = UserAccess.Reporter
ROUTES = ["/api/v1/packages/{package}/patches/{patch}"]
@aiohttp_apispec.docs(
@apidocs(
tags=["Packages"],
summary="Delete package patch",
description="Delete package patch by variable",
responses={
204: {"description": "Success response"},
401: {"description": "Authorization required", "schema": ErrorSchema},
403: {"description": "Access is forbidden", "schema": ErrorSchema},
500: {"description": "Internal server error", "schema": ErrorSchema},
},
security=[{"token": [DELETE_PERMISSION]}],
permission=DELETE_PERMISSION,
match_schema=PatchNameSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.match_info_schema(PatchNameSchema)
async def delete(self) -> None:
"""
delete package patch
@ -68,21 +60,15 @@ class PatchView(StatusViewGuard, BaseView):
raise HTTPNoContent
@aiohttp_apispec.docs(
@apidocs(
tags=["Packages"],
summary="Get package patch",
description="Retrieve package patch by variable",
responses={
200: {"description": "Success response", "schema": PatchSchema},
401: {"description": "Authorization required", "schema": ErrorSchema},
403: {"description": "Access is forbidden", "schema": ErrorSchema},
404: {"description": "Patch name is unknown", "schema": ErrorSchema},
500: {"description": "Internal server error", "schema": ErrorSchema},
},
security=[{"token": [GET_PERMISSION]}],
permission=GET_PERMISSION,
error_404_description="Patch name is unknown",
schema=PatchSchema,
match_schema=PatchNameSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.match_info_schema(PatchNameSchema)
async def get(self) -> Response:
"""
get package patch

View File

@ -17,13 +17,12 @@
# 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.pkgbuild_patch import PkgbuildPatch
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, PackageNameSchema, PatchSchema
from ahriman.web.apispec.decorators import apidocs
from ahriman.web.schemas import PackageNameSchema, PatchSchema
from ahriman.web.views.base import BaseView
from ahriman.web.views.status_view_guard import StatusViewGuard
@ -41,20 +40,14 @@ class PatchesView(StatusViewGuard, BaseView):
POST_PERMISSION = UserAccess.Full
ROUTES = ["/api/v1/packages/{package}/patches"]
@aiohttp_apispec.docs(
@apidocs(
tags=["Packages"],
summary="Get package patches",
description="Retrieve all package patches",
responses={
200: {"description": "Success response", "schema": PatchSchema(many=True)},
401: {"description": "Authorization required", "schema": ErrorSchema},
403: {"description": "Access is forbidden", "schema": ErrorSchema},
500: {"description": "Internal server error", "schema": ErrorSchema},
},
security=[{"token": [GET_PERMISSION]}],
permission=GET_PERMISSION,
schema=PatchSchema(many=True),
match_schema=PackageNameSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.match_info_schema(PackageNameSchema)
async def get(self) -> Response:
"""
get package patches
@ -68,22 +61,15 @@ class PatchesView(StatusViewGuard, BaseView):
response = [patch.view() for patch in patches]
return json_response(response)
@aiohttp_apispec.docs(
@apidocs(
tags=["Packages"],
summary="Update package patch",
description="Update or create package patch",
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},
500: {"description": "Internal server error", "schema": ErrorSchema},
},
security=[{"token": [GET_PERMISSION]}],
permission=POST_PERMISSION,
error_400_enabled=True,
match_schema=PackageNameSchema,
body_schema=PatchSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.match_info_schema(PackageNameSchema)
@aiohttp_apispec.json_schema(PatchSchema)
async def post(self) -> None:
"""
update or create package patch

View File

@ -17,13 +17,12 @@
# 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, Response, json_response
from ahriman.models.pkgbuild_patch import PkgbuildPatch
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, PackagePatchSchema, ProcessIdSchema, RepositoryIdSchema
from ahriman.web.apispec.decorators import apidocs
from ahriman.web.schemas import PackagePatchSchema, ProcessIdSchema, RepositoryIdSchema
from ahriman.web.views.base import BaseView
@ -38,23 +37,17 @@ class AddView(BaseView):
POST_PERMISSION = UserAccess.Full
ROUTES = ["/api/v1/service/add"]
@aiohttp_apispec.docs(
@apidocs(
tags=["Actions"],
summary="Add new package",
description="Add new package(s) from AUR",
responses={
200: {"description": "Success response", "schema": ProcessIdSchema},
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]}],
permission=POST_PERMISSION,
error_400_enabled=True,
error_404_description="Repository is unknown",
schema=ProcessIdSchema,
query_schema=RepositoryIdSchema,
body_schema=PackagePatchSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.querystring_schema(RepositoryIdSchema)
@aiohttp_apispec.json_schema(PackagePatchSchema)
async def post(self) -> Response:
"""
add new package

View File

@ -17,12 +17,11 @@
# 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, HTTPNotFound, Response, json_response
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, PGPKeyIdSchema, PGPKeySchema, ProcessIdSchema
from ahriman.web.apispec.decorators import apidocs
from ahriman.web.schemas import PGPKeyIdSchema, PGPKeySchema, ProcessIdSchema
from ahriman.web.views.base import BaseView
@ -39,22 +38,16 @@ class PGPView(BaseView):
POST_PERMISSION = UserAccess.Full
ROUTES = ["/api/v1/service/pgp"]
@aiohttp_apispec.docs(
@apidocs(
tags=["Actions"],
summary="Search for PGP key",
description="Search for PGP key and retrieve its body",
responses={
200: {"description": "Success response", "schema": PGPKeySchema},
400: {"description": "Bad data is supplied", "schema": ErrorSchema},
401: {"description": "Authorization required", "schema": ErrorSchema},
403: {"description": "Access is forbidden", "schema": ErrorSchema},
404: {"description": "PGP key is unknown", "schema": ErrorSchema},
500: {"description": "Internal server error", "schema": ErrorSchema},
},
security=[{"token": [GET_PERMISSION]}],
permission=GET_PERMISSION,
error_400_enabled=True,
error_404_description="PGP key is unknown",
schema=PGPKeySchema,
query_schema=PGPKeyIdSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.querystring_schema(PGPKeyIdSchema)
async def get(self) -> Response:
"""
retrieve key from the key server
@ -79,21 +72,15 @@ class PGPView(BaseView):
return json_response({"key": key})
@aiohttp_apispec.docs(
@apidocs(
tags=["Actions"],
summary="Fetch PGP key",
description="Fetch PGP key from the key server",
responses={
200: {"description": "Success response", "schema": ProcessIdSchema},
400: {"description": "Bad data is supplied", "schema": ErrorSchema},
401: {"description": "Authorization required", "schema": ErrorSchema},
403: {"description": "Access is forbidden", "schema": ErrorSchema},
500: {"description": "Internal server error", "schema": ErrorSchema},
},
security=[{"token": [POST_PERMISSION]}],
permission=POST_PERMISSION,
error_400_enabled=True,
schema=ProcessIdSchema,
body_schema=PGPKeyIdSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.json_schema(PGPKeyIdSchema)
async def post(self) -> Response:
"""
store key to the local service environment

View File

@ -17,12 +17,11 @@
# 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 HTTPNotFound, Response, json_response
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, ProcessIdSchema, ProcessSchema
from ahriman.web.apispec.decorators import apidocs
from ahriman.web.schemas import ProcessIdSchema, ProcessSchema
from ahriman.web.views.base import BaseView
@ -37,21 +36,15 @@ class ProcessView(BaseView):
GET_PERMISSION = UserAccess.Reporter
ROUTES = ["/api/v1/service/process/{process_id}"]
@aiohttp_apispec.docs(
@apidocs(
tags=["Actions"],
summary="Get process",
description="Get process information",
responses={
200: {"description": "Success response", "schema": ProcessSchema},
401: {"description": "Authorization required", "schema": ErrorSchema},
403: {"description": "Access is forbidden", "schema": ErrorSchema},
404: {"description": "Process is unknown", "schema": ErrorSchema},
500: {"description": "Internal server error", "schema": ErrorSchema},
},
security=[{"token": [GET_PERMISSION]}],
permission=GET_PERMISSION,
error_404_description="Process is unknown",
schema=ProcessSchema,
match_schema=ProcessIdSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.match_info_schema(ProcessIdSchema)
async def get(self) -> Response:
"""
get spawned process status

View File

@ -17,12 +17,11 @@
# 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, Response, json_response
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, PackageNamesSchema, ProcessIdSchema, RepositoryIdSchema
from ahriman.web.apispec.decorators import apidocs
from ahriman.web.schemas import PackageNamesSchema, ProcessIdSchema, RepositoryIdSchema
from ahriman.web.views.base import BaseView
@ -37,23 +36,17 @@ class RebuildView(BaseView):
POST_PERMISSION = UserAccess.Full
ROUTES = ["/api/v1/service/rebuild"]
@aiohttp_apispec.docs(
@apidocs(
tags=["Actions"],
summary="Rebuild packages",
description="Rebuild packages which depend on specified one",
responses={
200: {"description": "Success response", "schema": ProcessIdSchema},
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]}],
permission=POST_PERMISSION,
error_400_enabled=True,
error_404_description="Repository is unknown",
schema=ProcessIdSchema,
query_schema=RepositoryIdSchema,
body_schema=PackageNamesSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.querystring_schema(RepositoryIdSchema)
@aiohttp_apispec.json_schema(PackageNamesSchema)
async def post(self) -> Response:
"""
rebuild packages based on their dependency

View File

@ -17,12 +17,11 @@
# 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, Response, json_response
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, PackageNamesSchema, ProcessIdSchema, RepositoryIdSchema
from ahriman.web.apispec.decorators import apidocs
from ahriman.web.schemas import PackageNamesSchema, ProcessIdSchema, RepositoryIdSchema
from ahriman.web.views.base import BaseView
@ -37,23 +36,17 @@ class RemoveView(BaseView):
POST_PERMISSION = UserAccess.Full
ROUTES = ["/api/v1/service/remove"]
@aiohttp_apispec.docs(
@apidocs(
tags=["Actions"],
summary="Remove packages",
description="Remove specified packages from the repository",
responses={
200: {"description": "Success response", "schema": ProcessIdSchema},
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]}],
permission=POST_PERMISSION,
error_400_enabled=True,
error_404_description="Repository is unknown",
schema=ProcessIdSchema,
query_schema=RepositoryIdSchema,
body_schema=PackageNamesSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.querystring_schema(RepositoryIdSchema)
@aiohttp_apispec.json_schema(PackageNamesSchema)
async def post(self) -> Response:
"""
remove existing packages

View File

@ -17,13 +17,12 @@
# 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, Response, json_response
from ahriman.models.pkgbuild_patch import PkgbuildPatch
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, PackagePatchSchema, ProcessIdSchema, RepositoryIdSchema
from ahriman.web.apispec.decorators import apidocs
from ahriman.web.schemas import PackagePatchSchema, ProcessIdSchema, RepositoryIdSchema
from ahriman.web.views.base import BaseView
@ -38,23 +37,17 @@ class RequestView(BaseView):
POST_PERMISSION = UserAccess.Reporter
ROUTES = ["/api/v1/service/request"]
@aiohttp_apispec.docs(
@apidocs(
tags=["Actions"],
summary="Request new package",
description="Request new package(s) to be added from AUR",
responses={
200: {"description": "Success response", "schema": ProcessIdSchema},
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]}],
permission=POST_PERMISSION,
error_400_enabled=True,
error_404_description="Repository is unknown",
schema=ProcessIdSchema,
query_schema=RepositoryIdSchema,
body_schema=PackagePatchSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.querystring_schema(RepositoryIdSchema)
@aiohttp_apispec.json_schema(PackagePatchSchema)
async def post(self) -> Response:
"""
request to add new package

View File

@ -17,15 +17,14 @@
# 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, HTTPNotFound, Response, json_response
from collections.abc import Callable
from ahriman.core.alpm.remote import AUR
from ahriman.models.aur_package import AURPackage
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AURPackageSchema, AuthSchema, ErrorSchema, SearchSchema
from ahriman.web.apispec.decorators import apidocs
from ahriman.web.schemas import AURPackageSchema, SearchSchema
from ahriman.web.views.base import BaseView
@ -40,22 +39,16 @@ class SearchView(BaseView):
GET_PERMISSION = UserAccess.Reporter
ROUTES = ["/api/v1/service/search"]
@aiohttp_apispec.docs(
@apidocs(
tags=["Actions"],
summary="Search for package",
description="Search for package in AUR",
responses={
200: {"description": "Success response", "schema": AURPackageSchema(many=True)},
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},
500: {"description": "Internal server error", "schema": ErrorSchema},
},
security=[{"token": [GET_PERMISSION]}],
permission=GET_PERMISSION,
error_400_enabled=True,
error_404_description="Package base is unknown",
schema=AURPackageSchema(many=True),
query_schema=SearchSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.querystring_schema(SearchSchema)
async def get(self) -> Response:
"""
search packages in AUR

View File

@ -17,12 +17,11 @@
# 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, Response, json_response
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, ProcessIdSchema, RepositoryIdSchema, UpdateFlagsSchema
from ahriman.web.apispec.decorators import apidocs
from ahriman.web.schemas import ProcessIdSchema, RepositoryIdSchema, UpdateFlagsSchema
from ahriman.web.views.base import BaseView
@ -37,23 +36,17 @@ class UpdateView(BaseView):
POST_PERMISSION = UserAccess.Full
ROUTES = ["/api/v1/service/update"]
@aiohttp_apispec.docs(
@apidocs(
tags=["Actions"],
summary="Update packages",
description="Run repository update process",
responses={
200: {"description": "Success response", "schema": ProcessIdSchema},
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]}],
permission=POST_PERMISSION,
error_400_enabled=True,
error_404_description="Repository is unknown",
schema=ProcessIdSchema,
query_schema=RepositoryIdSchema,
body_schema=UpdateFlagsSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.querystring_schema(RepositoryIdSchema)
@aiohttp_apispec.json_schema(UpdateFlagsSchema)
async def post(self) -> Response:
"""
run repository update. No parameters supported here

View File

@ -17,7 +17,6 @@
# 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]
import shutil
from aiohttp import BodyPartReader
@ -27,7 +26,8 @@ from tempfile import NamedTemporaryFile
from ahriman.models.repository_paths import RepositoryPaths
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, FileSchema, RepositoryIdSchema
from ahriman.web.apispec.decorators import apidocs
from ahriman.web.schemas import FileSchema, RepositoryIdSchema
from ahriman.web.views.base import BaseView
@ -92,23 +92,17 @@ class UploadView(BaseView):
return archive_name, temporary_output
@aiohttp_apispec.docs(
@apidocs(
tags=["Actions"],
summary="Upload package",
description="Upload package to local filesystem",
responses={
201: {"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 or endpoint is disabled", "schema": ErrorSchema},
500: {"description": "Internal server error", "schema": ErrorSchema},
},
security=[{"token": [POST_PERMISSION]}],
permission=POST_PERMISSION,
error_400_enabled=True,
error_404_description="Repository is unknown or endpoint is disabled",
query_schema=RepositoryIdSchema,
body_schema=FileSchema,
body_location="form",
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.querystring_schema(RepositoryIdSchema)
@aiohttp_apispec.form_schema(FileSchema)
async def post(self) -> None:
"""
upload file from another instance to the server

View File

@ -17,13 +17,12 @@
# 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 Response, json_response
from ahriman import __version__
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, InfoSchema
from ahriman.web.apispec.decorators import apidocs
from ahriman.web.schemas import InfoSchema
from ahriman.web.views.base import BaseView
@ -38,19 +37,13 @@ class InfoView(BaseView):
GET_PERMISSION = UserAccess.Unauthorized
ROUTES = ["/api/v1/info"]
@aiohttp_apispec.docs(
@apidocs(
tags=["Status"],
summary="Service information",
description="Perform basic service health check and returns its information",
responses={
200: {"description": "Success response", "schema": InfoSchema},
401: {"description": "Authorization required", "schema": ErrorSchema},
403: {"description": "Access is forbidden", "schema": ErrorSchema},
500: {"description": "Internal server error", "schema": ErrorSchema},
},
security=[{"token": [GET_PERMISSION]}],
permission=GET_PERMISSION,
schema=InfoSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
async def get(self) -> Response:
"""
get service information

View File

@ -17,12 +17,11 @@
# 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 Response, json_response
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, RepositoryIdSchema
from ahriman.web.apispec.decorators import apidocs
from ahriman.web.schemas import RepositoryIdSchema
from ahriman.web.views.base import BaseView
@ -37,19 +36,13 @@ class RepositoriesView(BaseView):
GET_PERMISSION = UserAccess.Read
ROUTES = ["/api/v1/repositories"]
@aiohttp_apispec.docs(
@apidocs(
tags=["Status"],
summary="Available repositories",
description="List available repositories",
responses={
200: {"description": "Success response", "schema": RepositoryIdSchema(many=True)},
401: {"description": "Authorization required", "schema": ErrorSchema},
403: {"description": "Access is forbidden", "schema": ErrorSchema},
500: {"description": "Internal server error", "schema": ErrorSchema},
},
security=[{"token": [GET_PERMISSION]}],
permission=GET_PERMISSION,
schema=RepositoryIdSchema(many=True),
)
@aiohttp_apispec.cookies_schema(AuthSchema)
async def get(self) -> Response:
"""
get list of available repositories

View File

@ -17,8 +17,6 @@
# 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 import __version__
@ -26,7 +24,8 @@ from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.counters import Counters
from ahriman.models.internal_status import InternalStatus
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, InternalStatusSchema, RepositoryIdSchema, StatusSchema
from ahriman.web.apispec.decorators import apidocs
from ahriman.web.schemas import InternalStatusSchema, RepositoryIdSchema, StatusSchema
from ahriman.web.views.base import BaseView
from ahriman.web.views.status_view_guard import StatusViewGuard
@ -44,20 +43,15 @@ class StatusView(StatusViewGuard, BaseView):
POST_PERMISSION = UserAccess.Full
ROUTES = ["/api/v1/status"]
@aiohttp_apispec.docs(
@apidocs(
tags=["Status"],
summary="Web service status",
description="Get web service status counters",
responses={
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]}],
permission=GET_PERMISSION,
error_404_description="Repository is unknown",
schema=InternalStatusSchema,
query_schema=RepositoryIdSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
async def get(self) -> Response:
"""
get current service status
@ -77,23 +71,16 @@ class StatusView(StatusViewGuard, BaseView):
return json_response(status.view())
@aiohttp_apispec.docs(
@apidocs(
tags=["Status"],
summary="Set web service status",
description="Update web service status. Counters will remain unchanged",
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]}],
permission=POST_PERMISSION,
error_400_enabled=True,
error_404_description="Repository is unknown",
query_schema=RepositoryIdSchema,
body_schema=StatusSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.querystring_schema(RepositoryIdSchema)
@aiohttp_apispec.json_schema(StatusSchema)
async def post(self) -> None:
"""
update service status

View File

@ -17,13 +17,12 @@
# 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, HTTPFound, HTTPMethodNotAllowed, HTTPUnauthorized
from ahriman.core.auth.helpers import remember
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import ErrorSchema, LoginSchema, OAuth2Schema
from ahriman.web.apispec.decorators import apidocs
from ahriman.web.schemas import LoginSchema, OAuth2Schema
from ahriman.web.views.base import BaseView
@ -39,18 +38,14 @@ class LoginView(BaseView):
GET_PERMISSION = POST_PERMISSION = UserAccess.Unauthorized
ROUTES = ["/api/v1/login"]
@aiohttp_apispec.docs(
@apidocs(
tags=["Login"],
summary="Login via OAuth2",
description="Login by using OAuth2 authorization code. Only available if OAuth2 is enabled",
responses={
302: {"description": "Success response"},
401: {"description": "Authorization required", "schema": ErrorSchema},
500: {"description": "Internal server error", "schema": ErrorSchema},
},
security=[{"token": [GET_PERMISSION]}],
permission=GET_PERMISSION,
code=302,
query_schema=OAuth2Schema,
)
@aiohttp_apispec.querystring_schema(OAuth2Schema)
async def get(self) -> None:
"""
OAuth2 response handler
@ -87,19 +82,15 @@ class LoginView(BaseView):
raise HTTPUnauthorized
@aiohttp_apispec.docs(
@apidocs(
tags=["Login"],
summary="Login via basic authorization",
description="Login by using username and password",
responses={
302: {"description": "Success response"},
400: {"description": "Bad data is supplied", "schema": ErrorSchema},
401: {"description": "Authorization required", "schema": ErrorSchema},
500: {"description": "Internal server error", "schema": ErrorSchema},
},
security=[{"token": [POST_PERMISSION]}],
permission=POST_PERMISSION,
code=302,
error_400_enabled=True,
body_schema=LoginSchema,
)
@aiohttp_apispec.json_schema(LoginSchema)
async def post(self) -> None:
"""
login user to service. The authentication session will be passed in ``Set-Cookie`` header.

View File

@ -17,13 +17,11 @@
# 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 HTTPFound, HTTPUnauthorized
from ahriman.core.auth.helpers import check_authorized, forget
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema
from ahriman.web.apispec.decorators import apidocs
from ahriman.web.views.base import BaseView
@ -38,18 +36,13 @@ class LogoutView(BaseView):
POST_PERMISSION = UserAccess.Unauthorized
ROUTES = ["/api/v1/logout"]
@aiohttp_apispec.docs(
@apidocs(
tags=["Login"],
summary="Logout",
description="Logout user and remove authorization cookies",
responses={
302: {"description": "Success response"},
401: {"description": "Authorization required", "schema": ErrorSchema},
500: {"description": "Internal server error", "schema": ErrorSchema},
},
security=[{"token": [POST_PERMISSION]}],
permission=POST_PERMISSION,
code=302,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
async def post(self) -> None:
"""
logout user from the service

View File

@ -17,12 +17,11 @@
# 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 Response, json_response
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, LogSchema, PackageNameSchema, PaginationSchema
from ahriman.web.apispec.decorators import apidocs
from ahriman.web.schemas import LogSchema, PackageNameSchema, PaginationSchema
from ahriman.web.views.base import BaseView
from ahriman.web.views.status_view_guard import StatusViewGuard
@ -38,23 +37,17 @@ class LogsView(StatusViewGuard, BaseView):
GET_PERMISSION = UserAccess.Reporter
ROUTES = ["/api/v2/packages/{package}/logs"]
@aiohttp_apispec.docs(
@apidocs(
tags=["Packages"],
summary="Get paginated package logs",
description="Retrieve package logs and the last package status",
responses={
200: {"description": "Success response", "schema": LogSchema(many=True)},
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 and/or repository are unknown", "schema": ErrorSchema},
500: {"description": "Internal server error", "schema": ErrorSchema},
},
security=[{"token": [GET_PERMISSION]}],
permission=GET_PERMISSION,
error_400_enabled=True,
error_404_description="Package base and/or repository are unknown",
schema=LogSchema(many=True),
match_schema=PackageNameSchema,
query_schema=PaginationSchema,
)
@aiohttp_apispec.cookies_schema(AuthSchema)
@aiohttp_apispec.match_info_schema(PackageNameSchema)
@aiohttp_apispec.querystring_schema(PaginationSchema)
async def get(self) -> Response:
"""
get last package logs

View File

@ -33,7 +33,7 @@ from ahriman.core.spawn import Spawn
from ahriman.core.status import Client
from ahriman.core.status.watcher import Watcher
from ahriman.models.repository_id import RepositoryId
from ahriman.web.apispec import setup_apispec
from ahriman.web.apispec.spec import setup_apispec
from ahriman.web.cors import setup_cors
from ahriman.web.keys import AuthKey, ConfigurationKey, SpawnKey, WatcherKey, WorkersKey
from ahriman.web.middlewares.exception_handler import exception_handler

View File

@ -0,0 +1,160 @@
from pytest_mock import MockerFixture
from unittest.mock import MagicMock
from ahriman.models.user_access import UserAccess
from ahriman.web.apispec import decorators
from ahriman.web.apispec.decorators import _response_schema, apidocs
from ahriman.web.schemas import LoginSchema
def test_response_schema() -> None:
"""
must generate response schema
"""
schema = _response_schema(None)
assert schema.pop(204)
assert schema.pop(401)
assert schema.pop(403)
assert schema.pop(500)
def test_response_schema_no_403() -> None:
"""
must generate response schema without 403 error
"""
schema = _response_schema(None, error_403_enabled=False)
assert 403 not in schema
def test_response_schema_400() -> None:
"""
must generate response schema with 400 error
"""
schema = _response_schema(None, error_400_enabled=True)
assert schema.pop(400)
def test_response_schema_404() -> None:
"""
must generate response schema with 404 error
"""
schema = _response_schema(None, error_404_description="description")
assert schema.pop(404)
def test_response_schema_200() -> None:
"""
must generate response schema with 200 response
"""
schema = _response_schema(LoginSchema)
response = schema.pop(200)
assert response["schema"] == LoginSchema
assert 204 not in schema
def test_response_schema_code() -> None:
"""
must override status code
"""
schema = _response_schema(None, code=302)
assert schema.pop(302)
assert 204 not in schema
def test_apidocs() -> None:
"""
must return decorated function
"""
annotated = apidocs(
tags=["tags"],
summary="summary",
description="description",
permission=UserAccess.Unauthorized,
)(MagicMock())
assert annotated.__apispec__
def test_apidocs_authorization() -> None:
"""
must return decorated function with authorization details
"""
annotated = apidocs(
tags=["tags"],
summary="summary",
description="description",
permission=UserAccess.Full,
)(MagicMock())
assert any(schema["put_into"] == "cookies" for schema in annotated.__schemas__)
def test_apidocs_match() -> None:
"""
must return decorated function with match details
"""
annotated = apidocs(
tags=["tags"],
summary="summary",
description="description",
permission=UserAccess.Unauthorized,
match_schema=LoginSchema,
)(MagicMock())
assert any(schema["put_into"] == "match_info" for schema in annotated.__schemas__)
def test_apidocs_querystring() -> None:
"""
must return decorated function with query string details
"""
annotated = apidocs(
tags=["tags"],
summary="summary",
description="description",
permission=UserAccess.Unauthorized,
query_schema=LoginSchema,
)(MagicMock())
assert any(schema["put_into"] == "querystring" for schema in annotated.__schemas__)
def test_apidocs_json() -> None:
"""
must return decorated function with json details
"""
annotated = apidocs(
tags=["tags"],
summary="summary",
description="description",
permission=UserAccess.Unauthorized,
body_schema=LoginSchema,
)(MagicMock())
assert any(schema["put_into"] == "json" for schema in annotated.__schemas__)
def test_apidocs_form() -> None:
"""
must return decorated function with generic body details
"""
annotated = apidocs(
tags=["tags"],
summary="summary",
description="description",
permission=UserAccess.Unauthorized,
body_schema=LoginSchema,
body_location="form",
)(MagicMock())
assert any(schema["put_into"] == "form" for schema in annotated.__schemas__)
def test_apidocs_import_error(mocker: MockerFixture) -> None:
"""
must return same function if no apispec module available
"""
mocker.patch.object(decorators, "aiohttp_apispec", None)
mock = MagicMock()
annotated = apidocs(
tags=["tags"],
summary="summary",
description="description",
permission=UserAccess.Unauthorized,
)(mock)
assert annotated == mock

View File

@ -0,0 +1,24 @@
import importlib
import sys
from pytest_mock import MockerFixture
from ahriman.web import apispec
def test_import_apispec() -> None:
"""
must correctly import apispec
"""
assert apispec.aiohttp_apispec
def test_import_apispec_missing(mocker: MockerFixture) -> None:
"""
must correctly process missing module
"""
mocker.patch.dict(sys.modules, {"aiohttp_apispec": None})
importlib.reload(apispec)
assert apispec.aiohttp_apispec is None
assert apispec.Schema is object
assert apispec.fields("arg", kwargs=42)

View File

@ -4,7 +4,8 @@ from aiohttp.web import Application
from pytest_mock import MockerFixture
from ahriman import __version__
from ahriman.web.apispec import _info, _security, _servers, setup_apispec
from ahriman.web.apispec import spec
from ahriman.web.apispec.spec import _info, _security, _servers, setup_apispec
from ahriman.web.keys import ConfigurationKey
@ -47,7 +48,7 @@ def test_setup_apispec(application: Application, mocker: MockerFixture) -> None:
must set api specification
"""
apispec_mock = mocker.patch("aiohttp_apispec.setup_aiohttp_apispec")
setup_apispec(application)
assert setup_apispec(application)
apispec_mock.assert_called_once_with(
application,
url="/api-docs/swagger.json",
@ -56,3 +57,11 @@ def test_setup_apispec(application: Application, mocker: MockerFixture) -> None:
servers=pytest.helpers.anyvar(int),
security=pytest.helpers.anyvar(int),
)
def test_setup_apispec_import_error(application: Application, mocker: MockerFixture) -> None:
"""
must return none if apispec is not available
"""
mocker.patch.object(spec, "aiohttp_apispec", None)
assert setup_apispec(application) is None

View File

@ -90,6 +90,8 @@ def schema_request(handler: Callable[..., Awaitable[Any]], *, location: str = "j
Schema: request schema as set by the decorators
"""
schemas: list[dict[str, Any]] = handler.__schemas__ # type: ignore[attr-defined]
help(handler)
print(schemas)
return next(schema["schema"] for schema in schemas if schema["put_into"] == location)