chore: formalize dotnet backend repository

This commit is contained in:
2026-07-27 12:34:53 +08:00
parent c2e4cd2a12
commit ef4c8fc7e5
7 changed files with 659 additions and 19 deletions

95
scripts/compare_openapi.py Executable file
View File

@@ -0,0 +1,95 @@
#!/usr/bin/env python3
"""Compare two OpenAPI documents by normalized HTTP method and path."""
from __future__ import annotations
import argparse
import csv
import json
from pathlib import Path
from typing import Any
HTTP_METHODS = {"delete", "get", "head", "options", "patch", "post", "put", "trace"}
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--legacy", required=True, type=Path, help="Legacy OpenAPI JSON")
parser.add_argument("--target", required=True, type=Path, help="Target OpenAPI JSON")
parser.add_argument("--output", required=True, type=Path, help="Output CSV")
return parser.parse_args()
def load_operations(path: Path) -> dict[tuple[str, str], dict[str, str]]:
with path.open(encoding="utf-8") as source:
document: dict[str, Any] = json.load(source)
operations: dict[tuple[str, str], dict[str, str]] = {}
for route, path_item in document.get("paths", {}).items():
if not isinstance(path_item, dict):
continue
for method, operation in path_item.items():
normalized_method = method.lower()
if normalized_method not in HTTP_METHODS or not isinstance(operation, dict):
continue
operations[(normalized_method.upper(), route)] = {
"operation_id": str(operation.get("operationId", "")),
"summary": str(operation.get("summary", "")),
}
return operations
def main() -> None:
args = parse_args()
legacy = load_operations(args.legacy)
target = load_operations(args.target)
keys = sorted(legacy.keys() | target.keys(), key=lambda key: (key[1], key[0]))
args.output.parent.mkdir(parents=True, exist_ok=True)
with args.output.open("w", encoding="utf-8", newline="") as destination:
writer = csv.DictWriter(
destination,
fieldnames=(
"method",
"path",
"status",
"legacy_operation_id",
"legacy_summary",
"target_operation_id",
"target_summary",
),
lineterminator="\n",
)
writer.writeheader()
for method, route in keys:
legacy_operation = legacy.get((method, route), {})
target_operation = target.get((method, route), {})
status = (
"exact_match"
if legacy_operation and target_operation
else "legacy_only"
if legacy_operation
else "target_only"
)
writer.writerow(
{
"method": method,
"path": route,
"status": status,
"legacy_operation_id": legacy_operation.get("operation_id", ""),
"legacy_summary": legacy_operation.get("summary", ""),
"target_operation_id": target_operation.get("operation_id", ""),
"target_summary": target_operation.get("summary", ""),
}
)
exact_matches = len(legacy.keys() & target.keys())
print(
f"legacy={len(legacy)} target={len(target)} exact={exact_matches} "
f"legacy_only={len(legacy) - exact_matches} target_only={len(target) - exact_matches}"
)
if __name__ == "__main__":
main()