#!/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())