MM-39058 - invite people to join team and channels (#19849)

* MM-39058-invite-to-team-from-add-channel

* fix tests by validating the memberInvite is not nil

* fix i18n texts

* fix lint problem

* fix translation lines

* fix the data structure

* modify api4-team_local file to match with the expected structure

* fix unit tests, fix translation tests

* remove go routine cause not necesary

* add unit test for invite to team and channel

* remove unnecessary validation

* allow both data structures, simple string array and object with memberInvite struct

* fix texts

* fix linter

* fix problems with graceful invites workflow

* handle error while parsing body

* fix unit tests

* take the address just once

* rename channels to channelIds

* fix unit tests

* add tests and fix local channels invite support

Co-authored-by: Pablo Velez Vidal <pablo.velez@mattermost.com>
Этот коммит содержится в:
Pablo Andrés Vélez Vidal
2022-04-08 12:20:44 -05:00
коммит произвёл GitHub
родитель 0e4b0b6939
Коммит 4b354685e9
16 изменённых файлов: 517 добавлений и 52 удалений

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

@@ -817,7 +817,7 @@ type AppIface interface {
InviteGuestsToChannels(teamID string, guestsInvite *model.GuestsInvite, senderId string) *model.AppError
InviteGuestsToChannelsGracefully(teamID string, guestsInvite *model.GuestsInvite, senderId string) ([]*model.EmailInviteWithError, *model.AppError)
InviteNewUsersToTeam(emailList []string, teamID, senderId string) *model.AppError
InviteNewUsersToTeamGracefully(emailList []string, teamID, senderId string, reminderInterval string) ([]*model.EmailInviteWithError, *model.AppError)
InviteNewUsersToTeamGracefully(memberInvite *model.MemberInvite, teamID, senderId string, reminderInterval string) ([]*model.EmailInviteWithError, *model.AppError)
IsCRTEnabledForUser(userID string) bool
IsFirstUserAccount() bool
IsLeader() bool

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

@@ -606,6 +606,153 @@ func (es *Service) SendGuestInviteEmails(team *model.Team, channels []*model.Cha
return nil
}
func (es *Service) SendInviteEmailsToTeamAndChannels(
team *model.Team,
channels []*model.Channel,
senderName string,
senderUserId string,
senderProfileImage []byte,
invites []string,
siteURL string,
reminderData *model.TeamInviteReminderData,
message string,
errorWhenNotSent bool,
) ([]*model.EmailInviteWithError, error) {
if es.perHourEmailRateLimiter == nil {
return nil, NoRateLimiterError
}
rateLimited, result, err := es.perHourEmailRateLimiter.RateLimit(senderUserId, len(invites))
if err != nil {
return nil, SetupRateLimiterError
}
if rateLimited {
mlog.Error("rate limit exceeded", mlog.Duration("RetryAfter", result.RetryAfter), mlog.Duration("ResetAfter", result.ResetAfter), mlog.String("user_id", senderUserId),
mlog.String("team_id", team.Id), mlog.String("retry_after_secs", fmt.Sprintf("%f", result.RetryAfter.Seconds())), mlog.String("reset_after_secs", fmt.Sprintf("%f", result.ResetAfter.Seconds())))
return nil, RateLimitExceededError
}
channelsLen := len(channels)
subject := i18n.T("api.templates.invite_team_and_channels_subject", map[string]interface{}{
"SenderName": senderName,
"TeamDisplayName": team.DisplayName,
"ChannelsLen": channelsLen,
"SiteName": es.config().TeamSettings.SiteName})
title := i18n.T("api.templates.invite_team_and_channels_body.title", map[string]interface{}{
"SenderName": senderName,
"ChannelsLen": channelsLen,
"TeamDisplayName": team.DisplayName})
if channelsLen == 1 {
channelName := channels[0].DisplayName
subject = i18n.T("api.templates.invite_team_and_channel_subject",
map[string]interface{}{"SenderName": senderName,
"TeamDisplayName": team.DisplayName,
"ChannelName": channelName,
"SiteName": es.config().TeamSettings.SiteName},
)
title = i18n.T("api.templates.invite_team_and_channel_body.title", map[string]interface{}{
"SenderName": senderName,
"ChannelName": channelName,
"TeamDisplayName": team.DisplayName,
})
}
var invitesWithErrors []*model.EmailInviteWithError
for _, invite := range invites {
if invite == "" {
continue
}
channelIDs := []string{}
for _, channel := range channels {
channelIDs = append(channelIDs, channel.Id)
}
data := es.NewEmailTemplateData("")
data.Props["SiteURL"] = siteURL
data.Props["SubTitle"] = i18n.T("api.templates.invite_body.subTitle")
data.Props["Button"] = i18n.T("api.templates.invite_body.button")
data.Props["SenderName"] = senderName
data.Props["InviteFooterTitle"] = i18n.T("api.templates.invite_body_footer.title")
data.Props["InviteFooterInfo"] = i18n.T("api.templates.invite_body_footer.info")
data.Props["InviteFooterLearnMore"] = i18n.T("api.templates.invite_body_footer.learn_more")
if message != "" {
message = bluemonday.NewPolicy().Sanitize(message)
}
data.Props["Message"] = message
token := model.NewToken(
TokenTypeTeamInvitation,
model.MapToJSON(map[string]string{
"teamId": team.Id,
"email": invite,
"channels": strings.Join(channelIDs, " "),
}),
)
tokenProps := make(map[string]string)
tokenProps["email"] = invite
tokenProps["display_name"] = team.DisplayName
tokenProps["name"] = team.Name
if reminderData != nil {
reminder := i18n.T("api.templates.invite_body.title.reminder")
title = fmt.Sprintf("%s: %s", reminder, title)
tokenProps["reminder_interval"] = reminderData.Interval
}
data.Props["Title"] = title
tokenData := model.MapToJSON(tokenProps)
if err := es.store.Token().Save(token); err != nil {
mlog.Error("Failed to send invite email successfully ", mlog.Err(err))
continue
}
data.Props["ButtonURL"] = fmt.Sprintf("%s/signup_user_complete/?d=%s&t=%s", siteURL, url.QueryEscape(tokenData), url.QueryEscape(token.Token))
senderPhoto := ""
embeddedFiles := make(map[string]io.Reader)
if message != "" {
if senderProfileImage != nil {
senderPhoto = "user-avatar.png"
embeddedFiles = map[string]io.Reader{
senderPhoto: bytes.NewReader(senderProfileImage),
}
}
}
pData := postData{
SenderName: senderName,
Message: template.HTML(message),
SenderPhoto: senderPhoto,
}
data.Props["Posts"] = []postData{pData}
body, err := es.templatesContainer.RenderToString("invite_body", data)
if err != nil {
mlog.Error("Failed to send invite email successfully ", mlog.Err(err))
}
if nErr := es.SendMailWithEmbeddedFiles(invite, subject, body, embeddedFiles); nErr != nil {
mlog.Error("Failed to send invite email successfully", mlog.Err(nErr))
if errorWhenNotSent {
inviteWithError := &model.EmailInviteWithError{
Email: invite,
Error: &model.AppError{Message: nErr.Error()},
}
invitesWithErrors = append(invitesWithErrors, inviteWithError)
}
}
}
return invitesWithErrors, nil
}
func (es *Service) NewEmailTemplateData(locale string) templates.Data {
var localT i18n.TranslateFunc
if locale != "" {

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

@@ -237,6 +237,29 @@ func (_m *ServiceInterface) SendInviteEmails(team *model.Team, senderName string
return r0
}
// SendInviteEmailsToTeamAndChannels provides a mock function with given fields: team, channels, senderName, senderUserId, senderProfileImage, invites, siteURL, reminderData, message, errorWhenNotSent
func (_m *ServiceInterface) SendInviteEmailsToTeamAndChannels(team *model.Team, channels []*model.Channel, senderName string, senderUserId string, senderProfileImage []byte, invites []string, siteURL string, reminderData *model.TeamInviteReminderData, message string, errorWhenNotSent bool) ([]*model.EmailInviteWithError, error) {
ret := _m.Called(team, channels, senderName, senderUserId, senderProfileImage, invites, siteURL, reminderData, message, errorWhenNotSent)
var r0 []*model.EmailInviteWithError
if rf, ok := ret.Get(0).(func(*model.Team, []*model.Channel, string, string, []byte, []string, string, *model.TeamInviteReminderData, string, bool) []*model.EmailInviteWithError); ok {
r0 = rf(team, channels, senderName, senderUserId, senderProfileImage, invites, siteURL, reminderData, message, errorWhenNotSent)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*model.EmailInviteWithError)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(*model.Team, []*model.Channel, string, string, []byte, []string, string, *model.TeamInviteReminderData, string, bool) error); ok {
r1 = rf(team, channels, senderName, senderUserId, senderProfileImage, invites, siteURL, reminderData, message, errorWhenNotSent)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// SendLicenseInactivityEmail provides a mock function with given fields: _a0, name, locale, siteURL
func (_m *ServiceInterface) SendLicenseInactivityEmail(_a0 string, name string, locale string, siteURL string) error {
ret := _m.Called(_a0, name, locale, siteURL)

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

@@ -138,6 +138,7 @@ type ServiceInterface interface {
SendMfaChangeEmail(email string, activated bool, locale, siteURL string) error
SendInviteEmails(team *model.Team, senderName string, senderUserId string, invites []string, siteURL string, reminderData *model.TeamInviteReminderData, errorWhenNotSent bool) error
SendGuestInviteEmails(team *model.Team, channels []*model.Channel, senderName string, senderUserId string, senderProfileImage []byte, invites []string, siteURL string, message string, errorWhenNotSent bool) error
SendInviteEmailsToTeamAndChannels(team *model.Team, channels []*model.Channel, senderName string, senderUserId string, senderProfileImage []byte, invites []string, siteURL string, reminderData *model.TeamInviteReminderData, message string, errorWhenNotSent bool) ([]*model.EmailInviteWithError, error)
SendDeactivateAccountEmail(email string, locale, siteURL string) error
SendNotificationMail(to, subject, htmlBody string) error
SendMailWithEmbeddedFiles(to, subject, htmlBody string, embeddedFiles map[string]io.Reader) error

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

@@ -25,16 +25,17 @@ func TestSendInviteEmailRateLimits(t *testing.T) {
*cfg.ServiceSettings.EnableEmailInvitations = true
})
emailList := make([]string, 22)
memberInvite := &model.MemberInvite{}
memberInvite.Emails = make([]string, 22)
for i := 0; i < 22; i++ {
emailList[i] = "test-" + strconv.Itoa(i) + "@common.com"
memberInvite.Emails[i] = "test-" + strconv.Itoa(i) + "@common.com"
}
err = th.App.InviteNewUsersToTeam(emailList, th.BasicTeam.Id, th.BasicUser.Id)
err = th.App.InviteNewUsersToTeam(memberInvite.Emails, th.BasicTeam.Id, th.BasicUser.Id)
require.NotNil(t, err)
assert.Equal(t, "app.email.rate_limit_exceeded.app_error", err.Id)
assert.Equal(t, http.StatusRequestEntityTooLarge, err.StatusCode)
_, err = th.App.InviteNewUsersToTeamGracefully(emailList, th.BasicTeam.Id, th.BasicUser.Id, "")
_, err = th.App.InviteNewUsersToTeamGracefully(memberInvite, th.BasicTeam.Id, th.BasicUser.Id, "")
require.NotNil(t, err)
assert.Equal(t, "app.email.rate_limit_exceeded.app_error", err.Id)
assert.Equal(t, http.StatusRequestEntityTooLarge, err.StatusCode)

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

@@ -10966,7 +10966,7 @@ func (a *OpenTracingAppLayer) InviteNewUsersToTeam(emailList []string, teamID st
return resultVar0
}
func (a *OpenTracingAppLayer) InviteNewUsersToTeamGracefully(emailList []string, teamID string, senderId string, reminderInterval string) ([]*model.EmailInviteWithError, *model.AppError) {
func (a *OpenTracingAppLayer) InviteNewUsersToTeamGracefully(memberInvite *model.MemberInvite, teamID string, senderId string, reminderInterval string) ([]*model.EmailInviteWithError, *model.AppError) {
origCtx := a.ctx
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.InviteNewUsersToTeamGracefully")
@@ -10978,7 +10978,7 @@ func (a *OpenTracingAppLayer) InviteNewUsersToTeamGracefully(emailList []string,
}()
defer span.Finish()
resultVar0, resultVar1 := a.app.InviteNewUsersToTeamGracefully(emailList, teamID, senderId, reminderInterval)
resultVar0, resultVar1 := a.app.InviteNewUsersToTeamGracefully(memberInvite, teamID, senderId, reminderInterval)
if resultVar1 != nil {
span.LogFields(spanlog.Error(resultVar1))

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

@@ -1207,7 +1207,7 @@ func (a *App) postRemoveFromTeamMessage(c *request.Context, user *model.User, ch
return nil
}
func (a *App) prepareInviteNewUsersToTeam(teamID, senderId string) (*model.User, *model.Team, *model.AppError) {
func (a *App) prepareInviteNewUsersToTeam(teamID, senderId string, channelIds []string) (*model.User, *model.Team, []*model.Channel, *model.AppError) {
tchan := make(chan store.StoreResult, 1)
go func() {
team, err := a.Srv().Store.Team().Get(teamID)
@@ -1222,14 +1222,22 @@ func (a *App) prepareInviteNewUsersToTeam(teamID, senderId string) (*model.User,
close(uchan)
}()
var channels []*model.Channel
var err error
if len(channelIds) > 0 {
channels, err = a.Srv().Store.Channel().GetChannelsByIds(channelIds, false)
if err != nil {
return nil, nil, nil, model.NewAppError("prepareInviteNewUsersToTeam", "app.channel.get_channels_by_ids.app_error", nil, err.Error(), http.StatusInternalServerError)
}
}
result := <-tchan
if result.NErr != nil {
var nfErr *store.ErrNotFound
switch {
case errors.As(result.NErr, &nfErr):
return nil, nil, model.NewAppError("prepareInviteNewUsersToTeam", "app.team.get_by_invite_id.finding.app_error", nil, nfErr.Error(), http.StatusNotFound)
return nil, nil, nil, model.NewAppError("prepareInviteNewUsersToTeam", "app.team.get_by_invite_id.finding.app_error", nil, nfErr.Error(), http.StatusNotFound)
default:
return nil, nil, model.NewAppError("prepareInviteNewUsersToTeam", "app.team.get_by_invite_id.finding.app_error", nil, result.NErr.Error(), http.StatusInternalServerError)
return nil, nil, nil, model.NewAppError("prepareInviteNewUsersToTeam", "app.team.get_by_invite_id.finding.app_error", nil, result.NErr.Error(), http.StatusInternalServerError)
}
}
team := result.Data.(*model.Team)
@@ -1239,25 +1247,34 @@ func (a *App) prepareInviteNewUsersToTeam(teamID, senderId string) (*model.User,
var nfErr *store.ErrNotFound
switch {
case errors.As(result.NErr, &nfErr):
return nil, nil, model.NewAppError("prepareInviteNewUsersToTeam", MissingAccountError, nil, nfErr.Error(), http.StatusNotFound)
return nil, nil, nil, model.NewAppError("prepareInviteNewUsersToTeam", MissingAccountError, nil, nfErr.Error(), http.StatusNotFound)
default:
return nil, nil, model.NewAppError("prepareInviteNewUsersToTeam", "app.user.get.app_error", nil, result.NErr.Error(), http.StatusInternalServerError)
return nil, nil, nil, model.NewAppError("prepareInviteNewUsersToTeam", "app.user.get.app_error", nil, result.NErr.Error(), http.StatusInternalServerError)
}
}
user := result.Data.(*model.User)
return user, team, nil
for _, channel := range channels {
if channel.TeamId != teamID {
return nil, nil, nil, model.NewAppError("prepareInviteGuestsToChannels", "api.team.invite_guests.channel_in_invalid_team.app_error", nil, "", http.StatusBadRequest)
}
}
return user, team, channels, nil
}
func (a *App) InviteNewUsersToTeamGracefully(emailList []string, teamID, senderId string, reminderInterval string) ([]*model.EmailInviteWithError, *model.AppError) {
func (a *App) InviteNewUsersToTeamGracefully(memberInvite *model.MemberInvite, teamID, senderId string, reminderInterval string) ([]*model.EmailInviteWithError, *model.AppError) {
if !*a.Config().ServiceSettings.EnableEmailInvitations {
return nil, model.NewAppError("InviteNewUsersToTeam", "api.team.invite_members.disabled.app_error", nil, "", http.StatusNotImplemented)
}
emailList := memberInvite.Emails
if len(emailList) == 0 {
err := model.NewAppError("InviteNewUsersToTeam", "api.team.invite_members.no_one.app_error", nil, "", http.StatusBadRequest)
return nil, err
}
user, team, err := a.prepareInviteNewUsersToTeam(teamID, senderId)
user, team, channels, err := a.prepareInviteNewUsersToTeam(teamID, senderId, memberInvite.ChannelIds)
if err != nil {
return nil, err
}
@@ -1284,25 +1301,36 @@ func (a *App) InviteNewUsersToTeamGracefully(emailList []string, teamID, senderI
if len(goodEmails) > 0 {
nameFormat := *a.Config().TeamSettings.TeammateNameDisplay
eErr := a.Srv().EmailService.SendInviteEmails(team, user.GetDisplayName(nameFormat), user.Id, goodEmails, a.GetSiteURL(), reminderData, true)
senderProfileImage, _, err := a.GetProfileImage(user)
if err != nil {
a.Log().Warn("Unable to get the sender user profile image.", mlog.String("user_id", user.Id), mlog.String("team_id", team.Id), mlog.Err(err))
}
var eErr error
var invitesWithErrors2 []*model.EmailInviteWithError
if len(channels) > 0 {
invitesWithErrors2, eErr = a.Srv().EmailService.SendInviteEmailsToTeamAndChannels(team, channels, user.GetDisplayName(nameFormat), user.Id, senderProfileImage, goodEmails, a.GetSiteURL(), reminderData, memberInvite.Message, true)
inviteListWithErrors = append(inviteListWithErrors, invitesWithErrors2...)
} else {
eErr = a.Srv().EmailService.SendInviteEmails(team, user.GetDisplayName(nameFormat), user.Id, goodEmails, a.GetSiteURL(), reminderData, true)
}
if eErr != nil {
switch {
case errors.Is(eErr, email.SendMailError):
for i := range inviteListWithErrors {
if inviteListWithErrors[i].Error == nil {
if *a.Config().EmailSettings.SMTPServer == model.EmailSMTPDefaultServer && *a.Config().EmailSettings.SMTPPort == model.EmailSMTPDefaultPort {
inviteListWithErrors[i].Error = model.NewAppError("InviteGuestsToChannelsGracefully", "api.team.invite_members.unable_to_send_email_with_defaults.app_error", nil, "", http.StatusInternalServerError)
inviteListWithErrors[i].Error = model.NewAppError("InviteNewUsersToTeamGracefully", "api.team.invite_members.unable_to_send_email_with_defaults.app_error", nil, "", http.StatusInternalServerError)
} else {
inviteListWithErrors[i].Error = model.NewAppError("SendInviteEmails", "api.team.invite_members.unable_to_send_email.app_error", nil, "", http.StatusInternalServerError)
inviteListWithErrors[i].Error = model.NewAppError("InviteNewUsersToTeamGracefully", "api.team.invite_members.unable_to_send_email.app_error", nil, "", http.StatusInternalServerError)
}
}
}
case errors.Is(eErr, email.NoRateLimiterError):
return nil, model.NewAppError("SendInviteEmails", "app.email.no_rate_limiter.app_error", nil, fmt.Sprintf("user_id=%s, team_id=%s", user.Id, team.Id), http.StatusInternalServerError)
return nil, model.NewAppError("InviteNewUsersToTeamGracefully", "app.email.no_rate_limiter.app_error", nil, fmt.Sprintf("user_id=%s, team_id=%s", user.Id, team.Id), http.StatusInternalServerError)
case errors.Is(eErr, email.SetupRateLimiterError):
return nil, model.NewAppError("SendInviteEmails", "app.email.setup_rate_limiter.app_error", nil, fmt.Sprintf("user_id=%s, team_id=%s, error=%v", user.Id, team.Id, eErr), http.StatusInternalServerError)
return nil, model.NewAppError("InviteNewUsersToTeamGracefully", "app.email.setup_rate_limiter.app_error", nil, fmt.Sprintf("user_id=%s, team_id=%s, error=%v", user.Id, team.Id, eErr), http.StatusInternalServerError)
default:
return nil, model.NewAppError("SendInviteEmails", "app.email.rate_limit_exceeded.app_error", nil, fmt.Sprintf("user_id=%s, team_id=%s, error=%v", user.Id, team.Id, eErr), http.StatusRequestEntityTooLarge)
return nil, model.NewAppError("InviteNewUsersToTeamGracefully", "app.email.rate_limit_exceeded.app_error", nil, fmt.Sprintf("user_id=%s, team_id=%s, error=%v", user.Id, team.Id, eErr), http.StatusRequestEntityTooLarge)
}
}
}
@@ -1440,7 +1468,7 @@ func (a *App) InviteNewUsersToTeam(emailList []string, teamID, senderId string)
return err
}
user, team, err := a.prepareInviteNewUsersToTeam(teamID, senderId)
user, team, _, err := a.prepareInviteNewUsersToTeam(teamID, senderId, []string{})
if err != nil {
return err
}

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

@@ -1255,6 +1255,79 @@ func TestInviteNewUsersToTeamGracefully(t *testing.T) {
t.Run("it return list of email with no error on success", func(t *testing.T) {
emailServiceMock := emailmocks.ServiceInterface{}
memberInvite := &model.MemberInvite{
Emails: []string{"idontexist@mattermost.com"},
}
emailServiceMock.On("SendInviteEmails",
mock.AnythingOfType("*model.Team"),
mock.AnythingOfType("string"),
mock.AnythingOfType("string"),
memberInvite.Emails,
"",
mock.Anything,
true,
).Once().Return(nil)
th.App.Srv().EmailService = &emailServiceMock
res, err := th.App.InviteNewUsersToTeamGracefully(memberInvite, th.BasicTeam.Id, th.BasicUser.Id, "")
require.Nil(t, err)
require.Len(t, res, 1)
require.Nil(t, res[0].Error)
})
t.Run("it should assign errors to emails when failing to send", func(t *testing.T) {
emailServiceMock := emailmocks.ServiceInterface{}
memberInvite := &model.MemberInvite{
Emails: []string{"idontexist@mattermost.com"},
}
emailServiceMock.On("SendInviteEmails",
mock.AnythingOfType("*model.Team"),
mock.AnythingOfType("string"),
mock.AnythingOfType("string"),
memberInvite.Emails,
"",
mock.Anything,
true,
).Once().Return(email.SendMailError)
th.App.Srv().EmailService = &emailServiceMock
res, err := th.App.InviteNewUsersToTeamGracefully(memberInvite, th.BasicTeam.Id, th.BasicUser.Id, "")
require.Nil(t, err)
require.Len(t, res, 1)
require.NotNil(t, res[0].Error)
})
t.Run("it return list of email with no error when inviting to team and channels using memberInvite struct", func(t *testing.T) {
emailServiceMock := emailmocks.ServiceInterface{}
memberInvite := &model.MemberInvite{
Emails: []string{"idontexist@mattermost.com"},
ChannelIds: []string{th.BasicChannel.Id},
}
emailServiceMock.On("SendInviteEmailsToTeamAndChannels",
mock.AnythingOfType("*model.Team"),
mock.AnythingOfType("[]*model.Channel"),
mock.AnythingOfType("string"),
mock.AnythingOfType("string"),
mock.AnythingOfType("[]uint8"),
memberInvite.Emails,
"",
mock.Anything,
mock.AnythingOfType("string"),
true,
).Once().Return([]*model.EmailInviteWithError{}, nil)
th.App.Srv().EmailService = &emailServiceMock
res, err := th.App.InviteNewUsersToTeamGracefully(memberInvite, th.BasicTeam.Id, th.BasicUser.Id, "")
require.Nil(t, err)
require.Len(t, res, 1)
require.Nil(t, res[0].Error)
})
t.Run("it return list of email with no error when inviting to team and channels using plain emails array", func(t *testing.T) {
emailServiceMock := emailmocks.ServiceInterface{}
memberInvite := &model.MemberInvite{
Emails: []string{"idontexist@mattermost.com"},
}
emailServiceMock.On("SendInviteEmails",
mock.AnythingOfType("*model.Team"),
mock.AnythingOfType("string"),
@@ -1266,30 +1339,11 @@ func TestInviteNewUsersToTeamGracefully(t *testing.T) {
).Once().Return(nil)
th.App.Srv().EmailService = &emailServiceMock
res, err := th.App.InviteNewUsersToTeamGracefully([]string{"idontexist@mattermost.com"}, th.BasicTeam.Id, th.BasicUser.Id, "")
res, err := th.App.InviteNewUsersToTeamGracefully(memberInvite, th.BasicTeam.Id, th.BasicUser.Id, "")
require.Nil(t, err)
require.Len(t, res, 1)
require.Nil(t, res[0].Error)
})
t.Run("it should assign errors to emails when failing to send", func(t *testing.T) {
emailServiceMock := emailmocks.ServiceInterface{}
emailServiceMock.On("SendInviteEmails",
mock.AnythingOfType("*model.Team"),
mock.AnythingOfType("string"),
mock.AnythingOfType("string"),
[]string{"idontexist@mattermost.com"},
"",
mock.Anything,
true,
).Once().Return(email.SendMailError)
th.App.Srv().EmailService = &emailServiceMock
res, err := th.App.InviteNewUsersToTeamGracefully([]string{"idontexist@mattermost.com"}, th.BasicTeam.Id, th.BasicUser.Id, "")
require.Nil(t, err)
require.Len(t, res, 1)
require.NotNil(t, res[0].Error)
})
}
func TestInviteGuestsToChannelsGracefully(t *testing.T) {

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

@@ -99,7 +99,7 @@ func (a *App) CreateUserWithToken(c *request.Context, user *model.User, token *m
a.AddDirectChannels(team.Id, ruser)
if token.Type == TokenTypeGuestInvitation {
if token.Type == TokenTypeGuestInvitation || (token.Type == TokenTypeTeamInvitation && len(channels) > 0) {
for _, channel := range channels {
_, err := a.AddChannelMember(c, ruser.Id, channel, ChannelMemberOpts{})
if err != nil {