rdev/internal/handlers/sdlc_artifacts.go
jordan 6e8f5821af feat: add artifact pass/fail/needs-fix lifecycle for SDLC execution phases
- Add pass/fail/needs-fix CLI commands to cmd/sdlc/cmd_artifact.go
- Add 3 new methods to SDLCExecutor interface in internal/port
- Implement methods in kubernetes adapter
- Add service methods to SDLCService
- Add HTTP handlers for POST .../artifacts/{type}/pass|fail|needs-fix
- Update 6 skeleton commands to evaluate and set artifact status
- Update test mocks

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 22:14:53 -07:00

169 lines
4.6 KiB
Go

package handlers
import (
"context"
"net/http"
"github.com/go-chi/chi/v5"
"github.com/orchard9/rdev/internal/sdlc"
"github.com/orchard9/rdev/pkg/api"
)
// GetArtifactStatus returns artifact statuses for a feature.
// GET /projects/{id}/sdlc/features/{slug}/artifacts
func (h *SDLCHandler) GetArtifactStatus(w http.ResponseWriter, r *http.Request) {
projectID := chi.URLParam(r, "id")
slug := chi.URLParam(r, "slug")
ctx, cancel := context.WithTimeout(r.Context(), TimeoutStandard)
defer cancel()
artifacts, err := h.sdlcService.GetArtifactStatus(ctx, projectID, slug)
if err != nil {
writeSDLCError(w, r, err)
return
}
api.WriteSuccess(w, r, artifacts)
}
// ApproveArtifact approves a feature artifact.
// POST /projects/{id}/sdlc/features/{slug}/artifacts/{type}/approve
func (h *SDLCHandler) ApproveArtifact(w http.ResponseWriter, r *http.Request) {
projectID := chi.URLParam(r, "id")
slug := chi.URLParam(r, "slug")
artTypeStr := chi.URLParam(r, "type")
artType := sdlc.ArtifactType(artTypeStr)
if !sdlc.IsValidArtifactType(artType) {
api.WriteBadRequest(w, r, "invalid artifact type: "+artTypeStr)
return
}
ctx, cancel := context.WithTimeout(r.Context(), TimeoutHeavyWrite)
defer cancel()
if err := h.sdlcService.ApproveArtifact(ctx, projectID, slug, artType); err != nil {
writeSDLCError(w, r, err)
return
}
api.WriteSuccess(w, r, map[string]any{
"feature": slug,
"artifact": artTypeStr,
"status": "approved",
})
}
// RejectArtifact rejects a feature artifact.
// POST /projects/{id}/sdlc/features/{slug}/artifacts/{type}/reject
func (h *SDLCHandler) RejectArtifact(w http.ResponseWriter, r *http.Request) {
projectID := chi.URLParam(r, "id")
slug := chi.URLParam(r, "slug")
artTypeStr := chi.URLParam(r, "type")
artType := sdlc.ArtifactType(artTypeStr)
if !sdlc.IsValidArtifactType(artType) {
api.WriteBadRequest(w, r, "invalid artifact type: "+artTypeStr)
return
}
ctx, cancel := context.WithTimeout(r.Context(), TimeoutHeavyWrite)
defer cancel()
if err := h.sdlcService.RejectArtifact(ctx, projectID, slug, artType); err != nil {
writeSDLCError(w, r, err)
return
}
api.WriteSuccess(w, r, map[string]any{
"feature": slug,
"artifact": artTypeStr,
"status": "rejected",
})
}
// PassArtifact marks a feature artifact as passed (for execution artifacts).
// POST /projects/{id}/sdlc/features/{slug}/artifacts/{type}/pass
func (h *SDLCHandler) PassArtifact(w http.ResponseWriter, r *http.Request) {
projectID := chi.URLParam(r, "id")
slug := chi.URLParam(r, "slug")
artTypeStr := chi.URLParam(r, "type")
artType := sdlc.ArtifactType(artTypeStr)
if !sdlc.IsValidArtifactType(artType) {
api.WriteBadRequest(w, r, "invalid artifact type: "+artTypeStr)
return
}
ctx, cancel := context.WithTimeout(r.Context(), TimeoutHeavyWrite)
defer cancel()
if err := h.sdlcService.PassArtifact(ctx, projectID, slug, artType); err != nil {
writeSDLCError(w, r, err)
return
}
api.WriteSuccess(w, r, map[string]any{
"feature": slug,
"artifact": artTypeStr,
"status": "passed",
})
}
// FailArtifact marks a feature artifact as failed (for execution artifacts).
// POST /projects/{id}/sdlc/features/{slug}/artifacts/{type}/fail
func (h *SDLCHandler) FailArtifact(w http.ResponseWriter, r *http.Request) {
projectID := chi.URLParam(r, "id")
slug := chi.URLParam(r, "slug")
artTypeStr := chi.URLParam(r, "type")
artType := sdlc.ArtifactType(artTypeStr)
if !sdlc.IsValidArtifactType(artType) {
api.WriteBadRequest(w, r, "invalid artifact type: "+artTypeStr)
return
}
ctx, cancel := context.WithTimeout(r.Context(), TimeoutHeavyWrite)
defer cancel()
if err := h.sdlcService.FailArtifact(ctx, projectID, slug, artType); err != nil {
writeSDLCError(w, r, err)
return
}
api.WriteSuccess(w, r, map[string]any{
"feature": slug,
"artifact": artTypeStr,
"status": "failed",
})
}
// NeedsFixArtifact marks a feature artifact as needing fixes.
// POST /projects/{id}/sdlc/features/{slug}/artifacts/{type}/needs-fix
func (h *SDLCHandler) NeedsFixArtifact(w http.ResponseWriter, r *http.Request) {
projectID := chi.URLParam(r, "id")
slug := chi.URLParam(r, "slug")
artTypeStr := chi.URLParam(r, "type")
artType := sdlc.ArtifactType(artTypeStr)
if !sdlc.IsValidArtifactType(artType) {
api.WriteBadRequest(w, r, "invalid artifact type: "+artTypeStr)
return
}
ctx, cancel := context.WithTimeout(r.Context(), TimeoutHeavyWrite)
defer cancel()
if err := h.sdlcService.NeedsFixArtifact(ctx, projectID, slug, artType); err != nil {
writeSDLCError(w, r, err)
return
}
api.WriteSuccess(w, r, map[string]any{
"feature": slug,
"artifact": artTypeStr,
"status": "needs_fix",
})
}