mirror of
				https://github.com/arcan1s/ahriman.git
				synced 2025-11-04 07:43:42 +00:00 
			
		
		
		
	make apispec dependency optional
This commit is contained in:
		@ -305,7 +305,7 @@ max-branches=12
 | 
			
		||||
max-locals=15
 | 
			
		||||
 | 
			
		||||
# Maximum number of parents for a class (see R0901).
 | 
			
		||||
max-parents=7
 | 
			
		||||
max-parents=15
 | 
			
		||||
 | 
			
		||||
# Maximum number of public methods for a class (see R0904).
 | 
			
		||||
max-public-methods=20
 | 
			
		||||
 | 
			
		||||
@ -175,11 +175,10 @@ Again, the most checks can be performed by `tox` command, though some additional
 | 
			
		||||
* Web API methods must be documented by using `aiohttp_apispec` library. The schema testing mostly should be implemented in related view class tests. Recommended example for documentation (excluding comments):
 | 
			
		||||
 | 
			
		||||
    ```python
 | 
			
		||||
    import aiohttp_apispec
 | 
			
		||||
 | 
			
		||||
    from marshmallow import Schema, fields  
 | 
			
		||||
    
 | 
			
		||||
    from ahriman.web.schemas import AuthSchema, ErrorSchema, PackageNameSchema, PaginationSchema
 | 
			
		||||
    from ahriman.web.apispec.decorators import apidocs
 | 
			
		||||
    from ahriman.web.schemas import PackageNameSchema, PaginationSchema
 | 
			
		||||
    from ahriman.web.views.base import BaseView
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -198,25 +197,17 @@ Again, the most checks can be performed by `tox` command, though some additional
 | 
			
		||||
        POST_PERMISSION = ...
 | 
			
		||||
        ROUTES = ...
 | 
			
		||||
 | 
			
		||||
        @aiohttp_apispec.docs(
 | 
			
		||||
        @apidocs(
 | 
			
		||||
            tags=["Tag"],
 | 
			
		||||
            summary="Do foo",
 | 
			
		||||
            description="Extended description of the method which does foo",
 | 
			
		||||
            responses={
 | 
			
		||||
                200: {"description": "Success response", "schema": ResponseSchema},
 | 
			
		||||
                204: {"description": "Success response"},  # example without json schema response
 | 
			
		||||
                400: {"description": "Bad data is supplied", "schema": ErrorSchema},  # exception raised by this method
 | 
			
		||||
                401: {"description": "Authorization required", "schema": ErrorSchema},  # should be always presented
 | 
			
		||||
                403: {"description": "Access is forbidden", "schema": ErrorSchema},  # should be always presented
 | 
			
		||||
                404: {"description": "Repository is unknown", "schema": ErrorSchema},  # include if BaseView.service() method is called
 | 
			
		||||
                500: {"description": "Internal server error", "schema": ErrorSchema},  # should be always presented
 | 
			
		||||
            },
 | 
			
		||||
            security=[{"token": [POST_PERMISSION]}],
 | 
			
		||||
            error_400_enabled=True,  # exception raised by this method
 | 
			
		||||
            error_404_description="Repository is unknown",
 | 
			
		||||
            schema=ResponseSchema,  # leave empty if no responses here
 | 
			
		||||
            match_schema=PackageNameSchema,
 | 
			
		||||
            query_schema=PaginationSchema,
 | 
			
		||||
            body_schema=RequestSchema(many=True),
 | 
			
		||||
        )
 | 
			
		||||
        @aiohttp_apispec.cookies_schema(AuthSchema)  # should be always presented
 | 
			
		||||
        @aiohttp_apispec.match_info_schema(PackageNameSchema)
 | 
			
		||||
        @aiohttp_apispec.querystring_schema(PaginationSchema)
 | 
			
		||||
        @aiohttp_apispec.json_schema(RequestSchema(many=True))
 | 
			
		||||
        async def post(self) -> None: ...
 | 
			
		||||
    ```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										29
									
								
								docs/ahriman.web.apispec.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								docs/ahriman.web.apispec.rst
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
			
		||||
ahriman.web.apispec package
 | 
			
		||||
===========================
 | 
			
		||||
 | 
			
		||||
Submodules
 | 
			
		||||
----------
 | 
			
		||||
 | 
			
		||||
ahriman.web.apispec.decorators module
 | 
			
		||||
-------------------------------------
 | 
			
		||||
 | 
			
		||||
.. automodule:: ahriman.web.apispec.decorators
 | 
			
		||||
   :members:
 | 
			
		||||
   :no-undoc-members:
 | 
			
		||||
   :show-inheritance:
 | 
			
		||||
 | 
			
		||||
ahriman.web.apispec.info module
 | 
			
		||||
-------------------------------
 | 
			
		||||
 | 
			
		||||
.. automodule:: ahriman.web.apispec.info
 | 
			
		||||
   :members:
 | 
			
		||||
   :no-undoc-members:
 | 
			
		||||
   :show-inheritance:
 | 
			
		||||
 | 
			
		||||
Module contents
 | 
			
		||||
---------------
 | 
			
		||||
 | 
			
		||||
.. automodule:: ahriman.web.apispec
 | 
			
		||||
   :members:
 | 
			
		||||
   :no-undoc-members:
 | 
			
		||||
   :show-inheritance:
 | 
			
		||||
@ -7,6 +7,7 @@ Subpackages
 | 
			
		||||
.. toctree::
 | 
			
		||||
   :maxdepth: 4
 | 
			
		||||
 | 
			
		||||
   ahriman.web.apispec
 | 
			
		||||
   ahriman.web.middlewares
 | 
			
		||||
   ahriman.web.schemas
 | 
			
		||||
   ahriman.web.views
 | 
			
		||||
@ -14,14 +15,6 @@ Subpackages
 | 
			
		||||
Submodules
 | 
			
		||||
----------
 | 
			
		||||
 | 
			
		||||
ahriman.web.apispec module
 | 
			
		||||
--------------------------
 | 
			
		||||
 | 
			
		||||
.. automodule:: ahriman.web.apispec
 | 
			
		||||
   :members:
 | 
			
		||||
   :no-undoc-members:
 | 
			
		||||
   :show-inheritance:
 | 
			
		||||
 | 
			
		||||
ahriman.web.cors module
 | 
			
		||||
-----------------------
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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')
 | 
			
		||||
 | 
			
		||||
@ -119,7 +119,9 @@
 | 
			
		||||
                    <li><a id="badge-version" class="nav-link" href="https://github.com/arcan1s/ahriman" title="sources"><i class="bi bi-github"></i> ahriman</a></li>
 | 
			
		||||
                    <li><a class="nav-link" href="https://github.com/arcan1s/ahriman/releases" title="releases list">releases</a></li>
 | 
			
		||||
                    <li><a class="nav-link" href="https://github.com/arcan1s/ahriman/issues" title="issues tracker">report a bug</a></li>
 | 
			
		||||
                    {% if docs_enabled %}
 | 
			
		||||
                        <li><a class="nav-link" href="/api-docs" title="API documentation">api</a></li>
 | 
			
		||||
                    {% endif %}
 | 
			
		||||
                </ul>
 | 
			
		||||
 | 
			
		||||
                {% if index_url is not none %}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										32
									
								
								src/ahriman/web/apispec/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								src/ahriman/web/apispec/__init__.py
									
									
									
									
									
										Normal 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 = Mock  # type: ignore[misc]
 | 
			
		||||
    aiohttp_apispec = None
 | 
			
		||||
    fields = Mock()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__all__ = ["Schema", "aiohttp_apispec", "fields"]
 | 
			
		||||
							
								
								
									
										148
									
								
								src/ahriman/web/apispec/decorators.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										148
									
								
								src/ahriman/web/apispec/decorators.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,148 @@
 | 
			
		||||
#
 | 
			
		||||
# 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 aiohttp.web import HTTPException
 | 
			
		||||
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, response_code: type[HTTPException] | 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
 | 
			
		||||
        response_code(type[HTTPException] | 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},
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    match response_code:
 | 
			
		||||
        case None if response is None:
 | 
			
		||||
            code = 204
 | 
			
		||||
        case None:
 | 
			
		||||
            code = 200
 | 
			
		||||
        case exception:
 | 
			
		||||
            code = exception.status_code
 | 
			
		||||
    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,
 | 
			
		||||
            response_code: type[HTTPException] | 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
 | 
			
		||||
        permission(UserAccess): permission to access endpoint
 | 
			
		||||
        response_code(type[HTTPException] | 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)
 | 
			
		||||
        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
 | 
			
		||||
 | 
			
		||||
        responses = _response_schema(
 | 
			
		||||
            response=schema,
 | 
			
		||||
            response_code=response_code,
 | 
			
		||||
            error_400_enabled=error_400_enabled,
 | 
			
		||||
            error_403_enabled=authorization_required,
 | 
			
		||||
            error_404_description=error_404_description,
 | 
			
		||||
        )
 | 
			
		||||
        handler = aiohttp_apispec.docs(
 | 
			
		||||
            tags=tags,
 | 
			
		||||
            summary=summary,
 | 
			
		||||
            description=description,
 | 
			
		||||
            responses=responses,
 | 
			
		||||
            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
 | 
			
		||||
@ -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",
 | 
			
		||||
@ -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):
 | 
			
		||||
 | 
			
		||||
@ -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):
 | 
			
		||||
 | 
			
		||||
@ -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):
 | 
			
		||||
 | 
			
		||||
@ -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):
 | 
			
		||||
 | 
			
		||||
@ -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):
 | 
			
		||||
 | 
			
		||||
@ -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):
 | 
			
		||||
 | 
			
		||||
@ -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):
 | 
			
		||||
 | 
			
		||||
@ -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):
 | 
			
		||||
 | 
			
		||||
@ -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.models.event import EventType
 | 
			
		||||
from ahriman.web.apispec import fields
 | 
			
		||||
from ahriman.web.schemas.pagination_schema import PaginationSchema
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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):
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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):
 | 
			
		||||
 | 
			
		||||
@ -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):
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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):
 | 
			
		||||
 | 
			
		||||
@ -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):
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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):
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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):
 | 
			
		||||
 | 
			
		||||
@ -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):
 | 
			
		||||
 | 
			
		||||
@ -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):
 | 
			
		||||
 | 
			
		||||
@ -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):
 | 
			
		||||
 | 
			
		||||
@ -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):
 | 
			
		||||
 | 
			
		||||
@ -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):
 | 
			
		||||
 | 
			
		||||
@ -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):
 | 
			
		||||
 | 
			
		||||
@ -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):
 | 
			
		||||
 | 
			
		||||
@ -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):
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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):
 | 
			
		||||
 | 
			
		||||
@ -21,7 +21,9 @@ import aiohttp_jinja2
 | 
			
		||||
 | 
			
		||||
from typing import Any
 | 
			
		||||
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.models.user_access import UserAccess
 | 
			
		||||
from ahriman.web.apispec import aiohttp_apispec
 | 
			
		||||
from ahriman.web.views.base import BaseView
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -36,6 +38,22 @@ class DocsView(BaseView):
 | 
			
		||||
    GET_PERMISSION = UserAccess.Unauthorized
 | 
			
		||||
    ROUTES = ["/api-docs"]
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def routes(cls, configuration: Configuration) -> list[str]:
 | 
			
		||||
        """
 | 
			
		||||
        extract routes list for the view
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            configuration(Configuration): configuration instance
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            list[str]: list of routes defined for the view. By default, it tries to read :attr:`ROUTES` option if set
 | 
			
		||||
            and returns empty list otherwise
 | 
			
		||||
        """
 | 
			
		||||
        if aiohttp_apispec is None:
 | 
			
		||||
            return []
 | 
			
		||||
        return cls.ROUTES
 | 
			
		||||
 | 
			
		||||
    @aiohttp_jinja2.template("api.jinja2")
 | 
			
		||||
    async def get(self) -> dict[str, Any]:
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
@ -20,8 +20,10 @@
 | 
			
		||||
from aiohttp.web import Response, json_response
 | 
			
		||||
from collections.abc import Callable
 | 
			
		||||
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.core.utils import partition
 | 
			
		||||
from ahriman.models.user_access import UserAccess
 | 
			
		||||
from ahriman.web.apispec import aiohttp_apispec
 | 
			
		||||
from ahriman.web.views.base import BaseView
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -36,6 +38,22 @@ class SwaggerView(BaseView):
 | 
			
		||||
    GET_PERMISSION = UserAccess.Unauthorized
 | 
			
		||||
    ROUTES = ["/api-docs/swagger.json"]
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def routes(cls, configuration: Configuration) -> list[str]:
 | 
			
		||||
        """
 | 
			
		||||
        extract routes list for the view
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            configuration(Configuration): configuration instance
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            list[str]: list of routes defined for the view. By default, it tries to read :attr:`ROUTES` option if set
 | 
			
		||||
            and returns empty list otherwise
 | 
			
		||||
        """
 | 
			
		||||
        if aiohttp_apispec is None:
 | 
			
		||||
            return []
 | 
			
		||||
        return cls.ROUTES
 | 
			
		||||
 | 
			
		||||
    async def get(self) -> Response:
 | 
			
		||||
        """
 | 
			
		||||
        get api specification
 | 
			
		||||
 | 
			
		||||
@ -23,6 +23,7 @@ from typing import Any
 | 
			
		||||
 | 
			
		||||
from ahriman.core.auth.helpers import authorized_userid
 | 
			
		||||
from ahriman.models.user_access import UserAccess
 | 
			
		||||
from ahriman.web.apispec import aiohttp_apispec
 | 
			
		||||
from ahriman.web.views.base import BaseView
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -36,6 +37,7 @@ class IndexView(BaseView):
 | 
			
		||||
            * control - HTML to insert for login control, HTML string, required
 | 
			
		||||
            * enabled - whether authorization is enabled by configuration or not, boolean, required
 | 
			
		||||
            * username - authenticated username if any, string, null means not authenticated
 | 
			
		||||
        * docs_enabled - indicates if api docs is enabled, boolean, required
 | 
			
		||||
        * index_url - url to the repository index, string, optional
 | 
			
		||||
        * repositories - list of repositories unique identifiers, required
 | 
			
		||||
            * id - unique repository identifier, string, required
 | 
			
		||||
@ -66,6 +68,7 @@ class IndexView(BaseView):
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            "auth": auth,
 | 
			
		||||
            "docs_enabled": aiohttp_apispec is not None,
 | 
			
		||||
            "index_url": self.configuration.get("web", "index_url", fallback=None),
 | 
			
		||||
            "repositories": [
 | 
			
		||||
                {
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -17,17 +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[import-untyped]
 | 
			
		||||
import shutil
 | 
			
		||||
 | 
			
		||||
from aiohttp import BodyPartReader
 | 
			
		||||
from aiohttp.web import HTTPBadRequest, HTTPCreated, HTTPNotFound
 | 
			
		||||
from aiohttp.web import HTTPBadRequest, HTTPCreated
 | 
			
		||||
from pathlib import Path
 | 
			
		||||
from tempfile import NamedTemporaryFile
 | 
			
		||||
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -42,6 +43,22 @@ class UploadView(BaseView):
 | 
			
		||||
    POST_PERMISSION = UserAccess.Full
 | 
			
		||||
    ROUTES = ["/api/v1/service/upload"]
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def routes(cls, configuration: Configuration) -> list[str]:
 | 
			
		||||
        """
 | 
			
		||||
        extract routes list for the view
 | 
			
		||||
 | 
			
		||||
        Args:
 | 
			
		||||
            configuration(Configuration): configuration instance
 | 
			
		||||
 | 
			
		||||
        Returns:
 | 
			
		||||
            list[str]: list of routes defined for the view. By default, it tries to read :attr:`ROUTES` option if set
 | 
			
		||||
            and returns empty list otherwise
 | 
			
		||||
        """
 | 
			
		||||
        if not configuration.getboolean("web", "enable_archive_upload", fallback=False):
 | 
			
		||||
            return []
 | 
			
		||||
        return cls.ROUTES
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    async def save_file(part: BodyPartReader, target: Path, *, max_body_size: int | None = None) -> tuple[str, Path]:
 | 
			
		||||
        """
 | 
			
		||||
@ -92,23 +109,18 @@ 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,
 | 
			
		||||
        response_code=HTTPCreated,
 | 
			
		||||
        error_400_enabled=True,
 | 
			
		||||
        error_404_description="Repository is unknown",
 | 
			
		||||
        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
 | 
			
		||||
@ -118,9 +130,6 @@ class UploadView(BaseView):
 | 
			
		||||
            HTTPCreated: on success response
 | 
			
		||||
            HTTPNotFound: method is disabled by configuration
 | 
			
		||||
        """
 | 
			
		||||
        if not self.configuration.getboolean("web", "enable_archive_upload", fallback=False):
 | 
			
		||||
            raise HTTPNotFound
 | 
			
		||||
 | 
			
		||||
        try:
 | 
			
		||||
            reader = await self.request.multipart()
 | 
			
		||||
        except Exception as ex:
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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,
 | 
			
		||||
        response_code=HTTPFound,
 | 
			
		||||
        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,
 | 
			
		||||
        response_code=HTTPFound,
 | 
			
		||||
        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.
 | 
			
		||||
 | 
			
		||||
@ -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,
 | 
			
		||||
        response_code=HTTPFound,
 | 
			
		||||
    )
 | 
			
		||||
    @aiohttp_apispec.cookies_schema(AuthSchema)
 | 
			
		||||
    async def post(self) -> None:
 | 
			
		||||
        """
 | 
			
		||||
        logout user from the service
 | 
			
		||||
 | 
			
		||||
@ -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
 | 
			
		||||
 | 
			
		||||
@ -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.info 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
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										24
									
								
								tests/ahriman/web/apispec/test_apispec.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								tests/ahriman/web/apispec/test_apispec.py
									
									
									
									
									
										Normal 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
 | 
			
		||||
    assert apispec.fields("arg", kwargs=42)
 | 
			
		||||
							
								
								
									
										161
									
								
								tests/ahriman/web/apispec/test_decorators.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										161
									
								
								tests/ahriman/web/apispec/test_decorators.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,161 @@
 | 
			
		||||
from aiohttp.web import HTTPFound
 | 
			
		||||
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, response_code=HTTPFound)
 | 
			
		||||
    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
 | 
			
		||||
@ -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 info
 | 
			
		||||
from ahriman.web.apispec.info 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(info, "aiohttp_apispec", None)
 | 
			
		||||
    assert setup_apispec(application) is None
 | 
			
		||||
@ -1,7 +1,9 @@
 | 
			
		||||
import pytest
 | 
			
		||||
 | 
			
		||||
from aiohttp.test_utils import TestClient
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.models.user_access import UserAccess
 | 
			
		||||
from ahriman.web.views.api.docs import DocsView
 | 
			
		||||
 | 
			
		||||
@ -15,6 +17,28 @@ async def test_get_permission() -> None:
 | 
			
		||||
        assert await DocsView.get_permission(request) == UserAccess.Unauthorized
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_routes() -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must return correct routes
 | 
			
		||||
    """
 | 
			
		||||
    assert DocsView.ROUTES == ["/api-docs"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_routes_dynamic(configuration: Configuration) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must correctly return docs route
 | 
			
		||||
    """
 | 
			
		||||
    assert DocsView.ROUTES == DocsView.routes(configuration)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_routes_dynamic_not_found(configuration: Configuration, mocker: MockerFixture) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must disable docs route if no apispec package found
 | 
			
		||||
    """
 | 
			
		||||
    mocker.patch("ahriman.web.views.api.docs.aiohttp_apispec", None)
 | 
			
		||||
    assert DocsView.routes(configuration) == []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def test_get(client: TestClient) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must generate api-docs correctly
 | 
			
		||||
 | 
			
		||||
@ -4,6 +4,7 @@ from aiohttp.test_utils import TestClient
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
from typing import Any
 | 
			
		||||
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.models.user_access import UserAccess
 | 
			
		||||
from ahriman.web.views.api.swagger import SwaggerView
 | 
			
		||||
 | 
			
		||||
@ -86,6 +87,28 @@ async def test_get_permission() -> None:
 | 
			
		||||
        assert await SwaggerView.get_permission(request) == UserAccess.Unauthorized
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_routes() -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must return correct routes
 | 
			
		||||
    """
 | 
			
		||||
    assert SwaggerView.ROUTES == ["/api-docs/swagger.json"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_routes_dynamic(configuration: Configuration) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must correctly return openapi url
 | 
			
		||||
    """
 | 
			
		||||
    assert SwaggerView.ROUTES == SwaggerView.routes(configuration)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_routes_dynamic_not_found(configuration: Configuration, mocker: MockerFixture) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must disable openapi route if no apispec package found
 | 
			
		||||
    """
 | 
			
		||||
    mocker.patch("ahriman.web.views.api.swagger.aiohttp_apispec", None)
 | 
			
		||||
    assert SwaggerView.routes(configuration) == []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def test_get(client: TestClient, mocker: MockerFixture) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must generate api-docs correctly
 | 
			
		||||
 | 
			
		||||
@ -8,6 +8,7 @@ from pathlib import Path
 | 
			
		||||
from pytest_mock import MockerFixture
 | 
			
		||||
from unittest.mock import AsyncMock, MagicMock, call as MockCall
 | 
			
		||||
 | 
			
		||||
from ahriman.core.configuration import Configuration
 | 
			
		||||
from ahriman.models.repository_paths import RepositoryPaths
 | 
			
		||||
from ahriman.models.user_access import UserAccess
 | 
			
		||||
from ahriman.web.views.v1.service.upload import UploadView
 | 
			
		||||
@ -29,6 +30,21 @@ def test_routes() -> None:
 | 
			
		||||
    assert UploadView.ROUTES == ["/api/v1/service/upload"]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_routes_dynamic(configuration: Configuration) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must correctly return upload url
 | 
			
		||||
    """
 | 
			
		||||
    assert UploadView.ROUTES == UploadView.routes(configuration)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def test_routes_dynamic_not_found(configuration: Configuration) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must disable upload route if option is not set
 | 
			
		||||
    """
 | 
			
		||||
    configuration.set_option("web", "enable_archive_upload", "no")
 | 
			
		||||
    assert UploadView.routes(configuration) == []
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def test_save_file(mocker: MockerFixture) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must correctly save file
 | 
			
		||||
@ -134,20 +150,6 @@ async def test_post_with_sig(client: TestClient, repository_paths: RepositoryPat
 | 
			
		||||
    ])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def test_post_not_found(client: TestClient, mocker: MockerFixture) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must return 404 if request was disabled
 | 
			
		||||
    """
 | 
			
		||||
    mocker.patch("ahriman.core.configuration.Configuration.getboolean", return_value=False)
 | 
			
		||||
    data = FormData()
 | 
			
		||||
    data.add_field("package", BytesIO(b"content"), filename="filename", content_type="application/octet-stream")
 | 
			
		||||
    response_schema = pytest.helpers.schema_response(UploadView.post, code=404)
 | 
			
		||||
 | 
			
		||||
    response = await client.post("/api/v1/service/upload", data=data)
 | 
			
		||||
    assert response.status == 404
 | 
			
		||||
    assert not response_schema.validate(await response.json())
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
async def test_post_not_multipart(client: TestClient) -> None:
 | 
			
		||||
    """
 | 
			
		||||
    must return 400 on invalid payload
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user