mirror of
https://github.com/arcan1s/ahriman.git
synced 2026-04-01 06:03:39 +00:00
use api generated docs instead of comments (#92)
This commit is contained in:
19
src/ahriman/web/views/api/__init__.py
Normal file
19
src/ahriman/web/views/api/__init__.py
Normal file
@@ -0,0 +1,19 @@
|
||||
#
|
||||
# Copyright (c) 2021-2023 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/>.
|
||||
#
|
||||
46
src/ahriman/web/views/api/docs.py
Normal file
46
src/ahriman/web/views/api/docs.py
Normal file
@@ -0,0 +1,46 @@
|
||||
#
|
||||
# Copyright (c) 2021-2023 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_jinja2
|
||||
|
||||
from typing import Any, Dict
|
||||
|
||||
from ahriman.models.user_access import UserAccess
|
||||
from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
class DocsView(BaseView):
|
||||
"""
|
||||
api docs view
|
||||
|
||||
Attributes:
|
||||
GET_PERMISSION(UserAccess): (class attribute) get permissions of self
|
||||
"""
|
||||
|
||||
GET_PERMISSION = UserAccess.Unauthorized
|
||||
|
||||
@aiohttp_jinja2.template("api.jinja2")
|
||||
async def get(self) -> Dict[str, Any]:
|
||||
"""
|
||||
return static docs html
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: parameters for jinja template
|
||||
"""
|
||||
return {}
|
||||
76
src/ahriman/web/views/api/swagger.py
Normal file
76
src/ahriman/web/views/api/swagger.py
Normal file
@@ -0,0 +1,76 @@
|
||||
#
|
||||
# Copyright (c) 2021-2023 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 aiohttp.web import Response, json_response
|
||||
from typing import Callable, Dict
|
||||
|
||||
from ahriman.core.util import partition
|
||||
from ahriman.models.user_access import UserAccess
|
||||
from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
class SwaggerView(BaseView):
|
||||
"""
|
||||
api docs specification view
|
||||
|
||||
Attributes:
|
||||
GET_PERMISSION(UserAccess): (class attribute) get permissions of self
|
||||
"""
|
||||
|
||||
GET_PERMISSION = UserAccess.Unauthorized
|
||||
|
||||
async def get(self) -> Response:
|
||||
"""
|
||||
get api specification
|
||||
|
||||
Returns:
|
||||
Response: 200 with json api specification
|
||||
"""
|
||||
spec = self.request.app["swagger_dict"]
|
||||
is_body_parameter: Callable[[Dict[str, str]], bool] = lambda p: p["in"] == "body"
|
||||
|
||||
# special workaround because it writes request body to parameters section
|
||||
paths = spec["paths"]
|
||||
for methods in paths.values():
|
||||
for method in methods.values():
|
||||
if "parameters" not in method:
|
||||
continue
|
||||
|
||||
body, other = partition(method["parameters"], is_body_parameter)
|
||||
if not body:
|
||||
continue # there were no ``body`` parameters found
|
||||
|
||||
# there should be only one body parameters
|
||||
method["requestBody"] = {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": next(iter(body))["schema"]
|
||||
}
|
||||
}
|
||||
}
|
||||
method["parameters"] = other
|
||||
|
||||
# inject security schema
|
||||
spec["components"]["securitySchemes"] = {
|
||||
key: value
|
||||
for schema in spec["security"]
|
||||
for key, value in schema.items()
|
||||
}
|
||||
|
||||
return json_response(spec)
|
||||
@@ -19,8 +19,9 @@
|
||||
#
|
||||
from __future__ import annotations
|
||||
|
||||
from aiohttp.web import Request, View
|
||||
from typing import Any, Callable, Dict, List, Optional, Type, TypeVar
|
||||
from aiohttp_cors import CorsViewMixin # type: ignore
|
||||
from aiohttp.web import Request, StreamResponse, View
|
||||
from typing import Any, Awaitable, Callable, Dict, List, Optional, Type, TypeVar
|
||||
|
||||
from ahriman.core.auth import Auth
|
||||
from ahriman.core.configuration import Configuration
|
||||
@@ -28,15 +29,19 @@ from ahriman.core.spawn import Spawn
|
||||
from ahriman.core.status.watcher import Watcher
|
||||
from ahriman.models.user_access import UserAccess
|
||||
|
||||
|
||||
T = TypeVar("T", str, List[str])
|
||||
|
||||
|
||||
class BaseView(View):
|
||||
class BaseView(View, CorsViewMixin):
|
||||
"""
|
||||
base web view to make things typed
|
||||
|
||||
Attributes:
|
||||
OPTIONS_PERMISSION(UserAccess): (class attribute) options permissions of self
|
||||
"""
|
||||
|
||||
OPTIONS_PERMISSION = UserAccess.Unauthorized
|
||||
|
||||
@property
|
||||
def configuration(self) -> Configuration:
|
||||
"""
|
||||
@@ -92,7 +97,8 @@ class BaseView(View):
|
||||
Returns:
|
||||
UserAccess: extracted permission
|
||||
"""
|
||||
permission: UserAccess = getattr(cls, f"{request.method.upper()}_PERMISSION", UserAccess.Full)
|
||||
method = "GET" if (other := request.method.upper()) == "HEAD" else other
|
||||
permission: UserAccess = getattr(cls, f"{method}_PERMISSION", UserAccess.Full)
|
||||
return permission
|
||||
|
||||
@staticmethod
|
||||
@@ -118,23 +124,6 @@ class BaseView(View):
|
||||
raise KeyError(f"Key {key} is missing or empty")
|
||||
return value
|
||||
|
||||
async def extract_data(self, list_keys: Optional[List[str]] = None) -> Dict[str, Any]:
|
||||
"""
|
||||
extract json data from either json or form data
|
||||
|
||||
Args:
|
||||
list_keys(Optional[List[str]], optional): optional list of keys which must be forced to list from form data
|
||||
(Default value = None)
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: raw json object or form data converted to json
|
||||
"""
|
||||
try:
|
||||
json: Dict[str, Any] = await self.request.json()
|
||||
return json
|
||||
except ValueError:
|
||||
return await self.data_as_json(list_keys or [])
|
||||
|
||||
async def data_as_json(self, list_keys: List[str]) -> Dict[str, Any]:
|
||||
"""
|
||||
extract form data and convert it to json object
|
||||
@@ -158,3 +147,39 @@ class BaseView(View):
|
||||
else:
|
||||
json[key] = value
|
||||
return json
|
||||
|
||||
async def extract_data(self, list_keys: Optional[List[str]] = None) -> Dict[str, Any]:
|
||||
"""
|
||||
extract json data from either json or form data
|
||||
|
||||
Args:
|
||||
list_keys(Optional[List[str]], optional): optional list of keys which must be forced to list from form data
|
||||
(Default value = None)
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: raw json object or form data converted to json
|
||||
"""
|
||||
try:
|
||||
json: Dict[str, Any] = await self.request.json()
|
||||
return json
|
||||
except ValueError:
|
||||
return await self.data_as_json(list_keys or [])
|
||||
|
||||
# pylint: disable=not-callable,protected-access
|
||||
async def head(self) -> StreamResponse: # type: ignore
|
||||
"""
|
||||
HEAD method implementation based on the result of GET method
|
||||
|
||||
Raises:
|
||||
HTTPMethodNotAllowed: in case if there is no GET method implemented
|
||||
"""
|
||||
get_method: Optional[Callable[[], Awaitable[StreamResponse]]] = getattr(self, "get", None)
|
||||
# using if/else in order to suppress mypy warning which doesn't know that
|
||||
# ``_raise_allowed_methods`` raises exception
|
||||
if get_method is not None:
|
||||
# there is a bug in pylint, see https://github.com/pylint-dev/pylint/issues/6005
|
||||
response = await get_method()
|
||||
response._body = b"" # type: ignore
|
||||
return response
|
||||
|
||||
self._raise_allowed_methods()
|
||||
|
||||
@@ -41,10 +41,9 @@ class IndexView(BaseView):
|
||||
|
||||
Attributes:
|
||||
GET_PERMISSION(UserAccess): (class attribute) get permissions of self
|
||||
HEAD_PERMISSION(UserAccess): (class attribute) head permissions of self
|
||||
"""
|
||||
|
||||
GET_PERMISSION = HEAD_PERMISSION = UserAccess.Unauthorized
|
||||
GET_PERMISSION = UserAccess.Unauthorized
|
||||
|
||||
@aiohttp_jinja2.template("build-status.jinja2")
|
||||
async def get(self) -> Dict[str, Any]:
|
||||
|
||||
@@ -17,9 +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
|
||||
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent
|
||||
|
||||
from ahriman.models.user_access import UserAccess
|
||||
from ahriman.web.schemas.auth_schema import AuthSchema
|
||||
from ahriman.web.schemas.error_schema import ErrorSchema
|
||||
from ahriman.web.schemas.package_names_schema import PackageNamesSchema
|
||||
from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
@@ -33,34 +38,28 @@ class AddView(BaseView):
|
||||
|
||||
POST_PERMISSION = UserAccess.Full
|
||||
|
||||
@aiohttp_apispec.docs(
|
||||
tags=["Actions"],
|
||||
summary="Add new package",
|
||||
description="Add new package(s) from AUR",
|
||||
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]}],
|
||||
)
|
||||
@aiohttp_apispec.cookies_schema(AuthSchema)
|
||||
@aiohttp_apispec.json_schema(PackageNamesSchema)
|
||||
async def post(self) -> None:
|
||||
"""
|
||||
add new package
|
||||
|
||||
JSON body must be supplied, the following model is used::
|
||||
|
||||
{
|
||||
"packages": ["ahriman"] # either list of packages or package name as in AUR
|
||||
}
|
||||
|
||||
Raises:
|
||||
HTTPBadRequest: if bad data is supplied
|
||||
HTTPNoContent: in case of success response
|
||||
|
||||
Examples:
|
||||
Example of command by using curl::
|
||||
|
||||
$ curl -v -H 'Content-Type: application/json' 'http://example.com/api/v1/service/add' -d '{"packages": ["ahriman"]}'
|
||||
> POST /api/v1/service/add HTTP/1.1
|
||||
> Host: example.com
|
||||
> User-Agent: curl/7.86.0
|
||||
> Accept: */*
|
||||
> Content-Type: application/json
|
||||
> Content-Length: 25
|
||||
>
|
||||
< HTTP/1.1 204 No Content
|
||||
< Date: Wed, 23 Nov 2022 18:44:21 GMT
|
||||
< Server: Python/3.10 aiohttp/3.8.3
|
||||
<
|
||||
"""
|
||||
try:
|
||||
data = await self.extract_data(["packages"])
|
||||
|
||||
@@ -17,9 +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
|
||||
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, HTTPNotFound, Response, json_response
|
||||
|
||||
from ahriman.models.user_access import UserAccess
|
||||
from ahriman.web.schemas.auth_schema import AuthSchema
|
||||
from ahriman.web.schemas.error_schema import ErrorSchema
|
||||
from ahriman.web.schemas.pgp_key_id_schema import PGPKeyIdSchema
|
||||
from ahriman.web.schemas.pgp_key_schema import PGPKeySchema
|
||||
from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
@@ -29,17 +35,31 @@ class PGPView(BaseView):
|
||||
|
||||
Attributes:
|
||||
GET_PERMISSION(UserAccess): (class attribute) get permissions of self
|
||||
HEAD_PERMISSION(UserAccess): (class attribute) head permissions of self
|
||||
POST_PERMISSION(UserAccess): (class attribute) post permissions of self
|
||||
"""
|
||||
|
||||
POST_PERMISSION = UserAccess.Full
|
||||
GET_PERMISSION = HEAD_PERMISSION = UserAccess.Reporter
|
||||
GET_PERMISSION = UserAccess.Reporter
|
||||
|
||||
@aiohttp_apispec.docs(
|
||||
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": "Package base is unknown", "schema": ErrorSchema},
|
||||
500: {"description": "Internal server error", "schema": ErrorSchema},
|
||||
},
|
||||
security=[{"token": [GET_PERMISSION]}],
|
||||
)
|
||||
@aiohttp_apispec.cookies_schema(AuthSchema)
|
||||
@aiohttp_apispec.querystring_schema(PGPKeyIdSchema)
|
||||
async def get(self) -> Response:
|
||||
"""
|
||||
retrieve key from the key server. It supports two query parameters: ``key`` - pgp key fingerprint and
|
||||
``server`` which points to valid PGP key server
|
||||
retrieve key from the key server
|
||||
|
||||
Returns:
|
||||
Response: 200 with key body on success
|
||||
@@ -47,24 +67,7 @@ class PGPView(BaseView):
|
||||
Raises:
|
||||
HTTPBadRequest: if bad data is supplied
|
||||
HTTPNotFound: if key wasn't found or service was unable to fetch it
|
||||
|
||||
Examples:
|
||||
Example of command by using curl::
|
||||
|
||||
$ curl -v -H 'Accept: application/json' 'http://example.com/api/v1/service/pgp?key=0xE989490C&server=keyserver.ubuntu.com'
|
||||
> GET /api/v1/service/pgp?key=0xE989490C&server=keyserver.ubuntu.com HTTP/1.1
|
||||
> Host: example.com
|
||||
> User-Agent: curl/7.86.0
|
||||
> Accept: application/json
|
||||
>
|
||||
< HTTP/1.1 200 OK
|
||||
< Content-Type: application/json; charset=utf-8
|
||||
< Content-Length: 3275
|
||||
< Date: Fri, 25 Nov 2022 22:54:02 GMT
|
||||
< Server: Python/3.10 aiohttp/3.8.3
|
||||
<
|
||||
{"key": "key"}
|
||||
"""
|
||||
"""
|
||||
try:
|
||||
key = self.get_non_empty(self.request.query.getone, "key")
|
||||
server = self.get_non_empty(self.request.query.getone, "server")
|
||||
@@ -78,36 +81,28 @@ class PGPView(BaseView):
|
||||
|
||||
return json_response({"key": key})
|
||||
|
||||
@aiohttp_apispec.docs(
|
||||
tags=["Actions"],
|
||||
summary="Fetch PGP key",
|
||||
description="Fetch PGP key from the key server",
|
||||
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]}],
|
||||
)
|
||||
@aiohttp_apispec.cookies_schema(AuthSchema)
|
||||
@aiohttp_apispec.json_schema(PGPKeyIdSchema)
|
||||
async def post(self) -> None:
|
||||
"""
|
||||
store key to the local service environment
|
||||
|
||||
JSON body must be supplied, the following model is used::
|
||||
|
||||
{
|
||||
"key": "0x8BE91E5A773FB48AC05CC1EDBED105AED6246B39", # key fingerprint to import
|
||||
"server": "keyserver.ubuntu.com" # optional pgp server address
|
||||
}
|
||||
|
||||
Raises:
|
||||
HTTPBadRequest: if bad data is supplied
|
||||
HTTPNoContent: in case of success response
|
||||
|
||||
Examples:
|
||||
Example of command by using curl::
|
||||
|
||||
$ curl -v -H 'Content-Type: application/json' 'http://example.com/api/v1/service/pgp' -d '{"key": "0xE989490C"}'
|
||||
> POST /api/v1/service/pgp HTTP/1.1
|
||||
> Host: example.com
|
||||
> User-Agent: curl/7.86.0
|
||||
> Accept: */*
|
||||
> Content-Type: application/json
|
||||
> Content-Length: 21
|
||||
>
|
||||
< HTTP/1.1 204 No Content
|
||||
< Date: Fri, 25 Nov 2022 22:55:56 GMT
|
||||
< Server: Python/3.10 aiohttp/3.8.3
|
||||
<
|
||||
"""
|
||||
data = await self.extract_data()
|
||||
|
||||
|
||||
@@ -17,9 +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
|
||||
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent
|
||||
|
||||
from ahriman.models.user_access import UserAccess
|
||||
from ahriman.web.schemas.auth_schema import AuthSchema
|
||||
from ahriman.web.schemas.error_schema import ErrorSchema
|
||||
from ahriman.web.schemas.package_names_schema import PackageNamesSchema
|
||||
from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
@@ -33,40 +38,33 @@ class RebuildView(BaseView):
|
||||
|
||||
POST_PERMISSION = UserAccess.Full
|
||||
|
||||
@aiohttp_apispec.docs(
|
||||
tags=["Actions"],
|
||||
summary="Rebuild packages",
|
||||
description="Rebuild packages which depend on specified one",
|
||||
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]}],
|
||||
)
|
||||
@aiohttp_apispec.cookies_schema(AuthSchema)
|
||||
@aiohttp_apispec.json_schema(PackageNamesSchema)
|
||||
async def post(self) -> None:
|
||||
"""
|
||||
rebuild packages based on their dependency
|
||||
|
||||
JSON body must be supplied, the following model is used::
|
||||
|
||||
{
|
||||
"packages": ["ahriman"] # either list of packages or package name of dependency
|
||||
}
|
||||
|
||||
Raises:
|
||||
HTTPBadRequest: if bad data is supplied
|
||||
HTTPNoContent: in case of success response
|
||||
|
||||
Examples:
|
||||
Example of command by using curl::
|
||||
|
||||
$ curl -v -H 'Content-Type: application/json' 'http://example.com/api/v1/service/rebuild' -d '{"packages": ["python"]}'
|
||||
> POST /api/v1/service/rebuild HTTP/1.1
|
||||
> Host: example.com
|
||||
> User-Agent: curl/7.86.0
|
||||
> Accept: */*
|
||||
> Content-Type: application/json
|
||||
> Content-Length: 24
|
||||
>
|
||||
< HTTP/1.1 204 No Content
|
||||
< Date: Sun, 27 Nov 2022 00:22:26 GMT
|
||||
< Server: Python/3.10 aiohttp/3.8.3
|
||||
<
|
||||
"""
|
||||
try:
|
||||
data = await self.extract_data(["packages"])
|
||||
packages = self.get_non_empty(lambda key: [package for package in data[key] if package], "packages")
|
||||
depends_on = next(package for package in packages)
|
||||
depends_on = next(iter(packages))
|
||||
except Exception as e:
|
||||
raise HTTPBadRequest(reason=str(e))
|
||||
|
||||
|
||||
@@ -17,9 +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
|
||||
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent
|
||||
|
||||
from ahriman.models.user_access import UserAccess
|
||||
from ahriman.web.schemas.auth_schema import AuthSchema
|
||||
from ahriman.web.schemas.error_schema import ErrorSchema
|
||||
from ahriman.web.schemas.package_names_schema import PackageNamesSchema
|
||||
from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
@@ -33,35 +38,28 @@ class RemoveView(BaseView):
|
||||
|
||||
POST_PERMISSION = UserAccess.Full
|
||||
|
||||
@aiohttp_apispec.docs(
|
||||
tags=["Actions"],
|
||||
summary="Remove packages",
|
||||
description="Remove specified packages from the repository",
|
||||
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]}],
|
||||
)
|
||||
@aiohttp_apispec.cookies_schema(AuthSchema)
|
||||
@aiohttp_apispec.json_schema(PackageNamesSchema)
|
||||
async def post(self) -> None:
|
||||
"""
|
||||
remove existing packages
|
||||
|
||||
JSON body must be supplied, the following model is used::
|
||||
|
||||
{
|
||||
"packages": ["ahriman"] # either list of packages or package name
|
||||
}
|
||||
|
||||
Raises:
|
||||
HTTPBadRequest: if bad data is supplied
|
||||
HTTPNoContent: in case of success response
|
||||
|
||||
Examples:
|
||||
Example of command by using curl::
|
||||
|
||||
$ curl -v -H 'Content-Type: application/json' 'http://example.com/api/v1/service/remove' -d '{"packages": ["ahriman"]}'
|
||||
> POST /api/v1/service/remove HTTP/1.1
|
||||
> Host: example.com
|
||||
> User-Agent: curl/7.86.0
|
||||
> Accept: */*
|
||||
> Content-Type: application/json
|
||||
> Content-Length: 25
|
||||
>
|
||||
< HTTP/1.1 204 No Content
|
||||
< Date: Wed, 23 Nov 2022 18:57:56 GMT
|
||||
< Server: Python/3.10 aiohttp/3.8.3
|
||||
<
|
||||
"""
|
||||
try:
|
||||
data = await self.extract_data(["packages"])
|
||||
|
||||
@@ -17,9 +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
|
||||
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent
|
||||
|
||||
from ahriman.models.user_access import UserAccess
|
||||
from ahriman.web.schemas.auth_schema import AuthSchema
|
||||
from ahriman.web.schemas.error_schema import ErrorSchema
|
||||
from ahriman.web.schemas.package_names_schema import PackageNamesSchema
|
||||
from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
@@ -33,35 +38,28 @@ class RequestView(BaseView):
|
||||
|
||||
POST_PERMISSION = UserAccess.Reporter
|
||||
|
||||
@aiohttp_apispec.docs(
|
||||
tags=["Actions"],
|
||||
summary="Request new package",
|
||||
description="Request new package(s) to be added from AUR",
|
||||
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]}],
|
||||
)
|
||||
@aiohttp_apispec.cookies_schema(AuthSchema)
|
||||
@aiohttp_apispec.json_schema(PackageNamesSchema)
|
||||
async def post(self) -> None:
|
||||
"""
|
||||
request to add new package
|
||||
|
||||
JSON body must be supplied, the following model is used::
|
||||
|
||||
{
|
||||
"packages": ["ahriman"] # either list of packages or package name as in AUR
|
||||
}
|
||||
|
||||
Raises:
|
||||
HTTPBadRequest: if bad data is supplied
|
||||
HTTPNoContent: in case of success response
|
||||
|
||||
Examples:
|
||||
Example of command by using curl::
|
||||
|
||||
$ curl -v -H 'Content-Type: application/json' 'http://example.com/api/v1/service/request' -d '{"packages": ["ahriman"]}'
|
||||
> POST /api/v1/service/request HTTP/1.1
|
||||
> Host: example.com
|
||||
> User-Agent: curl/7.86.0
|
||||
> Accept: */*
|
||||
> Content-Type: application/json
|
||||
> Content-Length: 25
|
||||
>
|
||||
< HTTP/1.1 204 No Content
|
||||
< Date: Wed, 23 Nov 2022 18:59:32 GMT
|
||||
< Server: Python/3.10 aiohttp/3.8.3
|
||||
<
|
||||
"""
|
||||
try:
|
||||
data = await self.extract_data(["packages"])
|
||||
|
||||
@@ -17,12 +17,18 @@
|
||||
# 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
|
||||
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNotFound, Response, json_response
|
||||
from typing import Callable, List
|
||||
|
||||
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.aur_package_schema import AURPackageSchema
|
||||
from ahriman.web.schemas.auth_schema import AuthSchema
|
||||
from ahriman.web.schemas.error_schema import ErrorSchema
|
||||
from ahriman.web.schemas.search_schema import SearchSchema
|
||||
from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
@@ -32,14 +38,29 @@ class SearchView(BaseView):
|
||||
|
||||
Attributes:
|
||||
GET_PERMISSION(UserAccess): (class attribute) get permissions of self
|
||||
HEAD_PERMISSION(UserAccess): (class attribute) head permissions of self
|
||||
"""
|
||||
|
||||
GET_PERMISSION = HEAD_PERMISSION = UserAccess.Reporter
|
||||
GET_PERMISSION = UserAccess.Reporter
|
||||
|
||||
@aiohttp_apispec.docs(
|
||||
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]}],
|
||||
)
|
||||
@aiohttp_apispec.cookies_schema(AuthSchema)
|
||||
@aiohttp_apispec.querystring_schema(SearchSchema)
|
||||
async def get(self) -> Response:
|
||||
"""
|
||||
search packages in AUR. Search string (non-empty) must be supplied as ``for`` parameter
|
||||
search packages in AUR
|
||||
|
||||
Returns:
|
||||
Response: 200 with found package bases and descriptions sorted by base
|
||||
@@ -47,23 +68,6 @@ class SearchView(BaseView):
|
||||
Raises:
|
||||
HTTPBadRequest: in case if bad data is supplied
|
||||
HTTPNotFound: if no packages found
|
||||
|
||||
Examples:
|
||||
Example of command by using curl::
|
||||
|
||||
$ curl -v -H 'Accept: application/json' 'http://example.com/api/v1/service/search?for=ahriman'
|
||||
> GET /api/v1/service/search?for=ahriman HTTP/1.1
|
||||
> Host: example.com
|
||||
> User-Agent: curl/7.86.0
|
||||
> Accept: application/json
|
||||
>
|
||||
< HTTP/1.1 200 OK
|
||||
< Content-Type: application/json; charset=utf-8
|
||||
< Content-Length: 148
|
||||
< Date: Wed, 23 Nov 2022 19:07:13 GMT
|
||||
< Server: Python/3.10 aiohttp/3.8.3
|
||||
<
|
||||
[{"package": "ahriman", "description": "ArcH linux ReposItory MANager"}, {"package": "ahriman-git", "description": "ArcH Linux ReposItory MANager"}]
|
||||
"""
|
||||
try:
|
||||
search: List[str] = self.get_non_empty(lambda key: self.request.query.getall(key, default=[]), "for")
|
||||
|
||||
@@ -17,9 +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
|
||||
|
||||
from aiohttp.web import HTTPNoContent
|
||||
|
||||
from ahriman.models.user_access import UserAccess
|
||||
from ahriman.web.schemas.auth_schema import AuthSchema
|
||||
from ahriman.web.schemas.error_schema import ErrorSchema
|
||||
from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
@@ -33,26 +37,25 @@ class UpdateView(BaseView):
|
||||
|
||||
POST_PERMISSION = UserAccess.Full
|
||||
|
||||
@aiohttp_apispec.docs(
|
||||
tags=["Actions"],
|
||||
summary="Update packages",
|
||||
description="Run repository update process",
|
||||
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": [POST_PERMISSION]}],
|
||||
)
|
||||
@aiohttp_apispec.cookies_schema(AuthSchema)
|
||||
async def post(self) -> None:
|
||||
"""
|
||||
run repository update. No parameters supported here
|
||||
|
||||
Raises:
|
||||
HTTPNoContent: in case of success response
|
||||
|
||||
Examples:
|
||||
Example of command by using curl::
|
||||
|
||||
$ curl -v -XPOST 'http://example.com/api/v1/service/update'
|
||||
> POST /api/v1/service/update HTTP/1.1
|
||||
> Host: example.com
|
||||
> User-Agent: curl/7.86.0
|
||||
> Accept: */*
|
||||
>
|
||||
< HTTP/1.1 204 No Content
|
||||
< Date: Fri, 25 Nov 2022 22:57:56 GMT
|
||||
< Server: Python/3.10 aiohttp/3.8.3
|
||||
<
|
||||
"""
|
||||
self.spawner.packages_update()
|
||||
|
||||
|
||||
@@ -17,11 +17,18 @@
|
||||
# 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
|
||||
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, HTTPNotFound, Response, json_response
|
||||
|
||||
from ahriman.core.exceptions import UnknownPackageError
|
||||
from ahriman.models.log_record_id import LogRecordId
|
||||
from ahriman.models.user_access import UserAccess
|
||||
from ahriman.web.schemas.auth_schema import AuthSchema
|
||||
from ahriman.web.schemas.error_schema import ErrorSchema
|
||||
from ahriman.web.schemas.log_schema import LogSchema
|
||||
from ahriman.web.schemas.logs_schema import LogsSchema
|
||||
from ahriman.web.schemas.package_name_schema import PackageNameSchema
|
||||
from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
@@ -32,39 +39,53 @@ class LogsView(BaseView):
|
||||
Attributes:
|
||||
DELETE_PERMISSION(UserAccess): (class attribute) delete permissions of self
|
||||
GET_PERMISSION(UserAccess): (class attribute) get permissions of self
|
||||
HEAD_PERMISSION(UserAccess): (class attribute) head permissions of self
|
||||
POST_PERMISSION(UserAccess): (class attribute) post permissions of self
|
||||
"""
|
||||
|
||||
DELETE_PERMISSION = POST_PERMISSION = UserAccess.Full
|
||||
GET_PERMISSION = HEAD_PERMISSION = UserAccess.Reporter
|
||||
GET_PERMISSION = UserAccess.Reporter
|
||||
|
||||
@aiohttp_apispec.docs(
|
||||
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},
|
||||
500: {"description": "Internal server error", "schema": ErrorSchema},
|
||||
},
|
||||
security=[{"token": [DELETE_PERMISSION]}],
|
||||
)
|
||||
@aiohttp_apispec.cookies_schema(AuthSchema)
|
||||
@aiohttp_apispec.match_info_schema(PackageNameSchema)
|
||||
async def delete(self) -> None:
|
||||
"""
|
||||
delete package logs
|
||||
|
||||
Raises:
|
||||
HTTPNoContent: on success response
|
||||
|
||||
Examples:
|
||||
Example of command by using curl::
|
||||
|
||||
$ curl -v -XDELETE 'http://example.com/api/v1/packages/ahriman/logs'
|
||||
> DELETE /api/v1/packages/ahriman/logs HTTP/1.1
|
||||
> Host: example.com
|
||||
> User-Agent: curl/7.86.0
|
||||
> Accept: */*
|
||||
>
|
||||
< HTTP/1.1 204 No Content
|
||||
< Date: Wed, 23 Nov 2022 19:26:40 GMT
|
||||
< Server: Python/3.10 aiohttp/3.8.3
|
||||
<
|
||||
"""
|
||||
package_base = self.request.match_info["package"]
|
||||
self.service.remove_logs(package_base, None)
|
||||
|
||||
raise HTTPNoContent()
|
||||
|
||||
@aiohttp_apispec.docs(
|
||||
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 is 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)
|
||||
async def get(self) -> Response:
|
||||
"""
|
||||
get last package logs
|
||||
@@ -72,22 +93,8 @@ class LogsView(BaseView):
|
||||
Returns:
|
||||
Response: 200 with package logs on success
|
||||
|
||||
Examples:
|
||||
Example of command by using curl::
|
||||
|
||||
$ curl -v -H 'Accept: application/json' 'http://example.com/api/v1/packages/ahriman/logs'
|
||||
> GET /api/v1/packages/ahriman/logs HTTP/1.1
|
||||
> Host: example.com
|
||||
> User-Agent: curl/7.86.0
|
||||
> Accept: application/json
|
||||
>
|
||||
< HTTP/1.1 200 OK
|
||||
< Content-Type: application/json; charset=utf-8
|
||||
< Content-Length: 100112
|
||||
< Date: Wed, 23 Nov 2022 19:24:14 GMT
|
||||
< Server: Python/3.10 aiohttp/3.8.3
|
||||
<
|
||||
{"package_base": "ahriman", "status": {"status": "success", "timestamp": 1669231136}, "logs": "[2022-11-23 19:17:32] clone remote https://aur.archlinux.org/ahriman.git to /tmp/tmpy9j6fq9p using branch master"}
|
||||
Raises:
|
||||
HTTPNotFound: if package base is unknown
|
||||
"""
|
||||
package_base = self.request.match_info["package"]
|
||||
|
||||
@@ -104,37 +111,29 @@ class LogsView(BaseView):
|
||||
}
|
||||
return json_response(response)
|
||||
|
||||
@aiohttp_apispec.docs(
|
||||
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},
|
||||
500: {"description": "Internal server error", "schema": ErrorSchema},
|
||||
},
|
||||
security=[{"token": [POST_PERMISSION]}],
|
||||
)
|
||||
@aiohttp_apispec.cookies_schema(AuthSchema)
|
||||
@aiohttp_apispec.match_info_schema(PackageNameSchema)
|
||||
@aiohttp_apispec.json_schema(LogSchema)
|
||||
async def post(self) -> None:
|
||||
"""
|
||||
create new package log record
|
||||
|
||||
JSON body must be supplied, the following model is used::
|
||||
|
||||
{
|
||||
"created": 42.001, # log record created timestamp
|
||||
"message": "log message", # log record
|
||||
"process_id": 42 # process id from which log record was emitted
|
||||
}
|
||||
|
||||
Raises:
|
||||
HTTPBadRequest: if bad data is supplied
|
||||
HTTPNoContent: in case of success response
|
||||
|
||||
Examples:
|
||||
Example of command by using curl::
|
||||
|
||||
$ curl -v -H 'Content-Type: application/json' 'http://example.com/api/v1/packages/ahriman/logs' -d '{"created": 1669231764.042444, "message": "my log message", "process_id": 1}'
|
||||
> POST /api/v1/packages/ahriman/logs HTTP/1.1
|
||||
> Host: example.com
|
||||
> User-Agent: curl/7.86.0
|
||||
> Accept: */*
|
||||
> Content-Type: application/json
|
||||
> Content-Length: 76
|
||||
>
|
||||
< HTTP/1.1 204 No Content
|
||||
< Date: Wed, 23 Nov 2022 19:30:45 GMT
|
||||
< Server: Python/3.10 aiohttp/3.8.3
|
||||
<
|
||||
"""
|
||||
package_base = self.request.match_info["package"]
|
||||
data = await self.extract_data()
|
||||
|
||||
@@ -17,12 +17,18 @@
|
||||
# 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
|
||||
|
||||
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.auth_schema import AuthSchema
|
||||
from ahriman.web.schemas.error_schema import ErrorSchema
|
||||
from ahriman.web.schemas.package_name_schema import PackageNameSchema
|
||||
from ahriman.web.schemas.package_status_schema import PackageStatusSchema, PackageStatusSimplifiedSchema
|
||||
from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
@@ -33,39 +39,53 @@ class PackageView(BaseView):
|
||||
Attributes:
|
||||
DELETE_PERMISSION(UserAccess): (class attribute) delete permissions of self
|
||||
GET_PERMISSION(UserAccess): (class attribute) get permissions of self
|
||||
HEAD_PERMISSION(UserAccess): (class attribute) head permissions of self
|
||||
POST_PERMISSION(UserAccess): (class attribute) post permissions of self
|
||||
"""
|
||||
|
||||
DELETE_PERMISSION = POST_PERMISSION = UserAccess.Full
|
||||
GET_PERMISSION = HEAD_PERMISSION = UserAccess.Read
|
||||
GET_PERMISSION = UserAccess.Read
|
||||
|
||||
@aiohttp_apispec.docs(
|
||||
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},
|
||||
500: {"description": "Internal server error", "schema": ErrorSchema},
|
||||
},
|
||||
security=[{"token": [DELETE_PERMISSION]}],
|
||||
)
|
||||
@aiohttp_apispec.cookies_schema(AuthSchema)
|
||||
@aiohttp_apispec.match_info_schema(PackageNameSchema)
|
||||
async def delete(self) -> None:
|
||||
"""
|
||||
delete package base from status page
|
||||
|
||||
Raises:
|
||||
HTTPNoContent: on success response
|
||||
|
||||
Examples:
|
||||
Example of command by using curl::
|
||||
|
||||
$ curl -v -XDELETE 'http://example.com/api/v1/packages/ahriman'
|
||||
> DELETE /api/v1/packages/ahriman HTTP/1.1
|
||||
> Host: example.com
|
||||
> User-Agent: curl/7.86.0
|
||||
> Accept: */*
|
||||
>
|
||||
< HTTP/1.1 204 No Content
|
||||
< Date: Wed, 23 Nov 2022 19:43:40 GMT
|
||||
< Server: Python/3.10 aiohttp/3.8.3
|
||||
<
|
||||
"""
|
||||
package_base = self.request.match_info["package"]
|
||||
self.service.remove(package_base)
|
||||
|
||||
raise HTTPNoContent()
|
||||
|
||||
@aiohttp_apispec.docs(
|
||||
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 is 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)
|
||||
async def get(self) -> Response:
|
||||
"""
|
||||
get current package base status
|
||||
@@ -75,23 +95,6 @@ class PackageView(BaseView):
|
||||
|
||||
Raises:
|
||||
HTTPNotFound: if no package was found
|
||||
|
||||
Examples:
|
||||
Example of command by using curl::
|
||||
|
||||
$ curl -v -H 'Accept: application/json' 'http://example.com/api/v1/packages/ahriman'
|
||||
> GET /api/v1/packages/ahriman HTTP/1.1
|
||||
> Host: example.com
|
||||
> User-Agent: curl/7.86.0
|
||||
> Accept: application/json
|
||||
>
|
||||
< HTTP/1.1 200 OK
|
||||
< Content-Type: application/json; charset=utf-8
|
||||
< Content-Length: 743
|
||||
< Date: Wed, 23 Nov 2022 19:41:01 GMT
|
||||
< Server: Python/3.10 aiohttp/3.8.3
|
||||
<
|
||||
[{"package": {"base": "ahriman", "version": "2.3.0-1", "remote": {"git_url": "https://aur.archlinux.org/ahriman.git", "web_url": "https://aur.archlinux.org/packages/ahriman", "path": ".", "branch": "master", "source": "aur"}, "packages": {"ahriman": {"architecture": "any", "archive_size": 247573, "build_date": 1669231069, "depends": ["devtools", "git", "pyalpm", "python-inflection", "python-passlib", "python-requests", "python-setuptools", "python-srcinfo"], "description": "ArcH linux ReposItory MANager", "filename": "ahriman-2.3.0-1-any.pkg.tar.zst", "groups": [], "installed_size": 1676153, "licenses": ["GPL3"], "provides": [], "url": "https://github.com/arcan1s/ahriman"}}}, "status": {"status": "success", "timestamp": 1669231136}}]
|
||||
"""
|
||||
package_base = self.request.match_info["package"]
|
||||
|
||||
@@ -108,37 +111,29 @@ class PackageView(BaseView):
|
||||
]
|
||||
return json_response(response)
|
||||
|
||||
@aiohttp_apispec.docs(
|
||||
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},
|
||||
500: {"description": "Internal server error", "schema": ErrorSchema},
|
||||
},
|
||||
security=[{"token": [POST_PERMISSION]}],
|
||||
)
|
||||
@aiohttp_apispec.cookies_schema(AuthSchema)
|
||||
@aiohttp_apispec.match_info_schema(PackageNameSchema)
|
||||
@aiohttp_apispec.json_schema(PackageStatusSimplifiedSchema)
|
||||
async def post(self) -> None:
|
||||
"""
|
||||
update package build status
|
||||
|
||||
JSON body must be supplied, the following model is used::
|
||||
|
||||
{
|
||||
"status": "unknown", # package build status string, must be valid ``BuildStatusEnum``
|
||||
"package": {} # package body (use ``dataclasses.asdict`` to generate one), optional.
|
||||
# Must be supplied in case if package base is unknown
|
||||
}
|
||||
|
||||
Raises:
|
||||
HTTPBadRequest: if bad data is supplied
|
||||
HTTPNoContent: in case of success response
|
||||
|
||||
Examples:
|
||||
Example of command by using curl::
|
||||
|
||||
$ curl -v -H 'Content-Type: application/json' 'http://example.com/api/v1/packages/ahriman' -d '{"status": "success"}'
|
||||
> POST /api/v1/packages/ahriman HTTP/1.1
|
||||
> Host: example.com
|
||||
> User-Agent: curl/7.86.0
|
||||
> Accept: */*
|
||||
> Content-Type: application/json
|
||||
> Content-Length: 21
|
||||
>
|
||||
< HTTP/1.1 204 No Content
|
||||
< Date: Wed, 23 Nov 2022 19:42:49 GMT
|
||||
< Server: Python/3.10 aiohttp/3.8.3
|
||||
<
|
||||
"""
|
||||
package_base = self.request.match_info["package"]
|
||||
data = await self.extract_data()
|
||||
|
||||
@@ -17,9 +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
|
||||
|
||||
from aiohttp.web import HTTPNoContent, Response, json_response
|
||||
|
||||
from ahriman.models.user_access import UserAccess
|
||||
from ahriman.web.schemas.auth_schema import AuthSchema
|
||||
from ahriman.web.schemas.error_schema import ErrorSchema
|
||||
from ahriman.web.schemas.package_status_schema import PackageStatusSchema
|
||||
from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
@@ -29,36 +34,31 @@ class PackagesView(BaseView):
|
||||
|
||||
Attributes:
|
||||
GET_PERMISSION(UserAccess): (class attribute) get permissions of self
|
||||
HEAD_PERMISSION(UserAccess): (class attribute) head permissions of self
|
||||
POST_PERMISSION(UserAccess): (class attribute) post permissions of self
|
||||
"""
|
||||
|
||||
GET_PERMISSION = HEAD_PERMISSION = UserAccess.Read
|
||||
GET_PERMISSION = UserAccess.Read
|
||||
POST_PERMISSION = UserAccess.Full
|
||||
|
||||
@aiohttp_apispec.docs(
|
||||
tags=["Packages"],
|
||||
summary="Get packages list",
|
||||
description="Retrieve all packages and their descriptors",
|
||||
responses={
|
||||
200: {"description": "Success response", "schema": PackageStatusSchema(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]}],
|
||||
)
|
||||
@aiohttp_apispec.cookies_schema(AuthSchema)
|
||||
async def get(self) -> Response:
|
||||
"""
|
||||
get current packages status
|
||||
|
||||
Returns:
|
||||
Response: 200 with package description on success
|
||||
|
||||
Examples:
|
||||
Example of command by using curl::
|
||||
|
||||
$ curl -v -H 'Accept: application/json' 'http://example.com/api/v1/packages'
|
||||
> GET /api/v1/packages HTTP/1.1
|
||||
> Host: example.com
|
||||
> User-Agent: curl/7.86.0
|
||||
> Accept: application/json
|
||||
>
|
||||
< HTTP/1.1 200 OK
|
||||
< Content-Type: application/json; charset=utf-8
|
||||
< Content-Length: 2687
|
||||
< Date: Wed, 23 Nov 2022 19:35:24 GMT
|
||||
< Server: Python/3.10 aiohttp/3.8.3
|
||||
<
|
||||
[{"package": {"base": "ahriman", "version": "2.3.0-1", "remote": {"git_url": "https://aur.archlinux.org/ahriman.git", "web_url": "https://aur.archlinux.org/packages/ahriman", "path": ".", "branch": "master", "source": "aur"}, "packages": {"ahriman": {"architecture": "any", "archive_size": 247573, "build_date": 1669231069, "depends": ["devtools", "git", "pyalpm", "python-inflection", "python-passlib", "python-requests", "python-setuptools", "python-srcinfo"], "description": "ArcH linux ReposItory MANager", "filename": "ahriman-2.3.0-1-any.pkg.tar.zst", "groups": [], "installed_size": 1676153, "licenses": ["GPL3"], "provides": [], "url": "https://github.com/arcan1s/ahriman"}}}, "status": {"status": "success", "timestamp": 1669231136}}]
|
||||
"""
|
||||
response = [
|
||||
{
|
||||
@@ -68,26 +68,25 @@ class PackagesView(BaseView):
|
||||
]
|
||||
return json_response(response)
|
||||
|
||||
@aiohttp_apispec.docs(
|
||||
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},
|
||||
500: {"description": "Internal server error", "schema": ErrorSchema},
|
||||
},
|
||||
security=[{"token": [POST_PERMISSION]}],
|
||||
)
|
||||
@aiohttp_apispec.cookies_schema(AuthSchema)
|
||||
async def post(self) -> None:
|
||||
"""
|
||||
reload all packages from repository. No parameters supported here
|
||||
reload all packages from repository
|
||||
|
||||
Raises:
|
||||
HTTPNoContent: on success response
|
||||
|
||||
Examples:
|
||||
Example of command by using curl::
|
||||
|
||||
$ curl -v -XPOST 'http://example.com/api/v1/packages'
|
||||
> POST /api/v1/packages HTTP/1.1
|
||||
> Host: example.com
|
||||
> User-Agent: curl/7.86.0
|
||||
> Accept: */*
|
||||
>
|
||||
< HTTP/1.1 204 No Content
|
||||
< Date: Wed, 23 Nov 2022 19:38:06 GMT
|
||||
< Server: Python/3.10 aiohttp/3.8.3
|
||||
<
|
||||
"""
|
||||
self.service.load()
|
||||
|
||||
|
||||
@@ -17,6 +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/>.
|
||||
#
|
||||
import aiohttp_apispec # type: ignore
|
||||
|
||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, Response, json_response
|
||||
|
||||
from ahriman import version
|
||||
@@ -24,6 +26,10 @@ 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.auth_schema import AuthSchema
|
||||
from ahriman.web.schemas.error_schema import ErrorSchema
|
||||
from ahriman.web.schemas.internal_status_schema import InternalStatusSchema
|
||||
from ahriman.web.schemas.status_schema import StatusSchema
|
||||
from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
@@ -33,36 +39,31 @@ class StatusView(BaseView):
|
||||
|
||||
Attributes:
|
||||
GET_PERMISSION(UserAccess): (class attribute) get permissions of self
|
||||
HEAD_PERMISSION(UserAccess): (class attribute) head permissions of self
|
||||
POST_PERMISSION(UserAccess): (class attribute) post permissions of self
|
||||
"""
|
||||
|
||||
GET_PERMISSION = HEAD_PERMISSION = UserAccess.Read
|
||||
GET_PERMISSION = UserAccess.Read
|
||||
POST_PERMISSION = UserAccess.Full
|
||||
|
||||
@aiohttp_apispec.docs(
|
||||
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},
|
||||
500: {"description": "Internal server error", "schema": ErrorSchema},
|
||||
},
|
||||
security=[{"token": [GET_PERMISSION]}],
|
||||
)
|
||||
@aiohttp_apispec.cookies_schema(AuthSchema)
|
||||
async def get(self) -> Response:
|
||||
"""
|
||||
get current service status
|
||||
|
||||
Returns:
|
||||
Response: 200 with service status object
|
||||
|
||||
Examples:
|
||||
Example of command by using curl::
|
||||
|
||||
$ curl -v -H 'Accept: application/json' 'http://example.com/api/v1/status'
|
||||
> GET /api/v1/status HTTP/1.1
|
||||
> Host: example.com
|
||||
> User-Agent: curl/7.86.0
|
||||
> Accept: application/json
|
||||
>
|
||||
< HTTP/1.1 200 OK
|
||||
< Content-Type: application/json; charset=utf-8
|
||||
< Content-Length: 222
|
||||
< Date: Wed, 23 Nov 2022 19:32:31 GMT
|
||||
< Server: Python/3.10 aiohttp/3.8.3
|
||||
<
|
||||
{"status": {"status": "success", "timestamp": 1669231237}, "architecture": "x86_64", "packages": {"total": 4, "unknown": 0, "pending": 0, "building": 0, "failed": 0, "success": 4}, "repository": "repo", "version": "2.3.0"}
|
||||
"""
|
||||
counters = Counters.from_packages(self.service.packages)
|
||||
status = InternalStatus(
|
||||
@@ -74,35 +75,28 @@ class StatusView(BaseView):
|
||||
|
||||
return json_response(status.view())
|
||||
|
||||
@aiohttp_apispec.docs(
|
||||
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},
|
||||
500: {"description": "Internal server error", "schema": ErrorSchema},
|
||||
},
|
||||
security=[{"token": [POST_PERMISSION]}],
|
||||
)
|
||||
@aiohttp_apispec.cookies_schema(AuthSchema)
|
||||
@aiohttp_apispec.json_schema(StatusSchema)
|
||||
async def post(self) -> None:
|
||||
"""
|
||||
update service status
|
||||
|
||||
JSON body must be supplied, the following model is used::
|
||||
|
||||
{
|
||||
"status": "unknown", # service status string, must be valid ``BuildStatusEnum``
|
||||
}
|
||||
|
||||
Raises:
|
||||
HTTPBadRequest: if bad data is supplied
|
||||
HTTPNoContent: in case of success response
|
||||
|
||||
Examples:
|
||||
Example of command by using curl::
|
||||
|
||||
$ curl -v -H 'Content-Type: application/json' 'http://example.com/api/v1/status' -d '{"status": "success"}'
|
||||
> POST /api/v1/status HTTP/1.1
|
||||
> Host: example.com
|
||||
> User-Agent: curl/7.86.0
|
||||
> Accept: */*
|
||||
> Content-Type: application/json
|
||||
> Content-Length: 21
|
||||
>
|
||||
< HTTP/1.1 204 No Content
|
||||
< Date: Wed, 23 Nov 2022 19:33:57 GMT
|
||||
< Server: Python/3.10 aiohttp/3.8.3
|
||||
<
|
||||
"""
|
||||
try:
|
||||
data = await self.extract_data()
|
||||
|
||||
@@ -17,10 +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
|
||||
|
||||
from aiohttp.web import HTTPFound, HTTPMethodNotAllowed, HTTPUnauthorized
|
||||
|
||||
from ahriman.core.auth.helpers import remember
|
||||
from ahriman.models.user_access import UserAccess
|
||||
from ahriman.web.schemas.error_schema import ErrorSchema
|
||||
from ahriman.web.schemas.login_schema import LoginSchema
|
||||
from ahriman.web.schemas.oauth2_schema import OAuth2Schema
|
||||
from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
@@ -35,6 +40,18 @@ class LoginView(BaseView):
|
||||
|
||||
GET_PERMISSION = POST_PERMISSION = UserAccess.Unauthorized
|
||||
|
||||
@aiohttp_apispec.docs(
|
||||
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]}],
|
||||
)
|
||||
@aiohttp_apispec.querystring_schema(OAuth2Schema)
|
||||
async def get(self) -> None:
|
||||
"""
|
||||
OAuth2 response handler
|
||||
@@ -48,9 +65,6 @@ class LoginView(BaseView):
|
||||
HTTPFound: on success response
|
||||
HTTPMethodNotAllowed: in case if method is used, but OAuth is disabled
|
||||
HTTPUnauthorized: if case of authorization error
|
||||
|
||||
Examples:
|
||||
This request must not be used directly.
|
||||
"""
|
||||
from ahriman.core.auth.oauth import OAuth
|
||||
|
||||
@@ -70,43 +84,25 @@ class LoginView(BaseView):
|
||||
|
||||
raise HTTPUnauthorized()
|
||||
|
||||
@aiohttp_apispec.docs(
|
||||
tags=["Login"],
|
||||
summary="Login via basic authorization",
|
||||
description="Login by using username and password",
|
||||
responses={
|
||||
302: {"description": "Success response"},
|
||||
401: {"description": "Authorization required", "schema": ErrorSchema},
|
||||
500: {"description": "Internal server error", "schema": ErrorSchema},
|
||||
},
|
||||
security=[{"token": [POST_PERMISSION]}],
|
||||
)
|
||||
@aiohttp_apispec.json_schema(LoginSchema)
|
||||
async def post(self) -> None:
|
||||
"""
|
||||
login user to service
|
||||
|
||||
either JSON body or form data must be supplied the following fields are required::
|
||||
|
||||
{
|
||||
"username": "username", # username to use for login
|
||||
"password": "pa55w0rd" # password to use for login
|
||||
}
|
||||
|
||||
The authentication session will be passed in ``Set-Cookie`` header.
|
||||
login user to service. The authentication session will be passed in ``Set-Cookie`` header.
|
||||
|
||||
Raises:
|
||||
HTTPFound: on success response
|
||||
HTTPUnauthorized: if case of authorization error
|
||||
|
||||
Examples:
|
||||
Example of command by using curl::
|
||||
|
||||
$ curl -v -H 'Content-Type: application/json' 'http://example.com/api/v1/login' -d '{"username": "test", "password": "test"}'
|
||||
> POST /api/v1/login HTTP/1.1
|
||||
> Host: example.com
|
||||
> User-Agent: curl/7.86.0
|
||||
> Accept: */*
|
||||
> Content-Type: application/json
|
||||
> Content-Length: 40
|
||||
>
|
||||
< HTTP/1.1 302 Found
|
||||
< Content-Type: text/plain; charset=utf-8
|
||||
< Location: /
|
||||
< Content-Length: 10
|
||||
< Set-Cookie: ...
|
||||
< Date: Wed, 23 Nov 2022 17:51:27 GMT
|
||||
< Server: Python/3.10 aiohttp/3.8.3
|
||||
<
|
||||
302: Found
|
||||
"""
|
||||
data = await self.extract_data()
|
||||
identity = data.get("username")
|
||||
|
||||
@@ -17,10 +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
|
||||
|
||||
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.auth_schema import AuthSchema
|
||||
from ahriman.web.schemas.error_schema import ErrorSchema
|
||||
from ahriman.web.views.base import BaseView
|
||||
|
||||
|
||||
@@ -34,33 +38,26 @@ class LogoutView(BaseView):
|
||||
|
||||
POST_PERMISSION = UserAccess.Unauthorized
|
||||
|
||||
@aiohttp_apispec.docs(
|
||||
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]}],
|
||||
)
|
||||
@aiohttp_apispec.cookies_schema(AuthSchema)
|
||||
async def post(self) -> None:
|
||||
"""
|
||||
logout user from the service. No parameters supported here.
|
||||
logout user from the service
|
||||
|
||||
The server will respond with ``Set-Cookie`` header, in which API session cookie will be nullified.
|
||||
|
||||
Raises:
|
||||
HTTPFound: on success response
|
||||
|
||||
Examples:
|
||||
Example of command by using curl::
|
||||
|
||||
$ curl -v -XPOST 'http://example.com/api/v1/logout'
|
||||
> POST /api/v1/logout HTTP/1.1
|
||||
> Host: example.com
|
||||
> User-Agent: curl/7.86.0
|
||||
> Accept: */*
|
||||
>
|
||||
< HTTP/1.1 302 Found
|
||||
< Content-Type: text/plain; charset=utf-8
|
||||
< Location: /
|
||||
< Content-Length: 10
|
||||
< Set-Cookie: ...
|
||||
< Date: Wed, 23 Nov 2022 19:10:51 GMT
|
||||
< Server: Python/3.10 aiohttp/3.8.3
|
||||
<
|
||||
302: Found
|
||||
"""
|
||||
try:
|
||||
await check_authorized(self.request)
|
||||
|
||||
Reference in New Issue
Block a user