From 3a9b01632017da51a05cc14c53578942b8e38b83 Mon Sep 17 00:00:00 2001 From: Tim Van Baak Date: Thu, 2 Sep 2021 18:43:04 -0700 Subject: [PATCH] Add uuid werkzeug route converter for character routes --- amanuensis/server/__init__.py | 4 ++++ amanuensis/server/helpers.py | 20 ++++++++++++++++++- .../server/lexicon/characters/__init__.py | 11 +++------- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/amanuensis/server/__init__.py b/amanuensis/server/__init__.py index 8f5c842..f0f4d2f 100644 --- a/amanuensis/server/__init__.py +++ b/amanuensis/server/__init__.py @@ -9,6 +9,7 @@ from amanuensis.config import AmanuensisConfig, CommandLineConfig from amanuensis.db import DbContext from amanuensis.parser import filesafe_title import amanuensis.server.auth as auth +from amanuensis.server.helpers import UuidConverter import amanuensis.server.home as home import amanuensis.server.lexicon as lexicon @@ -75,6 +76,9 @@ def get_app( app.template_filter("articlelink")(article_link) app.context_processor(include_backend) + # Set up uuid route converter + app.url_map.converters["uuid"] = UuidConverter + # Set up Flask-Login auth.get_login_manager().init_app(app) diff --git a/amanuensis/server/helpers.py b/amanuensis/server/helpers.py index 434dcb3..06dc06c 100644 --- a/amanuensis/server/helpers.py +++ b/amanuensis/server/helpers.py @@ -1,13 +1,31 @@ from functools import wraps -from typing import Optional +from typing import Optional, Any +from uuid import UUID from flask import g, flash, redirect, url_for from flask_login import current_user +from werkzeug.routing import BaseConverter, ValidationError from amanuensis.backend import lexiq, memq from amanuensis.db import DbContext, Lexicon, User, Membership +class UuidConverter(BaseConverter): + """Converter that matches version 4 UUIDs""" + regex = r"[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-4[0-9A-Fa-f]{3}-[89aAbB][0-9A-Fa-f]{3}-[0-9A-Fa-f]{12}" + + def to_python(self, value: str) -> Any: + try: + return UUID(value) + except: + return ValidationError(f"Invalid UUID: {value}") + + def to_url(self, value: Any) -> str: + if not isinstance(value, UUID): + raise ValueError(f"Expected UUID, got {type(value)}: {value}") + return str(value) + + def lexicon_param(route): """ Wrapper for loading a route's lexicon to `g`. diff --git a/amanuensis/server/lexicon/characters/__init__.py b/amanuensis/server/lexicon/characters/__init__.py index c2b5c86..000aa68 100644 --- a/amanuensis/server/lexicon/characters/__init__.py +++ b/amanuensis/server/lexicon/characters/__init__.py @@ -22,16 +22,11 @@ def list(name): return render_template("characters.jinja", name=name) -@bp.route("/edit/", methods=["GET", "POST"]) +@bp.route("/edit/", methods=["GET", "POST"]) @lexicon_param @player_required -def edit(name, character_id): - try: - char_uuid = uuid.UUID(character_id) - except: - flash("Character not found") - return redirect(url_for("lexicon.characters.list", name=name)) - character: Optional[Character] = charq.try_from_public_id(g.db, char_uuid) +def edit(name, character_id: uuid.UUID): + character: Optional[Character] = charq.try_from_public_id(g.db, character_id) if not character: flash("Character not found") return redirect(url_for("lexicon.characters.list", name=name))