88 lines
2.7 KiB
Python
88 lines
2.7 KiB
Python
"""Flatten nested body_example values into PARAM drawer fields."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
|
|
def body_fields_from_example(body_example: dict[str, Any] | None) -> list[dict[str, Any]]:
|
|
"""PARAM inputs derived from body_example, including nested scalar paths.
|
|
|
|
Nested objects/arrays become dotted paths (``placement.host``, ``cpu.count``,
|
|
``disks.0.new_vmdk.name``) so the Params drawer can edit leaves while the
|
|
request body keeps the full nested JSON.
|
|
"""
|
|
|
|
if not isinstance(body_example, dict) or not body_example:
|
|
return []
|
|
|
|
fields: list[dict[str, Any]] = []
|
|
|
|
def _leaf_type(value: Any) -> str:
|
|
if isinstance(value, bool):
|
|
return "boolean"
|
|
if isinstance(value, int) and not isinstance(value, bool):
|
|
return "integer"
|
|
if isinstance(value, float):
|
|
return "number"
|
|
return "string"
|
|
|
|
def _walk(prefix: str, value: Any) -> None:
|
|
if isinstance(value, dict):
|
|
for key, child in value.items():
|
|
path = f"{prefix}.{key}" if prefix else str(key)
|
|
_walk(path, child)
|
|
return
|
|
if isinstance(value, list):
|
|
for index, child in enumerate(value):
|
|
path = f"{prefix}.{index}" if prefix else str(index)
|
|
_walk(path, child)
|
|
return
|
|
fields.append(
|
|
{
|
|
"name": prefix,
|
|
"type": _leaf_type(value),
|
|
"description": prefix,
|
|
"optional": True,
|
|
"enum": [],
|
|
"example": value,
|
|
}
|
|
)
|
|
|
|
_walk("", body_example)
|
|
return fields
|
|
|
|
|
|
def set_by_path(root: dict[str, Any], path: str, value: Any) -> None:
|
|
"""Assign ``value`` at a dotted path, creating intermediate dicts/lists."""
|
|
|
|
parts = [part for part in str(path).split(".") if part]
|
|
if not parts:
|
|
return
|
|
cur: Any = root
|
|
for index, part in enumerate(parts[:-1]):
|
|
nxt = parts[index + 1]
|
|
want_list = nxt.isdigit()
|
|
if isinstance(cur, list):
|
|
idx = int(part)
|
|
while len(cur) <= idx:
|
|
cur.append([] if want_list else {})
|
|
if cur[idx] is None or not isinstance(cur[idx], (dict, list)):
|
|
cur[idx] = [] if want_list else {}
|
|
cur = cur[idx]
|
|
continue
|
|
if part not in cur or not isinstance(cur[part], (dict, list)):
|
|
cur[part] = [] if want_list else {}
|
|
cur = cur[part]
|
|
last = parts[-1]
|
|
if isinstance(cur, list):
|
|
idx = int(last)
|
|
while len(cur) <= idx:
|
|
cur.append(None)
|
|
cur[idx] = value
|
|
else:
|
|
cur[last] = value
|
|
|
|
|
|
__all__ = ["body_fields_from_example", "set_by_path"]
|