add support of officiall repositories api

This commit is contained in:
2022-04-07 04:19:37 +03:00
parent 4990ce4198
commit 9ce1c36f35
22 changed files with 689 additions and 183 deletions

View File

@ -3,6 +3,7 @@ import dataclasses
import pytest
from pytest_mock import MockerFixture
from unittest import mock
from ahriman.application.handlers import Search
from ahriman.core.configuration import Configuration
@ -29,14 +30,17 @@ def test_run(args: argparse.Namespace, configuration: Configuration, aur_package
must run command
"""
args = _default_args(args)
search_mock = mocker.patch("ahriman.core.alpm.aur.AUR.multisearch", return_value=[aur_package_ahriman])
aur_search_mock = mocker.patch("ahriman.core.alpm.remote.aur.AUR.multisearch", return_value=[aur_package_ahriman])
official_search_mock = mocker.patch("ahriman.core.alpm.remote.official.Official.multisearch",
return_value=[aur_package_ahriman])
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_if_empty")
print_mock = mocker.patch("ahriman.core.formatters.printer.Printer.print")
Search.run(args, "x86_64", configuration, True, False)
search_mock.assert_called_once_with("ahriman")
aur_search_mock.assert_called_once_with("ahriman")
official_search_mock.assert_called_once_with("ahriman")
check_mock.assert_called_once_with(False, False)
print_mock.assert_called_once_with(False)
print_mock.assert_has_calls([mock.call(False), mock.call(False)])
def test_run_empty_exception(args: argparse.Namespace, configuration: Configuration, mocker: MockerFixture) -> None:
@ -45,7 +49,8 @@ def test_run_empty_exception(args: argparse.Namespace, configuration: Configurat
"""
args = _default_args(args)
args.exit_code = True
mocker.patch("ahriman.core.alpm.aur.AUR.multisearch", return_value=[])
mocker.patch("ahriman.core.alpm.remote.aur.AUR.multisearch", return_value=[])
mocker.patch("ahriman.core.alpm.remote.official.Official.multisearch", return_value=[])
mocker.patch("ahriman.core.formatters.printer.Printer.print")
check_mock = mocker.patch("ahriman.application.handlers.handler.Handler.check_if_empty")
@ -59,11 +64,15 @@ def test_run_sort(args: argparse.Namespace, configuration: Configuration, aur_pa
must run command with sorting
"""
args = _default_args(args)
mocker.patch("ahriman.core.alpm.aur.AUR.multisearch", return_value=[aur_package_ahriman])
mocker.patch("ahriman.core.alpm.remote.aur.AUR.multisearch", return_value=[aur_package_ahriman])
mocker.patch("ahriman.core.alpm.remote.official.Official.multisearch", return_value=[])
sort_mock = mocker.patch("ahriman.application.handlers.search.Search.sort")
Search.run(args, "x86_64", configuration, True, False)
sort_mock.assert_called_once_with([aur_package_ahriman], "name")
sort_mock.assert_has_calls([
mock.call([], "name"), mock.call().__iter__(),
mock.call([aur_package_ahriman], "name"), mock.call().__iter__()
])
def test_run_sort_by(args: argparse.Namespace, configuration: Configuration, aur_package_ahriman: AURPackage,
@ -73,11 +82,15 @@ def test_run_sort_by(args: argparse.Namespace, configuration: Configuration, aur
"""
args = _default_args(args)
args.sort_by = "field"
mocker.patch("ahriman.core.alpm.aur.AUR.multisearch", return_value=[aur_package_ahriman])
mocker.patch("ahriman.core.alpm.remote.aur.AUR.multisearch", return_value=[aur_package_ahriman])
mocker.patch("ahriman.core.alpm.remote.official.Official.multisearch", return_value=[])
sort_mock = mocker.patch("ahriman.application.handlers.search.Search.sort")
Search.run(args, "x86_64", configuration, True, False)
sort_mock.assert_called_once_with([aur_package_ahriman], "field")
sort_mock.assert_has_calls([
mock.call([], "field"), mock.call().__iter__(),
mock.call([aur_package_ahriman], "field"), mock.call().__iter__()
])
def test_sort(aur_package_ahriman: AURPackage) -> None:

View File

@ -124,6 +124,50 @@ def aur_package_ahriman() -> AURPackage:
)
@pytest.fixture
def aur_package_akonadi() -> AURPackage:
"""
fixture for AUR package
:return: AUR package test instance
"""
return AURPackage(
id=0,
name="akonadi",
package_base_id=0,
package_base="akonadi",
version="21.12.3-2",
description="PIM layer, which provides an asynchronous API to access all kind of PIM data",
num_votes=0,
popularity=0,
first_submitted=datetime.datetime(1970, 1, 1, 0, 0, 0),
last_modified=datetime.datetime(2022, 3, 6, 8, 39, 50, 610000),
url_path="",
url="https://kontact.kde.org",
out_of_date=None,
maintainer="felixonmars",
depends=[
"libakonadi",
"mariadb",
],
make_depends=[
"boost",
"doxygen",
"extra-cmake-modules",
"kaccounts-integration",
"kitemmodels",
"postgresql",
"qt5-tools",
],
opt_depends=[
"postgresql: PostgreSQL backend",
],
conflicts=[],
provides=[],
license=["LGPL"],
keywords=[],
)
@pytest.fixture
def auth(configuration: Configuration) -> Auth:
"""

View File

@ -1,12 +0,0 @@
import pytest
from ahriman.core.alpm.aur import AUR
@pytest.fixture
def aur() -> AUR:
"""
aur helper fixture
:return: aur helper instance
"""
return AUR()

View File

@ -0,0 +1,32 @@
import pytest
from ahriman.core.alpm.remote.aur import AUR
from ahriman.core.alpm.remote.official import Official
from ahriman.core.alpm.remote.remote import Remote
@pytest.fixture
def aur() -> AUR:
"""
aur helper fixture
:return: aur helper instance
"""
return AUR()
@pytest.fixture
def official() -> Official:
"""
official repository fixture
:return: official repository helper instance
"""
return Official()
@pytest.fixture
def remote() -> Remote:
"""
official repository fixture
:return: official repository helper instance
"""
return Remote()

View File

@ -4,10 +4,9 @@ import requests
from pathlib import Path
from pytest_mock import MockerFixture
from unittest import mock
from unittest.mock import MagicMock
from ahriman.core.alpm.aur import AUR
from ahriman.core.alpm.remote.aur import AUR
from ahriman.core.exceptions import InvalidPackageInfo
from ahriman.models.aur_package import AURPackage
@ -21,55 +20,6 @@ def _get_response(resource_path_root: Path) -> str:
return (resource_path_root / "models" / "package_ahriman_aur").read_text()
def test_info(mocker: MockerFixture) -> None:
"""
must call info method
"""
info_mock = mocker.patch("ahriman.core.alpm.aur.AUR.package_info")
AUR.info("ahriman")
info_mock.assert_called_once_with("ahriman")
def test_multisearch(aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
"""
must search in AUR with multiple words
"""
terms = ["ahriman", "is", "cool"]
search_mock = mocker.patch("ahriman.core.alpm.aur.AUR.search", return_value=[aur_package_ahriman])
assert AUR.multisearch(*terms) == [aur_package_ahriman]
search_mock.assert_has_calls([mock.call("ahriman"), mock.call("cool")])
def test_multisearch_empty(mocker: MockerFixture) -> None:
"""
must return empty list if no long terms supplied
"""
terms = ["it", "is"]
search_mock = mocker.patch("ahriman.core.alpm.aur.AUR.search")
assert AUR.multisearch(*terms) == []
search_mock.assert_not_called()
def test_multisearch_single(aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
"""
must search in AUR with one word
"""
search_mock = mocker.patch("ahriman.core.alpm.aur.AUR.search", return_value=[aur_package_ahriman])
assert AUR.multisearch("ahriman") == [aur_package_ahriman]
search_mock.assert_called_once_with("ahriman")
def test_search(mocker: MockerFixture) -> None:
"""
must call search method
"""
search_mock = mocker.patch("ahriman.core.alpm.aur.AUR.package_search")
AUR.search("ahriman")
search_mock.assert_called_once_with("ahriman")
def test_parse_response(aur_package_ahriman: AURPackage, resource_path_root: Path) -> None:
"""
must parse success response
@ -87,7 +37,7 @@ def test_parse_response_error(resource_path_root: Path) -> None:
AUR.parse_response(json.loads(response))
def test_parse_response_unknown_error(resource_path_root: Path) -> None:
def test_parse_response_unknown_error() -> None:
"""
must raise exception on invalid response with empty error message
"""
@ -159,7 +109,7 @@ def test_package_info(aur: AUR, aur_package_ahriman: AURPackage, mocker: MockerF
"""
must make request for info
"""
request_mock = mocker.patch("ahriman.core.alpm.aur.AUR.make_request", return_value=[aur_package_ahriman])
request_mock = mocker.patch("ahriman.core.alpm.remote.aur.AUR.make_request", return_value=[aur_package_ahriman])
assert aur.package_info(aur_package_ahriman.name) == aur_package_ahriman
request_mock.assert_called_once_with("info", aur_package_ahriman.name)
@ -168,6 +118,6 @@ def test_package_search(aur: AUR, aur_package_ahriman: AURPackage, mocker: Mocke
"""
must make request for search
"""
request_mock = mocker.patch("ahriman.core.alpm.aur.AUR.make_request", return_value=[aur_package_ahriman])
assert aur.package_search(aur_package_ahriman.name, by="name") == [aur_package_ahriman]
request_mock.assert_called_once_with("search", aur_package_ahriman.name, by="name")
request_mock = mocker.patch("ahriman.core.alpm.remote.aur.AUR.make_request", return_value=[aur_package_ahriman])
assert aur.package_search(aur_package_ahriman.name) == [aur_package_ahriman]
request_mock.assert_called_once_with("search", aur_package_ahriman.name, by="name-desc")

View File

@ -0,0 +1,88 @@
import json
import pytest
import requests
from pathlib import Path
from pytest_mock import MockerFixture
from unittest.mock import MagicMock
from ahriman.core.alpm.remote.official import Official
from ahriman.core.exceptions import InvalidPackageInfo
from ahriman.models.aur_package import AURPackage
def _get_response(resource_path_root: Path) -> str:
"""
load response from resource file
:param resource_path_root: path to resource root
:return: response text
"""
return (resource_path_root / "models" / "package_akonadi_aur").read_text()
def test_parse_response(aur_package_akonadi: AURPackage, resource_path_root: Path) -> None:
"""
must parse success response
"""
response = _get_response(resource_path_root)
assert Official.parse_response(json.loads(response)) == [aur_package_akonadi]
def test_parse_response_unknown_error(resource_path_root: Path) -> None:
"""
must raise exception on invalid response with empty error message
"""
response = (resource_path_root / "models" / "official_error").read_text()
with pytest.raises(InvalidPackageInfo, match="API validation error"):
Official.parse_response(json.loads(response))
def test_make_request(official: Official, aur_package_akonadi: AURPackage,
mocker: MockerFixture, resource_path_root: Path) -> None:
"""
must perform request to official repositories
"""
response_mock = MagicMock()
response_mock.json.return_value = json.loads(_get_response(resource_path_root))
request_mock = mocker.patch("requests.get", return_value=response_mock)
assert official.make_request("akonadi", by="q") == [aur_package_akonadi]
request_mock.assert_called_once_with("https://archlinux.org/packages/search/json", params={"q": ("akonadi",)})
def test_make_request_failed(official: Official, mocker: MockerFixture) -> None:
"""
must reraise generic exception
"""
mocker.patch("requests.get", side_effect=Exception())
with pytest.raises(Exception):
official.make_request("akonadi", by="q")
def test_make_request_failed_http_error(official: Official, mocker: MockerFixture) -> None:
"""
must reraise http exception
"""
mocker.patch("requests.get", side_effect=requests.exceptions.HTTPError())
with pytest.raises(requests.exceptions.HTTPError):
official.make_request("akonadi", by="q")
def test_package_info(official: Official, aur_package_akonadi: AURPackage, mocker: MockerFixture) -> None:
"""
must make request for info
"""
request_mock = mocker.patch("ahriman.core.alpm.remote.official.Official.make_request",
return_value=[aur_package_akonadi])
assert official.package_info(aur_package_akonadi.name) == aur_package_akonadi
request_mock.assert_called_once_with(aur_package_akonadi.name, by="name")
def test_package_search(official: Official, aur_package_akonadi: AURPackage, mocker: MockerFixture) -> None:
"""
must make request for search
"""
request_mock = mocker.patch("ahriman.core.alpm.remote.official.Official.make_request",
return_value=[aur_package_akonadi])
assert official.package_search(aur_package_akonadi.name) == [aur_package_akonadi]
request_mock.assert_called_once_with(aur_package_akonadi.name, by="q")

View File

@ -0,0 +1,72 @@
import pytest
from pytest_mock import MockerFixture
from unittest import mock
from ahriman.core.alpm.remote.remote import Remote
from ahriman.models.aur_package import AURPackage
def test_info(mocker: MockerFixture) -> None:
"""
must call info method
"""
info_mock = mocker.patch("ahriman.core.alpm.remote.remote.Remote.package_info")
Remote.info("ahriman")
info_mock.assert_called_once_with("ahriman")
def test_multisearch(aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
"""
must search in AUR with multiple words
"""
terms = ["ahriman", "is", "cool"]
search_mock = mocker.patch("ahriman.core.alpm.remote.remote.Remote.search", return_value=[aur_package_ahriman])
assert Remote.multisearch(*terms) == [aur_package_ahriman]
search_mock.assert_has_calls([mock.call("ahriman"), mock.call("cool")])
def test_multisearch_empty(mocker: MockerFixture) -> None:
"""
must return empty list if no long terms supplied
"""
terms = ["it", "is"]
search_mock = mocker.patch("ahriman.core.alpm.remote.remote.Remote.search")
assert Remote.multisearch(*terms) == []
search_mock.assert_not_called()
def test_multisearch_single(aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
"""
must search in AUR with one word
"""
search_mock = mocker.patch("ahriman.core.alpm.remote.remote.Remote.search", return_value=[aur_package_ahriman])
assert Remote.multisearch("ahriman") == [aur_package_ahriman]
search_mock.assert_called_once_with("ahriman")
def test_search(mocker: MockerFixture) -> None:
"""
must call search method
"""
search_mock = mocker.patch("ahriman.core.alpm.remote.remote.Remote.package_search")
Remote.search("ahriman")
search_mock.assert_called_once_with("ahriman")
def test_package_info(remote: Remote) -> None:
"""
must raise NotImplemented for missing package info method
"""
with pytest.raises(NotImplementedError):
remote.package_info("package")
def test_package_search(remote: Remote) -> None:
"""
must raise NotImplemented for missing package search method
"""
with pytest.raises(NotImplementedError):
remote.package_search("package")

View File

@ -9,8 +9,8 @@ from pytest_mock import MockerFixture
from unittest.mock import MagicMock
from ahriman.core.exceptions import BuildFailed, InvalidOption, UnsafeRun
from ahriman.core.util import check_output, check_user, exception_response_text, filter_json, package_like, \
pretty_datetime, pretty_size, tmpdir, walk
from ahriman.core.util import check_output, check_user, exception_response_text, filter_json, full_version, \
package_like, pretty_datetime, pretty_size, tmpdir, walk
from ahriman.models.package import Package
from ahriman.models.repository_paths import RepositoryPaths
@ -177,6 +177,16 @@ def test_filter_json_empty_value(package_ahriman: Package) -> None:
assert "base" not in filter_json(probe, probe.keys())
def test_full_version() -> None:
"""
must construct full version
"""
assert full_version("1", "r2388.d30e3201", "1") == "1:r2388.d30e3201-1"
assert full_version(None, "0.12.1", "1") == "0.12.1-1"
assert full_version(0, "0.12.1", "1") == "0.12.1-1"
assert full_version(1, "0.12.1", "1") == "1:0.12.1-1"
def test_package_like(package_ahriman: Package) -> None:
"""
package_like must return true for archives
@ -298,24 +308,26 @@ def test_walk(resource_path_root: Path) -> None:
must traverse directory recursively
"""
expected = sorted([
resource_path_root / "core/ahriman.ini",
resource_path_root / "core/logging.ini",
resource_path_root / "models/aur_error",
resource_path_root / "models/big_file_checksum",
resource_path_root / "models/empty_file_checksum",
resource_path_root / "models/package_ahriman_aur",
resource_path_root / "models/package_ahriman_srcinfo",
resource_path_root / "models/package_tpacpi-bat-git_srcinfo",
resource_path_root / "models/package_yay_srcinfo",
resource_path_root / "web/templates/build-status/login-modal.jinja2",
resource_path_root / "web/templates/build-status/package-actions-modals.jinja2",
resource_path_root / "web/templates/build-status/package-actions-script.jinja2",
resource_path_root / "web/templates/static/favicon.ico",
resource_path_root / "web/templates/utils/bootstrap-scripts.jinja2",
resource_path_root / "web/templates/utils/style.jinja2",
resource_path_root / "web/templates/build-status.jinja2",
resource_path_root / "web/templates/email-index.jinja2",
resource_path_root / "web/templates/repo-index.jinja2",
resource_path_root / "core" / "ahriman.ini",
resource_path_root / "core" / "logging.ini",
resource_path_root / "models" / "aur_error",
resource_path_root / "models" / "big_file_checksum",
resource_path_root / "models" / "empty_file_checksum",
resource_path_root / "models" / "official_error",
resource_path_root / "models" / "package_ahriman_aur",
resource_path_root / "models" / "package_akonadi_aur",
resource_path_root / "models" / "package_ahriman_srcinfo",
resource_path_root / "models" / "package_tpacpi-bat-git_srcinfo",
resource_path_root / "models" / "package_yay_srcinfo",
resource_path_root / "web" / "templates" / "build-status" / "login-modal.jinja2",
resource_path_root / "web" / "templates" / "build-status" / "package-actions-modals.jinja2",
resource_path_root / "web" / "templates" / "build-status" / "package-actions-script.jinja2",
resource_path_root / "web" / "templates" / "static" / "favicon.ico",
resource_path_root / "web" / "templates" / "utils" / "bootstrap-scripts.jinja2",
resource_path_root / "web" / "templates" / "utils" / "style.jinja2",
resource_path_root / "web" / "templates" / "build-status.jinja2",
resource_path_root / "web" / "templates" / "email-index.jinja2",
resource_path_root / "web" / "templates" / "repo-index.jinja2",
])
local_files = list(sorted(walk(resource_path_root)))
assert local_files == expected

View File

@ -9,7 +9,7 @@ from typing import Any, Dict
from ahriman.models.aur_package import AURPackage
def _get_data(resource_path_root: Path) -> Dict[str, Any]:
def _get_aur_data(resource_path_root: Path) -> Dict[str, Any]:
"""
load package description from resource file
:param resource_path_root: path to resource root
@ -19,11 +19,21 @@ def _get_data(resource_path_root: Path) -> Dict[str, Any]:
return json.loads(response)["results"][0]
def _get_official_data(resource_path_root: Path) -> Dict[str, Any]:
"""
load package description from resource file
:param resource_path_root: path to resource root
:return: json descriptor
"""
response = (resource_path_root / "models" / "package_akonadi_aur").read_text()
return json.loads(response)["results"][0]
def test_from_json(aur_package_ahriman: AURPackage, resource_path_root: Path) -> None:
"""
must load package from json
"""
model = _get_data(resource_path_root)
model = _get_aur_data(resource_path_root)
assert AURPackage.from_json(model) == aur_package_ahriman
@ -35,11 +45,19 @@ def test_from_json_2(aur_package_ahriman: AURPackage, mocker: MockerFixture) ->
assert AURPackage.from_json(asdict(aur_package_ahriman)) == aur_package_ahriman
def test_from_repo(aur_package_akonadi: AURPackage, resource_path_root: Path) -> None:
"""
must load package from repository api json
"""
model = _get_official_data(resource_path_root)
assert AURPackage.from_repo(model) == aur_package_akonadi
def test_convert(aur_package_ahriman: AURPackage, resource_path_root: Path) -> None:
"""
must convert fields to snakecase and also apply converters
"""
model = _get_data(resource_path_root)
model = _get_aur_data(resource_path_root)
converted = AURPackage.convert(model)
known_fields = [pair.name for pair in fields(AURPackage)]
assert all(field in known_fields for field in converted)

View File

@ -101,7 +101,7 @@ def test_from_aur(package_ahriman: Package, aur_package_ahriman: AURPackage, moc
"""
must construct package from aur
"""
mocker.patch("ahriman.core.alpm.aur.AUR.info", return_value=aur_package_ahriman)
mocker.patch("ahriman.core.alpm.remote.aur.AUR.info", return_value=aur_package_ahriman)
package = Package.from_aur(package_ahriman.base, package_ahriman.aur_url)
assert package_ahriman.base == package.base
@ -154,6 +154,18 @@ def test_from_json_view_3(package_tpacpi_bat_git: Package) -> None:
assert Package.from_json(package_tpacpi_bat_git.view()) == package_tpacpi_bat_git
def test_from_official(package_ahriman: Package, aur_package_ahriman: AURPackage, mocker: MockerFixture) -> None:
"""
must construct package from official repository
"""
mocker.patch("ahriman.core.alpm.remote.official.Official.info", return_value=aur_package_ahriman)
package = Package.from_official(package_ahriman.base, package_ahriman.aur_url)
assert package_ahriman.base == package.base
assert package_ahriman.version == package.version
assert package_ahriman.packages.keys() == package.packages.keys()
def test_load_resolve(package_ahriman: Package, pyalpm_handle: MagicMock, mocker: MockerFixture) -> None:
"""
must resolve source before package loading
@ -193,6 +205,15 @@ def test_load_from_build(package_ahriman: Package, pyalpm_handle: MagicMock, moc
load_mock.assert_called_once_with(Path("path"), package_ahriman.aur_url)
def test_load_from_official(package_ahriman: Package, pyalpm_handle: MagicMock, mocker: MockerFixture) -> None:
"""
must load package from AUR
"""
load_mock = mocker.patch("ahriman.models.package.Package.from_official")
Package.load("path", PackageSource.Repository, pyalpm_handle, package_ahriman.aur_url)
load_mock.assert_called_once_with("path", package_ahriman.aur_url)
def test_load_failure(package_ahriman: Package, pyalpm_handle: MagicMock, mocker: MockerFixture) -> None:
"""
must raise InvalidPackageInfo on exception
@ -240,14 +261,6 @@ def test_dependencies_with_version(mocker: MockerFixture, resource_path_root: Pa
assert Package.dependencies(Path("path")) == {"git", "go", "pacman"}
def test_full_version() -> None:
"""
must construct full version
"""
assert Package.full_version("1", "r2388.d30e3201", "1") == "1:r2388.d30e3201-1"
assert Package.full_version(None, "0.12.1", "1") == "0.12.1-1"
def test_actual_version(package_ahriman: Package, repository_paths: RepositoryPaths) -> None:
"""
must return same actual_version as version is

View File

@ -21,7 +21,7 @@ async def test_get(client: TestClient, aur_package_ahriman: AURPackage, mocker:
"""
must call get request correctly
"""
mocker.patch("ahriman.core.alpm.aur.AUR.multisearch", return_value=[aur_package_ahriman])
mocker.patch("ahriman.core.alpm.remote.aur.AUR.multisearch", return_value=[aur_package_ahriman])
response = await client.get("/service-api/v1/search", params={"for": "ahriman"})
assert response.ok
@ -33,7 +33,7 @@ async def test_get_exception(client: TestClient, mocker: MockerFixture) -> None:
"""
must raise 400 on empty search string
"""
search_mock = mocker.patch("ahriman.core.alpm.aur.AUR.multisearch", return_value=[])
search_mock = mocker.patch("ahriman.core.alpm.remote.aur.AUR.multisearch", return_value=[])
response = await client.get("/service-api/v1/search")
assert response.status == 404
@ -44,7 +44,7 @@ async def test_get_join(client: TestClient, mocker: MockerFixture) -> None:
"""
must join search args with space
"""
search_mock = mocker.patch("ahriman.core.alpm.aur.AUR.multisearch")
search_mock = mocker.patch("ahriman.core.alpm.remote.aur.AUR.multisearch")
response = await client.get("/service-api/v1/search", params=[("for", "ahriman"), ("for", "maybe")])
assert response.ok

View File

@ -0,0 +1,8 @@
{
"version": 2,
"limit": 250,
"valid": false,
"results": [],
"num_pages": 1,
"page": 1
}

View File

@ -0,0 +1,55 @@
{
"version": 2,
"limit": 250,
"valid": true,
"results": [
{
"pkgname": "akonadi",
"pkgbase": "akonadi",
"repo": "extra",
"arch": "x86_64",
"pkgver": "21.12.3",
"pkgrel": "2",
"epoch": 0,
"pkgdesc": "PIM layer, which provides an asynchronous API to access all kind of PIM data",
"url": "https://kontact.kde.org",
"filename": "akonadi-21.12.3-2-x86_64.pkg.tar.zst",
"compressed_size": 789510,
"installed_size": 2592656,
"build_date": "2022-03-04T11:50:03Z",
"last_update": "2022-03-06T08:39:50.610Z",
"flag_date": null,
"maintainers": [
"felixonmars",
"arojas"
],
"packager": "arojas",
"groups": [],
"licenses": [
"LGPL"
],
"conflicts": [],
"provides": [],
"replaces": [],
"depends": [
"libakonadi",
"mariadb"
],
"optdepends": [
"postgresql: PostgreSQL backend"
],
"makedepends": [
"boost",
"doxygen",
"extra-cmake-modules",
"kaccounts-integration",
"kitemmodels",
"postgresql",
"qt5-tools"
],
"checkdepends": []
}
],
"num_pages": 1,
"page": 1
}