Initial commit: breakpilot-compliance - Compliance SDK Platform
Services: Admin-Compliance, Backend-Compliance, AI-Compliance-SDK, Consent-SDK, Developer-Portal, PCA-Platform, DSMS Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,329 @@
|
||||
// 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
|
||||
}
|
||||
Reference in New Issue
Block a user