fix: surface platform-board integration errors (fixes #74) (#78)

* fix: surface platform-board integration errors instead of empty state (fixes #74)

Distinguish connection, auth, and HTTP failures from zero issues. Add
__main__ uvicorn bootstrap with HOST/PORT env overrides.

Co-authored-by: Cursor <[email protected]>

* fix: improve platform-board error handling

- Add explicit 200 status code check to prevent 3xx/204 from proceeding to JSON parsing
- Add safe JSON parsing with try/catch to handle invalid JSON responses
- Add defensive type checking for non-dict/non-list JSON responses

Addresses reviewer feedback from Gemini Code Assist review.

Co-authored-by: Mervin Praison <[email protected]>

---------

Co-authored-by: MervinPraison <[email protected]>
Co-authored-by: Cursor <[email protected]>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: Mervin Praison <[email protected]>
This commit is contained in:
authored and GitHub committed 2026-06-15 16:33:32 +01:00
1 parent 2162b61629
commit 7a320bde1b
1 file changed
+63 -17
+63 -17
View File
@@ -14,30 +14,76 @@ aiui.set_pages(["issues-board"])
aiui.set_dashboard(sidebar=True)
@aiui.page("issues-board", title="Issues", icon="📌", group="Work")
async def issues_board():
issues = []
async def _fetch_issues() -> tuple[list, str | None]:
"""Return (issues, error_message). error_message is None on success."""
url = f"{PLATFORM.rstrip('/')}/api/v1/workspaces/{WORKSPACE_ID}/issues"
try:
async with httpx.AsyncClient(timeout=10.0) as c:
r = await c.get(url)
if r.status_code == 200:
d = r.json()
issues = d if isinstance(d, list) else d.get("issues") or d.get("items") or []
except Exception:
pass
async with httpx.AsyncClient(timeout=10.0) as client:
response = await client.get(url)
except httpx.ConnectError:
return [], f"Cannot reach platform at {PLATFORM}"
except httpx.TimeoutException:
return [], f"Platform request timed out ({PLATFORM})"
except Exception as exc:
return [], f"Platform request failed: {exc}"
if response.status_code in (401, 403):
return [], f"Platform auth failed (HTTP {response.status_code})"
if response.status_code != 200:
return [], f"Platform error (HTTP {response.status_code})"
try:
data = response.json()
except ValueError:
return [], "Platform returned invalid JSON"
if isinstance(data, list):
issues = data
elif isinstance(data, dict):
issues = data.get("issues") or data.get("items") or []
else:
issues = []
return issues, None
@aiui.page("issues-board", title="Issues", icon="📌", group="Work")
async def issues_board():
issues, error = await _fetch_issues()
if error:
return aiui.layout([
aiui.text(f"Platform: {PLATFORM} · workspace: {WORKSPACE_ID}"),
aiui.card("Platform unavailable", footer=error),
])
by: dict[str, list] = {}
for i in issues:
s = (i.get("status") or "backlog").lower().replace(" ", "_")
by.setdefault(s, []).append(i)
for issue in issues:
status = (issue.get("status") or "backlog").lower().replace(" ", "_")
by.setdefault(status, []).append(issue)
columns = []
for sid in ("backlog", "in_progress", "done"):
cards = [aiui.card(i.get("title", "Issue"), footer=sid) for i in by.get(sid, [])]
columns.append({"id": sid, "title": sid.replace("_", " ").title(), "cards": cards or [aiui.card("(empty)")]})
for status_id in ("backlog", "in_progress", "done"):
cards = [aiui.card(i.get("title", "Issue"), footer=status_id) for i in by.get(status_id, [])]
if not cards:
cards = [aiui.card("(no issues)")]
columns.append({
"id": status_id,
"title": status_id.replace("_", " ").title(),
"cards": cards,
})
summary = f"{len(issues)} issue(s)" if issues else "No issues in workspace"
return aiui.layout([
aiui.text(f"Platform: {PLATFORM} · workspace: {WORKSPACE_ID}"),
aiui.text(f"Platform: {PLATFORM} · workspace: {WORKSPACE_ID} · {summary}"),
aiui.board(columns=columns),
])
app = create_app()
if __name__ == "__main__":
import uvicorn
host = os.getenv("HOST", "127.0.0.1")
port = int(os.getenv("PORT", "8082"))
uvicorn.run(app, host=host, port=port)