refactor(go): split obligations, portfolio, rbac, whistleblower handlers and stores, roadmap parser
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>
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/breakpilot/ai-compliance-sdk/internal/portfolio"
|
||||
"github.com/breakpilot/ai-compliance-sdk/internal/rbac"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// ============================================================================
|
||||
// Portfolio Items
|
||||
// ============================================================================
|
||||
|
||||
// AddItem adds an item to a portfolio
|
||||
// POST /sdk/v1/portfolios/:id/items
|
||||
func (h *PortfolioHandlers) AddItem(c *gin.Context) {
|
||||
portfolioID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid portfolio ID"})
|
||||
return
|
||||
}
|
||||
|
||||
var req portfolio.AddItemRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
userID := rbac.GetUserID(c)
|
||||
|
||||
item := &portfolio.PortfolioItem{
|
||||
PortfolioID: portfolioID,
|
||||
ItemType: req.ItemType,
|
||||
ItemID: req.ItemID,
|
||||
Tags: req.Tags,
|
||||
Notes: req.Notes,
|
||||
AddedBy: userID,
|
||||
}
|
||||
|
||||
if err := h.store.AddItem(c.Request.Context(), item); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, gin.H{"item": item})
|
||||
}
|
||||
|
||||
// ListItems lists items in a portfolio
|
||||
// GET /sdk/v1/portfolios/:id/items
|
||||
func (h *PortfolioHandlers) ListItems(c *gin.Context) {
|
||||
portfolioID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid portfolio ID"})
|
||||
return
|
||||
}
|
||||
|
||||
var itemType *portfolio.ItemType
|
||||
if t := c.Query("type"); t != "" {
|
||||
it := portfolio.ItemType(t)
|
||||
itemType = &it
|
||||
}
|
||||
|
||||
items, err := h.store.ListItems(c.Request.Context(), portfolioID, itemType)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"items": items,
|
||||
"total": len(items),
|
||||
})
|
||||
}
|
||||
|
||||
// BulkAddItems adds multiple items to a portfolio
|
||||
// POST /sdk/v1/portfolios/:id/items/bulk
|
||||
func (h *PortfolioHandlers) BulkAddItems(c *gin.Context) {
|
||||
portfolioID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid portfolio ID"})
|
||||
return
|
||||
}
|
||||
|
||||
var req portfolio.BulkAddItemsRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
userID := rbac.GetUserID(c)
|
||||
|
||||
items := make([]portfolio.PortfolioItem, len(req.Items))
|
||||
for i, r := range req.Items {
|
||||
items[i] = portfolio.PortfolioItem{
|
||||
ItemType: r.ItemType,
|
||||
ItemID: r.ItemID,
|
||||
Tags: r.Tags,
|
||||
Notes: r.Notes,
|
||||
}
|
||||
}
|
||||
|
||||
result, err := h.store.BulkAddItems(c.Request.Context(), portfolioID, items, userID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, result)
|
||||
}
|
||||
|
||||
// RemoveItem removes an item from a portfolio
|
||||
// DELETE /sdk/v1/portfolios/:id/items/:itemId
|
||||
func (h *PortfolioHandlers) RemoveItem(c *gin.Context) {
|
||||
itemID, err := uuid.Parse(c.Param("itemId"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid item ID"})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.store.RemoveItem(c.Request.Context(), itemID); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "item removed"})
|
||||
}
|
||||
|
||||
// ReorderItems updates the order of items
|
||||
// PUT /sdk/v1/portfolios/:id/items/order
|
||||
func (h *PortfolioHandlers) ReorderItems(c *gin.Context) {
|
||||
portfolioID, err := uuid.Parse(c.Param("id"))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid portfolio ID"})
|
||||
return
|
||||
}
|
||||
|
||||
var req struct {
|
||||
ItemIDs []uuid.UUID `json:"item_ids"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.store.UpdateItemOrder(c.Request.Context(), portfolioID, req.ItemIDs); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "items reordered"})
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Merge Operations
|
||||
// ============================================================================
|
||||
|
||||
// MergePortfolios merges two portfolios
|
||||
// POST /sdk/v1/portfolios/merge
|
||||
func (h *PortfolioHandlers) MergePortfolios(c *gin.Context) {
|
||||
var req portfolio.MergeRequest
|
||||
if err := c.ShouldBindJSON(&req); err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
source, err := h.store.GetPortfolio(c.Request.Context(), req.SourcePortfolioID)
|
||||
if err != nil || source == nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "source portfolio not found"})
|
||||
return
|
||||
}
|
||||
|
||||
target, err := h.store.GetPortfolio(c.Request.Context(), req.TargetPortfolioID)
|
||||
if err != nil || target == nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "target portfolio not found"})
|
||||
return
|
||||
}
|
||||
|
||||
if req.Strategy == "" {
|
||||
req.Strategy = portfolio.MergeStrategyUnion
|
||||
}
|
||||
|
||||
userID := rbac.GetUserID(c)
|
||||
|
||||
result, err := h.store.MergePortfolios(c.Request.Context(), &req, userID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"message": "portfolios merged",
|
||||
"result": result,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user