feat(FastAPI): add pkgbase comments (new, edit)

In PHP, this was implemented using an /rpc type 'get-comment-form'.
With FastAPI, we've decided to reorganize this into a non-RPC route:
`/pkgbase/{name}/comments/{id}/form`, rendered via the new
`templates/partials/packages/comment_form.html` template.

When the comment_form.html template is provided a `comment` object,
it will produce an edit comment form. Otherwise, it will produce a new
comment form.

A few new FastAPI routes have been introduced:

- GET `/pkgbase/{name}/comments/{id}/form`
    - Produces a JSON response based on {"form": "<form_markup>"}.
- POST `/pkgbase/{name}/comments'
    - Creates a new comment.
- POST `/pkgbase/{name}/comments/{id}`
    - Edits an existing comment.

In addition, some Javascript has been modified for our new routes.

Signed-off-by: Kevin Morris <kevr@0cost.org>
This commit is contained in:
Kevin Morris 2021-09-27 18:46:20 -07:00
parent 0d8216e8ea
commit fc28aad245
No known key found for this signature in database
GPG key ID: F7E46DED420788F3
6 changed files with 333 additions and 79 deletions

View file

@ -1,8 +1,9 @@
from datetime import datetime
from http import HTTPStatus
from typing import Any, Dict
from fastapi import APIRouter, Request, Response
from fastapi.responses import RedirectResponse
from fastapi import APIRouter, Form, HTTPException, Request, Response
from fastapi.responses import JSONResponse, RedirectResponse
from sqlalchemy import and_
import aurweb.filters
@ -11,9 +12,11 @@ import aurweb.models.package_keyword
import aurweb.packages.util
from aurweb import db
from aurweb.auth import auth_required
from aurweb.models.license import License
from aurweb.models.package import Package
from aurweb.models.package_base import PackageBase
from aurweb.models.package_comment import PackageComment
from aurweb.models.package_dependency import PackageDependency
from aurweb.models.package_license import PackageLicense
from aurweb.models.package_notification import PackageNotification
@ -23,8 +26,9 @@ from aurweb.models.package_source import PackageSource
from aurweb.models.package_vote import PackageVote
from aurweb.models.relation_type import CONFLICTS_ID
from aurweb.packages.search import PackageSearch
from aurweb.packages.util import get_pkg_or_base, query_notified, query_voted
from aurweb.templates import make_context, render_template
from aurweb.packages.util import get_pkg_or_base, get_pkgbase_comment, query_notified, query_voted
from aurweb.scripts.rendercomment import update_comment_render
from aurweb.templates import make_context, render_raw_template, render_template
router = APIRouter()
@ -124,7 +128,9 @@ async def make_single_context(request: Request,
context["pkgbase"] = pkgbase
context["packages_count"] = pkgbase.packages.count()
context["keywords"] = pkgbase.keywords
context["comments"] = pkgbase.comments
context["comments"] = pkgbase.comments.order_by(
PackageComment.CommentTS.desc()
)
context["is_maintainer"] = (request.user.is_authenticated()
and request.user.ID == pkgbase.MaintainerUID)
context["notified"] = request.user.notified(pkgbase)
@ -201,7 +207,94 @@ async def package_base(request: Request, name: str) -> Response:
@router.get("/pkgbase/{name}/voters")
async def package_base_voters(request: Request, name: str) -> Response:
# Get the PackageBase.
pkgbase = get_pkgbase(name)
pkgbase = get_pkg_or_base(name, PackageBase)
context = make_context(request, "Voters")
context["pkgbase"] = pkgbase
return render_template(request, "pkgbase/voters.html", context)
@router.post("/pkgbase/{name}/comments")
@auth_required(True)
async def pkgbase_comments_post(
request: Request, name: str,
comment: str = Form(default=str()),
enable_notifications: bool = Form(default=False)):
""" Add a new comment. """
pkgbase = get_pkg_or_base(name, PackageBase)
if not comment:
raise HTTPException(status_code=int(HTTPStatus.EXPECTATION_FAILED))
# If the provided comment is different than the record's version,
# update the db record.
now = int(datetime.utcnow().timestamp())
with db.begin():
comment = db.create(PackageComment, User=request.user,
PackageBase=pkgbase,
Comments=comment, RenderedComment=str(),
CommentTS=now)
if enable_notifications and not request.user.notified(pkgbase):
db.create(PackageNotification,
User=request.user,
PackageBase=pkgbase)
update_comment_render(comment.ID)
# Redirect to the pkgbase page.
return RedirectResponse(f"/pkgbase/{pkgbase.Name}#comment-{comment.ID}",
status_code=int(HTTPStatus.SEE_OTHER))
@router.get("/pkgbase/{name}/comments/{id}/form")
@auth_required(True)
async def pkgbase_comment_form(request: Request, name: str, id: int):
""" Produce a comment form for comment {id}. """
pkgbase = get_pkg_or_base(name, PackageBase)
comment = pkgbase.comments.filter(PackageComment.ID == id).first()
if not comment:
return JSONResponse({}, status_code=int(HTTPStatus.NOT_FOUND))
if not request.user.is_elevated() and request.user != comment.User:
return JSONResponse({}, status_code=int(HTTPStatus.UNAUTHORIZED))
context = await make_single_context(request, pkgbase)
context["comment"] = comment
form = render_raw_template(
request, "partials/packages/comment_form.html", context)
return JSONResponse({"form": form})
@router.post("/pkgbase/{name}/comments/{id}")
@auth_required(True)
async def pkgbase_comment_post(
request: Request, name: str, id: int,
comment: str = Form(default=str()),
enable_notifications: bool = Form(default=False)):
pkgbase = get_pkg_or_base(name, PackageBase)
db_comment = get_pkgbase_comment(pkgbase, id)
if not comment:
raise HTTPException(status_code=int(HTTPStatus.EXPECTATION_FAILED))
# If the provided comment is different than the record's version,
# update the db record.
now = int(datetime.utcnow().timestamp())
if db_comment.Comments != comment:
with db.begin():
db_comment.Comments = comment
db_comment.Editor = request.user
db_comment.EditedTS = now
db_notif = request.user.notifications.filter(
PackageNotification.PackageBaseID == pkgbase.ID
).first()
if enable_notifications and not db_notif:
db.create(PackageNotification,
User=request.user,
PackageBase=pkgbase)
update_comment_render(db_comment.ID)
# Redirect to the pkgbase page anchored to the updated comment.
return RedirectResponse(f"/pkgbase/{pkgbase.Name}#comment-{db_comment.ID}",
status_code=int(HTTPStatus.SEE_OTHER))