Split 7 files exceeding the 500 LOC hard cap into 16 files, all under 500 LOC. No exported symbols renamed; zero behavior changes. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
188 lines
5.2 KiB
Go
188 lines
5.2 KiB
Go
package handlers
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/breakpilot/ai-compliance-sdk/internal/rbac"
|
|
"github.com/breakpilot/ai-compliance-sdk/internal/ucca"
|
|
)
|
|
|
|
// GetByRegulation returns obligations grouped by regulation
|
|
// GET /sdk/v1/ucca/obligations/:assessmentId/by-regulation
|
|
func (h *ObligationsHandlers) GetByRegulation(c *gin.Context) {
|
|
tenantID := rbac.GetTenantID(c)
|
|
assessmentID := c.Param("assessmentId")
|
|
|
|
if h.store == nil {
|
|
c.JSON(http.StatusNotImplemented, gin.H{"error": "Persistence not configured"})
|
|
return
|
|
}
|
|
|
|
id, err := uuid.Parse(assessmentID)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid assessment ID"})
|
|
return
|
|
}
|
|
|
|
assessment, err := h.store.GetAssessment(c.Request.Context(), tenantID, id)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "Assessment not found"})
|
|
return
|
|
}
|
|
|
|
grouped := h.registry.GroupByRegulation(assessment.Overview.Obligations)
|
|
|
|
c.JSON(http.StatusOK, ucca.ObligationsByRegulationResponse{
|
|
Regulations: grouped,
|
|
})
|
|
}
|
|
|
|
// GetByDeadline returns obligations grouped by deadline timeframe
|
|
// GET /sdk/v1/ucca/obligations/:assessmentId/by-deadline
|
|
func (h *ObligationsHandlers) GetByDeadline(c *gin.Context) {
|
|
tenantID := rbac.GetTenantID(c)
|
|
assessmentID := c.Param("assessmentId")
|
|
|
|
if h.store == nil {
|
|
c.JSON(http.StatusNotImplemented, gin.H{"error": "Persistence not configured"})
|
|
return
|
|
}
|
|
|
|
id, err := uuid.Parse(assessmentID)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid assessment ID"})
|
|
return
|
|
}
|
|
|
|
assessment, err := h.store.GetAssessment(c.Request.Context(), tenantID, id)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "Assessment not found"})
|
|
return
|
|
}
|
|
|
|
grouped := h.registry.GroupByDeadline(assessment.Overview.Obligations)
|
|
|
|
c.JSON(http.StatusOK, grouped)
|
|
}
|
|
|
|
// GetByResponsible returns obligations grouped by responsible role
|
|
// GET /sdk/v1/ucca/obligations/:assessmentId/by-responsible
|
|
func (h *ObligationsHandlers) GetByResponsible(c *gin.Context) {
|
|
tenantID := rbac.GetTenantID(c)
|
|
assessmentID := c.Param("assessmentId")
|
|
|
|
if h.store == nil {
|
|
c.JSON(http.StatusNotImplemented, gin.H{"error": "Persistence not configured"})
|
|
return
|
|
}
|
|
|
|
id, err := uuid.Parse(assessmentID)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid assessment ID"})
|
|
return
|
|
}
|
|
|
|
assessment, err := h.store.GetAssessment(c.Request.Context(), tenantID, id)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "Assessment not found"})
|
|
return
|
|
}
|
|
|
|
grouped := h.registry.GroupByResponsible(assessment.Overview.Obligations)
|
|
|
|
c.JSON(http.StatusOK, ucca.ObligationsByResponsibleResponse{
|
|
ByRole: grouped,
|
|
})
|
|
}
|
|
|
|
// ListRegulations returns all available regulation modules
|
|
// GET /sdk/v1/ucca/obligations/regulations
|
|
func (h *ObligationsHandlers) ListRegulations(c *gin.Context) {
|
|
modules := h.registry.ListModules()
|
|
|
|
c.JSON(http.StatusOK, ucca.AvailableRegulationsResponse{
|
|
Regulations: modules,
|
|
})
|
|
}
|
|
|
|
// GetDecisionTree returns the decision tree for a specific regulation
|
|
// GET /sdk/v1/ucca/obligations/regulations/:regulationId/decision-tree
|
|
func (h *ObligationsHandlers) GetDecisionTree(c *gin.Context) {
|
|
regulationID := c.Param("regulationId")
|
|
|
|
tree, err := h.registry.GetDecisionTree(regulationID)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, tree)
|
|
}
|
|
|
|
// GetTOMControlsForObligation returns TOM controls linked to an obligation
|
|
// GET /sdk/v1/ucca/obligations/:id/tom-controls
|
|
func (h *ObligationsHandlers) GetTOMControlsForObligation(c *gin.Context) {
|
|
obligationID := c.Param("obligationId")
|
|
|
|
if h.tomMapper == nil {
|
|
c.JSON(http.StatusNotImplemented, gin.H{"error": "TOM mapping not available"})
|
|
return
|
|
}
|
|
|
|
controls := h.tomMapper.GetControlsForObligation(obligationID)
|
|
controlIDs := h.tomMapper.GetControlIDsForObligation(obligationID)
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"obligation_id": obligationID,
|
|
"control_ids": controlIDs,
|
|
"controls": controls,
|
|
"count": len(controls),
|
|
})
|
|
}
|
|
|
|
// GapAnalysis performs a TOM control gap analysis
|
|
// POST /sdk/v1/ucca/obligations/gap-analysis
|
|
func (h *ObligationsHandlers) GapAnalysis(c *gin.Context) {
|
|
if h.gapAnalyzer == nil {
|
|
c.JSON(http.StatusNotImplemented, gin.H{"error": "Gap analysis not available"})
|
|
return
|
|
}
|
|
|
|
var req ucca.GapAnalysisRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body", "details": err.Error()})
|
|
return
|
|
}
|
|
|
|
result := h.gapAnalyzer.Analyze(&req)
|
|
c.JSON(http.StatusOK, result)
|
|
}
|
|
|
|
// GetObligationsForControl returns obligations linked to a TOM control
|
|
// GET /sdk/v1/ucca/obligations/tom-controls/:controlId/obligations
|
|
func (h *ObligationsHandlers) GetObligationsForControl(c *gin.Context) {
|
|
controlID := c.Param("controlId")
|
|
|
|
if h.tomMapper == nil {
|
|
c.JSON(http.StatusNotImplemented, gin.H{"error": "TOM mapping not available"})
|
|
return
|
|
}
|
|
|
|
obligationIDs := h.tomMapper.GetObligationsForControl(controlID)
|
|
|
|
var control *ucca.TOMControl
|
|
if h.tomIndex != nil {
|
|
control, _ = h.tomIndex.GetControl(controlID)
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"control_id": controlID,
|
|
"control": control,
|
|
"obligation_ids": obligationIDs,
|
|
"count": len(obligationIDs),
|
|
})
|
|
}
|