Expand lab seed tiers, OpenAPI PARAMS, and console DATA/Params UX.
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
"""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"]
|
||||
Reference in New Issue
Block a user