MM-46410: adds urgency on mention counts (#20999)

* 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 a column, urgentmentioncount, in channelmembers.
Furthermore when asking for team/thread mention counts, we also return
urgent mention counts for the user.

Adds a new table to hold posts priorities
Refactors priority out of the props and into the new table

We are nilifying Metadata when post.ForPlugin(), which didn't save Priority
for a post when Boards was enabled.
This commit copies metadata again to the post, so metadata are
reinstated.

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Co-authored-by: Vishal Choudhary <vish9812@gmail.com>
Этот коммит содержится в:
Kyriakos Z
2022-11-23 21:08:21 +02:00
коммит произвёл GitHub
родитель ff1ea0599e
Коммит c44d37629a
47 изменённых файлов: 1676 добавлений и 343 удалений

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

@@ -37,6 +37,7 @@ type OpenTracingLayer struct {
OAuthStore store.OAuthStore
PluginStore store.PluginStore
PostStore store.PostStore
PostPriorityStore store.PostPriorityStore
PreferenceStore store.PreferenceStore
ProductNoticesStore store.ProductNoticesStore
ReactionStore store.ReactionStore
@@ -131,6 +132,10 @@ func (s *OpenTracingLayer) Post() store.PostStore {
return s.PostStore
}
func (s *OpenTracingLayer) PostPriority() store.PostPriorityStore {
return s.PostPriorityStore
}
func (s *OpenTracingLayer) Preference() store.PreferenceStore {
return s.PreferenceStore
}
@@ -301,6 +306,11 @@ type OpenTracingLayerPostStore struct {
Root *OpenTracingLayer
}
type OpenTracingLayerPostPriorityStore struct {
store.PostPriorityStore
Root *OpenTracingLayer
}
type OpenTracingLayerPreferenceStore struct {
store.PreferenceStore
Root *OpenTracingLayer
@@ -702,6 +712,24 @@ func (s *OpenTracingLayerChannelStore) CountPostsAfter(channelID string, timesta
return result, resultVar1, err
}
func (s *OpenTracingLayerChannelStore) CountUrgentPostsAfter(channelID string, timestamp int64, userID string) (int, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.CountUrgentPostsAfter")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
result, err := s.ChannelStore.CountUrgentPostsAfter(channelID, timestamp, userID)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
}
return result, err
}
func (s *OpenTracingLayerChannelStore) CreateDirectChannel(userID *model.User, otherUserID *model.User, channelOptions ...model.ChannelOption) (*model.Channel, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.CreateDirectChannel")
@@ -1867,7 +1895,7 @@ func (s *OpenTracingLayerChannelStore) GroupSyncedChannelCount() (int64, error)
return result, err
}
func (s *OpenTracingLayerChannelStore) IncrementMentionCount(channelID string, userIDs []string, isRoot bool) error {
func (s *OpenTracingLayerChannelStore) IncrementMentionCount(channelID string, userIDs []string, isRoot bool, isUrgent bool) error {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.IncrementMentionCount")
s.Root.Store.SetContext(newCtx)
@@ -1876,7 +1904,7 @@ func (s *OpenTracingLayerChannelStore) IncrementMentionCount(channelID string, u
}()
defer span.Finish()
err := s.ChannelStore.IncrementMentionCount(channelID, userIDs, isRoot)
err := s.ChannelStore.IncrementMentionCount(channelID, userIDs, isRoot, isUrgent)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
@@ -2439,7 +2467,7 @@ func (s *OpenTracingLayerChannelStore) UpdateLastViewedAt(channelIds []string, u
return result, err
}
func (s *OpenTracingLayerChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount int, mentionCountRoot int, setUnreadCountRoot bool) (*model.ChannelUnreadAt, error) {
func (s *OpenTracingLayerChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount int, mentionCountRoot int, urgentMentionCount int, setUnreadCountRoot bool) (*model.ChannelUnreadAt, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ChannelStore.UpdateLastViewedAtPost")
s.Root.Store.SetContext(newCtx)
@@ -2448,7 +2476,7 @@ func (s *OpenTracingLayerChannelStore) UpdateLastViewedAtPost(unreadPost *model.
}()
defer span.Finish()
result, err := s.ChannelStore.UpdateLastViewedAtPost(unreadPost, userID, mentionCount, mentionCountRoot, setUnreadCountRoot)
result, err := s.ChannelStore.UpdateLastViewedAtPost(unreadPost, userID, mentionCount, mentionCountRoot, urgentMentionCount, setUnreadCountRoot)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
@@ -6516,6 +6544,42 @@ func (s *OpenTracingLayerPostStore) Update(newPost *model.Post, oldPost *model.P
return result, 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")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
result, err := s.PostPriorityStore.GetForPost(postId)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
}
return result, err
}
func (s *OpenTracingLayerPostPriorityStore) GetForPosts(ids []string) ([]*model.PostPriority, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PostPriorityStore.GetForPosts")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
result, err := s.PostPriorityStore.GetForPosts(ids)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
}
return result, err
}
func (s *OpenTracingLayerPreferenceStore) CleanupFlagsBatch(limit int64) (int64, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "PreferenceStore.CleanupFlagsBatch")
@@ -9872,7 +9936,7 @@ func (s *OpenTracingLayerThreadStore) GetMembershipsForUser(userId string, teamI
return result, err
}
func (s *OpenTracingLayerThreadStore) GetTeamsUnreadForUser(userID string, teamIDs []string) (map[string]*model.TeamUnread, error) {
func (s *OpenTracingLayerThreadStore) GetTeamsUnreadForUser(userID string, teamIDs []string, includeUrgentMentionCount bool) (map[string]*model.TeamUnread, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ThreadStore.GetTeamsUnreadForUser")
s.Root.Store.SetContext(newCtx)
@@ -9881,7 +9945,7 @@ func (s *OpenTracingLayerThreadStore) GetTeamsUnreadForUser(userID string, teamI
}()
defer span.Finish()
result, err := s.ThreadStore.GetTeamsUnreadForUser(userID, teamIDs)
result, err := s.ThreadStore.GetTeamsUnreadForUser(userID, teamIDs, includeUrgentMentionCount)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
@@ -9908,7 +9972,7 @@ func (s *OpenTracingLayerThreadStore) GetThreadFollowers(threadID string, fetchO
return result, err
}
func (s *OpenTracingLayerThreadStore) GetThreadForUser(threadMembership *model.ThreadMembership, extended bool) (*model.ThreadResponse, error) {
func (s *OpenTracingLayerThreadStore) GetThreadForUser(threadMembership *model.ThreadMembership, extended bool, postPriorityIsEnabled bool) (*model.ThreadResponse, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ThreadStore.GetThreadForUser")
s.Root.Store.SetContext(newCtx)
@@ -9917,7 +9981,7 @@ func (s *OpenTracingLayerThreadStore) GetThreadForUser(threadMembership *model.T
}()
defer span.Finish()
result, err := s.ThreadStore.GetThreadForUser(threadMembership, extended)
result, err := s.ThreadStore.GetThreadForUser(threadMembership, extended, postPriorityIsEnabled)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
@@ -10052,6 +10116,24 @@ func (s *OpenTracingLayerThreadStore) GetTotalUnreadThreads(userId string, teamI
return result, err
}
func (s *OpenTracingLayerThreadStore) GetTotalUnreadUrgentMentions(userId string, teamID string, opts model.GetUserThreadsOpts) (int64, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ThreadStore.GetTotalUnreadUrgentMentions")
s.Root.Store.SetContext(newCtx)
defer func() {
s.Root.Store.SetContext(origCtx)
}()
defer span.Finish()
result, err := s.ThreadStore.GetTotalUnreadUrgentMentions(userId, teamID, opts)
if err != nil {
span.LogFields(spanlog.Error(err))
ext.Error.Set(span, true)
}
return result, err
}
func (s *OpenTracingLayerThreadStore) MaintainMembership(userID string, postID string, opts store.ThreadMembershipOpts) (*model.ThreadMembership, error) {
origCtx := s.Root.Store.Context()
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ThreadStore.MaintainMembership")
@@ -12509,6 +12591,7 @@ func New(childStore store.Store, ctx context.Context) *OpenTracingLayer {
newStore.OAuthStore = &OpenTracingLayerOAuthStore{OAuthStore: childStore.OAuth(), Root: &newStore}
newStore.PluginStore = &OpenTracingLayerPluginStore{PluginStore: childStore.Plugin(), Root: &newStore}
newStore.PostStore = &OpenTracingLayerPostStore{PostStore: childStore.Post(), 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}
newStore.ReactionStore = &OpenTracingLayerReactionStore{ReactionStore: childStore.Reaction(), Root: &newStore}

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

@@ -40,6 +40,7 @@ type RetryLayer struct {
OAuthStore store.OAuthStore
PluginStore store.PluginStore
PostStore store.PostStore
PostPriorityStore store.PostPriorityStore
PreferenceStore store.PreferenceStore
ProductNoticesStore store.ProductNoticesStore
ReactionStore store.ReactionStore
@@ -134,6 +135,10 @@ func (s *RetryLayer) Post() store.PostStore {
return s.PostStore
}
func (s *RetryLayer) PostPriority() store.PostPriorityStore {
return s.PostPriorityStore
}
func (s *RetryLayer) Preference() store.PreferenceStore {
return s.PreferenceStore
}
@@ -304,6 +309,11 @@ type RetryLayerPostStore struct {
Root *RetryLayer
}
type RetryLayerPostPriorityStore struct {
store.PostPriorityStore
Root *RetryLayer
}
type RetryLayerPreferenceStore struct {
store.PreferenceStore
Root *RetryLayer
@@ -762,6 +772,27 @@ func (s *RetryLayerChannelStore) CountPostsAfter(channelID string, timestamp int
}
func (s *RetryLayerChannelStore) CountUrgentPostsAfter(channelID string, timestamp int64, userID string) (int, error) {
tries := 0
for {
result, err := s.ChannelStore.CountUrgentPostsAfter(channelID, timestamp, userID)
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 *RetryLayerChannelStore) CreateDirectChannel(userID *model.User, otherUserID *model.User, channelOptions ...model.ChannelOption) (*model.Channel, error) {
tries := 0
@@ -2112,11 +2143,11 @@ func (s *RetryLayerChannelStore) GroupSyncedChannelCount() (int64, error) {
}
func (s *RetryLayerChannelStore) IncrementMentionCount(channelID string, userIDs []string, isRoot bool) error {
func (s *RetryLayerChannelStore) IncrementMentionCount(channelID string, userIDs []string, isRoot bool, isUrgent bool) error {
tries := 0
for {
err := s.ChannelStore.IncrementMentionCount(channelID, userIDs, isRoot)
err := s.ChannelStore.IncrementMentionCount(channelID, userIDs, isRoot, isUrgent)
if err == nil {
return nil
}
@@ -2706,11 +2737,11 @@ func (s *RetryLayerChannelStore) UpdateLastViewedAt(channelIds []string, userID
}
func (s *RetryLayerChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount int, mentionCountRoot int, setUnreadCountRoot bool) (*model.ChannelUnreadAt, error) {
func (s *RetryLayerChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount int, mentionCountRoot int, urgentMentionCount int, setUnreadCountRoot bool) (*model.ChannelUnreadAt, error) {
tries := 0
for {
result, err := s.ChannelStore.UpdateLastViewedAtPost(unreadPost, userID, mentionCount, mentionCountRoot, setUnreadCountRoot)
result, err := s.ChannelStore.UpdateLastViewedAtPost(unreadPost, userID, mentionCount, mentionCountRoot, urgentMentionCount, setUnreadCountRoot)
if err == nil {
return result, nil
}
@@ -7389,6 +7420,48 @@ func (s *RetryLayerPostStore) Update(newPost *model.Post, oldPost *model.Post) (
}
func (s *RetryLayerPostPriorityStore) GetForPost(postId string) (*model.PostPriority, error) {
tries := 0
for {
result, err := s.PostPriorityStore.GetForPost(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 *RetryLayerPostPriorityStore) GetForPosts(ids []string) ([]*model.PostPriority, error) {
tries := 0
for {
result, err := s.PostPriorityStore.GetForPosts(ids)
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 *RetryLayerPreferenceStore) CleanupFlagsBatch(limit int64) (int64, error) {
tries := 0
@@ -11286,11 +11359,11 @@ func (s *RetryLayerThreadStore) GetMembershipsForUser(userId string, teamID stri
}
func (s *RetryLayerThreadStore) GetTeamsUnreadForUser(userID string, teamIDs []string) (map[string]*model.TeamUnread, error) {
func (s *RetryLayerThreadStore) GetTeamsUnreadForUser(userID string, teamIDs []string, includeUrgentMentionCount bool) (map[string]*model.TeamUnread, error) {
tries := 0
for {
result, err := s.ThreadStore.GetTeamsUnreadForUser(userID, teamIDs)
result, err := s.ThreadStore.GetTeamsUnreadForUser(userID, teamIDs, includeUrgentMentionCount)
if err == nil {
return result, nil
}
@@ -11328,11 +11401,11 @@ func (s *RetryLayerThreadStore) GetThreadFollowers(threadID string, fetchOnlyAct
}
func (s *RetryLayerThreadStore) GetThreadForUser(threadMembership *model.ThreadMembership, extended bool) (*model.ThreadResponse, error) {
func (s *RetryLayerThreadStore) GetThreadForUser(threadMembership *model.ThreadMembership, extended bool, postPriorityIsEnabled bool) (*model.ThreadResponse, error) {
tries := 0
for {
result, err := s.ThreadStore.GetThreadForUser(threadMembership, extended)
result, err := s.ThreadStore.GetThreadForUser(threadMembership, extended, postPriorityIsEnabled)
if err == nil {
return result, nil
}
@@ -11496,6 +11569,27 @@ func (s *RetryLayerThreadStore) GetTotalUnreadThreads(userId string, teamID stri
}
func (s *RetryLayerThreadStore) GetTotalUnreadUrgentMentions(userId string, teamID string, opts model.GetUserThreadsOpts) (int64, error) {
tries := 0
for {
result, err := s.ThreadStore.GetTotalUnreadUrgentMentions(userId, teamID, opts)
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 *RetryLayerThreadStore) MaintainMembership(userID string, postID string, opts store.ThreadMembershipOpts) (*model.ThreadMembership, error) {
tries := 0
@@ -14261,6 +14355,7 @@ func New(childStore store.Store) *RetryLayer {
newStore.OAuthStore = &RetryLayerOAuthStore{OAuthStore: childStore.OAuth(), Root: &newStore}
newStore.PluginStore = &RetryLayerPluginStore{PluginStore: childStore.Plugin(), Root: &newStore}
newStore.PostStore = &RetryLayerPostStore{PostStore: childStore.Post(), 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}
newStore.ReactionStore = &RetryLayerReactionStore{ReactionStore: childStore.Reaction(), Root: &newStore}

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

@@ -54,6 +54,7 @@ func genStore() *mocks.Store {
mock.On("UserTermsOfService").Return(&mocks.UserTermsOfServiceStore{})
mock.On("Webhook").Return(&mocks.WebhookStore{})
mock.On("NotifyAdmin").Return(&mocks.NotifyAdminStore{})
mock.On("PostPriority").Return(&mocks.PostPriorityStore{})
return mock
}

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

@@ -41,36 +41,38 @@ type SqlChannelStore struct {
}
type channelMember struct {
ChannelId string
UserId string
Roles string
LastViewedAt int64
MsgCount int64
MentionCount int64
NotifyProps model.StringMap
LastUpdateAt int64
SchemeUser sql.NullBool
SchemeAdmin sql.NullBool
SchemeGuest sql.NullBool
MentionCountRoot int64
MsgCountRoot int64
ChannelId string
UserId string
Roles string
LastViewedAt int64
MsgCount int64
MentionCount int64
UrgentMentionCount int64
NotifyProps model.StringMap
LastUpdateAt int64
SchemeUser sql.NullBool
SchemeAdmin sql.NullBool
SchemeGuest sql.NullBool
MentionCountRoot int64
MsgCountRoot int64
}
func NewMapFromChannelMemberModel(cm *model.ChannelMember) map[string]any {
return map[string]any{
"ChannelId": cm.ChannelId,
"UserId": cm.UserId,
"Roles": cm.ExplicitRoles,
"LastViewedAt": cm.LastViewedAt,
"MsgCount": cm.MsgCount,
"MentionCount": cm.MentionCount,
"MentionCountRoot": cm.MentionCountRoot,
"MsgCountRoot": cm.MsgCountRoot,
"NotifyProps": cm.NotifyProps,
"LastUpdateAt": cm.LastUpdateAt,
"SchemeGuest": sql.NullBool{Valid: true, Bool: cm.SchemeGuest},
"SchemeUser": sql.NullBool{Valid: true, Bool: cm.SchemeUser},
"SchemeAdmin": sql.NullBool{Valid: true, Bool: cm.SchemeAdmin},
"ChannelId": cm.ChannelId,
"UserId": cm.UserId,
"Roles": cm.ExplicitRoles,
"LastViewedAt": cm.LastViewedAt,
"MsgCount": cm.MsgCount,
"MentionCount": cm.MentionCount,
"MentionCountRoot": cm.MentionCountRoot,
"UrgentMentionCount": cm.UrgentMentionCount,
"MsgCountRoot": cm.MsgCountRoot,
"NotifyProps": cm.NotifyProps,
"LastUpdateAt": cm.LastUpdateAt,
"SchemeGuest": sql.NullBool{Valid: true, Bool: cm.SchemeGuest},
"SchemeUser": sql.NullBool{Valid: true, Bool: cm.SchemeUser},
"SchemeAdmin": sql.NullBool{Valid: true, Bool: cm.SchemeAdmin},
}
}
@@ -82,6 +84,7 @@ type channelMemberWithSchemeRoles struct {
MsgCount int64
MentionCount int64
MentionCountRoot int64
UrgentMentionCount int64
NotifyProps model.StringMap
LastUpdateAt int64
SchemeGuest sql.NullBool
@@ -106,7 +109,7 @@ type channelMemberWithTeamWithSchemeRoles struct {
type channelMemberWithTeamWithSchemeRolesList []channelMemberWithTeamWithSchemeRoles
func channelMemberSliceColumns() []string {
return []string{"ChannelId", "UserId", "Roles", "LastViewedAt", "MsgCount", "MsgCountRoot", "MentionCount", "MentionCountRoot", "NotifyProps", "LastUpdateAt", "SchemeUser", "SchemeAdmin", "SchemeGuest"}
return []string{"ChannelId", "UserId", "Roles", "LastViewedAt", "MsgCount", "MsgCountRoot", "MentionCount", "MentionCountRoot", "UrgentMentionCount", "NotifyProps", "LastUpdateAt", "SchemeUser", "SchemeAdmin", "SchemeGuest"}
}
func channelMemberToSlice(member *model.ChannelMember) []any {
@@ -119,6 +122,7 @@ func channelMemberToSlice(member *model.ChannelMember) []any {
resultSlice = append(resultSlice, member.MsgCountRoot)
resultSlice = append(resultSlice, member.MentionCount)
resultSlice = append(resultSlice, member.MentionCountRoot)
resultSlice = append(resultSlice, member.UrgentMentionCount)
resultSlice = append(resultSlice, model.MapToJSON(member.NotifyProps))
resultSlice = append(resultSlice, member.LastUpdateAt)
resultSlice = append(resultSlice, member.SchemeUser)
@@ -244,20 +248,21 @@ func (db channelMemberWithSchemeRoles) ToModel() *model.ChannelMember {
strings.Fields(db.Roles),
)
return &model.ChannelMember{
ChannelId: db.ChannelId,
UserId: db.UserId,
Roles: strings.Join(rolesResult.roles, " "),
LastViewedAt: db.LastViewedAt,
MsgCount: db.MsgCount,
MsgCountRoot: db.MsgCountRoot,
MentionCount: db.MentionCount,
MentionCountRoot: db.MentionCountRoot,
NotifyProps: db.NotifyProps,
LastUpdateAt: db.LastUpdateAt,
SchemeAdmin: rolesResult.schemeAdmin,
SchemeUser: rolesResult.schemeUser,
SchemeGuest: rolesResult.schemeGuest,
ExplicitRoles: strings.Join(rolesResult.explicitRoles, " "),
ChannelId: db.ChannelId,
UserId: db.UserId,
Roles: strings.Join(rolesResult.roles, " "),
LastViewedAt: db.LastViewedAt,
MsgCount: db.MsgCount,
MsgCountRoot: db.MsgCountRoot,
MentionCount: db.MentionCount,
MentionCountRoot: db.MentionCountRoot,
UrgentMentionCount: db.UrgentMentionCount,
NotifyProps: db.NotifyProps,
LastUpdateAt: db.LastUpdateAt,
SchemeAdmin: rolesResult.schemeAdmin,
SchemeUser: rolesResult.schemeUser,
SchemeGuest: rolesResult.schemeGuest,
ExplicitRoles: strings.Join(rolesResult.explicitRoles, " "),
}
}
@@ -307,20 +312,21 @@ func (db channelMemberWithTeamWithSchemeRoles) ToModel() *model.ChannelMemberWit
)
return &model.ChannelMemberWithTeamData{
ChannelMember: model.ChannelMember{
ChannelId: db.ChannelId,
UserId: db.UserId,
Roles: strings.Join(rolesResult.roles, " "),
LastViewedAt: db.LastViewedAt,
MsgCount: db.MsgCount,
MsgCountRoot: db.MsgCountRoot,
MentionCount: db.MentionCount,
MentionCountRoot: db.MentionCountRoot,
NotifyProps: db.NotifyProps,
LastUpdateAt: db.LastUpdateAt,
SchemeAdmin: rolesResult.schemeAdmin,
SchemeUser: rolesResult.schemeUser,
SchemeGuest: rolesResult.schemeGuest,
ExplicitRoles: strings.Join(rolesResult.explicitRoles, " "),
ChannelId: db.ChannelId,
UserId: db.UserId,
Roles: strings.Join(rolesResult.roles, " "),
LastViewedAt: db.LastViewedAt,
MsgCount: db.MsgCount,
MsgCountRoot: db.MsgCountRoot,
MentionCount: db.MentionCount,
MentionCountRoot: db.MentionCountRoot,
UrgentMentionCount: db.UrgentMentionCount,
NotifyProps: db.NotifyProps,
LastUpdateAt: db.LastUpdateAt,
SchemeAdmin: rolesResult.schemeAdmin,
SchemeUser: rolesResult.schemeUser,
SchemeGuest: rolesResult.schemeGuest,
ExplicitRoles: strings.Join(rolesResult.explicitRoles, " "),
},
TeamName: db.TeamName,
TeamDisplayName: db.TeamDisplayName,
@@ -471,7 +477,20 @@ func newSqlChannelStore(sqlStore *SqlStore, metrics einterfaces.MetricsInterface
func (s *SqlChannelStore) initializeQueries() {
s.channelMembersForTeamWithSchemeSelectQuery = s.getQueryBuilder().
Select(
"ChannelMembers.*",
"ChannelMembers.ChannelId",
"ChannelMembers.UserId",
"ChannelMembers.Roles",
"ChannelMembers.LastViewedAt",
"ChannelMembers.MsgCount",
"ChannelMembers.MentionCount",
"ChannelMembers.MentionCountRoot",
"COALESCE(ChannelMembers.UrgentMentionCount, 0) AS UrgentMentionCount",
"ChannelMembers.MsgCountRoot",
"ChannelMembers.NotifyProps",
"ChannelMembers.LastUpdateAt",
"ChannelMembers.SchemeUser",
"ChannelMembers.SchemeAdmin",
"ChannelMembers.SchemeGuest",
"TeamScheme.DefaultChannelGuestRole TeamSchemeDefaultGuestRole",
"TeamScheme.DefaultChannelUserRole TeamSchemeDefaultUserRole",
"TeamScheme.DefaultChannelAdminRole TeamSchemeDefaultAdminRole",
@@ -779,7 +798,7 @@ func (s SqlChannelStore) GetChannelUnread(channelId, userId string) (*model.Chan
var unreadChannel model.ChannelUnread
err := s.GetReplicaX().Get(&unreadChannel,
`SELECT
Channels.TeamId TeamId, Channels.Id ChannelId, (Channels.TotalMsgCount - ChannelMembers.MsgCount) MsgCount, (Channels.TotalMsgCountRoot - ChannelMembers.MsgCountRoot) MsgCountRoot, ChannelMembers.MentionCount MentionCount, ChannelMembers.MentionCountRoot MentionCountRoot, ChannelMembers.NotifyProps NotifyProps
Channels.TeamId TeamId, Channels.Id ChannelId, (Channels.TotalMsgCount - ChannelMembers.MsgCount) MsgCount, (Channels.TotalMsgCountRoot - ChannelMembers.MsgCountRoot) MsgCountRoot, ChannelMembers.MentionCount MentionCount, ChannelMembers.MentionCountRoot MentionCountRoot, COALESCE(ChannelMembers.UrgentMentionCount, 0) UrgentMentionCount, ChannelMembers.NotifyProps NotifyProps
FROM
Channels, ChannelMembers
WHERE
@@ -1612,7 +1631,20 @@ func (s SqlChannelStore) GetDeleted(teamId string, offset int, limit int, userId
var channelMembersWithSchemeSelectQuery = `
SELECT
ChannelMembers.*,
ChannelMembers.ChannelId,
ChannelMembers.UserId,
ChannelMembers.Roles,
ChannelMembers.LastViewedAt,
ChannelMembers.MsgCount,
ChannelMembers.MentionCount,
ChannelMembers.MentionCountRoot,
COALESCE(ChannelMembers.UrgentMentionCount, 0) AS UrgentMentionCount,
ChannelMembers.MsgCountRoot,
ChannelMembers.NotifyProps,
ChannelMembers.LastUpdateAt,
ChannelMembers.SchemeUser,
ChannelMembers.SchemeAdmin,
ChannelMembers.SchemeGuest,
COALESCE(Teams.DisplayName, '') TeamDisplayName,
COALESCE(Teams.Name, '') TeamName,
COALESCE(Teams.UpdateAt, 0) TeamUpdateAt,
@@ -2048,7 +2080,20 @@ func (s SqlChannelStore) GetMemberForPost(postId string, userId string) (*model.
var dbMember channelMemberWithSchemeRoles
query := `
SELECT
ChannelMembers.*,
ChannelMembers.ChannelId,
ChannelMembers.UserId,
ChannelMembers.Roles,
ChannelMembers.LastViewedAt,
ChannelMembers.MsgCount,
ChannelMembers.MentionCount,
ChannelMembers.MentionCountRoot,
COALESCE(ChannelMembers.UrgentMentionCount, 0) AS UrgentMentionCount,
ChannelMembers.MsgCountRoot,
ChannelMembers.NotifyProps,
ChannelMembers.LastUpdateAt,
ChannelMembers.SchemeUser,
ChannelMembers.SchemeAdmin,
ChannelMembers.SchemeGuest,
TeamScheme.DefaultChannelGuestRole TeamSchemeDefaultGuestRole,
TeamScheme.DefaultChannelUserRole TeamSchemeDefaultUserRole,
TeamScheme.DefaultChannelAdminRole TeamSchemeDefaultAdminRole,
@@ -2438,6 +2483,7 @@ func (s SqlChannelStore) UpdateLastViewedAt(channelIds []string, userId string)
Update("ChannelMembers cm").
Set("MentionCount", 0).
Set("MentionCountRoot", 0).
Set("UrgentMentionCount", 0).
Set("MsgCount", sq.Expr("greatest(cm.MsgCount, c.TotalMsgCount)")).
Set("MsgCountRoot", sq.Expr("greatest(cm.MsgCountRoot, c.TotalMsgCountRoot)")).
Set("LastViewedAt", sq.Expr("greatest(cm.LastViewedAt, c.LastPostAt)")).
@@ -2497,6 +2543,7 @@ func (s SqlChannelStore) UpdateLastViewedAt(channelIds []string, userId string)
updateQuery := s.getQueryBuilder().Update("ChannelMembers").
Set("MentionCount", 0).
Set("MentionCountRoot", 0).
Set("UrgentMentionCount", 0).
Set("MsgCount", msgCountQuery).
Set("MsgCountRoot", msgCountQueryRoot).
Set("LastViewedAt", lastViewedQuery).
@@ -2518,6 +2565,31 @@ func (s SqlChannelStore) UpdateLastViewedAt(channelIds []string, userId string)
return times, nil
}
func (s SqlChannelStore) CountUrgentPostsAfter(channelId string, timestamp int64, userId string) (int, error) {
query := s.getQueryBuilder().
Select("count(*)").
From("PostsPriority").
Join("Posts ON Posts.Id = PostsPriority.PostId").
Where(sq.And{
sq.Eq{"PostsPriority.Priority": model.PostPriorityUrgent},
sq.Eq{"Posts.ChannelId": channelId},
sq.Gt{"Posts.CreateAt": timestamp},
sq.Eq{"Posts.DeleteAt": 0},
})
if userId != "" {
query = query.Where(sq.Eq{"Posts.UserId": userId})
}
var urgent int64
err := s.GetReplicaX().GetBuilder(&urgent, query)
if err != nil {
return 0, errors.Wrap(err, "failed to count urgent Posts")
}
return int(urgent), nil
}
// CountPostsAfter returns the number of posts in the given channel created after but not including the given timestamp. If given a non-empty user ID, only counts posts made by that user.
func (s SqlChannelStore) CountPostsAfter(channelId string, timestamp int64, userId string) (int, int, error) {
joinLeavePostTypes := []string{
@@ -2566,13 +2638,14 @@ func (s SqlChannelStore) CountPostsAfter(channelId string, timestamp int64, user
if err != nil {
return 0, 0, errors.Wrap(err, "failed to count root Posts")
}
return int(unread), int(unreadRoot), nil
}
// UpdateLastViewedAtPost updates a ChannelMember as if the user last read the channel at the time of the given post.
// If the provided mentionCount is -1, the given post and all posts after it are considered to be mentions. Returns
// an updated model.ChannelUnreadAt that can be returned to the client.
func (s SqlChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount, mentionCountRoot int, setUnreadCountRoot bool) (*model.ChannelUnreadAt, error) {
func (s SqlChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount, mentionCountRoot, urgentMentionCount int, setUnreadCountRoot bool) (*model.ChannelUnreadAt, error) {
unreadDate := unreadPost.CreateAt - 1
unread, unreadRoot, err := s.CountPostsAfter(unreadPost.ChannelId, unreadDate, "")
@@ -2587,6 +2660,7 @@ func (s SqlChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID s
params := map[string]any{
"mentions": mentionCount,
"mentionsroot": mentionCountRoot,
"urgentmentions": urgentMentionCount,
"unreadcount": unread,
"unreadcountroot": unreadRoot,
"lastviewedat": unreadDate,
@@ -2603,6 +2677,7 @@ func (s SqlChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID s
SET
MentionCount = :mentions,
MentionCountRoot = :mentionsroot,
UrgentMentionCount = :urgentmentions,
MsgCount = (SELECT TotalMsgCount FROM Channels WHERE ID = :channelid) - :unreadcount,
MsgCountRoot = (SELECT TotalMsgCountRoot FROM Channels WHERE ID = :channelid) - :unreadcountroot,
LastViewedAt = :lastviewedat,
@@ -2625,6 +2700,7 @@ func (s SqlChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID s
cm.MsgCountRoot MsgCountRoot,
cm.MentionCount MentionCount,
cm.MentionCountRoot MentionCountRoot,
COALESCE(cm.UrgentMentionCount, 0) UrgentMentionCount,
cm.LastViewedAt LastViewedAt,
cm.NotifyProps NotifyProps
FROM
@@ -2643,7 +2719,7 @@ func (s SqlChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID s
return result, nil
}
func (s SqlChannelStore) IncrementMentionCount(channelId string, userIDs []string, isRoot bool) error {
func (s SqlChannelStore) IncrementMentionCount(channelId string, userIDs []string, isRoot bool, isUrgent bool) error {
now := model.GetMillis()
rootInc := 0
@@ -2651,10 +2727,16 @@ func (s SqlChannelStore) IncrementMentionCount(channelId string, userIDs []strin
rootInc = 1
}
urgentInc := 0
if isUrgent {
urgentInc = 1
}
sql, args, err := s.getQueryBuilder().
Update("ChannelMembers").
Set("MentionCount", sq.Expr("MentionCount + 1")).
Set("MentionCountRoot", sq.Expr("MentionCountRoot + ?", rootInc)).
Set("UrgentMentionCount", sq.Expr("UrgentMentionCount + ?", urgentInc)).
Set("LastUpdateAt", now).
Where(sq.Eq{
"UserId": userIDs,
@@ -2832,7 +2914,21 @@ func (s SqlChannelStore) GetMembersForUser(teamID string, userID string) (model.
func (s SqlChannelStore) GetMembersForUserWithCursor(userID, teamID string, opts *store.ChannelMemberGraphQLSearchOpts) (model.ChannelMembers, error) {
query := s.getQueryBuilder().
Select("ChannelMembers.*",
Select(
"ChannelMembers.ChannelId",
"ChannelMembers.UserId",
"ChannelMembers.Roles",
"ChannelMembers.LastViewedAt",
"ChannelMembers.MsgCount",
"ChannelMembers.MentionCount",
"ChannelMembers.MentionCountRoot",
"COALESCE(ChannelMembers.UrgentMentionCount, 0) AS UrgentMentionCount",
"ChannelMembers.MsgCountRoot",
"ChannelMembers.NotifyProps",
"ChannelMembers.LastUpdateAt",
"ChannelMembers.SchemeUser",
"ChannelMembers.SchemeAdmin",
"ChannelMembers.SchemeGuest",
"TeamScheme.DefaultChannelGuestRole TeamSchemeDefaultGuestRole",
"TeamScheme.DefaultChannelUserRole TeamSchemeDefaultUserRole",
"TeamScheme.DefaultChannelAdminRole TeamSchemeDefaultAdminRole",
@@ -3790,6 +3886,7 @@ func (s SqlChannelStore) MigrateChannelMembers(fromChannelId string, fromUserId
LastViewedAt=:LastViewedAt,
MsgCount=:MsgCount,
MentionCount=:MentionCount,
UrgentMentionCount=:UrgentMentionCount,
NotifyProps=:NotifyProps,
LastUpdateAt=:LastUpdateAt,
SchemeUser=:SchemeUser,
@@ -3932,6 +4029,7 @@ func (s SqlChannelStore) GetChannelMembersForExport(userId string, teamId string
ChannelMembers.MsgCount,
ChannelMembers.MentionCount,
ChannelMembers.MentionCountRoot,
COALESCE(ChannelMembers.UrgentMentionCount, 0) AS UrgentMentionCount,
ChannelMembers.NotifyProps,
ChannelMembers.LastUpdateAt,
ChannelMembers.SchemeUser,
@@ -3981,7 +4079,7 @@ func (s SqlChannelStore) GetAllDirectChannelsForExportAfter(limit int, afterId s
channelIds = append(channelIds, channel.Id)
}
query = s.getQueryBuilder().
Select("u.Username as Username, ChannelId, UserId, cm.Roles as Roles, LastViewedAt, MsgCount, MentionCount, MentionCountRoot, cm.NotifyProps as NotifyProps, LastUpdateAt, SchemeUser, SchemeAdmin, (SchemeGuest IS NOT NULL AND SchemeGuest) as SchemeGuest").
Select("u.Username as Username, ChannelId, UserId, cm.Roles as Roles, LastViewedAt, MsgCount, MentionCount, MentionCountRoot, COALESCE(UrgentMentionCount, 0) UrgentMentionCount, cm.NotifyProps as NotifyProps, LastUpdateAt, SchemeUser, SchemeAdmin, (SchemeGuest IS NOT NULL AND SchemeGuest) as SchemeGuest").
From("ChannelMembers cm").
Join("Users u ON ( u.Id = cm.UserId )").
Where(sq.And{

63
store/sqlstore/post_priority_store.go Обычный файл
Просмотреть файл

@@ -0,0 +1,63 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package sqlstore
import (
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/store"
sq "github.com/mattermost/squirrel"
)
type SqlPostPriorityStore struct {
*SqlStore
}
func newSqlPostPriorityStore(sqlStore *SqlStore) store.PostPriorityStore {
return &SqlPostPriorityStore{
SqlStore: sqlStore,
}
}
func (s *SqlPostPriorityStore) GetForPost(postId string) (*model.PostPriority, error) {
query := s.getQueryBuilder().
Select("Priority", "RequestedAck", "PersistentNotifications").
From("PostsPriority").
Where(sq.Eq{"PostId": postId})
var postPriority model.PostPriority
err := s.GetReplicaX().GetBuilder(&postPriority, query)
if err != nil {
return nil, err
}
return &postPriority, nil
}
func (s *SqlPostPriorityStore) GetForPosts(postIds []string) ([]*model.PostPriority, error) {
var priority []*model.PostPriority
perPage := 200
for i := 0; i < len(postIds); i += perPage {
j := i + perPage
if len(postIds) < j {
j = len(postIds)
}
query := s.getQueryBuilder().
Select("PostId", "Priority", "RequestedAck", "PersistentNotifications").
From("PostsPriority").
Where(sq.Eq{"PostId": postIds[i:j]})
var priorityBatch []*model.PostPriority
err := s.GetReplicaX().SelectBuilder(&priority, query)
if err != nil {
return nil, err
}
priority = append(priority, priorityBatch...)
}
return priority, nil
}

14
store/sqlstore/post_priority_store_test.go Обычный файл
Просмотреть файл

@@ -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/v6/store/storetest"
)
func TestPostPriorityStore(t *testing.T) {
StoreTestWithSqlStore(t, storetest.TestPostPriorityStore)
}

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

@@ -219,6 +219,10 @@ func (s *SqlPostStore) SaveMultiple(posts []*model.Post) ([]*model.Post, int, er
return nil, -1, errors.Wrap(err, "update thread from posts failed")
}
if err = s.savePostsPriority(transaction, posts); err != nil {
return nil, -1, errors.Wrap(err, "failed to save PostPriority")
}
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")
@@ -2920,6 +2924,24 @@ func (s *SqlPostStore) updateThreadAfterReplyDeletion(transaction *sqlxTxWrapper
return nil
}
func (s *SqlPostStore) savePostsPriority(transaction *sqlxTxWrapper, posts []*model.Post) error {
for _, post := range posts {
if post.GetPriority() != nil {
postPriority := &model.PostPriority{
PostId: post.Id,
ChannelId: post.ChannelId,
Priority: post.Metadata.Priority.Priority,
RequestedAck: post.Metadata.Priority.RequestedAck,
PersistentNotifications: post.Metadata.Priority.PersistentNotifications,
}
if _, err := transaction.NamedExec(`INSERT INTO PostsPriority (PostId, ChannelId, Priority, RequestedAck, PersistentNotifications) VALUES (:PostId, :ChannelId, :Priority, :RequestedAck, :PersistentNotifications)`, postPriority); err != nil {
return err
}
}
}
return nil
}
func (s *SqlPostStore) updateThreadsFromPosts(transaction *sqlxTxWrapper, posts []*model.Post) error {
postsByRoot := map[string][]*model.Post{}
var rootIds []string

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

@@ -109,6 +109,7 @@ type SqlStoreStores struct {
linkMetadata store.LinkMetadataStore
sharedchannel store.SharedChannelStore
notifyAdmin store.NotifyAdminStore
postPriority store.PostPriorityStore
}
type SqlStore struct {
@@ -214,6 +215,7 @@ func New(settings model.SqlSettings, metrics einterfaces.MetricsInterface) *SqlS
store.stores.group = newSqlGroupStore(store)
store.stores.productNotices = newSqlProductNoticesStore(store)
store.stores.notifyAdmin = newSqlNotifyAdminStore(store)
store.stores.postPriority = newSqlPostPriorityStore(store)
store.stores.preference.(*SqlPreferenceStore).deleteUnusedFeatures()
@@ -955,6 +957,10 @@ func (ss *SqlStore) SharedChannel() store.SharedChannelStore {
return ss.stores.sharedchannel
}
func (ss *SqlStore) PostPriority() store.PostPriorityStore {
return ss.stores.postPriority
}
func (ss *SqlStore) DropAllTables() {
if ss.DriverName() == model.DatabaseDriverPostgres {
ss.masterX.Exec(`DO

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

@@ -7,11 +7,11 @@ import (
"context"
"database/sql"
"strconv"
"sync"
"time"
sq "github.com/mattermost/squirrel"
"github.com/pkg/errors"
"golang.org/x/sync/errgroup"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/store"
@@ -30,6 +30,7 @@ type JoinedThread struct {
Participants model.StringArray
ThreadDeleteAt int64
TeamId string
IsUrgent bool
model.Post
}
@@ -51,6 +52,7 @@ func (thread *JoinedThread) toThreadResponse(users map[string]*model.User) *mode
Participants: threadParticipants,
Post: thread.Post.ToNilIfInvalid(),
DeleteAt: thread.ThreadDeleteAt,
IsUrgent: thread.IsUrgent,
}
}
@@ -213,6 +215,46 @@ func (s *SqlThreadStore) GetTotalUnreadMentions(userId, teamId string, opts mode
return totalUnreadMentions, nil
}
// GetTotalUnreadUrgentMentions counts the number of unread mentions for the given user, optionally
// constrained to the given team + DMs/GMs.
func (s *SqlThreadStore) GetTotalUnreadUrgentMentions(userId, teamId string, opts model.GetUserThreadsOpts) (int64, error) {
var totalUnreadUrgentMentions int64
query := s.getQueryBuilder().
Select("COALESCE(SUM(ThreadMemberships.UnreadMentions),0)").
From("ThreadMemberships").
Join("PostsPriority ON PostsPriority.PostId = ThreadMemberships.PostId").
Where(sq.Eq{
"ThreadMemberships.UserId": userId,
"ThreadMemberships.Following": true,
"PostsPriority.Priority": model.PostPriorityUrgent,
})
if teamId != "" || !opts.Deleted {
query = query.Join("Threads ON Threads.PostId = ThreadMemberships.PostId")
}
if teamId != "" {
query = query.
Where(sq.Or{
sq.Eq{"Threads.ThreadTeamId": teamId},
sq.Eq{"Threads.ThreadTeamId": ""},
})
}
if !opts.Deleted {
query = query.
Where(sq.Eq{"COALESCE(Threads.ThreadDeleteAt, 0)": 0})
}
err := s.GetReplicaX().GetBuilder(&totalUnreadUrgentMentions, query)
if err != nil {
return 0, errors.Wrapf(err, "failed to count unread urgent mentions for user id=%s", userId)
}
return totalUnreadUrgentMentions, nil
}
func (s *SqlThreadStore) GetThreadsForUser(userId, teamId string, opts model.GetUserThreadsOpts) ([]*model.ThreadResponse, error) {
pageSize := uint64(30)
if opts.PageSize != 0 {
@@ -243,6 +285,17 @@ func (s *SqlThreadStore) GetThreadsForUser(userId, teamId string, opts model.Get
Where(sq.Eq{"ThreadMemberships.UserId": userId}).
Where(sq.Eq{"ThreadMemberships.Following": true})
if opts.IncludeIsUrgent {
urgencyCase := sq.
Case().
When(sq.Eq{"PostsPriority.Priority": model.PostPriorityUrgent}, "true").
Else("false")
query = query.
Column(sq.Alias(urgencyCase, "IsUrgent")).
LeftJoin("PostsPriority ON PostsPriority.PostId = Threads.PostId")
}
// If a team is specified, constrain to channels in that team or DMs/GMs without
// a team at all.
if teamId != "" {
@@ -322,7 +375,7 @@ func (s *SqlThreadStore) GetThreadsForUser(userId, teamId string, opts model.Get
// GetTeamsUnreadForUser returns the total unread threads and unread mentions
// for a user from all teams.
func (s *SqlThreadStore) GetTeamsUnreadForUser(userID string, teamIDs []string) (map[string]*model.TeamUnread, error) {
func (s *SqlThreadStore) GetTeamsUnreadForUser(userID string, teamIDs []string, includeUrgentMentionCount bool) (map[string]*model.TeamUnread, error) {
fetchConditions := sq.And{
sq.Eq{"ThreadMemberships.UserId": userID},
sq.Eq{"ThreadMemberships.Following": true},
@@ -330,8 +383,7 @@ func (s *SqlThreadStore) GetTeamsUnreadForUser(userID string, teamIDs []string)
sq.Eq{"COALESCE(Threads.ThreadDeleteAt, 0)": 0},
}
var wg sync.WaitGroup
var err1, err2 error
var eg errgroup.Group
unreadThreads := []struct {
Count int64
@@ -341,13 +393,15 @@ func (s *SqlThreadStore) GetTeamsUnreadForUser(userID string, teamIDs []string)
Count int64
TeamId string
}{}
unreadUrgentMentions := []struct {
Count int64
TeamId string
}{}
// Running these concurrently hasn't shown any major downside
// than running them serially. So using a bit of perf boost.
// In any case, they will be replaced by computed columns later.
wg.Add(1)
go func() {
defer wg.Done()
eg.Go(func() error {
repliesQuery := s.getQueryBuilder().
Select("COUNT(Threads.PostId) AS Count, ThreadTeamId AS TeamId").
From("Threads").
@@ -356,15 +410,10 @@ func (s *SqlThreadStore) GetTeamsUnreadForUser(userID string, teamIDs []string)
Where("Threads.LastReplyAt > ThreadMemberships.LastViewed").
GroupBy("Threads.ThreadTeamId")
err := s.GetReplicaX().SelectBuilder(&unreadThreads, repliesQuery)
if err != nil {
err1 = errors.Wrap(err, "failed to get total unread threads")
}
}()
return errors.Wrap(s.GetReplicaX().SelectBuilder(&unreadThreads, repliesQuery), "failed to get total unread threads")
})
wg.Add(1)
go func() {
defer wg.Done()
eg.Go(func() error {
mentionsQuery := s.getQueryBuilder().
Select("COALESCE(SUM(ThreadMemberships.UnreadMentions),0) AS Count, ThreadTeamId AS TeamId").
From("ThreadMemberships").
@@ -372,20 +421,27 @@ func (s *SqlThreadStore) GetTeamsUnreadForUser(userID string, teamIDs []string)
Where(fetchConditions).
GroupBy("Threads.ThreadTeamId")
err := s.GetReplicaX().SelectBuilder(&unreadMentions, mentionsQuery)
if err != nil {
err2 = errors.Wrap(err, "failed to get total unread mentions")
}
}()
return errors.Wrap(s.GetReplicaX().SelectBuilder(&unreadMentions, mentionsQuery), "failed to get total unread mentions")
})
if includeUrgentMentionCount {
eg.Go(func() error {
urgentMentionsQuery := s.getQueryBuilder().
Select("COALESCE(SUM(ThreadMemberships.UnreadMentions),0) AS Count, ThreadTeamId AS TeamId").
From("ThreadMemberships").
LeftJoin("Threads ON Threads.PostId = ThreadMemberships.PostId").
Join("PostsPriority ON PostsPriority.PostId = ThreadMemberships.PostId").
Where(sq.Eq{"PostsPriority.Priority": model.PostPriorityUrgent}).
Where(fetchConditions).
GroupBy("Threads.ThreadTeamId")
return errors.Wrap(s.GetReplicaX().SelectBuilder(&unreadUrgentMentions, urgentMentionsQuery), "failed to get total unread urgent mentions")
})
}
// Wait for them to be over
wg.Wait()
if err1 != nil {
return nil, err1
}
if err2 != nil {
return nil, err2
if err := eg.Wait(); err != nil {
return nil, err
}
res := make(map[string]*model.TeamUnread)
@@ -405,6 +461,15 @@ func (s *SqlThreadStore) GetTeamsUnreadForUser(userID string, teamIDs []string)
}
}
}
for _, item := range unreadUrgentMentions {
if _, ok := res[item.TeamId]; ok {
res[item.TeamId].ThreadUrgentMentionCount = item.Count
} else {
res[item.TeamId] = &model.TeamUnread{
ThreadUrgentMentionCount: item.Count,
}
}
}
return res, nil
}
@@ -436,7 +501,7 @@ func (s *SqlThreadStore) GetThreadFollowers(threadID string, fetchOnlyActive boo
return users, nil
}
func (s *SqlThreadStore) GetThreadForUser(threadMembership *model.ThreadMembership, extended bool) (*model.ThreadResponse, error) {
func (s *SqlThreadStore) GetThreadForUser(threadMembership *model.ThreadMembership, extended, postPriorityEnabled bool) (*model.ThreadResponse, error) {
if !threadMembership.Following {
return nil, nil // in case the thread is not followed anymore - return nil error to be interpreted as 404
}
@@ -462,6 +527,17 @@ func (s *SqlThreadStore) GetThreadForUser(threadMembership *model.ThreadMembersh
LeftJoin("Posts ON Posts.Id = Threads.PostId").
Where(sq.Eq{"Threads.PostId": threadMembership.PostId})
if postPriorityEnabled {
urgencyCase := sq.
Case().
When(sq.Eq{"PostsPriority.Priority": model.PostPriorityUrgent}, "true").
Else("false")
query = query.
Column(sq.Alias(urgencyCase, "IsUrgent")).
LeftJoin("PostsPriority ON PostsPriority.PostId = Threads.PostId")
}
err := s.GetReplicaX().GetBuilder(&thread, query)
if err != nil {
if err == sql.ErrNoRows {

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

@@ -84,6 +84,7 @@ type Store interface {
SetContext(context context.Context)
Context() context.Context
NotifyAdmin() NotifyAdminStore
PostPriority() PostPriorityStore
}
type RetentionPolicyStore interface {
@@ -240,9 +241,10 @@ type ChannelStore interface {
PermanentDeleteMembersByUser(userID string) error
PermanentDeleteMembersByChannel(channelID string) error
UpdateLastViewedAt(channelIds []string, userID string) (map[string]int64, error)
UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount, mentionCountRoot int, setUnreadCountRoot bool) (*model.ChannelUnreadAt, error)
UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount, mentionCountRoot, urgentMentionCount int, setUnreadCountRoot bool) (*model.ChannelUnreadAt, error)
CountPostsAfter(channelID string, timestamp int64, userID string) (int, int, error)
IncrementMentionCount(channelID string, userIDs []string, isRoot bool) error
CountUrgentPostsAfter(channelID string, timestamp int64, userID string) (int, error)
IncrementMentionCount(channelID string, userIDs []string, isRoot, isUrgent bool) error
AnalyticsTypeCount(teamID string, channelType model.ChannelType) (int64, error)
GetMembersForUser(teamID string, userID string) (model.ChannelMembers, error)
GetTeamMembersForChannel(channelID string) ([]string, error)
@@ -322,9 +324,10 @@ type ThreadStore interface {
GetTotalUnreadThreads(userId, teamID string, opts model.GetUserThreadsOpts) (int64, error)
GetTotalThreads(userId, teamID string, opts model.GetUserThreadsOpts) (int64, error)
GetTotalUnreadMentions(userId, teamID string, opts model.GetUserThreadsOpts) (int64, error)
GetTotalUnreadUrgentMentions(userId, teamID string, opts model.GetUserThreadsOpts) (int64, error)
GetThreadsForUser(userId, teamID string, opts model.GetUserThreadsOpts) ([]*model.ThreadResponse, error)
GetThreadForUser(threadMembership *model.ThreadMembership, extended bool) (*model.ThreadResponse, error)
GetTeamsUnreadForUser(userID string, teamIDs []string) (map[string]*model.TeamUnread, error)
GetThreadForUser(threadMembership *model.ThreadMembership, extended, postPriorityIsEnabled bool) (*model.ThreadResponse, error)
GetTeamsUnreadForUser(userID string, teamIDs []string, includeUrgentMentionCount bool) (map[string]*model.TeamUnread, error)
MarkAllAsRead(userID string, threadIds []string) error
MarkAllAsReadByTeam(userID, teamID string) error
@@ -970,6 +973,11 @@ type SharedChannelStore interface {
UpdateAttachmentLastSyncAt(id string, syncTime int64) error
}
type PostPriorityStore interface {
GetForPost(postId string) (*model.PostPriority, error)
GetForPosts(ids []string) ([]*model.PostPriority, error)
}
// ChannelSearchOpts contains options for searching channels.
//
// NotAssociatedToGroup will exclude channels that have associated, active GroupChannels records.

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

@@ -104,6 +104,7 @@ func TestChannelStore(t *testing.T, ss store.Store, s SqlStore) {
t.Run("GetMembersForUserWithCursor", func(t *testing.T) { testChannelStoreGetMembersForUserWithCursor(t, ss) })
t.Run("GetMembersForUserWithPagination", func(t *testing.T) { testChannelStoreGetMembersForUserWithPagination(t, ss) })
t.Run("CountPostsAfter", func(t *testing.T) { testCountPostsAfter(t, ss) })
t.Run("CountUrgentPostsAfter", func(t *testing.T) { testCountUrgentPostsAfter(t, ss) })
t.Run("UpdateLastViewedAt", func(t *testing.T) { testChannelStoreUpdateLastViewedAt(t, ss) })
t.Run("IncrementMentionCount", func(t *testing.T) { testChannelStoreIncrementMentionCount(t, ss) })
t.Run("UpdateChannelMember", func(t *testing.T) { testUpdateChannelMember(t, ss) })
@@ -4833,6 +4834,66 @@ func testCountPostsAfter(t *testing.T, ss store.Store) {
})
}
func testCountUrgentPostsAfter(t *testing.T, ss store.Store) {
t.Run("should count all posts with or without the given user ID", func(t *testing.T) {
userId1 := model.NewId()
userId2 := model.NewId()
channelId := model.NewId()
p1, err := ss.Post().Save(&model.Post{
UserId: userId1,
ChannelId: channelId,
CreateAt: 1000,
Metadata: &model.PostMetadata{
Priority: &model.PostPriority{
Priority: model.NewString(model.PostPriorityUrgent),
RequestedAck: model.NewBool(false),
PersistentNotifications: model.NewBool(false),
},
},
})
require.NoError(t, err)
_, err = ss.Post().Save(&model.Post{
UserId: userId1,
ChannelId: channelId,
CreateAt: 1001,
Metadata: &model.PostMetadata{
Priority: &model.PostPriority{
Priority: model.NewString("important"),
RequestedAck: model.NewBool(false),
PersistentNotifications: model.NewBool(false),
},
},
})
require.NoError(t, err)
_, err = ss.Post().Save(&model.Post{
UserId: userId2,
ChannelId: channelId,
CreateAt: 1002,
})
require.NoError(t, err)
count, err := ss.Channel().CountUrgentPostsAfter(channelId, p1.CreateAt-1, "")
require.NoError(t, err)
assert.Equal(t, 1, count)
count, err = ss.Channel().CountUrgentPostsAfter(channelId, p1.CreateAt, "")
require.NoError(t, err)
assert.Equal(t, 0, count)
count, err = ss.Channel().CountUrgentPostsAfter(channelId, p1.CreateAt-1, userId1)
require.NoError(t, err)
assert.Equal(t, 1, count)
count, err = ss.Channel().CountUrgentPostsAfter(channelId, p1.CreateAt, userId1)
require.NoError(t, err)
assert.Equal(t, 0, count)
})
}
func testChannelStoreUpdateLastViewedAt(t *testing.T, ss store.Store) {
o1 := model.Channel{}
o1.TeamId = model.NewId()
@@ -4912,16 +4973,16 @@ func testChannelStoreIncrementMentionCount(t *testing.T, ss store.Store) {
_, err := ss.Channel().SaveMember(&m1)
require.NoError(t, err)
err = ss.Channel().IncrementMentionCount(m1.ChannelId, []string{m1.UserId}, false)
err = ss.Channel().IncrementMentionCount(m1.ChannelId, []string{m1.UserId}, false, false)
require.NoError(t, err, "failed to update")
err = ss.Channel().IncrementMentionCount(m1.ChannelId, []string{"missing id"}, false)
err = ss.Channel().IncrementMentionCount(m1.ChannelId, []string{"missing id"}, false, false)
require.NoError(t, err, "failed to update")
err = ss.Channel().IncrementMentionCount("missing id", []string{m1.UserId}, false)
err = ss.Channel().IncrementMentionCount("missing id", []string{m1.UserId}, false, false)
require.NoError(t, err, "failed to update")
err = ss.Channel().IncrementMentionCount("missing id", []string{"missing id"}, false)
err = ss.Channel().IncrementMentionCount("missing id", []string{"missing id"}, false, false)
require.NoError(t, err, "failed to update")
}

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

@@ -192,6 +192,27 @@ func (_m *ChannelStore) CountPostsAfter(channelID string, timestamp int64, userI
return r0, r1, r2
}
// CountUrgentPostsAfter provides a mock function with given fields: channelID, timestamp, userID
func (_m *ChannelStore) CountUrgentPostsAfter(channelID string, timestamp int64, userID string) (int, error) {
ret := _m.Called(channelID, timestamp, userID)
var r0 int
if rf, ok := ret.Get(0).(func(string, int64, string) int); ok {
r0 = rf(channelID, timestamp, userID)
} else {
r0 = ret.Get(0).(int)
}
var r1 error
if rf, ok := ret.Get(1).(func(string, int64, string) error); ok {
r1 = rf(channelID, timestamp, userID)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// CreateDirectChannel provides a mock function with given fields: userID, otherUserID, channelOptions
func (_m *ChannelStore) CreateDirectChannel(userID *model.User, otherUserID *model.User, channelOptions ...model.ChannelOption) (*model.Channel, error) {
_va := make([]interface{}, len(channelOptions))
@@ -1646,13 +1667,13 @@ func (_m *ChannelStore) GroupSyncedChannelCount() (int64, error) {
return r0, r1
}
// IncrementMentionCount provides a mock function with given fields: channelID, userIDs, isRoot
func (_m *ChannelStore) IncrementMentionCount(channelID string, userIDs []string, isRoot bool) error {
ret := _m.Called(channelID, userIDs, isRoot)
// IncrementMentionCount provides a mock function with given fields: channelID, userIDs, isRoot, isUrgent
func (_m *ChannelStore) IncrementMentionCount(channelID string, userIDs []string, isRoot bool, isUrgent bool) error {
ret := _m.Called(channelID, userIDs, isRoot, isUrgent)
var r0 error
if rf, ok := ret.Get(0).(func(string, []string, bool) error); ok {
r0 = rf(channelID, userIDs, isRoot)
if rf, ok := ret.Get(0).(func(string, []string, bool, bool) error); ok {
r0 = rf(channelID, userIDs, isRoot, isUrgent)
} else {
r0 = ret.Error(0)
}
@@ -2192,13 +2213,13 @@ func (_m *ChannelStore) UpdateLastViewedAt(channelIds []string, userID string) (
return r0, r1
}
// UpdateLastViewedAtPost provides a mock function with given fields: unreadPost, userID, mentionCount, mentionCountRoot, setUnreadCountRoot
func (_m *ChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount int, mentionCountRoot int, setUnreadCountRoot bool) (*model.ChannelUnreadAt, error) {
ret := _m.Called(unreadPost, userID, mentionCount, mentionCountRoot, setUnreadCountRoot)
// UpdateLastViewedAtPost provides a mock function with given fields: unreadPost, userID, mentionCount, mentionCountRoot, urgentMentionCount, setUnreadCountRoot
func (_m *ChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount int, mentionCountRoot int, urgentMentionCount int, setUnreadCountRoot bool) (*model.ChannelUnreadAt, error) {
ret := _m.Called(unreadPost, userID, mentionCount, mentionCountRoot, urgentMentionCount, setUnreadCountRoot)
var r0 *model.ChannelUnreadAt
if rf, ok := ret.Get(0).(func(*model.Post, string, int, int, bool) *model.ChannelUnreadAt); ok {
r0 = rf(unreadPost, userID, mentionCount, mentionCountRoot, setUnreadCountRoot)
if rf, ok := ret.Get(0).(func(*model.Post, string, int, int, int, bool) *model.ChannelUnreadAt); ok {
r0 = rf(unreadPost, userID, mentionCount, mentionCountRoot, urgentMentionCount, setUnreadCountRoot)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.ChannelUnreadAt)
@@ -2206,8 +2227,8 @@ func (_m *ChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID st
}
var r1 error
if rf, ok := ret.Get(1).(func(*model.Post, string, int, int, bool) error); ok {
r1 = rf(unreadPost, userID, mentionCount, mentionCountRoot, setUnreadCountRoot)
if rf, ok := ret.Get(1).(func(*model.Post, string, int, int, int, bool) error); ok {
r1 = rf(unreadPost, userID, mentionCount, mentionCountRoot, urgentMentionCount, setUnreadCountRoot)
} else {
r1 = ret.Error(1)
}

61
store/storetest/mocks/PostPriorityStore.go Обычный файл
Просмотреть файл

@@ -0,0 +1,61 @@
// Code generated by mockery v2.10.4. DO NOT EDIT.
// Regenerate this file using `make store-mocks`.
package mocks
import (
model "github.com/mattermost/mattermost-server/v6/model"
mock "github.com/stretchr/testify/mock"
)
// PostPriorityStore is an autogenerated mock type for the PostPriorityStore type
type PostPriorityStore struct {
mock.Mock
}
// GetForPost provides a mock function with given fields: postId
func (_m *PostPriorityStore) GetForPost(postId string) (*model.PostPriority, error) {
ret := _m.Called(postId)
var r0 *model.PostPriority
if rf, ok := ret.Get(0).(func(string) *model.PostPriority); ok {
r0 = rf(postId)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.PostPriority)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(string) error); ok {
r1 = rf(postId)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// GetForPosts provides a mock function with given fields: ids
func (_m *PostPriorityStore) GetForPosts(ids []string) ([]*model.PostPriority, error) {
ret := _m.Called(ids)
var r0 []*model.PostPriority
if rf, ok := ret.Get(0).(func([]string) []*model.PostPriority); ok {
r0 = rf(ids)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.PostPriority)
}
}
var r1 error
if rf, ok := ret.Get(1).(func([]string) error); ok {
r1 = rf(ids)
} else {
r1 = ret.Error(1)
}
return r0, r1
}

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

@@ -475,6 +475,22 @@ func (_m *Store) Post() store.PostStore {
return r0
}
// PostPriority provides a mock function with given fields:
func (_m *Store) PostPriority() store.PostPriorityStore {
ret := _m.Called()
var r0 store.PostPriorityStore
if rf, ok := ret.Get(0).(func() store.PostPriorityStore); ok {
r0 = rf()
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(store.PostPriorityStore)
}
}
return r0
}
// Preference provides a mock function with given fields:
func (_m *Store) Preference() store.PreferenceStore {
ret := _m.Called()

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

@@ -119,13 +119,13 @@ func (_m *ThreadStore) GetMembershipsForUser(userId string, teamID string) ([]*m
return r0, r1
}
// GetTeamsUnreadForUser provides a mock function with given fields: userID, teamIDs
func (_m *ThreadStore) GetTeamsUnreadForUser(userID string, teamIDs []string) (map[string]*model.TeamUnread, error) {
ret := _m.Called(userID, teamIDs)
// GetTeamsUnreadForUser provides a mock function with given fields: userID, teamIDs, includeUrgentMentionCount
func (_m *ThreadStore) GetTeamsUnreadForUser(userID string, teamIDs []string, includeUrgentMentionCount bool) (map[string]*model.TeamUnread, error) {
ret := _m.Called(userID, teamIDs, includeUrgentMentionCount)
var r0 map[string]*model.TeamUnread
if rf, ok := ret.Get(0).(func(string, []string) map[string]*model.TeamUnread); ok {
r0 = rf(userID, teamIDs)
if rf, ok := ret.Get(0).(func(string, []string, bool) map[string]*model.TeamUnread); ok {
r0 = rf(userID, teamIDs, includeUrgentMentionCount)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(map[string]*model.TeamUnread)
@@ -133,8 +133,8 @@ func (_m *ThreadStore) GetTeamsUnreadForUser(userID string, teamIDs []string) (m
}
var r1 error
if rf, ok := ret.Get(1).(func(string, []string) error); ok {
r1 = rf(userID, teamIDs)
if rf, ok := ret.Get(1).(func(string, []string, bool) error); ok {
r1 = rf(userID, teamIDs, includeUrgentMentionCount)
} else {
r1 = ret.Error(1)
}
@@ -165,13 +165,13 @@ func (_m *ThreadStore) GetThreadFollowers(threadID string, fetchOnlyActive bool)
return r0, r1
}
// GetThreadForUser provides a mock function with given fields: threadMembership, extended
func (_m *ThreadStore) GetThreadForUser(threadMembership *model.ThreadMembership, extended bool) (*model.ThreadResponse, error) {
ret := _m.Called(threadMembership, extended)
// GetThreadForUser provides a mock function with given fields: threadMembership, extended, postPriorityIsEnabled
func (_m *ThreadStore) GetThreadForUser(threadMembership *model.ThreadMembership, extended bool, postPriorityIsEnabled bool) (*model.ThreadResponse, error) {
ret := _m.Called(threadMembership, extended, postPriorityIsEnabled)
var r0 *model.ThreadResponse
if rf, ok := ret.Get(0).(func(*model.ThreadMembership, bool) *model.ThreadResponse); ok {
r0 = rf(threadMembership, extended)
if rf, ok := ret.Get(0).(func(*model.ThreadMembership, bool, bool) *model.ThreadResponse); ok {
r0 = rf(threadMembership, extended, postPriorityIsEnabled)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*model.ThreadResponse)
@@ -179,8 +179,8 @@ func (_m *ThreadStore) GetThreadForUser(threadMembership *model.ThreadMembership
}
var r1 error
if rf, ok := ret.Get(1).(func(*model.ThreadMembership, bool) error); ok {
r1 = rf(threadMembership, extended)
if rf, ok := ret.Get(1).(func(*model.ThreadMembership, bool, bool) error); ok {
r1 = rf(threadMembership, extended, postPriorityIsEnabled)
} else {
r1 = ret.Error(1)
}
@@ -341,6 +341,27 @@ func (_m *ThreadStore) GetTotalUnreadThreads(userId string, teamID string, opts
return r0, r1
}
// GetTotalUnreadUrgentMentions provides a mock function with given fields: userId, teamID, opts
func (_m *ThreadStore) GetTotalUnreadUrgentMentions(userId string, teamID string, opts model.GetUserThreadsOpts) (int64, error) {
ret := _m.Called(userId, teamID, opts)
var r0 int64
if rf, ok := ret.Get(0).(func(string, string, model.GetUserThreadsOpts) int64); ok {
r0 = rf(userId, teamID, opts)
} else {
r0 = ret.Get(0).(int64)
}
var r1 error
if rf, ok := ret.Get(1).(func(string, string, model.GetUserThreadsOpts) error); ok {
r1 = rf(userId, teamID, opts)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// MaintainMembership provides a mock function with given fields: userID, postID, opts
func (_m *ThreadStore) MaintainMembership(userID string, postID string, opts store.ThreadMembershipOpts) (*model.ThreadMembership, error) {
ret := _m.Called(userID, postID, opts)

72
store/storetest/post_priority_store.go Обычный файл
Просмотреть файл

@@ -0,0 +1,72 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package storetest
import (
"database/sql"
"errors"
"testing"
"github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/store"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestPostPriorityStore(t *testing.T, ss store.Store, s SqlStore) {
t.Run("GetForPost", func(t *testing.T) { testPostPriorityStoreGetForPost(t, ss) })
}
func testPostPriorityStoreGetForPost(t *testing.T, ss store.Store) {
t.Run("Save post priority when in post's metadata", func(t *testing.T) {
p1 := model.Post{}
p1.ChannelId = model.NewId()
p1.UserId = model.NewId()
p1.Message = NewTestId()
p1.Metadata = &model.PostMetadata{
Priority: &model.PostPriority{
Priority: model.NewString("important"),
RequestedAck: model.NewBool(true),
PersistentNotifications: model.NewBool(false),
},
}
p2 := model.Post{}
p2.ChannelId = model.NewId()
p2.UserId = model.NewId()
p2.Message = NewTestId()
p2.Metadata = &model.PostMetadata{
Priority: &model.PostPriority{
Priority: model.NewString(model.PostPriorityUrgent),
RequestedAck: model.NewBool(false),
PersistentNotifications: model.NewBool(true),
},
}
p3 := model.Post{}
p3.ChannelId = model.NewId()
p3.UserId = model.NewId()
p3.Message = NewTestId()
_, errIdx, err := ss.Post().SaveMultiple([]*model.Post{&p1, &p2, &p3})
require.NoError(t, err)
require.Equal(t, -1, errIdx)
pp1, err := ss.PostPriority().GetForPost(p1.Id)
require.NoError(t, err)
assert.Equal(t, "important", *pp1.Priority)
assert.Equal(t, true, *pp1.RequestedAck)
assert.Equal(t, false, *pp1.PersistentNotifications)
pp2, err := ss.PostPriority().GetForPost(p2.Id)
require.NoError(t, err)
assert.Equal(t, model.PostPriorityUrgent, *pp2.Priority)
assert.Equal(t, false, *pp2.RequestedAck)
assert.Equal(t, true, *pp2.PersistentNotifications)
_, err = ss.PostPriority().GetForPost(p3.Id)
assert.True(t, errors.Is(err, sql.ErrNoRows))
})
}

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

@@ -238,6 +238,31 @@ func testPostStoreSave(t *testing.T, ss store.Store) {
assert.Greater(t, rchannel3.LastPostAt, rchannel2.LastPostAt)
assert.Equal(t, int64(3), rchannel3.TotalMsgCount)
})
t.Run("Save post with priority metadata set", func(t *testing.T) {
o1 := model.Post{}
o1.ChannelId = model.NewId()
o1.UserId = model.NewId()
o1.Message = NewTestId()
o1.Metadata = &model.PostMetadata{
Priority: &model.PostPriority{
Priority: model.NewString("important"),
RequestedAck: model.NewBool(true),
PersistentNotifications: model.NewBool(false),
},
}
p, err := ss.Post().Save(&o1)
require.NoError(t, err, "couldn't save item")
assert.Equal(t, int64(0), p.ReplyCount)
pp, err := ss.PostPriority().GetForPost(p.Id)
require.NoError(t, err, "couldn't save item")
assert.Equal(t, "important", *pp.Priority)
assert.Equal(t, true, *pp.RequestedAck)
assert.Equal(t, false, *pp.PersistentNotifications)
})
}
func testPostStoreSaveMultiple(t *testing.T, ss store.Store) {

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

@@ -56,6 +56,7 @@ type Store struct {
ProductNoticesStore mocks.ProductNoticesStore
context context.Context
NotifyAdminStore mocks.NotifyAdminStore
PostPriorityStore mocks.PostPriorityStore
}
func (s *Store) SetContext(context context.Context) { s.context = context }
@@ -100,6 +101,7 @@ func (s *Store) NotifyAdmin() store.NotifyAdminStore { return &s.NotifyAdmin
func (s *Store) Group() store.GroupStore { return &s.GroupStore }
func (s *Store) LinkMetadata() store.LinkMetadataStore { return &s.LinkMetadataStore }
func (s *Store) SharedChannel() store.SharedChannelStore { return &s.SharedChannelStore }
func (s *Store) PostPriority() store.PostPriorityStore { return &s.PostPriorityStore }
func (s *Store) MarkSystemRanUnitTests() { /* do nothing */ }
func (s *Store) Close() { /* do nothing */ }
func (s *Store) LockToMaster() { /* do nothing */ }
@@ -158,5 +160,6 @@ func (s *Store) AssertExpectations(t mock.TestingT) bool {
&s.ProductNoticesStore,
&s.SharedChannelStore,
&s.NotifyAdminStore,
&s.PostPriorityStore,
)
}

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

@@ -32,7 +32,7 @@ func TestThreadStore(t *testing.T, ss store.Store, s SqlStore) {
}
func testThreadStorePopulation(t *testing.T, ss store.Store) {
makeSomePosts := func() []*model.Post {
makeSomePosts := func(urgent bool) []*model.Post {
u1 := model.User{
Email: MakeEmail(),
@@ -61,6 +61,16 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
o.UserId = u.Id
o.Message = NewTestId()
if urgent {
o.Metadata = &model.PostMetadata{
Priority: &model.PostPriority{
Priority: model.NewString(model.PostPriorityUrgent),
RequestedAck: model.NewBool(false),
PersistentNotifications: model.NewBool(false),
},
}
}
otmp, err3 := ss.Post().Save(&o)
require.NoError(t, err3)
o2 := model.Post{}
@@ -100,7 +110,7 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
return newPosts
}
t.Run("Save replies creates a thread", func(t *testing.T) {
newPosts := makeSomePosts()
newPosts := makeSomePosts(false)
thread, err := ss.Thread().Get(newPosts[0].Id)
require.NoError(t, err, "couldn't get thread")
require.NotNil(t, thread)
@@ -133,7 +143,7 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
})
t.Run("Delete a reply updates count on a thread", func(t *testing.T) {
newPosts := makeSomePosts()
newPosts := makeSomePosts(false)
thread, err := ss.Thread().Get(newPosts[0].Id)
require.NoError(t, err, "couldn't get thread")
require.NotNil(t, thread)
@@ -307,7 +317,7 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
})
t.Run("Thread membership 'viewed' timestamp is updated properly", func(t *testing.T) {
newPosts := makeSomePosts()
newPosts := makeSomePosts(false)
opts := store.ThreadMembershipOpts{
Following: true,
@@ -341,7 +351,7 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
})
t.Run("Thread membership 'viewed' timestamp is updated properly for new membership", func(t *testing.T) {
newPosts := makeSomePosts()
newPosts := makeSomePosts(false)
opts := store.ThreadMembershipOpts{
Following: true,
@@ -356,7 +366,7 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
})
t.Run("Updating post does not make thread unread", func(t *testing.T) {
newPosts := makeSomePosts()
newPosts := makeSomePosts(false)
opts := store.ThreadMembershipOpts{
Following: true,
IncrementMentions: false,
@@ -366,14 +376,14 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
}
m, err := ss.Thread().MaintainMembership(newPosts[0].UserId, newPosts[0].Id, opts)
require.NoError(t, err)
th, err := ss.Thread().GetThreadForUser(m, false)
th, err := ss.Thread().GetThreadForUser(m, false, false)
require.NoError(t, err)
require.Equal(t, int64(2), th.UnreadReplies)
m.LastViewed = newPosts[2].UpdateAt + 1
_, err = ss.Thread().UpdateMembership(m)
require.NoError(t, err)
th, err = ss.Thread().GetThreadForUser(m, false)
th, err = ss.Thread().GetThreadForUser(m, false, false)
require.NoError(t, err)
require.Equal(t, int64(0), th.UnreadReplies)
@@ -382,13 +392,13 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
_, err = ss.Post().Update(editedPost, newPosts[2])
require.NoError(t, err)
th, err = ss.Thread().GetThreadForUser(m, false)
th, err = ss.Thread().GetThreadForUser(m, false, false)
require.NoError(t, err)
require.Equal(t, int64(0), th.UnreadReplies)
})
t.Run("Empty participantID should not appear in thread response", func(t *testing.T) {
newPosts := makeSomePosts()
newPosts := makeSomePosts(false)
opts := store.ThreadMembershipOpts{
Following: true,
IncrementMentions: false,
@@ -399,7 +409,7 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
m, err := ss.Thread().MaintainMembership("", newPosts[0].Id, opts)
require.NoError(t, err)
m.UserId = newPosts[0].UserId
th, err := ss.Thread().GetThreadForUser(m, true)
th, err := ss.Thread().GetThreadForUser(m, true, false)
require.NoError(t, err)
for _, user := range th.Participants {
require.NotNil(t, user)
@@ -407,7 +417,7 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
})
t.Run("Get unread reply counts for thread", func(t *testing.T) {
t.Skip("MM-41797")
newPosts := makeSomePosts()
newPosts := makeSomePosts(false)
opts := store.ThreadMembershipOpts{
Following: true,
IncrementMentions: false,
@@ -435,6 +445,36 @@ func testThreadStorePopulation(t *testing.T, ss store.Store) {
require.NoError(t, err)
require.Equal(t, int64(2), unreads)
})
testCases := []bool{true, false}
for _, isUrgent := range testCases {
t.Run("Return is urgent for user thread/s", func(t *testing.T) {
newPosts := makeSomePosts(isUrgent)
opts := store.ThreadMembershipOpts{
Following: true,
IncrementMentions: false,
UpdateFollowing: true,
UpdateViewedTimestamp: true,
UpdateParticipants: false,
}
userID := newPosts[0].UserId
_, e := ss.Thread().MaintainMembership(userID, newPosts[0].Id, opts)
require.NoError(t, e)
m, e := ss.Thread().GetMembershipForUser(userID, newPosts[0].Id)
require.NoError(t, e)
th, e := ss.Thread().GetThreadForUser(m, false, true)
require.NoError(t, e)
require.Equal(t, isUrgent, th.IsUrgent)
threads, e := ss.Thread().GetThreadsForUser(userID, "", model.GetUserThreadsOpts{IncludeIsUrgent: true})
require.NoError(t, e)
require.Equal(t, isUrgent, threads[0].IsUrgent)
})
}
}
func threadStoreCreateReply(t *testing.T, ss store.Store, channelID, postID, userID string, createAt int64) *model.Post {
@@ -660,7 +700,7 @@ func testGetTeamsUnreadForUser(t *testing.T, ss store.Store) {
threadStoreCreateReply(t, ss, channel1.Id, post.Id, post.UserId, model.GetMillis())
createThreadMembership(userID, post.Id)
teamsUnread, err := ss.Thread().GetTeamsUnreadForUser(userID, []string{team1.Id})
teamsUnread, err := ss.Thread().GetTeamsUnreadForUser(userID, []string{team1.Id}, true)
require.NoError(t, err)
assert.Len(t, teamsUnread, 1)
assert.Equal(t, int64(1), teamsUnread[team1.Id].ThreadCount)
@@ -674,7 +714,7 @@ func testGetTeamsUnreadForUser(t *testing.T, ss store.Store) {
threadStoreCreateReply(t, ss, channel1.Id, post.Id, post.UserId, model.GetMillis())
createThreadMembership(userID, post.Id)
teamsUnread, err = ss.Thread().GetTeamsUnreadForUser(userID, []string{team1.Id})
teamsUnread, err = ss.Thread().GetTeamsUnreadForUser(userID, []string{team1.Id}, true)
require.NoError(t, err)
assert.Len(t, teamsUnread, 1)
assert.Equal(t, int64(2), teamsUnread[team1.Id].ThreadCount)
@@ -693,16 +733,24 @@ func testGetTeamsUnreadForUser(t *testing.T, ss store.Store) {
Type: model.ChannelTypeOpen,
}, -1)
require.NoError(t, err)
post2, err := ss.Post().Save(&model.Post{
ChannelId: channel2.Id,
UserId: userID,
Message: model.NewRandomString(10),
Metadata: &model.PostMetadata{
Priority: &model.PostPriority{
Priority: model.NewString(model.PostPriorityUrgent),
RequestedAck: model.NewBool(false),
PersistentNotifications: model.NewBool(false),
},
},
})
require.NoError(t, err)
threadStoreCreateReply(t, ss, channel2.Id, post2.Id, post2.UserId, model.GetMillis())
createThreadMembership(userID, post2.Id)
teamsUnread, err = ss.Thread().GetTeamsUnreadForUser(userID, []string{team1.Id, team2.Id})
teamsUnread, err = ss.Thread().GetTeamsUnreadForUser(userID, []string{team1.Id, team2.Id}, true)
require.NoError(t, err)
assert.Len(t, teamsUnread, 2)
assert.Equal(t, int64(2), teamsUnread[team1.Id].ThreadCount)
@@ -715,11 +763,12 @@ func testGetTeamsUnreadForUser(t *testing.T, ss store.Store) {
_, err = ss.Thread().MaintainMembership(userID, post2.Id, opts)
require.NoError(t, err)
teamsUnread, err = ss.Thread().GetTeamsUnreadForUser(userID, []string{team2.Id})
teamsUnread, err = ss.Thread().GetTeamsUnreadForUser(userID, []string{team2.Id}, true)
require.NoError(t, err)
assert.Len(t, teamsUnread, 1)
assert.Equal(t, int64(1), teamsUnread[team2.Id].ThreadCount)
assert.Equal(t, int64(1), teamsUnread[team2.Id].ThreadMentionCount)
assert.Equal(t, int64(1), teamsUnread[team2.Id].ThreadUrgentMentionCount)
}
type byPostId []*model.Post
@@ -831,6 +880,13 @@ func testVarious(t *testing.T, ss store.Store) {
ChannelId: team1channel1.Id,
UserId: user1ID,
Message: model.NewRandomString(10),
Metadata: &model.PostMetadata{
Priority: &model.PostPriority{
Priority: model.NewString(model.PostPriorityUrgent),
RequestedAck: model.NewBool(false),
PersistentNotifications: model.NewBool(false),
},
},
})
require.NoError(t, err)
@@ -1032,6 +1088,33 @@ func testVarious(t *testing.T, ss store.Store) {
}
})
t.Run("GetTotalUnreadUrgentMentions", func(t *testing.T) {
testCases := []struct {
Description string
UserID string
TeamID string
Options model.GetUserThreadsOpts
ExpectedThreads []*model.Post
}{
{"all teams, user1", user1ID, "", model.GetUserThreadsOpts{}, []*model.Post{
team1channel1post3,
}},
{"team1, user1", user1ID, team1.Id, model.GetUserThreadsOpts{}, []*model.Post{
team1channel1post3,
}},
{"team2, user1", user1ID, team2.Id, model.GetUserThreadsOpts{}, []*model.Post{}},
}
for _, testCase := range testCases {
t.Run(testCase.Description, func(t *testing.T) {
totalUnreadUrgentMentions, err := ss.Thread().GetTotalUnreadUrgentMentions(testCase.UserID, testCase.TeamID, testCase.Options)
require.NoError(t, err)
assert.EqualValues(t, int64(len(testCase.ExpectedThreads)), totalUnreadUrgentMentions)
})
}
})
assertThreadPosts := func(t *testing.T, threads []*model.ThreadResponse, expectedPosts []*model.Post) {
t.Helper()
@@ -1166,7 +1249,7 @@ func testMarkAllAsReadByChannels(t *testing.T, ss store.Store) {
assertThreadReplyCount := func(t *testing.T, userID string, count int64) {
t.Helper()
teamsUnread, err := ss.Thread().GetTeamsUnreadForUser(userID, []string{team1.Id})
teamsUnread, err := ss.Thread().GetTeamsUnreadForUser(userID, []string{team1.Id}, false)
require.NoError(t, err)
require.Len(t, teamsUnread, 1, "unexpected unread teams count")
assert.Equal(t, count, teamsUnread[team1.Id].ThreadCount, "unexpected thread count")
@@ -1623,7 +1706,7 @@ func testMarkAllAsReadByTeam(t *testing.T, ss store.Store) {
assertThreadReplyCount := func(t *testing.T, userID, teamID string, count int64, message string) {
t.Helper()
teamsUnread, err := ss.Thread().GetTeamsUnreadForUser(userID, []string{teamID})
teamsUnread, err := ss.Thread().GetTeamsUnreadForUser(userID, []string{teamID}, true)
require.NoError(t, err)
require.Lenf(t, teamsUnread, 1, "unexpected unread teams count: %s", message)
assert.Equalf(t, count, teamsUnread[teamID].ThreadCount, "unexpected thread count: %s", message)

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

@@ -2468,7 +2468,7 @@ func testUserUnreadCount(t *testing.T, ss store.Store) {
// Post one message with mention to open channel
_, nErr = ss.Post().Save(&p1)
require.NoError(t, nErr)
nErr = ss.Channel().IncrementMentionCount(c1.Id, []string{u2.Id, u3.Id}, false)
nErr = ss.Channel().IncrementMentionCount(c1.Id, []string{u2.Id, u3.Id}, false, false)
require.NoError(t, nErr)
// Post 2 messages without mention to direct channel
@@ -2479,7 +2479,7 @@ func testUserUnreadCount(t *testing.T, ss store.Store) {
_, nErr = ss.Post().Save(&p2)
require.NoError(t, nErr)
nErr = ss.Channel().IncrementMentionCount(c2.Id, []string{u2.Id}, false)
nErr = ss.Channel().IncrementMentionCount(c2.Id, []string{u2.Id}, false, false)
require.NoError(t, nErr)
p3 := model.Post{}
@@ -2489,7 +2489,7 @@ func testUserUnreadCount(t *testing.T, ss store.Store) {
_, nErr = ss.Post().Save(&p3)
require.NoError(t, nErr)
nErr = ss.Channel().IncrementMentionCount(c2.Id, []string{u2.Id}, false)
nErr = ss.Channel().IncrementMentionCount(c2.Id, []string{u2.Id}, false, false)
require.NoError(t, nErr)
badge, unreadCountErr := ss.User().GetUnreadCount(u2.Id, false)
@@ -2501,7 +2501,7 @@ func testUserUnreadCount(t *testing.T, ss store.Store) {
require.Equal(t, int64(1), badge, "should have 1 unread message")
// Increment root mentions by 1
nErr = ss.Channel().IncrementMentionCount(c1.Id, []string{u3.Id}, true)
nErr = ss.Channel().IncrementMentionCount(c1.Id, []string{u3.Id}, true, false)
require.NoError(t, nErr)
// CRT is enabled, only root mentions are counted

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

@@ -36,6 +36,7 @@ type TimerLayer struct {
OAuthStore store.OAuthStore
PluginStore store.PluginStore
PostStore store.PostStore
PostPriorityStore store.PostPriorityStore
PreferenceStore store.PreferenceStore
ProductNoticesStore store.ProductNoticesStore
ReactionStore store.ReactionStore
@@ -130,6 +131,10 @@ func (s *TimerLayer) Post() store.PostStore {
return s.PostStore
}
func (s *TimerLayer) PostPriority() store.PostPriorityStore {
return s.PostPriorityStore
}
func (s *TimerLayer) Preference() store.PreferenceStore {
return s.PreferenceStore
}
@@ -300,6 +305,11 @@ type TimerLayerPostStore struct {
Root *TimerLayer
}
type TimerLayerPostPriorityStore struct {
store.PostPriorityStore
Root *TimerLayer
}
type TimerLayerPreferenceStore struct {
store.PreferenceStore
Root *TimerLayer
@@ -671,6 +681,22 @@ func (s *TimerLayerChannelStore) CountPostsAfter(channelID string, timestamp int
return result, resultVar1, err
}
func (s *TimerLayerChannelStore) CountUrgentPostsAfter(channelID string, timestamp int64, userID string) (int, error) {
start := time.Now()
result, err := s.ChannelStore.CountUrgentPostsAfter(channelID, timestamp, userID)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("ChannelStore.CountUrgentPostsAfter", success, elapsed)
}
return result, err
}
func (s *TimerLayerChannelStore) CreateDirectChannel(userID *model.User, otherUserID *model.User, channelOptions ...model.ChannelOption) (*model.Channel, error) {
start := time.Now()
@@ -1711,10 +1737,10 @@ func (s *TimerLayerChannelStore) GroupSyncedChannelCount() (int64, error) {
return result, err
}
func (s *TimerLayerChannelStore) IncrementMentionCount(channelID string, userIDs []string, isRoot bool) error {
func (s *TimerLayerChannelStore) IncrementMentionCount(channelID string, userIDs []string, isRoot bool, isUrgent bool) error {
start := time.Now()
err := s.ChannelStore.IncrementMentionCount(channelID, userIDs, isRoot)
err := s.ChannelStore.IncrementMentionCount(channelID, userIDs, isRoot, isUrgent)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
@@ -2248,10 +2274,10 @@ func (s *TimerLayerChannelStore) UpdateLastViewedAt(channelIds []string, userID
return result, err
}
func (s *TimerLayerChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount int, mentionCountRoot int, setUnreadCountRoot bool) (*model.ChannelUnreadAt, error) {
func (s *TimerLayerChannelStore) UpdateLastViewedAtPost(unreadPost *model.Post, userID string, mentionCount int, mentionCountRoot int, urgentMentionCount int, setUnreadCountRoot bool) (*model.ChannelUnreadAt, error) {
start := time.Now()
result, err := s.ChannelStore.UpdateLastViewedAtPost(unreadPost, userID, mentionCount, mentionCountRoot, setUnreadCountRoot)
result, err := s.ChannelStore.UpdateLastViewedAtPost(unreadPost, userID, mentionCount, mentionCountRoot, urgentMentionCount, setUnreadCountRoot)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
@@ -5891,6 +5917,38 @@ func (s *TimerLayerPostStore) Update(newPost *model.Post, oldPost *model.Post) (
return result, err
}
func (s *TimerLayerPostPriorityStore) GetForPost(postId string) (*model.PostPriority, error) {
start := time.Now()
result, err := s.PostPriorityStore.GetForPost(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("PostPriorityStore.GetForPost", success, elapsed)
}
return result, err
}
func (s *TimerLayerPostPriorityStore) GetForPosts(ids []string) ([]*model.PostPriority, error) {
start := time.Now()
result, err := s.PostPriorityStore.GetForPosts(ids)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("PostPriorityStore.GetForPosts", success, elapsed)
}
return result, err
}
func (s *TimerLayerPreferenceStore) CleanupFlagsBatch(limit int64) (int64, error) {
start := time.Now()
@@ -8881,10 +8939,10 @@ func (s *TimerLayerThreadStore) GetMembershipsForUser(userId string, teamID stri
return result, err
}
func (s *TimerLayerThreadStore) GetTeamsUnreadForUser(userID string, teamIDs []string) (map[string]*model.TeamUnread, error) {
func (s *TimerLayerThreadStore) GetTeamsUnreadForUser(userID string, teamIDs []string, includeUrgentMentionCount bool) (map[string]*model.TeamUnread, error) {
start := time.Now()
result, err := s.ThreadStore.GetTeamsUnreadForUser(userID, teamIDs)
result, err := s.ThreadStore.GetTeamsUnreadForUser(userID, teamIDs, includeUrgentMentionCount)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
@@ -8913,10 +8971,10 @@ func (s *TimerLayerThreadStore) GetThreadFollowers(threadID string, fetchOnlyAct
return result, err
}
func (s *TimerLayerThreadStore) GetThreadForUser(threadMembership *model.ThreadMembership, extended bool) (*model.ThreadResponse, error) {
func (s *TimerLayerThreadStore) GetThreadForUser(threadMembership *model.ThreadMembership, extended bool, postPriorityIsEnabled bool) (*model.ThreadResponse, error) {
start := time.Now()
result, err := s.ThreadStore.GetThreadForUser(threadMembership, extended)
result, err := s.ThreadStore.GetThreadForUser(threadMembership, extended, postPriorityIsEnabled)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
@@ -9041,6 +9099,22 @@ func (s *TimerLayerThreadStore) GetTotalUnreadThreads(userId string, teamID stri
return result, err
}
func (s *TimerLayerThreadStore) GetTotalUnreadUrgentMentions(userId string, teamID string, opts model.GetUserThreadsOpts) (int64, error) {
start := time.Now()
result, err := s.ThreadStore.GetTotalUnreadUrgentMentions(userId, teamID, opts)
elapsed := float64(time.Since(start)) / float64(time.Second)
if s.Root.Metrics != nil {
success := "false"
if err == nil {
success = "true"
}
s.Root.Metrics.ObserveStoreMethodDuration("ThreadStore.GetTotalUnreadUrgentMentions", success, elapsed)
}
return result, err
}
func (s *TimerLayerThreadStore) MaintainMembership(userID string, postID string, opts store.ThreadMembershipOpts) (*model.ThreadMembership, error) {
start := time.Now()
@@ -11270,6 +11344,7 @@ func New(childStore store.Store, metrics einterfaces.MetricsInterface) *TimerLay
newStore.OAuthStore = &TimerLayerOAuthStore{OAuthStore: childStore.OAuth(), Root: &newStore}
newStore.PluginStore = &TimerLayerPluginStore{PluginStore: childStore.Plugin(), Root: &newStore}
newStore.PostStore = &TimerLayerPostStore{PostStore: childStore.Post(), 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}
newStore.ReactionStore = &TimerLayerReactionStore{ReactionStore: childStore.Reaction(), Root: &newStore}