first
This commit is contained in:
Executable
+258
@@ -0,0 +1,258 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Seed source_file + code_smell artifacts per Code Analysis page."""
|
||||
import json
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
|
||||
HEADERS = {
|
||||
"Content-Type": "application/json",
|
||||
"X-ASF-Tenant-Id": "t-demo-003",
|
||||
"X-ASF-User-Id": "u-mario",
|
||||
}
|
||||
BASE = "http://localhost:8000/api/v1"
|
||||
|
||||
|
||||
def req(method, path, body=None):
|
||||
data = json.dumps(body).encode() if body is not None else None
|
||||
r = urllib.request.Request(
|
||||
BASE + path,
|
||||
data=data,
|
||||
headers=HEADERS,
|
||||
method=method,
|
||||
)
|
||||
try:
|
||||
with urllib.request.urlopen(r, timeout=15) as resp:
|
||||
return resp.status, json.loads(resp.read().decode() or "null")
|
||||
except urllib.error.HTTPError as e:
|
||||
txt = e.read().decode()
|
||||
try:
|
||||
payload = json.loads(txt)
|
||||
except Exception:
|
||||
payload = {"raw": txt}
|
||||
return e.code, payload
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 1. list projects
|
||||
code, projects = req("GET", "/projects?limit=10")
|
||||
assert code == 200, f"projects {code} {projects}"
|
||||
pid = projects[0]["id"]
|
||||
print(f"project_id={pid} name={projects[0]['name']}")
|
||||
|
||||
# 2. list existing to dedupe by external_id
|
||||
code, arts = req("GET", "/artifacts?limit=200")
|
||||
existing_external = {
|
||||
a["external_id"] for a in arts if a.get("external_id")
|
||||
}
|
||||
existing_types = {
|
||||
(a["type"], a["title"]) for a in arts
|
||||
}
|
||||
print(f"existing artifacts total: {len(arts)} ext_ids: {len(existing_external)} type/titles: {len(existing_types)}")
|
||||
|
||||
SOURCE_FILES = [
|
||||
{
|
||||
"external_id": "SRC-CORE-MW",
|
||||
"type": "source_file",
|
||||
"title": "asf-core/app/middleware/correlation.py",
|
||||
"status": "active",
|
||||
"source": "git",
|
||||
"content": (
|
||||
"\"\"\"Tenant & correlation middleware.\n\n"
|
||||
"Adds X-ASF-Correlation-Id and coerces X-ASF-Tenant-Id to UUID5.\n\"\"\"\n"
|
||||
"import uuid\nfrom fastapi import Request, Response\nfrom starlette.middleware.base import BaseHTTPMiddleware\n\n"
|
||||
"TENANT_NS = uuid.UUID('6ba7b812-9dad-11d1-80b4-00c04fd430c8')\n\n"
|
||||
"def _coerce_uuid(val):\n"
|
||||
" try:\n"
|
||||
" return uuid.UUID(val)\n"
|
||||
" except Exception:\n"
|
||||
" return uuid.uuid5(TENANT_NS, val or 'default')\n\n"
|
||||
"class CorrelationMiddleware(BaseHTTPMiddleware):\n"
|
||||
" async def dispatch(self, request: Request, call_next):\n"
|
||||
" cid = request.headers.get('X-ASF-Correlation-Id') or str(uuid.uuid4())\n"
|
||||
" request.state.correlation_id = cid\n"
|
||||
" tid = _coerce_uuid(request.headers.get('X-ASF-Tenant-Id'))\n"
|
||||
" request.state.tenant_id = tid\n"
|
||||
" response: Response = await call_next(request)\n"
|
||||
" response.headers['X-ASF-Correlation-Id'] = cid\n"
|
||||
" return response\n"
|
||||
),
|
||||
"metadata": {
|
||||
"language": "python",
|
||||
"path": "services/asf-core/app/middleware/correlation.py",
|
||||
"repo": "git@github.com:azurian/asf.git",
|
||||
"branch": "main",
|
||||
"loc": 36,
|
||||
"lines": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36],
|
||||
"commit_sha": "a1f22c33",
|
||||
},
|
||||
},
|
||||
{
|
||||
"external_id": "SRC-WEB-REQ",
|
||||
"type": "source_file",
|
||||
"title": "asf-web/app/requirements/page.tsx",
|
||||
"status": "active",
|
||||
"source": "git",
|
||||
"content": (
|
||||
"\"use client\";\n"
|
||||
"import React, { useMemo, useState } from \"react\";\n"
|
||||
"import { useQuery } from \"@tanstack/react-query\";\n"
|
||||
"import { ClipboardList, Search } from \"lucide-react\";\n"
|
||||
"import { artifacts, projects } from \"@/lib/api\";\n"
|
||||
"import { Card, CardBody, CardHeader, CardTitle } from \"@/components/ui/Card\";\n"
|
||||
"\n"
|
||||
"export default function RequirementsPage() {\n"
|
||||
" const [projectFilter, setProjectFilter] = useState('all');\n"
|
||||
" const [statusFilter, setStatusFilter] = useState('all');\n"
|
||||
" const projectsQ = useQuery({ queryKey: ['req-projects'], queryFn: () => projects.list({ limit: 50 }) });\n"
|
||||
" const listQ = useQuery({\n"
|
||||
" queryKey: ['req-list', projectFilter, statusFilter],\n"
|
||||
" queryFn: () => artifacts.list({\n"
|
||||
" status: statusFilter !== 'all' ? statusFilter : undefined,\n"
|
||||
" project_id: projectFilter !== 'all' ? projectFilter : undefined,\n"
|
||||
" type: 'requirement',\n"
|
||||
" limit: 200,\n"
|
||||
" }),\n"
|
||||
" });\n"
|
||||
" return (<Card>...</Card>);\n"
|
||||
"}\n"
|
||||
),
|
||||
"metadata": {
|
||||
"language": "typescript",
|
||||
"path": "frontend/asf-web/app/requirements/page.tsx",
|
||||
"repo": "git@github.com:azurian/asf.git",
|
||||
"branch": "main",
|
||||
"loc": 394,
|
||||
"framework": "nextjs14",
|
||||
"commit_sha": "a1f22c33",
|
||||
},
|
||||
},
|
||||
{
|
||||
"external_id": "SRC-WORKER-CB",
|
||||
"type": "source_file",
|
||||
"title": "asf-worker/app/worker/consumer.py",
|
||||
"status": "active",
|
||||
"source": "git",
|
||||
"content": (
|
||||
"\"\"\"RabbitMQ consumer factory + sync callbacks for DIRECT job queues.\"\"\"\n"
|
||||
"import asyncio\nimport structlog\nfrom aio_pika import IncomingMessage\n\n"
|
||||
"log = structlog.get_logger(__name__)\n\n"
|
||||
"def _make_consumer_callback(handler):\n"
|
||||
" def _cb(message: IncomingMessage):\n"
|
||||
" loop = asyncio.new_event_loop()\n"
|
||||
" try:\n"
|
||||
" loop.run_until_complete(handler(message))\n"
|
||||
" except Exception as exc:\n"
|
||||
" log.exception('consumer_handler_error', err=exc)\n"
|
||||
" finally:\n"
|
||||
" loop.close()\n"
|
||||
" return _cb\n"
|
||||
),
|
||||
"metadata": {
|
||||
"language": "python",
|
||||
"path": "services/asf-worker/app/worker/consumer.py",
|
||||
"repo": "git@github.com:azurian/asf.git",
|
||||
"branch": "main",
|
||||
"loc": 18,
|
||||
"commit_sha": "b4812a01",
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
# code smells stored as type=ai_analysis with metadata.kind = "code_smell"
|
||||
# (to keep ArtifactType enum closed — no tests break)
|
||||
SMELLS = [
|
||||
{
|
||||
"external_id": "CS-001",
|
||||
"type": "ai_analysis",
|
||||
"title": "Bare except clause in CorrelationMiddleware.dispatch",
|
||||
"status": "open",
|
||||
"source": "ruff:EM102",
|
||||
"content": (
|
||||
"Rule Ruff E722 — bare `except Exception` without narrow clause may swallow "
|
||||
"cancellation errors unrelated to UUID parse. Prefer explicit ValueError/AttributeError "
|
||||
"or log.exception with correlation_id bound.\n"
|
||||
"Lines affected: 14-16."
|
||||
),
|
||||
"metadata": {
|
||||
"kind": "code_smell",
|
||||
"language": "python",
|
||||
"severity": "medium",
|
||||
"rule_id": "E722",
|
||||
"engine": "ruff 0.5.0",
|
||||
"path": "services/asf-core/app/middleware/correlation.py",
|
||||
"lines": [14, 15, 16],
|
||||
"fingerprint": "corr_mw_except_001",
|
||||
"confidence_pct": 98,
|
||||
},
|
||||
},
|
||||
{
|
||||
"external_id": "CS-002",
|
||||
"type": "ai_analysis",
|
||||
"title": "Missing N+1 query guard in requirements listQ (no select limit on projects)",
|
||||
"status": "open",
|
||||
"source": "eslint:perf-nplus1",
|
||||
"content": (
|
||||
"Requirements page fetches all 50 projects and maps via projectsQ even when a single project filter is selected. "
|
||||
"Consider deriving projectNameById lazily and paginating projects lookup.\n"
|
||||
"Affected page.tsx lines 78-89."
|
||||
),
|
||||
"metadata": {
|
||||
"kind": "code_smell",
|
||||
"language": "typescript",
|
||||
"severity": "low",
|
||||
"rule_id": "asf-perf-003",
|
||||
"engine": "eslint custom",
|
||||
"path": "frontend/asf-web/app/requirements/page.tsx",
|
||||
"lines": [78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89],
|
||||
"fingerprint": "req_list_nplus1_002",
|
||||
"confidence_pct": 75,
|
||||
},
|
||||
},
|
||||
{
|
||||
"external_id": "CS-003",
|
||||
"type": "ai_analysis",
|
||||
"title": "Coroutine leak risk: event loop not shared re-created per message",
|
||||
"status": "open",
|
||||
"source": "pytype:event-loop",
|
||||
"content": (
|
||||
"Worker consumer factory _cb spawns asyncio.new_event_loop() per IncomingMessage. "
|
||||
"Long-lived handlers under high volume may accumulate GC pressure from leftover loop objects. "
|
||||
"Recommendation: pass the shared running event loop to handler() via q.consume() and make handler truly async.\n"
|
||||
"Severity HIGH — affects every DIRECT job 24/7."
|
||||
),
|
||||
"metadata": {
|
||||
"kind": "code_smell",
|
||||
"language": "python",
|
||||
"severity": "high",
|
||||
"rule_id": "ASF-SEC-203",
|
||||
"engine": "pytype + manual review",
|
||||
"path": "services/asf-worker/app/worker/consumer.py",
|
||||
"lines": [8, 9, 10, 11, 12, 13, 14, 15, 16],
|
||||
"fingerprint": "worker_loop_per_msg_003",
|
||||
"confidence_pct": 88,
|
||||
"cwe": "CWE-772",
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
created = 0
|
||||
for body in SOURCE_FILES + SMELLS:
|
||||
if (body["type"], body["title"]) in existing_types or body.get("external_id") in existing_external:
|
||||
print(f" SKIP exists ext_id={body.get('external_id')} type={body['type']} title={body['title'][:60]}")
|
||||
continue
|
||||
body_full = {"project_id": pid, **body}
|
||||
code, resp = req("POST", "/artifacts", body_full)
|
||||
if code in (201, 200):
|
||||
created += 1
|
||||
print(f" OK [{code}] type={body['type']} id={str(resp['id'])[:8]} title={body['title'][:60]}")
|
||||
else:
|
||||
print(f" FAIL [{code}] type={body['type']} body={resp}")
|
||||
|
||||
print(f"\nDONE: created={created} new code artifacts")
|
||||
|
||||
# print summary counts per type
|
||||
code, arts2 = req("GET", "/artifacts?limit=200")
|
||||
from collections import Counter
|
||||
cnt = Counter(a["type"] for a in arts2)
|
||||
for t, c in cnt.items():
|
||||
print(f" count {t}: {c}")
|
||||
Reference in New Issue
Block a user