cbd0adca91
Stateful FastAPI lab with contract packs, Compose/Helm, Docker Hub release targets, and Pulumi coverage across all Engine series (GET/POST/PUT/DELETE/HEAD).
56 lines
1.8 KiB
Python
56 lines
1.8 KiB
Python
"""Resolve Engine API version (v3/v4) and active series pack."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from dataclasses import dataclass
|
|
|
|
from fastapi import Request
|
|
|
|
from app.ovirt.errors import OVirtError
|
|
|
|
_VERSION_PREFIX = re.compile(r"^/ovirt-engine/api/(v[34])(/.*)?$")
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class ApiVersionContext:
|
|
api_version: str # "3" or "4"
|
|
series: str
|
|
path_suffix: str # path after /ovirt-engine/api[/vN]
|
|
|
|
|
|
def resolve_api_version(request: Request, default_series: str = "4.5") -> ApiVersionContext:
|
|
path = request.url.path
|
|
header = (request.headers.get("version") or "").strip()
|
|
series = getattr(request.app.state, "ovirt_series", None) or default_series
|
|
|
|
m = _VERSION_PREFIX.match(path)
|
|
if m:
|
|
ver = m.group(1)[1:] # strip v
|
|
rest = m.group(2) or ""
|
|
return ApiVersionContext(api_version=ver, series=series, path_suffix=rest or "/")
|
|
|
|
if path.startswith("/ovirt-engine/api"):
|
|
rest = path[len("/ovirt-engine/api") :] or "/"
|
|
if header in {"3", "4"}:
|
|
return ApiVersionContext(api_version=header, series=series, path_suffix=rest)
|
|
# default version 4 for 4.x series, 3 for 3.x
|
|
default = "3" if str(series).startswith("3.") else "4"
|
|
return ApiVersionContext(api_version=default, series=series, path_suffix=rest)
|
|
|
|
raise OVirtError("NotFound", f"Unknown path {path}", status_code=404)
|
|
|
|
|
|
def strip_api_prefix(path: str) -> str:
|
|
"""Normalize to collection-relative path starting with /."""
|
|
|
|
for prefix in (
|
|
"/ovirt-engine/api/v4",
|
|
"/ovirt-engine/api/v3",
|
|
"/ovirt-engine/api",
|
|
):
|
|
if path.startswith(prefix):
|
|
rest = path[len(prefix) :] or "/"
|
|
return rest if rest.startswith("/") else f"/{rest}"
|
|
return path
|