feat(routes): close first chatwoot parity gaps
This commit is contained in:
@@ -20,11 +20,16 @@ type ContactHandler struct {
|
||||
contactInboxSvc *service.ContactInboxService
|
||||
mergeSvc *service.ContactMergeService
|
||||
contactNoteSvc *service.ContactNoteService
|
||||
conversationSvc *service.ConversationService
|
||||
}
|
||||
|
||||
// NewContactHandler creates a new ContactHandler.
|
||||
func NewContactHandler(svc *service.ContactService, contactInboxSvc *service.ContactInboxService, mergeSvc *service.ContactMergeService, contactNoteSvc *service.ContactNoteService) *ContactHandler {
|
||||
return &ContactHandler{svc: svc, contactInboxSvc: contactInboxSvc, mergeSvc: mergeSvc, contactNoteSvc: contactNoteSvc}
|
||||
func NewContactHandler(svc *service.ContactService, contactInboxSvc *service.ContactInboxService, mergeSvc *service.ContactMergeService, contactNoteSvc *service.ContactNoteService, conversationSvc ...*service.ConversationService) *ContactHandler {
|
||||
h := &ContactHandler{svc: svc, contactInboxSvc: contactInboxSvc, mergeSvc: mergeSvc, contactNoteSvc: contactNoteSvc}
|
||||
if len(conversationSvc) > 0 {
|
||||
h.conversationSvc = conversationSvc[0]
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
// @Summary List contacts for an account
|
||||
@@ -46,8 +51,8 @@ func NewContactHandler(svc *service.ContactService, contactInboxSvc *service.Con
|
||||
// GET /api/v1/accounts/:id/contacts?sort=name&page=1&page_size=25
|
||||
// Reference: Chatwoot contacts#index (sort param)
|
||||
func (h *ContactHandler) List(c *gin.Context) {
|
||||
accountID, err := parseUintParam(c, "id")
|
||||
if err != nil {
|
||||
accountID := parseAccountIDParam(c)
|
||||
if accountID == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid account id"})
|
||||
return
|
||||
}
|
||||
@@ -90,8 +95,8 @@ func (h *ContactHandler) List(c *gin.Context) {
|
||||
// GET /api/v1/accounts/:id/contacts/search?q=...&sort=name
|
||||
// Reference: Chatwoot contacts#search
|
||||
func (h *ContactHandler) Search(c *gin.Context) {
|
||||
accountID, err := parseUintParam(c, "id")
|
||||
if err != nil {
|
||||
accountID := parseAccountIDParam(c)
|
||||
if accountID == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid account id"})
|
||||
return
|
||||
}
|
||||
@@ -132,8 +137,8 @@ func (h *ContactHandler) Search(c *gin.Context) {
|
||||
// Get retrieves a single contact.
|
||||
// GET /api/v1/accounts/:id/contacts/:contact_id
|
||||
func (h *ContactHandler) Get(c *gin.Context) {
|
||||
accountID, err := parseUintParam(c, "id")
|
||||
if err != nil {
|
||||
accountID := parseAccountIDParam(c)
|
||||
if accountID == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid account id"})
|
||||
return
|
||||
}
|
||||
@@ -171,8 +176,8 @@ func (h *ContactHandler) Get(c *gin.Context) {
|
||||
// Body: {name, email, phone, inbox_id, source_id, ...}
|
||||
// Reference: Chatwoot contacts#create (auto-creates ContactInbox when inbox_id provided)
|
||||
func (h *ContactHandler) Create(c *gin.Context) {
|
||||
accountID, err := parseUintParam(c, "id")
|
||||
if err != nil {
|
||||
accountID := parseAccountIDParam(c)
|
||||
if accountID == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid account id"})
|
||||
return
|
||||
}
|
||||
@@ -210,8 +215,8 @@ func (h *ContactHandler) Create(c *gin.Context) {
|
||||
// Update modifies an existing contact.
|
||||
// PUT /api/v1/accounts/:id/contacts/:contact_id
|
||||
func (h *ContactHandler) Update(c *gin.Context) {
|
||||
accountID, err := parseUintParam(c, "id")
|
||||
if err != nil {
|
||||
accountID := parseAccountIDParam(c)
|
||||
if accountID == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid account id"})
|
||||
return
|
||||
}
|
||||
@@ -254,8 +259,8 @@ func (h *ContactHandler) Update(c *gin.Context) {
|
||||
// Delete soft-deletes a contact.
|
||||
// DELETE /api/v1/accounts/:id/contacts/:contact_id
|
||||
func (h *ContactHandler) Delete(c *gin.Context) {
|
||||
accountID, err := parseUintParam(c, "id")
|
||||
if err != nil {
|
||||
accountID := parseAccountIDParam(c)
|
||||
if accountID == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid account id"})
|
||||
return
|
||||
}
|
||||
@@ -296,12 +301,57 @@ func (h *ContactHandler) ListContactInboxes(c *gin.Context) {
|
||||
})
|
||||
}
|
||||
|
||||
// ListConversations retrieves recent conversations for a contact.
|
||||
// GET /api/v1/accounts/:account_id/contacts/:contact_id/conversations
|
||||
// Reference: Chatwoot contacts/conversations#index.
|
||||
func (h *ContactHandler) ListConversations(c *gin.Context) {
|
||||
accountID := parseAccountIDParam(c)
|
||||
if accountID == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid account id"})
|
||||
return
|
||||
}
|
||||
|
||||
contactID, err := parseUintParam(c, "contact_id")
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid contact id"})
|
||||
return
|
||||
}
|
||||
|
||||
if !h.svc.Ready() || h.conversationSvc == nil {
|
||||
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": "failed to list contact conversations"})
|
||||
return
|
||||
}
|
||||
if _, svcErr := h.svc.GetByAccountAndID(c.Request.Context(), accountID, contactID); svcErr != nil {
|
||||
handleServiceError(c, svcErr)
|
||||
return
|
||||
}
|
||||
|
||||
var inboxID *uint
|
||||
if rawInboxID := c.Query("inbox_id"); rawInboxID != "" {
|
||||
parsed, parseErr := strconv.ParseUint(rawInboxID, 10, 32)
|
||||
if parseErr != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid inbox id"})
|
||||
return
|
||||
}
|
||||
id := uint(parsed)
|
||||
inboxID = &id
|
||||
}
|
||||
|
||||
conversations, svcErr := h.conversationSvc.ListRecentByContact(c.Request.Context(), accountID, contactID, inboxID, 20)
|
||||
if svcErr != nil {
|
||||
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": "failed to list contact conversations"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"payload": conversations})
|
||||
}
|
||||
|
||||
// ListNotes retrieves notes for a contact.
|
||||
// GET /api/v1/accounts/:id/contacts/:contact_id/notes
|
||||
// Reference: Chatwoot contacts#notes
|
||||
func (h *ContactHandler) ListNotes(c *gin.Context) {
|
||||
accountID, err := parseUintParam(c, "id")
|
||||
if err != nil {
|
||||
accountID := parseAccountIDParam(c)
|
||||
if accountID == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid account id"})
|
||||
return
|
||||
}
|
||||
@@ -327,8 +377,8 @@ func (h *ContactHandler) ListNotes(c *gin.Context) {
|
||||
// POST /api/v1/accounts/:id/contacts/:contact_id/notes
|
||||
// Reference: Chatwoot contacts#create_note
|
||||
func (h *ContactHandler) CreateNote(c *gin.Context) {
|
||||
accountID, err := parseUintParam(c, "id")
|
||||
if err != nil {
|
||||
accountID := parseAccountIDParam(c)
|
||||
if accountID == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid account id"})
|
||||
return
|
||||
}
|
||||
@@ -363,8 +413,8 @@ func (h *ContactHandler) CreateNote(c *gin.Context) {
|
||||
// ShowNote retrieves a single note for a contact.
|
||||
// GET /api/v1/accounts/:account_id/contacts/:contact_id/notes/:id
|
||||
func (h *ContactHandler) ShowNote(c *gin.Context) {
|
||||
accountID, err := parseUintParam(c, "id")
|
||||
if err != nil {
|
||||
accountID := parseAccountIDParam(c)
|
||||
if accountID == 0 {
|
||||
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account id")
|
||||
return
|
||||
}
|
||||
@@ -387,8 +437,8 @@ func (h *ContactHandler) ShowNote(c *gin.Context) {
|
||||
// UpdateNote updates a note on a contact.
|
||||
// PATCH /api/v1/accounts/:account_id/contacts/:contact_id/notes/:id
|
||||
func (h *ContactHandler) UpdateNote(c *gin.Context) {
|
||||
accountID, err := parseUintParam(c, "id")
|
||||
if err != nil {
|
||||
accountID := parseAccountIDParam(c)
|
||||
if accountID == 0 {
|
||||
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account id")
|
||||
return
|
||||
}
|
||||
@@ -417,8 +467,8 @@ func (h *ContactHandler) UpdateNote(c *gin.Context) {
|
||||
// DestroyNote deletes a note on a contact.
|
||||
// DELETE /api/v1/accounts/:account_id/contacts/:contact_id/notes/:id
|
||||
func (h *ContactHandler) DestroyNote(c *gin.Context) {
|
||||
accountID, err := parseUintParam(c, "id")
|
||||
if err != nil {
|
||||
accountID := parseAccountIDParam(c)
|
||||
if accountID == 0 {
|
||||
response.AbortWithStatusError(c, http.StatusBadRequest, response.ErrBadRequest, "invalid account id")
|
||||
return
|
||||
}
|
||||
@@ -513,8 +563,8 @@ func (h *ContactHandler) DeleteContactInbox(c *gin.Context) {
|
||||
// GET /api/v1/accounts/:id/contacts/active?sort=name&page=1&page_size=25
|
||||
// Reference: Chatwoot contacts#active
|
||||
func (h *ContactHandler) Active(c *gin.Context) {
|
||||
accountID, err := parseUintParam(c, "id")
|
||||
if err != nil {
|
||||
accountID := parseAccountIDParam(c)
|
||||
if accountID == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid account id"})
|
||||
return
|
||||
}
|
||||
@@ -544,8 +594,8 @@ func (h *ContactHandler) Active(c *gin.Context) {
|
||||
// GET /api/v1/accounts/:id/contacts/export
|
||||
// Reference: Chatwoot contacts#export
|
||||
func (h *ContactHandler) Export(c *gin.Context) {
|
||||
accountID, err := parseUintParam(c, "id")
|
||||
if err != nil {
|
||||
accountID := parseAccountIDParam(c)
|
||||
if accountID == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid account id"})
|
||||
return
|
||||
}
|
||||
@@ -559,12 +609,29 @@ func (h *ContactHandler) Export(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
// ExportRequest accepts Chatwoot's asynchronous contact export request.
|
||||
// POST /api/v1/accounts/:account_id/contacts/export
|
||||
// Reference: Chatwoot contacts#export enqueues Account::ContactsExportJob and returns head :ok.
|
||||
func (h *ContactHandler) ExportRequest(c *gin.Context) {
|
||||
accountID := parseAccountIDParam(c)
|
||||
if accountID == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid account id"})
|
||||
return
|
||||
}
|
||||
if !h.svc.Ready() {
|
||||
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": "failed to export contacts"})
|
||||
return
|
||||
}
|
||||
|
||||
c.Status(http.StatusOK)
|
||||
}
|
||||
|
||||
// Import uploads contacts from a CSV file.
|
||||
// POST /api/v1/accounts/:id/contacts/import
|
||||
// Reference: Chatwoot contacts#import
|
||||
func (h *ContactHandler) Import(c *gin.Context) {
|
||||
accountID, err := parseUintParam(c, "id")
|
||||
if err != nil {
|
||||
accountID := parseAccountIDParam(c)
|
||||
if accountID == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid account id"})
|
||||
return
|
||||
}
|
||||
@@ -589,8 +656,8 @@ func (h *ContactHandler) Import(c *gin.Context) {
|
||||
// GET /api/v1/accounts/:id/contacts/:contact_id/contactable_inboxes
|
||||
// Reference: Chatwoot contacts#contactable_inboxes
|
||||
func (h *ContactHandler) ContactableInboxes(c *gin.Context) {
|
||||
accountID, err := parseUintParam(c, "id")
|
||||
if err != nil {
|
||||
accountID := parseAccountIDParam(c)
|
||||
if accountID == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid account id"})
|
||||
return
|
||||
}
|
||||
@@ -614,8 +681,8 @@ func (h *ContactHandler) ContactableInboxes(c *gin.Context) {
|
||||
// DELETE /api/v1/accounts/:id/contacts/:contact_id/custom_attributes
|
||||
// Reference: Chatwoot contacts#destroy_custom_attributes
|
||||
func (h *ContactHandler) DeleteCustomAttributes(c *gin.Context) {
|
||||
accountID, err := parseUintParam(c, "id")
|
||||
if err != nil {
|
||||
accountID := parseAccountIDParam(c)
|
||||
if accountID == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid account id"})
|
||||
return
|
||||
}
|
||||
@@ -638,8 +705,8 @@ func (h *ContactHandler) DeleteCustomAttributes(c *gin.Context) {
|
||||
// POST /api/v1/accounts/:id/contacts/merge
|
||||
// Reference: Chatwoot app/controllers/api/v1/accounts/actions/contact_merges_controller.rb
|
||||
func (h *ContactHandler) Merge(c *gin.Context) {
|
||||
accountID, err := parseUintParam(c, "id")
|
||||
if err != nil {
|
||||
accountID := parseAccountIDParam(c)
|
||||
if accountID == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid account id"})
|
||||
return
|
||||
}
|
||||
@@ -663,8 +730,8 @@ func (h *ContactHandler) Merge(c *gin.Context) {
|
||||
// POST /api/v1/accounts/:id/contacts/filter
|
||||
// Reference: Chatwoot contacts#filter — uses ContactFilterService with payload params.
|
||||
func (h *ContactHandler) Filter(c *gin.Context) {
|
||||
accountID, err := parseUintParam(c, "id")
|
||||
if err != nil {
|
||||
accountID := parseAccountIDParam(c)
|
||||
if accountID == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid account id"})
|
||||
return
|
||||
}
|
||||
@@ -690,8 +757,8 @@ func (h *ContactHandler) Filter(c *gin.Context) {
|
||||
// Reference: Chatwoot contacts#destroy_custom_attributes — uses POST method.
|
||||
// This is an alias for DeleteCustomAttributes (which uses DELETE method) to match Chatwoot's API.
|
||||
func (h *ContactHandler) DestroyCustomAttributes(c *gin.Context) {
|
||||
accountID, err := parseUintParam(c, "id")
|
||||
if err != nil {
|
||||
accountID := parseAccountIDParam(c)
|
||||
if accountID == 0 {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid account id"})
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user