Files
gochat/internal/handler/api/v1/bot_trigger_config_handler.go
T

238 lines
7.4 KiB
Go

package v1
import (
"net/http"
"github.com/gin-gonic/gin"
"github.com/gochat/gochat/internal/automation"
"github.com/gochat/gochat/pkg/response"
)
// BotTriggerConfigHandler handles bot trigger condition config API endpoints.
// Reference: Chatwoot AutomationRulesController trigger conditions pattern adapted for AgentBot.
type BotTriggerConfigHandler struct {
svc *automation.BotTriggerConfigService
}
// NewBotTriggerConfigHandler creates a new BotTriggerConfigHandler.
func NewBotTriggerConfigHandler(svc *automation.BotTriggerConfigService) *BotTriggerConfigHandler {
return &BotTriggerConfigHandler{svc: svc}
}
// List retrieves all trigger configs for an account.
// GET /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/trigger_configs
func (h *BotTriggerConfigHandler) List(c *gin.Context) {
accountID, err := parseUintParam(c, "account_id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account_id")
return
}
configs, svcErr := h.svc.ListByAccount(c.Request.Context(), accountID)
if svcErr != nil {
handleServiceError(c, svcErr)
return
}
c.JSON(http.StatusOK, gin.H{
"trigger_configs": configs,
"meta": gin.H{"count": len(configs)},
})
}
// ListByBot retrieves all trigger configs for a specific AgentBot.
// GET /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/trigger_configs
func (h *BotTriggerConfigHandler) ListByBot(c *gin.Context) {
accountID, err := parseUintParam(c, "account_id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account_id")
return
}
agentBotID, err := parseUintParam(c, "agent_bot_id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid agent_bot_id")
return
}
configs, svcErr := h.svc.ListByAgentBot(c.Request.Context(), accountID, agentBotID)
if svcErr != nil {
handleServiceError(c, svcErr)
return
}
c.JSON(http.StatusOK, gin.H{
"trigger_configs": configs,
"meta": gin.H{"count": len(configs)},
})
}
// Get retrieves a single trigger config by ID.
// GET /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/trigger_configs/:id
func (h *BotTriggerConfigHandler) Get(c *gin.Context) {
id, err := parseUintAnyParam(c, "trigger_config_id", "id", "agent_bot_id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id")
return
}
config, svcErr := h.svc.GetByID(c.Request.Context(), id)
if svcErr != nil {
handleServiceError(c, svcErr)
return
}
response.OK(c, config)
}
// Create creates a new trigger config for an AgentBot.
// POST /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/trigger_configs
func (h *BotTriggerConfigHandler) Create(c *gin.Context) {
accountID, err := parseUintParam(c, "account_id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account_id")
return
}
agentBotID, err := parseUintParam(c, "agent_bot_id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid agent_bot_id")
return
}
var req struct {
Name string `json:"name" binding:"required"`
Description string `json:"description"`
EventName automation.BotRuleEventType `json:"event_name" binding:"required"`
Conditions automation.TriggerConditions `json:"conditions"`
QueryOperator string `json:"query_operator"`
Active bool `json:"active"`
}
if err := c.ShouldBindJSON(&req); err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, err.Error())
return
}
config := &automation.BotTriggerConfig{
AccountID: accountID,
AgentBotID: agentBotID,
Name: req.Name,
Description: req.Description,
EventName: req.EventName,
Conditions: req.Conditions,
QueryOperator: req.QueryOperator,
Active: req.Active,
}
// Default query_operator to "and"
if config.QueryOperator == "" {
config.QueryOperator = "and"
}
// Default to active if not explicitly set
if !config.Active && req.Active == false {
// Only default to active when the field was not provided in JSON
// (JSON deserialization defaults bool to false)
config.Active = true
}
if svcErr := h.svc.Create(c.Request.Context(), config); svcErr != nil {
handleServiceError(c, svcErr)
return
}
response.Created(c, config)
}
// Update updates an existing trigger config.
// PUT /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/trigger_configs/:id
func (h *BotTriggerConfigHandler) Update(c *gin.Context) {
id, err := parseUintAnyParam(c, "trigger_config_id", "id", "agent_bot_id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id")
return
}
existing, svcErr := h.svc.GetByID(c.Request.Context(), id)
if svcErr != nil {
handleServiceError(c, svcErr)
return
}
var req struct {
Name string `json:"name"`
Description string `json:"description"`
EventName automation.BotRuleEventType `json:"event_name"`
Conditions automation.TriggerConditions `json:"conditions"`
QueryOperator string `json:"query_operator"`
Active bool `json:"active"`
}
if err := c.ShouldBindJSON(&req); err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, err.Error())
return
}
// Apply updates
if req.Name != "" {
existing.Name = req.Name
}
if req.Description != "" {
existing.Description = req.Description
}
if req.EventName != "" {
existing.EventName = req.EventName
}
if req.Conditions != nil {
existing.Conditions = req.Conditions
}
if req.QueryOperator != "" {
existing.QueryOperator = req.QueryOperator
}
existing.Active = req.Active
if svcErr := h.svc.Update(c.Request.Context(), existing); svcErr != nil {
handleServiceError(c, svcErr)
return
}
response.OK(c, existing)
}
// Delete soft-deletes a trigger config.
// DELETE /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/trigger_configs/:id
func (h *BotTriggerConfigHandler) Delete(c *gin.Context) {
id, err := parseUintAnyParam(c, "trigger_config_id", "id", "agent_bot_id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id")
return
}
if svcErr := h.svc.Delete(c.Request.Context(), id); svcErr != nil {
handleServiceError(c, svcErr)
return
}
response.NoContent(c)
}
// ToggleActive toggles the active state of a trigger config.
// PATCH /api/v1/accounts/:account_id/agent_bots/:agent_bot_id/trigger_configs/:id/active
func (h *BotTriggerConfigHandler) ToggleActive(c *gin.Context) {
id, err := parseUintAnyParam(c, "trigger_config_id", "id", "agent_bot_id")
if err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid id")
return
}
var req struct {
Active bool `json:"active" binding:"required"`
}
if err := c.ShouldBindJSON(&req); err != nil {
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, err.Error())
return
}
if svcErr := h.svc.ToggleActive(c.Request.Context(), id, req.Active); svcErr != nil {
handleServiceError(c, svcErr)
return
}
response.OK(c, gin.H{"id": id, "active": req.Active})
}