webapp-scaffold/bin/postprocess-openapi.py
Uwe Schuster 55968fbd73 v0.1.0: fetch/postprocess/inject scripts + vite config factories
Bootstraps the shared frontend build glue for webapp-template-derived
projects:

  bin/fetch-openapi.sh         — pull Swagger JSON from a running backend
  bin/postprocess-openapi.py   — fix oatpp 1.3 rough edges before orval
  bin/inject-hashed-filenames.py — rewrite HTML tags, config-driven
  src/vite-config.ts           — defineAdminConfig / defineGuestConfig
  templates/orval.config.template.ts — starting point for derived repos

Package name @uschuster/webapp-scaffold. Consumed as a devDependency
through the internal Forgejo npm registry; binaries exposed for use in
package.json scripts. createCoreFetch + i18n deferred to v0.2 / v0.3.

Closes fewo-webapp#414

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 22:06:58 +02:00

47 lines
1.6 KiB
Python
Executable file

#!/usr/bin/env python3
"""Post-process openapi.json before handing it to Orval.
oatpp 1.3's Swagger generator produces a valid spec but with rough edges
that make the generated client less ergonomic:
- Missing operationIds on some endpoints → orval falls back to path-based
names that collide. We synthesise an operationId from method + path.
- summary/description strings occasionally contain stray control characters.
- Some endpoints have no tags, which throws off orval's file-splitting.
Run between `fetch-openapi.sh` and `orval` (see package.json `codegen`).
"""
import json
import re
import sys
from pathlib import Path
SRC = Path("openapi.json")
if not SRC.exists():
sys.exit(f"{SRC} not found — run fetch-openapi.sh first")
spec = json.loads(SRC.read_text())
def op_id(method: str, path: str) -> str:
"""`getApiAnnouncementsEntityIdHistory` from `GET /api/announcements/{entity_id}/history`."""
parts = [method.lower()]
for seg in path.strip("/").split("/"):
if seg.startswith("{") and seg.endswith("}"):
parts.append(seg[1:-1])
else:
parts.append(seg)
return re.sub(r"[^A-Za-z0-9]+(\w)", lambda m: m.group(1).upper(),
"_".join(parts))
paths = spec.get("paths", {})
for path, methods in paths.items():
for method, op in methods.items():
if not isinstance(op, dict):
continue
op.setdefault("operationId", op_id(method, path))
op.setdefault("tags", [path.strip("/").split("/")[1] if "/" in path.strip("/") else "default"])
SRC.write_text(json.dumps(spec, indent=2))
print(f" postprocessed {SRC}{len(paths)} paths")