first
This commit is contained in:
Executable
+125
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Seed tenant t-demo-003 with 5 artifacts + 5 relationships for a rich Traceability Graph demo.
|
||||
|
||||
Endpoint testati via curl:
|
||||
- POST /api/v1/artifacts
|
||||
- POST /api/v1/relationships
|
||||
- GET /api/v1/traceability/graph
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
BASE = "http://127.0.0.1:8000/api/v1"
|
||||
H = [
|
||||
"-H", "X-ASF-Tenant-Id: t-demo-003",
|
||||
"-H", "X-ASF-User-Id: u-mario",
|
||||
"-H", "Content-Type: application/json",
|
||||
]
|
||||
|
||||
|
||||
def curl(method: str, path: str, body: dict[str, Any] | None = None) -> tuple[int, dict[str, Any] | list | str]:
|
||||
cmd = ["curl", "-s", "-m", "15", "-o", "-", "-w", "\n%{http_code}", "-X", method] + H
|
||||
if body is not None:
|
||||
cmd += ["--data", json.dumps(body, ensure_ascii=False)]
|
||||
cmd.append(f"{BASE}{path}")
|
||||
r = subprocess.run(cmd, capture_output=True, text=True)
|
||||
out_raw = r.stdout.strip() or "{}"
|
||||
# Last line is the http code we injected with -w
|
||||
if "\n" in out_raw:
|
||||
json_part, code_part = out_raw.rsplit("\n", 1)
|
||||
else:
|
||||
json_part, code_part = "", out_raw
|
||||
try:
|
||||
code = int(code_part)
|
||||
except ValueError:
|
||||
code = 0
|
||||
try:
|
||||
data = json.loads(json_part or "null")
|
||||
except Exception as exc: # pragma: no cover
|
||||
data = f"JSON_ERR {exc} RAW={json_part[:200]!r}"
|
||||
print(f" [{method:6s} {path:24s}] HTTP={code:3d}",
|
||||
f"type={data.get('type','?') if isinstance(data,dict) else '?'}\tid={str(data.get('id',''))[:10] if isinstance(data,dict) else '?'}",
|
||||
file=sys.stderr)
|
||||
return code, data
|
||||
|
||||
|
||||
def main() -> int:
|
||||
print("# Step 1: list existing artifacts + project_id", file=sys.stderr)
|
||||
_, arts = curl("GET", "/artifacts?limit=50")
|
||||
assert isinstance(arts, list), f"artifacts list not a list: {arts!r}"
|
||||
req_103 = None
|
||||
project_id = None
|
||||
for a in arts:
|
||||
if isinstance(a, dict) and a.get("external_id") == "REQ-103":
|
||||
req_103 = a
|
||||
project_id = a.get("project_id")
|
||||
break
|
||||
if not req_103:
|
||||
# fallback: first artifact
|
||||
req_103 = arts[0] if arts else None
|
||||
project_id = req_103.get("project_id") if isinstance(req_103, dict) else None
|
||||
print(f" req_103 id={str(req_103['id'])[:10] if isinstance(req_103,dict) else None} project_id={str(project_id)[:10] if project_id else None}",
|
||||
file=sys.stderr)
|
||||
|
||||
def make(type_: str, ext: str, title: str, content: str) -> dict[str, Any]:
|
||||
code, data = curl(
|
||||
"POST", "/artifacts",
|
||||
{
|
||||
"type": type_,
|
||||
"external_id": ext,
|
||||
"title": title,
|
||||
"content": content,
|
||||
"status": "done",
|
||||
"project_id": project_id,
|
||||
},
|
||||
)
|
||||
assert code == 201 and isinstance(data, dict) and "id" in data, f"make {type_} FAILED HTTP={code} {data}"
|
||||
return data
|
||||
|
||||
print(file=sys.stderr); print("# Step 2: create 5 extra artifacts", file=sys.stderr)
|
||||
task = make("task", "TASK-101", "Implementa tenant trust header coerce", "FastAPI correlation middleware: UUID UUID5 per stringhe t-demo-xxx, no 400 invalid")
|
||||
pr = make("pull_request", "PR#88", "feat(rbac): tenant trust + policies v1", "feat(rbac): add trust header middleware + policy service helpers")
|
||||
test = make("test", "TEST-22", "cross-tenant reads/writes blocked", "Pytest suite proving cross-tenant artifact operations return 404/403")
|
||||
build = make("build", "BUILD-910","asf-core build #910 (main)", "GitHub Actions build main branch, all checks green.")
|
||||
deploy = make("deployment", "DEP-44", "asf-core v0.5.0 → prod-west-1", "ArgoCD sync asf-core to cluster prod-west-1 eu-west")
|
||||
|
||||
print(file=sys.stderr); print("# Step 3: create 5 relationships (chain: DEP → BUILD → PR → TASK → REQ, TEST → PR)", file=sys.stderr)
|
||||
rels_pairs = [
|
||||
(task, req_103, "implements"),
|
||||
(pr, task, "generated_from"),
|
||||
(build, pr, "generated_from"),
|
||||
(test, pr, "verifies"),
|
||||
(deploy, build, "deployed_as"),
|
||||
]
|
||||
for src, tgt, kind in rels_pairs:
|
||||
code, rel = curl(
|
||||
"POST", "/relationships",
|
||||
{
|
||||
"source_artifact_id": src["id"],
|
||||
"target_artifact_id": tgt["id"],
|
||||
"relationship_type": kind,
|
||||
"meta": {"seed": "traceability_demo"},
|
||||
},
|
||||
)
|
||||
assert code < 300 and isinstance(rel, dict) and "id" in rel, f"REL FAIL {kind} -> {rel}"
|
||||
|
||||
print(file=sys.stderr); print("# Step 4: GET traceability graph → counts", file=sys.stderr)
|
||||
code, graph = curl("GET", "/traceability/graph?limit=200")
|
||||
assert code == 200 and isinstance(graph, dict), f"GRAPH FAIL HTTP={code} {graph}"
|
||||
nodes = graph.get("nodes", [])
|
||||
edges = graph.get("edges", [])
|
||||
print(f"\n=== SEED SUCCESS ===", file=sys.stderr)
|
||||
print(f" graph.nodes = {len(nodes)} graph.edges = {len(edges)}", file=sys.stderr)
|
||||
# print to stdout for bash pipeline capture (JSON):
|
||||
print(json.dumps({"nodes": len(nodes), "edges": len(edges),
|
||||
"node_ids": [{"id":str(n["id"])[:10],"type":n.get("type"),"label":n.get("label")} for n in nodes],
|
||||
"edge_labels": [{"s":e["source"][:8],"t":e["target"][:8],"lbl":e.get("label")} for e in edges]}))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user