diff --git a/server/channels/app/export.go b/server/channels/app/export.go index 311bcc7014..a62facc9db 100644 --- a/server/channels/app/export.go +++ b/server/channels/app/export.go @@ -815,7 +815,7 @@ func (a *App) exportAllDirectChannels(ctx request.CTX, job *model.Job, writer io afterId = channel.Id // Skip if there are no active members in the channel - if len(*channel.Members) == 0 { + if len(channel.Members) == 0 { continue } @@ -829,7 +829,12 @@ func (a *App) exportAllDirectChannels(ctx request.CTX, job *model.Job, writer io return err } - channelLine := ImportLineFromDirectChannel(channel, favoritedBy) + shownBy, err := a.buildShownByList(channel) + if err != nil { + return err + } + + channelLine := ImportLineFromDirectChannel(channel, favoritedBy, shownBy) if err := a.exportWriteLine(writer, channelLine); err != nil { return err } @@ -862,6 +867,55 @@ func (a *App) buildFavoritedByList(channelID string) ([]string, *model.AppError) return userIDs, nil } +func (a *App) buildShownByList(channel *model.DirectChannelForExport) ([]string, *model.AppError) { + shownBy := make([]string, 0) + switch channel.Type { + case model.ChannelTypeGroup: + for _, member := range channel.Members { + prefs, err := a.Srv().Store().Preference().GetCategory(member.UserId, model.PreferenceCategoryGroupChannelShow) + if err != nil { + return nil, model.NewAppError("buildShownByList", "app.preference.get_category.app_error", nil, "", http.StatusInternalServerError).Wrap(err) + } + + for i := range prefs { + if prefs[i].Name == channel.Id && prefs[i].Value == "true" { + user, err := a.Srv().Store().User().Get(context.Background(), member.UserId) + if err != nil { + return nil, model.NewAppError("buildShownByList", "app.user.get.app_error", nil, "", http.StatusInternalServerError).Wrap(err) + } + + shownBy = append(shownBy, user.Username) + } + } + } + case model.ChannelTypeDirect: + for i, member := range channel.Members { + otherMember := member // in case it's a channel with self + if len(channel.Members) == 2 { + // since the are only two members, the other member is should be the remainder of i+1/2 + otherMember = channel.Members[(i+1)%2] + } + prefs, err := a.Srv().Store().Preference().GetCategoryAndName(model.PreferenceCategoryDirectChannelShow, otherMember.UserId) + if err != nil { + return nil, model.NewAppError("buildShownByList", "app.preference.get_category.app_error", nil, "", http.StatusInternalServerError).Wrap(err) + } + + for _, pref := range prefs { + if pref.Value == "true" && pref.UserId == member.UserId { + user, err := a.Srv().Store().User().Get(context.Background(), member.UserId) + if err != nil { + return nil, model.NewAppError("buildShownByList", "app.user.get.app_error", nil, "", http.StatusInternalServerError).Wrap(err) + } + + shownBy = append(shownBy, user.Username) + } + } + } + } + + return shownBy, nil +} + func (a *App) exportAllDirectPosts(ctx request.CTX, job *model.Job, writer io.Writer, withAttachments, includeArchivedChannels bool) ([]imports.AttachmentImportData, *model.AppError) { var attachments []imports.AttachmentImportData afterId := strings.Repeat("0", 26) diff --git a/server/channels/app/export_converters.go b/server/channels/app/export_converters.go index 9507679735..fe8f804893 100644 --- a/server/channels/app/export_converters.go +++ b/server/channels/app/export_converters.go @@ -40,17 +40,17 @@ func ImportLineFromChannel(channel *model.ChannelForExport) *imports.LineImportD } } -func ImportLineFromDirectChannel(channel *model.DirectChannelForExport, favoritedBy []string) *imports.LineImportData { - channelMembers := *channel.Members +func ImportLineFromDirectChannel(channel *model.DirectChannelForExport, favoritedBy, shownBy []string) *imports.LineImportData { + channelMembers := channel.Members if len(channelMembers) == 1 { - channelMembers = []string{channelMembers[0], channelMembers[0]} + channelMembers = []*model.ChannelMemberForExport{channelMembers[0], channelMembers[0]} } line := &imports.LineImportData{ Type: "direct_channel", DirectChannel: &imports.DirectChannelImportData{ - Header: &channel.Header, - Members: &channelMembers, + Header: &channel.Header, + Participants: importDirectChannelMembersFromChannelMembers(channelMembers), }, } @@ -58,9 +58,82 @@ func ImportLineFromDirectChannel(channel *model.DirectChannelForExport, favorite line.DirectChannel.FavoritedBy = &favoritedBy } + if len(shownBy) != 0 { + line.DirectChannel.ShownBy = &shownBy + } + return line } +func importDirectChannelMembersFromChannelMembers(members []*model.ChannelMemberForExport) []*imports.DirectChannelMemberImportData { + importedMembers := make([]*imports.DirectChannelMemberImportData, len(members)) + for i, member := range members { + props := member.NotifyProps + notifyProps := imports.UserChannelNotifyPropsImportData{} + + desktop, exist := props[model.DesktopNotifyProp] + if exist { + notifyProps.Desktop = &desktop + } + mobile, exist := props[model.PushNotifyProp] + if exist { + notifyProps.Mobile = &mobile + } + email, exist := props[model.EmailNotifyProp] + if exist { + notifyProps.Email = &email + } + ignoreMentions, exist := props[model.IgnoreChannelMentionsNotifyProp] + if exist { + notifyProps.IgnoreChannelMentions = &ignoreMentions + } + channelAutoFallow, exist := props[model.ChannelAutoFollowThreads] + if exist { + notifyProps.ChannelAutoFollowThreads = &channelAutoFallow + } + markUnread, exist := props[model.MarkUnreadNotifyProp] + if exist { + notifyProps.MarkUnread = &markUnread + } + + dcm := &imports.DirectChannelMemberImportData{ + Username: &member.Username, + NotifyProps: ¬ifyProps, + } + + if member.SchemeUser { + dcm.SchemeUser = &member.SchemeUser + } + if member.SchemeAdmin { + dcm.SchemeAdmin = &member.SchemeAdmin + } + if member.SchemeGuest { + dcm.SchemeGuest = &member.SchemeGuest + } + if member.LastViewedAt != 0 { + dcm.LastViewedAt = &member.LastViewedAt + } + if member.MentionCount != 0 { + dcm.MentionCount = &member.MentionCount + } + if member.MentionCountRoot != 0 { + dcm.MentionCountRoot = &member.MentionCountRoot + } + if member.MsgCount != 0 { + dcm.MsgCount = &member.MsgCount + } + if member.MsgCountRoot != 0 { + dcm.MsgCountRoot = &member.MsgCountRoot + } + if member.UrgentMentionCount != 0 { + dcm.UrgentMentionCount = &member.UrgentMentionCount + } + + importedMembers[i] = dcm + } + return importedMembers +} + func ImportLineFromUser(user *model.User, exportedPrefs map[string]*string) *imports.LineImportData { // Bulk Importer doesn't accept "empty string" for AuthService. var authService *string diff --git a/server/channels/app/export_test.go b/server/channels/app/export_test.go index 112575bab7..d9328ecded 100644 --- a/server/channels/app/export_test.go +++ b/server/channels/app/export_test.go @@ -269,8 +269,9 @@ func TestExportDMChannel(t *testing.T) { // Ensure the Members of the imported DM channel is the same was from the exported channels, nErr = th2.App.Srv().Store().Channel().GetAllDirectChannelsForExportAfter(1000, "00000000", false) require.NoError(t, nErr) - require.Equal(t, 1, len(channels)) - assert.ElementsMatch(t, []string{th1.BasicUser.Username, th1.BasicUser2.Username}, *channels[0].Members) + require.Len(t, channels, 1) + require.Len(t, channels[0].Members, 2) + assert.ElementsMatch(t, []string{th1.BasicUser.Username, th1.BasicUser2.Username}, []string{channels[0].Members[0].Username, channels[0].Members[1].Username}) // Ensure the favorited channel was retained fav, nErr := th2.App.Srv().Store().Preference().Get(th2.BasicUser2.Id, model.PreferenceCategoryFavoriteChannel, channels[0].Id) @@ -340,8 +341,8 @@ func TestExportDMChannelToSelf(t *testing.T) { channels, nErr = th2.App.Srv().Store().Channel().GetAllDirectChannelsForExportAfter(1000, "00000000", false) require.NoError(t, nErr) assert.Equal(t, 1, len(channels)) - assert.Equal(t, 1, len((*channels[0].Members))) - assert.Equal(t, th1.BasicUser.Username, (*channels[0].Members)[0]) + assert.Equal(t, 1, len((channels[0].Members))) + assert.Equal(t, th1.BasicUser.Username, channels[0].Members[0].Username) } func TestExportGMChannel(t *testing.T) { @@ -416,8 +417,8 @@ func TestExportGMandDMChannels(t *testing.T) { // Adding some determinism so its possible to assert on slice index sort.Slice(channels, func(i, j int) bool { return channels[i].Type > channels[j].Type }) assert.Equal(t, 2, len(channels)) - assert.ElementsMatch(t, []string{th1.BasicUser.Username, user1.Username, user2.Username}, *channels[0].Members) - assert.ElementsMatch(t, []string{th1.BasicUser.Username, th1.BasicUser2.Username}, *channels[1].Members) + assert.ElementsMatch(t, []string{th1.BasicUser.Username, user1.Username, user2.Username}, []string{channels[0].Members[0].Username, channels[0].Members[1].Username, channels[0].Members[2].Username}) + assert.ElementsMatch(t, []string{th1.BasicUser.Username, th1.BasicUser2.Username}, []string{channels[1].Members[0].Username, channels[1].Members[1].Username}) } func TestExportDMandGMPost(t *testing.T) { diff --git a/server/channels/app/import_functions.go b/server/channels/app/import_functions.go index 8089f50ceb..29544ea01e 100644 --- a/server/channels/app/import_functions.go +++ b/server/channels/app/import_functions.go @@ -1758,40 +1758,187 @@ func (a *App) importDirectChannel(rctx request.CTX, data *imports.DirectChannelI return nil } + var members []string + if data.Participants != nil { + members = make([]string, len(data.Participants)) + for i, member := range data.Participants { + members[i] = *member.Username + } + } else if data.Members != nil { + members = make([]string, len(*data.Members)) + copy(members, *data.Members) + } else { + return model.NewAppError("BulkImport", "app.import.import_direct_channel.no_members.error", nil, "", http.StatusBadRequest) + } + var userIDs []string - userMap, err := a.getUsersByUsernames(*data.Members) + userMap, err := a.getUsersByUsernames(members) if err != nil { return err } - for _, user := range *data.Members { + for _, user := range members { userIDs = append(userIDs, userMap[strings.ToLower(user)].Id) } var channel *model.Channel if len(userIDs) == 2 { - ch, err := a.createDirectChannel(rctx, userIDs[0], userIDs[1]) - if err != nil && err.Id != store.ChannelExistsError { - return model.NewAppError("BulkImport", "app.import.import_direct_channel.create_direct_channel.error", nil, "", http.StatusBadRequest).Wrap(err) + ch, err2 := a.createDirectChannel(rctx, userIDs[0], userIDs[1]) + if err2 != nil && err2.Id != store.ChannelExistsError { + return model.NewAppError("BulkImport", "app.import.import_direct_channel.create_direct_channel.error", nil, "", http.StatusBadRequest).Wrap(err2) } channel = ch } else { - ch, err := a.createGroupChannel(rctx, userIDs) - if err != nil && err.Id != store.ChannelExistsError { - return model.NewAppError("BulkImport", "app.import.import_direct_channel.create_group_channel.error", nil, "", http.StatusBadRequest).Wrap(err) + ch, err2 := a.createGroupChannel(rctx, userIDs) + if err2 != nil && err2.Id != store.ChannelExistsError { + return model.NewAppError("BulkImport", "app.import.import_direct_channel.create_group_channel.error", nil, "", http.StatusBadRequest).Wrap(err2) } channel = ch } + totalMembers, err := a.GetChannelMemberCount(rctx, channel.Id) + if err != nil { + return model.NewAppError("BulkImport", "app.import.import_direct_channel.get_channel_members.error", nil, "", http.StatusBadRequest).Wrap(err) + } + + var ems = make([]model.ChannelMember, 0, totalMembers) + var page int + + for int64(len(ems)) < totalMembers { + res, err := a.GetChannelMembersPage(rctx, channel.Id, page, 100) + if err != nil { + return model.NewAppError("BulkImport", "app.import.import_direct_channel.get_channel_members.error", nil, "", http.StatusBadRequest).Wrap(err) + } + ems = append(ems, res...) + page++ + } + + existingMembers := make(map[string]model.ChannelMember) + for _, member := range ems { + existingMembers[member.UserId] = member + } + + newChannelMembers := make([]*model.ChannelMember, 0) + for _, member := range data.Participants { + m := &model.ChannelMember{ + NotifyProps: model.GetDefaultChannelNotifyProps(), + } + if member.LastViewedAt != nil { + m.LastViewedAt = *member.LastViewedAt + } + if member.MsgCount != nil { + m.MsgCount = *member.MsgCount + } + if member.MentionCount != nil { + m.MentionCount = *member.MentionCount + } + if member.MentionCountRoot != nil { + m.MentionCountRoot = *member.MentionCountRoot + } + if member.UrgentMentionCount != nil { + m.UrgentMentionCount = *member.UrgentMentionCount + } + if member.MsgCountRoot != nil { + m.MsgCountRoot = *member.MsgCountRoot + } + if member.SchemeUser != nil { + m.SchemeUser = *member.SchemeUser + } + if member.SchemeAdmin != nil { + m.SchemeAdmin = *member.SchemeAdmin + } + if member.SchemeGuest != nil { + m.SchemeGuest = *member.SchemeGuest + } + + if member.NotifyProps != nil { + if member.NotifyProps.Desktop != nil { + if value, ok := m.NotifyProps[model.DesktopNotifyProp]; !ok || value != *member.NotifyProps.Desktop { + m.NotifyProps[model.DesktopNotifyProp] = *member.NotifyProps.Desktop + } + } + + if member.NotifyProps.MarkUnread != nil { + if value, ok := m.NotifyProps[model.DesktopSoundNotifyProp]; !ok || value != *member.NotifyProps.MarkUnread { + m.NotifyProps[model.MarkUnreadNotifyProp] = *member.NotifyProps.MarkUnread + } + } + + if member.NotifyProps.Mobile != nil { + if value, ok := m.NotifyProps[model.PushNotifyProp]; !ok || value != *member.NotifyProps.Mobile { + m.NotifyProps[model.PushNotifyProp] = *member.NotifyProps.Mobile + } + } + + if member.NotifyProps.Email != nil { + if value, ok := m.NotifyProps[model.EmailNotifyProp]; !ok || value != *member.NotifyProps.Email { + m.NotifyProps[model.EmailNotifyProp] = *member.NotifyProps.Email + } + } + + if member.NotifyProps.IgnoreChannelMentions != nil { + if value, ok := m.NotifyProps[model.IgnoreChannelMentionsNotifyProp]; !ok || value != *member.NotifyProps.IgnoreChannelMentions { + m.NotifyProps[model.IgnoreChannelMentionsNotifyProp] = *member.NotifyProps.IgnoreChannelMentions + } + } + + if member.NotifyProps.ChannelAutoFollowThreads != nil { + if value, ok := m.NotifyProps[model.ChannelAutoFollowThreads]; !ok || value != *member.NotifyProps.ChannelAutoFollowThreads { + m.NotifyProps[model.ChannelAutoFollowThreads] = *member.NotifyProps.ChannelAutoFollowThreads + } + } + } + + u := userMap[strings.ToLower(*member.Username)] + if existing, ok := existingMembers[u.Id]; ok { + // Decide which membership is newer. We have LastViewedAt in the import data, which should + // give us a good idea of which membership is newer. + if existing.LastViewedAt > m.LastViewedAt { + continue + } + } + m.UserId = u.Id + m.ChannelId = channel.Id + newChannelMembers = append(newChannelMembers, m) + } + + // the channel memberships are already created in the channel creation + // we always going to update the channel memberships + if len(newChannelMembers) > 0 { + _, nErr := a.Srv().Store().Channel().UpdateMultipleMembers(newChannelMembers) + if nErr != nil { + return model.NewAppError("BulkImport", "app.import.import_direct_channel.create_group_channel.error", nil, "", http.StatusBadRequest).Wrap(nErr) + } + } + var preferences model.Preferences - for _, userID := range userIDs { - preferences = append(preferences, model.Preference{ - UserId: userID, - Category: model.PreferenceCategoryDirectChannelShow, - Name: channel.Id, - Value: "true", - }) + if data.ShownBy != nil { + for _, username := range *data.ShownBy { + switch channel.Type { + case model.ChannelTypeDirect: + otherUserId := userMap[strings.ToLower(username)].Id + for uname, user := range userMap { + if uname != username { + otherUserId = user.Id + break + } + } + preferences = append(preferences, model.Preference{ + UserId: userMap[strings.ToLower(username)].Id, + Category: model.PreferenceCategoryDirectChannelShow, + Name: otherUserId, + Value: "true", + }) + case model.ChannelTypeGroup: + preferences = append(preferences, model.Preference{ + UserId: userMap[strings.ToLower(username)].Id, + Category: model.PreferenceCategoryGroupChannelShow, + Name: channel.Id, + Value: "true", + }) + } + } } if data.FavoritedBy != nil { @@ -1805,14 +1952,16 @@ func (a *App) importDirectChannel(rctx request.CTX, data *imports.DirectChannelI } } - if err := a.Srv().Store().Preference().Save(preferences); err != nil { - var appErr *model.AppError - switch { - case errors.As(err, &appErr): - appErr.StatusCode = http.StatusBadRequest - return appErr - default: - return model.NewAppError("importDirectChannel", "app.preference.save.updating.app_error", nil, "", http.StatusBadRequest).Wrap(err) + if len(preferences) > 0 { + if err := a.Srv().Store().Preference().Save(preferences); err != nil { + var appErr *model.AppError + switch { + case errors.As(err, &appErr): + appErr.StatusCode = http.StatusBadRequest + return appErr + default: + return model.NewAppError("importDirectChannel", "app.preference.save.updating.app_error", nil, "", http.StatusBadRequest).Wrap(err) + } } } diff --git a/server/channels/app/import_functions_test.go b/server/channels/app/import_functions_test.go index 488a21b586..149b1a6a80 100644 --- a/server/channels/app/import_functions_test.go +++ b/server/channels/app/import_functions_test.go @@ -3161,6 +3161,7 @@ func TestImportImportPost(t *testing.T) { func TestImportImportDirectChannel(t *testing.T) { th := Setup(t).InitBasic() defer th.TearDown() + user3 := th.CreateUser() // Check how many channels are in the database. directChannelCount, err := th.App.Srv().Store().Channel().AnalyticsTypeCount("", model.ChannelTypeDirect) @@ -3169,161 +3170,363 @@ func TestImportImportDirectChannel(t *testing.T) { groupChannelCount, err := th.App.Srv().Store().Channel().AnalyticsTypeCount("", model.ChannelTypeGroup) require.NoError(t, err, "Failed to get group channel count.") - // Do an invalid channel in dry-run mode. - data := imports.DirectChannelImportData{ - Members: &[]string{ - model.NewId(), - }, - Header: ptrStr("Channel Header"), + // We need to generate the dataset twice to test the same data with different formats. + generateDataset := func(data imports.DirectChannelImportData) map[string]imports.DirectChannelImportData { + members := make([]string, len(data.Participants)) + for i, member := range data.Participants { + members[i] = *member.Username + } + + return map[string]imports.DirectChannelImportData{ + "Participants": data, + "Members": { + Members: &members, + }, + } } - err = th.App.importDirectChannel(th.Context, &data, true) - require.Error(t, err) - // Check that no more channels are in the DB. - AssertChannelCount(t, th.App, model.ChannelTypeDirect, directChannelCount) - AssertChannelCount(t, th.App, model.ChannelTypeGroup, groupChannelCount) + t.Run("Invalid channel in dry-run mode", func(t *testing.T) { + dataset := generateDataset(imports.DirectChannelImportData{ + Participants: []*imports.DirectChannelMemberImportData{ + { + Username: model.NewString(model.NewId()), + }, + }, + Header: ptrStr("Channel Header"), + }) + for name, data := range dataset { + t.Run(name, func(t *testing.T) { + err = th.App.importDirectChannel(th.Context, &data, true) + require.Error(t, err) - // Do a valid DIRECT channel with a nonexistent member in dry-run mode. - data.Members = &[]string{ - model.NewId(), - model.NewId(), - } - appErr := th.App.importDirectChannel(th.Context, &data, true) - require.Nil(t, appErr) + // Check that no more channels are in the DB. + AssertChannelCount(t, th.App, model.ChannelTypeDirect, directChannelCount) + AssertChannelCount(t, th.App, model.ChannelTypeGroup, groupChannelCount) + }) + } + }) - // Check that no more channels are in the DB. - AssertChannelCount(t, th.App, model.ChannelTypeDirect, directChannelCount) - AssertChannelCount(t, th.App, model.ChannelTypeGroup, groupChannelCount) + t.Run("Valid DIRECT channel with a nonexistent member in dry-run mode", func(t *testing.T) { + dataset := generateDataset(imports.DirectChannelImportData{ + Participants: []*imports.DirectChannelMemberImportData{ + { + Username: model.NewString(model.NewId()), + }, + { + Username: model.NewString(model.NewId()), + }, + }, + }) + for name, data := range dataset { + t.Run(name, func(t *testing.T) { + appErr := th.App.importDirectChannel(th.Context, &data, true) + require.Nil(t, appErr) - // Do a valid GROUP channel with a nonexistent member in dry-run mode. - data.Members = &[]string{ - model.NewId(), - model.NewId(), - model.NewId(), - } - appErr = th.App.importDirectChannel(th.Context, &data, true) - require.Nil(t, appErr) + // Check that no more channels are in the DB. + AssertChannelCount(t, th.App, model.ChannelTypeDirect, directChannelCount) + AssertChannelCount(t, th.App, model.ChannelTypeGroup, groupChannelCount) + }) + } + }) - // Check that no more channels are in the DB. - AssertChannelCount(t, th.App, model.ChannelTypeDirect, directChannelCount) - AssertChannelCount(t, th.App, model.ChannelTypeGroup, groupChannelCount) + t.Run("Valid GROUP channel with a nonexistent member in dry-run mode", func(t *testing.T) { + dataset := generateDataset(imports.DirectChannelImportData{ + Participants: []*imports.DirectChannelMemberImportData{ + { + Username: model.NewString(model.NewId()), + }, + { + Username: model.NewString(model.NewId()), + }, + { + Username: model.NewString(model.NewId()), + }, + }, + }) + for name, data := range dataset { + t.Run(name, func(t *testing.T) { + appErr := th.App.importDirectChannel(th.Context, &data, true) + require.Nil(t, appErr) - // Do an invalid channel in apply mode. - data.Members = &[]string{ - model.NewId(), - } - err = th.App.importDirectChannel(th.Context, &data, false) - require.Error(t, err) + // Check that no more channels are in the DB. + AssertChannelCount(t, th.App, model.ChannelTypeDirect, directChannelCount) + AssertChannelCount(t, th.App, model.ChannelTypeGroup, groupChannelCount) + }) + } + }) - // Check that no more channels are in the DB. - AssertChannelCount(t, th.App, model.ChannelTypeDirect, directChannelCount) - AssertChannelCount(t, th.App, model.ChannelTypeGroup, groupChannelCount) + t.Run("Invalid channel in apply mode", func(t *testing.T) { + dataset := generateDataset(imports.DirectChannelImportData{ + Participants: []*imports.DirectChannelMemberImportData{ + { + Username: model.NewString(model.NewId()), + }, + }, + }) + for name, data := range dataset { + t.Run(name, func(t *testing.T) { + err = th.App.importDirectChannel(th.Context, &data, false) + require.Error(t, err) - // Do a valid DIRECT channel. - data.Members = &[]string{ - th.BasicUser.Username, - th.BasicUser2.Username, - } - appErr = th.App.importDirectChannel(th.Context, &data, false) - require.Nil(t, appErr) + // Check that no more channels are in the DB. + AssertChannelCount(t, th.App, model.ChannelTypeDirect, directChannelCount) + AssertChannelCount(t, th.App, model.ChannelTypeGroup, groupChannelCount) + }) + } + }) - // Check that one more DIRECT channel is in the DB. - AssertChannelCount(t, th.App, model.ChannelTypeDirect, directChannelCount+1) - AssertChannelCount(t, th.App, model.ChannelTypeGroup, groupChannelCount) + t.Run("Valid DIRECT channel ", func(t *testing.T) { + dataset := generateDataset(imports.DirectChannelImportData{ + Participants: []*imports.DirectChannelMemberImportData{ + { + Username: model.NewString(th.BasicUser.Username), + }, + { + Username: model.NewString(th.BasicUser2.Username), + }, + }, + }) + for name, data := range dataset { + t.Run(name, func(t *testing.T) { + appErr := th.App.importDirectChannel(th.Context, &data, false) + require.Nil(t, appErr) - // Do the same DIRECT channel again. - appErr = th.App.importDirectChannel(th.Context, &data, false) - require.Nil(t, appErr) + // Check that one more DIRECT channel is in the DB. + AssertChannelCount(t, th.App, model.ChannelTypeDirect, directChannelCount+1) + AssertChannelCount(t, th.App, model.ChannelTypeGroup, groupChannelCount) - // Check that no more channels are in the DB. - AssertChannelCount(t, th.App, model.ChannelTypeDirect, directChannelCount+1) - AssertChannelCount(t, th.App, model.ChannelTypeGroup, groupChannelCount) + // Do the same DIRECT channel again. + appErr = th.App.importDirectChannel(th.Context, &data, false) + require.Nil(t, appErr) - // Update the channel's HEADER - data.Header = ptrStr("New Channel Header 2") - appErr = th.App.importDirectChannel(th.Context, &data, false) - require.Nil(t, appErr) + // Check that no more channels are in the DB. + AssertChannelCount(t, th.App, model.ChannelTypeDirect, directChannelCount+1) + AssertChannelCount(t, th.App, model.ChannelTypeGroup, groupChannelCount) - // Check that no more channels are in the DB. - AssertChannelCount(t, th.App, model.ChannelTypeDirect, directChannelCount+1) - AssertChannelCount(t, th.App, model.ChannelTypeGroup, groupChannelCount) + // Update the channel's HEADER + data.Header = ptrStr("New Channel Header 2") + appErr = th.App.importDirectChannel(th.Context, &data, false) + require.Nil(t, appErr) - // Get the channel to check that the header was updated. - channel, appErr := th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, th.BasicUser2.Id) - require.Nil(t, appErr) - require.Equal(t, channel.Header, *data.Header) + // Check that no more channels are in the DB. + AssertChannelCount(t, th.App, model.ChannelTypeDirect, directChannelCount+1) + AssertChannelCount(t, th.App, model.ChannelTypeGroup, groupChannelCount) - // Do a GROUP channel with an extra invalid member. - user3 := th.CreateUser() - data.Members = &[]string{ - th.BasicUser.Username, - th.BasicUser2.Username, - user3.Username, - model.NewId(), - } - appErr = th.App.importDirectChannel(th.Context, &data, false) - require.NotNil(t, appErr) + // Get the channel to check that the header was updated. + channel, appErr := th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, th.BasicUser2.Id) + require.Nil(t, appErr) + require.Equal(t, channel.Header, *data.Header) + }) + } + }) - // Check that no more channels are in the DB. - AssertChannelCount(t, th.App, model.ChannelTypeDirect, directChannelCount+1) - AssertChannelCount(t, th.App, model.ChannelTypeGroup, groupChannelCount) + t.Run("GROUP channel with an extra invalid member", func(t *testing.T) { + dataset := generateDataset(imports.DirectChannelImportData{ + Participants: []*imports.DirectChannelMemberImportData{ + { + Username: model.NewString(th.BasicUser.Username), + }, + { + Username: model.NewString(th.BasicUser2.Username), + }, + { + Username: model.NewString(user3.Username), + }, + { + Username: model.NewString(model.NewId()), + }, + }, + }) + for name, data := range dataset { + t.Run(name, func(t *testing.T) { + appErr := th.App.importDirectChannel(th.Context, &data, false) + require.NotNil(t, appErr) - // Do a valid GROUP channel. - data.Members = &[]string{ - th.BasicUser.Username, - th.BasicUser2.Username, - user3.Username, - } - appErr = th.App.importDirectChannel(th.Context, &data, false) - require.Nil(t, appErr) + // Check that no more channels are in the DB. + AssertChannelCount(t, th.App, model.ChannelTypeDirect, directChannelCount+1) + AssertChannelCount(t, th.App, model.ChannelTypeGroup, groupChannelCount) + }) + } + }) - // Check that one more GROUP channel is in the DB. - AssertChannelCount(t, th.App, model.ChannelTypeDirect, directChannelCount+1) - AssertChannelCount(t, th.App, model.ChannelTypeGroup, groupChannelCount+1) + t.Run("Valid GROUP channel", func(t *testing.T) { + dataset := generateDataset(imports.DirectChannelImportData{ + Participants: []*imports.DirectChannelMemberImportData{ + { + Username: model.NewString(th.BasicUser.Username), + }, + { + Username: model.NewString(th.BasicUser2.Username), + }, + { + Username: model.NewString(user3.Username), + }, + }, + }) + for name, data := range dataset { + t.Run(name, func(t *testing.T) { + appErr := th.App.importDirectChannel(th.Context, &data, false) + require.Nil(t, appErr) - // Do the same DIRECT channel again. - appErr = th.App.importDirectChannel(th.Context, &data, false) - require.Nil(t, appErr) + // Check that one more GROUP channel is in the DB. + AssertChannelCount(t, th.App, model.ChannelTypeDirect, directChannelCount+1) + AssertChannelCount(t, th.App, model.ChannelTypeGroup, groupChannelCount+1) - // Check that no more channels are in the DB. - AssertChannelCount(t, th.App, model.ChannelTypeDirect, directChannelCount+1) - AssertChannelCount(t, th.App, model.ChannelTypeGroup, groupChannelCount+1) + // Do the same DIRECT channel again. + appErr = th.App.importDirectChannel(th.Context, &data, false) + require.Nil(t, appErr) - // Update the channel's HEADER - data.Header = ptrStr("New Channel Header 3") - appErr = th.App.importDirectChannel(th.Context, &data, false) - require.Nil(t, appErr) + // Check that no more channels are in the DB. + AssertChannelCount(t, th.App, model.ChannelTypeDirect, directChannelCount+1) + AssertChannelCount(t, th.App, model.ChannelTypeGroup, groupChannelCount+1) - // Check that no more channels are in the DB. - AssertChannelCount(t, th.App, model.ChannelTypeDirect, directChannelCount+1) - AssertChannelCount(t, th.App, model.ChannelTypeGroup, groupChannelCount+1) + // Update the channel's HEADER + data.Header = ptrStr("New Channel Header 3") + appErr = th.App.importDirectChannel(th.Context, &data, false) + require.Nil(t, appErr) - // Get the channel to check that the header was updated. - userIDs := []string{ - th.BasicUser.Id, - th.BasicUser2.Id, - user3.Id, - } - channel, appErr = th.App.createGroupChannel(th.Context, userIDs) - require.Equal(t, appErr.Id, store.ChannelExistsError) - require.Equal(t, channel.Header, *data.Header) + // Check that no more channels are in the DB. + AssertChannelCount(t, th.App, model.ChannelTypeDirect, directChannelCount+1) + AssertChannelCount(t, th.App, model.ChannelTypeGroup, groupChannelCount+1) - // Import a channel with some favorites. - data.Members = &[]string{ - th.BasicUser.Username, - th.BasicUser2.Username, - } - data.FavoritedBy = &[]string{ - th.BasicUser.Username, - th.BasicUser2.Username, - } - appErr = th.App.importDirectChannel(th.Context, &data, false) - require.Nil(t, appErr) + // Get the channel to check that the header was updated. + userIDs := []string{ + th.BasicUser.Id, + th.BasicUser2.Id, + user3.Id, + } + channel, appErr := th.App.createGroupChannel(th.Context, userIDs) + require.Equal(t, appErr.Id, store.ChannelExistsError) + require.Equal(t, channel.Header, *data.Header) + }) + } + }) - channel, appErr = th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, th.BasicUser2.Id) - require.Nil(t, appErr) - checkPreference(t, th.App, th.BasicUser.Id, model.PreferenceCategoryFavoriteChannel, channel.Id, "true") - checkPreference(t, th.App, th.BasicUser2.Id, model.PreferenceCategoryFavoriteChannel, channel.Id, "true") + t.Run("Import a channel with some favorites", func(t *testing.T) { + dataset := generateDataset(imports.DirectChannelImportData{ + Participants: []*imports.DirectChannelMemberImportData{ + { + Username: model.NewString(th.BasicUser.Username), + }, + { + Username: model.NewString(th.BasicUser2.Username), + }, + }, + }) + for name, data := range dataset { + t.Run(name, func(t *testing.T) { + data.FavoritedBy = &[]string{ + th.BasicUser.Username, + th.BasicUser2.Username, + } + appErr := th.App.importDirectChannel(th.Context, &data, false) + require.Nil(t, appErr) + + channel, appErr := th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, th.BasicUser2.Id) + require.Nil(t, appErr) + checkPreference(t, th.App, th.BasicUser.Id, model.PreferenceCategoryFavoriteChannel, channel.Id, "true") + checkPreference(t, th.App, th.BasicUser2.Id, model.PreferenceCategoryFavoriteChannel, channel.Id, "true") + }) + } + }) + + t.Run("Import a DM channel and user last view should be imported", func(t *testing.T) { + lastView := model.GetMillis() + data := imports.DirectChannelImportData{ + Participants: []*imports.DirectChannelMemberImportData{ + { + Username: model.NewString(th.BasicUser.Username), + LastViewedAt: ptrInt64(lastView), + }, + { + Username: model.NewString(th.BasicUser2.Username), + }, + }, + } + + appErr := th.App.importDirectChannel(th.Context, &data, false) + require.Nil(t, appErr) + + channel, appErr := th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, th.BasicUser2.Id) + require.Nil(t, appErr) + + members, appErr := th.App.GetChannelMembersPage(th.Context, channel.Id, 0, 100) + require.Nil(t, appErr) + require.Len(t, members, 2) + + for _, member := range members { + if member.UserId == th.BasicUser.Id { + require.Equal(t, member.LastViewedAt, lastView) + } + } + }) + + t.Run("Import a DM channel and preserve if the channel was shown to users", func(t *testing.T) { + data := imports.DirectChannelImportData{ + Participants: []*imports.DirectChannelMemberImportData{ + { + Username: model.NewString(th.BasicUser.Username), + }, + { + Username: model.NewString(th.BasicUser2.Username), + }, + }, + ShownBy: &[]string{ + th.BasicUser.Username, + }, + } + + appErr := th.App.importDirectChannel(th.Context, &data, false) + require.Nil(t, appErr) + + channel, appErr := th.App.GetOrCreateDirectChannel(th.Context, th.BasicUser.Id, th.BasicUser2.Id) + require.Nil(t, appErr) + + members, appErr := th.App.GetChannelMembersPage(th.Context, channel.Id, 0, 100) + require.Nil(t, appErr) + require.Len(t, members, 2) + + for _, member := range members { + if member.UserId == th.BasicUser.Id { + checkPreference(t, th.App, th.BasicUser.Id, model.PreferenceCategoryDirectChannelShow, th.BasicUser2.Id, "true") + } + } + }) + + t.Run("Import a GM channel and preserve if the channel was shown to users", func(t *testing.T) { + data := imports.DirectChannelImportData{ + Participants: []*imports.DirectChannelMemberImportData{ + { + Username: model.NewString(th.BasicUser.Username), + }, + { + Username: model.NewString(th.BasicUser2.Username), + }, + { + Username: model.NewString(user3.Username), + }, + }, + ShownBy: &[]string{ + th.BasicUser.Username, + }, + } + + appErr := th.App.importDirectChannel(th.Context, &data, false) + require.Nil(t, appErr) + + channel, appErr := th.App.GetGroupChannel(th.Context, []string{th.BasicUser.Id, th.BasicUser2.Id, user3.Id}) + require.Nil(t, appErr) + + members, appErr := th.App.GetChannelMembersPage(th.Context, channel.Id, 0, 100) + require.Nil(t, appErr) + require.Len(t, members, 3) + + for _, member := range members { + if member.UserId == th.BasicUser.Id { + checkPreference(t, th.App, th.BasicUser.Id, model.PreferenceCategoryGroupChannelShow, channel.Id, "true") + } + } + }) } func TestImportImportDirectPost(t *testing.T) { @@ -3332,9 +3535,13 @@ func TestImportImportDirectPost(t *testing.T) { // Create the DIRECT channel. channelData := imports.DirectChannelImportData{ - Members: &[]string{ - th.BasicUser.Username, - th.BasicUser2.Username, + Participants: []*imports.DirectChannelMemberImportData{ + { + Username: model.NewString(th.BasicUser.Username), + }, + { + Username: model.NewString(th.BasicUser2.Username), + }, }, } appErr := th.App.importDirectChannel(th.Context, &channelData, false) @@ -3688,10 +3895,16 @@ func TestImportImportDirectPost(t *testing.T) { // Create the GROUP channel. user3 := th.CreateUser() channelData = imports.DirectChannelImportData{ - Members: &[]string{ - th.BasicUser.Username, - th.BasicUser2.Username, - user3.Username, + Participants: []*imports.DirectChannelMemberImportData{ + { + Username: model.NewString(th.BasicUser.Username), + }, + { + Username: model.NewString(th.BasicUser2.Username), + }, + { + Username: model.NewString(user3.Username), + }, }, } appErr = th.App.importDirectChannel(th.Context, &channelData, false) diff --git a/server/channels/app/imports/import_types.go b/server/channels/app/imports/import_types.go index ca33181bd2..dde9d09324 100644 --- a/server/channels/app/imports/import_types.go +++ b/server/channels/app/imports/import_types.go @@ -123,10 +123,27 @@ type UserChannelImportData struct { LastViewedAt *int64 `json:"last_viewed_at,omitempty"` } +type DirectChannelMemberImportData struct { + Username *string `json:"username"` + NotifyProps *UserChannelNotifyPropsImportData `json:"notify_props,omitempty"` + SchemeUser *bool `json:"scheme_user,omitempty"` + SchemeAdmin *bool `json:"scheme_admin,omitempty"` + SchemeGuest *bool `json:"scheme_guest,omitempty"` + MentionCount *int64 `json:"mention_count,omitempty"` + MentionCountRoot *int64 `json:"mention_count_root,omitempty"` + UrgentMentionCount *int64 `json:"urgend_mention_count,omitempty"` + MsgCount *int64 `json:"msg_count,omitempty"` + MsgCountRoot *int64 `json:"msg_count_root,omitempty"` + LastViewedAt *int64 `json:"last_viewed_at,omitempty"` +} + type UserChannelNotifyPropsImportData struct { - Desktop *string `json:"desktop"` - Mobile *string `json:"mobile"` - MarkUnread *string `json:"mark_unread"` + Desktop *string `json:"desktop"` + Mobile *string `json:"mobile"` + MarkUnread *string `json:"mark_unread"` + Email *string `json:"email,omitempty"` + IgnoreChannelMentions *string `json:"ignore_channel_mentions,omitempty"` + ChannelAutoFollowThreads *string `json:"channel_auto_follow_threads,omitempty"` } type EmojiImportData struct { @@ -173,8 +190,10 @@ type PostImportData struct { } type DirectChannelImportData struct { - Members *[]string `json:"members"` - FavoritedBy *[]string `json:"favorited_by"` + Members *[]string `json:"members,omitempty"` + Participants []*DirectChannelMemberImportData `json:"participants,omitempty"` + FavoritedBy *[]string `json:"favorited_by,omitempty"` + ShownBy *[]string `json:"shown_by,omitempty"` Header *string `json:"header"` } diff --git a/server/channels/app/imports/import_validators.go b/server/channels/app/imports/import_validators.go index 3e98dc0d54..dbc542b813 100644 --- a/server/channels/app/imports/import_validators.go +++ b/server/channels/app/imports/import_validators.go @@ -489,11 +489,19 @@ func ValidatePostImportData(data *PostImportData, maxPostSize int) *model.AppErr } func ValidateDirectChannelImportData(data *DirectChannelImportData) *model.AppError { - if data.Members == nil { + if data.Participants == nil && data.Members == nil { return model.NewAppError("BulkImport", "app.import.validate_direct_channel_import_data.members_required.error", nil, "", http.StatusBadRequest) } - if len(*data.Members) != 2 { + if data.Participants != nil && len(data.Participants) != 2 { + if len(data.Participants) < model.ChannelGroupMinUsers { + return model.NewAppError("BulkImport", "app.import.validate_direct_channel_import_data.members_too_few.error", nil, "", http.StatusBadRequest) + } else if len(data.Participants) > model.ChannelGroupMaxUsers { + return model.NewAppError("BulkImport", "app.import.validate_direct_channel_import_data.members_too_many.error", nil, "", http.StatusBadRequest) + } + } + + if data.Members != nil && len(*data.Members) != 2 { if len(*data.Members) < model.ChannelGroupMinUsers { return model.NewAppError("BulkImport", "app.import.validate_direct_channel_import_data.members_too_few.error", nil, "", http.StatusBadRequest) } else if len(*data.Members) > model.ChannelGroupMaxUsers { @@ -508,12 +516,20 @@ func ValidateDirectChannelImportData(data *DirectChannelImportData) *model.AppEr if data.FavoritedBy != nil { for _, favoriter := range *data.FavoritedBy { found := false - for _, member := range *data.Members { - if favoriter == member { + for _, member := range data.Participants { + if favoriter == *member.Username { found = true break } } + if data.Members != nil { + for _, member := range *data.Members { + if favoriter == member { + found = true + break + } + } + } if !found { return model.NewAppError("BulkImport", "app.import.validate_direct_channel_import_data.unknown_favoriter.error", map[string]any{"Username": favoriter}, "", http.StatusBadRequest) } diff --git a/server/channels/app/imports/import_validators_test.go b/server/channels/app/imports/import_validators_test.go index b2de8ad0ea..425b2c7b1b 100644 --- a/server/channels/app/imports/import_validators_test.go +++ b/server/channels/app/imports/import_validators_test.go @@ -19,37 +19,37 @@ import ( func TestImportValidateSchemeImportData(t *testing.T) { // Test with minimum required valid properties and team scope. data := SchemeImportData{ - Name: ptrStr("name"), - DisplayName: ptrStr("display name"), - Scope: ptrStr("team"), + Name: model.NewPointer("name"), + DisplayName: model.NewPointer("display name"), + Scope: model.NewPointer("team"), DefaultTeamAdminRole: &RoleImportData{ - Name: ptrStr("name"), - DisplayName: ptrStr("display name"), + Name: model.NewPointer("name"), + DisplayName: model.NewPointer("display name"), Permissions: &[]string{"invite_user"}, }, DefaultTeamUserRole: &RoleImportData{ - Name: ptrStr("name"), - DisplayName: ptrStr("display name"), + Name: model.NewPointer("name"), + DisplayName: model.NewPointer("display name"), Permissions: &[]string{"invite_user"}, }, DefaultTeamGuestRole: &RoleImportData{ - Name: ptrStr("name"), - DisplayName: ptrStr("display name"), + Name: model.NewPointer("name"), + DisplayName: model.NewPointer("display name"), Permissions: &[]string{"invite_user"}, }, DefaultChannelAdminRole: &RoleImportData{ - Name: ptrStr("name"), - DisplayName: ptrStr("display name"), + Name: model.NewPointer("name"), + DisplayName: model.NewPointer("display name"), Permissions: &[]string{"invite_user"}, }, DefaultChannelUserRole: &RoleImportData{ - Name: ptrStr("name"), - DisplayName: ptrStr("display name"), + Name: model.NewPointer("name"), + DisplayName: model.NewPointer("display name"), Permissions: &[]string{"invite_user"}, }, DefaultChannelGuestRole: &RoleImportData{ - Name: ptrStr("name"), - DisplayName: ptrStr("display name"), + Name: model.NewPointer("name"), + DisplayName: model.NewPointer("display name"), Permissions: &[]string{"invite_user"}, }, } @@ -63,16 +63,16 @@ func TestImportValidateSchemeImportData(t *testing.T) { require.NotNil(t, err, "Should have failed due to invalid name.") // Test with empty string - data.Name = ptrStr("") + data.Name = model.NewPointer("") err = ValidateSchemeImportData(&data) require.NotNil(t, err, "Should have failed due to invalid name.") // Test with numbers - data.Name = ptrStr(strings.Repeat("1234567890", 100)) + data.Name = model.NewPointer(strings.Repeat("1234567890", 100)) err = ValidateSchemeImportData(&data) require.NotNil(t, err, "Should have failed due to invalid name.") - data.Name = ptrStr("name") + data.Name = model.NewPointer("name") // Test with invalid display name. data.DisplayName = nil @@ -80,16 +80,16 @@ func TestImportValidateSchemeImportData(t *testing.T) { require.NotNil(t, err, "Should have failed due to invalid display name.") // Test with display name. - data.DisplayName = ptrStr("") + data.DisplayName = model.NewPointer("") err = ValidateSchemeImportData(&data) require.NotNil(t, err, "Should have failed due to invalid display name.") // Test display name with numbers - data.DisplayName = ptrStr(strings.Repeat("1234567890", 100)) + data.DisplayName = model.NewPointer(strings.Repeat("1234567890", 100)) err = ValidateSchemeImportData(&data) require.NotNil(t, err, "Should have failed due to invalid display name.") - data.DisplayName = ptrStr("display name") + data.DisplayName = model.NewPointer("display name") // Test with various missing roles. data.DefaultTeamAdminRole = nil @@ -97,8 +97,8 @@ func TestImportValidateSchemeImportData(t *testing.T) { require.NotNil(t, err, "Should have failed due to missing role.") data.DefaultTeamAdminRole = &RoleImportData{ - Name: ptrStr("name"), - DisplayName: ptrStr("display name"), + Name: model.NewPointer("name"), + DisplayName: model.NewPointer("display name"), Permissions: &[]string{"invite_user"}, } @@ -107,8 +107,8 @@ func TestImportValidateSchemeImportData(t *testing.T) { require.NotNil(t, err, "Should have failed due to missing role.") data.DefaultTeamUserRole = &RoleImportData{ - Name: ptrStr("name"), - DisplayName: ptrStr("display name"), + Name: model.NewPointer("name"), + DisplayName: model.NewPointer("display name"), Permissions: &[]string{"invite_user"}, } data.DefaultChannelAdminRole = nil @@ -116,8 +116,8 @@ func TestImportValidateSchemeImportData(t *testing.T) { require.NotNil(t, err, "Should have failed due to missing role.") data.DefaultChannelAdminRole = &RoleImportData{ - Name: ptrStr("name"), - DisplayName: ptrStr("display name"), + Name: model.NewPointer("name"), + DisplayName: model.NewPointer("display name"), Permissions: &[]string{"invite_user"}, } data.DefaultChannelUserRole = nil @@ -125,8 +125,8 @@ func TestImportValidateSchemeImportData(t *testing.T) { require.NotNil(t, err, "Should have failed due to missing role.") data.DefaultChannelUserRole = &RoleImportData{ - Name: ptrStr("name"), - DisplayName: ptrStr("display name"), + Name: model.NewPointer("name"), + DisplayName: model.NewPointer("display name"), Permissions: &[]string{"invite_user"}, } @@ -135,32 +135,32 @@ func TestImportValidateSchemeImportData(t *testing.T) { err = ValidateSchemeImportData(&data) require.NotNil(t, err, "Should have failed due to invalid role.") - data.DefaultTeamAdminRole.Name = ptrStr("name") + data.DefaultTeamAdminRole.Name = model.NewPointer("name") data.DefaultTeamUserRole.Name = nil err = ValidateSchemeImportData(&data) require.NotNil(t, err, "Should have failed due to invalid role.") - data.DefaultTeamUserRole.Name = ptrStr("name") + data.DefaultTeamUserRole.Name = model.NewPointer("name") data.DefaultChannelAdminRole.Name = nil err = ValidateSchemeImportData(&data) require.NotNil(t, err, "Should have failed due to invalid role.") - data.DefaultChannelAdminRole.Name = ptrStr("name") + data.DefaultChannelAdminRole.Name = model.NewPointer("name") data.DefaultChannelUserRole.Name = nil err = ValidateSchemeImportData(&data) require.NotNil(t, err, "Should have failed due to invalid role.") - data.DefaultChannelUserRole.Name = ptrStr("name") + data.DefaultChannelUserRole.Name = model.NewPointer("name") // Change to a Channel scope role, and check with missing or extra roles again. - data.Scope = ptrStr("channel") + data.Scope = model.NewPointer("channel") data.DefaultTeamAdminRole = nil err = ValidateSchemeImportData(&data) require.NotNil(t, err, "Should have failed due to spurious role.") data.DefaultTeamAdminRole = &RoleImportData{ - Name: ptrStr("name"), - DisplayName: ptrStr("display name"), + Name: model.NewPointer("name"), + DisplayName: model.NewPointer("display name"), Permissions: &[]string{"invite_user"}, } data.DefaultTeamUserRole = nil @@ -168,8 +168,8 @@ func TestImportValidateSchemeImportData(t *testing.T) { require.NotNil(t, err, "Should have failed due to spurious role.") data.DefaultTeamUserRole = &RoleImportData{ - Name: ptrStr("name"), - DisplayName: ptrStr("display name"), + Name: model.NewPointer("name"), + DisplayName: model.NewPointer("display name"), Permissions: &[]string{"invite_user"}, } data.DefaultTeamGuestRole = nil @@ -183,11 +183,11 @@ func TestImportValidateSchemeImportData(t *testing.T) { require.Nil(t, err, "Should have succeeded.") // Test with all combinations of optional parameters. - data.Description = ptrStr(strings.Repeat("1234567890", 1024)) + data.Description = model.NewPointer(strings.Repeat("1234567890", 1024)) err = ValidateSchemeImportData(&data) require.NotNil(t, err, "Should have failed due to invalid description.") - data.Description = ptrStr("description") + data.Description = model.NewPointer("description") err = ValidateSchemeImportData(&data) require.Nil(t, err, "Should have succeeded.") } @@ -195,8 +195,8 @@ func TestImportValidateSchemeImportData(t *testing.T) { func TestImportValidateRoleImportData(t *testing.T) { // Test with minimum required valid properties. data := RoleImportData{ - Name: ptrStr("name"), - DisplayName: ptrStr("display name"), + Name: model.NewPointer("name"), + DisplayName: model.NewPointer("display name"), } err := ValidateRoleImportData(&data) require.Nil(t, err, "Validation failed but should have been valid.") @@ -206,30 +206,30 @@ func TestImportValidateRoleImportData(t *testing.T) { err = ValidateRoleImportData(&data) require.NotNil(t, err, "Should have failed due to invalid name.") - data.Name = ptrStr("") + data.Name = model.NewPointer("") err = ValidateRoleImportData(&data) require.NotNil(t, err, "Should have failed due to invalid name.") - data.Name = ptrStr(strings.Repeat("1234567890", 100)) + data.Name = model.NewPointer(strings.Repeat("1234567890", 100)) err = ValidateRoleImportData(&data) require.NotNil(t, err, "Should have failed due to invalid name.") - data.Name = ptrStr("name") + data.Name = model.NewPointer("name") // Test with invalid display name. data.DisplayName = nil err = ValidateRoleImportData(&data) require.NotNil(t, err, "Should have failed due to invalid display name.") - data.DisplayName = ptrStr("") + data.DisplayName = model.NewPointer("") err = ValidateRoleImportData(&data) require.NotNil(t, err, "Should have failed due to invalid display name.") - data.DisplayName = ptrStr(strings.Repeat("1234567890", 100)) + data.DisplayName = model.NewPointer(strings.Repeat("1234567890", 100)) err = ValidateRoleImportData(&data) require.NotNil(t, err, "Should have failed due to invalid display name.") - data.DisplayName = ptrStr("display name") + data.DisplayName = model.NewPointer("display name") // Test with various valid/invalid permissions. data.Permissions = &[]string{} @@ -247,11 +247,11 @@ func TestImportValidateRoleImportData(t *testing.T) { data.Permissions = &[]string{"invite_user", "add_user_to_team"} // Test with various valid/invalid descriptions. - data.Description = ptrStr(strings.Repeat("1234567890", 1024)) + data.Description = model.NewPointer(strings.Repeat("1234567890", 1024)) err = ValidateRoleImportData(&data) require.NotNil(t, err, "Validation should have failed due to invalid description.") - data.Description = ptrStr("description") + data.Description = model.NewPointer("description") err = ValidateRoleImportData(&data) require.Nil(t, err, "Validation failed but should have been valid.") } @@ -259,96 +259,96 @@ func TestImportValidateRoleImportData(t *testing.T) { func TestImportValidateTeamImportData(t *testing.T) { // Test with minimum required valid properties. data := TeamImportData{ - Name: ptrStr("teamname"), - DisplayName: ptrStr("Display Name"), - Type: ptrStr("O"), + Name: model.NewPointer("teamname"), + DisplayName: model.NewPointer("Display Name"), + Type: model.NewPointer("O"), } err := ValidateTeamImportData(&data) require.Nil(t, err, "Validation failed but should have been valid.") // Test with various invalid names. data = TeamImportData{ - DisplayName: ptrStr("Display Name"), - Type: ptrStr("O"), + DisplayName: model.NewPointer("Display Name"), + Type: model.NewPointer("O"), } err = ValidateTeamImportData(&data) require.NotNil(t, err, "Should have failed due to missing name.") - data.Name = ptrStr(strings.Repeat("abcdefghij", 7)) + data.Name = model.NewPointer(strings.Repeat("abcdefghij", 7)) err = ValidateTeamImportData(&data) require.NotNil(t, err, "Should have failed due to too long name.") - data.Name = ptrStr("login") + data.Name = model.NewPointer("login") err = ValidateTeamImportData(&data) require.NotNil(t, err, "Should have failed due to reserved word in name.") - data.Name = ptrStr("Test::''ASD") + data.Name = model.NewPointer("Test::''ASD") err = ValidateTeamImportData(&data) require.NotNil(t, err, "Should have failed due to non alphanum characters in name.") - data.Name = ptrStr("A") + data.Name = model.NewPointer("A") err = ValidateTeamImportData(&data) require.NotNil(t, err, "Should have failed due to short name.") // Test team various invalid display names. data = TeamImportData{ - Name: ptrStr("teamname"), - Type: ptrStr("O"), + Name: model.NewPointer("teamname"), + Type: model.NewPointer("O"), } err = ValidateTeamImportData(&data) require.NotNil(t, err, "Should have failed due to missing display_name.") - data.DisplayName = ptrStr("") + data.DisplayName = model.NewPointer("") err = ValidateTeamImportData(&data) require.NotNil(t, err, "Should have failed due to empty display_name.") - data.DisplayName = ptrStr(strings.Repeat("abcdefghij", 7)) + data.DisplayName = model.NewPointer(strings.Repeat("abcdefghij", 7)) err = ValidateTeamImportData(&data) require.NotNil(t, err, "Should have failed due to too long display_name.") // Test with various valid and invalid types. data = TeamImportData{ - Name: ptrStr("teamname"), - DisplayName: ptrStr("Display Name"), + Name: model.NewPointer("teamname"), + DisplayName: model.NewPointer("Display Name"), } err = ValidateTeamImportData(&data) require.NotNil(t, err, "Should have failed due to missing type.") - data.Type = ptrStr("A") + data.Type = model.NewPointer("A") err = ValidateTeamImportData(&data) require.NotNil(t, err, "Should have failed due to invalid type.") - data.Type = ptrStr("I") + data.Type = model.NewPointer("I") err = ValidateTeamImportData(&data) require.Nil(t, err, "Should have succeeded with valid type.") // Test with all the combinations of optional parameters. data = TeamImportData{ - Name: ptrStr("teamname"), - DisplayName: ptrStr("Display Name"), - Type: ptrStr("O"), - Description: ptrStr("The team description."), - AllowOpenInvite: ptrBool(true), + Name: model.NewPointer("teamname"), + DisplayName: model.NewPointer("Display Name"), + Type: model.NewPointer("O"), + Description: model.NewPointer("The team description."), + AllowOpenInvite: model.NewPointer(true), } err = ValidateTeamImportData(&data) require.Nil(t, err, "Should have succeeded with valid optional properties.") - data.AllowOpenInvite = ptrBool(false) + data.AllowOpenInvite = model.NewPointer(false) err = ValidateTeamImportData(&data) require.Nil(t, err, "Should have succeeded with allow open invites false.") - data.Description = ptrStr(strings.Repeat("abcdefghij ", 26)) + data.Description = model.NewPointer(strings.Repeat("abcdefghij ", 26)) err = ValidateTeamImportData(&data) require.NotNil(t, err, "Should have failed due to too long description.") // Test with an empty scheme name. - data.Description = ptrStr("abcdefg") - data.Scheme = ptrStr("") + data.Description = model.NewPointer("abcdefg") + data.Scheme = model.NewPointer("") err = ValidateTeamImportData(&data) require.NotNil(t, err, "Should have failed due to empty scheme name.") // Test with a valid scheme name. - data.Scheme = ptrStr("abcdefg") + data.Scheme = model.NewPointer("abcdefg") err = ValidateTeamImportData(&data) require.Nil(t, err, "Should have succeeded with valid scheme name.") } @@ -357,9 +357,9 @@ func TestImportValidateChannelImportData(t *testing.T) { // Test with minimum required valid properties. chanTypeOpen := model.ChannelTypeOpen data := ChannelImportData{ - Team: ptrStr("teamname"), - Name: ptrStr("channelname"), - DisplayName: ptrStr("Display Name"), + Team: model.NewPointer("teamname"), + Name: model.NewPointer("channelname"), + DisplayName: model.NewPointer("Display Name"), Type: &chanTypeOpen, } err := ValidateChannelImportData(&data) @@ -367,8 +367,8 @@ func TestImportValidateChannelImportData(t *testing.T) { // Test with missing team. data = ChannelImportData{ - Name: ptrStr("channelname"), - DisplayName: ptrStr("Display Name"), + Name: model.NewPointer("channelname"), + DisplayName: model.NewPointer("Display Name"), Type: &chanTypeOpen, } err = ValidateChannelImportData(&data) @@ -376,49 +376,49 @@ func TestImportValidateChannelImportData(t *testing.T) { // Test with various invalid names. data = ChannelImportData{ - Team: ptrStr("teamname"), - DisplayName: ptrStr("Display Name"), + Team: model.NewPointer("teamname"), + DisplayName: model.NewPointer("Display Name"), Type: &chanTypeOpen, } err = ValidateChannelImportData(&data) require.NotNil(t, err, "Should have failed due to missing name.") - data.Name = ptrStr(strings.Repeat("abcdefghij", 7)) + data.Name = model.NewPointer(strings.Repeat("abcdefghij", 7)) err = ValidateChannelImportData(&data) require.NotNil(t, err, "Should have failed due to too long name.") - data.Name = ptrStr("Test::''ASD") + data.Name = model.NewPointer("Test::''ASD") err = ValidateChannelImportData(&data) require.NotNil(t, err, "Should have failed due to non alphanum characters in name.") - data.Name = ptrStr("A") + data.Name = model.NewPointer("A") err = ValidateChannelImportData(&data) require.NotNil(t, err, "Should have failed due to short name.") // Test team various invalid display names. data = ChannelImportData{ - Team: ptrStr("teamname"), - Name: ptrStr("channelname"), + Team: model.NewPointer("teamname"), + Name: model.NewPointer("channelname"), Type: &chanTypeOpen, } err = ValidateChannelImportData(&data) require.Nil(t, err, "Should have accepted having an empty display_name.") require.Equal(t, data.Name, data.DisplayName, "Name and DisplayName should be the same if DisplayName is missing") - data.DisplayName = ptrStr("") + data.DisplayName = model.NewPointer("") err = ValidateChannelImportData(&data) require.Nil(t, err, "Should have accepted having an empty display_name.") require.Equal(t, data.Name, data.DisplayName, "Name and DisplayName should be the same if DisplayName is missing") - data.DisplayName = ptrStr(strings.Repeat("abcdefghij", 7)) + data.DisplayName = model.NewPointer(strings.Repeat("abcdefghij", 7)) err = ValidateChannelImportData(&data) require.NotNil(t, err, "Should have failed due to too long display_name.") // Test with various valid and invalid types. data = ChannelImportData{ - Team: ptrStr("teamname"), - Name: ptrStr("channelname"), - DisplayName: ptrStr("Display Name"), + Team: model.NewPointer("teamname"), + Name: model.NewPointer("channelname"), + DisplayName: model.NewPointer("Display Name"), } err = ValidateChannelImportData(&data) require.NotNil(t, err, "Should have failed due to missing type.") @@ -435,33 +435,33 @@ func TestImportValidateChannelImportData(t *testing.T) { // Test with all the combinations of optional parameters. data = ChannelImportData{ - Team: ptrStr("teamname"), - Name: ptrStr("channelname"), - DisplayName: ptrStr("Display Name"), + Team: model.NewPointer("teamname"), + Name: model.NewPointer("channelname"), + DisplayName: model.NewPointer("Display Name"), Type: &chanTypeOpen, - Header: ptrStr("Channel Header Here"), - Purpose: ptrStr("Channel Purpose Here"), + Header: model.NewPointer("Channel Header Here"), + Purpose: model.NewPointer("Channel Purpose Here"), } err = ValidateChannelImportData(&data) require.Nil(t, err, "Should have succeeded with valid optional properties.") - data.Header = ptrStr(strings.Repeat("abcdefghij ", 103)) + data.Header = model.NewPointer(strings.Repeat("abcdefghij ", 103)) err = ValidateChannelImportData(&data) require.NotNil(t, err, "Should have failed due to too long header.") - data.Header = ptrStr("Channel Header Here") - data.Purpose = ptrStr(strings.Repeat("abcdefghij ", 26)) + data.Header = model.NewPointer("Channel Header Here") + data.Purpose = model.NewPointer(strings.Repeat("abcdefghij ", 26)) err = ValidateChannelImportData(&data) require.NotNil(t, err, "Should have failed due to too long purpose.") // Test with an empty scheme name. - data.Purpose = ptrStr("abcdefg") - data.Scheme = ptrStr("") + data.Purpose = model.NewPointer("abcdefg") + data.Scheme = model.NewPointer("") err = ValidateChannelImportData(&data) require.NotNil(t, err, "Should have failed due to empty scheme name.") // Test with a valid scheme name. - data.Scheme = ptrStr("abcdefg") + data.Scheme = model.NewPointer("abcdefg") err = ValidateChannelImportData(&data) require.Nil(t, err, "Should have succeeded with valid scheme name.") } @@ -469,8 +469,8 @@ func TestImportValidateChannelImportData(t *testing.T) { func TestImportValidateUserImportData(t *testing.T) { // Test with minimum required valid properties. data := UserImportData{ - Username: ptrStr("bob"), - Email: ptrStr("bob@example.com"), + Username: model.NewPointer("bob"), + Email: model.NewPointer("bob@example.com"), } err := ValidateUserImportData(&data) require.Nil(t, err, "Validation failed but should have been valid.") @@ -480,22 +480,22 @@ func TestImportValidateUserImportData(t *testing.T) { err = ValidateUserImportData(&data) require.NotNil(t, err, "Validation should have failed due to nil Username.") - data.Username = ptrStr("") + data.Username = model.NewPointer("") err = ValidateUserImportData(&data) require.NotNil(t, err, "Validation should have failed due to 0 length Username.") - data.Username = ptrStr(strings.Repeat("abcdefghij", 7)) + data.Username = model.NewPointer(strings.Repeat("abcdefghij", 7)) err = ValidateUserImportData(&data) require.NotNil(t, err, "Validation should have failed due to too long Username.") - data.Username = ptrStr("i am a username with spaces and !!!") + data.Username = model.NewPointer("i am a username with spaces and !!!") err = ValidateUserImportData(&data) require.NotNil(t, err, "Validation should have failed due to invalid characters in Username.") - data.Username = ptrStr("bob") + data.Username = model.NewPointer("bob") // Unexisting Picture Image - data.ProfileImage = ptrStr("not-existing-file") + data.ProfileImage = model.NewPointer("not-existing-file") err = ValidateUserImportData(&data) require.NotNil(t, err, "Validation should have failed due to not existing profile image file.") @@ -506,132 +506,132 @@ func TestImportValidateUserImportData(t *testing.T) { err = ValidateUserImportData(&data) require.NotNil(t, err, "Validation should have failed due to nil Email.") - data.Email = ptrStr("") + data.Email = model.NewPointer("") err = ValidateUserImportData(&data) require.NotNil(t, err, "Validation should have failed due to 0 length Email.") - data.Email = ptrStr(strings.Repeat("abcdefghij", 13)) + data.Email = model.NewPointer(strings.Repeat("abcdefghij", 13)) err = ValidateUserImportData(&data) require.NotNil(t, err, "Validation should have failed due to too long Email.") - data.Email = ptrStr("bob@example.com") + data.Email = model.NewPointer("bob@example.com") // Empty AuthService indicates user/password auth. - data.AuthService = ptrStr("") + data.AuthService = model.NewPointer("") checkNoError(t, ValidateUserImportData(&data)) - data.AuthService = ptrStr("saml") - data.AuthData = ptrStr(strings.Repeat("abcdefghij", 15)) + data.AuthService = model.NewPointer("saml") + data.AuthData = model.NewPointer(strings.Repeat("abcdefghij", 15)) err = ValidateUserImportData(&data) require.NotNil(t, err, "Validation should have failed due to too long auth data.") - data.AuthData = ptrStr("bobbytables") + data.AuthData = model.NewPointer("bobbytables") err = ValidateUserImportData(&data) require.Nil(t, err, "Validation should have succeeded with valid auth service and auth data.") // Test a valid User with all fields populated. testsDir, _ := fileutils.FindDir("tests") data = UserImportData{ - ProfileImage: ptrStr(filepath.Join(testsDir, "test.png")), - Username: ptrStr("bob"), - Email: ptrStr("bob@example.com"), - AuthService: ptrStr("ldap"), - AuthData: ptrStr("bob"), - Nickname: ptrStr("BobNick"), - FirstName: ptrStr("Bob"), - LastName: ptrStr("Blob"), - Position: ptrStr("The Boss"), - Roles: ptrStr("system_user"), - Locale: ptrStr("en"), + ProfileImage: model.NewPointer(filepath.Join(testsDir, "test.png")), + Username: model.NewPointer("bob"), + Email: model.NewPointer("bob@example.com"), + AuthService: model.NewPointer("ldap"), + AuthData: model.NewPointer("bob"), + Nickname: model.NewPointer("BobNick"), + FirstName: model.NewPointer("Bob"), + LastName: model.NewPointer("Blob"), + Position: model.NewPointer("The Boss"), + Roles: model.NewPointer("system_user"), + Locale: model.NewPointer("en"), } err = ValidateUserImportData(&data) require.Nil(t, err, "Validation failed but should have been valid.") // Test various invalid optional field values. - data.Nickname = ptrStr(strings.Repeat("abcdefghij", 7)) + data.Nickname = model.NewPointer(strings.Repeat("abcdefghij", 7)) err = ValidateUserImportData(&data) require.NotNil(t, err, "Validation should have failed due to too long Nickname.") - data.Nickname = ptrStr("BobNick") + data.Nickname = model.NewPointer("BobNick") - data.FirstName = ptrStr(strings.Repeat("abcdefghij", 7)) + data.FirstName = model.NewPointer(strings.Repeat("abcdefghij", 7)) err = ValidateUserImportData(&data) require.NotNil(t, err, "Validation should have failed due to too long First Name.") - data.FirstName = ptrStr("Bob") + data.FirstName = model.NewPointer("Bob") - data.LastName = ptrStr(strings.Repeat("abcdefghij", 7)) + data.LastName = model.NewPointer(strings.Repeat("abcdefghij", 7)) err = ValidateUserImportData(&data) require.NotNil(t, err, "Validation should have failed due to too long Last name.") - data.LastName = ptrStr("Blob") + data.LastName = model.NewPointer("Blob") - data.Position = ptrStr(strings.Repeat("abcdefghij", 13)) + data.Position = model.NewPointer(strings.Repeat("abcdefghij", 13)) err = ValidateUserImportData(&data) require.NotNil(t, err, "Validation should have failed due to too long Position.") - data.Position = ptrStr("The Boss") + data.Position = model.NewPointer("The Boss") data.Roles = nil err = ValidateUserImportData(&data) require.Nil(t, err, "Validation failed but should have been valid.") - data.Roles = ptrStr("") + data.Roles = model.NewPointer("") err = ValidateUserImportData(&data) require.Nil(t, err, "Validation failed but should have been valid.") - data.Roles = ptrStr("system_user") + data.Roles = model.NewPointer("system_user") // Try various valid/invalid notify props. data.NotifyProps = &UserNotifyPropsImportData{} - data.NotifyProps.Desktop = ptrStr("invalid") + data.NotifyProps.Desktop = model.NewPointer("invalid") checkError(t, ValidateUserImportData(&data)) - data.NotifyProps.Desktop = ptrStr(model.UserNotifyAll) - data.NotifyProps.DesktopSound = ptrStr("invalid") + data.NotifyProps.Desktop = model.NewPointer(model.UserNotifyAll) + data.NotifyProps.DesktopSound = model.NewPointer("invalid") checkError(t, ValidateUserImportData(&data)) - data.NotifyProps.DesktopSound = ptrStr("true") - data.NotifyProps.Email = ptrStr("invalid") + data.NotifyProps.DesktopSound = model.NewPointer("true") + data.NotifyProps.Email = model.NewPointer("invalid") checkError(t, ValidateUserImportData(&data)) - data.NotifyProps.Email = ptrStr("true") - data.NotifyProps.Mobile = ptrStr("invalid") + data.NotifyProps.Email = model.NewPointer("true") + data.NotifyProps.Mobile = model.NewPointer("invalid") checkError(t, ValidateUserImportData(&data)) - data.NotifyProps.Mobile = ptrStr(model.UserNotifyAll) - data.NotifyProps.MobilePushStatus = ptrStr("invalid") + data.NotifyProps.Mobile = model.NewPointer(model.UserNotifyAll) + data.NotifyProps.MobilePushStatus = model.NewPointer("invalid") checkError(t, ValidateUserImportData(&data)) - data.NotifyProps.MobilePushStatus = ptrStr(model.StatusOnline) - data.NotifyProps.ChannelTrigger = ptrStr("invalid") + data.NotifyProps.MobilePushStatus = model.NewPointer(model.StatusOnline) + data.NotifyProps.ChannelTrigger = model.NewPointer("invalid") checkError(t, ValidateUserImportData(&data)) - data.NotifyProps.ChannelTrigger = ptrStr("true") - data.NotifyProps.CommentsTrigger = ptrStr("invalid") + data.NotifyProps.ChannelTrigger = model.NewPointer("true") + data.NotifyProps.CommentsTrigger = model.NewPointer("invalid") checkError(t, ValidateUserImportData(&data)) - data.NotifyProps.CommentsTrigger = ptrStr(model.CommentsNotifyRoot) - data.NotifyProps.MentionKeys = ptrStr("valid") + data.NotifyProps.CommentsTrigger = model.NewPointer(model.CommentsNotifyRoot) + data.NotifyProps.MentionKeys = model.NewPointer("valid") checkNoError(t, ValidateUserImportData(&data)) //Test the email batching interval validators //Happy paths - data.EmailInterval = ptrStr("immediately") + data.EmailInterval = model.NewPointer("immediately") checkNoError(t, ValidateUserImportData(&data)) - data.EmailInterval = ptrStr("fifteen") + data.EmailInterval = model.NewPointer("fifteen") checkNoError(t, ValidateUserImportData(&data)) - data.EmailInterval = ptrStr("hour") + data.EmailInterval = model.NewPointer("hour") checkNoError(t, ValidateUserImportData(&data)) //Invalid values - data.EmailInterval = ptrStr("invalid") + data.EmailInterval = model.NewPointer("invalid") checkError(t, ValidateUserImportData(&data)) - data.EmailInterval = ptrStr("") + data.EmailInterval = model.NewPointer("") checkError(t, ValidateUserImportData(&data)) } @@ -642,21 +642,21 @@ func TestImportValidateUserAuth(t *testing.T) { isValid bool }{ {nil, nil, true}, - {ptrStr(""), ptrStr(""), true}, - {ptrStr("foo"), ptrStr("foo"), false}, - {nil, ptrStr(""), true}, - {ptrStr(""), nil, true}, + {model.NewPointer(""), model.NewPointer(""), true}, + {model.NewPointer("foo"), model.NewPointer("foo"), false}, + {nil, model.NewPointer(""), true}, + {model.NewPointer(""), nil, true}, - {ptrStr("foo"), nil, false}, - {ptrStr("foo"), ptrStr(""), false}, - {nil, ptrStr("foo"), false}, - {ptrStr(""), ptrStr("foo"), false}, + {model.NewPointer("foo"), nil, false}, + {model.NewPointer("foo"), model.NewPointer(""), false}, + {nil, model.NewPointer("foo"), false}, + {model.NewPointer(""), model.NewPointer("foo"), false}, } for _, test := range tests { data := UserImportData{ - Username: ptrStr("bob"), - Email: ptrStr("bob@example.com"), + Username: model.NewPointer("bob"), + Email: model.NewPointer("bob@example.com"), AuthService: test.authService, AuthData: test.authData, } @@ -674,13 +674,13 @@ func TestImportValidateUserTeamsImportData(t *testing.T) { // Invalid Name. data := []UserTeamImportData{ { - Roles: ptrStr("team_admin team_user"), + Roles: model.NewPointer("team_admin team_user"), }, } err := ValidateUserTeamsImportData(&data) require.NotNil(t, err, "Should have failed due to invalid name.") - data[0].Name = ptrStr("teamname") + data[0].Name = model.NewPointer("teamname") // Valid (nil roles) data[0].Roles = nil @@ -688,27 +688,27 @@ func TestImportValidateUserTeamsImportData(t *testing.T) { require.Nil(t, err, "Should have succeeded with empty roles.") // Valid (empty roles) - data[0].Roles = ptrStr("") + data[0].Roles = model.NewPointer("") err = ValidateUserTeamsImportData(&data) require.Nil(t, err, "Should have succeeded with empty roles.") // Valid (with roles) - data[0].Roles = ptrStr("team_admin team_user") + data[0].Roles = model.NewPointer("team_admin team_user") err = ValidateUserTeamsImportData(&data) require.Nil(t, err, "Should have succeeded with valid roles.") // Valid (with JSON string of theme) - data[0].Theme = ptrStr(`{"awayIndicator":"#DBBD4E","buttonBg":"#23A1FF","buttonColor":"#FFFFFF","centerChannelBg":"#ffffff","centerChannelColor":"#333333","codeTheme":"github","image":"/static/files/a4a388b38b32678e83823ef1b3e17766.png","linkColor":"#2389d7","mentionBg":"#2389d7","mentionColor":"#ffffff","mentionHighlightBg":"#fff2bb","mentionHighlightLink":"#2f81b7","newMessageSeparator":"#FF8800","onlineIndicator":"#7DBE00","sidebarBg":"#fafafa","sidebarHeaderBg":"#3481B9","sidebarHeaderTextColor":"#ffffff","sidebarText":"#333333","sidebarTextActiveBorder":"#378FD2","sidebarTextActiveColor":"#111111","sidebarTextHoverBg":"#e6f2fa","sidebarUnreadText":"#333333","type":"Mattermost"}`) + data[0].Theme = model.NewPointer(`{"awayIndicator":"#DBBD4E","buttonBg":"#23A1FF","buttonColor":"#FFFFFF","centerChannelBg":"#ffffff","centerChannelColor":"#333333","codeTheme":"github","image":"/static/files/a4a388b38b32678e83823ef1b3e17766.png","linkColor":"#2389d7","mentionBg":"#2389d7","mentionColor":"#ffffff","mentionHighlightBg":"#fff2bb","mentionHighlightLink":"#2f81b7","newMessageSeparator":"#FF8800","onlineIndicator":"#7DBE00","sidebarBg":"#fafafa","sidebarHeaderBg":"#3481B9","sidebarHeaderTextColor":"#ffffff","sidebarText":"#333333","sidebarTextActiveBorder":"#378FD2","sidebarTextActiveColor":"#111111","sidebarTextHoverBg":"#e6f2fa","sidebarUnreadText":"#333333","type":"Mattermost"}`) err = ValidateUserTeamsImportData(&data) require.Nil(t, err, "Should have succeeded with valid theme.") // Invalid (invalid JSON string of theme) - data[0].Theme = ptrStr(`This is the invalid string which cannot be marshalled to JSON object :) + {"#DBBD4E","buttonBg", "#23A1FF", buttonColor`) + data[0].Theme = model.NewPointer(`This is the invalid string which cannot be marshalled to JSON object :) + {"#DBBD4E","buttonBg", "#23A1FF", buttonColor`) err = ValidateUserTeamsImportData(&data) require.NotNil(t, err, "Should have fail with invalid JSON string of theme.") // Invalid (valid JSON but invalid theme description) - data[0].Theme = ptrStr(`{"somekey": 25, "json_obj1": {"color": "#DBBD4E","buttonBg": "#23A1FF"}}`) + data[0].Theme = model.NewPointer(`{"somekey": 25, "json_obj1": {"color": "#DBBD4E","buttonBg": "#23A1FF"}}`) err = ValidateUserTeamsImportData(&data) require.NotNil(t, err, "Should have fail with valid JSON which contains invalid string of theme description.") @@ -719,12 +719,12 @@ func TestImportValidateUserChannelsImportData(t *testing.T) { // Invalid Name. data := []UserChannelImportData{ { - Roles: ptrStr("channel_admin channel_user"), + Roles: model.NewPointer("channel_admin channel_user"), }, } err := ValidateUserChannelsImportData(&data) require.NotNil(t, err, "Should have failed due to invalid name.") - data[0].Name = ptrStr("channelname") + data[0].Name = model.NewPointer("channelname") // Valid (nil roles) data[0].Roles = nil @@ -732,12 +732,12 @@ func TestImportValidateUserChannelsImportData(t *testing.T) { require.Nil(t, err, "Should have succeeded with empty roles.") // Valid (empty roles) - data[0].Roles = ptrStr("") + data[0].Roles = model.NewPointer("") err = ValidateUserChannelsImportData(&data) require.Nil(t, err, "Should have succeeded with empty roles.") // Valid (with roles) - data[0].Roles = ptrStr("channel_admin channel_user") + data[0].Roles = model.NewPointer("channel_admin channel_user") err = ValidateUserChannelsImportData(&data) require.Nil(t, err, "Should have succeeded with valid roles.") @@ -747,24 +747,24 @@ func TestImportValidateUserChannelsImportData(t *testing.T) { require.Nil(t, err, "Should have succeeded with empty notify props.") // Invalid desktop notify props. - data[0].NotifyProps.Desktop = ptrStr("invalid") + data[0].NotifyProps.Desktop = model.NewPointer("invalid") err = ValidateUserChannelsImportData(&data) require.NotNil(t, err, "Should have failed with invalid desktop notify props.") // Invalid mobile notify props. - data[0].NotifyProps.Desktop = ptrStr("mention") - data[0].NotifyProps.Mobile = ptrStr("invalid") + data[0].NotifyProps.Desktop = model.NewPointer("mention") + data[0].NotifyProps.Mobile = model.NewPointer("invalid") err = ValidateUserChannelsImportData(&data) require.NotNil(t, err, "Should have failed with invalid mobile notify props.") // Invalid mark_unread notify props. - data[0].NotifyProps.Mobile = ptrStr("mention") - data[0].NotifyProps.MarkUnread = ptrStr("invalid") + data[0].NotifyProps.Mobile = model.NewPointer("mention") + data[0].NotifyProps.MarkUnread = model.NewPointer("invalid") err = ValidateUserChannelsImportData(&data) require.NotNil(t, err, "Should have failed with invalid mark_unread notify props.") // Valid notify props. - data[0].NotifyProps.MarkUnread = ptrStr("mention") + data[0].NotifyProps.MarkUnread = model.NewPointer("mention") err = ValidateUserChannelsImportData(&data) require.Nil(t, err, "Should have succeeded with valid notify props.") } @@ -773,57 +773,57 @@ func TestImportValidateReactionImportData(t *testing.T) { // Test with minimum required valid properties. parentCreateAt := model.GetMillis() - 100 data := ReactionImportData{ - User: ptrStr("username"), - EmojiName: ptrStr("emoji"), - CreateAt: ptrInt64(model.GetMillis()), + User: model.NewPointer("username"), + EmojiName: model.NewPointer("emoji"), + CreateAt: model.NewPointer(model.GetMillis()), } err := ValidateReactionImportData(&data, parentCreateAt) require.Nil(t, err, "Validation failed but should have been valid.") // Test with missing required properties. data = ReactionImportData{ - EmojiName: ptrStr("emoji"), - CreateAt: ptrInt64(model.GetMillis()), + EmojiName: model.NewPointer("emoji"), + CreateAt: model.NewPointer(model.GetMillis()), } err = ValidateReactionImportData(&data, parentCreateAt) require.NotNil(t, err, "Should have failed due to missing required property.") data = ReactionImportData{ - User: ptrStr("username"), - CreateAt: ptrInt64(model.GetMillis()), + User: model.NewPointer("username"), + CreateAt: model.NewPointer(model.GetMillis()), } err = ValidateReactionImportData(&data, parentCreateAt) require.NotNil(t, err, "Should have failed due to missing required property.") data = ReactionImportData{ - User: ptrStr("username"), - EmojiName: ptrStr("emoji"), + User: model.NewPointer("username"), + EmojiName: model.NewPointer("emoji"), } err = ValidateReactionImportData(&data, parentCreateAt) require.NotNil(t, err, "Should have failed due to missing required property.") // Test with invalid emoji name. data = ReactionImportData{ - User: ptrStr("username"), - EmojiName: ptrStr(strings.Repeat("1234567890", 500)), - CreateAt: ptrInt64(model.GetMillis()), + User: model.NewPointer("username"), + EmojiName: model.NewPointer(strings.Repeat("1234567890", 500)), + CreateAt: model.NewPointer(model.GetMillis()), } err = ValidateReactionImportData(&data, parentCreateAt) require.NotNil(t, err, "Should have failed due to too long emoji name.") // Test with invalid CreateAt data = ReactionImportData{ - User: ptrStr("username"), - EmojiName: ptrStr("emoji"), - CreateAt: ptrInt64(0), + User: model.NewPointer("username"), + EmojiName: model.NewPointer("emoji"), + CreateAt: model.NewPointer(int64(0)), } err = ValidateReactionImportData(&data, parentCreateAt) require.NotNil(t, err, "Should have failed due to 0 create-at value.") data = ReactionImportData{ - User: ptrStr("username"), - EmojiName: ptrStr("emoji"), - CreateAt: ptrInt64(parentCreateAt - 100), + User: model.NewPointer("username"), + EmojiName: model.NewPointer("emoji"), + CreateAt: model.NewPointer(parentCreateAt - 100), } err = ValidateReactionImportData(&data, parentCreateAt) require.NotNil(t, err, "Should have failed due parent with newer create-at value.") @@ -834,49 +834,49 @@ func TestImportValidateReplyImportData(t *testing.T) { parentCreateAt := model.GetMillis() - 100 maxPostSize := 10000 data := ReplyImportData{ - User: ptrStr("username"), - Message: ptrStr("message"), - CreateAt: ptrInt64(model.GetMillis()), + User: model.NewPointer("username"), + Message: model.NewPointer("message"), + CreateAt: model.NewPointer(model.GetMillis()), } err := ValidateReplyImportData(&data, parentCreateAt, maxPostSize) require.Nil(t, err, "Validation failed but should have been valid.") // Test with missing required properties. data = ReplyImportData{ - Message: ptrStr("message"), - CreateAt: ptrInt64(model.GetMillis()), + Message: model.NewPointer("message"), + CreateAt: model.NewPointer(model.GetMillis()), } err = ValidateReplyImportData(&data, parentCreateAt, maxPostSize) require.NotNil(t, err, "Should have failed due to missing required property.") data = ReplyImportData{ - User: ptrStr("username"), - CreateAt: ptrInt64(model.GetMillis()), + User: model.NewPointer("username"), + CreateAt: model.NewPointer(model.GetMillis()), } err = ValidateReplyImportData(&data, parentCreateAt, maxPostSize) require.NotNil(t, err, "Should have failed due to missing required property.") data = ReplyImportData{ - User: ptrStr("username"), - Message: ptrStr("message"), + User: model.NewPointer("username"), + Message: model.NewPointer("message"), } err = ValidateReplyImportData(&data, parentCreateAt, maxPostSize) require.NotNil(t, err, "Should have failed due to missing required property.") // Test with invalid message. data = ReplyImportData{ - User: ptrStr("username"), - Message: ptrStr(strings.Repeat("0", maxPostSize+1)), - CreateAt: ptrInt64(model.GetMillis()), + User: model.NewPointer("username"), + Message: model.NewPointer(strings.Repeat("0", maxPostSize+1)), + CreateAt: model.NewPointer(model.GetMillis()), } err = ValidateReplyImportData(&data, parentCreateAt, maxPostSize) require.NotNil(t, err, "Should have failed due to too long message.") // Test with invalid CreateAt data = ReplyImportData{ - User: ptrStr("username"), - Message: ptrStr("message"), - CreateAt: ptrInt64(0), + User: model.NewPointer("username"), + Message: model.NewPointer("message"), + CreateAt: model.NewPointer(int64(0)), } err = ValidateReplyImportData(&data, parentCreateAt, maxPostSize) require.NotNil(t, err, "Should have failed due to 0 create-at value.") @@ -887,11 +887,11 @@ func TestImportValidatePostImportData(t *testing.T) { t.Run("Test with minimum required valid properties", func(t *testing.T) { data := PostImportData{ - Team: ptrStr("teamname"), - Channel: ptrStr("channelname"), - User: ptrStr("username"), - Message: ptrStr("message"), - CreateAt: ptrInt64(model.GetMillis()), + Team: model.NewPointer("teamname"), + Channel: model.NewPointer("channelname"), + User: model.NewPointer("username"), + Message: model.NewPointer("message"), + CreateAt: model.NewPointer(model.GetMillis()), } err := ValidatePostImportData(&data, maxPostSize) require.Nil(t, err, "Validation failed but should have been valid.") @@ -899,50 +899,50 @@ func TestImportValidatePostImportData(t *testing.T) { t.Run("Test with missing required properties", func(t *testing.T) { data := PostImportData{ - Channel: ptrStr("channelname"), - User: ptrStr("username"), - Message: ptrStr("message"), - CreateAt: ptrInt64(model.GetMillis()), + Channel: model.NewPointer("channelname"), + User: model.NewPointer("username"), + Message: model.NewPointer("message"), + CreateAt: model.NewPointer(model.GetMillis()), } err := ValidatePostImportData(&data, maxPostSize) require.NotNil(t, err, "Should have failed due to missing required property.") assert.Equal(t, err.Id, "app.import.validate_post_import_data.team_missing.error") data = PostImportData{ - Team: ptrStr("teamname"), - User: ptrStr("username"), - Message: ptrStr("message"), - CreateAt: ptrInt64(model.GetMillis()), + Team: model.NewPointer("teamname"), + User: model.NewPointer("username"), + Message: model.NewPointer("message"), + CreateAt: model.NewPointer(model.GetMillis()), } err = ValidatePostImportData(&data, maxPostSize) require.NotNil(t, err, "Should have failed due to missing required property.") assert.Equal(t, err.Id, "app.import.validate_post_import_data.channel_missing.error") data = PostImportData{ - Team: ptrStr("teamname"), - Channel: ptrStr("channelname"), - Message: ptrStr("message"), - CreateAt: ptrInt64(model.GetMillis()), + Team: model.NewPointer("teamname"), + Channel: model.NewPointer("channelname"), + Message: model.NewPointer("message"), + CreateAt: model.NewPointer(model.GetMillis()), } err = ValidatePostImportData(&data, maxPostSize) require.NotNil(t, err, "Should have failed due to missing required property.") assert.Equal(t, err.Id, "app.import.validate_post_import_data.user_missing.error") data = PostImportData{ - Team: ptrStr("teamname"), - Channel: ptrStr("channelname"), - User: ptrStr("username"), - CreateAt: ptrInt64(model.GetMillis()), + Team: model.NewPointer("teamname"), + Channel: model.NewPointer("channelname"), + User: model.NewPointer("username"), + CreateAt: model.NewPointer(model.GetMillis()), } err = ValidatePostImportData(&data, maxPostSize) require.NotNil(t, err, "Should have failed due to missing required property.") assert.Equal(t, err.Id, "app.import.validate_post_import_data.message_missing.error") data = PostImportData{ - Team: ptrStr("teamname"), - Channel: ptrStr("channelname"), - User: ptrStr("username"), - Message: ptrStr("message"), + Team: model.NewPointer("teamname"), + Channel: model.NewPointer("channelname"), + User: model.NewPointer("username"), + Message: model.NewPointer("message"), } err = ValidatePostImportData(&data, maxPostSize) require.NotNil(t, err, "Should have failed due to missing required property.") @@ -951,11 +951,11 @@ func TestImportValidatePostImportData(t *testing.T) { t.Run("Test with invalid message", func(t *testing.T) { data := PostImportData{ - Team: ptrStr("teamname"), - Channel: ptrStr("channelname"), - User: ptrStr("username"), - Message: ptrStr(strings.Repeat("0", maxPostSize+1)), - CreateAt: ptrInt64(model.GetMillis()), + Team: model.NewPointer("teamname"), + Channel: model.NewPointer("channelname"), + User: model.NewPointer("username"), + Message: model.NewPointer(strings.Repeat("0", maxPostSize+1)), + CreateAt: model.NewPointer(model.GetMillis()), } err := ValidatePostImportData(&data, maxPostSize) require.NotNil(t, err, "Should have failed due to too long message.") @@ -964,11 +964,11 @@ func TestImportValidatePostImportData(t *testing.T) { t.Run("Test with invalid CreateAt", func(t *testing.T) { data := PostImportData{ - Team: ptrStr("teamname"), - Channel: ptrStr("channelname"), - User: ptrStr("username"), - Message: ptrStr("message"), - CreateAt: ptrInt64(0), + Team: model.NewPointer("teamname"), + Channel: model.NewPointer("channelname"), + User: model.NewPointer("username"), + Message: model.NewPointer("message"), + CreateAt: model.NewPointer(int64(0)), } err := ValidatePostImportData(&data, maxPostSize) require.NotNil(t, err, "Should have failed due to 0 create-at value.") @@ -977,23 +977,23 @@ func TestImportValidatePostImportData(t *testing.T) { t.Run("Test with valid all optional parameters", func(t *testing.T) { reactions := []ReactionImportData{{ - User: ptrStr("username"), - EmojiName: ptrStr("emoji"), - CreateAt: ptrInt64(model.GetMillis()), + User: model.NewPointer("username"), + EmojiName: model.NewPointer("emoji"), + CreateAt: model.NewPointer(model.GetMillis()), }} replies := []ReplyImportData{{ - User: ptrStr("username"), - Message: ptrStr("message"), - CreateAt: ptrInt64(model.GetMillis()), + User: model.NewPointer("username"), + Message: model.NewPointer("message"), + CreateAt: model.NewPointer(model.GetMillis()), }} data := PostImportData{ - Team: ptrStr("teamname"), - Channel: ptrStr("channelname"), - User: ptrStr("username"), - Message: ptrStr("message"), - CreateAt: ptrInt64(model.GetMillis()), + Team: model.NewPointer("teamname"), + Channel: model.NewPointer("channelname"), + User: model.NewPointer("username"), + Message: model.NewPointer("message"), + CreateAt: model.NewPointer(model.GetMillis()), Reactions: &reactions, Replies: &replies, } @@ -1007,12 +1007,12 @@ func TestImportValidatePostImportData(t *testing.T) { } data := PostImportData{ - Team: ptrStr("teamname"), - Channel: ptrStr("channelname"), - User: ptrStr("username"), - Message: ptrStr("message"), + Team: model.NewPointer("teamname"), + Channel: model.NewPointer("channelname"), + User: model.NewPointer("username"), + Message: model.NewPointer("message"), Props: &props, - CreateAt: ptrInt64(model.GetMillis()), + CreateAt: model.NewPointer(model.GetMillis()), } err := ValidatePostImportData(&data, maxPostSize) require.NotNil(t, err, "Should have failed due to long props.") @@ -1023,9 +1023,16 @@ func TestImportValidatePostImportData(t *testing.T) { func TestImportValidateDirectChannelImportData(t *testing.T) { // Test with valid number of members for direct message. data := DirectChannelImportData{ - Members: &[]string{ - model.NewId(), - model.NewId(), + Participants: []*DirectChannelMemberImportData{ + { + Username: model.NewPointer(model.NewId()), + }, + { + Username: model.NewPointer(model.NewId()), + }, + { + Username: model.NewPointer(model.NewId()), + }, }, } err := ValidateDirectChannelImportData(&data) @@ -1033,10 +1040,16 @@ func TestImportValidateDirectChannelImportData(t *testing.T) { // Test with valid number of members for group message. data = DirectChannelImportData{ - Members: &[]string{ - model.NewId(), - model.NewId(), - model.NewId(), + Participants: []*DirectChannelMemberImportData{ + { + Username: model.NewPointer(model.NewId()), + }, + { + Username: model.NewPointer(model.NewId()), + }, + { + Username: model.NewPointer(model.NewId()), + }, }, } err = ValidateDirectChannelImportData(&data) @@ -1044,46 +1057,70 @@ func TestImportValidateDirectChannelImportData(t *testing.T) { // Test with all the combinations of optional parameters. data = DirectChannelImportData{ - Members: &[]string{ - model.NewId(), - model.NewId(), + Participants: []*DirectChannelMemberImportData{ + { + Username: model.NewPointer(model.NewId()), + }, + { + Username: model.NewPointer(model.NewId()), + }, }, - Header: ptrStr("Channel Header Here"), + Header: model.NewPointer("Channel Header Here"), } err = ValidateDirectChannelImportData(&data) require.Nil(t, err, "Should have succeeded with valid optional properties.") // Test with invalid Header. - data.Header = ptrStr(strings.Repeat("abcdefghij ", 103)) + data.Header = model.NewPointer(strings.Repeat("abcdefghij ", 103)) err = ValidateDirectChannelImportData(&data) require.NotNil(t, err, "Should have failed due to too long header.") // Test with different combinations of invalid member counts. data = DirectChannelImportData{ - Members: &[]string{}, + Participants: []*DirectChannelMemberImportData{}, } err = ValidateDirectChannelImportData(&data) require.NotNil(t, err, "Validation should have failed due to invalid number of members.") data = DirectChannelImportData{ - Members: &[]string{ - model.NewId(), + Participants: []*DirectChannelMemberImportData{ + { + Username: model.NewPointer(model.NewId()), + }, }, } err = ValidateDirectChannelImportData(&data) require.NotNil(t, err, "Validation should have failed due to invalid number of members.") data = DirectChannelImportData{ - Members: &[]string{ - model.NewId(), - model.NewId(), - model.NewId(), - model.NewId(), - model.NewId(), - model.NewId(), - model.NewId(), - model.NewId(), - model.NewId(), + Participants: []*DirectChannelMemberImportData{ + { + Username: model.NewPointer(model.NewId()), + }, + { + Username: model.NewPointer(model.NewId()), + }, + { + Username: model.NewPointer(model.NewId()), + }, + { + Username: model.NewPointer(model.NewId()), + }, + { + Username: model.NewPointer(model.NewId()), + }, + { + Username: model.NewPointer(model.NewId()), + }, + { + Username: model.NewPointer(model.NewId()), + }, + { + Username: model.NewPointer(model.NewId()), + }, + { + Username: model.NewPointer(model.NewId()), + }, }, } err = ValidateDirectChannelImportData(&data) @@ -1093,9 +1130,13 @@ func TestImportValidateDirectChannelImportData(t *testing.T) { member1 := model.NewId() member2 := model.NewId() data = DirectChannelImportData{ - Members: &[]string{ - member1, - member2, + Participants: []*DirectChannelMemberImportData{ + { + Username: model.NewPointer(model.NewId()), + }, + { + Username: model.NewPointer(model.NewId()), + }, }, FavoritedBy: &[]string{ member1, @@ -1107,9 +1148,13 @@ func TestImportValidateDirectChannelImportData(t *testing.T) { // Test with valid FavoritedBy data = DirectChannelImportData{ - Members: &[]string{ - member1, - member2, + Participants: []*DirectChannelMemberImportData{ + { + Username: model.NewPointer(member1), + }, + { + Username: model.NewPointer(member2), + }, }, FavoritedBy: &[]string{ member1, @@ -1129,18 +1174,18 @@ func TestImportValidateDirectPostImportData(t *testing.T) { model.NewId(), model.NewId(), }, - User: ptrStr("username"), - Message: ptrStr("message"), - CreateAt: ptrInt64(model.GetMillis()), + User: model.NewPointer("username"), + Message: model.NewPointer("message"), + CreateAt: model.NewPointer(model.GetMillis()), } err := ValidateDirectPostImportData(&data, maxPostSize) require.Nil(t, err, "Validation failed but should have been valid.") // Test with missing required properties. data = DirectPostImportData{ - User: ptrStr("username"), - Message: ptrStr("message"), - CreateAt: ptrInt64(model.GetMillis()), + User: model.NewPointer("username"), + Message: model.NewPointer("message"), + CreateAt: model.NewPointer(model.GetMillis()), } err = ValidateDirectPostImportData(&data, maxPostSize) require.NotNil(t, err, "Should have failed due to missing required property.") @@ -1150,8 +1195,8 @@ func TestImportValidateDirectPostImportData(t *testing.T) { model.NewId(), model.NewId(), }, - Message: ptrStr("message"), - CreateAt: ptrInt64(model.GetMillis()), + Message: model.NewPointer("message"), + CreateAt: model.NewPointer(model.GetMillis()), } err = ValidateDirectPostImportData(&data, maxPostSize) require.NotNil(t, err, "Should have failed due to missing required property.") @@ -1161,8 +1206,8 @@ func TestImportValidateDirectPostImportData(t *testing.T) { model.NewId(), model.NewId(), }, - User: ptrStr("username"), - CreateAt: ptrInt64(model.GetMillis()), + User: model.NewPointer("username"), + CreateAt: model.NewPointer(model.GetMillis()), } err = ValidateDirectPostImportData(&data, maxPostSize) require.NotNil(t, err, "Should have failed due to missing required property.") @@ -1172,8 +1217,8 @@ func TestImportValidateDirectPostImportData(t *testing.T) { model.NewId(), model.NewId(), }, - User: ptrStr("username"), - Message: ptrStr("message"), + User: model.NewPointer("username"), + Message: model.NewPointer("message"), } err = ValidateDirectPostImportData(&data, maxPostSize) require.NotNil(t, err, "Should have failed due to missing required property.") @@ -1181,9 +1226,9 @@ func TestImportValidateDirectPostImportData(t *testing.T) { // Test with invalid numbers of channel members. data = DirectPostImportData{ ChannelMembers: &[]string{}, - User: ptrStr("username"), - Message: ptrStr("message"), - CreateAt: ptrInt64(model.GetMillis()), + User: model.NewPointer("username"), + Message: model.NewPointer("message"), + CreateAt: model.NewPointer(model.GetMillis()), } err = ValidateDirectPostImportData(&data, maxPostSize) require.NotNil(t, err, "Should have failed due to unsuitable number of members.") @@ -1192,9 +1237,9 @@ func TestImportValidateDirectPostImportData(t *testing.T) { ChannelMembers: &[]string{ model.NewId(), }, - User: ptrStr("username"), - Message: ptrStr("message"), - CreateAt: ptrInt64(model.GetMillis()), + User: model.NewPointer("username"), + Message: model.NewPointer("message"), + CreateAt: model.NewPointer(model.GetMillis()), } err = ValidateDirectPostImportData(&data, maxPostSize) require.NotNil(t, err, "Should have failed due to unsuitable number of members.") @@ -1212,9 +1257,9 @@ func TestImportValidateDirectPostImportData(t *testing.T) { model.NewId(), model.NewId(), }, - User: ptrStr("username"), - Message: ptrStr("message"), - CreateAt: ptrInt64(model.GetMillis()), + User: model.NewPointer("username"), + Message: model.NewPointer("message"), + CreateAt: model.NewPointer(model.GetMillis()), } err = ValidateDirectPostImportData(&data, maxPostSize) require.NotNil(t, err, "Should have failed due to unsuitable number of members.") @@ -1226,9 +1271,9 @@ func TestImportValidateDirectPostImportData(t *testing.T) { model.NewId(), model.NewId(), }, - User: ptrStr("username"), - Message: ptrStr("message"), - CreateAt: ptrInt64(model.GetMillis()), + User: model.NewPointer("username"), + Message: model.NewPointer("message"), + CreateAt: model.NewPointer(model.GetMillis()), } err = ValidateDirectPostImportData(&data, maxPostSize) require.Nil(t, err, "Validation failed but should have been valid.") @@ -1239,9 +1284,9 @@ func TestImportValidateDirectPostImportData(t *testing.T) { model.NewId(), model.NewId(), }, - User: ptrStr("username"), - Message: ptrStr(strings.Repeat("0", maxPostSize+1)), - CreateAt: ptrInt64(model.GetMillis()), + User: model.NewPointer("username"), + Message: model.NewPointer(strings.Repeat("0", maxPostSize+1)), + CreateAt: model.NewPointer(model.GetMillis()), } err = ValidateDirectPostImportData(&data, maxPostSize) require.NotNil(t, err, "Should have failed due to too long message.") @@ -1252,9 +1297,9 @@ func TestImportValidateDirectPostImportData(t *testing.T) { model.NewId(), model.NewId(), }, - User: ptrStr("username"), - Message: ptrStr("message"), - CreateAt: ptrInt64(0), + User: model.NewPointer("username"), + Message: model.NewPointer("message"), + CreateAt: model.NewPointer(int64(0)), } err = ValidateDirectPostImportData(&data, maxPostSize) require.NotNil(t, err, "Should have failed due to 0 create-at value.") @@ -1271,9 +1316,9 @@ func TestImportValidateDirectPostImportData(t *testing.T) { member1, model.NewId(), }, - User: ptrStr("username"), - Message: ptrStr("message"), - CreateAt: ptrInt64(model.GetMillis()), + User: model.NewPointer("username"), + Message: model.NewPointer("message"), + CreateAt: model.NewPointer(model.GetMillis()), } err = ValidateDirectPostImportData(&data, maxPostSize) require.NotNil(t, err, "Validation should have failed due to non-member flagged.") @@ -1288,24 +1333,24 @@ func TestImportValidateDirectPostImportData(t *testing.T) { member1, member2, }, - User: ptrStr("username"), - Message: ptrStr("message"), - CreateAt: ptrInt64(model.GetMillis()), + User: model.NewPointer("username"), + Message: model.NewPointer("message"), + CreateAt: model.NewPointer(model.GetMillis()), } err = ValidateDirectPostImportData(&data, maxPostSize) require.Nil(t, err, "Validation should succeed with post flagged by members") // Test with valid all optional parameters. reactions := []ReactionImportData{{ - User: ptrStr("username"), - EmojiName: ptrStr("emoji"), - CreateAt: ptrInt64(model.GetMillis()), + User: model.NewPointer("username"), + EmojiName: model.NewPointer("emoji"), + CreateAt: model.NewPointer(model.GetMillis()), }} replies := []ReplyImportData{{ - User: ptrStr("username"), - Message: ptrStr("message"), - CreateAt: ptrInt64(model.GetMillis()), + User: model.NewPointer("username"), + Message: model.NewPointer("message"), + CreateAt: model.NewPointer(model.GetMillis()), }} data = DirectPostImportData{ @@ -1317,9 +1362,9 @@ func TestImportValidateDirectPostImportData(t *testing.T) { member1, member2, }, - User: ptrStr("username"), - Message: ptrStr("message"), - CreateAt: ptrInt64(model.GetMillis()), + User: model.NewPointer("username"), + Message: model.NewPointer("message"), + CreateAt: model.NewPointer(model.GetMillis()), Reactions: &reactions, Replies: &replies, } @@ -1336,13 +1381,13 @@ func TestImportValidateEmojiImportData(t *testing.T) { expectError bool expectSystemEmoji bool }{ - {"success", ptrStr("parrot2"), ptrStr("/path/to/image"), false, false}, - {"system emoji", ptrStr("smiley"), ptrStr("/path/to/image"), true, true}, - {"empty name", ptrStr(""), ptrStr("/path/to/image"), true, false}, - {"empty image", ptrStr("parrot2"), ptrStr(""), true, false}, - {"empty name and image", ptrStr(""), ptrStr(""), true, false}, - {"nil name", nil, ptrStr("/path/to/image"), true, false}, - {"nil image", ptrStr("parrot2"), nil, true, false}, + {"success", model.NewPointer("parrot2"), model.NewPointer("/path/to/image"), false, false}, + {"system emoji", model.NewPointer("smiley"), model.NewPointer("/path/to/image"), true, true}, + {"empty name", model.NewPointer(""), model.NewPointer("/path/to/image"), true, false}, + {"empty image", model.NewPointer("parrot2"), model.NewPointer(""), true, false}, + {"empty name and image", model.NewPointer(""), model.NewPointer(""), true, false}, + {"nil name", nil, model.NewPointer("/path/to/image"), true, false}, + {"nil image", model.NewPointer("parrot2"), nil, true, false}, {"nil name and image", nil, nil, true, false}, } @@ -1364,18 +1409,6 @@ func TestImportValidateEmojiImportData(t *testing.T) { } } -func ptrStr(s string) *string { - return &s -} - -func ptrInt64(i int64) *int64 { - return &i -} - -func ptrBool(b bool) *bool { - return &b -} - func checkError(t *testing.T, err *model.AppError) { require.NotNil(t, err, "Should have returned an error.") } diff --git a/server/channels/store/sqlstore/channel_store.go b/server/channels/store/sqlstore/channel_store.go index 805406ddad..dce8d6ddcf 100644 --- a/server/channels/store/sqlstore/channel_store.go +++ b/server/channels/store/sqlstore/channel_store.go @@ -4088,7 +4088,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, COALESCE(UrgentMentionCount, 0) UrgentMentionCount, 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, MsgCountRoot, 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.Eq{"cm.ChannelId": channelIds}) @@ -4106,12 +4106,11 @@ func (s SqlChannelStore) GetAllDirectChannelsForExportAfter(limit int, afterId s // Populate each channel with its members dmChannelsMap := make(map[string]*model.DirectChannelForExport) for _, channel := range directChannelsForExport { - channel.Members = &[]string{} + channel.Members = []*model.ChannelMemberForExport{} dmChannelsMap[channel.Id] = channel } for _, member := range channelMembers { - members := dmChannelsMap[member.ChannelId].Members - *members = append(*members, member.Username) + dmChannelsMap[member.ChannelId].Members = append(dmChannelsMap[member.ChannelId].Members, member) } return directChannelsForExport, nil diff --git a/server/channels/store/storetest/channel_store.go b/server/channels/store/storetest/channel_store.go index 8f86b29426..a9877e0eaa 100644 --- a/server/channels/store/storetest/channel_store.go +++ b/server/channels/store/storetest/channel_store.go @@ -7852,7 +7852,7 @@ func testChannelStoreExportAllDirectChannelsDeletedChannel(t *testing.T, rctx re d1, nErr = ss.Channel().GetAllDirectChannelsForExportAfter(10000, strings.Repeat("0", 26), true) assert.NoError(t, nErr) assert.Len(t, d1, 1) - assert.Len(t, *d1[0].Members, 2) + assert.Len(t, d1[0].Members, 2) // Manually truncate Channels table until testlib can handle cleanups s.GetMasterX().Exec("TRUNCATE Channels") diff --git a/server/cmd/mmctl/commands/importer/validate.go b/server/cmd/mmctl/commands/importer/validate.go index 3e6fc92e1c..f9bac13d56 100644 --- a/server/cmd/mmctl/commands/importer/validate.go +++ b/server/cmd/mmctl/commands/importer/validate.go @@ -832,7 +832,17 @@ func (v *Validator) validateDirectChannel(info ImportFileInfo, line imports.Line } } - if data.Members != nil { + if data.Participants != nil { + for i, member := range data.Participants { + if _, ok := v.users[*member.Username]; !ok { + return &ImportValidationError{ + ImportFileInfo: info, + FieldName: fmt.Sprintf("direct_channel.members[%d]", i), + Err: fmt.Errorf("reference to unknown user %q", *member.Username), + } + } + } + } else if data.Members != nil { for i, member := range *data.Members { if _, ok := v.users[member]; !ok { return &ImportValidationError{ diff --git a/server/cmd/mmctl/commands/sampledata_util.go b/server/cmd/mmctl/commands/sampledata_util.go index ab8a052028..04f841c590 100644 --- a/server/cmd/mmctl/commands/sampledata_util.go +++ b/server/cmd/mmctl/commands/sampledata_util.go @@ -394,10 +394,17 @@ func createPost(team string, channel string, allUsers []string, createAt int64) func createDirectChannel(members []string) imports.LineImportData { header := fake.Sentence() + var p []*imports.DirectChannelMemberImportData + + for _, m := range members { + p = append(p, &imports.DirectChannelMemberImportData{ + Username: model.NewString(m), + }) + } channel := imports.DirectChannelImportData{ - Members: &members, - Header: &header, + Participants: p, + Header: &header, } return imports.LineImportData{ Type: "direct_channel", diff --git a/server/i18n/en.json b/server/i18n/en.json index 4b2da1e64e..4e7caf260d 100644 --- a/server/i18n/en.json +++ b/server/i18n/en.json @@ -5174,6 +5174,14 @@ "id": "app.import.import_direct_channel.create_group_channel.error", "translation": "Failed to create group channel" }, + { + "id": "app.import.import_direct_channel.get_channel_members.error", + "translation": "Failed to get channel members for direct channel" + }, + { + "id": "app.import.import_direct_channel.no_members.error", + "translation": "There are no members for the direct channel" + }, { "id": "app.import.import_direct_channel.update_header_failed.error", "translation": "Failed to update direct channel header" diff --git a/server/public/model/channel.go b/server/public/model/channel.go index 4f9b4cf4d0..547ca30618 100644 --- a/server/public/model/channel.go +++ b/server/public/model/channel.go @@ -122,7 +122,7 @@ type ChannelForExport struct { type DirectChannelForExport struct { Channel - Members *[]string + Members []*ChannelMemberForExport } type ChannelModeration struct {