MM-20977 - Inviting multiple users with valid/allowed and inva… (#13372)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
51a61f6bc1
Коммит
e77ef9733c
13
api4/team.go
13
api4/team.go
@@ -522,6 +522,8 @@ func addUserToTeamFromInvite(c *Context, w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
|
||||
func addTeamMembers(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
graceful := r.URL.Query().Get("graceful") != ""
|
||||
|
||||
c.RequireTeamId()
|
||||
if c.Err != nil {
|
||||
return
|
||||
@@ -587,7 +589,7 @@ func addTeamMembers(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
members, err = c.App.AddTeamMembers(c.Params.TeamId, userIds, c.App.Session.UserId)
|
||||
membersWithErrors, err := c.App.AddTeamMembers(c.Params.TeamId, userIds, c.App.Session.UserId, graceful)
|
||||
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
@@ -595,7 +597,14 @@ func addTeamMembers(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
w.Write([]byte(model.TeamMembersToJson(members)))
|
||||
|
||||
if graceful {
|
||||
// in 'graceful' mode we allow a different return value, notifying the client which users were not added
|
||||
w.Write([]byte(model.TeamMembersWithErrorToJson(membersWithErrors)))
|
||||
} else {
|
||||
w.Write([]byte(model.TeamMembersToJson(model.TeamMembersWithErrorToTeamMembers(membersWithErrors))))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func removeTeamMember(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
@@ -1651,6 +1651,66 @@ func TestAddTeamMemberMyself(t *testing.T) {
|
||||
|
||||
}
|
||||
|
||||
func TestAddTeamMembersDomainConstrained(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
client := th.SystemAdminClient
|
||||
team := th.BasicTeam
|
||||
team.AllowedDomains = "domain1.com, domain2.com"
|
||||
_, response := client.UpdateTeam(team)
|
||||
require.Nil(t, response.Error)
|
||||
|
||||
// create two users on allowed domains
|
||||
user1, response := client.CreateUser(&model.User{
|
||||
Email: "user@domain1.com",
|
||||
Password: "Pa$$word11",
|
||||
Username: GenerateTestUsername(),
|
||||
})
|
||||
require.Nil(t, response.Error)
|
||||
user2, response := client.CreateUser(&model.User{
|
||||
Email: "user@domain2.com",
|
||||
Password: "Pa$$word11",
|
||||
Username: GenerateTestUsername(),
|
||||
})
|
||||
require.Nil(t, response.Error)
|
||||
|
||||
userList := []string{
|
||||
user1.Id,
|
||||
user2.Id,
|
||||
}
|
||||
|
||||
// validate that they can be added
|
||||
tm, response := client.AddTeamMembers(team.Id, userList)
|
||||
require.Nil(t, response.Error)
|
||||
require.Len(t, tm, 2)
|
||||
|
||||
// cleanup
|
||||
_, response = client.RemoveTeamMember(team.Id, user1.Id)
|
||||
require.Nil(t, response.Error)
|
||||
_, response = client.RemoveTeamMember(team.Id, user2.Id)
|
||||
require.Nil(t, response.Error)
|
||||
|
||||
// disable one of the allowed domains
|
||||
team.AllowedDomains = "domain1.com"
|
||||
_, response = client.UpdateTeam(team)
|
||||
require.Nil(t, response.Error)
|
||||
|
||||
// validate that they cannot be added
|
||||
_, response = client.AddTeamMembers(team.Id, userList)
|
||||
require.NotNil(t, response.Error)
|
||||
|
||||
// validate that one user can be added gracefully
|
||||
members, response := client.AddTeamMembersGracefully(team.Id, userList)
|
||||
require.Nil(t, response.Error)
|
||||
require.Len(t, members, 2)
|
||||
require.NotNil(t, members[0].Member)
|
||||
require.NotNil(t, members[1].Error)
|
||||
require.Equal(t, members[0].UserId, user1.Id)
|
||||
require.Equal(t, members[1].UserId, user2.Id)
|
||||
require.Nil(t, members[0].Error)
|
||||
require.Nil(t, members[1].Member)
|
||||
}
|
||||
|
||||
func TestAddTeamMembers(t *testing.T) {
|
||||
th := Setup().InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
@@ -174,7 +174,15 @@ func (api *PluginAPI) CreateTeamMember(teamId, userId string) (*model.TeamMember
|
||||
}
|
||||
|
||||
func (api *PluginAPI) CreateTeamMembers(teamId string, userIds []string, requestorId string) ([]*model.TeamMember, *model.AppError) {
|
||||
return api.app.AddTeamMembers(teamId, userIds, requestorId)
|
||||
members, err := api.app.AddTeamMembers(teamId, userIds, requestorId, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return model.TeamMembersWithErrorToTeamMembers(members), nil
|
||||
}
|
||||
|
||||
func (api *PluginAPI) CreateTeamMembersGracefully(teamId string, userIds []string, requestorId string) ([]*model.TeamMemberWithError, *model.AppError) {
|
||||
return api.app.AddTeamMembers(teamId, userIds, requestorId, true)
|
||||
}
|
||||
|
||||
func (api *PluginAPI) DeleteTeamMember(teamId, userId, requestorId string) *model.AppError {
|
||||
|
||||
18
app/team.go
18
app/team.go
@@ -777,11 +777,18 @@ func (a *App) AddTeamMember(teamId, userId string) (*model.TeamMember, *model.Ap
|
||||
return teamMember, nil
|
||||
}
|
||||
|
||||
func (a *App) AddTeamMembers(teamId string, userIds []string, userRequestorId string) ([]*model.TeamMember, *model.AppError) {
|
||||
var members []*model.TeamMember
|
||||
func (a *App) AddTeamMembers(teamId string, userIds []string, userRequestorId string, graceful bool) ([]*model.TeamMemberWithError, *model.AppError) {
|
||||
var membersWithErrors []*model.TeamMemberWithError
|
||||
|
||||
for _, userId := range userIds {
|
||||
if _, err := a.AddUserToTeam(teamId, userId, userRequestorId); err != nil {
|
||||
if graceful {
|
||||
membersWithErrors = append(membersWithErrors, &model.TeamMemberWithError{
|
||||
UserId: userId,
|
||||
Error: err,
|
||||
})
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -789,7 +796,10 @@ func (a *App) AddTeamMembers(teamId string, userIds []string, userRequestorId st
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
members = append(members, teamMember)
|
||||
membersWithErrors = append(membersWithErrors, &model.TeamMemberWithError{
|
||||
UserId: userId,
|
||||
Member: teamMember,
|
||||
})
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_ADDED_TO_TEAM, "", "", userId, nil)
|
||||
message.Add("team_id", teamId)
|
||||
@@ -797,7 +807,7 @@ func (a *App) AddTeamMembers(teamId string, userIds []string, userRequestorId st
|
||||
a.Publish(message)
|
||||
}
|
||||
|
||||
return members, nil
|
||||
return membersWithErrors, nil
|
||||
}
|
||||
|
||||
func (a *App) AddTeamMemberByToken(userId, tokenId string) (*model.TeamMember, *model.AppError) {
|
||||
|
||||
2
go.mod
2
go.mod
@@ -39,7 +39,7 @@ require (
|
||||
github.com/icrowley/fake v0.0.0-20180203215853-4178557ae428
|
||||
github.com/jaytaylor/html2text v0.0.0-20190408195923-01ec452cbe43
|
||||
github.com/jmoiron/sqlx v1.2.0
|
||||
github.com/jonboulle/clockwork v0.1.0
|
||||
github.com/jonboulle/clockwork v0.1.0 // indirect
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
||||
github.com/lib/pq v1.2.0
|
||||
github.com/magiconair/properties v1.8.1 // indirect
|
||||
|
||||
@@ -1861,6 +1861,22 @@ func (c *Client4) AddTeamMembers(teamId string, userIds []string) ([]*TeamMember
|
||||
return TeamMembersFromJson(r.Body), BuildResponse(r)
|
||||
}
|
||||
|
||||
// AddTeamMembers adds a number of users to a team and returns the team members.
|
||||
func (c *Client4) AddTeamMembersGracefully(teamId string, userIds []string) ([]*TeamMemberWithError, *Response) {
|
||||
var members []*TeamMember
|
||||
for _, userId := range userIds {
|
||||
member := &TeamMember{TeamId: teamId, UserId: userId}
|
||||
members = append(members, member)
|
||||
}
|
||||
|
||||
r, err := c.DoApiPost(c.GetTeamMembersRoute(teamId)+"/batch?graceful=true", TeamMembersToJson(members))
|
||||
if err != nil {
|
||||
return nil, BuildErrorResponse(r, err)
|
||||
}
|
||||
defer closeBody(r)
|
||||
return TeamMembersWithErrorFromJson(r.Body), BuildResponse(r)
|
||||
}
|
||||
|
||||
// RemoveTeamMember will remove a user from a team.
|
||||
func (c *Client4) RemoveTeamMember(teamId, userId string) (bool, *Response) {
|
||||
r, err := c.DoApiDelete(c.GetTeamMemberRoute(teamId, userId))
|
||||
|
||||
@@ -32,6 +32,12 @@ type TeamMemberForExport struct {
|
||||
TeamName string
|
||||
}
|
||||
|
||||
type TeamMemberWithError struct {
|
||||
UserId string `json:"user_id"`
|
||||
Member *TeamMember `json:"member"`
|
||||
Error *AppError `json:"error"`
|
||||
}
|
||||
|
||||
func (o *TeamMember) ToJson() string {
|
||||
b, _ := json.Marshal(o)
|
||||
return string(b)
|
||||
@@ -54,6 +60,30 @@ func TeamUnreadFromJson(data io.Reader) *TeamUnread {
|
||||
return o
|
||||
}
|
||||
|
||||
func TeamMembersWithErrorToTeamMembers(o []*TeamMemberWithError) []*TeamMember {
|
||||
var ret []*TeamMember
|
||||
for _, o := range o {
|
||||
if o.Error == nil {
|
||||
ret = append(ret, o.Member)
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func TeamMembersWithErrorToJson(o []*TeamMemberWithError) string {
|
||||
if b, err := json.Marshal(o); err != nil {
|
||||
return "[]"
|
||||
} else {
|
||||
return string(b)
|
||||
}
|
||||
}
|
||||
|
||||
func TeamMembersWithErrorFromJson(data io.Reader) []*TeamMemberWithError {
|
||||
var o []*TeamMemberWithError
|
||||
json.NewDecoder(data).Decode(&o)
|
||||
return o
|
||||
}
|
||||
|
||||
func TeamMembersToJson(o []*TeamMember) string {
|
||||
if b, err := json.Marshal(o); err != nil {
|
||||
return "[]"
|
||||
|
||||
@@ -288,6 +288,13 @@ type API interface {
|
||||
// Minimum server version: 5.2
|
||||
CreateTeamMembers(teamId string, userIds []string, requestorId string) ([]*model.TeamMember, *model.AppError)
|
||||
|
||||
// CreateTeamMembersGracefully creates a team membership for all provided user ids and reports the users that were not added.
|
||||
//
|
||||
// @tag Team
|
||||
// @tag User
|
||||
// Minimum server version: 5.20
|
||||
CreateTeamMembersGracefully(teamId string, userIds []string, requestorId string) ([]*model.TeamMemberWithError, *model.AppError)
|
||||
|
||||
// DeleteTeamMember deletes a team membership.
|
||||
//
|
||||
// @tag Team
|
||||
|
||||
@@ -1682,6 +1682,37 @@ func (s *apiRPCServer) CreateTeamMembers(args *Z_CreateTeamMembersArgs, returns
|
||||
return nil
|
||||
}
|
||||
|
||||
type Z_CreateTeamMembersGracefullyArgs struct {
|
||||
A string
|
||||
B []string
|
||||
C string
|
||||
}
|
||||
|
||||
type Z_CreateTeamMembersGracefullyReturns struct {
|
||||
A []*model.TeamMemberWithError
|
||||
B *model.AppError
|
||||
}
|
||||
|
||||
func (g *apiRPCClient) CreateTeamMembersGracefully(teamId string, userIds []string, requestorId string) ([]*model.TeamMemberWithError, *model.AppError) {
|
||||
_args := &Z_CreateTeamMembersGracefullyArgs{teamId, userIds, requestorId}
|
||||
_returns := &Z_CreateTeamMembersGracefullyReturns{}
|
||||
if err := g.client.Call("Plugin.CreateTeamMembersGracefully", _args, _returns); err != nil {
|
||||
log.Printf("RPC call to CreateTeamMembersGracefully API failed: %s", err.Error())
|
||||
}
|
||||
return _returns.A, _returns.B
|
||||
}
|
||||
|
||||
func (s *apiRPCServer) CreateTeamMembersGracefully(args *Z_CreateTeamMembersGracefullyArgs, returns *Z_CreateTeamMembersGracefullyReturns) error {
|
||||
if hook, ok := s.impl.(interface {
|
||||
CreateTeamMembersGracefully(teamId string, userIds []string, requestorId string) ([]*model.TeamMemberWithError, *model.AppError)
|
||||
}); ok {
|
||||
returns.A, returns.B = hook.CreateTeamMembersGracefully(args.A, args.B, args.C)
|
||||
} else {
|
||||
return encodableError(fmt.Errorf("API CreateTeamMembersGracefully called but not implemented."))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type Z_DeleteTeamMemberArgs struct {
|
||||
A string
|
||||
B string
|
||||
|
||||
@@ -268,6 +268,31 @@ func (_m *API) CreateTeamMembers(teamId string, userIds []string, requestorId st
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CreateTeamMembersGracefully provides a mock function with given fields: teamId, userIds, requestorId
|
||||
func (_m *API) CreateTeamMembersGracefully(teamId string, userIds []string, requestorId string) ([]*model.TeamMemberWithError, *model.AppError) {
|
||||
ret := _m.Called(teamId, userIds, requestorId)
|
||||
|
||||
var r0 []*model.TeamMemberWithError
|
||||
if rf, ok := ret.Get(0).(func(string, []string, string) []*model.TeamMemberWithError); ok {
|
||||
r0 = rf(teamId, userIds, requestorId)
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).([]*model.TeamMemberWithError)
|
||||
}
|
||||
}
|
||||
|
||||
var r1 *model.AppError
|
||||
if rf, ok := ret.Get(1).(func(string, []string, string) *model.AppError); ok {
|
||||
r1 = rf(teamId, userIds, requestorId)
|
||||
} else {
|
||||
if ret.Get(1) != nil {
|
||||
r1 = ret.Get(1).(*model.AppError)
|
||||
}
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// CreateUser provides a mock function with given fields: user
|
||||
func (_m *API) CreateUser(user *model.User) (*model.User, *model.AppError) {
|
||||
ret := _m.Called(user)
|
||||
|
||||
Ссылка в новой задаче
Block a user