[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>
Этот коммит содержится в:
@@ -19,48 +19,49 @@ import (
|
||||
|
||||
type OpenTracingLayer struct {
|
||||
store.Store
|
||||
AuditStore store.AuditStore
|
||||
BotStore store.BotStore
|
||||
ChannelStore store.ChannelStore
|
||||
ChannelMemberHistoryStore store.ChannelMemberHistoryStore
|
||||
ClusterDiscoveryStore store.ClusterDiscoveryStore
|
||||
CommandStore store.CommandStore
|
||||
CommandWebhookStore store.CommandWebhookStore
|
||||
ComplianceStore store.ComplianceStore
|
||||
DraftStore store.DraftStore
|
||||
EmojiStore store.EmojiStore
|
||||
FileInfoStore store.FileInfoStore
|
||||
GroupStore store.GroupStore
|
||||
JobStore store.JobStore
|
||||
LicenseStore store.LicenseStore
|
||||
LinkMetadataStore store.LinkMetadataStore
|
||||
NotifyAdminStore store.NotifyAdminStore
|
||||
OAuthStore store.OAuthStore
|
||||
PluginStore store.PluginStore
|
||||
PostStore store.PostStore
|
||||
PostAcknowledgementStore store.PostAcknowledgementStore
|
||||
PostPriorityStore store.PostPriorityStore
|
||||
PreferenceStore store.PreferenceStore
|
||||
ProductNoticesStore store.ProductNoticesStore
|
||||
ReactionStore store.ReactionStore
|
||||
RemoteClusterStore store.RemoteClusterStore
|
||||
RetentionPolicyStore store.RetentionPolicyStore
|
||||
RoleStore store.RoleStore
|
||||
SchemeStore store.SchemeStore
|
||||
SessionStore store.SessionStore
|
||||
SharedChannelStore store.SharedChannelStore
|
||||
StatusStore store.StatusStore
|
||||
SystemStore store.SystemStore
|
||||
TeamStore store.TeamStore
|
||||
TermsOfServiceStore store.TermsOfServiceStore
|
||||
ThreadStore store.ThreadStore
|
||||
TokenStore store.TokenStore
|
||||
TrueUpReviewStore store.TrueUpReviewStore
|
||||
UploadSessionStore store.UploadSessionStore
|
||||
UserStore store.UserStore
|
||||
UserAccessTokenStore store.UserAccessTokenStore
|
||||
UserTermsOfServiceStore store.UserTermsOfServiceStore
|
||||
WebhookStore store.WebhookStore
|
||||
AuditStore store.AuditStore
|
||||
BotStore store.BotStore
|
||||
ChannelStore store.ChannelStore
|
||||
ChannelMemberHistoryStore store.ChannelMemberHistoryStore
|
||||
ClusterDiscoveryStore store.ClusterDiscoveryStore
|
||||
CommandStore store.CommandStore
|
||||
CommandWebhookStore store.CommandWebhookStore
|
||||
ComplianceStore store.ComplianceStore
|
||||
DraftStore store.DraftStore
|
||||
EmojiStore store.EmojiStore
|
||||
FileInfoStore store.FileInfoStore
|
||||
GroupStore store.GroupStore
|
||||
JobStore store.JobStore
|
||||
LicenseStore store.LicenseStore
|
||||
LinkMetadataStore store.LinkMetadataStore
|
||||
NotifyAdminStore store.NotifyAdminStore
|
||||
OAuthStore store.OAuthStore
|
||||
PluginStore store.PluginStore
|
||||
PostStore store.PostStore
|
||||
PostAcknowledgementStore store.PostAcknowledgementStore
|
||||
PostPersistentNotificationStore store.PostPersistentNotificationStore
|
||||
PostPriorityStore store.PostPriorityStore
|
||||
PreferenceStore store.PreferenceStore
|
||||
ProductNoticesStore store.ProductNoticesStore
|
||||
ReactionStore store.ReactionStore
|
||||
RemoteClusterStore store.RemoteClusterStore
|
||||
RetentionPolicyStore store.RetentionPolicyStore
|
||||
RoleStore store.RoleStore
|
||||
SchemeStore store.SchemeStore
|
||||
SessionStore store.SessionStore
|
||||
SharedChannelStore store.SharedChannelStore
|
||||
StatusStore store.StatusStore
|
||||
SystemStore store.SystemStore
|
||||
TeamStore store.TeamStore
|
||||
TermsOfServiceStore store.TermsOfServiceStore
|
||||
ThreadStore store.ThreadStore
|
||||
TokenStore store.TokenStore
|
||||
TrueUpReviewStore store.TrueUpReviewStore
|
||||
UploadSessionStore store.UploadSessionStore
|
||||
UserStore store.UserStore
|
||||
UserAccessTokenStore store.UserAccessTokenStore
|
||||
UserTermsOfServiceStore store.UserTermsOfServiceStore
|
||||
WebhookStore store.WebhookStore
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayer) Audit() store.AuditStore {
|
||||
@@ -143,6 +144,10 @@ func (s *OpenTracingLayer) PostAcknowledgement() store.PostAcknowledgementStore
|
||||
return s.PostAcknowledgementStore
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayer) PostPersistentNotification() store.PostPersistentNotificationStore {
|
||||
return s.PostPersistentNotificationStore
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayer) PostPriority() store.PostPriorityStore {
|
||||
return s.PostPriorityStore
|
||||
}
|
||||
@@ -331,6 +336,11 @@ type OpenTracingLayerPostAcknowledgementStore struct {
|
||||
Root *OpenTracingLayer
|
||||
}
|
||||
|
||||
type OpenTracingLayerPostPersistentNotificationStore struct {
|
||||
store.PostPersistentNotificationStore
|
||||
Root *OpenTracingLayer
|
||||
}
|
||||
|
||||
type OpenTracingLayerPostPriorityStore struct {
|
||||
store.PostPriorityStore
|
||||
Root *OpenTracingLayer
|
||||
@@ -6839,6 +6849,132 @@ func (s *OpenTracingLayerPostAcknowledgementStore) Save(postID string, userID st
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayerPostPersistentNotificationStore) Delete(postIds []string) error {
|
||||
origCtx := s.Root.Store.Context()
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PostPersistentNotificationStore.Delete")
|
||||
s.Root.Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
s.Root.Store.SetContext(origCtx)
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
err := s.PostPersistentNotificationStore.Delete(postIds)
|
||||
if err != nil {
|
||||
span.LogFields(spanlog.Error(err))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayerPostPersistentNotificationStore) DeleteByChannel(channelIds []string) error {
|
||||
origCtx := s.Root.Store.Context()
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PostPersistentNotificationStore.DeleteByChannel")
|
||||
s.Root.Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
s.Root.Store.SetContext(origCtx)
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
err := s.PostPersistentNotificationStore.DeleteByChannel(channelIds)
|
||||
if err != nil {
|
||||
span.LogFields(spanlog.Error(err))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayerPostPersistentNotificationStore) DeleteByTeam(teamIds []string) error {
|
||||
origCtx := s.Root.Store.Context()
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PostPersistentNotificationStore.DeleteByTeam")
|
||||
s.Root.Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
s.Root.Store.SetContext(origCtx)
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
err := s.PostPersistentNotificationStore.DeleteByTeam(teamIds)
|
||||
if err != nil {
|
||||
span.LogFields(spanlog.Error(err))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayerPostPersistentNotificationStore) DeleteExpired(maxSentCount int16) error {
|
||||
origCtx := s.Root.Store.Context()
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PostPersistentNotificationStore.DeleteExpired")
|
||||
s.Root.Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
s.Root.Store.SetContext(origCtx)
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
err := s.PostPersistentNotificationStore.DeleteExpired(maxSentCount)
|
||||
if err != nil {
|
||||
span.LogFields(spanlog.Error(err))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayerPostPersistentNotificationStore) Get(params model.GetPersistentNotificationsPostsParams) ([]*model.PostPersistentNotifications, error) {
|
||||
origCtx := s.Root.Store.Context()
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PostPersistentNotificationStore.Get")
|
||||
s.Root.Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
s.Root.Store.SetContext(origCtx)
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
result, err := s.PostPersistentNotificationStore.Get(params)
|
||||
if err != nil {
|
||||
span.LogFields(spanlog.Error(err))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayerPostPersistentNotificationStore) GetSingle(postID string) (*model.PostPersistentNotifications, error) {
|
||||
origCtx := s.Root.Store.Context()
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PostPersistentNotificationStore.GetSingle")
|
||||
s.Root.Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
s.Root.Store.SetContext(origCtx)
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
result, err := s.PostPersistentNotificationStore.GetSingle(postID)
|
||||
if err != nil {
|
||||
span.LogFields(spanlog.Error(err))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayerPostPersistentNotificationStore) UpdateLastActivity(postIds []string) error {
|
||||
origCtx := s.Root.Store.Context()
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PostPersistentNotificationStore.UpdateLastActivity")
|
||||
s.Root.Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
s.Root.Store.SetContext(origCtx)
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
err := s.PostPersistentNotificationStore.UpdateLastActivity(postIds)
|
||||
if err != nil {
|
||||
span.LogFields(spanlog.Error(err))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayerPostPriorityStore) GetForPost(postId string) (*model.PostPriority, error) {
|
||||
origCtx := s.Root.Store.Context()
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PostPriorityStore.GetForPost")
|
||||
@@ -12942,6 +13078,7 @@ func New(childStore store.Store, ctx context.Context) *OpenTracingLayer {
|
||||
newStore.PluginStore = &OpenTracingLayerPluginStore{PluginStore: childStore.Plugin(), Root: &newStore}
|
||||
newStore.PostStore = &OpenTracingLayerPostStore{PostStore: childStore.Post(), Root: &newStore}
|
||||
newStore.PostAcknowledgementStore = &OpenTracingLayerPostAcknowledgementStore{PostAcknowledgementStore: childStore.PostAcknowledgement(), Root: &newStore}
|
||||
newStore.PostPersistentNotificationStore = &OpenTracingLayerPostPersistentNotificationStore{PostPersistentNotificationStore: childStore.PostPersistentNotification(), Root: &newStore}
|
||||
newStore.PostPriorityStore = &OpenTracingLayerPostPriorityStore{PostPriorityStore: childStore.PostPriority(), Root: &newStore}
|
||||
newStore.PreferenceStore = &OpenTracingLayerPreferenceStore{PreferenceStore: childStore.Preference(), Root: &newStore}
|
||||
newStore.ProductNoticesStore = &OpenTracingLayerProductNoticesStore{ProductNoticesStore: childStore.ProductNotices(), Root: &newStore}
|
||||
|
||||
@@ -22,48 +22,49 @@ const mySQLDeadlockCode = uint16(1213)
|
||||
|
||||
type RetryLayer struct {
|
||||
store.Store
|
||||
AuditStore store.AuditStore
|
||||
BotStore store.BotStore
|
||||
ChannelStore store.ChannelStore
|
||||
ChannelMemberHistoryStore store.ChannelMemberHistoryStore
|
||||
ClusterDiscoveryStore store.ClusterDiscoveryStore
|
||||
CommandStore store.CommandStore
|
||||
CommandWebhookStore store.CommandWebhookStore
|
||||
ComplianceStore store.ComplianceStore
|
||||
DraftStore store.DraftStore
|
||||
EmojiStore store.EmojiStore
|
||||
FileInfoStore store.FileInfoStore
|
||||
GroupStore store.GroupStore
|
||||
JobStore store.JobStore
|
||||
LicenseStore store.LicenseStore
|
||||
LinkMetadataStore store.LinkMetadataStore
|
||||
NotifyAdminStore store.NotifyAdminStore
|
||||
OAuthStore store.OAuthStore
|
||||
PluginStore store.PluginStore
|
||||
PostStore store.PostStore
|
||||
PostAcknowledgementStore store.PostAcknowledgementStore
|
||||
PostPriorityStore store.PostPriorityStore
|
||||
PreferenceStore store.PreferenceStore
|
||||
ProductNoticesStore store.ProductNoticesStore
|
||||
ReactionStore store.ReactionStore
|
||||
RemoteClusterStore store.RemoteClusterStore
|
||||
RetentionPolicyStore store.RetentionPolicyStore
|
||||
RoleStore store.RoleStore
|
||||
SchemeStore store.SchemeStore
|
||||
SessionStore store.SessionStore
|
||||
SharedChannelStore store.SharedChannelStore
|
||||
StatusStore store.StatusStore
|
||||
SystemStore store.SystemStore
|
||||
TeamStore store.TeamStore
|
||||
TermsOfServiceStore store.TermsOfServiceStore
|
||||
ThreadStore store.ThreadStore
|
||||
TokenStore store.TokenStore
|
||||
TrueUpReviewStore store.TrueUpReviewStore
|
||||
UploadSessionStore store.UploadSessionStore
|
||||
UserStore store.UserStore
|
||||
UserAccessTokenStore store.UserAccessTokenStore
|
||||
UserTermsOfServiceStore store.UserTermsOfServiceStore
|
||||
WebhookStore store.WebhookStore
|
||||
AuditStore store.AuditStore
|
||||
BotStore store.BotStore
|
||||
ChannelStore store.ChannelStore
|
||||
ChannelMemberHistoryStore store.ChannelMemberHistoryStore
|
||||
ClusterDiscoveryStore store.ClusterDiscoveryStore
|
||||
CommandStore store.CommandStore
|
||||
CommandWebhookStore store.CommandWebhookStore
|
||||
ComplianceStore store.ComplianceStore
|
||||
DraftStore store.DraftStore
|
||||
EmojiStore store.EmojiStore
|
||||
FileInfoStore store.FileInfoStore
|
||||
GroupStore store.GroupStore
|
||||
JobStore store.JobStore
|
||||
LicenseStore store.LicenseStore
|
||||
LinkMetadataStore store.LinkMetadataStore
|
||||
NotifyAdminStore store.NotifyAdminStore
|
||||
OAuthStore store.OAuthStore
|
||||
PluginStore store.PluginStore
|
||||
PostStore store.PostStore
|
||||
PostAcknowledgementStore store.PostAcknowledgementStore
|
||||
PostPersistentNotificationStore store.PostPersistentNotificationStore
|
||||
PostPriorityStore store.PostPriorityStore
|
||||
PreferenceStore store.PreferenceStore
|
||||
ProductNoticesStore store.ProductNoticesStore
|
||||
ReactionStore store.ReactionStore
|
||||
RemoteClusterStore store.RemoteClusterStore
|
||||
RetentionPolicyStore store.RetentionPolicyStore
|
||||
RoleStore store.RoleStore
|
||||
SchemeStore store.SchemeStore
|
||||
SessionStore store.SessionStore
|
||||
SharedChannelStore store.SharedChannelStore
|
||||
StatusStore store.StatusStore
|
||||
SystemStore store.SystemStore
|
||||
TeamStore store.TeamStore
|
||||
TermsOfServiceStore store.TermsOfServiceStore
|
||||
ThreadStore store.ThreadStore
|
||||
TokenStore store.TokenStore
|
||||
TrueUpReviewStore store.TrueUpReviewStore
|
||||
UploadSessionStore store.UploadSessionStore
|
||||
UserStore store.UserStore
|
||||
UserAccessTokenStore store.UserAccessTokenStore
|
||||
UserTermsOfServiceStore store.UserTermsOfServiceStore
|
||||
WebhookStore store.WebhookStore
|
||||
}
|
||||
|
||||
func (s *RetryLayer) Audit() store.AuditStore {
|
||||
@@ -146,6 +147,10 @@ func (s *RetryLayer) PostAcknowledgement() store.PostAcknowledgementStore {
|
||||
return s.PostAcknowledgementStore
|
||||
}
|
||||
|
||||
func (s *RetryLayer) PostPersistentNotification() store.PostPersistentNotificationStore {
|
||||
return s.PostPersistentNotificationStore
|
||||
}
|
||||
|
||||
func (s *RetryLayer) PostPriority() store.PostPriorityStore {
|
||||
return s.PostPriorityStore
|
||||
}
|
||||
@@ -334,6 +339,11 @@ type RetryLayerPostAcknowledgementStore struct {
|
||||
Root *RetryLayer
|
||||
}
|
||||
|
||||
type RetryLayerPostPersistentNotificationStore struct {
|
||||
store.PostPersistentNotificationStore
|
||||
Root *RetryLayer
|
||||
}
|
||||
|
||||
type RetryLayerPostPriorityStore struct {
|
||||
store.PostPriorityStore
|
||||
Root *RetryLayer
|
||||
@@ -7750,6 +7760,153 @@ func (s *RetryLayerPostAcknowledgementStore) Save(postID string, userID string,
|
||||
|
||||
}
|
||||
|
||||
func (s *RetryLayerPostPersistentNotificationStore) Delete(postIds []string) error {
|
||||
|
||||
tries := 0
|
||||
for {
|
||||
err := s.PostPersistentNotificationStore.Delete(postIds)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if !isRepeatableError(err) {
|
||||
return err
|
||||
}
|
||||
tries++
|
||||
if tries >= 3 {
|
||||
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
|
||||
return err
|
||||
}
|
||||
timepkg.Sleep(100 * timepkg.Millisecond)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *RetryLayerPostPersistentNotificationStore) DeleteByChannel(channelIds []string) error {
|
||||
|
||||
tries := 0
|
||||
for {
|
||||
err := s.PostPersistentNotificationStore.DeleteByChannel(channelIds)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if !isRepeatableError(err) {
|
||||
return err
|
||||
}
|
||||
tries++
|
||||
if tries >= 3 {
|
||||
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
|
||||
return err
|
||||
}
|
||||
timepkg.Sleep(100 * timepkg.Millisecond)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *RetryLayerPostPersistentNotificationStore) DeleteByTeam(teamIds []string) error {
|
||||
|
||||
tries := 0
|
||||
for {
|
||||
err := s.PostPersistentNotificationStore.DeleteByTeam(teamIds)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if !isRepeatableError(err) {
|
||||
return err
|
||||
}
|
||||
tries++
|
||||
if tries >= 3 {
|
||||
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
|
||||
return err
|
||||
}
|
||||
timepkg.Sleep(100 * timepkg.Millisecond)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *RetryLayerPostPersistentNotificationStore) DeleteExpired(maxSentCount int16) error {
|
||||
|
||||
tries := 0
|
||||
for {
|
||||
err := s.PostPersistentNotificationStore.DeleteExpired(maxSentCount)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if !isRepeatableError(err) {
|
||||
return err
|
||||
}
|
||||
tries++
|
||||
if tries >= 3 {
|
||||
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
|
||||
return err
|
||||
}
|
||||
timepkg.Sleep(100 * timepkg.Millisecond)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *RetryLayerPostPersistentNotificationStore) Get(params model.GetPersistentNotificationsPostsParams) ([]*model.PostPersistentNotifications, error) {
|
||||
|
||||
tries := 0
|
||||
for {
|
||||
result, err := s.PostPersistentNotificationStore.Get(params)
|
||||
if err == nil {
|
||||
return result, nil
|
||||
}
|
||||
if !isRepeatableError(err) {
|
||||
return result, err
|
||||
}
|
||||
tries++
|
||||
if tries >= 3 {
|
||||
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
|
||||
return result, err
|
||||
}
|
||||
timepkg.Sleep(100 * timepkg.Millisecond)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *RetryLayerPostPersistentNotificationStore) GetSingle(postID string) (*model.PostPersistentNotifications, error) {
|
||||
|
||||
tries := 0
|
||||
for {
|
||||
result, err := s.PostPersistentNotificationStore.GetSingle(postID)
|
||||
if err == nil {
|
||||
return result, nil
|
||||
}
|
||||
if !isRepeatableError(err) {
|
||||
return result, err
|
||||
}
|
||||
tries++
|
||||
if tries >= 3 {
|
||||
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
|
||||
return result, err
|
||||
}
|
||||
timepkg.Sleep(100 * timepkg.Millisecond)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *RetryLayerPostPersistentNotificationStore) UpdateLastActivity(postIds []string) error {
|
||||
|
||||
tries := 0
|
||||
for {
|
||||
err := s.PostPersistentNotificationStore.UpdateLastActivity(postIds)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if !isRepeatableError(err) {
|
||||
return err
|
||||
}
|
||||
tries++
|
||||
if tries >= 3 {
|
||||
err = errors.Wrap(err, "giving up after 3 consecutive repeatable transaction failures")
|
||||
return err
|
||||
}
|
||||
timepkg.Sleep(100 * timepkg.Millisecond)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *RetryLayerPostPriorityStore) GetForPost(postId string) (*model.PostPriority, error) {
|
||||
|
||||
tries := 0
|
||||
@@ -14750,6 +14907,7 @@ func New(childStore store.Store) *RetryLayer {
|
||||
newStore.PluginStore = &RetryLayerPluginStore{PluginStore: childStore.Plugin(), Root: &newStore}
|
||||
newStore.PostStore = &RetryLayerPostStore{PostStore: childStore.Post(), Root: &newStore}
|
||||
newStore.PostAcknowledgementStore = &RetryLayerPostAcknowledgementStore{PostAcknowledgementStore: childStore.PostAcknowledgement(), Root: &newStore}
|
||||
newStore.PostPersistentNotificationStore = &RetryLayerPostPersistentNotificationStore{PostPersistentNotificationStore: childStore.PostPersistentNotification(), Root: &newStore}
|
||||
newStore.PostPriorityStore = &RetryLayerPostPriorityStore{PostPriorityStore: childStore.PostPriority(), Root: &newStore}
|
||||
newStore.PreferenceStore = &RetryLayerPreferenceStore{PreferenceStore: childStore.Preference(), Root: &newStore}
|
||||
newStore.ProductNoticesStore = &RetryLayerProductNoticesStore{ProductNoticesStore: childStore.ProductNotices(), Root: &newStore}
|
||||
|
||||
@@ -57,6 +57,7 @@ func genStore() *mocks.Store {
|
||||
mock.On("Draft").Return(&mocks.DraftStore{})
|
||||
mock.On("PostPriority").Return(&mocks.PostPriorityStore{})
|
||||
mock.On("PostAcknowledgement").Return(&mocks.PostAcknowledgementStore{})
|
||||
mock.On("PostPersistentNotification").Return(&mocks.PostPersistentNotificationStore{})
|
||||
mock.On("TrueUpReview").Return(&mocks.TrueUpReviewStore{})
|
||||
return mock
|
||||
}
|
||||
|
||||
193
server/channels/store/sqlstore/post_persistent_notification_store.go
Обычный файл
193
server/channels/store/sqlstore/post_persistent_notification_store.go
Обычный файл
@@ -0,0 +1,193 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
|
||||
"github.com/mattermost/mattermost-server/server/public/model"
|
||||
"github.com/mattermost/mattermost-server/server/v8/channels/store"
|
||||
sq "github.com/mattermost/squirrel"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type SqlPostPersistentNotificationStore struct {
|
||||
*SqlStore
|
||||
}
|
||||
|
||||
func newSqlPostPersistentNotificationStore(sqlStore *SqlStore) store.PostPersistentNotificationStore {
|
||||
return &SqlPostPersistentNotificationStore{
|
||||
SqlStore: sqlStore,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SqlPostPersistentNotificationStore) GetSingle(postID string) (*model.PostPersistentNotifications, error) {
|
||||
builder := s.getQueryBuilder().
|
||||
Select("PostId, CreateAt, LastSentAt, DeleteAt, SentCount").
|
||||
From("PersistentNotifications").
|
||||
Where(sq.And{
|
||||
sq.Eq{"DeleteAt": 0},
|
||||
sq.Eq{"PostId": postID},
|
||||
})
|
||||
|
||||
post := &model.PostPersistentNotifications{}
|
||||
err := s.GetReplicaX().GetBuilder(post, builder)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, store.NewErrNotFound("Persistent Notification Post", postID)
|
||||
}
|
||||
return nil, errors.Wrapf(err, "failed to get the persistent notification post=%s", postID)
|
||||
}
|
||||
return post, nil
|
||||
}
|
||||
|
||||
// Get returns only valid posts.
|
||||
func (s *SqlPostPersistentNotificationStore) Get(params model.GetPersistentNotificationsPostsParams) ([]*model.PostPersistentNotifications, error) {
|
||||
if params.PerPage == 0 {
|
||||
params.PerPage = 1000
|
||||
}
|
||||
|
||||
builder := s.getQueryBuilder().
|
||||
Select("PostId, CreateAt, LastSentAt, DeleteAt, SentCount").
|
||||
From("PersistentNotifications").
|
||||
Where(sq.And{
|
||||
sq.Eq{"DeleteAt": 0},
|
||||
sq.LtOrEq{"CreateAt": params.MaxTime},
|
||||
sq.LtOrEq{"LastSentAt": params.MaxTime},
|
||||
sq.Lt{"SentCount": params.MaxSentCount},
|
||||
}).
|
||||
Limit(uint64(params.PerPage))
|
||||
|
||||
var posts []*model.PostPersistentNotifications
|
||||
// Replica may not have the latest changes(done by UpdateLastActivity func)
|
||||
// by the time this Get func is called again in the loop.
|
||||
err := s.GetMasterX().SelectBuilder(&posts, builder)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to get notifications")
|
||||
}
|
||||
|
||||
return posts, nil
|
||||
}
|
||||
|
||||
func (s *SqlPostPersistentNotificationStore) UpdateLastActivity(postIds []string) error {
|
||||
builder := s.getQueryBuilder().
|
||||
Update("PersistentNotifications").
|
||||
Set("LastSentAt", model.GetMillis()).
|
||||
Set("SentCount", sq.Expr("SentCount+1")).
|
||||
Where(sq.Eq{"PostId": postIds})
|
||||
|
||||
_, err := s.GetMasterX().ExecBuilder(builder)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to update last activity for posts %s", postIds)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SqlPostPersistentNotificationStore) Delete(postIds []string) error {
|
||||
count := len(postIds)
|
||||
if count == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
builder := s.getQueryBuilder().
|
||||
Update("PersistentNotifications").
|
||||
Set("DeleteAt", model.GetMillis()).
|
||||
Where(sq.Eq{"PostId": postIds})
|
||||
|
||||
_, err := s.GetMasterX().ExecBuilder(builder)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to delete notifications for posts %s", postIds)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SqlPostPersistentNotificationStore) DeleteExpired(maxSentCount int16) error {
|
||||
builder := s.getQueryBuilder().
|
||||
Update("PersistentNotifications").
|
||||
Set("DeleteAt", model.GetMillis()).
|
||||
Where(sq.And{
|
||||
sq.Eq{"DeleteAt": 0},
|
||||
sq.GtOrEq{"SentCount": maxSentCount},
|
||||
})
|
||||
|
||||
_, err := s.GetMasterX().ExecBuilder(builder)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to delete notifications")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SqlPostPersistentNotificationStore) DeleteByChannel(channelIds []string) error {
|
||||
count := len(channelIds)
|
||||
if count == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
deleteAt := model.GetMillis()
|
||||
var builder sq.UpdateBuilder
|
||||
builderType := s.getQueryBuilder()
|
||||
if s.DriverName() == model.DatabaseDriverMysql {
|
||||
builder = builderType.
|
||||
Update("PersistentNotifications, Posts").
|
||||
Set("PersistentNotifications.DeleteAt", deleteAt)
|
||||
}
|
||||
|
||||
if s.DriverName() == model.DatabaseDriverPostgres {
|
||||
builder = builderType.
|
||||
Update("PersistentNotifications").
|
||||
Set("DeleteAt", deleteAt).
|
||||
From("Posts")
|
||||
}
|
||||
|
||||
builder = builder.Where(sq.And{
|
||||
sq.Expr("Posts.Id = PersistentNotifications.PostId"),
|
||||
sq.Eq{"Posts.ChannelId": channelIds},
|
||||
})
|
||||
|
||||
_, err := s.GetMasterX().ExecBuilder(builder)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to delete notifications for channels %s", channelIds)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SqlPostPersistentNotificationStore) DeleteByTeam(teamIds []string) error {
|
||||
count := len(teamIds)
|
||||
if count == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
deleteAt := model.GetMillis()
|
||||
var builder sq.UpdateBuilder
|
||||
builderType := s.getQueryBuilder()
|
||||
if s.DriverName() == model.DatabaseDriverMysql {
|
||||
builder = builderType.
|
||||
Update("PersistentNotifications, Posts, Channels").
|
||||
Set("PersistentNotifications.DeleteAt", deleteAt)
|
||||
}
|
||||
|
||||
if s.DriverName() == model.DatabaseDriverPostgres {
|
||||
builder = builderType.
|
||||
Update("PersistentNotifications").
|
||||
Set("DeleteAt", deleteAt).
|
||||
From("Posts, Channels")
|
||||
}
|
||||
|
||||
builder = builder.Where(sq.And{
|
||||
sq.Expr("Posts.Id = PersistentNotifications.PostId"),
|
||||
sq.Expr("Posts.ChannelId = Channels.Id"),
|
||||
sq.Eq{"Channels.TeamId": teamIds},
|
||||
})
|
||||
|
||||
_, err := s.GetMasterX().ExecBuilder(builder)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to delete notifications for teams %s", teamIds)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package sqlstore
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/mattermost/mattermost-server/server/v8/channels/store/storetest"
|
||||
)
|
||||
|
||||
func TestPostPersistentNotificationStore(t *testing.T) {
|
||||
StoreTestWithSqlStore(t, storetest.TestPostPersistentNotificationStore)
|
||||
}
|
||||
@@ -223,6 +223,10 @@ func (s *SqlPostStore) SaveMultiple(posts []*model.Post) ([]*model.Post, int, er
|
||||
return nil, -1, errors.Wrap(err, "failed to save PostPriority")
|
||||
}
|
||||
|
||||
if err = s.savePostsPersistentNotifications(transaction, posts); err != nil {
|
||||
return nil, -1, errors.Wrap(err, "failed to save posts persistent notifications")
|
||||
}
|
||||
|
||||
if err = transaction.Commit(); err != nil {
|
||||
// don't need to rollback here since the transaction is already closed
|
||||
return posts, -1, errors.Wrap(err, "commit_transaction")
|
||||
@@ -3004,6 +3008,20 @@ func (s *SqlPostStore) savePostsPriority(transaction *sqlxTxWrapper, posts []*mo
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SqlPostStore) savePostsPersistentNotifications(transaction *sqlxTxWrapper, posts []*model.Post) error {
|
||||
for _, post := range posts {
|
||||
if priority := post.GetPriority(); priority != nil && priority.PersistentNotifications != nil && *priority.PersistentNotifications {
|
||||
if _, err := transaction.NamedExec(`INSERT INTO PersistentNotifications (PostId, CreateAt, LastSentAt, DeleteAt, SentCount) VALUES (:PostId, :CreateAt, :LastSentAt, :DeleteAt, :SentCount)`, &model.PostPersistentNotifications{
|
||||
PostId: post.Id,
|
||||
CreateAt: post.CreateAt,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SqlPostStore) updateThreadsFromPosts(transaction *sqlxTxWrapper, posts []*model.Post) error {
|
||||
postsByRoot := map[string][]*model.Post{}
|
||||
var rootIds []string
|
||||
|
||||
@@ -71,48 +71,49 @@ const (
|
||||
var tablesToCheckForCollation = []string{"incomingwebhooks", "preferences", "users", "uploadsessions", "channels", "publicchannels"}
|
||||
|
||||
type SqlStoreStores struct {
|
||||
team store.TeamStore
|
||||
channel store.ChannelStore
|
||||
post store.PostStore
|
||||
retentionPolicy store.RetentionPolicyStore
|
||||
thread store.ThreadStore
|
||||
user store.UserStore
|
||||
bot store.BotStore
|
||||
audit store.AuditStore
|
||||
cluster store.ClusterDiscoveryStore
|
||||
remoteCluster store.RemoteClusterStore
|
||||
compliance store.ComplianceStore
|
||||
session store.SessionStore
|
||||
oauth store.OAuthStore
|
||||
system store.SystemStore
|
||||
webhook store.WebhookStore
|
||||
command store.CommandStore
|
||||
commandWebhook store.CommandWebhookStore
|
||||
preference store.PreferenceStore
|
||||
license store.LicenseStore
|
||||
token store.TokenStore
|
||||
emoji store.EmojiStore
|
||||
status store.StatusStore
|
||||
fileInfo store.FileInfoStore
|
||||
uploadSession store.UploadSessionStore
|
||||
reaction store.ReactionStore
|
||||
job store.JobStore
|
||||
userAccessToken store.UserAccessTokenStore
|
||||
plugin store.PluginStore
|
||||
channelMemberHistory store.ChannelMemberHistoryStore
|
||||
role store.RoleStore
|
||||
scheme store.SchemeStore
|
||||
TermsOfService store.TermsOfServiceStore
|
||||
productNotices store.ProductNoticesStore
|
||||
group store.GroupStore
|
||||
UserTermsOfService store.UserTermsOfServiceStore
|
||||
linkMetadata store.LinkMetadataStore
|
||||
sharedchannel store.SharedChannelStore
|
||||
draft store.DraftStore
|
||||
notifyAdmin store.NotifyAdminStore
|
||||
postPriority store.PostPriorityStore
|
||||
postAcknowledgement store.PostAcknowledgementStore
|
||||
trueUpReview store.TrueUpReviewStore
|
||||
team store.TeamStore
|
||||
channel store.ChannelStore
|
||||
post store.PostStore
|
||||
retentionPolicy store.RetentionPolicyStore
|
||||
thread store.ThreadStore
|
||||
user store.UserStore
|
||||
bot store.BotStore
|
||||
audit store.AuditStore
|
||||
cluster store.ClusterDiscoveryStore
|
||||
remoteCluster store.RemoteClusterStore
|
||||
compliance store.ComplianceStore
|
||||
session store.SessionStore
|
||||
oauth store.OAuthStore
|
||||
system store.SystemStore
|
||||
webhook store.WebhookStore
|
||||
command store.CommandStore
|
||||
commandWebhook store.CommandWebhookStore
|
||||
preference store.PreferenceStore
|
||||
license store.LicenseStore
|
||||
token store.TokenStore
|
||||
emoji store.EmojiStore
|
||||
status store.StatusStore
|
||||
fileInfo store.FileInfoStore
|
||||
uploadSession store.UploadSessionStore
|
||||
reaction store.ReactionStore
|
||||
job store.JobStore
|
||||
userAccessToken store.UserAccessTokenStore
|
||||
plugin store.PluginStore
|
||||
channelMemberHistory store.ChannelMemberHistoryStore
|
||||
role store.RoleStore
|
||||
scheme store.SchemeStore
|
||||
TermsOfService store.TermsOfServiceStore
|
||||
productNotices store.ProductNoticesStore
|
||||
group store.GroupStore
|
||||
UserTermsOfService store.UserTermsOfServiceStore
|
||||
linkMetadata store.LinkMetadataStore
|
||||
sharedchannel store.SharedChannelStore
|
||||
draft store.DraftStore
|
||||
notifyAdmin store.NotifyAdminStore
|
||||
postPriority store.PostPriorityStore
|
||||
postAcknowledgement store.PostAcknowledgementStore
|
||||
postPersistentNotification store.PostPersistentNotificationStore
|
||||
trueUpReview store.TrueUpReviewStore
|
||||
}
|
||||
|
||||
type SqlStore struct {
|
||||
@@ -232,6 +233,7 @@ func New(settings model.SqlSettings, metrics einterfaces.MetricsInterface) *SqlS
|
||||
store.stores.notifyAdmin = newSqlNotifyAdminStore(store)
|
||||
store.stores.postPriority = newSqlPostPriorityStore(store)
|
||||
store.stores.postAcknowledgement = newSqlPostAcknowledgementStore(store)
|
||||
store.stores.postPersistentNotification = newSqlPostPersistentNotificationStore(store)
|
||||
store.stores.trueUpReview = newSqlTrueUpReviewStore(store)
|
||||
|
||||
store.stores.preference.(*SqlPreferenceStore).deleteUnusedFeatures()
|
||||
@@ -1076,6 +1078,10 @@ func (ss *SqlStore) PostAcknowledgement() store.PostAcknowledgementStore {
|
||||
return ss.stores.postAcknowledgement
|
||||
}
|
||||
|
||||
func (ss *SqlStore) PostPersistentNotification() store.PostPersistentNotificationStore {
|
||||
return ss.stores.postPersistentNotification
|
||||
}
|
||||
|
||||
func (ss *SqlStore) TrueUpReview() store.TrueUpReviewStore {
|
||||
return ss.stores.trueUpReview
|
||||
}
|
||||
|
||||
@@ -84,6 +84,7 @@ type Store interface {
|
||||
NotifyAdmin() NotifyAdminStore
|
||||
PostPriority() PostPriorityStore
|
||||
PostAcknowledgement() PostAcknowledgementStore
|
||||
PostPersistentNotification() PostPersistentNotificationStore
|
||||
TrueUpReview() TrueUpReviewStore
|
||||
}
|
||||
|
||||
@@ -999,6 +1000,16 @@ type PostAcknowledgementStore interface {
|
||||
Delete(acknowledgement *model.PostAcknowledgement) error
|
||||
}
|
||||
|
||||
type PostPersistentNotificationStore interface {
|
||||
Get(params model.GetPersistentNotificationsPostsParams) ([]*model.PostPersistentNotifications, error)
|
||||
GetSingle(postID string) (*model.PostPersistentNotifications, error)
|
||||
UpdateLastActivity(postIds []string) error
|
||||
Delete(postIds []string) error
|
||||
DeleteExpired(maxSentCount int16) error
|
||||
DeleteByChannel(channelIds []string) error
|
||||
DeleteByTeam(teamIds []string) error
|
||||
}
|
||||
|
||||
type TrueUpReviewStore interface {
|
||||
GetTrueUpReviewStatus(dueDate int64) (*model.TrueUpReviewStatus, error)
|
||||
CreateTrueUpReviewStatusRecord(reviewStatus *model.TrueUpReviewStatus) (*model.TrueUpReviewStatus, error)
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -17,49 +17,50 @@ import (
|
||||
|
||||
type TimerLayer struct {
|
||||
store.Store
|
||||
Metrics einterfaces.MetricsInterface
|
||||
AuditStore store.AuditStore
|
||||
BotStore store.BotStore
|
||||
ChannelStore store.ChannelStore
|
||||
ChannelMemberHistoryStore store.ChannelMemberHistoryStore
|
||||
ClusterDiscoveryStore store.ClusterDiscoveryStore
|
||||
CommandStore store.CommandStore
|
||||
CommandWebhookStore store.CommandWebhookStore
|
||||
ComplianceStore store.ComplianceStore
|
||||
DraftStore store.DraftStore
|
||||
EmojiStore store.EmojiStore
|
||||
FileInfoStore store.FileInfoStore
|
||||
GroupStore store.GroupStore
|
||||
JobStore store.JobStore
|
||||
LicenseStore store.LicenseStore
|
||||
LinkMetadataStore store.LinkMetadataStore
|
||||
NotifyAdminStore store.NotifyAdminStore
|
||||
OAuthStore store.OAuthStore
|
||||
PluginStore store.PluginStore
|
||||
PostStore store.PostStore
|
||||
PostAcknowledgementStore store.PostAcknowledgementStore
|
||||
PostPriorityStore store.PostPriorityStore
|
||||
PreferenceStore store.PreferenceStore
|
||||
ProductNoticesStore store.ProductNoticesStore
|
||||
ReactionStore store.ReactionStore
|
||||
RemoteClusterStore store.RemoteClusterStore
|
||||
RetentionPolicyStore store.RetentionPolicyStore
|
||||
RoleStore store.RoleStore
|
||||
SchemeStore store.SchemeStore
|
||||
SessionStore store.SessionStore
|
||||
SharedChannelStore store.SharedChannelStore
|
||||
StatusStore store.StatusStore
|
||||
SystemStore store.SystemStore
|
||||
TeamStore store.TeamStore
|
||||
TermsOfServiceStore store.TermsOfServiceStore
|
||||
ThreadStore store.ThreadStore
|
||||
TokenStore store.TokenStore
|
||||
TrueUpReviewStore store.TrueUpReviewStore
|
||||
UploadSessionStore store.UploadSessionStore
|
||||
UserStore store.UserStore
|
||||
UserAccessTokenStore store.UserAccessTokenStore
|
||||
UserTermsOfServiceStore store.UserTermsOfServiceStore
|
||||
WebhookStore store.WebhookStore
|
||||
Metrics einterfaces.MetricsInterface
|
||||
AuditStore store.AuditStore
|
||||
BotStore store.BotStore
|
||||
ChannelStore store.ChannelStore
|
||||
ChannelMemberHistoryStore store.ChannelMemberHistoryStore
|
||||
ClusterDiscoveryStore store.ClusterDiscoveryStore
|
||||
CommandStore store.CommandStore
|
||||
CommandWebhookStore store.CommandWebhookStore
|
||||
ComplianceStore store.ComplianceStore
|
||||
DraftStore store.DraftStore
|
||||
EmojiStore store.EmojiStore
|
||||
FileInfoStore store.FileInfoStore
|
||||
GroupStore store.GroupStore
|
||||
JobStore store.JobStore
|
||||
LicenseStore store.LicenseStore
|
||||
LinkMetadataStore store.LinkMetadataStore
|
||||
NotifyAdminStore store.NotifyAdminStore
|
||||
OAuthStore store.OAuthStore
|
||||
PluginStore store.PluginStore
|
||||
PostStore store.PostStore
|
||||
PostAcknowledgementStore store.PostAcknowledgementStore
|
||||
PostPersistentNotificationStore store.PostPersistentNotificationStore
|
||||
PostPriorityStore store.PostPriorityStore
|
||||
PreferenceStore store.PreferenceStore
|
||||
ProductNoticesStore store.ProductNoticesStore
|
||||
ReactionStore store.ReactionStore
|
||||
RemoteClusterStore store.RemoteClusterStore
|
||||
RetentionPolicyStore store.RetentionPolicyStore
|
||||
RoleStore store.RoleStore
|
||||
SchemeStore store.SchemeStore
|
||||
SessionStore store.SessionStore
|
||||
SharedChannelStore store.SharedChannelStore
|
||||
StatusStore store.StatusStore
|
||||
SystemStore store.SystemStore
|
||||
TeamStore store.TeamStore
|
||||
TermsOfServiceStore store.TermsOfServiceStore
|
||||
ThreadStore store.ThreadStore
|
||||
TokenStore store.TokenStore
|
||||
TrueUpReviewStore store.TrueUpReviewStore
|
||||
UploadSessionStore store.UploadSessionStore
|
||||
UserStore store.UserStore
|
||||
UserAccessTokenStore store.UserAccessTokenStore
|
||||
UserTermsOfServiceStore store.UserTermsOfServiceStore
|
||||
WebhookStore store.WebhookStore
|
||||
}
|
||||
|
||||
func (s *TimerLayer) Audit() store.AuditStore {
|
||||
@@ -142,6 +143,10 @@ func (s *TimerLayer) PostAcknowledgement() store.PostAcknowledgementStore {
|
||||
return s.PostAcknowledgementStore
|
||||
}
|
||||
|
||||
func (s *TimerLayer) PostPersistentNotification() store.PostPersistentNotificationStore {
|
||||
return s.PostPersistentNotificationStore
|
||||
}
|
||||
|
||||
func (s *TimerLayer) PostPriority() store.PostPriorityStore {
|
||||
return s.PostPriorityStore
|
||||
}
|
||||
@@ -330,6 +335,11 @@ type TimerLayerPostAcknowledgementStore struct {
|
||||
Root *TimerLayer
|
||||
}
|
||||
|
||||
type TimerLayerPostPersistentNotificationStore struct {
|
||||
store.PostPersistentNotificationStore
|
||||
Root *TimerLayer
|
||||
}
|
||||
|
||||
type TimerLayerPostPriorityStore struct {
|
||||
store.PostPriorityStore
|
||||
Root *TimerLayer
|
||||
@@ -6186,6 +6196,118 @@ func (s *TimerLayerPostAcknowledgementStore) Save(postID string, userID string,
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *TimerLayerPostPersistentNotificationStore) Delete(postIds []string) error {
|
||||
start := time.Now()
|
||||
|
||||
err := s.PostPersistentNotificationStore.Delete(postIds)
|
||||
|
||||
elapsed := float64(time.Since(start)) / float64(time.Second)
|
||||
if s.Root.Metrics != nil {
|
||||
success := "false"
|
||||
if err == nil {
|
||||
success = "true"
|
||||
}
|
||||
s.Root.Metrics.ObserveStoreMethodDuration("PostPersistentNotificationStore.Delete", success, elapsed)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *TimerLayerPostPersistentNotificationStore) DeleteByChannel(channelIds []string) error {
|
||||
start := time.Now()
|
||||
|
||||
err := s.PostPersistentNotificationStore.DeleteByChannel(channelIds)
|
||||
|
||||
elapsed := float64(time.Since(start)) / float64(time.Second)
|
||||
if s.Root.Metrics != nil {
|
||||
success := "false"
|
||||
if err == nil {
|
||||
success = "true"
|
||||
}
|
||||
s.Root.Metrics.ObserveStoreMethodDuration("PostPersistentNotificationStore.DeleteByChannel", success, elapsed)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *TimerLayerPostPersistentNotificationStore) DeleteByTeam(teamIds []string) error {
|
||||
start := time.Now()
|
||||
|
||||
err := s.PostPersistentNotificationStore.DeleteByTeam(teamIds)
|
||||
|
||||
elapsed := float64(time.Since(start)) / float64(time.Second)
|
||||
if s.Root.Metrics != nil {
|
||||
success := "false"
|
||||
if err == nil {
|
||||
success = "true"
|
||||
}
|
||||
s.Root.Metrics.ObserveStoreMethodDuration("PostPersistentNotificationStore.DeleteByTeam", success, elapsed)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *TimerLayerPostPersistentNotificationStore) DeleteExpired(maxSentCount int16) error {
|
||||
start := time.Now()
|
||||
|
||||
err := s.PostPersistentNotificationStore.DeleteExpired(maxSentCount)
|
||||
|
||||
elapsed := float64(time.Since(start)) / float64(time.Second)
|
||||
if s.Root.Metrics != nil {
|
||||
success := "false"
|
||||
if err == nil {
|
||||
success = "true"
|
||||
}
|
||||
s.Root.Metrics.ObserveStoreMethodDuration("PostPersistentNotificationStore.DeleteExpired", success, elapsed)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *TimerLayerPostPersistentNotificationStore) Get(params model.GetPersistentNotificationsPostsParams) ([]*model.PostPersistentNotifications, error) {
|
||||
start := time.Now()
|
||||
|
||||
result, err := s.PostPersistentNotificationStore.Get(params)
|
||||
|
||||
elapsed := float64(time.Since(start)) / float64(time.Second)
|
||||
if s.Root.Metrics != nil {
|
||||
success := "false"
|
||||
if err == nil {
|
||||
success = "true"
|
||||
}
|
||||
s.Root.Metrics.ObserveStoreMethodDuration("PostPersistentNotificationStore.Get", success, elapsed)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *TimerLayerPostPersistentNotificationStore) GetSingle(postID string) (*model.PostPersistentNotifications, error) {
|
||||
start := time.Now()
|
||||
|
||||
result, err := s.PostPersistentNotificationStore.GetSingle(postID)
|
||||
|
||||
elapsed := float64(time.Since(start)) / float64(time.Second)
|
||||
if s.Root.Metrics != nil {
|
||||
success := "false"
|
||||
if err == nil {
|
||||
success = "true"
|
||||
}
|
||||
s.Root.Metrics.ObserveStoreMethodDuration("PostPersistentNotificationStore.GetSingle", success, elapsed)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *TimerLayerPostPersistentNotificationStore) UpdateLastActivity(postIds []string) error {
|
||||
start := time.Now()
|
||||
|
||||
err := s.PostPersistentNotificationStore.UpdateLastActivity(postIds)
|
||||
|
||||
elapsed := float64(time.Since(start)) / float64(time.Second)
|
||||
if s.Root.Metrics != nil {
|
||||
success := "false"
|
||||
if err == nil {
|
||||
success = "true"
|
||||
}
|
||||
s.Root.Metrics.ObserveStoreMethodDuration("PostPersistentNotificationStore.UpdateLastActivity", success, elapsed)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *TimerLayerPostPriorityStore) GetForPost(postId string) (*model.PostPriority, error) {
|
||||
start := time.Now()
|
||||
|
||||
@@ -11663,6 +11785,7 @@ func New(childStore store.Store, metrics einterfaces.MetricsInterface) *TimerLay
|
||||
newStore.PluginStore = &TimerLayerPluginStore{PluginStore: childStore.Plugin(), Root: &newStore}
|
||||
newStore.PostStore = &TimerLayerPostStore{PostStore: childStore.Post(), Root: &newStore}
|
||||
newStore.PostAcknowledgementStore = &TimerLayerPostAcknowledgementStore{PostAcknowledgementStore: childStore.PostAcknowledgement(), Root: &newStore}
|
||||
newStore.PostPersistentNotificationStore = &TimerLayerPostPersistentNotificationStore{PostPersistentNotificationStore: childStore.PostPersistentNotification(), Root: &newStore}
|
||||
newStore.PostPriorityStore = &TimerLayerPostPriorityStore{PostPriorityStore: childStore.PostPriority(), Root: &newStore}
|
||||
newStore.PreferenceStore = &TimerLayerPreferenceStore{PreferenceStore: childStore.Preference(), Root: &newStore}
|
||||
newStore.ProductNoticesStore = &TimerLayerProductNoticesStore{ProductNoticesStore: childStore.ProductNotices(), Root: &newStore}
|
||||
|
||||
Ссылка в новой задаче
Block a user