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 удалений

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

@@ -1275,7 +1275,18 @@ func inviteUsersToTeam(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
emailList := model.ArrayFromJSON(r.Body)
bf, err := io.ReadAll(r.Body)
if err != nil {
c.Err = model.NewAppError("Api4.inviteUsersToTeams", "api.team.invite_members_to_team_and_channels.invalid_body.app_error", nil, err.Error(), http.StatusBadRequest)
return
}
memberInvite := &model.MemberInvite{}
if jsonErr := json.Unmarshal(bf, memberInvite); jsonErr != nil {
c.Err = model.NewAppError("Api4.inviteUsersToTeams", "api.team.invite_members_to_team_and_channels.invalid_body_parsing.app_error", nil, jsonErr.Error(), http.StatusBadRequest)
return
}
emailList := memberInvite.Emails
for i := range emailList {
emailList[i] = strings.ToLower(emailList[i])
@@ -1292,11 +1303,16 @@ func inviteUsersToTeam(c *Context, w http.ResponseWriter, r *http.Request) {
auditRec.AddMeta("count", len(emailList))
auditRec.AddMeta("emails", emailList)
if len(memberInvite.ChannelIds) > 0 {
auditRec.AddMeta("channel_count", len(memberInvite.ChannelIds))
auditRec.AddMeta("channels", memberInvite.ChannelIds)
}
if graceful {
var invitesWithError []*model.EmailInviteWithError
var err *model.AppError
if emailList != nil {
invitesWithError, err = c.App.InviteNewUsersToTeamGracefully(emailList, c.Params.TeamId, c.AppContext.Session().UserId, "")
invitesWithError, err = c.App.InviteNewUsersToTeamGracefully(memberInvite, c.Params.TeamId, c.AppContext.Session().UserId, "")
}
if invitesWithError != nil {
@@ -1322,6 +1338,10 @@ func inviteUsersToTeam(c *Context, w http.ResponseWriter, r *http.Request) {
"scheduledAt": strconv.FormatInt(scheduledAt, 10),
}
if len(memberInvite.ChannelIds) > 0 {
jobData["channelList"] = model.ArrayToJSON(memberInvite.ChannelIds)
}
// we then manually schedule the job to send another invite after 48 hours
_, e := c.App.Srv().Jobs.CreateJob(model.JobTypeResendInvitationEmail, jobData)
if e != nil {

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

@@ -6,6 +6,7 @@ package api4
import (
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
@@ -76,7 +77,19 @@ func localInviteUsersToTeam(c *Context, w http.ResponseWriter, r *http.Request)
return
}
emailList := model.ArrayFromJSON(r.Body)
bf, err := io.ReadAll(r.Body)
if err != nil {
c.Err = model.NewAppError("Api4.inviteUsersToTeams", "api.team.invite_members_to_team_and_channels.invalid_body.app_error", nil, err.Error(), http.StatusBadRequest)
return
}
memberInvite := &model.MemberInvite{}
if jsonErr := json.Unmarshal(bf, memberInvite); jsonErr != nil {
c.Err = model.NewAppError("Api4.inviteUsersToTeams", "api.team.invite_members_to_team_and_channels.invalid_body_parsing.app_error", nil, jsonErr.Error(), http.StatusBadRequest)
return
}
emailList := memberInvite.Emails
if len(emailList) == 0 {
c.SetInvalidParam("user_email")
return
@@ -96,6 +109,11 @@ func localInviteUsersToTeam(c *Context, w http.ResponseWriter, r *http.Request)
auditRec.AddMeta("count", len(emailList))
auditRec.AddMeta("emails", emailList)
if len(memberInvite.ChannelIds) > 0 {
auditRec.AddMeta("channel_count", len(memberInvite.ChannelIds))
auditRec.AddMeta("channels", memberInvite.ChannelIds)
}
team, nErr := c.App.Srv().Store.Team().Get(c.Params.TeamId)
if nErr != nil {
var nfErr *store.ErrNotFound
@@ -110,6 +128,14 @@ func localInviteUsersToTeam(c *Context, w http.ResponseWriter, r *http.Request)
allowedDomains := []string{team.AllowedDomains, *c.App.Config().TeamSettings.RestrictCreationToDomains}
var channels []*model.Channel
if len(memberInvite.ChannelIds) > 0 {
channels, err = c.App.Srv().Store.Channel().GetChannelsByIds(memberInvite.ChannelIds, false)
if err != nil {
c.Err = model.NewAppError("prepareLocalInviteNewUsersToTeam", "app.channel.get_channels_by_ids.app_error", nil, err.Error(), http.StatusInternalServerError)
}
}
if r.URL.Query().Get("graceful") != "" {
var invitesWithErrors []*model.EmailInviteWithError
var goodEmails, errList []string
@@ -128,8 +154,16 @@ func localInviteUsersToTeam(c *Context, w http.ResponseWriter, r *http.Request)
}
auditRec.AddMeta("errors", errList)
if len(goodEmails) > 0 {
err := c.App.Srv().EmailService.SendInviteEmails(team, "Administrator", "mmctl "+model.NewId(), goodEmails, *c.App.Config().ServiceSettings.SiteURL, nil, false)
if err != nil {
var eErr error
var invitesWithErrors2 []*model.EmailInviteWithError
if len(channels) > 0 {
invitesWithErrors2, eErr = c.App.Srv().EmailService.SendInviteEmailsToTeamAndChannels(team, channels, "Administrator", "mmctl "+model.NewId(), nil, goodEmails, *c.App.Config().ServiceSettings.SiteURL, nil, memberInvite.Message, true)
invitesWithErrors = append(invitesWithErrors, invitesWithErrors2...)
} else {
eErr = c.App.Srv().EmailService.SendInviteEmails(team, "Administrator", "mmctl "+model.NewId(), goodEmails, *c.App.Config().ServiceSettings.SiteURL, nil, false)
}
if eErr != nil {
switch {
case errors.Is(err, email.NoRateLimiterError):
c.Err = model.NewAppError("SendInviteEmails", "app.email.no_rate_limiter.app_error", nil, fmt.Sprintf("team_id=%s", team.Id), http.StatusInternalServerError)

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

@@ -2982,7 +2982,8 @@ func TestInviteUsersToTeam(t *testing.T) {
user1 := th.GenerateTestEmail()
user2 := th.GenerateTestEmail()
emailList := []string{user1, user2}
memberInvite := &model.MemberInvite{Emails: []string{user1, user2}}
emailList := memberInvite.Emails
//Delete all the messages before check the sample email
mail.DeleteMailBox(user1)
@@ -3018,7 +3019,7 @@ func TestInviteUsersToTeam(t *testing.T) {
require.True(t, strings.ContainsAny(resultsMailbox[len(resultsMailbox)-1].To[0], email), "Wrong To recipient")
resultsEmail, err := mail.GetMessageFromMailbox(email, resultsMailbox[len(resultsMailbox)-1].ID)
if err == nil {
require.Equalf(t, resultsEmail.Subject, expectedSubject, "Wrong Subject, actual: %s, expected: %s", resultsEmail.Subject, expectedSubject)
require.Equalf(t, resultsEmail.Subject, expectedSubject, "Wrong Subject, \nactual: %s, \nexpected: %s", resultsEmail.Subject, expectedSubject)
}
}
}
@@ -3034,6 +3035,18 @@ func TestInviteUsersToTeam(t *testing.T) {
"SiteName": th.App.ClientConfig()["SiteName"]})
checkEmail(t, expectedSubject)
// Test the invite to team and channel
mail.DeleteMailBox(user1)
mail.DeleteMailBox(user2)
_, _, err = th.SystemAdminClient.InviteUsersToTeamAndChannelsGracefully(th.BasicTeam.Id, []string{user1, user2}, []string{th.BasicChannel.Id}, "")
require.NoError(t, err)
expectedSubject = i18n.T("api.templates.invite_team_and_channel_subject",
map[string]interface{}{"SenderName": th.SystemAdminUser.GetDisplayName(nameFormat),
"TeamDisplayName": th.BasicTeam.DisplayName,
"ChannelName": th.BasicChannel.DisplayName,
"SiteName": th.App.ClientConfig()["SiteName"]})
checkEmail(t, expectedSubject)
mail.DeleteMailBox(user1)
mail.DeleteMailBox(user2)
_, err = th.LocalClient.InviteUsersToTeam(th.BasicTeam.Id, emailList)
@@ -3044,6 +3057,18 @@ func TestInviteUsersToTeam(t *testing.T) {
"SiteName": th.App.ClientConfig()["SiteName"]})
checkEmail(t, expectedSubject)
// Test the invite local to team and channel
mail.DeleteMailBox(user1)
mail.DeleteMailBox(user2)
_, _, err = th.LocalClient.InviteUsersToTeamAndChannelsGracefully(th.BasicTeam.Id, []string{user1, user2}, []string{th.BasicChannel.Id}, "")
require.NoError(t, err)
expectedSubject = i18n.T("api.templates.invite_team_and_channel_subject",
map[string]interface{}{"SenderName": "Administrator",
"TeamDisplayName": th.BasicTeam.DisplayName,
"ChannelName": th.BasicChannel.DisplayName,
"SiteName": th.App.ClientConfig()["SiteName"]})
checkEmail(t, expectedSubject)
th.App.UpdateConfig(func(cfg *model.Config) { *cfg.TeamSettings.RestrictCreationToDomains = "@global.com,@common.com" })
th.TestForAllClients(t, func(t *testing.T, client *model.Client4) {