Skip to content
This repository was archived by the owner on Jun 5, 2025. It is now read-only.

Commit 676aee4

Browse files
authored
Add workspace-namespaced APIs for messages and alerts (#676)
This adds APIs under `/api/v1/workspaces/...` that allows API users to fetch alerts and messages for a particular namespaces. The APIs under `/dashboard` were modified to display resources for the active workspace. Closes: #667 Signed-off-by: Juan Antonio Osorio <[email protected]>
1 parent d56d451 commit 676aee4

File tree

3 files changed

+85
-12
lines changed

3 files changed

+85
-12
lines changed

src/codegate/api/dashboard/dashboard.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import requests
55
import structlog
6-
from fastapi import APIRouter, Depends
6+
from fastapi import APIRouter, Depends, HTTPException
77
from fastapi.responses import StreamingResponse
88
from fastapi.routing import APIRoute
99

@@ -14,12 +14,15 @@
1414
)
1515
from codegate.api.dashboard.request_models import AlertConversation, Conversation
1616
from codegate.db.connection import DbReader, alert_queue
17+
from codegate.workspaces import crud
1718

1819
logger = structlog.get_logger("codegate")
1920

2021
dashboard_router = APIRouter()
2122
db_reader = None
2223

24+
wscrud = crud.WorkspaceCrud()
25+
2326

2427
def uniq_name(route: APIRoute):
2528
return f"v1_{route.name}"
@@ -48,9 +51,14 @@ def get_messages(db_reader: DbReader = Depends(get_db_reader)) -> List[Conversat
4851
"""
4952
Get all the messages from the database and return them as a list of conversations.
5053
"""
51-
prompts_outputs = asyncio.run(db_reader.get_prompts_with_output())
54+
try:
55+
active_ws = asyncio.run(wscrud.get_active_workspace())
56+
prompts_outputs = asyncio.run(db_reader.get_prompts_with_output(active_ws.id))
5257

53-
return asyncio.run(parse_messages_in_conversations(prompts_outputs))
58+
return asyncio.run(parse_messages_in_conversations(prompts_outputs))
59+
except Exception as e:
60+
logger.error(f"Error getting messages: {str(e)}")
61+
raise HTTPException(status_code=500, detail="Internal server error")
5462

5563

5664
@dashboard_router.get(
@@ -60,8 +68,15 @@ def get_alerts(db_reader: DbReader = Depends(get_db_reader)) -> List[Optional[Al
6068
"""
6169
Get all the messages from the database and return them as a list of conversations.
6270
"""
63-
alerts_prompt_output = asyncio.run(db_reader.get_alerts_with_prompt_and_output())
64-
return asyncio.run(parse_get_alert_conversation(alerts_prompt_output))
71+
try:
72+
active_ws = asyncio.run(wscrud.get_active_workspace())
73+
alerts_prompt_output = asyncio.run(
74+
db_reader.get_alerts_with_prompt_and_output(active_ws.id)
75+
)
76+
return asyncio.run(parse_get_alert_conversation(alerts_prompt_output))
77+
except Exception as e:
78+
logger.error(f"Error getting alerts: {str(e)}")
79+
raise HTTPException(status_code=500, detail="Internal server error")
6580

6681

6782
async def generate_sse_events() -> AsyncGenerator[str, None]:

src/codegate/api/v1.py

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
1+
from typing import List, Optional
2+
13
from fastapi import APIRouter, HTTPException, Response
24
from fastapi.routing import APIRoute
35
from pydantic import ValidationError
46

57
from codegate.api import v1_models
6-
from codegate.api.dashboard.dashboard import dashboard_router
7-
from codegate.db.connection import AlreadyExistsError
8+
from codegate.api.dashboard import dashboard
9+
from codegate.api.dashboard.request_models import AlertConversation, Conversation
10+
from codegate.db.connection import AlreadyExistsError, DbReader
811
from codegate.workspaces import crud
912

1013
v1 = APIRouter()
11-
v1.include_router(dashboard_router)
14+
v1.include_router(dashboard.dashboard_router)
1215
wscrud = crud.WorkspaceCrud()
1316

17+
# This is a singleton object
18+
dbreader = DbReader()
19+
1420

1521
def uniq_name(route: APIRoute):
1622
return f"v1_{route.name}"
@@ -94,3 +100,45 @@ async def delete_workspace(workspace_name: str):
94100
raise HTTPException(status_code=500, detail="Internal server error")
95101

96102
return Response(status_code=204)
103+
104+
105+
@v1.get(
106+
"/workspaces/{workspace_name}/alerts",
107+
tags=["Workspaces"],
108+
generate_unique_id_function=uniq_name,
109+
)
110+
async def get_workspace_alerts(workspace_name: str) -> List[Optional[AlertConversation]]:
111+
"""Get alerts for a workspace."""
112+
try:
113+
ws = await wscrud.get_workspace_by_name(workspace_name)
114+
except crud.WorkspaceDoesNotExistError:
115+
raise HTTPException(status_code=404, detail="Workspace does not exist")
116+
except Exception:
117+
raise HTTPException(status_code=500, detail="Internal server error")
118+
119+
try:
120+
alerts = await dbreader.get_alerts_with_prompt_and_output(ws.id)
121+
return await dashboard.parse_get_alert_conversation(alerts)
122+
except Exception:
123+
raise HTTPException(status_code=500, detail="Internal server error")
124+
125+
126+
@v1.get(
127+
"/workspaces/{workspace_name}/messages",
128+
tags=["Workspaces"],
129+
generate_unique_id_function=uniq_name,
130+
)
131+
async def get_workspace_messages(workspace_name: str) -> List[Conversation]:
132+
"""Get messages for a workspace."""
133+
try:
134+
ws = await wscrud.get_workspace_by_name(workspace_name)
135+
except crud.WorkspaceDoesNotExistError:
136+
raise HTTPException(status_code=404, detail="Workspace does not exist")
137+
except Exception:
138+
raise HTTPException(status_code=500, detail="Internal server error")
139+
140+
try:
141+
prompts_outputs = await dbreader.get_prompts_with_output(ws.id)
142+
return await dashboard.parse_messages_in_conversations(prompts_outputs)
143+
except Exception:
144+
raise HTTPException(status_code=500, detail="Internal server error")

src/codegate/db/connection.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ async def _exec_select_conditions_to_pydantic(
365365
raise e
366366
return None
367367

368-
async def get_prompts_with_output(self) -> List[GetPromptWithOutputsRow]:
368+
async def get_prompts_with_output(self, workpace_id: str) -> List[GetPromptWithOutputsRow]:
369369
sql = text(
370370
"""
371371
SELECT
@@ -375,13 +375,19 @@ async def get_prompts_with_output(self) -> List[GetPromptWithOutputsRow]:
375375
o.timestamp as output_timestamp
376376
FROM prompts p
377377
LEFT JOIN outputs o ON p.id = o.prompt_id
378+
WHERE p.workspace_id = :workspace_id
378379
ORDER BY o.timestamp DESC
379380
"""
380381
)
381-
prompts = await self._execute_select_pydantic_model(GetPromptWithOutputsRow, sql)
382+
conditions = {"workspace_id": workpace_id}
383+
prompts = await self._exec_select_conditions_to_pydantic(
384+
GetPromptWithOutputsRow, sql, conditions, should_raise=True
385+
)
382386
return prompts
383387

384-
async def get_alerts_with_prompt_and_output(self) -> List[GetAlertsWithPromptAndOutputRow]:
388+
async def get_alerts_with_prompt_and_output(
389+
self, workspace_id: str
390+
) -> List[GetAlertsWithPromptAndOutputRow]:
385391
sql = text(
386392
"""
387393
SELECT
@@ -402,10 +408,14 @@ async def get_alerts_with_prompt_and_output(self) -> List[GetAlertsWithPromptAnd
402408
FROM alerts a
403409
LEFT JOIN prompts p ON p.id = a.prompt_id
404410
LEFT JOIN outputs o ON p.id = o.prompt_id
411+
WHERE p.workspace_id = :workspace_id
405412
ORDER BY a.timestamp DESC
406413
"""
407414
)
408-
prompts = await self._execute_select_pydantic_model(GetAlertsWithPromptAndOutputRow, sql)
415+
conditions = {"workspace_id": workspace_id}
416+
prompts = await self._exec_select_conditions_to_pydantic(
417+
GetAlertsWithPromptAndOutputRow, sql, conditions, should_raise=True
418+
)
409419
return prompts
410420

411421
async def get_workspaces(self) -> List[WorkspaceActive]:

0 commit comments

Comments
 (0)