feat: path templating for endpoint signatures
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
|
||||
_UUID = re.compile(r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$")
|
||||
_HEX_TOKEN = re.compile(r"^[0-9a-fA-F]{16,}$")
|
||||
_LONG_OPAQUE = re.compile(r"^[A-Za-z0-9_\-]{20,}$")
|
||||
|
||||
|
||||
def _is_variable(segment: str) -> bool:
|
||||
if segment.isdigit():
|
||||
return True
|
||||
if _UUID.match(segment):
|
||||
return True
|
||||
if _HEX_TOKEN.match(segment):
|
||||
return True
|
||||
return bool(_LONG_OPAQUE.match(segment) and any(c.isdigit() for c in segment))
|
||||
|
||||
|
||||
def path_template(path: str) -> str:
|
||||
"""Collapse variable path segments (ids, UUIDs, hashes, opaque tokens) to {id}."""
|
||||
if not path or path == "/":
|
||||
return "/"
|
||||
parts = path.split("/")
|
||||
out = ["{id}" if part and _is_variable(part) else part for part in parts]
|
||||
return "/".join(out)
|
||||
@@ -0,0 +1,23 @@
|
||||
from auto_reverse.store import path_template
|
||||
|
||||
|
||||
def test_collapses_numeric_ids():
|
||||
assert path_template("/api/users/4812/orders/99") == "/api/users/{id}/orders/{id}"
|
||||
|
||||
|
||||
def test_collapses_uuid():
|
||||
p = "/api/items/550e8400-e29b-41d4-a716-446655440000"
|
||||
assert path_template(p) == "/api/items/{id}"
|
||||
|
||||
|
||||
def test_collapses_long_hex_token():
|
||||
assert path_template("/files/a1b2c3d4e5f60718293a4b5c") == "/files/{id}"
|
||||
|
||||
|
||||
def test_keeps_short_words():
|
||||
assert path_template("/api/users/me/settings") == "/api/users/me/settings"
|
||||
|
||||
|
||||
def test_root_and_empty():
|
||||
assert path_template("/") == "/"
|
||||
assert path_template("") == "/"
|
||||
Reference in New Issue
Block a user