roadmap_handlers.go (740 LOC) → roadmap_handlers.go, roadmap_item_handlers.go, roadmap_import_handlers.go academy/store.go (683 LOC) → store_courses.go, store_enrollments.go cmd/server/main.go (681 LOC) → internal/app/app.go (Run+buildRouter) + internal/app/routes.go (registerXxx helpers) main.go reduced to 7 LOC thin entrypoint calling app.Run() All files under 410 LOC. Zero behavior changes, same package declarations. go vet passes on all directly-split packages. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
200 lines
5.0 KiB
Go
200 lines
5.0 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/breakpilot/ai-compliance-sdk/internal/rbac"
|
|
"github.com/breakpilot/ai-compliance-sdk/internal/roadmap"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// RoadmapHandlers handles roadmap-related HTTP requests
|
|
type RoadmapHandlers struct {
|
|
store *roadmap.Store
|
|
parser *roadmap.Parser
|
|
}
|
|
|
|
// NewRoadmapHandlers creates new roadmap handlers
|
|
func NewRoadmapHandlers(store *roadmap.Store) *RoadmapHandlers {
|
|
return &RoadmapHandlers{
|
|
store: store,
|
|
parser: roadmap.NewParser(),
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|
|
// Roadmap CRUD
|
|
// ============================================================================
|
|
|
|
// CreateRoadmap creates a new roadmap
|
|
// POST /sdk/v1/roadmaps
|
|
func (h *RoadmapHandlers) CreateRoadmap(c *gin.Context) {
|
|
var req roadmap.CreateRoadmapRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
tenantID := rbac.GetTenantID(c)
|
|
userID := rbac.GetUserID(c)
|
|
|
|
r := &roadmap.Roadmap{
|
|
TenantID: tenantID,
|
|
Title: req.Title,
|
|
Description: req.Description,
|
|
AssessmentID: req.AssessmentID,
|
|
PortfolioID: req.PortfolioID,
|
|
StartDate: req.StartDate,
|
|
TargetDate: req.TargetDate,
|
|
Status: "draft",
|
|
CreatedBy: userID,
|
|
}
|
|
|
|
if err := h.store.CreateRoadmap(c.Request.Context(), r); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusCreated, roadmap.CreateRoadmapResponse{Roadmap: *r})
|
|
}
|
|
|
|
// ListRoadmaps lists roadmaps for the tenant
|
|
// GET /sdk/v1/roadmaps
|
|
func (h *RoadmapHandlers) ListRoadmaps(c *gin.Context) {
|
|
tenantID := rbac.GetTenantID(c)
|
|
|
|
filters := &roadmap.RoadmapFilters{
|
|
Status: c.Query("status"),
|
|
Limit: 50,
|
|
}
|
|
|
|
if assessmentID := c.Query("assessment_id"); assessmentID != "" {
|
|
if id, err := uuid.Parse(assessmentID); err == nil {
|
|
filters.AssessmentID = &id
|
|
}
|
|
}
|
|
|
|
roadmaps, err := h.store.ListRoadmaps(c.Request.Context(), tenantID, filters)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"roadmaps": roadmaps,
|
|
"total": len(roadmaps),
|
|
})
|
|
}
|
|
|
|
// GetRoadmap retrieves a roadmap by ID
|
|
// GET /sdk/v1/roadmaps/:id
|
|
func (h *RoadmapHandlers) GetRoadmap(c *gin.Context) {
|
|
id, err := uuid.Parse(c.Param("id"))
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"})
|
|
return
|
|
}
|
|
|
|
r, err := h.store.GetRoadmap(c.Request.Context(), id)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
if r == nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "roadmap not found"})
|
|
return
|
|
}
|
|
|
|
// Get items
|
|
items, err := h.store.ListItems(c.Request.Context(), id, nil)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
// Get stats
|
|
stats, _ := h.store.GetRoadmapStats(c.Request.Context(), id)
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"roadmap": r,
|
|
"items": items,
|
|
"stats": stats,
|
|
})
|
|
}
|
|
|
|
// UpdateRoadmap updates a roadmap
|
|
// PUT /sdk/v1/roadmaps/:id
|
|
func (h *RoadmapHandlers) UpdateRoadmap(c *gin.Context) {
|
|
id, err := uuid.Parse(c.Param("id"))
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"})
|
|
return
|
|
}
|
|
|
|
r, err := h.store.GetRoadmap(c.Request.Context(), id)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
if r == nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "roadmap not found"})
|
|
return
|
|
}
|
|
|
|
var req roadmap.CreateRoadmapRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
r.Title = req.Title
|
|
r.Description = req.Description
|
|
r.AssessmentID = req.AssessmentID
|
|
r.PortfolioID = req.PortfolioID
|
|
r.StartDate = req.StartDate
|
|
r.TargetDate = req.TargetDate
|
|
|
|
if err := h.store.UpdateRoadmap(c.Request.Context(), r); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"roadmap": r})
|
|
}
|
|
|
|
// DeleteRoadmap deletes a roadmap
|
|
// DELETE /sdk/v1/roadmaps/:id
|
|
func (h *RoadmapHandlers) DeleteRoadmap(c *gin.Context) {
|
|
id, err := uuid.Parse(c.Param("id"))
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"})
|
|
return
|
|
}
|
|
|
|
if err := h.store.DeleteRoadmap(c.Request.Context(), id); err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{"message": "roadmap deleted"})
|
|
}
|
|
|
|
// GetRoadmapStats returns statistics for a roadmap
|
|
// GET /sdk/v1/roadmaps/:id/stats
|
|
func (h *RoadmapHandlers) GetRoadmapStats(c *gin.Context) {
|
|
id, err := uuid.Parse(c.Param("id"))
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid ID"})
|
|
return
|
|
}
|
|
|
|
stats, err := h.store.GetRoadmapStats(c.Request.Context(), id)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, stats)
|
|
}
|