[MM-47751][MM-48102] MPA: Send Persistent Notifications (#21619)
* MM-46410: adds urgency on mention counts We have introduced priority for posts in https://github.com/mattermost/mattermost-webapp/pull/10951. We do need to color the mention badges in the webapp with a prominent color when a mention is posted in an urgent message. A thread has urgent mentions if the root post is marked as urgent, and the replies contain mentions to the user viewing the thread. This PR adds two columns, urgentmentioncount, and isurgent, in channelmembers, and threads tables respectively. Furthermore when asking for team/thread mention counts, we also return urgent mention counts for the user. * Adds PostAcknowledgements table and apis * job init and fetch mentions * add-migrations * delete-expired * send-notifications * Fetches post priority in batches * stop-notifications * stop-notification-on-reply * MM-47750: Adds PostAcknowledgements table and apis - Adds post acknowledgement api/app/store methods to be able to save and delete post acknowledgements by users. - Adds wesbsocket events for acknowledgement created/deleted - Returns post acknowledgements in the post's metadata * add-license-check * add-pagination * delete on channel and team * validate guests * add configs * move create priority post check from app to api * Add desktop notifications * check status * use config in job * add IsUrgent check * Add last-sent-at * validate max recipients * Update lastSentAt * Validate min. recipient * send email notification only once * remove email notifications * use latest time from config to run job * Add notifications counter * publish events to mentioned users only * pickup license updates in scheduler * don't allow post owner to stop notifications * follow normal notifications behaviour * Validates persistent notifications interval * move logic of handling valid and expired posts into sql * Adds persistent notifications in the webapp --------- Co-authored-by: koox00 <3829551+koox00@users.noreply.github.com> Co-authored-by: Mattermod <mattermod@users.noreply.github.com> Co-authored-by: Mattermost Build <build@mattermost.com>
Этот коммит содержится в:
@@ -0,0 +1,152 @@
|
||||
// Code generated by mockery v2.23.2. DO NOT EDIT.
|
||||
|
||||
// Regenerate this file using `make store-mocks`.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
model "github.com/mattermost/mattermost-server/server/public/model"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// PostPersistentNotificationStore is an autogenerated mock type for the PostPersistentNotificationStore type
|
||||
type PostPersistentNotificationStore struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Delete provides a mock function with given fields: postIds
|
||||
func (_m *PostPersistentNotificationStore) Delete(postIds []string) error {
|
||||
ret := _m.Called(postIds)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func([]string) error); ok {
|
||||
r0 = rf(postIds)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// DeleteByChannel provides a mock function with given fields: channelIds
|
||||
func (_m *PostPersistentNotificationStore) DeleteByChannel(channelIds []string) error {
|
||||
ret := _m.Called(channelIds)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func([]string) error); ok {
|
||||
r0 = rf(channelIds)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// DeleteByTeam provides a mock function with given fields: teamIds
|
||||
func (_m *PostPersistentNotificationStore) DeleteByTeam(teamIds []string) error {
|
||||
ret := _m.Called(teamIds)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func([]string) error); ok {
|
||||
r0 = rf(teamIds)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// DeleteExpired provides a mock function with given fields: maxSentCount
|
||||
func (_m *PostPersistentNotificationStore) DeleteExpired(maxSentCount int16) error {
|
||||
ret := _m.Called(maxSentCount)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(int16) error); ok {
|
||||
r0 = rf(maxSentCount)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Get provides a mock function with given fields: params
|
||||
func (_m *PostPersistentNotificationStore) Get(params model.GetPersistentNotificationsPostsParams) ([]*model.PostPersistentNotifications, error) {
|
||||
ret := _m.Called(params)
|
||||
|
||||
var r0 []*model.PostPersistentNotifications
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(model.GetPersistentNotificationsPostsParams) ([]*model.PostPersistentNotifications, error)); ok {
|
||||
return rf(params)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(model.GetPersistentNotificationsPostsParams) []*model.PostPersistentNotifications); ok {
|
||||
r0 = rf(params)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*model.PostPersistentNotifications)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(model.GetPersistentNotificationsPostsParams) error); ok {
|
||||
r1 = rf(params)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetSingle provides a mock function with given fields: postID
|
||||
func (_m *PostPersistentNotificationStore) GetSingle(postID string) (*model.PostPersistentNotifications, error) {
|
||||
ret := _m.Called(postID)
|
||||
|
||||
var r0 *model.PostPersistentNotifications
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(string) (*model.PostPersistentNotifications, error)); ok {
|
||||
return rf(postID)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(string) *model.PostPersistentNotifications); ok {
|
||||
r0 = rf(postID)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.PostPersistentNotifications)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||
r1 = rf(postID)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// UpdateLastActivity provides a mock function with given fields: postIds
|
||||
func (_m *PostPersistentNotificationStore) UpdateLastActivity(postIds []string) error {
|
||||
ret := _m.Called(postIds)
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func([]string) error); ok {
|
||||
r0 = rf(postIds)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
type mockConstructorTestingTNewPostPersistentNotificationStore interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}
|
||||
|
||||
// NewPostPersistentNotificationStore creates a new instance of PostPersistentNotificationStore. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
func NewPostPersistentNotificationStore(t mockConstructorTestingTNewPostPersistentNotificationStore) *PostPersistentNotificationStore {
|
||||
mock := &PostPersistentNotificationStore{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
@@ -500,6 +500,22 @@ func (_m *Store) PostAcknowledgement() store.PostAcknowledgementStore {
|
||||
return r0
|
||||
}
|
||||
|
||||
// PostPersistentNotification provides a mock function with given fields:
|
||||
func (_m *Store) PostPersistentNotification() store.PostPersistentNotificationStore {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 store.PostPersistentNotificationStore
|
||||
if rf, ok := ret.Get(0).(func() store.PostPersistentNotificationStore); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(store.PostPersistentNotificationStore)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// PostPriority provides a mock function with given fields:
|
||||
func (_m *Store) PostPriority() store.PostPriorityStore {
|
||||
ret := _m.Called()
|
||||
|
||||
@@ -0,0 +1,452 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package storetest
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/server/public/model"
|
||||
"github.com/mattermost/mattermost-server/server/v8/channels/store"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPostPersistentNotificationStore(t *testing.T, ss store.Store, s SqlStore) {
|
||||
t.Run("Get", func(t *testing.T) { testPostPersistentNotificationStoreGet(t, ss) })
|
||||
t.Run("Delete", func(t *testing.T) { testPostPersistentNotificationStoreDelete(t, ss) })
|
||||
t.Run("UpdateLastSentAt", func(t *testing.T) { testPostPersistentNotificationStoreUpdateLastSentAt(t, ss) })
|
||||
}
|
||||
|
||||
func testPostPersistentNotificationStoreGet(t *testing.T, ss store.Store) {
|
||||
p1 := model.Post{}
|
||||
p1.ChannelId = model.NewId()
|
||||
p1.UserId = model.NewId()
|
||||
p1.Message = NewTestId()
|
||||
p1.CreateAt = 10
|
||||
p1.Metadata = &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString("important"),
|
||||
RequestedAck: model.NewBool(false),
|
||||
PersistentNotifications: model.NewBool(true),
|
||||
},
|
||||
}
|
||||
|
||||
p2 := model.Post{}
|
||||
p2.ChannelId = p1.ChannelId
|
||||
p2.UserId = model.NewId()
|
||||
p2.Message = NewTestId()
|
||||
p2.CreateAt = 20
|
||||
p2.Metadata = &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString(model.PostPriorityUrgent),
|
||||
RequestedAck: model.NewBool(true),
|
||||
PersistentNotifications: model.NewBool(true),
|
||||
},
|
||||
}
|
||||
|
||||
// Invalid - Has no Priority
|
||||
p3 := model.Post{}
|
||||
p3.ChannelId = p1.ChannelId
|
||||
p3.UserId = model.NewId()
|
||||
p3.Message = NewTestId()
|
||||
p3.CreateAt = 30
|
||||
|
||||
// Invalid - Notification is false
|
||||
p4 := model.Post{}
|
||||
p4.ChannelId = p1.ChannelId
|
||||
p4.UserId = model.NewId()
|
||||
p4.Message = NewTestId()
|
||||
p4.CreateAt = 40
|
||||
p4.Metadata = &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString(model.PostPriorityUrgent),
|
||||
RequestedAck: model.NewBool(false),
|
||||
PersistentNotifications: model.NewBool(false),
|
||||
},
|
||||
}
|
||||
|
||||
p5 := model.Post{}
|
||||
p5.ChannelId = p1.ChannelId
|
||||
p5.UserId = model.NewId()
|
||||
p5.Message = NewTestId()
|
||||
p5.CreateAt = 50
|
||||
p5.Metadata = &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString(model.PostPriorityUrgent),
|
||||
RequestedAck: model.NewBool(false),
|
||||
PersistentNotifications: model.NewBool(true),
|
||||
},
|
||||
}
|
||||
|
||||
_, errIdx, err := ss.Post().SaveMultiple([]*model.Post{&p1, &p2, &p3, &p4, &p5})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, -1, errIdx)
|
||||
|
||||
defer ss.Post().PermanentDeleteByChannel(p1.ChannelId)
|
||||
defer ss.PostPersistentNotification().Delete([]string{p1.Id, p2.Id, p3.Id, p4.Id, p5.Id})
|
||||
|
||||
t.Run("Get Single", func(t *testing.T) {
|
||||
pn, err := ss.PostPersistentNotification().GetSingle(p1.Id)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, p1.Id, pn.PostId)
|
||||
|
||||
pn, err = ss.PostPersistentNotification().GetSingle(p2.Id)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, p2.Id, pn.PostId)
|
||||
|
||||
pn, err = ss.PostPersistentNotification().GetSingle(p5.Id)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, p5.Id, pn.PostId)
|
||||
|
||||
pn, err = ss.PostPersistentNotification().GetSingle(p3.Id)
|
||||
require.Error(t, err)
|
||||
require.Zero(t, pn)
|
||||
|
||||
pn, err = ss.PostPersistentNotification().GetSingle(p4.Id)
|
||||
require.Error(t, err)
|
||||
require.Zero(t, pn)
|
||||
})
|
||||
|
||||
t.Run("Get all before MaxTime", func(t *testing.T) {
|
||||
validIDs := []string{p1.Id, p2.Id, p5.Id}
|
||||
getIDs := func(posts []*model.PostPersistentNotifications) (ids []string) {
|
||||
for _, p := range posts {
|
||||
ids = append(ids, p.PostId)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// p5 is filtered by maxTime
|
||||
pn, err := ss.PostPersistentNotification().Get(model.GetPersistentNotificationsPostsParams{
|
||||
MaxTime: 45,
|
||||
MaxSentCount: 60,
|
||||
PerPage: 20,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, pn, 2)
|
||||
assert.Contains(t, getIDs(pn), p1.Id)
|
||||
assert.Contains(t, getIDs(pn), p2.Id)
|
||||
|
||||
// nothing is filtered out
|
||||
pn, err = ss.PostPersistentNotification().Get(model.GetPersistentNotificationsPostsParams{
|
||||
MaxTime: 100,
|
||||
MaxSentCount: 60,
|
||||
PerPage: 20,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, pn, 3)
|
||||
assert.ElementsMatch(t, validIDs, getIDs(pn))
|
||||
})
|
||||
}
|
||||
|
||||
func testPostPersistentNotificationStoreUpdateLastSentAt(t *testing.T, ss store.Store) {
|
||||
p1 := model.Post{}
|
||||
p1.ChannelId = model.NewId()
|
||||
p1.UserId = model.NewId()
|
||||
p1.Message = NewTestId()
|
||||
p1.CreateAt = 10
|
||||
p1.Metadata = &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString("important"),
|
||||
RequestedAck: model.NewBool(false),
|
||||
PersistentNotifications: model.NewBool(true),
|
||||
},
|
||||
}
|
||||
|
||||
_, errIdx, err := ss.Post().SaveMultiple([]*model.Post{&p1})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, -1, errIdx)
|
||||
|
||||
defer ss.Post().PermanentDeleteByChannel(p1.ChannelId)
|
||||
defer ss.PostPersistentNotification().Delete([]string{p1.Id})
|
||||
|
||||
// Update from 0 value
|
||||
now := model.GetTimeForMillis(model.GetMillis())
|
||||
delta := 2 * time.Second
|
||||
err = ss.PostPersistentNotification().UpdateLastActivity([]string{p1.Id})
|
||||
require.NoError(t, err)
|
||||
|
||||
pn, err := ss.PostPersistentNotification().Get(model.GetPersistentNotificationsPostsParams{
|
||||
MaxTime: model.GetMillisForTime(now.Add(delta)),
|
||||
MaxSentCount: 60,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, pn, 1)
|
||||
assert.WithinDuration(t, now, model.GetTimeForMillis(pn[0].LastSentAt), delta)
|
||||
|
||||
time.Sleep(time.Second)
|
||||
|
||||
// Update from non-zero value
|
||||
now = model.GetTimeForMillis(model.GetMillis())
|
||||
delta = 2 * time.Second
|
||||
err = ss.PostPersistentNotification().UpdateLastActivity([]string{p1.Id})
|
||||
require.NoError(t, err)
|
||||
|
||||
pn, err = ss.PostPersistentNotification().Get(model.GetPersistentNotificationsPostsParams{
|
||||
MaxTime: model.GetMillisForTime(now.Add(delta)),
|
||||
MaxSentCount: 60,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, pn, 1)
|
||||
assert.WithinDuration(t, now, model.GetTimeForMillis(pn[0].LastSentAt), delta)
|
||||
}
|
||||
|
||||
func testPostPersistentNotificationStoreDelete(t *testing.T, ss store.Store) {
|
||||
t.Run("Delete", func(t *testing.T) {
|
||||
p1 := model.Post{}
|
||||
p1.ChannelId = model.NewId()
|
||||
p1.UserId = model.NewId()
|
||||
p1.Message = NewTestId()
|
||||
p1.CreateAt = 10
|
||||
p1.Metadata = &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString("important"),
|
||||
RequestedAck: model.NewBool(false),
|
||||
PersistentNotifications: model.NewBool(true),
|
||||
},
|
||||
}
|
||||
|
||||
p2 := model.Post{}
|
||||
p2.ChannelId = p1.ChannelId
|
||||
p2.UserId = model.NewId()
|
||||
p2.Message = NewTestId()
|
||||
p2.CreateAt = 20
|
||||
p2.Metadata = &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString(model.PostPriorityUrgent),
|
||||
RequestedAck: model.NewBool(true),
|
||||
PersistentNotifications: model.NewBool(true),
|
||||
},
|
||||
}
|
||||
|
||||
p3 := model.Post{}
|
||||
p3.ChannelId = p1.ChannelId
|
||||
p3.UserId = model.NewId()
|
||||
p3.Message = NewTestId()
|
||||
p3.CreateAt = 30
|
||||
p3.Metadata = &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString(model.PostPriorityUrgent),
|
||||
RequestedAck: model.NewBool(false),
|
||||
PersistentNotifications: model.NewBool(true),
|
||||
},
|
||||
}
|
||||
|
||||
_, errIdx, err := ss.Post().SaveMultiple([]*model.Post{&p1, &p2, &p3})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, -1, errIdx)
|
||||
|
||||
defer ss.Post().PermanentDeleteByChannel(p1.ChannelId)
|
||||
defer ss.PostPersistentNotification().Delete([]string{p1.Id, p2.Id, p3.Id})
|
||||
|
||||
err = ss.PostPersistentNotification().Delete([]string{p1.Id, p3.Id})
|
||||
require.NoError(t, err)
|
||||
|
||||
pn, err := ss.PostPersistentNotification().Get(model.GetPersistentNotificationsPostsParams{
|
||||
MaxTime: 100,
|
||||
MaxSentCount: 6,
|
||||
PerPage: 20,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, pn, 1)
|
||||
assert.Equal(t, p2.Id, pn[0].PostId)
|
||||
})
|
||||
|
||||
t.Run("Delete By Channel", func(t *testing.T) {
|
||||
p1 := model.Post{}
|
||||
p1.ChannelId = model.NewId()
|
||||
p1.UserId = model.NewId()
|
||||
p1.Message = NewTestId()
|
||||
p1.CreateAt = 10
|
||||
p1.Metadata = &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString("important"),
|
||||
RequestedAck: model.NewBool(false),
|
||||
PersistentNotifications: model.NewBool(true),
|
||||
},
|
||||
}
|
||||
|
||||
p2 := model.Post{}
|
||||
p2.ChannelId = p1.ChannelId
|
||||
p2.UserId = model.NewId()
|
||||
p2.Message = NewTestId()
|
||||
p2.CreateAt = 20
|
||||
p2.Metadata = &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString(model.PostPriorityUrgent),
|
||||
RequestedAck: model.NewBool(true),
|
||||
PersistentNotifications: model.NewBool(true),
|
||||
},
|
||||
}
|
||||
|
||||
p3 := model.Post{}
|
||||
p3.ChannelId = p1.ChannelId
|
||||
p3.UserId = model.NewId()
|
||||
p3.Message = NewTestId()
|
||||
p3.CreateAt = 30
|
||||
p3.Metadata = &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString(model.PostPriorityUrgent),
|
||||
RequestedAck: model.NewBool(false),
|
||||
PersistentNotifications: model.NewBool(true),
|
||||
},
|
||||
}
|
||||
|
||||
p4 := model.Post{}
|
||||
p4.ChannelId = model.NewId()
|
||||
p4.UserId = model.NewId()
|
||||
p4.Message = NewTestId()
|
||||
p4.CreateAt = 40
|
||||
p4.Metadata = &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString("important"),
|
||||
RequestedAck: model.NewBool(false),
|
||||
PersistentNotifications: model.NewBool(true),
|
||||
},
|
||||
}
|
||||
|
||||
p5 := model.Post{}
|
||||
p5.ChannelId = p4.ChannelId
|
||||
p5.UserId = model.NewId()
|
||||
p5.Message = NewTestId()
|
||||
p5.CreateAt = 50
|
||||
p5.Metadata = &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString("important"),
|
||||
RequestedAck: model.NewBool(false),
|
||||
PersistentNotifications: model.NewBool(true),
|
||||
},
|
||||
}
|
||||
|
||||
_, errIdx, err := ss.Post().SaveMultiple([]*model.Post{&p1, &p2, &p3, &p4, &p5})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, -1, errIdx)
|
||||
|
||||
defer ss.Post().PermanentDeleteByChannel(p1.ChannelId)
|
||||
defer ss.Post().PermanentDeleteByChannel(p4.ChannelId)
|
||||
defer ss.PostPersistentNotification().Delete([]string{p1.Id, p2.Id, p3.Id, p4.Id, p5.Id})
|
||||
|
||||
err = ss.PostPersistentNotification().DeleteByChannel([]string{p1.ChannelId})
|
||||
require.NoError(t, err)
|
||||
|
||||
pn, err := ss.PostPersistentNotification().Get(model.GetPersistentNotificationsPostsParams{
|
||||
MaxTime: 100,
|
||||
MaxSentCount: 6,
|
||||
PerPage: 20,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, pn, 2)
|
||||
assert.ElementsMatch(t, []string{p4.Id, p5.Id}, []string{pn[0].PostId, pn[1].PostId})
|
||||
})
|
||||
|
||||
t.Run("Delete By Team", func(t *testing.T) {
|
||||
t1 := &model.Team{DisplayName: "t1", Name: NewTestId(), Email: MakeEmail(), Type: model.TeamOpen}
|
||||
_, err := ss.Team().Save(t1)
|
||||
require.NoError(t, err)
|
||||
t2 := &model.Team{DisplayName: "t2", Name: NewTestId(), Email: MakeEmail(), Type: model.TeamOpen}
|
||||
_, err = ss.Team().Save(t2)
|
||||
require.NoError(t, err)
|
||||
|
||||
c1 := &model.Channel{TeamId: t1.Id, Name: model.NewId(), DisplayName: "c1", Type: model.ChannelTypeOpen}
|
||||
_, err = ss.Channel().Save(c1, -1)
|
||||
require.NoError(t, err)
|
||||
c2 := &model.Channel{TeamId: t1.Id, Name: model.NewId(), DisplayName: "c2", Type: model.ChannelTypeOpen}
|
||||
_, err = ss.Channel().Save(c2, -1)
|
||||
require.NoError(t, err)
|
||||
c3 := &model.Channel{TeamId: t2.Id, Name: model.NewId(), DisplayName: "c1", Type: model.ChannelTypeOpen}
|
||||
_, err = ss.Channel().Save(c3, -1)
|
||||
require.NoError(t, err)
|
||||
|
||||
p1 := model.Post{}
|
||||
p1.ChannelId = c1.Id
|
||||
p1.UserId = model.NewId()
|
||||
p1.Message = NewTestId()
|
||||
p1.CreateAt = 10
|
||||
p1.Metadata = &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString("important"),
|
||||
RequestedAck: model.NewBool(false),
|
||||
PersistentNotifications: model.NewBool(true),
|
||||
},
|
||||
}
|
||||
|
||||
p2 := model.Post{}
|
||||
p2.ChannelId = p1.ChannelId
|
||||
p2.UserId = model.NewId()
|
||||
p2.Message = NewTestId()
|
||||
p2.CreateAt = 20
|
||||
p2.Metadata = &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString(model.PostPriorityUrgent),
|
||||
RequestedAck: model.NewBool(true),
|
||||
PersistentNotifications: model.NewBool(true),
|
||||
},
|
||||
}
|
||||
|
||||
p3 := model.Post{}
|
||||
p3.ChannelId = c2.Id
|
||||
p3.UserId = model.NewId()
|
||||
p3.Message = NewTestId()
|
||||
p3.CreateAt = 30
|
||||
p3.Metadata = &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString(model.PostPriorityUrgent),
|
||||
RequestedAck: model.NewBool(false),
|
||||
PersistentNotifications: model.NewBool(true),
|
||||
},
|
||||
}
|
||||
|
||||
p4 := model.Post{}
|
||||
p4.ChannelId = c3.Id
|
||||
p4.UserId = model.NewId()
|
||||
p4.Message = NewTestId()
|
||||
p4.CreateAt = 40
|
||||
p4.Metadata = &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString("important"),
|
||||
RequestedAck: model.NewBool(false),
|
||||
PersistentNotifications: model.NewBool(true),
|
||||
},
|
||||
}
|
||||
|
||||
p5 := model.Post{}
|
||||
p5.ChannelId = p4.ChannelId
|
||||
p5.UserId = model.NewId()
|
||||
p5.Message = NewTestId()
|
||||
p5.CreateAt = 50
|
||||
p5.Metadata = &model.PostMetadata{
|
||||
Priority: &model.PostPriority{
|
||||
Priority: model.NewString("important"),
|
||||
RequestedAck: model.NewBool(false),
|
||||
PersistentNotifications: model.NewBool(true),
|
||||
},
|
||||
}
|
||||
|
||||
_, errIdx, err := ss.Post().SaveMultiple([]*model.Post{&p1, &p2, &p3, &p4, &p5})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, -1, errIdx)
|
||||
|
||||
defer ss.Post().PermanentDeleteByChannel(c1.Id)
|
||||
defer ss.Post().PermanentDeleteByChannel(c2.Id)
|
||||
defer ss.Post().PermanentDeleteByChannel(c3.Id)
|
||||
defer ss.Channel().PermanentDeleteByTeam(t1.Id)
|
||||
defer ss.Channel().PermanentDeleteByTeam(t2.Id)
|
||||
defer ss.Team().PermanentDelete(t1.Id)
|
||||
defer ss.Team().PermanentDelete(t2.Id)
|
||||
defer ss.PostPersistentNotification().Delete([]string{p1.Id, p2.Id, p3.Id, p4.Id, p5.Id})
|
||||
|
||||
err = ss.PostPersistentNotification().DeleteByTeam([]string{t1.Id})
|
||||
require.NoError(t, err)
|
||||
|
||||
pn, err := ss.PostPersistentNotification().Get(model.GetPersistentNotificationsPostsParams{
|
||||
MaxTime: 100,
|
||||
MaxSentCount: 6,
|
||||
PerPage: 20,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Len(t, pn, 2)
|
||||
assert.ElementsMatch(t, []string{p4.Id, p5.Id}, []string{pn[0].PostId, pn[1].PostId})
|
||||
})
|
||||
}
|
||||
@@ -17,49 +17,50 @@ import (
|
||||
|
||||
// Store can be used to provide mock stores for testing.
|
||||
type Store struct {
|
||||
TeamStore mocks.TeamStore
|
||||
ChannelStore mocks.ChannelStore
|
||||
PostStore mocks.PostStore
|
||||
UserStore mocks.UserStore
|
||||
RetentionPolicyStore mocks.RetentionPolicyStore
|
||||
BotStore mocks.BotStore
|
||||
AuditStore mocks.AuditStore
|
||||
ClusterDiscoveryStore mocks.ClusterDiscoveryStore
|
||||
RemoteClusterStore mocks.RemoteClusterStore
|
||||
ComplianceStore mocks.ComplianceStore
|
||||
SessionStore mocks.SessionStore
|
||||
OAuthStore mocks.OAuthStore
|
||||
SystemStore mocks.SystemStore
|
||||
WebhookStore mocks.WebhookStore
|
||||
CommandStore mocks.CommandStore
|
||||
CommandWebhookStore mocks.CommandWebhookStore
|
||||
PreferenceStore mocks.PreferenceStore
|
||||
LicenseStore mocks.LicenseStore
|
||||
TokenStore mocks.TokenStore
|
||||
EmojiStore mocks.EmojiStore
|
||||
ThreadStore mocks.ThreadStore
|
||||
StatusStore mocks.StatusStore
|
||||
FileInfoStore mocks.FileInfoStore
|
||||
UploadSessionStore mocks.UploadSessionStore
|
||||
ReactionStore mocks.ReactionStore
|
||||
JobStore mocks.JobStore
|
||||
UserAccessTokenStore mocks.UserAccessTokenStore
|
||||
PluginStore mocks.PluginStore
|
||||
ChannelMemberHistoryStore mocks.ChannelMemberHistoryStore
|
||||
RoleStore mocks.RoleStore
|
||||
SchemeStore mocks.SchemeStore
|
||||
TermsOfServiceStore mocks.TermsOfServiceStore
|
||||
GroupStore mocks.GroupStore
|
||||
UserTermsOfServiceStore mocks.UserTermsOfServiceStore
|
||||
LinkMetadataStore mocks.LinkMetadataStore
|
||||
SharedChannelStore mocks.SharedChannelStore
|
||||
ProductNoticesStore mocks.ProductNoticesStore
|
||||
DraftStore mocks.DraftStore
|
||||
context context.Context
|
||||
NotifyAdminStore mocks.NotifyAdminStore
|
||||
PostPriorityStore mocks.PostPriorityStore
|
||||
PostAcknowledgementStore mocks.PostAcknowledgementStore
|
||||
TrueUpReviewStore mocks.TrueUpReviewStore
|
||||
TeamStore mocks.TeamStore
|
||||
ChannelStore mocks.ChannelStore
|
||||
PostStore mocks.PostStore
|
||||
UserStore mocks.UserStore
|
||||
RetentionPolicyStore mocks.RetentionPolicyStore
|
||||
BotStore mocks.BotStore
|
||||
AuditStore mocks.AuditStore
|
||||
ClusterDiscoveryStore mocks.ClusterDiscoveryStore
|
||||
RemoteClusterStore mocks.RemoteClusterStore
|
||||
ComplianceStore mocks.ComplianceStore
|
||||
SessionStore mocks.SessionStore
|
||||
OAuthStore mocks.OAuthStore
|
||||
SystemStore mocks.SystemStore
|
||||
WebhookStore mocks.WebhookStore
|
||||
CommandStore mocks.CommandStore
|
||||
CommandWebhookStore mocks.CommandWebhookStore
|
||||
PreferenceStore mocks.PreferenceStore
|
||||
LicenseStore mocks.LicenseStore
|
||||
TokenStore mocks.TokenStore
|
||||
EmojiStore mocks.EmojiStore
|
||||
ThreadStore mocks.ThreadStore
|
||||
StatusStore mocks.StatusStore
|
||||
FileInfoStore mocks.FileInfoStore
|
||||
UploadSessionStore mocks.UploadSessionStore
|
||||
ReactionStore mocks.ReactionStore
|
||||
JobStore mocks.JobStore
|
||||
UserAccessTokenStore mocks.UserAccessTokenStore
|
||||
PluginStore mocks.PluginStore
|
||||
ChannelMemberHistoryStore mocks.ChannelMemberHistoryStore
|
||||
RoleStore mocks.RoleStore
|
||||
SchemeStore mocks.SchemeStore
|
||||
TermsOfServiceStore mocks.TermsOfServiceStore
|
||||
GroupStore mocks.GroupStore
|
||||
UserTermsOfServiceStore mocks.UserTermsOfServiceStore
|
||||
LinkMetadataStore mocks.LinkMetadataStore
|
||||
SharedChannelStore mocks.SharedChannelStore
|
||||
ProductNoticesStore mocks.ProductNoticesStore
|
||||
DraftStore mocks.DraftStore
|
||||
context context.Context
|
||||
NotifyAdminStore mocks.NotifyAdminStore
|
||||
PostPriorityStore mocks.PostPriorityStore
|
||||
PostAcknowledgementStore mocks.PostAcknowledgementStore
|
||||
PostPersistentNotificationStore mocks.PostPersistentNotificationStore
|
||||
TrueUpReviewStore mocks.TrueUpReviewStore
|
||||
}
|
||||
|
||||
func (s *Store) SetContext(context context.Context) { s.context = context }
|
||||
@@ -110,6 +111,9 @@ func (s *Store) PostPriority() store.PostPriorityStore { return &s.PostPriorit
|
||||
func (s *Store) PostAcknowledgement() store.PostAcknowledgementStore {
|
||||
return &s.PostAcknowledgementStore
|
||||
}
|
||||
func (s *Store) PostPersistentNotification() store.PostPersistentNotificationStore {
|
||||
return &s.PostPersistentNotificationStore
|
||||
}
|
||||
func (s *Store) MarkSystemRanUnitTests() { /* do nothing */ }
|
||||
func (s *Store) Close() { /* do nothing */ }
|
||||
func (s *Store) LockToMaster() { /* do nothing */ }
|
||||
@@ -171,5 +175,6 @@ func (s *Store) AssertExpectations(t mock.TestingT) bool {
|
||||
&s.NotifyAdminStore,
|
||||
&s.PostPriorityStore,
|
||||
&s.PostAcknowledgementStore,
|
||||
&s.PostPersistentNotificationStore,
|
||||
)
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user