rewrite api under single /api endpoint

This commit is contained in:
Evgenii Alekseev 2022-05-29 17:15:49 +03:00
parent d7966e419d
commit 1ea3911531
19 changed files with 119 additions and 130 deletions

View File

@ -198,8 +198,7 @@ PGP key to import from public server
key server for key import key server for key import
.SH COMMAND \fI\,'ahriman package-add'\/\fR .SH COMMAND \fI\,'ahriman package-add'\/\fR
usage: ahriman package-add [-h] [-e] [-n] usage: ahriman package-add [-h] [-e] [-n] [-s {auto,archive,aur,directory,local,remote,repository}]
[-s {PackageSource.Auto,PackageSource.Archive,PackageSource.AUR,PackageSource.Directory,PackageSource.Local,PackageSource.Remote,PackageSource.Repository}]
[--without-dependencies] [--without-dependencies]
package [package ...] package [package ...]
@ -219,7 +218,7 @@ return non\-zero exit status if result is empty
run update function after run update function after
.TP .TP
\fB\-s\fR \fI\,{PackageSource.Auto,PackageSource.Archive,PackageSource.AUR,PackageSource.Directory,PackageSource.Local,PackageSource.Remote,PackageSource.Repository}\/\fR, \fB\-\-source\fR \fI\,{PackageSource.Auto,PackageSource.Archive,PackageSource.AUR,PackageSource.Directory,PackageSource.Local,PackageSource.Remote,PackageSource.Repository}\/\fR \fB\-s\fR \fI\,{auto,archive,aur,directory,local,remote,repository}\/\fR, \fB\-\-source\fR \fI\,{auto,archive,aur,directory,local,remote,repository}\/\fR
explicitly specify the package source for this command explicitly specify the package source for this command
.TP .TP
@ -236,9 +235,7 @@ remove package from the repository
package name or base package name or base
.SH COMMAND \fI\,'ahriman package-status'\/\fR .SH COMMAND \fI\,'ahriman package-status'\/\fR
usage: ahriman package-status [-h] [--ahriman] [-e] [-i] usage: ahriman package-status [-h] [--ahriman] [-e] [-i] [-s {unknown,pending,building,failed,success}] [package ...]
[-s {BuildStatusEnum.Unknown,BuildStatusEnum.Pending,BuildStatusEnum.Building,BuildStatusEnum.Failed,BuildStatusEnum.Success}]
[package ...]
request status of the package request status of the package
@ -260,7 +257,7 @@ return non\-zero exit status if result is empty
show additional package information show additional package information
.TP .TP
\fB\-s\fR \fI\,{BuildStatusEnum.Unknown,BuildStatusEnum.Pending,BuildStatusEnum.Building,BuildStatusEnum.Failed,BuildStatusEnum.Success}\/\fR, \fB\-\-status\fR \fI\,{BuildStatusEnum.Unknown,BuildStatusEnum.Pending,BuildStatusEnum.Building,BuildStatusEnum.Failed,BuildStatusEnum.Success}\/\fR \fB\-s\fR \fI\,{unknown,pending,building,failed,success}\/\fR, \fB\-\-status\fR \fI\,{unknown,pending,building,failed,success}\/\fR
filter packages by status filter packages by status
.SH COMMAND \fI\,'ahriman package-status-remove'\/\fR .SH COMMAND \fI\,'ahriman package-status-remove'\/\fR
@ -273,9 +270,7 @@ remove the package from the status page
remove specified packages remove specified packages
.SH COMMAND \fI\,'ahriman package-status-update'\/\fR .SH COMMAND \fI\,'ahriman package-status-update'\/\fR
usage: ahriman package-status-update [-h] usage: ahriman package-status-update [-h] [-s {unknown,pending,building,failed,success}] [package ...]
[-s {BuildStatusEnum.Unknown,BuildStatusEnum.Pending,BuildStatusEnum.Building,BuildStatusEnum.Failed,BuildStatusEnum.Success}]
[package ...]
update package status on the status page update package status on the status page
@ -285,7 +280,7 @@ set status for specified packages. If no packages supplied, service status will
.SH OPTIONS \fI\,'ahriman package-status-update'\/\fR .SH OPTIONS \fI\,'ahriman package-status-update'\/\fR
.TP .TP
\fB\-s\fR \fI\,{BuildStatusEnum.Unknown,BuildStatusEnum.Pending,BuildStatusEnum.Building,BuildStatusEnum.Failed,BuildStatusEnum.Success}\/\fR, \fB\-\-status\fR \fI\,{BuildStatusEnum.Unknown,BuildStatusEnum.Pending,BuildStatusEnum.Building,BuildStatusEnum.Failed,BuildStatusEnum.Success}\/\fR \fB\-s\fR \fI\,{unknown,pending,building,failed,success}\/\fR, \fB\-\-status\fR \fI\,{unknown,pending,building,failed,success}\/\fR
new status new status
.SH COMMAND \fI\,'ahriman patch-add'\/\fR .SH COMMAND \fI\,'ahriman patch-add'\/\fR
@ -439,8 +434,7 @@ root path of the extracted files
.SH COMMAND \fI\,'ahriman repo-setup'\/\fR .SH COMMAND \fI\,'ahriman repo-setup'\/\fR
usage: ahriman repo-setup [-h] [--build-as-user BUILD_AS_USER] [--build-command BUILD_COMMAND] usage: ahriman repo-setup [-h] [--build-as-user BUILD_AS_USER] [--build-command BUILD_COMMAND]
[--from-configuration FROM_CONFIGURATION] [--no-multilib] --packager PACKAGER --repository [--from-configuration FROM_CONFIGURATION] [--no-multilib] --packager PACKAGER --repository
REPOSITORY [--sign-key SIGN_KEY] REPOSITORY [--sign-key SIGN_KEY] [--sign-target {disabled,pacakges,repository}]
[--sign-target {SignSettings.Disabled,SignSettings.Packages,SignSettings.Repository}]
[--web-port WEB_PORT] [--web-port WEB_PORT]
create initial service configuration, requires root create initial service configuration, requires root
@ -475,7 +469,7 @@ repository name
sign key id sign key id
.TP .TP
\fB\-\-sign\-target\fR \fI\,{SignSettings.Disabled,SignSettings.Packages,SignSettings.Repository}\/\fR \fB\-\-sign\-target\fR \fI\,{disabled,pacakges,repository}\/\fR
sign options sign options
.TP .TP
@ -492,14 +486,13 @@ usage: ahriman repo-sign [-h] [package ...]
sign only specified packages sign only specified packages
.SH COMMAND \fI\,'ahriman repo-status-update'\/\fR .SH COMMAND \fI\,'ahriman repo-status-update'\/\fR
usage: ahriman repo-status-update [-h] usage: ahriman repo-status-update [-h] [-s {unknown,pending,building,failed,success}]
[-s {BuildStatusEnum.Unknown,BuildStatusEnum.Pending,BuildStatusEnum.Building,BuildStatusEnum.Failed,BuildStatusEnum.Success}]
update repository status on the status page update repository status on the status page
.SH OPTIONS \fI\,'ahriman repo-status-update'\/\fR .SH OPTIONS \fI\,'ahriman repo-status-update'\/\fR
.TP .TP
\fB\-s\fR \fI\,{BuildStatusEnum.Unknown,BuildStatusEnum.Pending,BuildStatusEnum.Building,BuildStatusEnum.Failed,BuildStatusEnum.Success}\/\fR, \fB\-\-status\fR \fI\,{BuildStatusEnum.Unknown,BuildStatusEnum.Pending,BuildStatusEnum.Building,BuildStatusEnum.Failed,BuildStatusEnum.Success}\/\fR \fB\-s\fR \fI\,{unknown,pending,building,failed,success}\/\fR, \fB\-\-status\fR \fI\,{unknown,pending,building,failed,success}\/\fR
new status new status
.SH COMMAND \fI\,'ahriman repo-sync'\/\fR .SH COMMAND \fI\,'ahriman repo-sync'\/\fR
@ -556,9 +549,7 @@ usage: ahriman shell [-h]
drop into python shell while having created application drop into python shell while having created application
.SH COMMAND \fI\,'ahriman user-add'\/\fR .SH COMMAND \fI\,'ahriman user-add'\/\fR
usage: ahriman user-add [-h] [--as-service] [-p PASSWORD] usage: ahriman user-add [-h] [--as-service] [-p PASSWORD] [-r {unauthorized,read,reporter,full}] [-s] username
[-r {UserAccess.Unauthorized,UserAccess.Read,UserAccess.Reporter,UserAccess.Full}] [-s]
username
update user for web services with the given password and role. In case if password was not entered it will be asked interactively update user for web services with the given password and role. In case if password was not entered it will be asked interactively
@ -577,7 +568,7 @@ user password. Blank password will be treated as empty password, which is in par
authorization type. authorization type.
.TP .TP
\fB\-r\fR \fI\,{UserAccess.Unauthorized,UserAccess.Read,UserAccess.Reporter,UserAccess.Full}\/\fR, \fB\-\-role\fR \fI\,{UserAccess.Unauthorized,UserAccess.Read,UserAccess.Reporter,UserAccess.Full}\/\fR \fB\-r\fR \fI\,{unauthorized,read,reporter,full}\/\fR, \fB\-\-role\fR \fI\,{unauthorized,read,reporter,full}\/\fR
user access level user access level
.TP .TP
@ -585,8 +576,7 @@ user access level
set file permissions to user\-only set file permissions to user\-only
.SH COMMAND \fI\,'ahriman user-list'\/\fR .SH COMMAND \fI\,'ahriman user-list'\/\fR
usage: ahriman user-list [-h] [-e] [-r {UserAccess.Unauthorized,UserAccess.Read,UserAccess.Reporter,UserAccess.Full}] usage: ahriman user-list [-h] [-e] [-r {unauthorized,read,reporter,full}] [username]
[username]
list users from the user mapping and their roles list users from the user mapping and their roles
@ -600,7 +590,7 @@ filter users by username
return non\-zero exit status if result is empty return non\-zero exit status if result is empty
.TP .TP
\fB\-r\fR \fI\,{UserAccess.Unauthorized,UserAccess.Read,UserAccess.Reporter,UserAccess.Full}\/\fR, \fB\-\-role\fR \fI\,{UserAccess.Unauthorized,UserAccess.Read,UserAccess.Reporter,UserAccess.Full}\/\fR \fB\-r\fR \fI\,{unauthorized,read,reporter,full}\/\fR, \fB\-\-role\fR \fI\,{unauthorized,read,reporter,full}\/\fR
filter users by role filter users by role
.SH COMMAND \fI\,'ahriman user-remove'\/\fR .SH COMMAND \fI\,'ahriman user-remove'\/\fR

View File

@ -91,7 +91,7 @@
{% if auth.username is none %} {% if auth.username is none %}
{{ auth.control|safe }} {{ auth.control|safe }}
{% else %} {% else %}
<form action="/user-api/v1/logout" method="post"> <form action="/api/v1/logout" method="post">
<button class="btn btn-link" style="text-decoration: none">logout ({{ auth.username }})</button> <button class="btn btn-link" style="text-decoration: none">logout ({{ auth.username }})</button>
</form> </form>
{% endif %} {% endif %}

View File

@ -1,7 +1,7 @@
<div id="loginForm" tabindex="-1" role="dialog" class="modal fade"> <div id="loginForm" tabindex="-1" role="dialog" class="modal fade">
<div class="modal-dialog" role="document"> <div class="modal-dialog" role="document">
<div class="modal-content"> <div class="modal-content">
<form action="/user-api/v1/login" method="post"> <form action="/api/v1/login" method="post">
<div class="modal-header"> <div class="modal-header">
<h4 class="modal-title">login</h4> <h4 class="modal-title">login</h4>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="close"></button>

View File

@ -9,7 +9,7 @@
<div class="form-group row"> <div class="form-group row">
<label for="package" class="col-sm-2 col-form-label">package</label> <label for="package" class="col-sm-2 col-form-label">package</label>
<div class="col-sm-10"> <div class="col-sm-10">
<input id="package-form" type="text" list="known-packages-dlist" class="form-control" placeholder="AUR package" name="package" required> <input id="package-form" type="text" list="known-packages-dlist" autocomplete="off" class="form-control" placeholder="AUR package" name="package" required>
<datalist id="known-packages-dlist"></datalist> <datalist id="known-packages-dlist"></datalist>
</div> </div>
</div> </div>
@ -32,7 +32,7 @@
const value = packageInput.val(); const value = packageInput.val();
$.ajax({ $.ajax({
url: "/service-api/v1/search", url: "/api/v1/service/search",
data: {"for": value}, data: {"for": value},
type: "GET", type: "GET",
dataType: "json", dataType: "json",
@ -44,7 +44,6 @@
return option; return option;
}); });
knownPackages.empty().append(options); knownPackages.empty().append(options);
packageInput.focus();
}, },
}) })
}, this), 500)); }, this), 500));
@ -52,11 +51,11 @@
function addPackages() { function addPackages() {
const packages = [packageInput.val()] const packages = [packageInput.val()]
doPackageAction("/service-api/v1/add", packages); doPackageAction("/api/v1/service/add", packages);
} }
function requestPackages() { function requestPackages() {
const packages = [packageInput.val()] const packages = [packageInput.val()]
doPackageAction("/service-api/v1/request", packages); doPackageAction("/api/v1/service/request", packages);
} }
</script> </script>

View File

@ -39,9 +39,9 @@
return table.bootstrapTable("getSelections").map(row => { return row.id; }); return table.bootstrapTable("getSelections").map(row => { return row.id; });
} }
function removePackages() { doPackageAction("/service-api/v1/remove", getSelection()); } function removePackages() { doPackageAction("/api/v1/service/remove", getSelection()); }
function updatePackages() { doPackageAction("/service-api/v1/add", getSelection()); } function updatePackages() { doPackageAction("/api/v1/service/add", getSelection()); }
function hideControls(hidden) { function hideControls(hidden) {
addButton.attr("hidden", hidden); addButton.attr("hidden", hidden);
@ -53,7 +53,7 @@
table.bootstrapTable("showLoading"); table.bootstrapTable("showLoading");
$.ajax({ $.ajax({
url: "/status-api/v1/packages", url: "/api/v1/packages",
type: "GET", type: "GET",
dataType: "json", dataType: "json",
success: response => { success: response => {
@ -100,7 +100,7 @@
}); });
$.ajax({ $.ajax({
url: "/status-api/v1/status", url: "/api/v1/status",
type: "GET", type: "GET",
dataType: "json", dataType: "json",
success: response => { success: response => {

View File

@ -56,7 +56,7 @@ class OAuth(Mapping):
self.client_secret = configuration.get("auth", "client_secret") self.client_secret = configuration.get("auth", "client_secret")
# in order to use OAuth feature the service must be publicity available # in order to use OAuth feature the service must be publicity available
# thus we expect that address is set # thus we expect that address is set
self.redirect_uri = f"""{configuration.get("web", "address")}/user-api/v1/login""" self.redirect_uri = f"""{configuration.get("web", "address")}/api/v1/login"""
self.provider = self.get_provider(configuration.get("auth", "oauth_provider")) self.provider = self.get_provider(configuration.get("auth", "oauth_provider"))
# it is list but we will have to convert to string it anyway # it is list but we will have to convert to string it anyway
self.scopes = configuration.get("auth", "oauth_scopes") self.scopes = configuration.get("auth", "oauth_scopes")
@ -69,7 +69,7 @@ class OAuth(Mapping):
Returns: Returns:
str: login control as html code to insert str: login control as html code to insert
""" """
return """<a class="nav-link" href="/user-api/v1/login" title="login via OAuth2">login</a>""" return """<a class="nav-link" href="/api/v1/login" title="login via OAuth2">login</a>"""
@staticmethod @staticmethod
def get_provider(name: str) -> Type[aioauth_client.OAuth2Client]: def get_provider(name: str) -> Type[aioauth_client.OAuth2Client]:

View File

@ -65,7 +65,7 @@ class WebClient(Client):
Returns: Returns:
str: full url for web service to login str: full url for web service to login
""" """
return f"{self.address}/user-api/v1/login" return f"{self.address}/api/v1/login"
@property @property
def _status_url(self) -> str: def _status_url(self) -> str:
@ -75,7 +75,7 @@ class WebClient(Client):
Returns: Returns:
str: full url for web service for status str: full url for web service for status
""" """
return f"{self.address}/status-api/v1/status" return f"{self.address}/api/v1/status"
@staticmethod @staticmethod
def parse_address(configuration: Configuration) -> str: def parse_address(configuration: Configuration) -> str:
@ -126,7 +126,7 @@ class WebClient(Client):
Returns: Returns:
str: full url of web service for specific package base str: full url of web service for specific package base
""" """
return f"{self.address}/status-api/v1/packages/{base}" return f"{self.address}/api/v1/packages/{base}"
def add(self, package: Package, status: BuildStatusEnum) -> None: def add(self, package: Package, status: BuildStatusEnum) -> None:
""" """

View File

@ -41,32 +41,32 @@ def setup_routes(application: Application, static_path: Path) -> None:
Available routes are: Available routes are:
* GET / get build status page * ``GET /`` get build status page
* GET /index.html same as above * ``GET /index.html`` same as above
* POST /service-api/v1/add add new packages to repository * ``POST /api/v1/service/add`` add new packages to repository
* POST /service-api/v1/remove remove existing package from repository * ``POST /api/v1/service/remove`` remove existing package from repository
* POST /service-api/v1/request request to add new packages to repository * ``POST /api/v1/service/request`` request to add new packages to repository
* GET /service-api/v1/search search for substring in AUR * ``GET /api/v1/service/search`` search for substring in AUR
* POST /service-api/v1/update update packages in repository, actually it is just alias for add * ``POST /api/v1/service/update`` update packages in repository, actually it is just alias for add
* GET /status-api/v1/packages get all known packages * ``GET /api/v1/packages`` get all known packages
* POST /status-api/v1/packages force update every package from repository * ``POST /api/v1/packages`` force update every package from repository
* DELETE /status-api/v1/package/:base delete package base from status page * ``DELETE /api/v1/package/:base`` delete package base from status page
* GET /status-api/v1/package/:base get package base status * ``GET /api/v1/package/:base`` get package base status
* POST /status-api/v1/package/:base update package base status * ``POST /api/v1/package/:base`` update package base status
* GET /status-api/v1/status get service status itself * ``GET /api/v1/status`` get service status itself
* POST /status-api/v1/status update service status itself * ``POST /api/v1/status`` update service status itself
* GET /user-api/v1/login OAuth2 handler for login * ``GET /api/v1/login`` OAuth2 handler for login
* POST /user-api/v1/login login to service * ``POST /api/v1/login`` login to service
* POST /user-api/v1/logout logout from service * ``POST /api/v1/logout`` logout from service
Args: Args:
application(Application): web application instance application(Application): web application instance
@ -77,26 +77,26 @@ 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_post("/service-api/v1/add", AddView) application.router.add_post("/api/v1/service/add", AddView)
application.router.add_post("/service-api/v1/remove", RemoveView) application.router.add_post("/api/v1/service/remove", RemoveView)
application.router.add_post("/service-api/v1/request", RequestView) application.router.add_post("/api/v1/service/request", RequestView)
application.router.add_get("/service-api/v1/search", SearchView, allow_head=False) application.router.add_get("/api/v1/service/search", SearchView, allow_head=False)
application.router.add_post("/service-api/v1/update", AddView) application.router.add_post("/api/v1/service/update", AddView)
application.router.add_get("/status-api/v1/packages", PackagesView, allow_head=True) application.router.add_get("/api/v1/packages", PackagesView, allow_head=True)
application.router.add_post("/status-api/v1/packages", PackagesView) application.router.add_post("/api/v1/packages", PackagesView)
application.router.add_delete("/status-api/v1/packages/{package}", PackageView) application.router.add_delete("/api/v1/packages/{package}", PackageView)
application.router.add_get("/status-api/v1/packages/{package}", PackageView, allow_head=True) application.router.add_get("/api/v1/packages/{package}", PackageView, allow_head=True)
application.router.add_post("/status-api/v1/packages/{package}", PackageView) application.router.add_post("/api/v1/packages/{package}", PackageView)
application.router.add_get("/status-api/v1/status", StatusView, allow_head=True) application.router.add_get("/api/v1/status", StatusView, allow_head=True)
application.router.add_post("/status-api/v1/status", StatusView) application.router.add_post("/api/v1/status", StatusView)
application.router.add_get("/user-api/v1/login", LoginView) application.router.add_get("/api/v1/login", LoginView)
application.router.add_post("/user-api/v1/login", LoginView) application.router.add_post("/api/v1/login", LoginView)
application.router.add_post("/user-api/v1/logout", LogoutView) application.router.add_post("/api/v1/logout", LogoutView)

View File

@ -18,7 +18,7 @@ def test_status_url(web_client: WebClient) -> None:
must generate package status url correctly must generate package status url correctly
""" """
assert web_client._status_url.startswith(web_client.address) assert web_client._status_url.startswith(web_client.address)
assert web_client._status_url.endswith("/status-api/v1/status") assert web_client._status_url.endswith("/api/v1/status")
def test_parse_address(configuration: Configuration) -> None: def test_parse_address(configuration: Configuration) -> None:
@ -80,7 +80,7 @@ def test_package_url(web_client: WebClient, package_ahriman: Package) -> None:
must generate package status correctly must generate package status correctly
""" """
assert web_client._package_url(package_ahriman.base).startswith(web_client.address) assert web_client._package_url(package_ahriman.base).startswith(web_client.address)
assert web_client._package_url(package_ahriman.base).endswith(f"/status-api/v1/packages/{package_ahriman.base}") assert web_client._package_url(package_ahriman.base).endswith(f"/api/v1/packages/{package_ahriman.base}")
def test_add(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None: def test_add(web_client: WebClient, package_ahriman: Package, mocker: MockerFixture) -> None:

View File

@ -59,7 +59,7 @@ async def test_auth_handler_api(mocker: MockerFixture) -> None:
""" """
must ask for status permission for api calls must ask for status permission for api calls
""" """
aiohttp_request = pytest.helpers.request("", "/status-api", "GET") aiohttp_request = pytest.helpers.request("", "/api/v1/status", "GET")
request_handler = AsyncMock() request_handler = AsyncMock()
request_handler.get_permission.return_value = UserAccess.Read request_handler.get_permission.return_value = UserAccess.Read
check_permission_mock = mocker.patch("aiohttp_security.check_permission") check_permission_mock = mocker.patch("aiohttp_security.check_permission")
@ -91,7 +91,7 @@ async def test_auth_handler_allow_read_only(mocker: MockerFixture) -> None:
""" """
must allow pages with allow read only flag must allow pages with allow read only flag
""" """
aiohttp_request = pytest.helpers.request("", "/status-api", "GET") aiohttp_request = pytest.helpers.request("", "/api/v1/status", "GET")
request_handler = AsyncMock() request_handler = AsyncMock()
request_handler.get_permission.return_value = UserAccess.Read request_handler.get_permission.return_value = UserAccess.Read
check_permission_mock = mocker.patch("aiohttp_security.check_permission") check_permission_mock = mocker.patch("aiohttp_security.check_permission")
@ -105,7 +105,7 @@ async def test_auth_handler_api_no_method(mocker: MockerFixture) -> None:
""" """
must ask for write permission if handler does not have get_permission method must ask for write permission if handler does not have get_permission method
""" """
aiohttp_request = pytest.helpers.request("", "/status-api", "GET") aiohttp_request = pytest.helpers.request("", "/api/v1/status", "GET")
request_handler = AsyncMock() request_handler = AsyncMock()
request_handler.get_permission = None request_handler.get_permission = None
check_permission_mock = mocker.patch("aiohttp_security.check_permission") check_permission_mock = mocker.patch("aiohttp_security.check_permission")
@ -119,7 +119,7 @@ async def test_auth_handler_api_post(mocker: MockerFixture) -> None:
""" """
must ask for status permission for api calls with POST must ask for status permission for api calls with POST
""" """
aiohttp_request = pytest.helpers.request("", "/status-api", "POST") aiohttp_request = pytest.helpers.request("", "/api/v1/status", "POST")
request_handler = AsyncMock() request_handler = AsyncMock()
request_handler.get_permission.return_value = UserAccess.Full request_handler.get_permission.return_value = UserAccess.Full
check_permission_mock = mocker.patch("aiohttp_security.check_permission") check_permission_mock = mocker.patch("aiohttp_security.check_permission")

View File

@ -21,7 +21,7 @@ async def test_post(client: TestClient, mocker: MockerFixture) -> None:
must call post request correctly must call post request correctly
""" """
add_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_add") add_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_add")
response = await client.post("/service-api/v1/add", json={"packages": ["ahriman"]}) response = await client.post("/api/v1/service/add", json={"packages": ["ahriman"]})
assert response.ok assert response.ok
add_mock.assert_called_once_with(["ahriman"], now=True) add_mock.assert_called_once_with(["ahriman"], now=True)
@ -32,7 +32,7 @@ async def test_post_exception(client: TestClient, mocker: MockerFixture) -> None
must raise exception on missing packages payload must raise exception on missing packages payload
""" """
add_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_add") add_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_add")
response = await client.post("/service-api/v1/add") response = await client.post("/api/v1/service/add")
assert response.status == 400 assert response.status == 400
add_mock.assert_not_called() add_mock.assert_not_called()
@ -43,7 +43,7 @@ async def test_post_update(client: TestClient, mocker: MockerFixture) -> None:
must call post request correctly for alias must call post request correctly for alias
""" """
add_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_add") add_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_add")
response = await client.post("/service-api/v1/update", json={"packages": ["ahriman"]}) response = await client.post("/api/v1/service/update", json={"packages": ["ahriman"]})
assert response.ok assert response.ok
add_mock.assert_called_once_with(["ahriman"], now=True) add_mock.assert_called_once_with(["ahriman"], now=True)

View File

@ -21,7 +21,7 @@ async def test_post(client: TestClient, mocker: MockerFixture) -> None:
must call post request correctly must call post request correctly
""" """
remove_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_remove") remove_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_remove")
response = await client.post("/service-api/v1/remove", json={"packages": ["ahriman"]}) response = await client.post("/api/v1/service/remove", json={"packages": ["ahriman"]})
assert response.ok assert response.ok
remove_mock.assert_called_once_with(["ahriman"]) remove_mock.assert_called_once_with(["ahriman"])
@ -32,7 +32,7 @@ async def test_post_exception(client: TestClient, mocker: MockerFixture) -> None
must raise exception on missing packages payload must raise exception on missing packages payload
""" """
remove_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_remove") remove_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_remove")
response = await client.post("/service-api/v1/remove") response = await client.post("/api/v1/service/remove")
assert response.status == 400 assert response.status == 400
remove_mock.assert_not_called() remove_mock.assert_not_called()

View File

@ -21,7 +21,7 @@ async def test_post(client: TestClient, mocker: MockerFixture) -> None:
must call post request correctly must call post request correctly
""" """
add_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_add") add_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_add")
response = await client.post("/service-api/v1/request", json={"packages": ["ahriman"]}) response = await client.post("/api/v1/service/request", json={"packages": ["ahriman"]})
assert response.ok assert response.ok
add_mock.assert_called_once_with(["ahriman"], now=False) add_mock.assert_called_once_with(["ahriman"], now=False)
@ -32,7 +32,7 @@ async def test_post_exception(client: TestClient, mocker: MockerFixture) -> None
must raise exception on missing packages payload must raise exception on missing packages payload
""" """
add_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_add") add_mock = mocker.patch("ahriman.core.spawn.Spawn.packages_add")
response = await client.post("/service-api/v1/request") response = await client.post("/api/v1/service/request")
assert response.status == 400 assert response.status == 400
add_mock.assert_not_called() add_mock.assert_not_called()

View File

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

View File

@ -24,12 +24,12 @@ async def test_get(client: TestClient, package_ahriman: Package, package_python_
""" """
must return status for specific package must return status for specific package
""" """
await client.post(f"/status-api/v1/packages/{package_ahriman.base}", await client.post(f"/api/v1/packages/{package_ahriman.base}",
json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()}) json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()})
await client.post(f"/status-api/v1/packages/{package_python_schedule.base}", await client.post(f"/api/v1/packages/{package_python_schedule.base}",
json={"status": BuildStatusEnum.Success.value, "package": package_python_schedule.view()}) json={"status": BuildStatusEnum.Success.value, "package": package_python_schedule.view()})
response = await client.get(f"/status-api/v1/packages/{package_ahriman.base}") response = await client.get(f"/api/v1/packages/{package_ahriman.base}")
assert response.ok assert response.ok
packages = [Package.from_json(item["package"]) for item in await response.json()] packages = [Package.from_json(item["package"]) for item in await response.json()]
@ -41,7 +41,7 @@ async def test_get_not_found(client: TestClient, package_ahriman: Package) -> No
""" """
must return Not Found for unknown package must return Not Found for unknown package
""" """
response = await client.get(f"/status-api/v1/packages/{package_ahriman.base}") response = await client.get(f"/api/v1/packages/{package_ahriman.base}")
assert response.status == 404 assert response.status == 404
@ -49,18 +49,18 @@ async def test_delete(client: TestClient, package_ahriman: Package, package_pyth
""" """
must delete single base must delete single base
""" """
await client.post(f"/status-api/v1/packages/{package_ahriman.base}", await client.post(f"/api/v1/packages/{package_ahriman.base}",
json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()}) json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()})
await client.post(f"/status-api/v1/packages/{package_python_schedule.base}", await client.post(f"/api/v1/packages/{package_python_schedule.base}",
json={"status": BuildStatusEnum.Success.value, "package": package_python_schedule.view()}) json={"status": BuildStatusEnum.Success.value, "package": package_python_schedule.view()})
response = await client.delete(f"/status-api/v1/packages/{package_ahriman.base}") response = await client.delete(f"/api/v1/packages/{package_ahriman.base}")
assert response.status == 204 assert response.status == 204
response = await client.get(f"/status-api/v1/packages/{package_ahriman.base}") response = await client.get(f"/api/v1/packages/{package_ahriman.base}")
assert response.status == 404 assert response.status == 404
response = await client.get(f"/status-api/v1/packages/{package_python_schedule.base}") response = await client.get(f"/api/v1/packages/{package_python_schedule.base}")
assert response.ok assert response.ok
@ -68,16 +68,16 @@ async def test_delete_unknown(client: TestClient, package_ahriman: Package, pack
""" """
must suppress errors on unknown package deletion must suppress errors on unknown package deletion
""" """
await client.post(f"/status-api/v1/packages/{package_python_schedule.base}", await client.post(f"/api/v1/packages/{package_python_schedule.base}",
json={"status": BuildStatusEnum.Success.value, "package": package_python_schedule.view()}) json={"status": BuildStatusEnum.Success.value, "package": package_python_schedule.view()})
response = await client.delete(f"/status-api/v1/packages/{package_ahriman.base}") response = await client.delete(f"/api/v1/packages/{package_ahriman.base}")
assert response.status == 204 assert response.status == 204
response = await client.get(f"/status-api/v1/packages/{package_ahriman.base}") response = await client.get(f"/api/v1/packages/{package_ahriman.base}")
assert response.status == 404 assert response.status == 404
response = await client.get(f"/status-api/v1/packages/{package_python_schedule.base}") response = await client.get(f"/api/v1/packages/{package_python_schedule.base}")
assert response.ok assert response.ok
@ -86,11 +86,11 @@ async def test_post(client: TestClient, package_ahriman: Package) -> None:
must update package status must update package status
""" """
post_response = await client.post( post_response = await client.post(
f"/status-api/v1/packages/{package_ahriman.base}", f"/api/v1/packages/{package_ahriman.base}",
json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()}) json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()})
assert post_response.status == 204 assert post_response.status == 204
response = await client.get(f"/status-api/v1/packages/{package_ahriman.base}") response = await client.get(f"/api/v1/packages/{package_ahriman.base}")
assert response.ok assert response.ok
@ -98,7 +98,7 @@ async def test_post_exception(client: TestClient, package_ahriman: Package) -> N
""" """
must raise exception on invalid payload must raise exception on invalid payload
""" """
post_response = await client.post(f"/status-api/v1/packages/{package_ahriman.base}", json={}) post_response = await client.post(f"/api/v1/packages/{package_ahriman.base}", json={})
assert post_response.status == 400 assert post_response.status == 400
@ -107,15 +107,15 @@ async def test_post_light(client: TestClient, package_ahriman: Package) -> None:
must update package status only must update package status only
""" """
post_response = await client.post( post_response = await client.post(
f"/status-api/v1/packages/{package_ahriman.base}", f"/api/v1/packages/{package_ahriman.base}",
json={"status": BuildStatusEnum.Unknown.value, "package": package_ahriman.view()}) json={"status": BuildStatusEnum.Unknown.value, "package": package_ahriman.view()})
assert post_response.status == 204 assert post_response.status == 204
post_response = await client.post( post_response = await client.post(
f"/status-api/v1/packages/{package_ahriman.base}", json={"status": BuildStatusEnum.Success.value}) f"/api/v1/packages/{package_ahriman.base}", json={"status": BuildStatusEnum.Success.value})
assert post_response.status == 204 assert post_response.status == 204
response = await client.get(f"/status-api/v1/packages/{package_ahriman.base}") response = await client.get(f"/api/v1/packages/{package_ahriman.base}")
assert response.ok assert response.ok
statuses = { statuses = {
Package.from_json(item["package"]).base: BuildStatus.from_json(item["status"]) Package.from_json(item["package"]).base: BuildStatus.from_json(item["status"])
@ -129,5 +129,5 @@ async def test_post_not_found(client: TestClient, package_ahriman: Package) -> N
must raise exception on status update for unknown package must raise exception on status update for unknown package
""" """
post_response = await client.post( post_response = await client.post(
f"/status-api/v1/packages/{package_ahriman.base}", json={"status": BuildStatusEnum.Success.value}) f"/api/v1/packages/{package_ahriman.base}", json={"status": BuildStatusEnum.Success.value})
assert post_response.status == 400 assert post_response.status == 400

View File

@ -25,12 +25,12 @@ async def test_get(client: TestClient, package_ahriman: Package, package_python_
""" """
must return status for all packages must return status for all packages
""" """
await client.post(f"/status-api/v1/packages/{package_ahriman.base}", await client.post(f"/api/v1/packages/{package_ahriman.base}",
json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()}) json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()})
await client.post(f"/status-api/v1/packages/{package_python_schedule.base}", await client.post(f"/api/v1/packages/{package_python_schedule.base}",
json={"status": BuildStatusEnum.Success.value, "package": package_python_schedule.view()}) json={"status": BuildStatusEnum.Success.value, "package": package_python_schedule.view()})
response = await client.get("/status-api/v1/packages") response = await client.get("/api/v1/packages")
assert response.ok assert response.ok
packages = [Package.from_json(item["package"]) for item in await response.json()] packages = [Package.from_json(item["package"]) for item in await response.json()]
@ -43,6 +43,6 @@ async def test_post(client: TestClient, mocker: MockerFixture) -> None:
must be able to reload packages must be able to reload packages
""" """
load_mock = mocker.patch("ahriman.core.status.watcher.Watcher.load") load_mock = mocker.patch("ahriman.core.status.watcher.Watcher.load")
response = await client.post("/status-api/v1/packages") response = await client.post("/api/v1/packages")
assert response.status == 204 assert response.status == 204
load_mock.assert_called_once_with() load_mock.assert_called_once_with()

View File

@ -28,10 +28,10 @@ async def test_get(client: TestClient, package_ahriman: Package) -> None:
""" """
must generate web service status correctly must generate web service status correctly
""" """
await client.post(f"/status-api/v1/packages/{package_ahriman.base}", await client.post(f"/api/v1/packages/{package_ahriman.base}",
json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()}) json={"status": BuildStatusEnum.Success.value, "package": package_ahriman.view()})
response = await client.get("/status-api/v1/status") response = await client.get("/api/v1/status")
assert response.ok assert response.ok
json = await response.json() json = await response.json()
@ -45,10 +45,10 @@ async def test_post(client: TestClient) -> None:
must update service status correctly must update service status correctly
""" """
payload = {"status": BuildStatusEnum.Success.value} payload = {"status": BuildStatusEnum.Success.value}
post_response = await client.post("/status-api/v1/status", json=payload) post_response = await client.post("/api/v1/status", json=payload)
assert post_response.status == 204 assert post_response.status == 204
response = await client.get("/status-api/v1/status") response = await client.get("/api/v1/status")
status = InternalStatus.from_json(await response.json()) status = InternalStatus.from_json(await response.json())
assert response.ok assert response.ok
@ -59,7 +59,7 @@ async def test_post_exception(client: TestClient) -> None:
""" """
must raise exception on invalid payload must raise exception on invalid payload
""" """
post_response = await client.post("/status-api/v1/status", json={}) post_response = await client.post("/api/v1/status", json={})
assert post_response.status == 400 assert post_response.status == 400
@ -70,5 +70,5 @@ async def test_post_exception_inside(client: TestClient, mocker: MockerFixture)
payload = {"status": BuildStatusEnum.Success.value} payload = {"status": BuildStatusEnum.Success.value}
mocker.patch("ahriman.core.status.watcher.Watcher.update_self", side_effect=Exception()) mocker.patch("ahriman.core.status.watcher.Watcher.update_self", side_effect=Exception())
post_response = await client.post("/status-api/v1/status", json=payload) post_response = await client.post("/api/v1/status", json=payload)
assert post_response.status == 500 assert post_response.status == 500

View File

@ -21,7 +21,7 @@ async def test_get_default_validator(client_with_auth: TestClient) -> None:
""" """
must return 405 in case if no OAuth enabled must return 405 in case if no OAuth enabled
""" """
get_response = await client_with_auth.get("/user-api/v1/login") get_response = await client_with_auth.get("/api/v1/login")
assert get_response.status == 405 assert get_response.status == 405
@ -32,7 +32,7 @@ async def test_get_redirect_to_oauth(client_with_oauth_auth: TestClient) -> None
oauth = client_with_oauth_auth.app["validator"] oauth = client_with_oauth_auth.app["validator"]
oauth.get_oauth_url.return_value = "https://httpbin.org" oauth.get_oauth_url.return_value = "https://httpbin.org"
get_response = await client_with_oauth_auth.get("/user-api/v1/login") get_response = await client_with_oauth_auth.get("/api/v1/login")
assert get_response.ok assert get_response.ok
oauth.get_oauth_url.assert_called_once_with() oauth.get_oauth_url.assert_called_once_with()
@ -44,7 +44,7 @@ async def test_get_redirect_to_oauth_empty_code(client_with_oauth_auth: TestClie
oauth = client_with_oauth_auth.app["validator"] oauth = client_with_oauth_auth.app["validator"]
oauth.get_oauth_url.return_value = "https://httpbin.org" oauth.get_oauth_url.return_value = "https://httpbin.org"
get_response = await client_with_oauth_auth.get("/user-api/v1/login", params={"code": ""}) get_response = await client_with_oauth_auth.get("/api/v1/login", params={"code": ""})
assert get_response.ok assert get_response.ok
oauth.get_oauth_url.assert_called_once_with() oauth.get_oauth_url.assert_called_once_with()
@ -60,7 +60,7 @@ async def test_get(client_with_oauth_auth: TestClient, mocker: MockerFixture) ->
oauth.max_age = 60 oauth.max_age = 60
remember_mock = mocker.patch("aiohttp_security.remember") remember_mock = mocker.patch("aiohttp_security.remember")
get_response = await client_with_oauth_auth.get("/user-api/v1/login", params={"code": "code"}) get_response = await client_with_oauth_auth.get("/api/v1/login", params={"code": "code"})
assert get_response.ok assert get_response.ok
oauth.get_oauth_username.assert_called_once_with("code") oauth.get_oauth_username.assert_called_once_with("code")
@ -78,7 +78,7 @@ async def test_get_unauthorized(client_with_oauth_auth: TestClient, mocker: Mock
oauth.max_age = 60 oauth.max_age = 60
remember_mock = mocker.patch("aiohttp_security.remember") remember_mock = mocker.patch("aiohttp_security.remember")
get_response = await client_with_oauth_auth.get("/user-api/v1/login", params={"code": "code"}) get_response = await client_with_oauth_auth.get("/api/v1/login", params={"code": "code"})
assert get_response.status == 401 assert get_response.status == 401
remember_mock.assert_not_called() remember_mock.assert_not_called()
@ -91,10 +91,10 @@ async def test_post(client_with_auth: TestClient, user: User, mocker: MockerFixt
payload = {"username": user.username, "password": user.password} payload = {"username": user.username, "password": user.password}
remember_mock = mocker.patch("aiohttp_security.remember") remember_mock = mocker.patch("aiohttp_security.remember")
post_response = await client_with_auth.post("/user-api/v1/login", json=payload) post_response = await client_with_auth.post("/api/v1/login", json=payload)
assert post_response.ok assert post_response.ok
post_response = await client_with_auth.post("/user-api/v1/login", data=payload) post_response = await client_with_auth.post("/api/v1/login", data=payload)
assert post_response.ok assert post_response.ok
remember_mock.assert_called() remember_mock.assert_called()
@ -105,7 +105,7 @@ async def test_post_skip(client: TestClient, user: User) -> None:
must process if no auth configured must process if no auth configured
""" """
payload = {"username": user.username, "password": user.password} payload = {"username": user.username, "password": user.password}
post_response = await client.post("/user-api/v1/login", json=payload) post_response = await client.post("/api/v1/login", json=payload)
assert post_response.ok assert post_response.ok
@ -116,6 +116,6 @@ async def test_post_unauthorized(client_with_auth: TestClient, user: User, mocke
payload = {"username": user.username, "password": ""} payload = {"username": user.username, "password": ""}
remember_mock = mocker.patch("aiohttp_security.remember") remember_mock = mocker.patch("aiohttp_security.remember")
post_response = await client_with_auth.post("/user-api/v1/login", json=payload) post_response = await client_with_auth.post("/api/v1/login", json=payload)
assert post_response.status == 401 assert post_response.status == 401
remember_mock.assert_not_called() remember_mock.assert_not_called()

View File

@ -24,7 +24,7 @@ async def test_post(client_with_auth: TestClient, mocker: MockerFixture) -> None
mocker.patch("aiohttp_security.check_authorized") mocker.patch("aiohttp_security.check_authorized")
forget_mock = mocker.patch("aiohttp_security.forget") forget_mock = mocker.patch("aiohttp_security.forget")
post_response = await client_with_auth.post("/user-api/v1/logout") post_response = await client_with_auth.post("/api/v1/logout")
assert post_response.ok assert post_response.ok
forget_mock.assert_called_once_with(pytest.helpers.anyvar(int), pytest.helpers.anyvar(int)) forget_mock.assert_called_once_with(pytest.helpers.anyvar(int), pytest.helpers.anyvar(int))
@ -36,7 +36,7 @@ async def test_post_unauthorized(client_with_auth: TestClient, mocker: MockerFix
mocker.patch("aiohttp_security.check_authorized", side_effect=HTTPUnauthorized()) mocker.patch("aiohttp_security.check_authorized", side_effect=HTTPUnauthorized())
forget_mock = mocker.patch("aiohttp_security.forget") forget_mock = mocker.patch("aiohttp_security.forget")
post_response = await client_with_auth.post("/user-api/v1/logout") post_response = await client_with_auth.post("/api/v1/logout")
assert post_response.status == 401 assert post_response.status == 401
forget_mock.assert_not_called() forget_mock.assert_not_called()
@ -45,5 +45,5 @@ async def test_post_disabled(client: TestClient) -> None:
""" """
must raise exception if auth is disabled must raise exception if auth is disabled
""" """
post_response = await client.post("/user-api/v1/logout") post_response = await client.post("/api/v1/logout")
assert post_response.ok assert post_response.ok