mirror of
https://github.com/arcan1s/ahriman.git
synced 2026-10-14 17:02:17 +00:00
feat: pagination support for logs request
This commit is contained in:
1 parent
853185c659
commit
c915d68c97
66 files changed
+650
-238
No files matched your search
+5
-3
@@ -174,9 +174,7 @@ Again, the most checks can be performed by `make check` command, though some add
|
|||||||
|
|
||||||
from marshmallow import Schema, fields
|
from marshmallow import Schema, fields
|
||||||
|
|
||||||
from ahriman.web.schemas.auth_schema import AuthSchema
|
from ahriman.web.schemas import AuthSchema, ErrorSchema, PackageNameSchema, PaginationSchema
|
||||||
from ahriman.web.schemas.error_schema import ErrorSchema
|
|
||||||
from ahriman.web.schemas.package_name_schema import PackageNameSchema
|
|
||||||
from ahriman.web.views.base import BaseView
|
from ahriman.web.views.base import BaseView
|
||||||
|
|
||||||
|
|
||||||
@@ -210,10 +208,14 @@ Again, the most checks can be performed by `make check` command, though some add
|
|||||||
)
|
)
|
||||||
@aiohttp_apispec.cookies_schema(AuthSchema) # should be always presented
|
@aiohttp_apispec.cookies_schema(AuthSchema) # should be always presented
|
||||||
@aiohttp_apispec.match_info_schema(PackageNameSchema)
|
@aiohttp_apispec.match_info_schema(PackageNameSchema)
|
||||||
|
@aiohttp_apispec.querystring_schema(PaginationSchema)
|
||||||
@aiohttp_apispec.json_schema(RequestSchema(many=True))
|
@aiohttp_apispec.json_schema(RequestSchema(many=True))
|
||||||
async def post(self) -> None: ...
|
async def post(self) -> None: ...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
* It is allowed to change web API to add new fields or remove optional ones. However, in case of model changes, new API version must be introduced.
|
||||||
|
* On the other hand, it is allowed to change method signatures, however, it is recommended to add new parameters as optional if possible. Deprecated API can be dropped during major release.
|
||||||
|
|
||||||
### Other checks
|
### Other checks
|
||||||
|
|
||||||
The projects also uses typing checks (provided by `mypy`) and some linter checks provided by `pylint` and `bandit`. Those checks must be passed successfully for any open pull requests.
|
The projects also uses typing checks (provided by `mypy`) and some linter checks provided by `pylint` and `bandit`. Those checks must be passed successfully for any open pull requests.
|
||||||
@@ -124,6 +124,14 @@ ahriman.web.schemas.package\_status\_schema module
|
|||||||
:no-undoc-members:
|
:no-undoc-members:
|
||||||
:show-inheritance:
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.web.schemas.pagination\_schema module
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.schemas.pagination_schema
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
ahriman.web.schemas.pgp\_key\_id\_schema module
|
ahriman.web.schemas.pgp\_key\_id\_schema module
|
||||||
-----------------------------------------------
|
-----------------------------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,8 @@ Subpackages
|
|||||||
:maxdepth: 4
|
:maxdepth: 4
|
||||||
|
|
||||||
ahriman.web.views.api
|
ahriman.web.views.api
|
||||||
ahriman.web.views.service
|
ahriman.web.views.v1
|
||||||
ahriman.web.views.status
|
ahriman.web.views.v2
|
||||||
ahriman.web.views.user
|
|
||||||
|
|
||||||
Submodules
|
Submodules
|
||||||
----------
|
----------
|
||||||
|
|||||||
@@ -1,85 +0,0 @@
|
|||||||
ahriman.web.views.service package
|
|
||||||
=================================
|
|
||||||
|
|
||||||
Submodules
|
|
||||||
----------
|
|
||||||
|
|
||||||
ahriman.web.views.service.add module
|
|
||||||
------------------------------------
|
|
||||||
|
|
||||||
.. automodule:: ahriman.web.views.service.add
|
|
||||||
:members:
|
|
||||||
:no-undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
ahriman.web.views.service.pgp module
|
|
||||||
------------------------------------
|
|
||||||
|
|
||||||
.. automodule:: ahriman.web.views.service.pgp
|
|
||||||
:members:
|
|
||||||
:no-undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
ahriman.web.views.service.process module
|
|
||||||
----------------------------------------
|
|
||||||
|
|
||||||
.. automodule:: ahriman.web.views.service.process
|
|
||||||
:members:
|
|
||||||
:no-undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
ahriman.web.views.service.rebuild module
|
|
||||||
----------------------------------------
|
|
||||||
|
|
||||||
.. automodule:: ahriman.web.views.service.rebuild
|
|
||||||
:members:
|
|
||||||
:no-undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
ahriman.web.views.service.remove module
|
|
||||||
---------------------------------------
|
|
||||||
|
|
||||||
.. automodule:: ahriman.web.views.service.remove
|
|
||||||
:members:
|
|
||||||
:no-undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
ahriman.web.views.service.request module
|
|
||||||
----------------------------------------
|
|
||||||
|
|
||||||
.. automodule:: ahriman.web.views.service.request
|
|
||||||
:members:
|
|
||||||
:no-undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
ahriman.web.views.service.search module
|
|
||||||
---------------------------------------
|
|
||||||
|
|
||||||
.. automodule:: ahriman.web.views.service.search
|
|
||||||
:members:
|
|
||||||
:no-undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
ahriman.web.views.service.update module
|
|
||||||
---------------------------------------
|
|
||||||
|
|
||||||
.. automodule:: ahriman.web.views.service.update
|
|
||||||
:members:
|
|
||||||
:no-undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
ahriman.web.views.service.upload module
|
|
||||||
---------------------------------------
|
|
||||||
|
|
||||||
.. automodule:: ahriman.web.views.service.upload
|
|
||||||
:members:
|
|
||||||
:no-undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
Module contents
|
|
||||||
---------------
|
|
||||||
|
|
||||||
.. automodule:: ahriman.web.views.service
|
|
||||||
:members:
|
|
||||||
:no-undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
ahriman.web.views.status package
|
|
||||||
================================
|
|
||||||
|
|
||||||
Submodules
|
|
||||||
----------
|
|
||||||
|
|
||||||
ahriman.web.views.status.logs module
|
|
||||||
------------------------------------
|
|
||||||
|
|
||||||
.. automodule:: ahriman.web.views.status.logs
|
|
||||||
:members:
|
|
||||||
:no-undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
ahriman.web.views.status.package module
|
|
||||||
---------------------------------------
|
|
||||||
|
|
||||||
.. automodule:: ahriman.web.views.status.package
|
|
||||||
:members:
|
|
||||||
:no-undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
ahriman.web.views.status.packages module
|
|
||||||
----------------------------------------
|
|
||||||
|
|
||||||
.. automodule:: ahriman.web.views.status.packages
|
|
||||||
:members:
|
|
||||||
:no-undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
ahriman.web.views.status.status module
|
|
||||||
--------------------------------------
|
|
||||||
|
|
||||||
.. automodule:: ahriman.web.views.status.status
|
|
||||||
:members:
|
|
||||||
:no-undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
Module contents
|
|
||||||
---------------
|
|
||||||
|
|
||||||
.. automodule:: ahriman.web.views.status
|
|
||||||
:members:
|
|
||||||
:no-undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
ahriman.web.views.user package
|
|
||||||
==============================
|
|
||||||
|
|
||||||
Submodules
|
|
||||||
----------
|
|
||||||
|
|
||||||
ahriman.web.views.user.login module
|
|
||||||
-----------------------------------
|
|
||||||
|
|
||||||
.. automodule:: ahriman.web.views.user.login
|
|
||||||
:members:
|
|
||||||
:no-undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
ahriman.web.views.user.logout module
|
|
||||||
------------------------------------
|
|
||||||
|
|
||||||
.. automodule:: ahriman.web.views.user.logout
|
|
||||||
:members:
|
|
||||||
:no-undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
|
|
||||||
Module contents
|
|
||||||
---------------
|
|
||||||
|
|
||||||
.. automodule:: ahriman.web.views.user
|
|
||||||
:members:
|
|
||||||
:no-undoc-members:
|
|
||||||
:show-inheritance:
|
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
ahriman.web.views.v1 package
|
||||||
|
============================
|
||||||
|
|
||||||
|
Subpackages
|
||||||
|
-----------
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 4
|
||||||
|
|
||||||
|
ahriman.web.views.v1.service
|
||||||
|
ahriman.web.views.v1.status
|
||||||
|
ahriman.web.views.v1.user
|
||||||
|
|
||||||
|
Module contents
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.views.v1
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
ahriman.web.views.v1.service package
|
||||||
|
====================================
|
||||||
|
|
||||||
|
Submodules
|
||||||
|
----------
|
||||||
|
|
||||||
|
ahriman.web.views.v1.service.add module
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.views.v1.service.add
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.web.views.v1.service.pgp module
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.views.v1.service.pgp
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.web.views.v1.service.process module
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.views.v1.service.process
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.web.views.v1.service.rebuild module
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.views.v1.service.rebuild
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.web.views.v1.service.remove module
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.views.v1.service.remove
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.web.views.v1.service.request module
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.views.v1.service.request
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.web.views.v1.service.search module
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.views.v1.service.search
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.web.views.v1.service.update module
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.views.v1.service.update
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.web.views.v1.service.upload module
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.views.v1.service.upload
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
Module contents
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.views.v1.service
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
ahriman.web.views.v1.status package
|
||||||
|
===================================
|
||||||
|
|
||||||
|
Submodules
|
||||||
|
----------
|
||||||
|
|
||||||
|
ahriman.web.views.v1.status.logs module
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.views.v1.status.logs
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.web.views.v1.status.package module
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.views.v1.status.package
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.web.views.v1.status.packages module
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.views.v1.status.packages
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.web.views.v1.status.status module
|
||||||
|
-----------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.views.v1.status.status
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
Module contents
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.views.v1.status
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
ahriman.web.views.v1.user package
|
||||||
|
=================================
|
||||||
|
|
||||||
|
Submodules
|
||||||
|
----------
|
||||||
|
|
||||||
|
ahriman.web.views.v1.user.login module
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.views.v1.user.login
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
ahriman.web.views.v1.user.logout module
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.views.v1.user.logout
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
Module contents
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.views.v1.user
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
ahriman.web.views.v2 package
|
||||||
|
============================
|
||||||
|
|
||||||
|
Subpackages
|
||||||
|
-----------
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 4
|
||||||
|
|
||||||
|
ahriman.web.views.v2.status
|
||||||
|
|
||||||
|
Module contents
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.views.v2
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
ahriman.web.views.v2.status package
|
||||||
|
===================================
|
||||||
|
|
||||||
|
Submodules
|
||||||
|
----------
|
||||||
|
|
||||||
|
ahriman.web.views.v2.status.logs module
|
||||||
|
---------------------------------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.views.v2.status.logs
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
||||||
|
Module contents
|
||||||
|
---------------
|
||||||
|
|
||||||
|
.. automodule:: ahriman.web.views.v2.status
|
||||||
|
:members:
|
||||||
|
:no-undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
@@ -45,12 +45,16 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: `/api/v1/packages/${packageBase}/logs`,
|
url: `/api/v2/packages/${packageBase}/logs`,
|
||||||
type: "GET",
|
type: "GET",
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
success: response => {
|
success: response => {
|
||||||
packageInfo.text(`${response.package_base} ${response.status.status} at ${new Date(1000 * response.status.timestamp).toISOString()}`);
|
packageInfo.text(`${response.package_base} ${response.status.status} at ${new Date(1000 * response.status.timestamp).toISOString()}`);
|
||||||
packageInfoLogsInput.text(response.logs);
|
const logs = response.logs.map(log_record => {
|
||||||
|
const [timestamp, record] = log_record;
|
||||||
|
return `[${new Date(1000 * timestamp).toISOString()}] ${record}`;
|
||||||
|
});
|
||||||
|
packageInfoLogsInput.text(logs.join("\n"));
|
||||||
|
|
||||||
packageInfoModalHeader.removeClass();
|
packageInfoModalHeader.removeClass();
|
||||||
packageInfoModalHeader.addClass("modal-header");
|
packageInfoModalHeader.addClass("modal-header");
|
||||||
|
|||||||
@@ -73,9 +73,9 @@ SigLevel = Database{% if has_repo_signed %}Required{% else %}Never{% endif %} Pa
|
|||||||
<td>{{ package.architecture }}</td>
|
<td>{{ package.architecture }}</td>
|
||||||
<td>{{ package.description }}</td>
|
<td>{{ package.description }}</td>
|
||||||
<td><a href="{{ package.url }}" title="{{ package.name }} upstream url">{{ package.url }}</a></td>
|
<td><a href="{{ package.url }}" title="{{ package.name }} upstream url">{{ package.url }}</a></td>
|
||||||
<td>{{ package.licenses|join("<br>"|safe) }}</td>
|
<td>{{ package.licenses | join("<br>" | safe) }}</td>
|
||||||
<td>{{ package.groups|join("<br>"|safe) }}</td>
|
<td>{{ package.groups | join("<br>" | safe) }}</td>
|
||||||
<td>{{ package.depends|join("<br>"|safe) }}</td>
|
<td>{{ package.depends | join("<br>" | safe) }}</td>
|
||||||
<td>{{ package.archive_size }}</td>
|
<td>{{ package.archive_size }}</td>
|
||||||
<td>{{ package.installed_size }}</td>
|
<td>{{ package.installed_size }}</td>
|
||||||
<td>{{ package.build_date }}</td>
|
<td>{{ package.build_date }}</td>
|
||||||
|
|||||||
@@ -20,7 +20,6 @@
|
|||||||
from sqlite3 import Connection
|
from sqlite3 import Connection
|
||||||
|
|
||||||
from ahriman.core.database.operations import Operations
|
from ahriman.core.database.operations import Operations
|
||||||
from ahriman.core.util import pretty_datetime
|
|
||||||
from ahriman.models.log_record_id import LogRecordId
|
from ahriman.models.log_record_id import LogRecordId
|
||||||
|
|
||||||
|
|
||||||
@@ -29,29 +28,34 @@ class LogsOperations(Operations):
|
|||||||
logs operations
|
logs operations
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def logs_get(self, package_base: str) -> str:
|
def logs_get(self, package_base: str, limit: int = -1, offset: int = 0) -> list[tuple[float, str]]:
|
||||||
"""
|
"""
|
||||||
extract logs for specified package base
|
extract logs for specified package base
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
package_base(str): package base to extract logs
|
package_base(str): package base to extract logs
|
||||||
|
limit(int, optional): limit records to the specified count, -1 means unlimited (Default value = -1)
|
||||||
|
offset(int, optional): records offset (Default value = 0)
|
||||||
|
|
||||||
Return:
|
Return:
|
||||||
str: full package log
|
list[tuple[float, str]]: sorted package log records and their timestamps
|
||||||
"""
|
"""
|
||||||
def run(connection: Connection) -> list[str]:
|
def run(connection: Connection) -> list[tuple[float, str]]:
|
||||||
return [
|
return [
|
||||||
f"""[{pretty_datetime(row["created"])}] {row["record"]}"""
|
(row["created"], row["record"])
|
||||||
for row in connection.execute(
|
for row in connection.execute(
|
||||||
"""
|
"""
|
||||||
select created, record from logs where package_base = :package_base
|
select created, record from logs where package_base = :package_base
|
||||||
order by created
|
order by created limit :limit offset :offset
|
||||||
""",
|
""",
|
||||||
{"package_base": package_base})
|
{
|
||||||
|
"package_base": package_base,
|
||||||
|
"limit": limit,
|
||||||
|
"offset": offset,
|
||||||
|
})
|
||||||
]
|
]
|
||||||
|
|
||||||
records = self.with_connection(run)
|
return self.with_connection(run)
|
||||||
return "\n".join(records)
|
|
||||||
|
|
||||||
def logs_insert(self, log_record_id: LogRecordId, created: float, record: str) -> None:
|
def logs_insert(self, log_record_id: LogRecordId, created: float, record: str) -> None:
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -85,17 +85,19 @@ class Watcher(LazyLogging):
|
|||||||
if package.base in self.known:
|
if package.base in self.known:
|
||||||
self.known[package.base] = (package, status)
|
self.known[package.base] = (package, status)
|
||||||
|
|
||||||
def logs_get(self, package_base: str) -> str:
|
def logs_get(self, package_base: str, limit: int = -1, offset: int = 0) -> list[tuple[float, str]]:
|
||||||
"""
|
"""
|
||||||
extract logs for the package base
|
extract logs for the package base
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
package_base(str): package base
|
package_base(str): package base
|
||||||
|
limit(int, optional): limit records to the specified count, -1 means unlimited (Default value = -1)
|
||||||
|
offset(int, optional): records offset (Default value = 0)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
str: package logs
|
list[tuple[float, str]]: package logs
|
||||||
"""
|
"""
|
||||||
return self.database.logs_get(package_base)
|
return self.database.logs_get(package_base, limit, offset)
|
||||||
|
|
||||||
def logs_remove(self, package_base: str, version: str | None) -> None:
|
def logs_remove(self, package_base: str, version: str | None) -> None:
|
||||||
"""
|
"""
|
||||||
|
|||||||
+17
-30
@@ -23,21 +23,7 @@ from pathlib import Path
|
|||||||
from ahriman.web.views.api.docs import DocsView
|
from ahriman.web.views.api.docs import DocsView
|
||||||
from ahriman.web.views.api.swagger import SwaggerView
|
from ahriman.web.views.api.swagger import SwaggerView
|
||||||
from ahriman.web.views.index import IndexView
|
from ahriman.web.views.index import IndexView
|
||||||
from ahriman.web.views.service.add import AddView
|
from ahriman.web.views import v1, v2
|
||||||
from ahriman.web.views.service.pgp import PGPView
|
|
||||||
from ahriman.web.views.service.process import ProcessView
|
|
||||||
from ahriman.web.views.service.rebuild import RebuildView
|
|
||||||
from ahriman.web.views.service.remove import RemoveView
|
|
||||||
from ahriman.web.views.service.request import RequestView
|
|
||||||
from ahriman.web.views.service.search import SearchView
|
|
||||||
from ahriman.web.views.service.update import UpdateView
|
|
||||||
from ahriman.web.views.service.upload import UploadView
|
|
||||||
from ahriman.web.views.status.logs import LogsView
|
|
||||||
from ahriman.web.views.status.package import PackageView
|
|
||||||
from ahriman.web.views.status.packages import PackagesView
|
|
||||||
from ahriman.web.views.status.status import StatusView
|
|
||||||
from ahriman.web.views.user.login import LoginView
|
|
||||||
from ahriman.web.views.user.logout import LogoutView
|
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["setup_routes"]
|
__all__ = ["setup_routes"]
|
||||||
@@ -59,21 +45,22 @@ def setup_routes(application: Application, static_path: Path) -> None:
|
|||||||
|
|
||||||
application.router.add_static("/static", static_path, follow_symlinks=True)
|
application.router.add_static("/static", static_path, follow_symlinks=True)
|
||||||
|
|
||||||
application.router.add_view("/api/v1/service/add", AddView)
|
application.router.add_view("/api/v1/service/add", v1.AddView)
|
||||||
application.router.add_view("/api/v1/service/pgp", PGPView)
|
application.router.add_view("/api/v1/service/pgp", v1.PGPView)
|
||||||
application.router.add_view("/api/v1/service/rebuild", RebuildView)
|
application.router.add_view("/api/v1/service/rebuild", v1.RebuildView)
|
||||||
application.router.add_view("/api/v1/service/process/{process_id}", ProcessView)
|
application.router.add_view("/api/v1/service/process/{process_id}", v1.ProcessView)
|
||||||
application.router.add_view("/api/v1/service/remove", RemoveView)
|
application.router.add_view("/api/v1/service/remove", v1.RemoveView)
|
||||||
application.router.add_view("/api/v1/service/request", RequestView)
|
application.router.add_view("/api/v1/service/request", v1.RequestView)
|
||||||
application.router.add_view("/api/v1/service/search", SearchView)
|
application.router.add_view("/api/v1/service/search", v1.SearchView)
|
||||||
application.router.add_view("/api/v1/service/update", UpdateView)
|
application.router.add_view("/api/v1/service/update", v1.UpdateView)
|
||||||
application.router.add_view("/api/v1/service/upload", UploadView)
|
application.router.add_view("/api/v1/service/upload", v1.UploadView)
|
||||||
|
|
||||||
application.router.add_view("/api/v1/packages", PackagesView)
|
application.router.add_view("/api/v1/packages", v1.PackagesView)
|
||||||
application.router.add_view("/api/v1/packages/{package}", PackageView)
|
application.router.add_view("/api/v1/packages/{package}", v1.PackageView)
|
||||||
application.router.add_view("/api/v1/packages/{package}/logs", LogsView)
|
application.router.add_view("/api/v1/packages/{package}/logs", v1.LogsView)
|
||||||
|
application.router.add_view("/api/v2/packages/{package}/logs", v2.LogsView)
|
||||||
|
|
||||||
application.router.add_view("/api/v1/status", StatusView)
|
application.router.add_view("/api/v1/status", v1.StatusView)
|
||||||
|
|
||||||
application.router.add_view("/api/v1/login", LoginView)
|
application.router.add_view("/api/v1/login", v1.LoginView)
|
||||||
application.router.add_view("/api/v1/logout", LogoutView)
|
application.router.add_view("/api/v1/logout", v1.LogoutView)
|
||||||
@@ -25,13 +25,14 @@ from ahriman.web.schemas.file_schema import FileSchema
|
|||||||
from ahriman.web.schemas.internal_status_schema import InternalStatusSchema
|
from ahriman.web.schemas.internal_status_schema import InternalStatusSchema
|
||||||
from ahriman.web.schemas.log_schema import LogSchema
|
from ahriman.web.schemas.log_schema import LogSchema
|
||||||
from ahriman.web.schemas.login_schema import LoginSchema
|
from ahriman.web.schemas.login_schema import LoginSchema
|
||||||
from ahriman.web.schemas.logs_schema import LogsSchema
|
from ahriman.web.schemas.logs_schema import LogsSchema, LogsSchemaV2
|
||||||
from ahriman.web.schemas.oauth2_schema import OAuth2Schema
|
from ahriman.web.schemas.oauth2_schema import OAuth2Schema
|
||||||
from ahriman.web.schemas.package_name_schema import PackageNameSchema
|
from ahriman.web.schemas.package_name_schema import PackageNameSchema
|
||||||
from ahriman.web.schemas.package_names_schema import PackageNamesSchema
|
from ahriman.web.schemas.package_names_schema import PackageNamesSchema
|
||||||
from ahriman.web.schemas.package_properties_schema import PackagePropertiesSchema
|
from ahriman.web.schemas.package_properties_schema import PackagePropertiesSchema
|
||||||
from ahriman.web.schemas.package_schema import PackageSchema
|
from ahriman.web.schemas.package_schema import PackageSchema
|
||||||
from ahriman.web.schemas.package_status_schema import PackageStatusSimplifiedSchema, PackageStatusSchema
|
from ahriman.web.schemas.package_status_schema import PackageStatusSimplifiedSchema, PackageStatusSchema
|
||||||
|
from ahriman.web.schemas.pagination_schema import PaginationSchema
|
||||||
from ahriman.web.schemas.pgp_key_id_schema import PGPKeyIdSchema
|
from ahriman.web.schemas.pgp_key_id_schema import PGPKeyIdSchema
|
||||||
from ahriman.web.schemas.pgp_key_schema import PGPKeySchema
|
from ahriman.web.schemas.pgp_key_schema import PGPKeySchema
|
||||||
from ahriman.web.schemas.process_id_schema import ProcessIdSchema
|
from ahriman.web.schemas.process_id_schema import ProcessIdSchema
|
||||||
|
|||||||
@@ -37,3 +37,21 @@ class LogsSchema(Schema):
|
|||||||
logs = fields.String(required=True, metadata={
|
logs = fields.String(required=True, metadata={
|
||||||
"description": "Full package log from the last build",
|
"description": "Full package log from the last build",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
class LogsSchemaV2(Schema):
|
||||||
|
"""
|
||||||
|
response package logs api v2 schema
|
||||||
|
"""
|
||||||
|
|
||||||
|
package_base = fields.String(required=True, metadata={
|
||||||
|
"description": "Package base name",
|
||||||
|
"example": "ahriman",
|
||||||
|
})
|
||||||
|
status = fields.Nested(StatusSchema(), required=True, metadata={
|
||||||
|
"description": "Last package status",
|
||||||
|
})
|
||||||
|
logs = fields.List(fields.Tuple([fields.Float(), fields.String()]), required=True, metadata={ # type: ignore[no-untyped-call]
|
||||||
|
"description": "Package log records timestamp and message",
|
||||||
|
"example": [(1680537091.233495, "log record")]
|
||||||
|
})
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2021-2023 ahriman team.
|
||||||
|
#
|
||||||
|
# This file is part of ahriman
|
||||||
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
from marshmallow import Schema, fields
|
||||||
|
|
||||||
|
|
||||||
|
class PaginationSchema(Schema):
|
||||||
|
"""
|
||||||
|
request pagination schema
|
||||||
|
"""
|
||||||
|
|
||||||
|
limit = fields.Integer(metadata={
|
||||||
|
"description": "Limit records by specified amount",
|
||||||
|
"example": 42,
|
||||||
|
})
|
||||||
|
offset = fields.Integer(metadata={
|
||||||
|
"description": "Start records with the offset",
|
||||||
|
"example": 100,
|
||||||
|
})
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2021-2023 ahriman team.
|
||||||
|
#
|
||||||
|
# This file is part of ahriman
|
||||||
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
from ahriman.web.views.v1.service.add import AddView
|
||||||
|
from ahriman.web.views.v1.service.pgp import PGPView
|
||||||
|
from ahriman.web.views.v1.service.process import ProcessView
|
||||||
|
from ahriman.web.views.v1.service.rebuild import RebuildView
|
||||||
|
from ahriman.web.views.v1.service.remove import RemoveView
|
||||||
|
from ahriman.web.views.v1.service.request import RequestView
|
||||||
|
from ahriman.web.views.v1.service.search import SearchView
|
||||||
|
from ahriman.web.views.v1.service.update import UpdateView
|
||||||
|
from ahriman.web.views.v1.service.upload import UploadView
|
||||||
|
|
||||||
|
from ahriman.web.views.v1.status.logs import LogsView
|
||||||
|
from ahriman.web.views.v1.status.package import PackageView
|
||||||
|
from ahriman.web.views.v1.status.packages import PackagesView
|
||||||
|
from ahriman.web.views.v1.status.status import StatusView
|
||||||
|
|
||||||
|
from ahriman.web.views.v1.user.login import LoginView
|
||||||
|
from ahriman.web.views.v1.user.logout import LogoutView
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -22,6 +22,7 @@ import aiohttp_apispec # type: ignore[import]
|
|||||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, HTTPNotFound, Response, json_response
|
from aiohttp.web import HTTPBadRequest, HTTPNoContent, HTTPNotFound, Response, json_response
|
||||||
|
|
||||||
from ahriman.core.exceptions import UnknownPackageError
|
from ahriman.core.exceptions import UnknownPackageError
|
||||||
|
from ahriman.core.util import pretty_datetime
|
||||||
from ahriman.models.log_record_id import LogRecordId
|
from ahriman.models.log_record_id import LogRecordId
|
||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
from ahriman.web.schemas import AuthSchema, ErrorSchema, LogSchema, LogsSchema, PackageNameSchema
|
from ahriman.web.schemas import AuthSchema, ErrorSchema, LogSchema, LogsSchema, PackageNameSchema
|
||||||
@@ -103,7 +104,7 @@ class LogsView(BaseView):
|
|||||||
response = {
|
response = {
|
||||||
"package_base": package_base,
|
"package_base": package_base,
|
||||||
"status": status.view(),
|
"status": status.view(),
|
||||||
"logs": logs
|
"logs": "\n".join(f"[{pretty_datetime(created)}] {message}" for created, message in logs)
|
||||||
}
|
}
|
||||||
return json_response(response)
|
return json_response(response)
|
||||||
|
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,20 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2021-2023 ahriman team.
|
||||||
|
#
|
||||||
|
# This file is part of ahriman
|
||||||
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
from ahriman.web.views.v2.status.logs import LogsView
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2021-2023 ahriman team.
|
||||||
|
#
|
||||||
|
# This file is part of ahriman
|
||||||
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
#
|
||||||
|
# Copyright (c) 2021-2023 ahriman team.
|
||||||
|
#
|
||||||
|
# This file is part of ahriman
|
||||||
|
# (see https://github.com/arcan1s/ahriman).
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
import aiohttp_apispec # type: ignore[import]
|
||||||
|
|
||||||
|
from aiohttp.web import HTTPBadRequest, HTTPNotFound, Response, json_response
|
||||||
|
|
||||||
|
from ahriman.core.exceptions import UnknownPackageError
|
||||||
|
from ahriman.models.user_access import UserAccess
|
||||||
|
from ahriman.web.schemas import AuthSchema, ErrorSchema, PackageNameSchema, PaginationSchema
|
||||||
|
from ahriman.web.schemas.logs_schema import LogsSchemaV2
|
||||||
|
from ahriman.web.views.base import BaseView
|
||||||
|
|
||||||
|
|
||||||
|
class LogsView(BaseView):
|
||||||
|
"""
|
||||||
|
package logs web view
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
GET_PERMISSION(UserAccess): (class attribute) get permissions of self
|
||||||
|
"""
|
||||||
|
|
||||||
|
GET_PERMISSION = UserAccess.Reporter
|
||||||
|
|
||||||
|
@aiohttp_apispec.docs(
|
||||||
|
tags=["Packages"],
|
||||||
|
summary="Get paginated package logs",
|
||||||
|
description="Retrieve package logs and the last package status",
|
||||||
|
responses={
|
||||||
|
200: {"description": "Success response", "schema": LogsSchemaV2},
|
||||||
|
400: {"description": "Bad data is supplied", "schema": ErrorSchema},
|
||||||
|
401: {"description": "Authorization required", "schema": ErrorSchema},
|
||||||
|
403: {"description": "Access is forbidden", "schema": ErrorSchema},
|
||||||
|
404: {"description": "Package base is unknown", "schema": ErrorSchema},
|
||||||
|
500: {"description": "Internal server error", "schema": ErrorSchema},
|
||||||
|
},
|
||||||
|
security=[{"token": [GET_PERMISSION]}],
|
||||||
|
)
|
||||||
|
@aiohttp_apispec.cookies_schema(AuthSchema)
|
||||||
|
@aiohttp_apispec.match_info_schema(PackageNameSchema)
|
||||||
|
@aiohttp_apispec.querystring_schema(PaginationSchema)
|
||||||
|
async def get(self) -> Response:
|
||||||
|
"""
|
||||||
|
get last package logs
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Response: 200 with package logs on success
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
HTTPBadRequest: if supplied parameters are invalid
|
||||||
|
HTTPNotFound: if package base is unknown
|
||||||
|
"""
|
||||||
|
package_base = self.request.match_info["package"]
|
||||||
|
try:
|
||||||
|
limit = int(self.request.query.getone("limit", default=-1))
|
||||||
|
offset = int(self.request.query.getone("offset", default=0))
|
||||||
|
except Exception as ex:
|
||||||
|
raise HTTPBadRequest(reason=str(ex))
|
||||||
|
|
||||||
|
try:
|
||||||
|
_, status = self.service.package_get(package_base)
|
||||||
|
except UnknownPackageError:
|
||||||
|
raise HTTPNotFound
|
||||||
|
logs = self.service.logs_get(package_base, limit, offset)
|
||||||
|
|
||||||
|
response = {
|
||||||
|
"package_base": package_base,
|
||||||
|
"status": status.view(),
|
||||||
|
"logs": logs,
|
||||||
|
}
|
||||||
|
return json_response(response)
|
||||||
@@ -13,8 +13,8 @@ def test_logs_insert_remove_process(database: SQLite, package_ahriman: Package,
|
|||||||
database.logs_insert(LogRecordId(package_python_schedule.base, "1"), 42.0, "message 3")
|
database.logs_insert(LogRecordId(package_python_schedule.base, "1"), 42.0, "message 3")
|
||||||
|
|
||||||
database.logs_remove(package_ahriman.base, "1")
|
database.logs_remove(package_ahriman.base, "1")
|
||||||
assert database.logs_get(package_ahriman.base) == "[1970-01-01 00:00:42] message 1"
|
assert database.logs_get(package_ahriman.base) == [(42.0, "message 1")]
|
||||||
assert database.logs_get(package_python_schedule.base) == "[1970-01-01 00:00:42] message 3"
|
assert database.logs_get(package_python_schedule.base) == [(42.0, "message 3")]
|
||||||
|
|
||||||
|
|
||||||
def test_logs_insert_remove_full(database: SQLite, package_ahriman: Package, package_python_schedule: Package) -> None:
|
def test_logs_insert_remove_full(database: SQLite, package_ahriman: Package, package_python_schedule: Package) -> None:
|
||||||
@@ -27,7 +27,7 @@ def test_logs_insert_remove_full(database: SQLite, package_ahriman: Package, pac
|
|||||||
|
|
||||||
database.logs_remove(package_ahriman.base, None)
|
database.logs_remove(package_ahriman.base, None)
|
||||||
assert not database.logs_get(package_ahriman.base)
|
assert not database.logs_get(package_ahriman.base)
|
||||||
assert database.logs_get(package_python_schedule.base) == "[1970-01-01 00:00:42] message 3"
|
assert database.logs_get(package_python_schedule.base) == [(42.0, "message 3")]
|
||||||
|
|
||||||
|
|
||||||
def test_logs_insert_get(database: SQLite, package_ahriman: Package) -> None:
|
def test_logs_insert_get(database: SQLite, package_ahriman: Package) -> None:
|
||||||
@@ -36,4 +36,13 @@ def test_logs_insert_get(database: SQLite, package_ahriman: Package) -> None:
|
|||||||
"""
|
"""
|
||||||
database.logs_insert(LogRecordId(package_ahriman.base, "1"), 43.0, "message 2")
|
database.logs_insert(LogRecordId(package_ahriman.base, "1"), 43.0, "message 2")
|
||||||
database.logs_insert(LogRecordId(package_ahriman.base, "1"), 42.0, "message 1")
|
database.logs_insert(LogRecordId(package_ahriman.base, "1"), 42.0, "message 1")
|
||||||
assert database.logs_get(package_ahriman.base) == "[1970-01-01 00:00:42] message 1\n[1970-01-01 00:00:43] message 2"
|
assert database.logs_get(package_ahriman.base) == [(42.0, "message 1"), (43.0, "message 2")]
|
||||||
|
|
||||||
|
|
||||||
|
def test_logs_insert_get_pagination(database: SQLite, package_ahriman: Package) -> None:
|
||||||
|
"""
|
||||||
|
must insert and get package logs with pagination
|
||||||
|
"""
|
||||||
|
database.logs_insert(LogRecordId(package_ahriman.base, "1"), 42.0, "message 1")
|
||||||
|
database.logs_insert(LogRecordId(package_ahriman.base, "1"), 43.0, "message 2")
|
||||||
|
assert database.logs_get(package_ahriman.base, 1, 1) == [(43.0, "message 2")]
|
||||||
@@ -55,8 +55,8 @@ def test_logs_get(watcher: Watcher, package_ahriman: Package, mocker: MockerFixt
|
|||||||
must return package logs
|
must return package logs
|
||||||
"""
|
"""
|
||||||
logs_mock = mocker.patch("ahriman.core.database.SQLite.logs_get")
|
logs_mock = mocker.patch("ahriman.core.database.SQLite.logs_get")
|
||||||
watcher.logs_get(package_ahriman.base)
|
watcher.logs_get(package_ahriman.base, 1, 2)
|
||||||
logs_mock.assert_called_once_with(package_ahriman.base)
|
logs_mock.assert_called_once_with(package_ahriman.base, 1, 2)
|
||||||
|
|
||||||
|
|
||||||
def test_logs_remove(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
|
def test_logs_remove(watcher: Watcher, package_ahriman: Package, mocker: MockerFixture) -> None:
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from ahriman.core.util import walk
|
||||||
|
|
||||||
|
|
||||||
|
def test_test_coverage() -> None:
|
||||||
|
"""
|
||||||
|
must have test files for each source file
|
||||||
|
"""
|
||||||
|
root = Path()
|
||||||
|
for source_file in filter(lambda fn: fn.suffix == ".py" and fn.name != "__init__.py", walk(root / "src")):
|
||||||
|
# some workaround for well known files
|
||||||
|
if source_file.parts[2:4] == ("application", "handlers") and source_file.name != "handler.py":
|
||||||
|
filename = f"test_handler_{source_file.name}"
|
||||||
|
elif source_file.parts[2:4] == ("web", "views"):
|
||||||
|
if (api := source_file.parts[4]) == "api":
|
||||||
|
filename = f"test_view_{api}_{source_file.name}"
|
||||||
|
elif (version := source_file.parts[4]) in ("v1", "v2"):
|
||||||
|
api = source_file.parts[5]
|
||||||
|
filename = f"test_view_{version}_{api}_{source_file.name}"
|
||||||
|
else:
|
||||||
|
filename = f"test_view_{source_file.name}"
|
||||||
|
else:
|
||||||
|
filename = f"test_{source_file.name}"
|
||||||
|
|
||||||
|
test_file = Path("tests", *source_file.parts[1:-1], filename)
|
||||||
|
assert test_file.is_file(), test_file
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
# schema testing goes in view class tests
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+1
-1
@@ -5,7 +5,7 @@ from pytest_mock import MockerFixture
|
|||||||
from unittest.mock import AsyncMock
|
from unittest.mock import AsyncMock
|
||||||
|
|
||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
from ahriman.web.views.service.add import AddView
|
from ahriman.web.views.v1 import AddView
|
||||||
|
|
||||||
|
|
||||||
async def test_get_permission() -> None:
|
async def test_get_permission() -> None:
|
||||||
+1
-1
@@ -4,7 +4,7 @@ from aiohttp.test_utils import TestClient
|
|||||||
from pytest_mock import MockerFixture
|
from pytest_mock import MockerFixture
|
||||||
|
|
||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
from ahriman.web.views.service.pgp import PGPView
|
from ahriman.web.views.v1 import PGPView
|
||||||
|
|
||||||
|
|
||||||
async def test_get_permission() -> None:
|
async def test_get_permission() -> None:
|
||||||
+1
-1
@@ -4,7 +4,7 @@ from aiohttp.test_utils import TestClient
|
|||||||
from pytest_mock import MockerFixture
|
from pytest_mock import MockerFixture
|
||||||
|
|
||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
from ahriman.web.views.service.process import ProcessView
|
from ahriman.web.views.v1 import ProcessView
|
||||||
|
|
||||||
|
|
||||||
async def test_get_permission() -> None:
|
async def test_get_permission() -> None:
|
||||||
+1
-1
@@ -5,7 +5,7 @@ from pytest_mock import MockerFixture
|
|||||||
from unittest.mock import AsyncMock
|
from unittest.mock import AsyncMock
|
||||||
|
|
||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
from ahriman.web.views.service.rebuild import RebuildView
|
from ahriman.web.views.v1 import RebuildView
|
||||||
|
|
||||||
|
|
||||||
async def test_get_permission() -> None:
|
async def test_get_permission() -> None:
|
||||||
+1
-1
@@ -4,7 +4,7 @@ from aiohttp.test_utils import TestClient
|
|||||||
from pytest_mock import MockerFixture
|
from pytest_mock import MockerFixture
|
||||||
|
|
||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
from ahriman.web.views.service.remove import RemoveView
|
from ahriman.web.views.v1 import RemoveView
|
||||||
|
|
||||||
|
|
||||||
async def test_get_permission() -> None:
|
async def test_get_permission() -> None:
|
||||||
+1
-1
@@ -5,7 +5,7 @@ from pytest_mock import MockerFixture
|
|||||||
from unittest.mock import AsyncMock
|
from unittest.mock import AsyncMock
|
||||||
|
|
||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
from ahriman.web.views.service.request import RequestView
|
from ahriman.web.views.v1 import RequestView
|
||||||
|
|
||||||
|
|
||||||
async def test_get_permission() -> None:
|
async def test_get_permission() -> None:
|
||||||
+1
-1
@@ -5,7 +5,7 @@ from pytest_mock import MockerFixture
|
|||||||
|
|
||||||
from ahriman.models.aur_package import AURPackage
|
from ahriman.models.aur_package import AURPackage
|
||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
from ahriman.web.views.service.search import SearchView
|
from ahriman.web.views.v1 import SearchView
|
||||||
|
|
||||||
|
|
||||||
async def test_get_permission() -> None:
|
async def test_get_permission() -> None:
|
||||||
+1
-1
@@ -5,7 +5,7 @@ from pytest_mock import MockerFixture
|
|||||||
from unittest.mock import AsyncMock
|
from unittest.mock import AsyncMock
|
||||||
|
|
||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
from ahriman.web.views.service.update import UpdateView
|
from ahriman.web.views.v1 import UpdateView
|
||||||
|
|
||||||
|
|
||||||
async def test_get_permission() -> None:
|
async def test_get_permission() -> None:
|
||||||
+4
-4
@@ -10,7 +10,7 @@ from unittest.mock import AsyncMock, MagicMock, call as MockCall
|
|||||||
|
|
||||||
from ahriman.models.repository_paths import RepositoryPaths
|
from ahriman.models.repository_paths import RepositoryPaths
|
||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
from ahriman.web.views.service.upload import UploadView
|
from ahriman.web.views.v1 import UploadView
|
||||||
|
|
||||||
|
|
||||||
async def test_get_permission() -> None:
|
async def test_get_permission() -> None:
|
||||||
@@ -30,7 +30,7 @@ async def test_save_file(mocker: MockerFixture) -> None:
|
|||||||
part_mock.filename = "filename"
|
part_mock.filename = "filename"
|
||||||
part_mock.read_chunk = AsyncMock(side_effect=[b"content", None])
|
part_mock.read_chunk = AsyncMock(side_effect=[b"content", None])
|
||||||
|
|
||||||
tempfile_mock = mocker.patch("ahriman.web.views.service.upload.NamedTemporaryFile")
|
tempfile_mock = mocker.patch("ahriman.web.views.v1.service.upload.NamedTemporaryFile")
|
||||||
file_mock = MagicMock()
|
file_mock = MagicMock()
|
||||||
tempfile_mock.return_value.__enter__.return_value = file_mock
|
tempfile_mock.return_value.__enter__.return_value = file_mock
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@ async def test_post(client: TestClient, repository_paths: RepositoryPaths, mocke
|
|||||||
must process file upload via http
|
must process file upload via http
|
||||||
"""
|
"""
|
||||||
local = Path("local")
|
local = Path("local")
|
||||||
save_mock = mocker.patch("ahriman.web.views.service.upload.UploadView.save_file",
|
save_mock = mocker.patch("ahriman.web.views.v1.UploadView.save_file",
|
||||||
side_effect=AsyncMock(return_value=("filename", local / ".filename")))
|
side_effect=AsyncMock(return_value=("filename", local / ".filename")))
|
||||||
rename_mock = mocker.patch("pathlib.Path.rename")
|
rename_mock = mocker.patch("pathlib.Path.rename")
|
||||||
# no content validation here because it has invalid schema
|
# no content validation here because it has invalid schema
|
||||||
@@ -103,7 +103,7 @@ async def test_post_with_sig(client: TestClient, repository_paths: RepositoryPat
|
|||||||
must process file upload with signature via http
|
must process file upload with signature via http
|
||||||
"""
|
"""
|
||||||
local = Path("local")
|
local = Path("local")
|
||||||
save_mock = mocker.patch("ahriman.web.views.service.upload.UploadView.save_file",
|
save_mock = mocker.patch("ahriman.web.views.v1.UploadView.save_file",
|
||||||
side_effect=AsyncMock(side_effect=[
|
side_effect=AsyncMock(side_effect=[
|
||||||
("filename", local / ".filename"),
|
("filename", local / ".filename"),
|
||||||
("filename.sig", local / ".filename.sig"),
|
("filename.sig", local / ".filename.sig"),
|
||||||
+1
-1
@@ -5,7 +5,7 @@ from aiohttp.test_utils import TestClient
|
|||||||
from ahriman.models.build_status import BuildStatusEnum
|
from ahriman.models.build_status import BuildStatusEnum
|
||||||
from ahriman.models.package import Package
|
from ahriman.models.package import Package
|
||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
from ahriman.web.views.status.logs import LogsView
|
from ahriman.web.views.v1 import LogsView
|
||||||
|
|
||||||
|
|
||||||
async def test_get_permission() -> None:
|
async def test_get_permission() -> None:
|
||||||
+1
-1
@@ -5,7 +5,7 @@ from aiohttp.test_utils import TestClient
|
|||||||
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
|
from ahriman.models.build_status import BuildStatus, BuildStatusEnum
|
||||||
from ahriman.models.package import Package
|
from ahriman.models.package import Package
|
||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
from ahriman.web.views.status.package import PackageView
|
from ahriman.web.views.v1 import PackageView
|
||||||
|
|
||||||
|
|
||||||
async def test_get_permission() -> None:
|
async def test_get_permission() -> None:
|
||||||
+1
-1
@@ -6,7 +6,7 @@ from pytest_mock import MockerFixture
|
|||||||
from ahriman.models.build_status import BuildStatusEnum
|
from ahriman.models.build_status import BuildStatusEnum
|
||||||
from ahriman.models.package import Package
|
from ahriman.models.package import Package
|
||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
from ahriman.web.views.status.packages import PackagesView
|
from ahriman.web.views.v1 import PackagesView
|
||||||
|
|
||||||
|
|
||||||
async def test_get_permission() -> None:
|
async def test_get_permission() -> None:
|
||||||
+1
-1
@@ -8,7 +8,7 @@ from ahriman.models.build_status import BuildStatusEnum
|
|||||||
from ahriman.models.internal_status import InternalStatus
|
from ahriman.models.internal_status import InternalStatus
|
||||||
from ahriman.models.package import Package
|
from ahriman.models.package import Package
|
||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
from ahriman.web.views.status.status import StatusView
|
from ahriman.web.views.v1 import StatusView
|
||||||
|
|
||||||
|
|
||||||
async def test_get_permission() -> None:
|
async def test_get_permission() -> None:
|
||||||
+1
-1
@@ -5,7 +5,7 @@ from pytest_mock import MockerFixture
|
|||||||
|
|
||||||
from ahriman.models.user import User
|
from ahriman.models.user import User
|
||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
from ahriman.web.views.user.login import LoginView
|
from ahriman.web.views.v1 import LoginView
|
||||||
|
|
||||||
|
|
||||||
async def test_get_permission() -> None:
|
async def test_get_permission() -> None:
|
||||||
+1
-1
@@ -5,7 +5,7 @@ from aiohttp.web import HTTPUnauthorized
|
|||||||
from pytest_mock import MockerFixture
|
from pytest_mock import MockerFixture
|
||||||
|
|
||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
from ahriman.web.views.user.logout import LogoutView
|
from ahriman.web.views.v1 import LogoutView
|
||||||
|
|
||||||
|
|
||||||
async def test_get_permission() -> None:
|
async def test_get_permission() -> None:
|
||||||
@@ -0,0 +1,93 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from aiohttp.test_utils import TestClient
|
||||||
|
|
||||||
|
from ahriman.models.build_status import BuildStatusEnum
|
||||||
|
from ahriman.models.package import Package
|
||||||
|
from ahriman.models.user_access import UserAccess
|
||||||
|
from ahriman.web.views.v2 import LogsView
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_permission() -> None:
|
||||||
|
"""
|
||||||
|
must return correct permission for the request
|
||||||
|
"""
|
||||||
|
for method in ("GET",):
|
||||||
|
request = pytest.helpers.request("", "", method)
|
||||||
|
assert await LogsView.get_permission(request) == UserAccess.Reporter
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get(client: TestClient, package_ahriman: Package) -> None:
|
||||||
|
"""
|
||||||
|
must get logs for package
|
||||||
|
"""
|
||||||
|
await client.post(f"/api/v1/packages/{package_ahriman.base}",
|
||||||
|
json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()})
|
||||||
|
await client.post(f"/api/v1/packages/{package_ahriman.base}/logs",
|
||||||
|
json={"created": 42.0, "message": "message 1", "version": "42"})
|
||||||
|
await client.post(f"/api/v1/packages/{package_ahriman.base}/logs",
|
||||||
|
json={"created": 43.0, "message": "message 2", "version": "42"})
|
||||||
|
request_schema = pytest.helpers.schema_request(LogsView.get, location="querystring")
|
||||||
|
response_schema = pytest.helpers.schema_response(LogsView.get)
|
||||||
|
|
||||||
|
payload = {}
|
||||||
|
assert not request_schema.validate(payload)
|
||||||
|
response = await client.get(f"/api/v2/packages/{package_ahriman.base}/logs", params=payload)
|
||||||
|
assert response.status == 200
|
||||||
|
|
||||||
|
logs = await response.json()
|
||||||
|
assert not response_schema.validate(logs)
|
||||||
|
assert logs["logs"] == [[42.0, "message 1"], [43.0, "message 2"]]
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_with_pagination(client: TestClient, package_ahriman: Package) -> None:
|
||||||
|
"""
|
||||||
|
must get logs with pagination
|
||||||
|
"""
|
||||||
|
await client.post(f"/api/v1/packages/{package_ahriman.base}",
|
||||||
|
json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()})
|
||||||
|
await client.post(f"/api/v1/packages/{package_ahriman.base}/logs",
|
||||||
|
json={"created": 42.0, "message": "message 1", "version": "42"})
|
||||||
|
await client.post(f"/api/v1/packages/{package_ahriman.base}/logs",
|
||||||
|
json={"created": 43.0, "message": "message 2", "version": "42"})
|
||||||
|
request_schema = pytest.helpers.schema_request(LogsView.get, location="querystring")
|
||||||
|
response_schema = pytest.helpers.schema_response(LogsView.get)
|
||||||
|
|
||||||
|
payload = {"limit": 1, "offset": 1}
|
||||||
|
assert not request_schema.validate(payload)
|
||||||
|
response = await client.get(f"/api/v2/packages/{package_ahriman.base}/logs", params=payload)
|
||||||
|
assert response.status == 200
|
||||||
|
|
||||||
|
logs = await response.json()
|
||||||
|
assert not response_schema.validate(logs)
|
||||||
|
assert logs["logs"] == [[43.0, "message 2"]]
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_not_found(client: TestClient, package_ahriman: Package) -> None:
|
||||||
|
"""
|
||||||
|
must return not found for missing package
|
||||||
|
"""
|
||||||
|
response_schema = pytest.helpers.schema_response(LogsView.get, code=404)
|
||||||
|
|
||||||
|
response = await client.get(f"/api/v2/packages/{package_ahriman.base}/logs")
|
||||||
|
assert response.status == 404
|
||||||
|
assert not response_schema.validate(await response.json())
|
||||||
|
|
||||||
|
|
||||||
|
async def test_get_bad_request(client: TestClient, package_ahriman: Package) -> None:
|
||||||
|
"""
|
||||||
|
must return bad request for invalid query parameters
|
||||||
|
"""
|
||||||
|
await client.post(f"/api/v1/packages/{package_ahriman.base}",
|
||||||
|
json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()})
|
||||||
|
await client.post(f"/api/v1/packages/{package_ahriman.base}/logs",
|
||||||
|
json={"created": 42.0, "message": "message", "version": "42"})
|
||||||
|
response_schema = pytest.helpers.schema_response(LogsView.get, code=400)
|
||||||
|
|
||||||
|
response = await client.get(f"/api/v2/packages/{package_ahriman.base}/logs", params={"limit": "limit"})
|
||||||
|
assert response.status == 400
|
||||||
|
assert not response_schema.validate(await response.json())
|
||||||
|
|
||||||
|
response = await client.get(f"/api/v2/packages/{package_ahriman.base}/logs", params={"offset": "offset"})
|
||||||
|
assert response.status == 400
|
||||||
|
assert not response_schema.validate(await response.json())
|
||||||
Reference in new issue
Block a user