feat(FastAPI): add /pkgbase/{name}/delete (get, post)

In addition, we've had to add cascade arguments to backref so
sqlalchemy treats the relationships as proper cascades.

Furthermore, our pkgbase actions template was not rendering
actions properly based on TU credentials.

Signed-off-by: Kevin Morris <kevr@0cost.org>
This commit is contained in:
Kevin Morris 2021-10-06 13:54:14 -07:00
parent 4e7d2295da
commit d38abd7832
No known key found for this signature in database
GPG key ID: F7E46DED420788F3
3 changed files with 139 additions and 0 deletions

View file

@ -940,3 +940,40 @@ async def pkgbase_disown_post(request: Request, name: str,
disown_pkgbase(pkgbase, request.user)
return RedirectResponse(f"/pkgbase/{name}",
status_code=int(HTTPStatus.SEE_OTHER))
@router.get("/pkgbase/{name}/delete")
@auth_required(True)
async def pkgbase_delete_get(request: Request, name: str):
if not request.user.has_credential("CRED_PKGBASE_DELETE"):
return RedirectResponse(f"/pkgbase/{name}",
status_code=int(HTTPStatus.SEE_OTHER))
context = make_context(request, "Package Deletion")
context["pkgbase"] = get_pkg_or_base(name, PackageBase)
return render_template(request, "packages/delete.html", context)
@router.post("/pkgbase/{name}/delete")
@auth_required(True)
async def pkgbase_delete_post(request: Request, name: str,
confirm: bool = Form(default=False)):
pkgbase = get_pkg_or_base(name, PackageBase)
if not request.user.has_credential("CRED_PKGBASE_DELETE"):
return RedirectResponse(f"/pkgbase/{name}",
status_code=int(HTTPStatus.SEE_OTHER))
if not confirm:
context = make_context(request, "Package Deletion")
context["pkgbase"] = pkgbase
context["errors"] = [("The selected packages have not been deleted, "
"check the confirmation checkbox.")]
return render_template(request, "packages/delete.html", context,
status_code=int(HTTPStatus.EXPECTATION_FAILED))
packages = pkgbase.packages.all()
for package in packages:
delete_package(request.user, package)
return RedirectResponse("/packages", status_code=int(HTTPStatus.SEE_OTHER))

View file

@ -0,0 +1,56 @@
{% extends "partials/layout.html" %}
{% block pageContent %}
{% if errors %}
<ul class="errorlist">
{% for error in errors %}
<li>{{ error | tr }}</li>
{% endfor %}
</ul>
{% endif %}
<div class="box">
<h2>{{ "Delete Package" | tr }}: {{ pkgbase.Name }}</h2>
<p>
{{
"Use this form to delete the package base %s%s%s and "
"the following packages from the AUR: "
| tr | format("<strong>", pkgbase.Name, "</strong>") | safe
}}
</p>
<ul>
{% for package in pkgbase.packages.all() %}
<li>{{ package.Name }}</li>
{% endfor %}
</ul>
<p>
{{
"Deletion of a package is permanent. "
"Select the checkbox to confirm action." | tr
}}
</p>
<form action="/pkgbase/{{ pkgbase.Name }}/delete" method="post">
<fieldset>
<p>
<label class="confirmation">
<input type="checkbox" name="confirm" value="1" />
{{ "Confirm package deletion" | tr }}
</label>
</p>
<p>
<input class="button"
type="submit"
value="{{ 'Delete' | tr }}"
/>
</p>
</fieldset>
</form>
</div>
{% endblock %}

View file

@ -1845,3 +1845,49 @@ def test_pkgbase_disown(client: TestClient, user: User, maintainer: User,
with client as request:
resp = request.post(endpoint, data={"confirm": True}, cookies=cookies)
assert resp.status_code == int(HTTPStatus.SEE_OTHER)
def test_pkgbase_delete_unauthorized(client: TestClient, user: User,
package: Package):
pkgbase = package.PackageBase
cookies = {"AURSID": user.login(Request(), "testPassword")}
endpoint = f"/pkgbase/{pkgbase.Name}/delete"
# Test GET.
with client as request:
resp = request.get(endpoint, cookies=cookies, allow_redirects=False)
assert resp.status_code == int(HTTPStatus.SEE_OTHER)
assert resp.headers.get("location") == f"/pkgbase/{pkgbase.Name}"
# Test POST.
with client as request:
resp = request.post(endpoint, cookies=cookies)
assert resp.status_code == int(HTTPStatus.SEE_OTHER)
assert resp.headers.get("location") == f"/pkgbase/{pkgbase.Name}"
def test_pkgbase_delete(client: TestClient, tu_user: User, package: Package):
pkgbase = package.PackageBase
# Test that the GET request works.
cookies = {"AURSID": tu_user.login(Request(), "testPassword")}
endpoint = f"/pkgbase/{pkgbase.Name}/delete"
with client as request:
resp = request.get(endpoint, cookies=cookies)
assert resp.status_code == int(HTTPStatus.OK)
# Test that POST works and denies us because we haven't confirmed.
with client as request:
resp = request.post(endpoint, cookies=cookies)
assert resp.status_code == int(HTTPStatus.EXPECTATION_FAILED)
# Test that we can actually delete the pkgbase.
with client as request:
resp = request.post(endpoint, data={"confirm": True}, cookies=cookies)
assert resp.status_code == int(HTTPStatus.SEE_OTHER)
# Let's assert that the package base record got removed.
record = db.query(PackageBase).filter(
PackageBase.Name == pkgbase.Name
).first()
assert record is None