feat(contacts): align filter payloads
This commit is contained in:
@@ -1080,6 +1080,10 @@ func (h *ContactHandler) Filter(c *gin.Context) {
|
||||
offset := (page - 1) * perPage
|
||||
contacts, total, svcErr := h.svc.Filter(c.Request.Context(), accountID, params, offset, perPage)
|
||||
if svcErr != nil {
|
||||
if len(params.Payload) > 0 {
|
||||
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": svcErr.Error()})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusUnprocessableEntity, gin.H{"error": "failed to filter contacts"})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/stretchr/testify/suite"
|
||||
"gorm.io/datatypes"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
"gorm.io/gorm/logger"
|
||||
@@ -52,6 +53,7 @@ func (s *ContactHandlerCRUDTestSuite) SetupSuite() {
|
||||
&model.Contact{},
|
||||
&model.Tag{},
|
||||
&model.ContactLabel{},
|
||||
&model.CustomAttributeDefinition{},
|
||||
&model.ContactExport{},
|
||||
&model.DataImport{},
|
||||
&model.Notification{},
|
||||
@@ -89,6 +91,7 @@ func (s *ContactHandlerCRUDTestSuite) SetupSuite() {
|
||||
s.router.GET("/api/v1/accounts/:id/contacts", s.handler.List)
|
||||
s.router.GET("/api/v1/accounts/:id/contacts/search", s.handler.Search)
|
||||
s.router.GET("/api/v1/accounts/:id/contacts/active", s.handler.Active)
|
||||
s.router.POST("/api/v1/accounts/:id/contacts/filter", s.handler.Filter)
|
||||
s.router.POST("/api/v1/accounts/:id/contacts/export", s.handler.ExportRequest)
|
||||
s.router.GET("/api/v1/accounts/:id/contacts/export/:export_id/download", s.handler.DownloadExport)
|
||||
s.router.POST("/api/v1/accounts/:id/contacts/import", s.handler.Import)
|
||||
@@ -137,6 +140,7 @@ func (s *ContactHandlerCRUDTestSuite) SetupSuite() {
|
||||
func (s *ContactHandlerCRUDTestSuite) SetupTest() {
|
||||
s.db.Exec("DELETE FROM contact_notes")
|
||||
s.db.Exec("DELETE FROM contact_labels")
|
||||
s.db.Exec("DELETE FROM custom_attribute_definitions")
|
||||
s.db.Exec("DELETE FROM tags")
|
||||
s.db.Exec("DELETE FROM contact_exports")
|
||||
s.db.Exec("DELETE FROM data_imports")
|
||||
@@ -854,6 +858,119 @@ func (s *ContactHandlerCRUDTestSuite) TestLabels_UpdateListAndFilter() {
|
||||
s.Equal(float64(s.contact.ID), payload[0].(map[string]interface{})["id"])
|
||||
}
|
||||
|
||||
func (s *ContactHandlerCRUDTestSuite) TestFilter_ChatwootPayloadStandardAndAdditionalAttributes() {
|
||||
s.contact.PhoneNumber = "+1234567890"
|
||||
s.contact.AdditionalAttributes = datatypes.JSON(`{"country_code":"uk","city":"London"}`)
|
||||
s.Require().NoError(s.db.Save(s.contact).Error)
|
||||
|
||||
other := &model.Contact{AccountID: s.account.ID, Name: "Other User", Email: "other@example.com", PhoneNumber: "+1987654321", AdditionalAttributes: datatypes.JSON(`{"country_code":"gr","city":"Athens"}`)}
|
||||
s.Require().NoError(s.db.Create(other).Error)
|
||||
|
||||
body, _ := json.Marshal(map[string]interface{}{
|
||||
"payload": []map[string]interface{}{
|
||||
{"attribute_key": "email", "filter_operator": "contains", "values": []string{"JANE"}, "query_operator": "AND"},
|
||||
{"attribute_key": "phone_number", "filter_operator": "equal_to", "values": []string{"1234567890"}, "query_operator": "AND"},
|
||||
{"attribute_key": "country_code", "filter_operator": "equal_to", "values": []string{"UK"}},
|
||||
},
|
||||
})
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", fmt.Sprintf("/api/v1/accounts/%d/contacts/filter", s.account.ID), bytes.NewReader(body))
|
||||
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))
|
||||
payload := resp["payload"].([]interface{})
|
||||
s.Len(payload, 1)
|
||||
s.Equal(float64(s.contact.ID), payload[0].(map[string]interface{})["id"])
|
||||
}
|
||||
|
||||
func (s *ContactHandlerCRUDTestSuite) TestFilter_ChatwootPayloadLabelsAndDate() {
|
||||
tag := &model.Tag{AccountID: s.account.ID, Name: "support", Color: "#1f93ff"}
|
||||
s.Require().NoError(s.db.Create(tag).Error)
|
||||
s.Require().NoError(s.db.Create(&model.ContactLabel{AccountID: s.account.ID, ContactID: s.contact.ID, TagID: tag.ID}).Error)
|
||||
recentActivity := time.Now().UTC().AddDate(0, 0, -1).Unix()
|
||||
oldActivity := time.Now().UTC().AddDate(0, 0, -8).Unix()
|
||||
s.contact.LastActivityAt = &oldActivity
|
||||
s.Require().NoError(s.db.Save(s.contact).Error)
|
||||
|
||||
other := &model.Contact{AccountID: s.account.ID, Name: "Other User", LastActivityAt: &recentActivity}
|
||||
s.Require().NoError(s.db.Create(other).Error)
|
||||
|
||||
body, _ := json.Marshal(map[string]interface{}{
|
||||
"payload": []map[string]interface{}{
|
||||
{"attribute_key": "labels", "filter_operator": "is_present", "values": []string{}, "query_operator": "AND"},
|
||||
{"attribute_key": "last_activity_at", "filter_operator": "days_before", "values": []string{"3"}},
|
||||
},
|
||||
})
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", fmt.Sprintf("/api/v1/accounts/%d/contacts/filter", s.account.ID), bytes.NewReader(body))
|
||||
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))
|
||||
payload := resp["payload"].([]interface{})
|
||||
s.Len(payload, 1)
|
||||
s.Equal(float64(s.contact.ID), payload[0].(map[string]interface{})["id"])
|
||||
}
|
||||
|
||||
func (s *ContactHandlerCRUDTestSuite) TestFilter_ChatwootPayloadCustomAttributeNotEqualIncludesNull() {
|
||||
def := &model.CustomAttributeDefinition{
|
||||
AccountID: s.account.ID,
|
||||
AttributeName: "customer_type",
|
||||
AttributeDisplayName: "Customer type",
|
||||
AttributeType: "list",
|
||||
AttributeModel: "contact_attribute",
|
||||
AttributeValues: datatypes.JSON(`["platinum","regular"]`),
|
||||
}
|
||||
s.Require().NoError(s.db.Create(def).Error)
|
||||
s.contact.CustomAttributes = datatypes.JSON(`{"customer_type":"platinum"}`)
|
||||
s.Require().NoError(s.db.Save(s.contact).Error)
|
||||
|
||||
regular := &model.Contact{AccountID: s.account.ID, Name: "Regular User", CustomAttributes: datatypes.JSON(`{"customer_type":"regular"}`)}
|
||||
s.Require().NoError(s.db.Create(regular).Error)
|
||||
missing := &model.Contact{AccountID: s.account.ID, Name: "Missing User", CustomAttributes: datatypes.JSON(`{}`)}
|
||||
s.Require().NoError(s.db.Create(missing).Error)
|
||||
|
||||
body, _ := json.Marshal(map[string]interface{}{
|
||||
"payload": []map[string]interface{}{
|
||||
{"attribute_key": "customer_type", "custom_attribute_type": "contact_attribute", "filter_operator": "not_equal_to", "values": []string{"platinum"}},
|
||||
},
|
||||
})
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", fmt.Sprintf("/api/v1/accounts/%d/contacts/filter", s.account.ID), bytes.NewReader(body))
|
||||
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))
|
||||
payload := resp["payload"].([]interface{})
|
||||
s.Len(payload, 2)
|
||||
ids := []float64{payload[0].(map[string]interface{})["id"].(float64), payload[1].(map[string]interface{})["id"].(float64)}
|
||||
s.ElementsMatch([]float64{float64(regular.ID), float64(missing.ID)}, ids)
|
||||
}
|
||||
|
||||
func (s *ContactHandlerCRUDTestSuite) TestFilter_ChatwootPayloadInvalidAttribute() {
|
||||
body, _ := json.Marshal(map[string]interface{}{
|
||||
"payload": []map[string]interface{}{
|
||||
{"attribute_key": "unknown", "filter_operator": "equal_to", "values": []string{"x"}},
|
||||
},
|
||||
})
|
||||
w := httptest.NewRecorder()
|
||||
req, _ := http.NewRequest("POST", fmt.Sprintf("/api/v1/accounts/%d/contacts/filter", s.account.ID), bytes.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
s.router.ServeHTTP(w, req)
|
||||
|
||||
s.Equal(http.StatusUnprocessableEntity, w.Code)
|
||||
var resp map[string]string
|
||||
s.NoError(json.Unmarshal(w.Body.Bytes(), &resp))
|
||||
s.Contains(resp["error"], "Invalid attribute key - [unknown]")
|
||||
}
|
||||
|
||||
func (s *ContactHandlerCRUDTestSuite) TestDestroyCustomAttributes_SelectedKeysPayload() {
|
||||
attrs := model.JSONMap{"tier": "gold", "plan": "pro", "vip": true}
|
||||
s.contact.CustomAttributes = model.ToDatatypesJSON(&attrs)
|
||||
|
||||
Reference in New Issue
Block a user