mirror of
https://gitlab.archlinux.org/archlinux/aurweb.git
synced 2025-02-03 10:43:03 +01:00
fix(fastapi): utilize auto_{orphan,deletion}_age
Didn't get this in when the initial request port went down; here it is. Auto-accept orphan requests when the package has been out of date for longer than auto_orphan_age. Auto-accept deletion requests by the package's maintainer if the package has been uploaded within auto_deletion_age seconds ago. Signed-off-by: Kevin Morris <kevr@0cost.org>
This commit is contained in:
parent
a348cdaac3
commit
7f981b9ed7
3 changed files with 109 additions and 33 deletions
34
aurweb/packages/validate.py
Normal file
34
aurweb/packages/validate.py
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
|
from aurweb import db, models
|
||||||
|
from aurweb.exceptions import ValidationError
|
||||||
|
|
||||||
|
|
||||||
|
def request(pkgbase: models.PackageBase,
|
||||||
|
type: str, comments: str, merge_into: str,
|
||||||
|
context: Dict[str, Any]) -> None:
|
||||||
|
if not comments:
|
||||||
|
raise ValidationError(["The comment field must not be empty."])
|
||||||
|
|
||||||
|
if type == "merge":
|
||||||
|
# Perform merge-related checks.
|
||||||
|
if not merge_into:
|
||||||
|
# TODO: This error needs to be translated.
|
||||||
|
raise ValidationError(
|
||||||
|
['The "Merge into" field must not be empty.'])
|
||||||
|
|
||||||
|
target = db.query(models.PackageBase).filter(
|
||||||
|
models.PackageBase.Name == merge_into
|
||||||
|
).first()
|
||||||
|
if not target:
|
||||||
|
# TODO: This error needs to be translated.
|
||||||
|
raise ValidationError([
|
||||||
|
"The package base you want to merge into does not exist."
|
||||||
|
])
|
||||||
|
|
||||||
|
db.refresh(target)
|
||||||
|
if target.ID == pkgbase.ID:
|
||||||
|
# TODO: This error needs to be translated.
|
||||||
|
raise ValidationError([
|
||||||
|
"You cannot merge a package base into itself."
|
||||||
|
])
|
|
@ -11,9 +11,11 @@ import aurweb.packages.util
|
||||||
|
|
||||||
from aurweb import db, defaults, l10n, logging, models, util
|
from aurweb import db, defaults, l10n, logging, models, util
|
||||||
from aurweb.auth import auth_required
|
from aurweb.auth import auth_required
|
||||||
|
from aurweb.exceptions import ValidationError
|
||||||
from aurweb.models.package_request import ACCEPTED_ID, PENDING_ID, REJECTED_ID
|
from aurweb.models.package_request import ACCEPTED_ID, PENDING_ID, REJECTED_ID
|
||||||
from aurweb.models.relation_type import CONFLICTS_ID, PROVIDES_ID, REPLACES_ID
|
from aurweb.models.relation_type import CONFLICTS_ID, PROVIDES_ID, REPLACES_ID
|
||||||
from aurweb.models.request_type import DELETION_ID, MERGE, MERGE_ID
|
from aurweb.models.request_type import DELETION_ID, MERGE, MERGE_ID
|
||||||
|
from aurweb.packages import validate
|
||||||
from aurweb.packages.search import PackageSearch
|
from aurweb.packages.search import PackageSearch
|
||||||
from aurweb.packages.util import get_pkg_or_base, get_pkgbase_comment, get_pkgreq_by_id, query_notified, query_voted
|
from aurweb.packages.util import get_pkg_or_base, get_pkgbase_comment, get_pkgreq_by_id, query_notified, query_voted
|
||||||
from aurweb.scripts import notify, popupdate
|
from aurweb.scripts import notify, popupdate
|
||||||
|
@ -153,7 +155,7 @@ def delete_package(deleter: models.User, package: models.Package):
|
||||||
with db.begin():
|
with db.begin():
|
||||||
pkgreq = create_request_if_missing(
|
pkgreq = create_request_if_missing(
|
||||||
requests, reqtype, deleter, package)
|
requests, reqtype, deleter, package)
|
||||||
db.refresh(pkgreq)
|
pkgreq.Status = ACCEPTED_ID
|
||||||
|
|
||||||
bases_to_delete.append(package.PackageBase)
|
bases_to_delete.append(package.PackageBase)
|
||||||
|
|
||||||
|
@ -707,35 +709,13 @@ async def pkgbase_request_post(request: Request, name: str,
|
||||||
return render_template(request, "pkgbase/request.html", context,
|
return render_template(request, "pkgbase/request.html", context,
|
||||||
status_code=HTTPStatus.BAD_REQUEST)
|
status_code=HTTPStatus.BAD_REQUEST)
|
||||||
|
|
||||||
if not comments:
|
try:
|
||||||
context["errors"] = ["The comment field must not be empty."]
|
validate.request(pkgbase, type, comments, merge_into, context)
|
||||||
|
except ValidationError as exc:
|
||||||
|
logger.error(f"Request Validation Error: {str(exc.data)}")
|
||||||
|
context["errors"] = exc.data
|
||||||
return render_template(request, "pkgbase/request.html", context)
|
return render_template(request, "pkgbase/request.html", context)
|
||||||
|
|
||||||
if type == "merge":
|
|
||||||
# Perform merge-related checks.
|
|
||||||
if not merge_into:
|
|
||||||
# TODO: This error needs to be translated.
|
|
||||||
context["errors"] = ['The "Merge into" field must not be empty.']
|
|
||||||
return render_template(request, "pkgbase/request.html", context)
|
|
||||||
|
|
||||||
target = db.query(models.PackageBase).filter(
|
|
||||||
models.PackageBase.Name == merge_into
|
|
||||||
).first()
|
|
||||||
if not target:
|
|
||||||
# TODO: This error needs to be translated.
|
|
||||||
context["errors"] = [
|
|
||||||
"The package base you want to merge into does not exist."
|
|
||||||
]
|
|
||||||
return render_template(request, "pkgbase/request.html", context)
|
|
||||||
|
|
||||||
db.refresh(target)
|
|
||||||
if target.ID == pkgbase.ID:
|
|
||||||
# TODO: This error needs to be translated.
|
|
||||||
context["errors"] = [
|
|
||||||
"You cannot merge a package base into itself."
|
|
||||||
]
|
|
||||||
return render_template(request, "pkgbase/request.html", context)
|
|
||||||
|
|
||||||
# All good. Create a new PackageRequest based on the given type.
|
# All good. Create a new PackageRequest based on the given type.
|
||||||
now = int(datetime.utcnow().timestamp())
|
now = int(datetime.utcnow().timestamp())
|
||||||
reqtype = db.query(models.RequestType).filter(
|
reqtype = db.query(models.RequestType).filter(
|
||||||
|
@ -748,16 +728,37 @@ async def pkgbase_request_post(request: Request, name: str,
|
||||||
PackageBase=pkgbase,
|
PackageBase=pkgbase,
|
||||||
PackageBaseName=pkgbase.Name,
|
PackageBaseName=pkgbase.Name,
|
||||||
MergeBaseName=merge_into,
|
MergeBaseName=merge_into,
|
||||||
Comments=comments, ClosureComment=str())
|
Comments=comments,
|
||||||
|
ClosureComment=str())
|
||||||
|
|
||||||
# Prepare notification object.
|
|
||||||
conn = db.ConnectionExecutor(db.get_engine().raw_connection())
|
conn = db.ConnectionExecutor(db.get_engine().raw_connection())
|
||||||
notify_ = notify.RequestOpenNotification(
|
# Prepare notification object.
|
||||||
|
notif = notify.RequestOpenNotification(
|
||||||
conn, request.user.ID, pkgreq.ID, reqtype.Name,
|
conn, request.user.ID, pkgreq.ID, reqtype.Name,
|
||||||
pkgreq.PackageBase.ID, merge_into=merge_into or None)
|
pkgreq.PackageBase.ID, merge_into=merge_into or None)
|
||||||
|
|
||||||
# Send the notification now that we're out of the DB scope.
|
# Send the notification now that we're out of the DB scope.
|
||||||
notify_.send()
|
notif.send()
|
||||||
|
|
||||||
|
auto_orphan_age = aurweb.config.getint("options", "auto_orphan_age")
|
||||||
|
auto_delete_age = aurweb.config.getint("options", "auto_delete_age")
|
||||||
|
|
||||||
|
flagged = pkgbase.OutOfDateTS and pkgbase.OutOfDateTS >= auto_orphan_age
|
||||||
|
is_maintainer = pkgbase.Maintainer == request.user
|
||||||
|
outdated = now - pkgbase.SubmittedTS <= auto_delete_age
|
||||||
|
|
||||||
|
if type == "orphan" and flagged:
|
||||||
|
with db.begin():
|
||||||
|
pkgbase.Maintainer = None
|
||||||
|
pkgreq.Status = ACCEPTED_ID
|
||||||
|
db.refresh(pkgreq)
|
||||||
|
notif = notify.RequestCloseNotification(
|
||||||
|
conn, request.user.ID, pkgreq.ID, pkgreq.status_display())
|
||||||
|
notif.send()
|
||||||
|
elif type == "deletion" and is_maintainer and outdated:
|
||||||
|
packages = pkgbase.packages.all()
|
||||||
|
for package in packages:
|
||||||
|
delete_package(request.user, package)
|
||||||
|
|
||||||
# Redirect the submitting user to /packages.
|
# Redirect the submitting user to /packages.
|
||||||
return RedirectResponse("/packages",
|
return RedirectResponse("/packages",
|
||||||
|
|
|
@ -10,7 +10,7 @@ import pytest
|
||||||
from fastapi.testclient import TestClient
|
from fastapi.testclient import TestClient
|
||||||
from sqlalchemy import and_
|
from sqlalchemy import and_
|
||||||
|
|
||||||
from aurweb import asgi, db, defaults
|
from aurweb import asgi, config, db, defaults
|
||||||
from aurweb.models import License, PackageLicense
|
from aurweb.models import License, PackageLicense
|
||||||
from aurweb.models.account_type import USER_ID, AccountType
|
from aurweb.models.account_type import USER_ID, AccountType
|
||||||
from aurweb.models.dependency_type import DependencyType
|
from aurweb.models.dependency_type import DependencyType
|
||||||
|
@ -1536,6 +1536,24 @@ def test_pkgbase_request_post_deletion(client: TestClient, user: User,
|
||||||
assert pkgreq.Comments == "We want to delete this."
|
assert pkgreq.Comments == "We want to delete this."
|
||||||
|
|
||||||
|
|
||||||
|
def test_pkgbase_request_post_maintainer_deletion(
|
||||||
|
client: TestClient, maintainer: User, package: Package):
|
||||||
|
pkgbasename = package.PackageBase.Name
|
||||||
|
endpoint = f"/pkgbase/{package.PackageBase.Name}/request"
|
||||||
|
cookies = {"AURSID": maintainer.login(Request(), "testPassword")}
|
||||||
|
with client as request:
|
||||||
|
resp = request.post(endpoint, data={
|
||||||
|
"type": "deletion",
|
||||||
|
"comments": "We want to delete this."
|
||||||
|
}, cookies=cookies, allow_redirects=False)
|
||||||
|
assert resp.status_code == int(HTTPStatus.SEE_OTHER)
|
||||||
|
|
||||||
|
pkgreq = db.query(PackageRequest).filter(
|
||||||
|
PackageRequest.PackageBaseName == pkgbasename
|
||||||
|
).first()
|
||||||
|
assert pkgreq.Status == ACCEPTED_ID
|
||||||
|
|
||||||
|
|
||||||
def test_pkgbase_request_post_orphan(client: TestClient, user: User,
|
def test_pkgbase_request_post_orphan(client: TestClient, user: User,
|
||||||
package: Package):
|
package: Package):
|
||||||
endpoint = f"/pkgbase/{package.PackageBase.Name}/request"
|
endpoint = f"/pkgbase/{package.PackageBase.Name}/request"
|
||||||
|
@ -1556,6 +1574,29 @@ def test_pkgbase_request_post_orphan(client: TestClient, user: User,
|
||||||
assert pkgreq.Comments == "We want to disown this."
|
assert pkgreq.Comments == "We want to disown this."
|
||||||
|
|
||||||
|
|
||||||
|
def test_pkgbase_request_post_auto_orphan(client: TestClient, user: User,
|
||||||
|
package: Package):
|
||||||
|
now = int(datetime.utcnow().timestamp())
|
||||||
|
auto_orphan_age = config.getint("options", "auto_orphan_age")
|
||||||
|
with db.begin():
|
||||||
|
package.PackageBase.OutOfDateTS = now - auto_orphan_age - 1
|
||||||
|
|
||||||
|
endpoint = f"/pkgbase/{package.PackageBase.Name}/request"
|
||||||
|
cookies = {"AURSID": user.login(Request(), "testPassword")}
|
||||||
|
with client as request:
|
||||||
|
resp = request.post(endpoint, data={
|
||||||
|
"type": "orphan",
|
||||||
|
"comments": "We want to disown this."
|
||||||
|
}, cookies=cookies, allow_redirects=False)
|
||||||
|
assert resp.status_code == int(HTTPStatus.SEE_OTHER)
|
||||||
|
|
||||||
|
pkgreq = db.query(PackageRequest).filter(
|
||||||
|
PackageRequest.PackageBaseID == package.PackageBase.ID
|
||||||
|
).first()
|
||||||
|
assert pkgreq is not None
|
||||||
|
assert pkgreq.Status == ACCEPTED_ID
|
||||||
|
|
||||||
|
|
||||||
def test_pkgbase_request_post_merge(client: TestClient, user: User,
|
def test_pkgbase_request_post_merge(client: TestClient, user: User,
|
||||||
package: Package):
|
package: Package):
|
||||||
with db.begin():
|
with db.begin():
|
||||||
|
|
Loading…
Add table
Reference in a new issue