This is an automated email from the ASF dual-hosted git repository.
sbp pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tooling-trusted-releases.git
The following commit(s) were added to refs/heads/main by this push:
new fcc034b Make the form to generate an announcement preview more type
safe
fcc034b is described below
commit fcc034b391e279071b4b75bb6590e62e44c6b782
Author: Sean B. Palmer <[email protected]>
AuthorDate: Tue Nov 11 16:51:33 2025 +0000
Make the form to generate an announcement preview more type safe
---
atr/post/preview.py | 79 +++++++++++++----------------------------------------
1 file changed, 19 insertions(+), 60 deletions(-)
diff --git a/atr/post/preview.py b/atr/post/preview.py
index d036a23..f956806 100644
--- a/atr/post/preview.py
+++ b/atr/post/preview.py
@@ -15,46 +15,35 @@
# specific language governing permissions and limitations
# under the License.
-import quart
+import pydantic
import atr.blueprints.post as post
import atr.construct as construct
-import atr.forms as forms
+import atr.form as form
import atr.log as log
-import atr.models.sql as sql
-import atr.storage as storage
import atr.web as web
-class AnnouncePreviewForm(forms.Typed):
- """Form for validating preview request data."""
-
- subject = forms.optional("Subject")
- body = forms.textarea("Body")
-
-
-class DeleteForm(forms.Typed):
- """Form for deleting a release preview."""
-
- release_name = forms.hidden()
- project_name = forms.hidden()
- version_name = forms.hidden()
- confirm_delete = forms.string("Confirmation",
validators=forms.constant("DELETE"))
- submit = forms.submit("Delete preview")
+class AnnouncePreviewForm(form.Form):
+ body: str = form.label("Body", widget=form.Widget.TEXTAREA)
@post.committer("/preview/announce/<project_name>/<version_name>")
-async def announce_preview(session: web.Committer, project_name: str,
version_name: str) -> web.QuartResponse | str:
- """Generate a preview of the announcement email body."""
+async def announce_preview(session: web.Committer, project_name: str,
version_name: str) -> web.QuartResponse:
+ """Generate a preview of the announcement email body from JavaScript."""
- # TODO: Where does this come from? A static template?
- form = await AnnouncePreviewForm.create_form(data=await quart.request.form)
- if not await form.validate_on_submit():
- error_message = "Invalid preview request"
- if form.errors:
- error_details = "; ".join([f"{field}: {', '.join(errs)}" for
field, errs in form.errors.items()])
- error_message = f"{error_message}: {error_details}"
- return web.TextResponse(f"Error: {error_message}", status=400)
+ form_data = await form.quart_request()
+
+ try:
+ # Because this is requested from JavaScript, we validate manually
+ # Otherwise errors redirect back to a page which does not exist
+ validated_form = form.validate(AnnouncePreviewForm, form_data)
+ if not isinstance(validated_form, AnnouncePreviewForm):
+ raise ValueError("Invalid form data")
+ except pydantic.ValidationError as e:
+ errors = e.errors()
+ error_details = "; ".join([f"{err['loc'][0]}: {err['msg']}" for err in
errors])
+ return web.TextResponse(f"Error: Invalid preview request:
{error_details}", status=400)
try:
# Construct options and generate body
@@ -64,40 +53,10 @@ async def announce_preview(session: web.Committer,
project_name: str, version_na
project_name=project_name,
version_name=version_name,
)
- preview_body = await
construct.announce_release_body(str(form.body.data), options)
+ preview_body = await
construct.announce_release_body(validated_form.body, options)
return web.TextResponse(preview_body)
except Exception as e:
log.exception("Error generating announcement preview:")
return web.TextResponse(f"Error generating preview: {e!s}", status=500)
-
-
[email protected]("/preview/delete")
-async def delete(session: web.Committer) -> web.WerkzeugResponse:
- """Delete a preview and all its associated files."""
- import atr.get.root as root
-
- # TODO: Where does this come from? A static template?
- form = await DeleteForm.create_form(data=await quart.request.form)
-
- if not await form.validate_on_submit():
- for _field, errors in form.errors.items():
- for error in errors:
- await quart.flash(f"{error}", "error")
- return await session.redirect(root.index)
-
- release_name = form.release_name.data
- project_name = form.project_name.data
- version_name = form.version_name.data
- if not (release_name and project_name and version_name):
- return await session.redirect(root.index, error="Missing required
parameters")
-
- # Check that the user has access to the project
- async with storage.write(session) as write:
- wacp = await write.as_project_committee_participant(project_name)
- await wacp.release.delete(
- project_name, version_name,
phase=sql.ReleasePhase.RELEASE_PREVIEW, include_downloads=False
- )
-
- return await session.redirect(root.index, success="Preview deleted
successfully")
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]