summaryrefslogtreecommitdiff
path: root/internal/api/epics.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/api/epics.go')
-rw-r--r--internal/api/epics.go90
1 files changed, 90 insertions, 0 deletions
diff --git a/internal/api/epics.go b/internal/api/epics.go
new file mode 100644
index 0000000..e22a377
--- /dev/null
+++ b/internal/api/epics.go
@@ -0,0 +1,90 @@
+package api
+
+import (
+ "encoding/json"
+ "net/http"
+
+ "github.com/google/uuid"
+ "github.com/thepeterstone/claudomator/internal/storage"
+ "github.com/thepeterstone/claudomator/internal/story"
+)
+
+func (s *Server) handleListEpics(w http.ResponseWriter, r *http.Request) {
+ status := r.URL.Query().Get("status")
+ epics, err := s.store.ListEpics(status)
+ if err != nil {
+ writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
+ return
+ }
+ if epics == nil {
+ epics = []*story.Epic{}
+ }
+ writeJSON(w, http.StatusOK, epics)
+}
+
+func (s *Server) handleCreateEpic(w http.ResponseWriter, r *http.Request) {
+ var e story.Epic
+ if err := json.NewDecoder(r.Body).Decode(&e); err != nil {
+ writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid JSON: " + err.Error()})
+ return
+ }
+ if e.Name == "" {
+ writeJSON(w, http.StatusBadRequest, map[string]string{"error": "name is required"})
+ return
+ }
+ if e.ID == "" {
+ e.ID = uuid.New().String()
+ }
+ if err := s.store.CreateEpic(&e); err != nil {
+ writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
+ return
+ }
+ writeJSON(w, http.StatusCreated, e)
+}
+
+func (s *Server) handleGetEpic(w http.ResponseWriter, r *http.Request) {
+ id := r.PathValue("id")
+ e, err := s.store.GetEpic(id)
+ if err != nil {
+ writeJSON(w, http.StatusNotFound, map[string]string{"error": "epic not found"})
+ return
+ }
+ writeJSON(w, http.StatusOK, e)
+}
+
+func (s *Server) handleUpdateEpic(w http.ResponseWriter, r *http.Request) {
+ id := r.PathValue("id")
+ existing, err := s.store.GetEpic(id)
+ if err != nil {
+ writeJSON(w, http.StatusNotFound, map[string]string{"error": "epic not found"})
+ return
+ }
+ createdAt := existing.CreatedAt
+ if err := json.NewDecoder(r.Body).Decode(existing); err != nil {
+ writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid JSON: " + err.Error()})
+ return
+ }
+ existing.ID = id // ensure ID cannot be changed via body
+ existing.CreatedAt = createdAt
+ if err := s.store.UpdateEpic(existing); err != nil {
+ writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
+ return
+ }
+ writeJSON(w, http.StatusOK, existing)
+}
+
+// handleListEpicStories lists stories whose epic_id matches the path {id}.
+// Mirrors handleListSubtasks: no existence check on the epic itself, just
+// returns an empty list if there are no matching stories.
+func (s *Server) handleListEpicStories(w http.ResponseWriter, r *http.Request) {
+ id := r.PathValue("id")
+ stories, err := s.store.ListStories(storage.StoryFilter{EpicID: id})
+ if err != nil {
+ writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
+ return
+ }
+ if stories == nil {
+ stories = []*story.Story{}
+ }
+ writeJSON(w, http.StatusOK, stories)
+}