feat: complete current GoChat updates
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/gochat/gochat/internal/model"
|
||||
"github.com/gochat/gochat/internal/repository"
|
||||
"github.com/gochat/gochat/internal/service"
|
||||
"github.com/gochat/gochat/internal/ws"
|
||||
applogger "github.com/gochat/gochat/pkg/logger"
|
||||
"github.com/gochat/gochat/pkg/response"
|
||||
)
|
||||
@@ -18,8 +19,9 @@ import (
|
||||
// Reference: Chatwoot app/controllers/api/v1/accounts/agents_controller.rb
|
||||
// An "agent" in Chatwoot is a User with an AccountUser membership in a specific account.
|
||||
type AgentHandler struct {
|
||||
svc *service.AgentService
|
||||
audit *service.AuditService
|
||||
svc *service.AgentService
|
||||
audit *service.AuditService
|
||||
events *ws.EventPublisher
|
||||
}
|
||||
|
||||
func (h *AgentHandler) WithAuditService(audit *service.AuditService) *AgentHandler {
|
||||
@@ -27,6 +29,11 @@ func (h *AgentHandler) WithAuditService(audit *service.AuditService) *AgentHandl
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *AgentHandler) WithEventPublisher(events *ws.EventPublisher) *AgentHandler {
|
||||
h.events = events
|
||||
return h
|
||||
}
|
||||
|
||||
// NewAgentHandler creates a new AgentHandler.
|
||||
func NewAgentHandler(svc *service.AgentService) *AgentHandler {
|
||||
return &AgentHandler{svc: svc}
|
||||
@@ -127,6 +134,7 @@ func (h *AgentHandler) Create(c *gin.Context) {
|
||||
|
||||
c.Header("Cache-Control", "no-store")
|
||||
recordAuditMutation(c, h.audit, auditMutation{AccountID: accountID, AuditableType: "User", AuditableID: agent.ID, Action: "create", AuditedChanges: gin.H{"role": agent.Role, "active": agent.Active}})
|
||||
publishRealtimeEvent(h.events, accountID, "page:reload", nil)
|
||||
c.JSON(http.StatusOK, serializeAgentDetail(agent, accountID))
|
||||
}
|
||||
|
||||
@@ -171,6 +179,7 @@ func (h *AgentHandler) Update(c *gin.Context) {
|
||||
}
|
||||
|
||||
recordAuditMutation(c, h.audit, auditMutation{AccountID: accountID, AuditableType: "User", AuditableID: uint(id), Action: "update", AuditedChanges: agentUpdateAuditChanges(req)})
|
||||
publishRealtimeEvent(h.events, accountID, "page:reload", nil)
|
||||
c.JSON(http.StatusOK, serializeAgentDetail(agent, accountID))
|
||||
}
|
||||
|
||||
@@ -198,6 +207,7 @@ func (h *AgentHandler) Delete(c *gin.Context) {
|
||||
}
|
||||
|
||||
recordAuditMutation(c, h.audit, auditMutation{AccountID: accountID, AuditableType: "User", AuditableID: uint(id), Action: "destroy", AuditedChanges: gin.H{"account_id": accountID}})
|
||||
publishRealtimeEvent(h.events, accountID, "page:reload", nil)
|
||||
c.Status(http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -267,6 +277,7 @@ func (h *AgentHandler) BulkCreate(c *gin.Context) {
|
||||
}
|
||||
|
||||
c.Header("Cache-Control", "no-store")
|
||||
publishRealtimeEvent(h.events, accountID, "page:reload", nil)
|
||||
c.JSON(http.StatusOK, serializeAgentDetails(agents, accountID))
|
||||
}
|
||||
|
||||
|
||||
@@ -169,6 +169,7 @@ func (s *AgentHandlerTestSuite) TestListIgnoresPerPageLikeChatwoot() {
|
||||
|
||||
func (s *AgentHandlerTestSuite) TestCreateAgent() {
|
||||
customRoleID := uint(7)
|
||||
s.Require().NoError(s.db.Create(&model.CustomRole{ID: customRoleID, AccountID: s.account.ID, Name: "Custom", Permissions: "[]"}).Error)
|
||||
req := service.CreateAgentRequest{
|
||||
Email: "agent1@test.com",
|
||||
Name: "Agent One",
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/gochat/gochat/internal/model"
|
||||
"github.com/gochat/gochat/internal/service"
|
||||
"github.com/gochat/gochat/internal/ws"
|
||||
applogger "github.com/gochat/gochat/pkg/logger"
|
||||
"github.com/gochat/gochat/pkg/response"
|
||||
)
|
||||
@@ -16,6 +17,7 @@ import (
|
||||
type CustomRoleHandler struct {
|
||||
svc *service.CustomRoleService
|
||||
auditSvc *service.AuditService
|
||||
events *ws.EventPublisher
|
||||
}
|
||||
|
||||
// NewCustomRoleHandler creates a new CustomRole handler.
|
||||
@@ -28,6 +30,11 @@ func (h *CustomRoleHandler) WithAuditService(auditSvc *service.AuditService) *Cu
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *CustomRoleHandler) WithEventPublisher(events *ws.EventPublisher) *CustomRoleHandler {
|
||||
h.events = events
|
||||
return h
|
||||
}
|
||||
|
||||
// List returns all custom roles for an account.
|
||||
// GET /api/v1/accounts/:account_id/custom_roles
|
||||
func (h *CustomRoleHandler) List(c *gin.Context) {
|
||||
@@ -66,14 +73,18 @@ func (h *CustomRoleHandler) Create(c *gin.Context) {
|
||||
}
|
||||
|
||||
var wrapper struct {
|
||||
CustomRole service.CreateCustomRoleRequest `json:"custom_role"`
|
||||
CustomRole *service.CreateCustomRoleRequest `json:"custom_role"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&wrapper); err != nil {
|
||||
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, err.Error())
|
||||
return
|
||||
}
|
||||
if wrapper.CustomRole == nil {
|
||||
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, "custom_role envelope is required")
|
||||
return
|
||||
}
|
||||
|
||||
role, err := h.svc.Create(c.Request.Context(), accountID, wrapper.CustomRole)
|
||||
role, err := h.svc.Create(c.Request.Context(), accountID, *wrapper.CustomRole)
|
||||
if err != nil {
|
||||
applogger.L().Errorf("Create custom role for account %d: %v", accountID, err)
|
||||
handleServiceError(c, err)
|
||||
@@ -86,6 +97,7 @@ func (h *CustomRoleHandler) Create(c *gin.Context) {
|
||||
Action: "create",
|
||||
AuditedChanges: role,
|
||||
})
|
||||
publishRealtimeEvent(h.events, accountID, "page:reload", nil)
|
||||
|
||||
c.JSON(http.StatusOK, serializeCustomRole(role))
|
||||
}
|
||||
@@ -140,14 +152,18 @@ func (h *CustomRoleHandler) Update(c *gin.Context) {
|
||||
}
|
||||
|
||||
var wrapper struct {
|
||||
CustomRole service.UpdateCustomRoleRequest `json:"custom_role"`
|
||||
CustomRole *service.UpdateCustomRoleRequest `json:"custom_role"`
|
||||
}
|
||||
if err := c.ShouldBindJSON(&wrapper); err != nil {
|
||||
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, err.Error())
|
||||
return
|
||||
}
|
||||
if wrapper.CustomRole == nil {
|
||||
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrValidation, "custom_role envelope is required")
|
||||
return
|
||||
}
|
||||
|
||||
role, svcErr := h.svc.Update(c.Request.Context(), id, accountID, wrapper.CustomRole)
|
||||
role, svcErr := h.svc.Update(c.Request.Context(), id, accountID, *wrapper.CustomRole)
|
||||
if svcErr != nil {
|
||||
applogger.L().Errorf("Update custom role %d for account %d: %v", id, accountID, svcErr)
|
||||
handleServiceError(c, svcErr)
|
||||
@@ -160,6 +176,7 @@ func (h *CustomRoleHandler) Update(c *gin.Context) {
|
||||
Action: "update",
|
||||
AuditedChanges: role,
|
||||
})
|
||||
publishRealtimeEvent(h.events, accountID, "page:reload", nil)
|
||||
|
||||
c.JSON(http.StatusOK, serializeCustomRole(role))
|
||||
}
|
||||
@@ -195,6 +212,7 @@ func (h *CustomRoleHandler) Delete(c *gin.Context) {
|
||||
Action: "destroy",
|
||||
AuditedChanges: gin.H{"id": id},
|
||||
})
|
||||
publishRealtimeEvent(h.events, accountID, "page:reload", nil)
|
||||
|
||||
c.Status(http.StatusOK)
|
||||
}
|
||||
|
||||
@@ -92,6 +92,18 @@ func (s *CustomRoleHandlerTestSuite) TestCreate_BadRequest_EmptyBody() {
|
||||
assert.Equal(s.T(), http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func (s *CustomRoleHandlerTestSuite) TestCreate_BadRequest_RequiresEnvelope() {
|
||||
r := gin.New()
|
||||
r.POST("/api/v1/accounts/:account_id/custom_roles", withCustomRoleAdminContext(s.account.ID, s.handler.Create))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", fmt.Sprintf("/api/v1/accounts/%d/custom_roles", s.account.ID), bytes.NewBufferString(`{"name":"raw-role","permissions":[]}`))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(s.T(), http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func (s *CustomRoleHandlerTestSuite) TestGet_BadRequest_InvalidID() {
|
||||
r := gin.New()
|
||||
r.GET("/api/v1/accounts/:account_id/custom_roles/:id", withCustomRoleAdminContext(s.account.ID, s.handler.Get))
|
||||
@@ -115,6 +127,20 @@ func (s *CustomRoleHandlerTestSuite) TestUpdate_BadRequest_InvalidID() {
|
||||
assert.Equal(s.T(), http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func (s *CustomRoleHandlerTestSuite) TestUpdate_BadRequest_RequiresEnvelope() {
|
||||
role := &model.CustomRole{AccountID: s.account.ID, Name: "raw-update", Permissions: `[]`}
|
||||
s.Require().NoError(s.db.Create(role).Error)
|
||||
|
||||
r := gin.New()
|
||||
r.PATCH("/api/v1/accounts/:account_id/custom_roles/:id", withCustomRoleAdminContext(s.account.ID, s.handler.Update))
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("PATCH", fmt.Sprintf("/api/v1/accounts/%d/custom_roles/%d", s.account.ID, role.ID), bytes.NewBufferString(`{"name":"raw-update-edited","permissions":[]}`))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(s.T(), http.StatusBadRequest, w.Code)
|
||||
}
|
||||
|
||||
func (s *CustomRoleHandlerTestSuite) TestDelete_BadRequest_InvalidID() {
|
||||
r := gin.New()
|
||||
r.DELETE("/api/v1/accounts/:account_id/custom_roles/:id", withCustomRoleAdminContext(s.account.ID, s.handler.Delete))
|
||||
|
||||
Reference in New Issue
Block a user