mirror of
https://gitlab.archlinux.org/archlinux/aurweb.git
synced 2025-02-03 10:43:03 +01:00
feat(FastAPI): add /packages (post)
The POST /packages route takes an `action`, `merge_into` and `confirm` form data arguments. It then routes over to `action`'s callback provided by `PACKAGE_ACTIONS`. This commit does not implement actions, but mocks out the flow we would expect from the POST route. Signed-off-by: Kevin Morris <kevr@0cost.org>
This commit is contained in:
parent
927f5e8567
commit
c588a4e82e
3 changed files with 94 additions and 7 deletions
|
@ -23,7 +23,8 @@ from aurweb.templates import make_context, render_raw_template, render_template
|
||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
async def packages_get(request: Request, context: Dict[str, Any]):
|
async def packages_get(request: Request, context: Dict[str, Any],
|
||||||
|
status_code: HTTPStatus = HTTPStatus.OK):
|
||||||
# Query parameters used in this request.
|
# Query parameters used in this request.
|
||||||
context["q"] = dict(request.query_params)
|
context["q"] = dict(request.query_params)
|
||||||
|
|
||||||
|
@ -94,7 +95,8 @@ async def packages_get(request: Request, context: Dict[str, Any]):
|
||||||
context.get("packages"), request.user)
|
context.get("packages"), request.user)
|
||||||
context["packages_count"] = search.total_count
|
context["packages_count"] = search.total_count
|
||||||
|
|
||||||
return render_template(request, "packages.html", context)
|
return render_template(request, "packages.html", context,
|
||||||
|
status_code=status_code)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/packages")
|
@router.get("/packages")
|
||||||
|
@ -1024,3 +1026,42 @@ async def pkgbase_delete_post(request: Request, name: str,
|
||||||
delete_package(request.user, package)
|
delete_package(request.user, package)
|
||||||
|
|
||||||
return RedirectResponse("/packages", status_code=HTTPStatus.SEE_OTHER)
|
return RedirectResponse("/packages", status_code=HTTPStatus.SEE_OTHER)
|
||||||
|
|
||||||
|
# A mapping of action string -> callback functions used within the
|
||||||
|
# `packages_post` route below. We expect any action callback to
|
||||||
|
# return a tuple in the format: (succeeded: bool, message: List[str]).
|
||||||
|
PACKAGE_ACTIONS = {}
|
||||||
|
|
||||||
|
|
||||||
|
@router.post("/packages")
|
||||||
|
@auth_required(redirect="/packages")
|
||||||
|
async def packages_post(request: Request,
|
||||||
|
IDs: List[int] = Form(default=[]),
|
||||||
|
action: str = Form(default=str()),
|
||||||
|
merge_into: str = Form(default=str()),
|
||||||
|
confirm: bool = Form(default=False)):
|
||||||
|
|
||||||
|
# If an invalid action is specified, just render GET /packages
|
||||||
|
# with an BAD_REQUEST status_code.
|
||||||
|
if action not in PACKAGE_ACTIONS:
|
||||||
|
context = make_context(request, "Packages")
|
||||||
|
return await packages_get(request, context, HTTPStatus.BAD_REQUEST)
|
||||||
|
|
||||||
|
context = make_context(request, "Packages")
|
||||||
|
|
||||||
|
# We deal with `IDs`, `merge_into` and `confirm` arguments
|
||||||
|
# within action callbacks.
|
||||||
|
callback = PACKAGE_ACTIONS.get(action)
|
||||||
|
retval = await callback(request, package_ids=IDs, merge_into=merge_into,
|
||||||
|
confirm=confirm)
|
||||||
|
if retval: # If *anything* was returned:
|
||||||
|
success, messages = retval
|
||||||
|
if not success:
|
||||||
|
# If the first element was False:
|
||||||
|
context["errors"] = messages
|
||||||
|
return await packages_get(request, context, HTTPStatus.BAD_REQUEST)
|
||||||
|
else:
|
||||||
|
# Otherwise:
|
||||||
|
context["success"] = messages
|
||||||
|
|
||||||
|
return await packages_get(request, context)
|
||||||
|
|
|
@ -55,11 +55,10 @@
|
||||||
{% include "partials/widgets/pager.html" %}
|
{% include "partials/widgets/pager.html" %}
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
{# Package action form #}
|
{# Package action form: persists query parameters. #}
|
||||||
<form id="pkglist-results-form"
|
<form id="pkglist-results-form"
|
||||||
action="/packages/?{{ q | urlencode }}"
|
action="/packages?{{ q | urlencode }}" method="post"
|
||||||
method="post">
|
>
|
||||||
|
|
||||||
{# Search results #}
|
{# Search results #}
|
||||||
{% with voted = packages_voted, notified = packages_notified %}
|
{% with voted = packages_voted, notified = packages_notified %}
|
||||||
{% include "partials/packages/search_results.html" %}
|
{% include "partials/packages/search_results.html" %}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import re
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from http import HTTPStatus
|
from http import HTTPStatus
|
||||||
from typing import List
|
from typing import List
|
||||||
|
from unittest import mock
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
@ -26,7 +27,7 @@ from aurweb.models.relation_type import PROVIDES_ID, RelationType
|
||||||
from aurweb.models.request_type import DELETION_ID, RequestType
|
from aurweb.models.request_type import DELETION_ID, RequestType
|
||||||
from aurweb.models.user import User
|
from aurweb.models.user import User
|
||||||
from aurweb.testing import setup_test_db
|
from aurweb.testing import setup_test_db
|
||||||
from aurweb.testing.html import parse_root
|
from aurweb.testing.html import get_errors, get_successes, parse_root
|
||||||
from aurweb.testing.requests import Request
|
from aurweb.testing.requests import Request
|
||||||
|
|
||||||
|
|
||||||
|
@ -1987,3 +1988,49 @@ def test_pkgbase_delete(client: TestClient, tu_user: User, package: Package):
|
||||||
PackageBase.Name == pkgbase.Name
|
PackageBase.Name == pkgbase.Name
|
||||||
).first()
|
).first()
|
||||||
assert record is None
|
assert record is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_packages_post_unknown_action(client: TestClient, user: User,
|
||||||
|
package: Package):
|
||||||
|
|
||||||
|
cookies = {"AURSID": user.login(Request(), "testPassword")}
|
||||||
|
with client as request:
|
||||||
|
resp = request.post("/packages", data={"action": "unknown"},
|
||||||
|
cookies=cookies, allow_redirects=False)
|
||||||
|
assert resp.status_code == int(HTTPStatus.BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
def test_packages_post_error(client: TestClient, user: User, package: Package):
|
||||||
|
|
||||||
|
async def stub_action(request: Request, **kwargs):
|
||||||
|
return (False, ["Some error."])
|
||||||
|
|
||||||
|
actions = {"stub": stub_action}
|
||||||
|
with mock.patch.dict("aurweb.routers.packages.PACKAGE_ACTIONS", actions):
|
||||||
|
cookies = {"AURSID": user.login(Request(), "testPassword")}
|
||||||
|
with client as request:
|
||||||
|
resp = request.post("/packages", data={"action": "stub"},
|
||||||
|
cookies=cookies, allow_redirects=False)
|
||||||
|
assert resp.status_code == int(HTTPStatus.BAD_REQUEST)
|
||||||
|
|
||||||
|
errors = get_errors(resp.text)
|
||||||
|
expected = "Some error."
|
||||||
|
assert errors[0].text.strip() == expected
|
||||||
|
|
||||||
|
|
||||||
|
def test_packages_post(client: TestClient, user: User, package: Package):
|
||||||
|
|
||||||
|
async def stub_action(request: Request, **kwargs):
|
||||||
|
return (True, ["Some success."])
|
||||||
|
|
||||||
|
actions = {"stub": stub_action}
|
||||||
|
with mock.patch.dict("aurweb.routers.packages.PACKAGE_ACTIONS", actions):
|
||||||
|
cookies = {"AURSID": user.login(Request(), "testPassword")}
|
||||||
|
with client as request:
|
||||||
|
resp = request.post("/packages", data={"action": "stub"},
|
||||||
|
cookies=cookies, allow_redirects=False)
|
||||||
|
assert resp.status_code == int(HTTPStatus.OK)
|
||||||
|
|
||||||
|
errors = get_successes(resp.text)
|
||||||
|
expected = "Some success."
|
||||||
|
assert errors[0].text.strip() == expected
|
||||||
|
|
Loading…
Add table
Reference in a new issue