MM-31713 Server/API: GetUserThread method (#16659)
Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
c14194d78b
Коммит
77da23e84b
@@ -7828,6 +7828,24 @@ func (s *OpenTracingLayerThreadStore) GetPosts(threadId string, since int64) ([]
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayerThreadStore) GetThreadForUser(userId string, teamId string, threadId string, extended bool) (*model.ThreadResponse, error) {
|
||||
origCtx := s.Root.Store.Context()
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ThreadStore.GetThreadForUser")
|
||||
s.Root.Store.SetContext(newCtx)
|
||||
defer func() {
|
||||
s.Root.Store.SetContext(origCtx)
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
result, err := s.ThreadStore.GetThreadForUser(userId, teamId, threadId, extended)
|
||||
if err != nil {
|
||||
span.LogFields(spanlog.Error(err))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *OpenTracingLayerThreadStore) GetThreadsForUser(userId string, teamId string, opts model.GetUserThreadsOpts) (*model.Threads, error) {
|
||||
origCtx := s.Root.Store.Context()
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(s.Root.Store.Context(), "ThreadStore.GetThreadsForUser")
|
||||
|
||||
@@ -8498,6 +8498,26 @@ func (s *RetryLayerThreadStore) GetPosts(threadId string, since int64) ([]*model
|
||||
|
||||
}
|
||||
|
||||
func (s *RetryLayerThreadStore) GetThreadForUser(userId string, teamId string, threadId string, extended bool) (*model.ThreadResponse, error) {
|
||||
|
||||
tries := 0
|
||||
for {
|
||||
result, err := s.ThreadStore.GetThreadForUser(userId, teamId, threadId, extended)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (s *RetryLayerThreadStore) GetThreadsForUser(userId string, teamId string, opts model.GetUserThreadsOpts) (*model.Threads, error) {
|
||||
|
||||
tries := 0
|
||||
|
||||
@@ -283,6 +283,71 @@ func (s *SqlThreadStore) GetThreadsForUser(userId, teamId string, opts model.Get
|
||||
|
||||
return result, nil
|
||||
}
|
||||
func (s *SqlThreadStore) GetThreadForUser(userId, teamId, threadId string, extended bool) (*model.ThreadResponse, error) {
|
||||
type JoinedThread struct {
|
||||
PostId string
|
||||
Following bool
|
||||
ReplyCount int64
|
||||
LastReplyAt int64
|
||||
LastViewedAt int64
|
||||
UnreadReplies int64
|
||||
UnreadMentions int64
|
||||
Participants model.StringArray
|
||||
model.Post
|
||||
}
|
||||
|
||||
unreadRepliesQuery := "SELECT COUNT(Posts.Id) From Posts Where Posts.RootId=ThreadMemberships.PostId AND Posts.UpdateAt >= ThreadMemberships.LastViewed AND Posts.DeleteAt=0"
|
||||
fetchConditions := sq.And{
|
||||
sq.Or{sq.Eq{"Channels.TeamId": teamId}, sq.Eq{"Channels.TeamId": ""}},
|
||||
sq.Eq{"ThreadMemberships.UserId": userId},
|
||||
sq.Eq{"Threads.PostId": threadId},
|
||||
}
|
||||
|
||||
var thread JoinedThread
|
||||
query, args, _ := s.getQueryBuilder().
|
||||
Select("Threads.*, Posts.*, ThreadMemberships.LastViewed as LastViewedAt, ThreadMemberships.UnreadMentions as UnreadMentions, ThreadMemberships.Following").
|
||||
From("Threads").
|
||||
Column(sq.Alias(sq.Expr(unreadRepliesQuery), "UnreadReplies")).
|
||||
LeftJoin("Posts ON Posts.Id = Threads.PostId").
|
||||
LeftJoin("Channels ON Posts.ChannelId = Channels.Id").
|
||||
LeftJoin("ThreadMemberships ON ThreadMemberships.PostId = Threads.PostId").
|
||||
Where(fetchConditions).ToSql()
|
||||
err := s.GetReplica().SelectOne(&thread, query, args...)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !thread.Following {
|
||||
return nil, nil // in case the thread is not followed anymore - return nil error to be interpreted as 404
|
||||
}
|
||||
|
||||
var users []*model.User
|
||||
if extended {
|
||||
var err error
|
||||
users, err = s.User().GetProfileByIds(thread.Participants, &store.UserGetByIdsOpts{}, true)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get threads for user id=%s", userId)
|
||||
}
|
||||
} else {
|
||||
for _, userId := range thread.Participants {
|
||||
users = append(users, &model.User{Id: userId})
|
||||
}
|
||||
}
|
||||
|
||||
result := &model.ThreadResponse{
|
||||
PostId: thread.PostId,
|
||||
ReplyCount: thread.ReplyCount,
|
||||
LastReplyAt: thread.LastReplyAt,
|
||||
LastViewedAt: thread.LastViewedAt,
|
||||
UnreadReplies: thread.UnreadReplies,
|
||||
UnreadMentions: thread.UnreadMentions,
|
||||
Participants: users,
|
||||
Post: &thread.Post,
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *SqlThreadStore) MarkAllAsRead(userId, teamId string) error {
|
||||
memberships, err := s.GetMembershipsForUser(userId, teamId)
|
||||
|
||||
@@ -252,6 +252,7 @@ type ThreadStore interface {
|
||||
Update(thread *model.Thread) (*model.Thread, error)
|
||||
Get(id string) (*model.Thread, error)
|
||||
GetThreadsForUser(userId, teamId string, opts model.GetUserThreadsOpts) (*model.Threads, error)
|
||||
GetThreadForUser(userId, teamId, threadId string, extended bool) (*model.ThreadResponse, error)
|
||||
Delete(postId string) error
|
||||
GetPosts(threadId string, since int64) ([]*model.Post, error)
|
||||
|
||||
|
||||
@@ -171,6 +171,29 @@ func (_m *ThreadStore) GetPosts(threadId string, since int64) ([]*model.Post, er
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetThreadForUser provides a mock function with given fields: userId, teamId, threadId, extended
|
||||
func (_m *ThreadStore) GetThreadForUser(userId string, teamId string, threadId string, extended bool) (*model.ThreadResponse, error) {
|
||||
ret := _m.Called(userId, teamId, threadId, extended)
|
||||
|
||||
var r0 *model.ThreadResponse
|
||||
if rf, ok := ret.Get(0).(func(string, string, string, bool) *model.ThreadResponse); ok {
|
||||
r0 = rf(userId, teamId, threadId, extended)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(*model.ThreadResponse)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(string, string, string, bool) error); ok {
|
||||
r1 = rf(userId, teamId, threadId, extended)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// GetThreadsForUser provides a mock function with given fields: userId, teamId, opts
|
||||
func (_m *ThreadStore) GetThreadsForUser(userId string, teamId string, opts model.GetUserThreadsOpts) (*model.Threads, error) {
|
||||
ret := _m.Called(userId, teamId, opts)
|
||||
|
||||
@@ -7064,6 +7064,22 @@ func (s *TimerLayerThreadStore) GetPosts(threadId string, since int64) ([]*model
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *TimerLayerThreadStore) GetThreadForUser(userId string, teamId string, threadId string, extended bool) (*model.ThreadResponse, error) {
|
||||
start := timemodule.Now()
|
||||
|
||||
result, err := s.ThreadStore.GetThreadForUser(userId, teamId, threadId, extended)
|
||||
|
||||
elapsed := float64(timemodule.Since(start)) / float64(timemodule.Second)
|
||||
if s.Root.Metrics != nil {
|
||||
success := "false"
|
||||
if err == nil {
|
||||
success = "true"
|
||||
}
|
||||
s.Root.Metrics.ObserveStoreMethodDuration("ThreadStore.GetThreadForUser", success, elapsed)
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
|
||||
func (s *TimerLayerThreadStore) GetThreadsForUser(userId string, teamId string, opts model.GetUserThreadsOpts) (*model.Threads, error) {
|
||||
start := timemodule.Now()
|
||||
|
||||
|
||||
Ссылка в новой задаче
Block a user