H-337: restore Web widget reply visibility (#64)
* H-337: restore widget reply delivery * H-337: harden widget conversation ownership --------- Co-authored-by: Rogee <rogee@ipao.vip>
This commit is contained in:
@@ -39,6 +39,16 @@ func (r *ContactInboxRepo) FindByContactAndInbox(ctx context.Context, contactID,
|
||||
return &ci, nil
|
||||
}
|
||||
|
||||
// CountByContactAndInbox returns the number of channel identities for a contact
|
||||
// in one inbox. Historical conversations without contact_inbox_id are safe to
|
||||
// resolve only when this count is exactly one.
|
||||
func (r *ContactInboxRepo) CountByContactAndInbox(ctx context.Context, contactID, inboxID uint) (int64, error) {
|
||||
var count int64
|
||||
err := r.db.WithContext(ctx).Model(&model.ContactInbox{}).
|
||||
Where("contact_id = ? AND inbox_id = ?", contactID, inboxID).Count(&count).Error
|
||||
return count, err
|
||||
}
|
||||
|
||||
// FindByContactInboxSource retrieves a contact_inbox by the Chatwoot builder identity.
|
||||
func (r *ContactInboxRepo) FindByContactInboxSource(ctx context.Context, contactID, inboxID uint, sourceID string) (*model.ContactInbox, error) {
|
||||
var ci model.ContactInbox
|
||||
|
||||
@@ -190,6 +190,22 @@ func (r *ConversationRepo) FindByContact(ctx context.Context, accountID, contact
|
||||
return conversations, total, err
|
||||
}
|
||||
|
||||
// FindByContactInbox retrieves conversations belonging to one channel identity.
|
||||
// A contact may have several inbox identities, but a widget token must only see
|
||||
// the conversations created through its own ContactInbox.
|
||||
func (r *ConversationRepo) FindByContactInbox(ctx context.Context, accountID, contactID, inboxID, contactInboxID uint, includeLegacy bool, offset, limit int) ([]model.Conversation, int64, error) {
|
||||
var conversations []model.Conversation
|
||||
var total int64
|
||||
query := r.db.WithContext(ctx).Model(&model.Conversation{}).
|
||||
Where("account_id = ? AND (contact_inbox_id = ? OR (contact_inbox_id IS NULL AND contact_id = ? AND inbox_id = ? AND ?))",
|
||||
accountID, contactInboxID, contactID, inboxID, includeLegacy)
|
||||
if err := query.Count(&total).Error; err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
err := query.Offset(offset).Limit(limit).Order("id DESC").Find(&conversations).Error
|
||||
return conversations, total, err
|
||||
}
|
||||
|
||||
// FindRecentByContact retrieves the latest conversations for a contact.
|
||||
// Reference: Chatwoot contacts/conversations#index limits to the latest 20 conversations ordered by last_activity_at.
|
||||
func (r *ConversationRepo) FindRecentByContact(ctx context.Context, accountID, contactID uint, inboxID *uint, limit int) ([]model.Conversation, error) {
|
||||
|
||||
@@ -58,6 +58,33 @@ func TestConversationRepo_FindByID_NotFound(t *testing.T) {
|
||||
assert.Nil(t, found)
|
||||
}
|
||||
|
||||
func TestConversationRepo_FindByContactInboxIncludesLegacyOnlyWhenAllowed(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
repo := NewConversationRepo(db)
|
||||
account := &model.Account{Name: "Widget identity account", Active: true}
|
||||
require.NoError(t, db.Create(account).Error)
|
||||
inbox := &model.Inbox{AccountID: account.ID, Name: "Widget", ChannelType: "web_widget"}
|
||||
require.NoError(t, db.Create(inbox).Error)
|
||||
contact := &model.Contact{AccountID: account.ID, Name: "Visitor"}
|
||||
require.NoError(t, db.Create(contact).Error)
|
||||
contactInbox := &model.ContactInbox{ContactID: contact.ID, InboxID: inbox.ID, PubsubToken: "token-a"}
|
||||
require.NoError(t, db.Create(contactInbox).Error)
|
||||
legacy := createTestConversation(t, db, account.ID, inbox.ID, contact.ID, "open")
|
||||
owned := createTestConversation(t, db, account.ID, inbox.ID, contact.ID, "open")
|
||||
require.NoError(t, db.Model(owned).Update("contact_inbox_id", contactInbox.ID).Error)
|
||||
|
||||
conversations, total, err := repo.FindByContactInbox(context.Background(), account.ID, contact.ID, inbox.ID, contactInbox.ID, true, 0, 10)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(2), total)
|
||||
assert.ElementsMatch(t, []uint{legacy.ID, owned.ID}, []uint{conversations[0].ID, conversations[1].ID})
|
||||
|
||||
conversations, total, err = repo.FindByContactInbox(context.Background(), account.ID, contact.ID, inbox.ID, contactInbox.ID, false, 0, 10)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, int64(1), total)
|
||||
require.Len(t, conversations, 1)
|
||||
assert.Equal(t, owned.ID, conversations[0].ID)
|
||||
}
|
||||
|
||||
func TestConversationRepo_FindByAccountAndID(t *testing.T) {
|
||||
db := setupTestDB(t)
|
||||
repo := NewConversationRepo(db)
|
||||
|
||||
Reference in New Issue
Block a user