[MM-59367] export: enable exporting thread followers for CRT (#27623)

Этот коммит содержится в:
Ibrahim Serdar Acikgoz
2024-08-29 14:06:41 +02:00
коммит произвёл GitHub
родитель 3dc0e63c03
Коммит d5cc2eb2f6
17 изменённых файлов: 1430 добавлений и 63 удалений

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

@@ -1567,11 +1567,13 @@ func (a *App) importMultiplePostLines(rctx request.CTX, lines []imports.LineImpo
}
var (
postsWithData = []postAndData{}
postsForCreateList = []*model.Post{}
postsForCreateMap = map[string]int{}
postsForOverwriteList = []*model.Post{}
postsForOverwriteMap = map[string]int{}
postsWithData = []postAndData{}
postsForCreateList = []*model.Post{}
postsForCreateMap = map[string]int{}
postsForOverwriteList = []*model.Post{}
postsForOverwriteMap = map[string]int{}
threadMembersToCreateMap = map[string][]*model.ThreadMembership{}
threadMembersToOverwriteList = []*model.ThreadMembership{}
)
for _, line := range lines {
@@ -1615,6 +1617,18 @@ func (a *App) importMultiplePostLines(rctx request.CTX, lines []imports.LineImpo
if line.Post.IsPinned != nil {
post.IsPinned = *line.Post.IsPinned
}
if line.Post.ThreadFollowers != nil {
threadMemberships, lineNumber, err := a.extractThreadMembers(&line, users, post)
if err != nil {
return lineNumber, err
}
if post.Id == "" {
threadMembersToCreateMap[getPostStrID(post)] = threadMemberships
} else {
threadMembersToOverwriteList = append(threadMembersToOverwriteList, threadMemberships...)
}
}
fileIDs := a.uploadAttachments(rctx, line.Post.Attachments, post, team.Id, extractContent)
for _, fileID := range post.FileIds {
@@ -1634,11 +1648,13 @@ func (a *App) importMultiplePostLines(rctx request.CTX, lines []imports.LineImpo
postsForOverwriteList = append(postsForOverwriteList, post)
postsForOverwriteMap[getPostStrID(post)] = line.LineNumber
}
// Tip: the post ID is getting populated after the post is saved, if it's a new post. Otherwise, it's already set.
postsWithData = append(postsWithData, postAndData{post: post, postData: line.Post, team: team, lineNumber: line.LineNumber})
}
if len(postsForCreateList) > 0 {
if _, idx, nErr := a.Srv().Store().Post().SaveMultiple(postsForCreateList); nErr != nil {
_, idx, nErr := a.Srv().Store().Post().SaveMultiple(postsForCreateList)
if nErr != nil {
var appErr *model.AppError
var invErr *store.ErrInvalidInput
var retErr *model.AppError
@@ -1659,6 +1675,36 @@ func (a *App) importMultiplePostLines(rctx request.CTX, lines []imports.LineImpo
}
return 0, retErr
}
var membersToCreate []*model.ThreadMembership
for _, post := range postsForCreateList {
members, ok := threadMembersToCreateMap[getPostStrID(post)]
if !ok {
continue
}
for _, member := range members {
if post.Id == "" {
appErr := model.NewAppError("importMultiplePostLines", "app.post.save.thread_membership.app_error", nil, "", http.StatusInternalServerError).Wrap(errors.New("post id cannot be empty"))
if lineNumber, ok := postsForCreateMap[getPostStrID(post)]; ok {
return lineNumber, appErr
}
return 0, appErr
}
member.PostId = post.Id
}
membersToCreate = append(membersToCreate, members...)
}
// we have an assumption here is that all these memberships should be brand new because the corresponding posts
// do not exist in the target until the import.
if _, err := a.Srv().Store().Thread().SaveMultipleMemberships(membersToCreate); err != nil {
// we don't know the line number of the post that caused the error
// so we return 0. But at this stage, it's unlikely to receive an error
// due to the thread member itself, most likely it's due to the DB connection etc.
return 0, model.NewAppError("importMultiplePostLines", "app.post.save.thread_membership.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
}
if _, idx, err := a.Srv().Store().Post().OverwriteMultiple(postsForOverwriteList); err != nil {
@@ -1671,6 +1717,15 @@ func (a *App) importMultiplePostLines(rctx request.CTX, lines []imports.LineImpo
return 0, model.NewAppError("importMultiplePostLines", "app.post.overwrite.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
// Update thread memberships for posts that were overwritten. Here some of the memberships
// can be brand new, needs to be updated or an older membership should not get updated.
// MaintainMembership method has some logic within to handle those decisions. Unfortunately
// some application code leaked to the store layer here, which should be revisited when there
// is resource (eg. time, human or maybe AI).
if _, sErr := a.Srv().Store().Thread().MaintainMultipleFromImport(threadMembersToOverwriteList); sErr != nil {
return 0, model.NewAppError("importMultiplePostLines", "app.post.save.thread_membership.app_error", nil, "", http.StatusInternalServerError).Wrap(sErr)
}
for _, postWithData := range postsWithData {
postWithData := postWithData
if postWithData.postData.FlaggedBy != nil {
@@ -2008,11 +2063,13 @@ func (a *App) importMultipleDirectPostLines(rctx request.CTX, lines []imports.Li
}
var (
postsWithData = []postAndData{}
postsForCreateList = []*model.Post{}
postsForCreateMap = map[string]int{}
postsForOverwriteList = []*model.Post{}
postsForOverwriteMap = map[string]int{}
postsWithData = []postAndData{}
postsForCreateList = []*model.Post{}
postsForCreateMap = map[string]int{}
postsForOverwriteList = []*model.Post{}
postsForOverwriteMap = map[string]int{}
threadMembersToCreateMap = map[string][]*model.ThreadMembership{}
threadMembersToOverwriteList = []*model.ThreadMembership{}
)
for _, line := range lines {
@@ -2077,6 +2134,18 @@ func (a *App) importMultipleDirectPostLines(rctx request.CTX, lines []imports.Li
if line.DirectPost.IsPinned != nil {
post.IsPinned = *line.DirectPost.IsPinned
}
if line.DirectPost.ThreadFollowers != nil {
threadMemberships, lineNumber, err := a.extractThreadMembers(&line, users, post)
if err != nil {
return lineNumber, err
}
if post.Id == "" {
threadMembersToCreateMap[getPostStrID(post)] = threadMemberships
} else {
threadMembersToOverwriteList = append(threadMembersToOverwriteList, threadMemberships...)
}
}
fileIDs := a.uploadAttachments(rctx, line.DirectPost.Attachments, post, "noteam", extractContent)
for _, fileID := range post.FileIds {
@@ -2121,7 +2190,33 @@ func (a *App) importMultipleDirectPostLines(rctx request.CTX, lines []imports.Li
}
return 0, retErr
}
var membersToCreate []*model.ThreadMembership
for _, post := range postsForCreateList {
members, ok := threadMembersToCreateMap[getPostStrID(post)]
if !ok {
continue
}
for _, member := range members {
if post.Id == "" {
appErr := model.NewAppError("importMultiplePostLines", "app.post.save.thread_membership.app_error", nil, "", http.StatusInternalServerError).Wrap(errors.New("post id cannot be empty"))
if lineNumber, ok := postsForCreateMap[getPostStrID(post)]; ok {
return lineNumber, appErr
}
return 0, appErr
}
member.PostId = post.Id
}
membersToCreate = append(membersToCreate, members...)
}
if _, err := a.Srv().Store().Thread().SaveMultipleMemberships(membersToCreate); err != nil {
return 0, model.NewAppError("importMultiplePostLines", "app.post.save.thread_membership.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
}
if _, idx, err := a.Srv().Store().Post().OverwriteMultiple(postsForOverwriteList); err != nil {
if idx != -1 && idx < len(postsForOverwriteList) {
post := postsForOverwriteList[idx]
@@ -2132,6 +2227,10 @@ func (a *App) importMultipleDirectPostLines(rctx request.CTX, lines []imports.Li
return 0, model.NewAppError("importMultiplePostLines", "app.post.overwrite.app_error", nil, "", http.StatusInternalServerError).Wrap(err)
}
if _, sErr := a.Srv().Store().Thread().MaintainMultipleFromImport(threadMembersToOverwriteList); sErr != nil {
return 0, model.NewAppError("importMultiplePostLines", "app.post.save.thread_membership.app_error", nil, "", http.StatusInternalServerError).Wrap(sErr)
}
for _, postWithData := range postsWithData {
if postWithData.directPostData.FlaggedBy != nil {
var preferences model.Preferences
@@ -2240,3 +2339,48 @@ func (a *App) importEmoji(rctx request.CTX, data *imports.EmojiImportData, dryRu
return nil
}
func (a *App) extractThreadMembers(line *imports.LineImportWorkerData, users map[string]*model.User, post *model.Post) ([]*model.ThreadMembership, int, *model.AppError) {
threadMemberships := []*model.ThreadMembership{}
var importedFollowers []imports.ThreadFollowerImportData
if line.Post != nil {
importedFollowers = *line.Post.ThreadFollowers
} else if line.DirectPost != nil {
importedFollowers = *line.DirectPost.ThreadFollowers
}
participants := make([]*model.User, len(importedFollowers))
for i, member := range importedFollowers {
user, ok := users[strings.ToLower(*member.User)]
if !ok {
// maybe it's a user on target instance but not in the import data.
// This is a rare case, but we need to or can to handle it.
// alternatively, we can continue and discard this follower as maybe they
// were deleted.
var uErr error
user, uErr = a.Srv().Store().User().GetByUsername(*member.User)
if uErr != nil {
return nil, line.LineNumber, model.NewAppError("importMultiplePostLines", "app.import.get_users_by_username.some_users_not_found.error", nil, "", http.StatusBadRequest).Wrap(uErr)
}
}
membership := &model.ThreadMembership{
PostId: post.Id, // empty if it's a new post, will set later while inserting to the DB.
UserId: user.Id,
Following: true,
}
if member.LastViewed != nil {
membership.LastViewed = *member.LastViewed
}
if member.UnreadMentions != nil {
membership.UnreadMentions = *member.UnreadMentions
}
// We only need the user ID to update the thread.
participants[i] = &model.User{Id: user.Id}
threadMemberships = append(threadMemberships, membership)
}
post.Participants = participants
return threadMemberships, 0, nil
}