feat(conversations): align filter attributes

This commit is contained in:
2026-06-07 04:42:03 +08:00
parent f43ca571c2
commit f8d326ca2a
3 changed files with 310 additions and 35 deletions
@@ -13,6 +13,7 @@ import (
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"gorm.io/datatypes"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
@@ -88,6 +89,7 @@ func (s *ConversationCrudTestSuite) SetupSuite() {
&model.SlaEvent{},
&model.CaptainAssistant{},
&model.CaptainInbox{},
&model.CustomAttributeDefinition{},
)
s.Require().NoError(err)
@@ -189,6 +191,7 @@ func (s *ConversationCrudTestSuite) TearDownTest() {
s.db.Exec("DELETE FROM sla_policies")
s.db.Exec("DELETE FROM captain_inboxes")
s.db.Exec("DELETE FROM captain_assistants")
s.db.Exec("DELETE FROM custom_attribute_definitions")
}
func (s *ConversationCrudTestSuite) accountURL() string {
@@ -1093,6 +1096,127 @@ func (s *ConversationCrudTestSuite) TestFilter_ChatwootPayloadQueryOperatorChain
assert.ElementsMatch(s.T(), []float64{float64(primaryID), float64(secondaryID)}, ids)
}
func (s *ConversationCrudTestSuite) TestFilter_ChatwootPayloadAdditionalAttribute() {
s.testConv.AdditionalAttributes = datatypes.JSON(`{"browser_language":"en"}`)
s.Require().NoError(s.db.Save(s.testConv).Error)
secondary := &model.Conversation{AccountID: s.testAccount.ID, InboxID: s.testInbox.ID, ContactID: s.testContact.ID, Status: "open", AdditionalAttributes: datatypes.JSON(`{"browser_language":"fr"}`), ChannelType: "web_widget", Channel: "web_widget"}
s.Require().NoError(s.db.Create(secondary).Error)
body, _ := json.Marshal(map[string]interface{}{
"payload": []map[string]interface{}{
{
"attribute_key": "browser_language",
"filter_operator": "equal_to",
"values": []string{"en"},
},
},
})
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", s.accountURL()+"/conversations/filter", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
s.router.ServeHTTP(w, req)
assert.Equal(s.T(), http.StatusOK, w.Code)
var resp struct {
Meta struct {
AllCount int64 `json:"all_count"`
} `json:"meta"`
Payload []map[string]interface{} `json:"payload"`
}
s.Require().NoError(json.Unmarshal(w.Body.Bytes(), &resp))
assert.Equal(s.T(), int64(1), resp.Meta.AllCount)
s.Require().Len(resp.Payload, 1)
assert.Equal(s.T(), float64(s.testConv.ID), resp.Payload[0]["id"])
}
func (s *ConversationCrudTestSuite) TestFilter_ChatwootPayloadAdditionalAttributeContains() {
s.testConv.AdditionalAttributes = datatypes.JSON(`{"mail_subject":"Welcome to Billing"}`)
s.Require().NoError(s.db.Save(s.testConv).Error)
secondary := &model.Conversation{AccountID: s.testAccount.ID, InboxID: s.testInbox.ID, ContactID: s.testContact.ID, Status: "open", AdditionalAttributes: datatypes.JSON(`{"mail_subject":"Shipping update"}`), ChannelType: "web_widget", Channel: "web_widget"}
s.Require().NoError(s.db.Create(secondary).Error)
body, _ := json.Marshal(map[string]interface{}{
"payload": []map[string]interface{}{
{
"attribute_key": "mail_subject",
"filter_operator": "contains",
"values": []string{"billing"},
},
},
})
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", s.accountURL()+"/conversations/filter", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
s.router.ServeHTTP(w, req)
assert.Equal(s.T(), http.StatusOK, w.Code)
var resp struct {
Meta struct {
AllCount int64 `json:"all_count"`
} `json:"meta"`
Payload []map[string]interface{} `json:"payload"`
}
s.Require().NoError(json.Unmarshal(w.Body.Bytes(), &resp))
assert.Equal(s.T(), int64(1), resp.Meta.AllCount)
s.Require().Len(resp.Payload, 1)
assert.Equal(s.T(), float64(s.testConv.ID), resp.Payload[0]["id"])
}
func (s *ConversationCrudTestSuite) TestFilter_ChatwootPayloadCustomAttributeNotEqualIncludesNull() {
def := &model.CustomAttributeDefinition{
AccountID: s.testAccount.ID,
AttributeName: "conversation_type",
AttributeDisplayName: "Conversation type",
AttributeType: "list",
AttributeModel: "conversation_attribute",
AttributeValues: datatypes.JSON(`["platinum","silver"]`),
}
s.Require().NoError(s.db.Create(def).Error)
platinumID := uint(22201)
s.testConv.DisplayID = &platinumID
s.testConv.CustomAttributes = datatypes.JSON(`{"conversation_type":"platinum"}`)
s.Require().NoError(s.db.Save(s.testConv).Error)
silverID := uint(22202)
silver := &model.Conversation{AccountID: s.testAccount.ID, InboxID: s.testInbox.ID, ContactID: s.testContact.ID, DisplayID: &silverID, Status: "open", CustomAttributes: datatypes.JSON(`{"conversation_type":"silver"}`), ChannelType: "web_widget", Channel: "web_widget"}
s.Require().NoError(s.db.Create(silver).Error)
nilID := uint(22203)
missing := &model.Conversation{AccountID: s.testAccount.ID, InboxID: s.testInbox.ID, ContactID: s.testContact.ID, DisplayID: &nilID, Status: "open", CustomAttributes: datatypes.JSON(`{}`), ChannelType: "web_widget", Channel: "web_widget"}
s.Require().NoError(s.db.Create(missing).Error)
body, _ := json.Marshal(map[string]interface{}{
"payload": []map[string]interface{}{
{
"attribute_key": "conversation_type",
"custom_attribute_type": "conversation_attribute",
"filter_operator": "not_equal_to",
"values": []string{"platinum"},
},
},
})
w := httptest.NewRecorder()
req, _ := http.NewRequest("POST", s.accountURL()+"/conversations/filter", bytes.NewReader(body))
req.Header.Set("Content-Type", "application/json")
s.router.ServeHTTP(w, req)
assert.Equal(s.T(), http.StatusOK, w.Code)
var resp struct {
Meta struct {
AllCount int64 `json:"all_count"`
} `json:"meta"`
Payload []map[string]interface{} `json:"payload"`
}
s.Require().NoError(json.Unmarshal(w.Body.Bytes(), &resp))
assert.Equal(s.T(), int64(2), resp.Meta.AllCount)
s.Require().Len(resp.Payload, 2)
ids := []float64{resp.Payload[0]["id"].(float64), resp.Payload[1]["id"].(float64)}
assert.ElementsMatch(s.T(), []float64{float64(silverID), float64(nilID)}, ids)
}
func (s *ConversationCrudTestSuite) TestFilter_ChatwootPayloadInvalidQueryOperator() {
body, _ := json.Marshal(map[string]interface{}{
"payload": []map[string]interface{}{