feat(crm): align contact merge action
This commit is contained in:
@@ -818,7 +818,7 @@ func (h *ContactHandler) Merge(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
response.OK(c, result)
|
||||
c.JSON(http.StatusOK, serializeCRMContact(c.Request.Context(), h.svc.DB(), result, false))
|
||||
}
|
||||
|
||||
// Filter retrieves contacts matching advanced filter criteria.
|
||||
|
||||
@@ -52,6 +52,7 @@ func (s *ContactHandlerCRUDTestSuite) SetupSuite() {
|
||||
&model.Tag{},
|
||||
&model.ContactLabel{},
|
||||
&model.Conversation{},
|
||||
&model.Message{},
|
||||
&model.ContactInbox{},
|
||||
&model.InboxMember{},
|
||||
&model.ContactNote{},
|
||||
@@ -92,6 +93,7 @@ func (s *ContactHandlerCRUDTestSuite) SetupSuite() {
|
||||
s.router.GET("/api/v1/accounts/:id/contacts/:contact_id/contact_inboxes", s.handler.ListContactInboxes)
|
||||
s.router.GET("/api/v1/accounts/:id/contacts/:contact_id/notes", s.handler.ListNotes)
|
||||
s.router.POST("/api/v1/accounts/:id/contacts/:contact_id/notes", s.handler.CreateNote)
|
||||
s.router.POST("/api/v1/accounts/:id/actions/contact_merge", s.handler.Merge)
|
||||
|
||||
// Create test data
|
||||
s.account = &model.Account{Name: "Test Account", Locale: "en", Status: "active"}
|
||||
@@ -118,6 +120,7 @@ func (s *ContactHandlerCRUDTestSuite) SetupTest() {
|
||||
s.db.Exec("DELETE FROM contact_notes")
|
||||
s.db.Exec("DELETE FROM contact_labels")
|
||||
s.db.Exec("DELETE FROM tags")
|
||||
s.db.Exec("DELETE FROM messages")
|
||||
s.db.Exec("DELETE FROM notes")
|
||||
s.db.Exec("DELETE FROM contact_inboxes")
|
||||
s.db.Exec("DELETE FROM conversations")
|
||||
@@ -559,6 +562,41 @@ func (s *ContactHandlerCRUDTestSuite) TestDeleteAvatar_Success() {
|
||||
s.Equal("", found.AvatarURL)
|
||||
}
|
||||
|
||||
func (s *ContactHandlerCRUDTestSuite) TestMerge_ChatwootActionsPathReturnsRawContact() {
|
||||
base := &model.Contact{AccountID: s.account.ID, Name: "Base Contact", Email: "base@example.com"}
|
||||
mergee := &model.Contact{AccountID: s.account.ID, Name: "Mergee Contact", PhoneNumber: "+12212345"}
|
||||
s.Require().NoError(s.db.Create(base).Error)
|
||||
s.Require().NoError(s.db.Create(mergee).Error)
|
||||
s.Require().NoError(s.db.Create(&model.Conversation{AccountID: s.account.ID, InboxID: 1, ContactID: mergee.ID, Status: "open", ChannelType: "Channel::WebWidget", Channel: "web_widget"}).Error)
|
||||
s.Require().NoError(s.db.Create(&model.Note{AccountID: s.account.ID, ContactID: mergee.ID, Content: "mergee note", UserID: &s.user.ID}).Error)
|
||||
|
||||
bodyBytes, _ := json.Marshal(map[string]uint{"base_contact_id": base.ID, "mergee_contact_id": mergee.ID})
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST",
|
||||
fmt.Sprintf("/api/v1/accounts/%d/actions/contact_merge", s.account.ID),
|
||||
bytes.NewReader(bodyBytes))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
s.router.ServeHTTP(w, req)
|
||||
|
||||
s.Equal(http.StatusOK, w.Code)
|
||||
var resp map[string]interface{}
|
||||
s.NoError(json.Unmarshal(w.Body.Bytes(), &resp))
|
||||
s.Equal(float64(base.ID), resp["id"])
|
||||
s.Equal("Base Contact", resp["name"])
|
||||
s.Equal("base@example.com", resp["email"])
|
||||
s.Equal("+12212345", resp["phone_number"])
|
||||
s.NotContains(resp, "payload")
|
||||
s.NotContains(resp, "success")
|
||||
|
||||
var count int64
|
||||
s.db.Model(&model.Contact{}).Where("id = ?", mergee.ID).Count(&count)
|
||||
s.Equal(int64(0), count)
|
||||
s.db.Model(&model.Conversation{}).Where("contact_id = ?", base.ID).Count(&count)
|
||||
s.Equal(int64(1), count)
|
||||
s.db.Model(&model.Note{}).Where("contact_id = ?", base.ID).Count(&count)
|
||||
s.Equal(int64(1), count)
|
||||
}
|
||||
|
||||
func (s *ContactHandlerCRUDTestSuite) TestLabels_UpdateListAndFilter() {
|
||||
bodyBytes, _ := json.Marshal(map[string]interface{}{"labels": []string{"vip", "trial"}})
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ package v1
|
||||
// Chatwoot's response: returns the base contact after merge on success, 200 OK
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -32,12 +33,13 @@ func NewContactMergeHandler(mergeService *service.ContactMergeService) *ContactM
|
||||
|
||||
// Create merges the mergee contact into the base contact.
|
||||
// Reference: Chatwoot `def create`
|
||||
// contact_merge_action = ContactMergeAction.new(
|
||||
// account: Current.account,
|
||||
// base_contact: @base_contact,
|
||||
// mergee_contact: @mergee_contact
|
||||
// )
|
||||
// contact_merge_action.perform
|
||||
//
|
||||
// contact_merge_action = ContactMergeAction.new(
|
||||
// account: Current.account,
|
||||
// base_contact: @base_contact,
|
||||
// mergee_contact: @mergee_contact
|
||||
// )
|
||||
// contact_merge_action.perform
|
||||
//
|
||||
// Chatwoot returns the base contact on success with 200 OK.
|
||||
func (h *ContactMergeHandler) Create(c *gin.Context) {
|
||||
@@ -75,5 +77,5 @@ func (h *ContactMergeHandler) Create(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
response.OK(c, contact)
|
||||
}
|
||||
c.JSON(http.StatusOK, serializeCRMContact(c.Request.Context(), nil, contact, false))
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ type ContactMergeHandlerTestSuite struct {
|
||||
db *gorm.DB
|
||||
handler *ContactMergeHandler
|
||||
|
||||
account *model.Account
|
||||
account *model.Account
|
||||
contact1 *model.Contact
|
||||
contact2 *model.Contact
|
||||
}
|
||||
@@ -63,31 +63,42 @@ func TestContactMergeHandlerSuite(t *testing.T) {
|
||||
|
||||
func (s *ContactMergeHandlerTestSuite) TestCreate_Success() {
|
||||
r := gin.New()
|
||||
r.POST("/api/v1/accounts/:account_id/actions/contact_merges", func(c *gin.Context) {
|
||||
r.POST("/api/v1/accounts/:account_id/actions/contact_merge", func(c *gin.Context) {
|
||||
c.Set("account_id", float64(s.account.ID))
|
||||
c.Next()
|
||||
}, s.handler.Create)
|
||||
|
||||
body := map[string]uint{"base_contact_id": s.contact1.ID, "mergee_contact_id": s.contact2.ID}
|
||||
base := &model.Contact{Name: "merge-base", Email: "base@example.com", AccountID: s.account.ID}
|
||||
mergee := &model.Contact{Name: "mergee-contact", PhoneNumber: "+12212345", AccountID: s.account.ID}
|
||||
s.Require().NoError(s.db.Create(base).Error)
|
||||
s.Require().NoError(s.db.Create(mergee).Error)
|
||||
|
||||
body := map[string]uint{"base_contact_id": base.ID, "mergee_contact_id": mergee.ID}
|
||||
b, _ := json.Marshal(body)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", fmt.Sprintf("/api/v1/accounts/%d/actions/contact_merges", s.account.ID), bytes.NewBuffer(b))
|
||||
req, _ := http.NewRequest("POST", fmt.Sprintf("/api/v1/accounts/%d/actions/contact_merge", s.account.ID), bytes.NewBuffer(b))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(s.T(), http.StatusOK, w.Code)
|
||||
var payload map[string]any
|
||||
s.Require().NoError(json.Unmarshal(w.Body.Bytes(), &payload))
|
||||
assert.Equal(s.T(), float64(base.ID), payload["id"])
|
||||
assert.Equal(s.T(), "base@example.com", payload["email"])
|
||||
assert.NotContains(s.T(), payload, "success")
|
||||
assert.NotContains(s.T(), payload, "data")
|
||||
}
|
||||
|
||||
func (s *ContactMergeHandlerTestSuite) TestCreate_BadRequest_NoBody() {
|
||||
r := gin.New()
|
||||
r.POST("/api/v1/accounts/:account_id/actions/contact_merges", func(c *gin.Context) {
|
||||
r.POST("/api/v1/accounts/:account_id/actions/contact_merge", func(c *gin.Context) {
|
||||
c.Set("account_id", float64(s.account.ID))
|
||||
c.Next()
|
||||
}, s.handler.Create)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", fmt.Sprintf("/api/v1/accounts/%d/actions/contact_merges", s.account.ID), nil)
|
||||
req, _ := http.NewRequest("POST", fmt.Sprintf("/api/v1/accounts/%d/actions/contact_merge", s.account.ID), nil)
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
@@ -96,15 +107,15 @@ func (s *ContactMergeHandlerTestSuite) TestCreate_BadRequest_NoBody() {
|
||||
|
||||
func (s *ContactMergeHandlerTestSuite) TestCreate_BadRequest_InvalidAccountID() {
|
||||
r := gin.New()
|
||||
r.POST("/api/v1/accounts/:account_id/actions/contact_merges", s.handler.Create)
|
||||
r.POST("/api/v1/accounts/:account_id/actions/contact_merge", s.handler.Create)
|
||||
|
||||
body := map[string]uint{"base_contact_id": s.contact1.ID, "mergee_contact_id": s.contact2.ID}
|
||||
b, _ := json.Marshal(body)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", "/api/v1/accounts/abc/actions/contact_merges", bytes.NewBuffer(b))
|
||||
req, _ := http.NewRequest("POST", "/api/v1/accounts/abc/actions/contact_merge", bytes.NewBuffer(b))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(s.T(), http.StatusBadRequest, w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user