feat: add sessions and runs endpoints to query-api
GET /v1/sessions - list sessions with filters GET /v1/sessions/:id - session detail with runs GET /v1/runs/:id - run detail with spans Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,10 +1,12 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"database/sql"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
"agentmon/internal/httpx"
|
"agentmon/internal/httpx"
|
||||||
"agentmon/internal/store/postgres"
|
"agentmon/internal/store/postgres"
|
||||||
@@ -47,6 +49,81 @@ func main() {
|
|||||||
httpx.WriteJSON(w, http.StatusOK, map[string]any{"events": events})
|
httpx.WriteJSON(w, http.StatusOK, map[string]any{"events": events})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
r.Get("/v1/sessions", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
q := r.URL.Query()
|
||||||
|
f := postgres.SessionsFilter{
|
||||||
|
Framework: q.Get("framework"),
|
||||||
|
Host: q.Get("host"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := q.Get("limit"); v != "" {
|
||||||
|
f.Limit, _ = strconv.Atoi(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := q.Get("from"); v != "" {
|
||||||
|
if t, err := time.Parse(time.RFC3339, v); err == nil {
|
||||||
|
f.From = &t
|
||||||
|
} else if t, err := time.Parse("2006-01-02", v); err == nil {
|
||||||
|
f.From = &t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := q.Get("to"); v != "" {
|
||||||
|
if t, err := time.Parse(time.RFC3339, v); err == nil {
|
||||||
|
f.To = &t
|
||||||
|
} else if t, err := time.Parse("2006-01-02", v); err == nil {
|
||||||
|
end := t.Add(24*time.Hour - time.Nanosecond)
|
||||||
|
f.To = &end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v := q.Get("cursor"); v != "" {
|
||||||
|
if t, err := time.Parse(time.RFC3339Nano, v); err == nil {
|
||||||
|
f.Cursor = &t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sessions, nextCursor, err := db.ListSessions(r.Context(), f)
|
||||||
|
if err != nil {
|
||||||
|
httpx.WriteJSON(w, http.StatusInternalServerError, map[string]any{"error": "db_error"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp := map[string]any{"sessions": sessions}
|
||||||
|
if nextCursor != nil {
|
||||||
|
resp["next_cursor"] = nextCursor.Format(time.RFC3339Nano)
|
||||||
|
}
|
||||||
|
httpx.WriteJSON(w, http.StatusOK, resp)
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Get("/v1/sessions/{sessionID}", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
sessionID := chi.URLParam(r, "sessionID")
|
||||||
|
session, runs, err := db.GetSessionWithRuns(r.Context(), sessionID)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
httpx.WriteJSON(w, http.StatusNotFound, map[string]any{"error": "not_found"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
httpx.WriteJSON(w, http.StatusInternalServerError, map[string]any{"error": "db_error"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
httpx.WriteJSON(w, http.StatusOK, map[string]any{"session": session, "runs": runs})
|
||||||
|
})
|
||||||
|
|
||||||
|
r.Get("/v1/runs/{runID}", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
runID := chi.URLParam(r, "runID")
|
||||||
|
run, spans, err := db.GetRunWithSpans(r.Context(), runID)
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
httpx.WriteJSON(w, http.StatusNotFound, map[string]any{"error": "not_found"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
httpx.WriteJSON(w, http.StatusInternalServerError, map[string]any{"error": "db_error"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
httpx.WriteJSON(w, http.StatusOK, map[string]any{"run": run, "spans": spans})
|
||||||
|
})
|
||||||
|
|
||||||
log.Printf("query-api listening on %s", addr)
|
log.Printf("query-api listening on %s", addr)
|
||||||
log.Fatal(http.ListenAndServe(addr, r))
|
log.Fatal(http.ListenAndServe(addr, r))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user