Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
SDLCGenerateHandler was using r.Route() to create a sub-router at
/projects/{id}/sdlc/features/{slug}, which shadowed SDLCHandler's
nested routes like /features/{slug}/artifacts/{type}/approve.
Changed to direct route registration to avoid chi route conflicts.
This fixes 404 errors on SDLC feature and artifact endpoints.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
90 lines
2.8 KiB
Go
90 lines
2.8 KiB
Go
package handlers
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
"github.com/orchard9/rdev/internal/auth"
|
|
"github.com/orchard9/rdev/internal/domain"
|
|
"github.com/orchard9/rdev/internal/service"
|
|
"github.com/orchard9/rdev/pkg/api"
|
|
)
|
|
|
|
// SDLCGenerateHandler handles async SDLC artifact generation endpoints.
|
|
type SDLCGenerateHandler struct {
|
|
generateSvc *service.SDLCGenerateService
|
|
}
|
|
|
|
// NewSDLCGenerateHandler creates a new SDLC generate handler.
|
|
func NewSDLCGenerateHandler(generateSvc *service.SDLCGenerateService) *SDLCGenerateHandler {
|
|
return &SDLCGenerateHandler{
|
|
generateSvc: generateSvc,
|
|
}
|
|
}
|
|
|
|
// Mount registers the generate endpoint on the router.
|
|
// Note: Uses direct route to avoid conflict with /projects/{id}/sdlc in sdlc.go.
|
|
// Creating a Route() group at /projects/{id}/sdlc/features/{slug} shadows
|
|
// the SDLCHandler's nested routes like /features/{slug}/artifacts/{type}/approve.
|
|
func (h *SDLCGenerateHandler) Mount(r api.Router) {
|
|
r.With(auth.RequireScope(auth.ScopeProjectsExecute, auth.ScopeAdmin)).
|
|
Post("/projects/{id}/sdlc/features/{slug}/generate", h.Generate)
|
|
}
|
|
|
|
// GenerateRequest is the request body for POST /projects/{id}/sdlc/features/{slug}/generate.
|
|
type GenerateRequest struct {
|
|
// ArtifactType is the type of artifact to generate: spec, design, tasks, code, qa
|
|
ArtifactType string `json:"artifact_type"`
|
|
|
|
// TaskID is the specific task to implement (required for artifact_type: "code")
|
|
TaskID string `json:"task_id,omitempty"`
|
|
|
|
// Provider specifies which code agent to use (optional)
|
|
Provider string `json:"provider,omitempty"`
|
|
}
|
|
|
|
// Generate handles POST /projects/{id}/sdlc/features/{slug}/generate.
|
|
// It enqueues an async task to generate an SDLC artifact.
|
|
func (h *SDLCGenerateHandler) Generate(w http.ResponseWriter, r *http.Request) {
|
|
projectID := chi.URLParam(r, "id")
|
|
featureSlug := chi.URLParam(r, "slug")
|
|
|
|
var req GenerateRequest
|
|
if err := api.DecodeJSON(r, &req); err != nil {
|
|
api.WriteBadRequest(w, r, "invalid request body")
|
|
return
|
|
}
|
|
|
|
// Validate artifact_type
|
|
if req.ArtifactType == "" {
|
|
api.WriteBadRequest(w, r, "artifact_type is required")
|
|
return
|
|
}
|
|
if !domain.IsValidGenerateArtifactType(req.ArtifactType) {
|
|
api.WriteBadRequest(w, r, "invalid artifact_type: must be one of spec, design, tasks, code, qa")
|
|
return
|
|
}
|
|
|
|
// Validate task_id is provided for code generation
|
|
if req.ArtifactType == "code" && req.TaskID == "" {
|
|
api.WriteBadRequest(w, r, "task_id is required for artifact_type: code")
|
|
return
|
|
}
|
|
|
|
ctx, cancel := context.WithTimeout(r.Context(), TimeoutStandard)
|
|
defer cancel()
|
|
|
|
result, err := h.generateSvc.GenerateArtifact(ctx, projectID, featureSlug, &domain.SDLCGenerateRequest{
|
|
ArtifactType: req.ArtifactType,
|
|
TaskID: req.TaskID,
|
|
Provider: req.Provider,
|
|
})
|
|
if err != nil {
|
|
writeSDLCError(w, r, err)
|
|
return
|
|
}
|
|
|
|
api.WriteJSON(w, r, http.StatusAccepted, result)
|
|
}
|