[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>
Этот коммит содержится в:
Vishal
2023-05-18 23:44:12 +05:30
коммит произвёл GitHub
родитель ce165302cf
Коммит 9399ce8637
74 изменённых файлов: 4052 добавлений и 888 удалений

Просмотреть файл

@@ -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}