diff options
| author | Claudomator Agent <agent@claudomator> | 2026-03-09 07:30:18 +0000 |
|---|---|---|
| committer | Claudomator Agent <agent@claudomator> | 2026-03-09 07:30:24 +0000 |
| commit | 933af819ae3ee7ea0cf6b750815ab185043e19fc (patch) | |
| tree | d3de98a3168e578cbe06222e7141c66e678eb82a /internal/api/server.go | |
| parent | b33566b534185a444a392c36e3f307a5ecad8d4b (diff) | |
api: validate ?state= param in handleListTasks; standardize operation response shapes
- handleListTasks: validate ?state= against known states, return 400 with clear
error for unrecognized values (e.g. ?state=BOGUS)
- handleCancelTask: replace {"status":"cancelling"|"cancelled"} with
{"message":"...","task_id":"..."} to match run/resume shape
- handleAnswerQuestion: replace {"status":"queued"} with
{"message":"task queued for resume","task_id":"..."}
- Tests: add TestListTasks_InvalidState_Returns400, TestListTasks_ValidState_Returns200,
TestCancelTask_ResponseShape, TestAnswerQuestion_ResponseShape,
TestRunTask_ResponseShape, TestResumeTimedOut_ResponseShape
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/api/server.go')
| -rw-r--r-- | internal/api/server.go | 27 |
1 files changed, 23 insertions, 4 deletions
diff --git a/internal/api/server.go b/internal/api/server.go index 0868295..a6e708a 100644 --- a/internal/api/server.go +++ b/internal/api/server.go @@ -203,7 +203,7 @@ func (s *Server) handleCancelTask(w http.ResponseWriter, r *http.Request) { } // If the task is actively running in the pool, cancel it there. if s.pool.Cancel(taskID) { - writeJSON(w, http.StatusOK, map[string]string{"status": "cancelling"}) + writeJSON(w, http.StatusOK, map[string]string{"message": "task cancellation requested", "task_id": taskID}) return } // For non-running tasks (PENDING, QUEUED), transition directly to CANCELLED. @@ -215,7 +215,7 @@ func (s *Server) handleCancelTask(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusInternalServerError, map[string]string{"error": "failed to cancel task"}) return } - writeJSON(w, http.StatusOK, map[string]string{"status": "cancelled"}) + writeJSON(w, http.StatusOK, map[string]string{"message": "task cancelled", "task_id": taskID}) } func (s *Server) handleAnswerQuestion(w http.ResponseWriter, r *http.Request) { @@ -272,7 +272,7 @@ func (s *Server) handleAnswerQuestion(w http.ResponseWriter, r *http.Request) { return } - writeJSON(w, http.StatusOK, map[string]string{"status": "queued"}) + writeJSON(w, http.StatusOK, map[string]string{"message": "task queued for resume", "task_id": taskID}) } func (s *Server) handleResumeTimedOutTask(w http.ResponseWriter, r *http.Request) { @@ -412,10 +412,29 @@ func (s *Server) handleCreateTask(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusCreated, t) } +// validTaskStates is the set of all known task states for query param validation. +var validTaskStates = map[task.State]bool{ + task.StatePending: true, + task.StateQueued: true, + task.StateRunning: true, + task.StateReady: true, + task.StateCompleted: true, + task.StateFailed: true, + task.StateTimedOut: true, + task.StateCancelled: true, + task.StateBudgetExceeded: true, + task.StateBlocked: true, +} + func (s *Server) handleListTasks(w http.ResponseWriter, r *http.Request) { filter := storage.TaskFilter{} if state := r.URL.Query().Get("state"); state != "" { - filter.State = task.State(state) + ts := task.State(state) + if !validTaskStates[ts] { + writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid state: " + state}) + return + } + filter.State = ts } tasks, err := s.store.ListTasks(filter) if err != nil { |
