generated from coulomb/repo-seed
feat(api): CUST-WP-0018 — API hardening & code quality
T01: Fix datetime.utcnow() → datetime.now(tz=timezone.utc) in MCP server T02: Wrap _get/_post/_patch/_delete with try/except; return error dicts T03: Log warnings when write_log skips missing project path T04: Add priority + due_date_before filters to GET /tasks/ T05: Add owner + slug filters to GET /workstreams/ T06: Add offset param to GET /progress/ for proper pagination T07: Low-severity bundle: - CORS origins from CORS_ORIGINS env var (TD-017) - seed.py upsert domains+topics on re-run (TD-011) - normalise filter bar CSS → filter-text-input everywhere (TD-016) - add 30.5 avg-days-per-month comment in decisions.md (TD-019) - TD-009, TD-018 already resolved by existing code Closes CUST-WP-0018. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,7 @@ import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from uuid import UUID
|
||||
@@ -41,34 +41,54 @@ def _client() -> httpx.Client:
|
||||
def _get(path: str, params: dict | None = None) -> Any:
|
||||
if not path.endswith("/"):
|
||||
path = path + "/"
|
||||
with _client() as c:
|
||||
r = c.get(path, params={k: v for k, v in (params or {}).items() if v is not None})
|
||||
r.raise_for_status()
|
||||
return r.json()
|
||||
try:
|
||||
with _client() as c:
|
||||
r = c.get(path, params={k: v for k, v in (params or {}).items() if v is not None})
|
||||
r.raise_for_status()
|
||||
return r.json()
|
||||
except httpx.HTTPStatusError as e:
|
||||
return {"error": f"API {e.response.status_code}: {e.response.text[:300]}"}
|
||||
except Exception as e:
|
||||
return {"error": f"Request failed: {e}"}
|
||||
|
||||
|
||||
def _post(path: str, body: dict) -> Any:
|
||||
if not path.endswith("/"):
|
||||
path = path + "/"
|
||||
with _client() as c:
|
||||
r = c.post(path, json={k: v for k, v in body.items() if v is not None})
|
||||
r.raise_for_status()
|
||||
return r.json()
|
||||
try:
|
||||
with _client() as c:
|
||||
r = c.post(path, json={k: v for k, v in body.items() if v is not None})
|
||||
r.raise_for_status()
|
||||
return r.json()
|
||||
except httpx.HTTPStatusError as e:
|
||||
return {"error": f"API {e.response.status_code}: {e.response.text[:300]}"}
|
||||
except Exception as e:
|
||||
return {"error": f"Request failed: {e}"}
|
||||
|
||||
|
||||
def _patch(path: str, body: dict) -> Any:
|
||||
if not path.endswith("/"):
|
||||
path = path + "/"
|
||||
with _client() as c:
|
||||
r = c.patch(path, json={k: v for k, v in body.items() if v is not None})
|
||||
r.raise_for_status()
|
||||
return r.json()
|
||||
try:
|
||||
with _client() as c:
|
||||
r = c.patch(path, json={k: v for k, v in body.items() if v is not None})
|
||||
r.raise_for_status()
|
||||
return r.json()
|
||||
except httpx.HTTPStatusError as e:
|
||||
return {"error": f"API {e.response.status_code}: {e.response.text[:300]}"}
|
||||
except Exception as e:
|
||||
return {"error": f"Request failed: {e}"}
|
||||
|
||||
|
||||
def _delete(path: str) -> None:
|
||||
with _client() as c:
|
||||
r = c.delete(path)
|
||||
r.raise_for_status()
|
||||
try:
|
||||
with _client() as c:
|
||||
r = c.delete(path)
|
||||
r.raise_for_status()
|
||||
except httpx.HTTPStatusError as e:
|
||||
return {"error": f"API {e.response.status_code}: {e.response.text[:300]}"}
|
||||
except Exception as e:
|
||||
return {"error": f"Request failed: {e}"}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -526,7 +546,7 @@ def resolve_decision(
|
||||
"decision_type": "made",
|
||||
"rationale": rationale,
|
||||
"decided_by": decided_by,
|
||||
"decided_at": datetime.utcnow().isoformat() + "Z",
|
||||
"decided_at": datetime.now(tz=timezone.utc).isoformat(),
|
||||
})
|
||||
_post("/progress", {
|
||||
"topic_id": decision.get("topic_id"),
|
||||
|
||||
Reference in New Issue
Block a user