// Package api provides HTTP handlers for the Compliance Engine package api import ( "net/http" "time" "github.com/breakpilot/compliance-sdk/services/compliance-engine/internal/ucca" "github.com/gin-gonic/gin" ) // Assess performs a full compliance assessment func Assess(engine *ucca.Engine) gin.HandlerFunc { return func(c *gin.Context) { var state map[string]interface{} if err := c.ShouldBindJSON(&state); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } result := engine.Assess(state) result.Timestamp = time.Now().Format(time.RFC3339) c.JSON(http.StatusOK, result) } } // AssessControl assesses a single control func AssessControl(engine *ucca.Engine) gin.HandlerFunc { return func(c *gin.Context) { var req struct { ControlID string `json:"control_id" binding:"required"` State map[string]interface{} `json:"state"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } controls := engine.GetControls() ctrl, exists := controls[req.ControlID] if !exists { c.JSON(http.StatusNotFound, gin.H{"error": "Control not found"}) return } c.JSON(http.StatusOK, gin.H{ "control": ctrl, "status": "ASSESSED", "compliance": true, "recommendations": []string{}, }) } } // AssessRegulation assesses compliance with a specific regulation func AssessRegulation(engine *ucca.Engine) gin.HandlerFunc { return func(c *gin.Context) { var req struct { RegulationCode string `json:"regulation_code" binding:"required"` State map[string]interface{} `json:"state"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } regulations := engine.GetRegulations() reg, exists := regulations[req.RegulationCode] if !exists { c.JSON(http.StatusNotFound, gin.H{"error": "Regulation not found"}) return } result := engine.Assess(req.State) c.JSON(http.StatusOK, gin.H{ "regulation": reg, "score": result.ByRegulation[req.RegulationCode], "findings": filterFindings(result.Findings, req.RegulationCode), "assessed_at": time.Now().Format(time.RFC3339), }) } } // CalculateScore calculates the compliance score func CalculateScore(engine *ucca.Engine) gin.HandlerFunc { return func(c *gin.Context) { var state map[string]interface{} if err := c.ShouldBindJSON(&state); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } result := engine.Assess(state) c.JSON(http.StatusOK, gin.H{ "overall": result.OverallScore, "trend": result.Trend, "by_regulation": result.ByRegulation, "by_domain": result.ByDomain, "calculated_at": time.Now().Format(time.RFC3339), }) } } // ScoreBreakdown provides a detailed score breakdown func ScoreBreakdown(engine *ucca.Engine) gin.HandlerFunc { return func(c *gin.Context) { var state map[string]interface{} if err := c.ShouldBindJSON(&state); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } result := engine.Assess(state) // Calculate detailed breakdown breakdown := gin.H{ "overall": gin.H{ "score": result.OverallScore, "max": 100, "trend": result.Trend, "description": getScoreDescription(result.OverallScore), }, "by_regulation": []gin.H{}, "by_domain": []gin.H{}, "by_category": gin.H{ "documentation": 75, "technical": 80, "organizational": 70, }, } for reg, score := range result.ByRegulation { breakdown["by_regulation"] = append( breakdown["by_regulation"].([]gin.H), gin.H{"code": reg, "score": score, "status": getStatus(score)}, ) } for domain, score := range result.ByDomain { breakdown["by_domain"] = append( breakdown["by_domain"].([]gin.H), gin.H{"domain": domain, "score": score, "status": getStatus(score)}, ) } c.JSON(http.StatusOK, breakdown) } } // GetObligations returns all regulatory obligations func GetObligations(engine *ucca.Engine) gin.HandlerFunc { return func(c *gin.Context) { regulations := engine.GetRegulations() obligations := []gin.H{} for code, reg := range regulations { for _, article := range reg.Articles { obligations = append(obligations, gin.H{ "id": code + "-" + article, "regulation_code": code, "regulation_name": reg.Name, "article": article, "status": "PENDING", }) } } c.JSON(http.StatusOK, gin.H{ "obligations": obligations, "total": len(obligations), }) } } // GetObligationsByRegulation returns obligations for a specific regulation func GetObligationsByRegulation(engine *ucca.Engine) gin.HandlerFunc { return func(c *gin.Context) { regCode := c.Param("regulation") regulations := engine.GetRegulations() reg, exists := regulations[regCode] if !exists { c.JSON(http.StatusNotFound, gin.H{"error": "Regulation not found"}) return } obligations := []gin.H{} for _, article := range reg.Articles { obligations = append(obligations, gin.H{ "id": regCode + "-" + article, "regulation_code": regCode, "article": article, "status": "PENDING", }) } c.JSON(http.StatusOK, gin.H{ "regulation": reg, "obligations": obligations, "total": len(obligations), }) } } // GetControlsCatalog returns the controls catalog func GetControlsCatalog(engine *ucca.Engine) gin.HandlerFunc { return func(c *gin.Context) { controls := engine.GetControls() result := []gin.H{} for _, ctrl := range controls { result = append(result, gin.H{ "id": ctrl.ID, "name": ctrl.Name, "description": ctrl.Description, "domain": ctrl.Domain, "category": ctrl.Category, "objective": ctrl.Objective, }) } c.JSON(http.StatusOK, gin.H{ "controls": result, "total": len(result), }) } } // GetControlsByDomain returns controls for a specific domain func GetControlsByDomain(engine *ucca.Engine) gin.HandlerFunc { return func(c *gin.Context) { domain := c.Param("domain") controls := engine.GetControlsByDomain(domain) result := []gin.H{} for _, ctrl := range controls { result = append(result, gin.H{ "id": ctrl.ID, "name": ctrl.Name, "description": ctrl.Description, "category": ctrl.Category, "objective": ctrl.Objective, "guidance": ctrl.Guidance, "evidence": ctrl.Evidence, }) } c.JSON(http.StatusOK, gin.H{ "domain": domain, "controls": result, "total": len(result), }) } } // ListPolicies lists all loaded policies func ListPolicies(engine *ucca.Engine) gin.HandlerFunc { return func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "total_rules": engine.RuleCount(), "total_regulations": len(engine.GetRegulations()), "total_controls": len(engine.GetControls()), "regulations": getRegulationCodes(engine), }) } } // GetPolicy returns details of a specific policy func GetPolicy(engine *ucca.Engine) gin.HandlerFunc { return func(c *gin.Context) { policyID := c.Param("id") regulations := engine.GetRegulations() if reg, exists := regulations[policyID]; exists { c.JSON(http.StatusOK, reg) return } c.JSON(http.StatusNotFound, gin.H{"error": "Policy not found"}) } } // Helper functions func filterFindings(findings []ucca.Finding, regulation string) []ucca.Finding { result := []ucca.Finding{} for _, f := range findings { if f.Regulation == regulation { result = append(result, f) } } return result } func getScoreDescription(score int) string { switch { case score >= 90: return "Excellent - Full compliance achieved" case score >= 75: return "Good - Minor improvements needed" case score >= 50: return "Fair - Significant work required" default: return "Poor - Major compliance gaps exist" } } func getStatus(score int) string { switch { case score >= 80: return "COMPLIANT" case score >= 60: return "PARTIAL" default: return "NON_COMPLIANT" } } func getRegulationCodes(engine *ucca.Engine) []string { regulations := engine.GetRegulations() codes := make([]string, 0, len(regulations)) for code := range regulations { codes = append(codes, code) } return codes }