[MM-58834] Review user preferences export and import (#28286)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
e8ebbcc980
Коммит
9d5993d89d
@@ -107,6 +107,15 @@ func postSliceColumns() []string {
|
||||
return cols
|
||||
}
|
||||
|
||||
func postSliceColumnsWithName(name string) []string {
|
||||
colInfos := postSliceColumnsWithTypes()
|
||||
cols := make([]string, len(colInfos))
|
||||
for i, colInfo := range colInfos {
|
||||
cols[i] = name + "." + colInfo.Name
|
||||
}
|
||||
return cols
|
||||
}
|
||||
|
||||
func postSliceCoalesceQuery() string {
|
||||
colInfos := postSliceColumnsWithTypes()
|
||||
cols := make([]string, len(colInfos))
|
||||
@@ -2661,13 +2670,22 @@ func (s *SqlPostStore) GetParentsForExportAfter(limit int, afterId string, inclu
|
||||
excludeDeletedCond = append(excludeDeletedCond, sq.Eq{"Channels.DeleteAt": 0})
|
||||
}
|
||||
|
||||
aggFn := "COALESCE(json_agg(u1.username) FILTER (WHERE u1.username IS NOT NULL), '[]')"
|
||||
if s.DriverName() == model.DatabaseDriverMysql {
|
||||
aggFn = "IF (COUNT(u1.Username) = 0, JSON_ARRAY(), JSON_ARRAYAGG(u1.Username))"
|
||||
}
|
||||
result := []*model.PostForExport{}
|
||||
|
||||
builder := s.getQueryBuilder().
|
||||
Select("p1.*, Users.Username as Username, Teams.Name as TeamName, Channels.Name as ChannelName").
|
||||
Select(fmt.Sprintf("%s, Users.Username as Username, Teams.Name as TeamName, Channels.Name as ChannelName, %s as FlaggedBy", strings.Join(postSliceColumnsWithName("p1"), ", "), aggFn)).
|
||||
FromSelect(sq.Select("*").From("Posts").Where(sq.Eq{"Posts.Id": rootIds}), "p1").
|
||||
LeftJoin("Preferences ON p1.Id = Preferences.Name").
|
||||
LeftJoin("Users u1 ON Preferences.UserId = u1.Id").
|
||||
InnerJoin("Channels ON p1.ChannelId = Channels.Id").
|
||||
InnerJoin("Teams ON Channels.TeamId = Teams.Id").
|
||||
InnerJoin("Users ON p1.UserId = Users.Id").
|
||||
Where(excludeDeletedCond).
|
||||
GroupBy(fmt.Sprintf("%s, Users.Username, Teams.Name, Channels.Name", strings.Join(postSliceColumnsWithName("p1"), ", "))).
|
||||
OrderBy("p1.Id")
|
||||
|
||||
query, args, err := builder.ToSql()
|
||||
@@ -2675,56 +2693,72 @@ func (s *SqlPostStore) GetParentsForExportAfter(limit int, afterId string, inclu
|
||||
return nil, errors.Wrap(err, "postsForExport_toSql")
|
||||
}
|
||||
|
||||
err = s.GetSearchReplicaX().Select(&postsForExport, query, args...)
|
||||
err = s.GetSearchReplicaX().Select(&result, query, args...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to find Posts")
|
||||
}
|
||||
|
||||
if len(postsForExport) == 0 {
|
||||
if len(result) == 0 {
|
||||
// All of the posts were in channels or teams that were deleted.
|
||||
// Update the afterId and try again.
|
||||
afterId = rootIds[len(rootIds)-1]
|
||||
continue
|
||||
}
|
||||
|
||||
return postsForExport, nil
|
||||
return result, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SqlPostStore) GetRepliesForExport(rootId string) ([]*model.ReplyForExport, error) {
|
||||
posts := []*model.ReplyForExport{}
|
||||
err := s.GetSearchReplicaX().Select(&posts, `
|
||||
SELECT
|
||||
Posts.*,
|
||||
Users.Username as Username
|
||||
FROM
|
||||
Posts
|
||||
INNER JOIN
|
||||
Users ON Posts.UserId = Users.Id
|
||||
WHERE
|
||||
Posts.RootId = ?
|
||||
AND Posts.DeleteAt = 0
|
||||
ORDER BY
|
||||
Posts.Id`, rootId)
|
||||
aggFn := "COALESCE(json_agg(u1.username) FILTER (WHERE u1.username IS NOT NULL), '[]')"
|
||||
if s.DriverName() == model.DatabaseDriverMysql {
|
||||
aggFn = "IF (COUNT(u1.Username) = 0, JSON_ARRAY(), JSON_ARRAYAGG(u1.Username))"
|
||||
}
|
||||
result := []*model.ReplyForExport{}
|
||||
|
||||
qb := s.getQueryBuilder().Select(fmt.Sprintf("Posts.*, u2.Username as Username, %s as FlaggedBy", aggFn)).
|
||||
From("Posts").
|
||||
LeftJoin("Preferences ON Posts.Id = Preferences.Name").
|
||||
LeftJoin("Users u1 ON Preferences.UserId = u1.Id").
|
||||
InnerJoin("Users u2 ON Posts.UserId = u2.Id").
|
||||
Where(sq.And{sq.Eq{"Posts.RootId": rootId}, sq.Eq{"Posts.DeleteAt": 0}}).
|
||||
GroupBy("Posts.Id, u2.Username").
|
||||
OrderBy("Posts.Id")
|
||||
|
||||
query, args, err := qb.ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "postsForExport_toSql")
|
||||
}
|
||||
|
||||
err = s.GetSearchReplicaX().Select(&result, query, args...)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "failed to find Posts")
|
||||
}
|
||||
|
||||
return posts, nil
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (s *SqlPostStore) GetDirectPostParentsForExportAfter(limit int, afterId string, includeArchivedChannels bool) ([]*model.DirectPostForExport, error) {
|
||||
aggFn := "COALESCE(json_agg(u1.username) FILTER (WHERE u1.username IS NOT NULL), '[]')"
|
||||
if s.DriverName() == model.DatabaseDriverMysql {
|
||||
aggFn = "IF (COUNT(u1.Username) = 0, JSON_ARRAY(), JSON_ARRAYAGG(u1.Username))"
|
||||
}
|
||||
result := []*model.DirectPostForExport{}
|
||||
|
||||
query := s.getQueryBuilder().
|
||||
Select("p.*", "Users.Username as User").
|
||||
Select(fmt.Sprintf("p.*, u2.Username as User, %s as FlaggedBy", aggFn)).
|
||||
From("Posts p").
|
||||
LeftJoin("Preferences ON p.Id = Preferences.Name").
|
||||
LeftJoin("Users u1 ON Preferences.UserId = u1.Id").
|
||||
Join("Channels ON p.ChannelId = Channels.Id").
|
||||
Join("Users ON p.UserId = Users.Id").
|
||||
Join("Users u2 ON p.UserId = u2.Id").
|
||||
Where(sq.And{
|
||||
sq.Gt{"p.Id": afterId},
|
||||
sq.Eq{"p.RootId": ""},
|
||||
sq.Eq{"p.DeleteAt": 0},
|
||||
sq.Eq{"Channels.Type": []model.ChannelType{model.ChannelTypeDirect, model.ChannelTypeGroup}},
|
||||
}).
|
||||
GroupBy("p.Id, u2.Username").
|
||||
OrderBy("p.Id").
|
||||
Limit(uint64(limit))
|
||||
|
||||
@@ -2739,13 +2773,12 @@ func (s *SqlPostStore) GetDirectPostParentsForExportAfter(limit int, afterId str
|
||||
return nil, errors.Wrap(err, "post_tosql")
|
||||
}
|
||||
|
||||
posts := []*model.DirectPostForExport{}
|
||||
if err2 := s.GetReplicaX().Select(&posts, queryString, args...); err2 != nil {
|
||||
if err2 := s.GetReplicaX().Select(&result, queryString, args...); err2 != nil {
|
||||
return nil, errors.Wrap(err2, "failed to find Posts")
|
||||
}
|
||||
var channelIds []string
|
||||
for _, post := range posts {
|
||||
channelIds = append(channelIds, post.ChannelId)
|
||||
for _, p := range result {
|
||||
channelIds = append(channelIds, p.ChannelId)
|
||||
}
|
||||
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").
|
||||
@@ -2761,13 +2794,13 @@ func (s *SqlPostStore) GetDirectPostParentsForExportAfter(limit int, afterId str
|
||||
}
|
||||
|
||||
channelMembers := []*model.ChannelMemberForExport{}
|
||||
if err := s.GetReplicaX().Select(&channelMembers, queryString, args...); err != nil {
|
||||
if err = s.GetReplicaX().Select(&channelMembers, queryString, args...); err != nil {
|
||||
return nil, errors.Wrap(err, "failed to find ChannelMembers")
|
||||
}
|
||||
|
||||
// Build a map of channels and their posts
|
||||
postsChannelMap := make(map[string][]*model.DirectPostForExport)
|
||||
for _, post := range posts {
|
||||
for _, post := range result {
|
||||
post.ChannelMembers = &[]string{}
|
||||
postsChannelMap[post.ChannelId] = append(postsChannelMap[post.ChannelId], post)
|
||||
}
|
||||
@@ -2784,7 +2817,8 @@ func (s *SqlPostStore) GetDirectPostParentsForExportAfter(limit int, afterId str
|
||||
*post.ChannelMembers = channelMembersMap[channelId]
|
||||
}
|
||||
}
|
||||
return posts, nil
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
//nolint:unparam
|
||||
|
||||
@@ -4349,6 +4349,28 @@ func testPostStoreGetParentsForExportAfter(t *testing.T, rctx request.CTX, ss st
|
||||
}
|
||||
assert.True(t, found)
|
||||
})
|
||||
|
||||
t.Run("with flagged post", func(t *testing.T) {
|
||||
err := ss.Preference().Save(model.Preferences([]model.Preference{
|
||||
{
|
||||
UserId: u1.Id,
|
||||
Category: model.PreferenceCategoryFlaggedPost,
|
||||
Name: p1.Id,
|
||||
Value: "true",
|
||||
},
|
||||
}))
|
||||
require.NoError(t, err)
|
||||
|
||||
posts, err := ss.Post().GetParentsForExportAfter(10000, strings.Repeat("0", 26), false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
for _, p := range posts {
|
||||
if p.Id == p1.Id {
|
||||
require.NotNil(t, p.FlaggedBy)
|
||||
assert.Equal(t, model.StringArray([]string{u1.Username}), p.FlaggedBy)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func testPostStoreGetRepliesForExport(t *testing.T, rctx request.CTX, ss store.Store) {
|
||||
@@ -4394,7 +4416,7 @@ func testPostStoreGetRepliesForExport(t *testing.T, rctx request.CTX, ss store.S
|
||||
r1, err := ss.Post().GetRepliesForExport(p1.Id)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Len(t, r1, 1)
|
||||
require.Len(t, r1, 1)
|
||||
|
||||
reply1 := r1[0]
|
||||
assert.Equal(t, reply1.Id, p2.Id)
|
||||
@@ -4409,7 +4431,7 @@ func testPostStoreGetRepliesForExport(t *testing.T, rctx request.CTX, ss store.S
|
||||
r1, err = ss.Post().GetRepliesForExport(p1.Id)
|
||||
assert.NoError(t, err)
|
||||
|
||||
assert.Len(t, r1, 1)
|
||||
require.Len(t, r1, 1)
|
||||
|
||||
reply1 = r1[0]
|
||||
assert.Equal(t, reply1.Id, p2.Id)
|
||||
|
||||
Ссылка в новой задаче
Block a user