From 82096a8cbb974f7bb06f6dbe3c61c6ac5325898e Mon Sep 17 00:00:00 2001 From: cyrilzhang-mm <112951043+cyrilzhang-mm@users.noreply.github.com> Date: Wed, 26 Oct 2022 09:08:24 -0400 Subject: [PATCH] [MM-44336] Add timestamp parameter to test data slash commands (#21400) --- app/slashcommands/auto_channels.go | 23 +-- app/slashcommands/auto_posts.go | 60 ++++-- app/slashcommands/auto_users.go | 26 ++- app/slashcommands/command_loadtest.go | 275 +++++++++++++++++++------- model/user.go | 4 +- store/sqlstore/team_store.go | 2 +- 6 files changed, 276 insertions(+), 114 deletions(-) diff --git a/app/slashcommands/auto_channels.go b/app/slashcommands/auto_channels.go index 53726177cf..733d6a20a2 100644 --- a/app/slashcommands/auto_channels.go +++ b/app/slashcommands/auto_channels.go @@ -20,6 +20,7 @@ type AutoChannelCreator struct { NameLen utils.Range NameCharset string ChannelType model.ChannelType + CreateTime int64 } func NewAutoChannelCreator(a *app.App, team *model.Team, userID string) *AutoChannelCreator { @@ -33,6 +34,7 @@ func NewAutoChannelCreator(a *app.App, team *model.Team, userID string) *AutoCha NameLen: ChannelNameLen, NameCharset: utils.LOWERCASE, ChannelType: ChannelType, + CreateTime: 0, } } @@ -51,6 +53,7 @@ func (cfg *AutoChannelCreator) createRandomChannel(c request.CTX) (*model.Channe Name: name, Type: cfg.ChannelType, CreatorId: cfg.userID, + CreateAt: cfg.CreateTime, } channel, err := cfg.a.CreateChannel(c, channel, true) @@ -74,23 +77,3 @@ func (cfg *AutoChannelCreator) CreateTestChannels(c request.CTX, num utils.Range return channels, nil } - -func (cfg *AutoChannelCreator) CreateTestDMs(c request.CTX, num utils.Range) ([]*model.Channel, error) { - numDMs := utils.RandIntFromRange(num) - dms := make([]*model.Channel, numDMs) - - users, err := cfg.a.GetUsersFromProfiles(&model.UserGetOptions{Page: 0, PerPage: numDMs}) - if err != nil { - return nil, err - } - - for i, user := range users { - var err *model.AppError - dms[i], err = cfg.a.GetOrCreateDirectChannel(c, cfg.userID, user.Id) - if err != nil { - return nil, err - } - } - - return dms, nil -} diff --git a/app/slashcommands/auto_posts.go b/app/slashcommands/auto_posts.go index 666bbea5a4..d0ef6b6070 100644 --- a/app/slashcommands/auto_posts.go +++ b/app/slashcommands/auto_posts.go @@ -17,31 +17,37 @@ import ( ) type AutoPostCreator struct { - a *app.App - channelid string - userid string - Fuzzy bool - TextLength utils.Range - HasImage bool - ImageFilenames []string - Users []string - Mentions utils.Range - Tags utils.Range + a *app.App + channelid string + userid string + Fuzzy bool + TextLength utils.Range + HasImage bool + ImageFilenames []string + Users []string + UsersToPostFrom []string + Mentions utils.Range + Tags utils.Range + CreateTime int64 + postsCreated int } // Automatic poster used for testing func NewAutoPostCreator(a *app.App, channelid, userid string) *AutoPostCreator { return &AutoPostCreator{ - a: a, - channelid: channelid, - userid: userid, - Fuzzy: false, - TextLength: utils.Range{Begin: 100, End: 200}, - HasImage: false, - ImageFilenames: TestImageFileNames, - Users: []string{}, - Mentions: utils.Range{Begin: 0, End: 5}, - Tags: utils.Range{Begin: 0, End: 7}, + a: a, + channelid: channelid, + userid: userid, + Fuzzy: false, + TextLength: utils.Range{Begin: 100, End: 200}, + HasImage: false, + ImageFilenames: TestImageFileNames, + Users: []string{}, + UsersToPostFrom: []string{}, + Mentions: utils.Range{Begin: 0, End: 5}, + Tags: utils.Range{Begin: 0, End: 7}, + CreateTime: 0, + postsCreated: 0, } } @@ -97,9 +103,23 @@ func (cfg *AutoPostCreator) CreateRandomPostNested(c request.CTX, rootId string) Message: postText, FileIds: fileIDs, } + if cfg.CreateTime != 0 { + // Creating posts with the exact same timestamp results in some posts being skipped + // when they are retrieved by the API based on timestamp. + post.CreateAt = cfg.CreateTime + int64(cfg.postsCreated) + } + if len(cfg.UsersToPostFrom) != 0 { + i := utils.RandIntFromRange(utils.Range{Begin: 0, End: len(cfg.UsersToPostFrom)}) + if i < len(cfg.UsersToPostFrom) { + post.UserId = cfg.UsersToPostFrom[i] + } + } rpost, err := cfg.a.CreatePostMissingChannel(c, post, true) if err != nil { return nil, err } + + cfg.postsCreated += 1 + return rpost, nil } diff --git a/app/slashcommands/auto_users.go b/app/slashcommands/auto_users.go index 0fe51f5e28..6edb31c2cf 100644 --- a/app/slashcommands/auto_users.go +++ b/app/slashcommands/auto_users.go @@ -23,6 +23,7 @@ type AutoUserCreator struct { NameLength utils.Range NameCharset string Fuzzy bool + JoinTime int64 } func NewAutoUserCreator(a *app.App, client *model.Client4, team *model.Team) *AutoUserCreator { @@ -35,6 +36,7 @@ func NewAutoUserCreator(a *app.App, client *model.Client4, team *model.Team) *Au NameLength: UserNameLen, NameCharset: utils.LOWERCASE, Fuzzy: false, + JoinTime: 0, } } @@ -92,14 +94,22 @@ func (cfg *AutoUserCreator) createRandomUser(c request.CTX) (*model.User, error) user := &model.User{ Email: userEmail, Nickname: userName, - Password: UserPassword} + Password: UserPassword, + CreateAt: cfg.JoinTime, + } ruser, appErr := cfg.app.CreateUserWithInviteId(c, user, cfg.team.InviteId, "") if appErr != nil { return nil, appErr } - status := &model.Status{UserId: ruser.Id, Status: model.StatusOnline, Manual: false, LastActivityAt: model.GetMillis(), ActiveChannel: ""} + status := &model.Status{ + UserId: ruser.Id, + Status: model.StatusOnline, + Manual: false, + LastActivityAt: ruser.CreateAt, + ActiveChannel: "", + } if err := cfg.app.Srv().Store().Status().SaveOrUpdate(status); err != nil { return nil, err } @@ -110,6 +120,18 @@ func (cfg *AutoUserCreator) createRandomUser(c request.CTX) (*model.User, error) return nil, err } + if cfg.JoinTime != 0 { + teamMember, appErr := cfg.app.GetTeamMember(cfg.team.Id, ruser.Id) + if appErr != nil { + return nil, appErr + } + teamMember.CreateAt = cfg.JoinTime + _, err := cfg.app.Srv().Store().Team().UpdateMember(teamMember) + if err != nil { + return nil, err + } + } + return ruser, nil } diff --git a/app/slashcommands/command_loadtest.go b/app/slashcommands/command_loadtest.go index abe6a36129..2774c8bbd5 100644 --- a/app/slashcommands/command_loadtest.go +++ b/app/slashcommands/command_loadtest.go @@ -30,34 +30,52 @@ var usage = `Mattermost testing commands to help configure the system /test setup [teams] [fuzz] Example: - /test setup teams fuzz 10 20 50 + /test setup teams fuzz 10 20 50 - Users - Add a specified number of random users with fuzz text to current team. - /test users [fuzz] + Users - Add a specified number of random users with fuzz text to current team, at the specified Unix timestamp in milliseconds. + /test users [fuzz] [range=min[,max]] [time=user_join_timestamp] + + Default: range=2,5 time= + + Examples: + /test users fuzz range=3,8 time=1565076128000 + /test users range=1 + + Channels - Add a specified number of random public (o) or private (p) channels with fuzz text to current team, at the specified Unix timestamp in milliseconds. + /test channels [fuzz] [range=min[,max]] [type=(o|p)] [time=channel_create_timestamp] + + Default: range=2,5 type=o time= + + Examples: + /test channels fuzz range=5,10 type=p time=1565076128000 + /test channels range=1 + + DMs - Add a specified number of random DM messages between the current user and a specified user, at the specified Unix timestamp in milliseconds. If a timestamp is provided, posts are created one millisecond apart. Note: You may need to clear your browser cache in order to see these posts in the UI. + /test dms u=@username [range=min[,max]] [time=dm_create_timestamp] + + Default: range=2,5 time= + + Examples: + /test dms u=@user range=5,10 time=1565076128000 + /test dms u=@user range=2 + + ThreadedPost - Create a threaded post with a specified number of replies at the specified Unix timestamp in milliseconds. If a timestamp is provided, posts are created one millisecond apart. Note: You may need to clear your browser cache in order to see these posts in the UI. + /test threaded_post [range=min[,max]] [time=post_timestamp] + + Default: range=1000 time= + + Examples: + /test threaded_post + /test threaded_post range=100,200 time=1565076128000 + + Posts - Add some random posts with fuzz text to current channel, at the specified Unix timestamp in milliseconds. If a timestamp is provided, posts are created one millisecond apart. Note: You may need to clear your browser cache in order to see these posts in the UI. + /test posts [fuzz] [range=min[,max]] [images=max_images] [time=post_timestamp] + + Default: range=2,5 images=0 time= Example: - /test users fuzz 5 10 - - Channels - Add a specified number of random channels with fuzz text to current team. - /test channels [fuzz] - - Example: - /test channels fuzz 5 10 - - DMs - Add a specified number of DMs for current user. Requires enough users to be loaded. - /test dms - - Example: - /test dms 5 10 - - ThreadedPost - create a large threaded post - /test threaded_post - - Posts - Add some random posts with fuzz text to current channel. - /test posts [fuzz] - - Example: - /test posts fuzz 5 10 3 + /test posts fuzz range=5,10 images=3 time=1565076128000 + /test posts range=2 Post - Add post to a channel as another user. /test post u=@username p=passwd c=~channelname t=teamname "message" @@ -72,10 +90,10 @@ var usage = `Mattermost testing commands to help configure the system /test http://www.example.com/sample_file.md Json - Add a post using the JSON file as payload to the current channel. - /test json url + /test json url - Example - /test json http://www.example.com/sample_body.json + Example: + /test json http://www.example.com/sample_body.json ` @@ -84,11 +102,16 @@ const ( ) var ( - userRE = regexp.MustCompile(`u=@([^\s]+)`) + userRE = regexp.MustCompile(`u=@?([^\s]+)`) passwdRE = regexp.MustCompile(`p=([^\s]+)`) teamRE = regexp.MustCompile(`t=([^\s]+)`) channelRE = regexp.MustCompile(`c=~([^\s]+)`) messageRE = regexp.MustCompile(`"(.*)"`) + fuzzRE = regexp.MustCompile(`fuzz`) + rangeRE = regexp.MustCompile(`range=([^\s]+)`) + timeRE = regexp.MustCompile(`time=([^\s]+)`) + imagesRE = regexp.MustCompile(`images=([^\s]+)`) + typeRE = regexp.MustCompile(`type=([^\s])+`) ) type LoadTestProvider struct { @@ -289,14 +312,19 @@ func (*LoadTestProvider) UsersCommand(a *app.App, c request.CTX, args *model.Com cmd := strings.TrimSpace(strings.TrimPrefix(message, "users")) doFuzz := false - if strings.Index(cmd, "fuzz") == 0 { + if fuzzRE.MatchString(cmd) { doFuzz = true - cmd = strings.TrimSpace(strings.TrimPrefix(cmd, "fuzz")) } - usersr, ok := parseRange(cmd, "") - if !ok { - usersr = utils.Range{Begin: 2, End: 5} + var err error + rng := utils.Range{Begin: 2, End: 5} + rangeParam := getMatch(rangeRE, cmd) + if rangeParam != "" { + rng, err = parseRange(rangeParam) + if err != nil { + return &model.CommandResponse{Text: "Failed to add users: " + err.Error(), ResponseType: model.CommandResponseTypeEphemeral}, err + } + } team, err := a.Srv().Store().Team().Get(args.TeamId) @@ -304,10 +332,20 @@ func (*LoadTestProvider) UsersCommand(a *app.App, c request.CTX, args *model.Com return &model.CommandResponse{Text: "Failed to add users", ResponseType: model.CommandResponseTypeEphemeral}, err } + time := int64(0) + timeParam := getMatch(timeRE, cmd) + if timeParam != "" { + time, err = strconv.ParseInt(timeParam, 10, 64) + if err != nil || time < 0 { + return &model.CommandResponse{Text: "Failed to add users: Invalid time parameter", ResponseType: model.CommandResponseTypeEphemeral}, errors.New("Invalid time parameter") + } + } + client := model.NewAPIv4Client(args.SiteURL) userCreator := NewAutoUserCreator(a, client, team) userCreator.Fuzzy = doFuzz - if _, err := userCreator.CreateTestUsers(c, usersr); err != nil { + userCreator.JoinTime = time + if _, err := userCreator.CreateTestUsers(c, rng); err != nil { return &model.CommandResponse{Text: "Failed to add users: " + err.Error(), ResponseType: model.CommandResponseTypeEphemeral}, err } @@ -318,14 +356,19 @@ func (*LoadTestProvider) ChannelsCommand(a *app.App, c request.CTX, args *model. cmd := strings.TrimSpace(strings.TrimPrefix(message, "channels")) doFuzz := false - if strings.Index(cmd, "fuzz") == 0 { + if fuzzRE.MatchString(cmd) { doFuzz = true - cmd = strings.TrimSpace(strings.TrimPrefix(cmd, "fuzz")) } - channelsr, ok := parseRange(cmd, "") - if !ok { - channelsr = utils.Range{Begin: 2, End: 5} + var err error + rng := utils.Range{Begin: 2, End: 5} + rangeParam := getMatch(rangeRE, cmd) + if rangeParam != "" { + rng, err = parseRange(rangeParam) + if err != nil { + return &model.CommandResponse{Text: "Failed to add channels: " + err.Error(), ResponseType: model.CommandResponseTypeEphemeral}, err + } + } team, err := a.Srv().Store().Team().Get(args.TeamId) @@ -333,9 +376,32 @@ func (*LoadTestProvider) ChannelsCommand(a *app.App, c request.CTX, args *model. return &model.CommandResponse{Text: "Failed to add channels", ResponseType: model.CommandResponseTypeEphemeral}, err } + typ := model.ChannelTypeOpen + typeParam := getMatch(typeRE, cmd) + if typeParam != "" { + switch strings.ToUpper(typeParam) { + case "O": + case "P": + typ = model.ChannelTypePrivate + default: + return &model.CommandResponse{Text: "Failed to add channels: Invalid type parameter", ResponseType: model.CommandResponseTypeEphemeral}, errors.New("Invalid type parameter") + } + } + + time := int64(0) + timeParam := getMatch(timeRE, cmd) + if timeParam != "" { + time, err = strconv.ParseInt(timeParam, 10, 64) + if err != nil || time < 0 { + return &model.CommandResponse{Text: "Failed to add channels: Invalid time parameter", ResponseType: model.CommandResponseTypeEphemeral}, errors.New("Invalid time parameter") + } + } + channelCreator := NewAutoChannelCreator(a, team, args.UserId) channelCreator.Fuzzy = doFuzz - if _, err := channelCreator.CreateTestChannels(c, channelsr); err != nil { + channelCreator.CreateTime = time + channelCreator.ChannelType = typ + if _, err := channelCreator.CreateTestChannels(c, rng); err != nil { return &model.CommandResponse{Text: "Failed to create test channels: " + err.Error(), ResponseType: model.CommandResponseTypeEphemeral}, err } @@ -345,20 +411,71 @@ func (*LoadTestProvider) ChannelsCommand(a *app.App, c request.CTX, args *model. func (*LoadTestProvider) DMsCommand(a *app.App, c request.CTX, args *model.CommandArgs, message string) (*model.CommandResponse, error) { cmd := strings.TrimSpace(strings.TrimPrefix(message, "dms")) - channelsr, ok := parseRange(cmd, "") - if !ok { - channelsr = utils.Range{Begin: 2, End: 5} + var err error + + username := getMatch(userRE, message) + user, appErr := a.GetUserByUsername(username) + if appErr != nil { + return &model.CommandResponse{Text: "Failed to add DMS: Invalid username", ResponseType: model.CommandResponseTypeEphemeral}, appErr } - channelCreator := NewAutoChannelCreator(a, nil, args.UserId) - if _, err := channelCreator.CreateTestDMs(c, channelsr); err != nil { - return &model.CommandResponse{Text: "Failed to create test DMs: " + err.Error(), ResponseType: model.CommandResponseTypeEphemeral}, err + rng := utils.Range{Begin: 2, End: 5} + rangeParam := getMatch(rangeRE, cmd) + if rangeParam != "" { + rng, err = parseRange(rangeParam) + if err != nil { + return &model.CommandResponse{Text: "Failed to add DMs: " + err.Error(), ResponseType: model.CommandResponseTypeEphemeral}, err + } + + } + + time := int64(0) + timeParam := getMatch(timeRE, cmd) + if timeParam != "" { + time, err = strconv.ParseInt(timeParam, 10, 64) + if err != nil || time < 0 { + return &model.CommandResponse{Text: "Failed to add DMs: Invalid time parameter", ResponseType: model.CommandResponseTypeEphemeral}, errors.New("Invalid time parameter") + } + } + + channel, err := a.GetOrCreateDirectChannel(c, args.UserId, user.Id) + + postCreator := NewAutoPostCreator(a, channel.Id, args.UserId) + postCreator.CreateTime = time + postCreator.UsersToPostFrom = []string{user.Id} + numPosts := utils.RandIntFromRange(rng) + for i := 0; i < numPosts; i++ { + if _, err := postCreator.CreateRandomPost(c); err != nil { + return &model.CommandResponse{Text: "Failed to create test DMs: " + err.Error(), ResponseType: model.CommandResponseTypeEphemeral}, err + } } return &model.CommandResponse{Text: "Added DMs", ResponseType: model.CommandResponseTypeEphemeral}, nil } func (*LoadTestProvider) ThreadedPostCommand(a *app.App, c request.CTX, args *model.CommandArgs, message string) (*model.CommandResponse, error) { + cmd := strings.TrimSpace(strings.TrimPrefix(message, "threaded_post")) + + var err error + rng := utils.Range{Begin: 1000, End: 1000} + rangeParam := getMatch(rangeRE, cmd) + if rangeParam != "" { + rng, err = parseRange(rangeParam) + if err != nil { + return &model.CommandResponse{Text: "Failed to create post: " + err.Error(), ResponseType: model.CommandResponseTypeEphemeral}, err + } + + } + + time := int64(0) + timeParam := getMatch(timeRE, cmd) + if timeParam != "" { + time, err = strconv.ParseInt(timeParam, 10, 64) + if err != nil || time < 0 { + return &model.CommandResponse{Text: "Failed to create post: Invalid time parameter", ResponseType: model.CommandResponseTypeEphemeral}, errors.New("Invalid time parameter") + } + } + var usernames []string options := &model.UserGetOptions{InTeamId: args.TeamId, Page: 0, PerPage: 1000} if profileUsers, err := a.Srv().Store().User().GetProfiles(options); err == nil { @@ -373,11 +490,13 @@ func (*LoadTestProvider) ThreadedPostCommand(a *app.App, c request.CTX, args *mo testPoster := NewAutoPostCreator(a, args.ChannelId, args.UserId) testPoster.Fuzzy = true testPoster.Users = usernames + testPoster.CreateTime = time rpost, err2 := testPoster.CreateRandomPost(c) if err2 != nil { return &model.CommandResponse{Text: "Failed to create a post", ResponseType: model.CommandResponseTypeEphemeral}, err2 } - for i := 0; i < 1000; i++ { + numPosts := utils.RandIntFromRange(rng) + for i := 0; i < numPosts; i++ { testPoster.CreateRandomPostNested(c, rpost.Id) } @@ -388,21 +507,36 @@ func (*LoadTestProvider) PostsCommand(a *app.App, c request.CTX, args *model.Com cmd := strings.TrimSpace(strings.TrimPrefix(message, "posts")) doFuzz := false - if strings.Index(cmd, "fuzz") == 0 { + if fuzzRE.MatchString(cmd) { doFuzz = true - cmd = strings.TrimSpace(strings.TrimPrefix(cmd, "fuzz")) } - postsr, ok := parseRange(cmd, "") - if !ok { - postsr = utils.Range{Begin: 20, End: 30} + var err error + rng := utils.Range{Begin: 2, End: 5} + rangeParam := getMatch(rangeRE, cmd) + if rangeParam != "" { + rng, err = parseRange(rangeParam) + if err != nil { + return &model.CommandResponse{Text: "Failed to add posts: " + err.Error(), ResponseType: model.CommandResponseTypeEphemeral}, err + } + } - tokens := strings.Fields(cmd) - rimages := utils.Range{Begin: 0, End: 0} - if len(tokens) >= 3 { - if numImages, err := strconv.Atoi(tokens[2]); err == nil { - rimages = utils.Range{Begin: numImages, End: numImages} + maxImages := 0 + imagesParam := getMatch(imagesRE, cmd) + if imagesParam != "" { + maxImages, err = strconv.Atoi(imagesParam) + if err != nil { + return &model.CommandResponse{Text: "Failed to add posts: Invalid images parameter", ResponseType: model.CommandResponseTypeEphemeral}, errors.New("Invalid images parameter") + } + } + + time := int64(0) + timeParam := getMatch(timeRE, cmd) + if timeParam != "" { + time, err = strconv.ParseInt(timeParam, 10, 64) + if err != nil || time < 0 { + return &model.CommandResponse{Text: "Failed to add posts: Invalid time parameter", ResponseType: model.CommandResponseTypeEphemeral}, errors.New("Invalid time parameter") } } @@ -420,9 +554,10 @@ func (*LoadTestProvider) PostsCommand(a *app.App, c request.CTX, args *model.Com testPoster := NewAutoPostCreator(a, args.ChannelId, args.UserId) testPoster.Fuzzy = doFuzz testPoster.Users = usernames + testPoster.CreateTime = time - numImages := utils.RandIntFromRange(rimages) - numPosts := utils.RandIntFromRange(postsr) + numImages := utils.RandIntFromRange(utils.Range{Begin: 0, End: maxImages}) + numPosts := utils.RandIntFromRange(rng) for i := 0; i < numPosts; i++ { testPoster.HasImage = (i < numImages) _, err := testPoster.CreateRandomPost(c) @@ -585,8 +720,8 @@ func (*LoadTestProvider) JsonCommand(a *app.App, c request.CTX, args *model.Comm return &model.CommandResponse{Text: "Loaded data", ResponseType: model.CommandResponseTypeEphemeral}, nil } -func parseRange(command string, cmd string) (utils.Range, bool) { - tokens := strings.Fields(strings.TrimPrefix(command, cmd)) +func parseRange(rng string) (utils.Range, error) { + tokens := strings.Split(rng, ",") var begin int var end int var err1 error @@ -594,20 +729,20 @@ func parseRange(command string, cmd string) (utils.Range, bool) { switch { case len(tokens) == 1: begin, err1 = strconv.Atoi(tokens[0]) - end = begin - if err1 != nil { - return utils.Range{Begin: 0, End: 0}, false + if err1 != nil || begin < 0 { + return utils.Range{Begin: 0, End: 0}, errors.New("Invalid range parameter") } - case len(tokens) >= 2: + end = begin + case len(tokens) == 2: begin, err1 = strconv.Atoi(tokens[0]) end, err2 = strconv.Atoi(tokens[1]) - if err1 != nil || err2 != nil { - return utils.Range{Begin: 0, End: 0}, false + if err1 != nil || err2 != nil || begin < 0 || end < begin { + return utils.Range{Begin: 0, End: 0}, errors.New("Invalid range parameter") } default: - return utils.Range{Begin: 0, End: 0}, false + return utils.Range{Begin: 0, End: 0}, errors.New("Invalid range parameter") } - return utils.Range{Begin: begin, End: end}, true + return utils.Range{Begin: begin, End: end}, nil } func contains(items []string, token string) bool { diff --git a/model/user.go b/model/user.go index 2140663006..12d630b3b3 100644 --- a/model/user.go +++ b/model/user.go @@ -437,7 +437,9 @@ func (u *User) PreSave() { u.Username = NormalizeUsername(u.Username) u.Email = NormalizeEmail(u.Email) - u.CreateAt = GetMillis() + if u.CreateAt == 0 { + u.CreateAt = GetMillis() + } u.UpdateAt = u.CreateAt u.LastPasswordUpdate = u.CreateAt diff --git a/store/sqlstore/team_store.go b/store/sqlstore/team_store.go index 63da971abb..cbe8535a21 100644 --- a/store/sqlstore/team_store.go +++ b/store/sqlstore/team_store.go @@ -876,7 +876,7 @@ func (s SqlTeamStore) UpdateMultipleMembers(members []*model.TeamMember) ([]*mod } if _, err := s.GetMasterX().NamedExec(`UPDATE TeamMembers - SET Roles=:Roles, DeleteAt=:DeleteAt, SchemeGuest=:SchemeGuest, + SET Roles=:Roles, DeleteAt=:DeleteAt, CreateAt=:CreateAt, SchemeGuest=:SchemeGuest, SchemeUser=:SchemeUser, SchemeAdmin=:SchemeAdmin WHERE TeamId=:TeamId AND UserId=:UserId`, newTeamMember); err != nil { return nil, errors.Wrap(err, "failed to update TeamMember")