[MM-59367] export: enable exporting thread followers for CRT (#27623)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
3dc0e63c03
Коммит
d5cc2eb2f6
@@ -505,6 +505,28 @@ func (s *SqlThreadStore) GetThreadFollowers(threadID string, fetchOnlyActive boo
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func (s *SqlThreadStore) GetThreadMembershipsForExport(postID string) ([]*model.ThreadMembershipForExport, error) {
|
||||
members := []*model.ThreadMembershipForExport{}
|
||||
|
||||
fetchConditions := sq.And{
|
||||
sq.Eq{"PostId": postID},
|
||||
sq.Eq{"Following": true},
|
||||
}
|
||||
|
||||
query := s.getQueryBuilder().
|
||||
Select("Users.Username, ThreadMemberships.LastViewed, ThreadMemberships.UnreadMentions").
|
||||
From("ThreadMemberships").
|
||||
InnerJoin("Users ON ThreadMemberships.UserId = Users.Id").
|
||||
Where(fetchConditions)
|
||||
|
||||
err := s.GetReplicaX().SelectBuilder(&members, query)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "failed to get thread members for thread id=%s", postID)
|
||||
}
|
||||
|
||||
return members, nil
|
||||
}
|
||||
|
||||
func (s *SqlThreadStore) GetThreadForUser(threadMembership *model.ThreadMembership, extended, postPriorityEnabled bool) (*model.ThreadResponse, error) {
|
||||
if !threadMembership.Following {
|
||||
return nil, store.NewErrNotFound("ThreadMembership", "<following>")
|
||||
@@ -807,22 +829,83 @@ func (s *SqlThreadStore) DeleteMembershipForUser(userId string, postId string) e
|
||||
// - post creation (mentions handling)
|
||||
// - channel marked unread
|
||||
// - user explicitly following a thread
|
||||
func (s *SqlThreadStore) MaintainMembership(userId, postId string, opts store.ThreadMembershipOpts) (_ *model.ThreadMembership, err error) {
|
||||
func (s *SqlThreadStore) MaintainMembership(userID, postID string, opts store.ThreadMembershipOpts) (_ *model.ThreadMembership, err error) {
|
||||
trx, err := s.GetMasterX().Beginx()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "begin_transaction")
|
||||
}
|
||||
defer finalizeTransactionX(trx, &err)
|
||||
|
||||
membership, err := s.getMembershipForUser(trx, userId, postId)
|
||||
membership, err := s.maintainMembershipTx(trx, userID, postID, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = trx.Commit(); err != nil {
|
||||
return nil, errors.Wrap(err, "commit_transaction")
|
||||
}
|
||||
|
||||
return membership, nil
|
||||
}
|
||||
|
||||
func (s *SqlThreadStore) MaintainMultipleFromImport(memberships []*model.ThreadMembership) (_ []*model.ThreadMembership, err error) {
|
||||
trx, err := s.GetMasterX().Beginx()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "begin_transaction")
|
||||
}
|
||||
defer finalizeTransactionX(trx, &err)
|
||||
|
||||
for _, member := range memberships {
|
||||
membership, err2 := s.maintainMembershipTx(trx, member.UserId, member.PostId, store.ThreadMembershipOpts{
|
||||
ImportData: &store.ThreadMembershipImportData{
|
||||
UnreadMentions: member.UnreadMentions,
|
||||
LastViewed: member.LastViewed,
|
||||
},
|
||||
})
|
||||
if err2 != nil {
|
||||
return nil, err2
|
||||
}
|
||||
|
||||
memberships = append(memberships, membership)
|
||||
}
|
||||
|
||||
if err = trx.Commit(); err != nil {
|
||||
return nil, errors.Wrap(err, "commit_transaction")
|
||||
}
|
||||
|
||||
return memberships, nil
|
||||
}
|
||||
|
||||
func (s *SqlThreadStore) maintainMembershipTx(trx *sqlxTxWrapper, userID, postID string, opts store.ThreadMembershipOpts) (_ *model.ThreadMembership, err error) {
|
||||
membership, err := s.getMembershipForUser(trx, userID, postID)
|
||||
now := utils.MillisFromTime(time.Now())
|
||||
// if membership exists, update it if:
|
||||
// a. user started/stopped following a thread
|
||||
// b. mention count changed
|
||||
// c. user viewed a thread
|
||||
// d. the membership is imported
|
||||
if err == nil {
|
||||
followingNeedsUpdate := (opts.UpdateFollowing && (membership.Following != opts.Following))
|
||||
if followingNeedsUpdate || opts.IncrementMentions || opts.UpdateViewedTimestamp {
|
||||
if imported := opts.ImportData; imported != nil {
|
||||
// Only the active followers are getting exported, so we can safely assume
|
||||
// that the user is following the thread.
|
||||
if membership.LastUpdated > imported.LastViewed {
|
||||
// User may have stopped following the thread,
|
||||
// we need to be smart if we should activate the membership
|
||||
return membership, nil
|
||||
}
|
||||
membership.Following = true
|
||||
membership.LastUpdated = now
|
||||
membership.UnreadMentions = imported.UnreadMentions
|
||||
membership.LastViewed = imported.LastViewed
|
||||
if _, err = s.updateMembership(trx, membership); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = s.updateThreadParticipantsForUserTx(trx, postID, userID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else if followingNeedsUpdate || opts.IncrementMentions || opts.UpdateViewedTimestamp {
|
||||
if followingNeedsUpdate {
|
||||
membership.Following = opts.Following
|
||||
}
|
||||
@@ -838,10 +921,6 @@ func (s *SqlThreadStore) MaintainMembership(userId, postId string, opts store.Th
|
||||
}
|
||||
}
|
||||
|
||||
if err = trx.Commit(); err != nil {
|
||||
return nil, errors.Wrap(err, "commit_transaction")
|
||||
}
|
||||
|
||||
return membership, err
|
||||
}
|
||||
|
||||
@@ -851,16 +930,25 @@ func (s *SqlThreadStore) MaintainMembership(userId, postId string, opts store.Th
|
||||
}
|
||||
|
||||
membership = &model.ThreadMembership{
|
||||
PostId: postId,
|
||||
UserId: userId,
|
||||
PostId: postID,
|
||||
UserId: userID,
|
||||
Following: opts.Following,
|
||||
LastUpdated: now,
|
||||
}
|
||||
if opts.IncrementMentions {
|
||||
membership.UnreadMentions = 1
|
||||
}
|
||||
if opts.UpdateViewedTimestamp {
|
||||
membership.LastViewed = now
|
||||
if opts.ImportData != nil {
|
||||
membership.UnreadMentions = opts.ImportData.UnreadMentions
|
||||
membership.LastViewed = opts.ImportData.LastViewed
|
||||
membership.Following = true
|
||||
// If we are importing data, we need to update the thread participants regardless
|
||||
// of what is given from the options.
|
||||
opts.UpdateParticipants = true
|
||||
} else {
|
||||
if opts.IncrementMentions {
|
||||
membership.UnreadMentions = 1
|
||||
}
|
||||
if opts.UpdateViewedTimestamp {
|
||||
membership.LastViewed = now
|
||||
}
|
||||
}
|
||||
membership, err = s.saveMembership(trx, membership)
|
||||
if err != nil {
|
||||
@@ -868,37 +956,11 @@ func (s *SqlThreadStore) MaintainMembership(userId, postId string, opts store.Th
|
||||
}
|
||||
|
||||
if opts.UpdateParticipants {
|
||||
if s.DriverName() == model.DatabaseDriverPostgres {
|
||||
userIdParam, err2 := jsonArray([]string{userId}).Value()
|
||||
if err2 != nil {
|
||||
return nil, err2
|
||||
}
|
||||
if s.IsBinaryParamEnabled() {
|
||||
userIdParam = AppendBinaryFlag(userIdParam.([]byte))
|
||||
}
|
||||
|
||||
if _, err2 := trx.ExecRaw(`UPDATE Threads
|
||||
SET participants = participants || $1::jsonb
|
||||
WHERE postid=$2
|
||||
AND NOT participants ? $3`, userIdParam, postId, userId); err2 != nil {
|
||||
return nil, err2
|
||||
}
|
||||
} else {
|
||||
// CONCAT('$[', JSON_LENGTH(Participants), ']') just generates $[n]
|
||||
// which is the positional syntax required for appending.
|
||||
if _, err2 := trx.Exec(`UPDATE Threads
|
||||
SET Participants = JSON_ARRAY_INSERT(Participants, CONCAT('$[', JSON_LENGTH(Participants), ']'), ?)
|
||||
WHERE PostId=?
|
||||
AND NOT JSON_CONTAINS(Participants, ?)`, userId, postId, strconv.Quote(userId)); err2 != nil {
|
||||
return nil, err2
|
||||
}
|
||||
if err = s.updateThreadParticipantsForUserTx(trx, postID, userID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err = trx.Commit(); err != nil {
|
||||
return nil, errors.Wrap(err, "commit_transaction")
|
||||
}
|
||||
|
||||
return membership, err
|
||||
}
|
||||
|
||||
@@ -987,3 +1049,71 @@ func (s *SqlThreadStore) GetThreadUnreadReplyCount(threadMembership *model.Threa
|
||||
|
||||
return unreadReplies, nil
|
||||
}
|
||||
|
||||
// SaveMultipleMemberships saves multiple NEW thread memberships in a single query and meant to be used only in the import
|
||||
// process. Unlike MaintainMembership, this method does not update the thread participants (which is handled separately
|
||||
// in the post creation).
|
||||
func (s *SqlThreadStore) SaveMultipleMemberships(memberships []*model.ThreadMembership) ([]*model.ThreadMembership, error) {
|
||||
if len(memberships) == 0 {
|
||||
return memberships, nil
|
||||
}
|
||||
|
||||
query := s.getQueryBuilder().
|
||||
Insert("ThreadMemberships").
|
||||
Columns("PostId", "UserId", "Following", "LastViewed", "LastUpdated", "UnreadMentions")
|
||||
|
||||
for _, member := range memberships {
|
||||
if err := member.IsValid(); err != nil {
|
||||
return memberships, err
|
||||
}
|
||||
member.LastUpdated = model.GetMillis()
|
||||
query = query.Values(member.PostId, member.UserId, member.Following, member.LastViewed, member.LastUpdated, member.UnreadMentions)
|
||||
}
|
||||
|
||||
tx, err := s.GetMasterX().Beginx()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "begin_transaction")
|
||||
}
|
||||
defer finalizeTransactionX(tx, &err)
|
||||
|
||||
_, err = tx.ExecBuilder(query)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to save thread memberships")
|
||||
}
|
||||
err = tx.Commit()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "commit_transaction")
|
||||
}
|
||||
|
||||
return memberships, nil
|
||||
}
|
||||
|
||||
func (s *SqlThreadStore) updateThreadParticipantsForUserTx(trx *sqlxTxWrapper, postID, userID string) error {
|
||||
if s.DriverName() == model.DatabaseDriverPostgres {
|
||||
userIdParam, err := jsonArray([]string{userID}).Value()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if s.IsBinaryParamEnabled() {
|
||||
userIdParam = AppendBinaryFlag(userIdParam.([]byte))
|
||||
}
|
||||
|
||||
if _, err := trx.ExecRaw(`UPDATE Threads
|
||||
SET participants = participants || $1::jsonb
|
||||
WHERE postid=$2
|
||||
AND NOT participants ? $3`, userIdParam, postID, userID); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// CONCAT('$[', JSON_LENGTH(Participants), ']') just generates $[n]
|
||||
// which is the positional syntax required for appending.
|
||||
if _, err := trx.Exec(`UPDATE Threads
|
||||
SET Participants = JSON_ARRAY_INSERT(Participants, CONCAT('$[', JSON_LENGTH(Participants), ']'), ?)
|
||||
WHERE PostId=?
|
||||
AND NOT JSON_CONTAINS(Participants, ?)`, userID, postID, strconv.Quote(userID)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
Ссылка в новой задаче
Block a user