From fc15eda37f25329344e023f37ec56d131a7b97e9 Mon Sep 17 00:00:00 2001 From: Siyuan Liu Date: Mon, 6 May 2019 07:59:51 -0700 Subject: [PATCH] [MM-14719] convert users to bots through cmd (#10672) * [MM-14719] convert users to bots through cmd * address review comments, add / fix unit tests * change command from "modify" to "convert"; review comments * code review comments, clean up --- app/bot.go | 9 +++++ app/bot_test.go | 42 +++++++++++++++++++++++ cmd/mattermost/commands/user.go | 50 ++++++++++++++++++++++++++++ cmd/mattermost/commands/user_test.go | 9 +++++ model/bot.go | 10 ++++++ model/bot_test.go | 17 ++++++++++ 6 files changed, 137 insertions(+) diff --git a/app/bot.go b/app/bot.go index 371ead7081..7fac314a82 100644 --- a/app/bot.go +++ b/app/bot.go @@ -176,3 +176,12 @@ func (a *App) disableUserBots(userId string) *model.AppError { return nil } + +// ConvertUserToBot converts a user to bot +func (a *App) ConvertUserToBot(user *model.User) (*model.Bot, *model.AppError) { + result := <-a.Srv.Store.Bot().Save(model.BotFromUser(user)) + if result.Err != nil { + return nil, result.Err + } + return result.Data.(*model.Bot), nil +} diff --git a/app/bot_test.go b/app/bot_test.go index 60b95ece93..ded8b3411f 100644 --- a/app/bot_test.go +++ b/app/bot_test.go @@ -544,6 +544,48 @@ func TestDisableUserBots(t *testing.T) { require.Nil(t, err) } +func TestConvertUserToBot(t *testing.T) { + t.Run("invalid user", func(t *testing.T) { + t.Run("invalid user id", func(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + + _, err := th.App.ConvertUserToBot(&model.User{ + Username: "username", + Id: "", + }) + require.NotNil(t, err) + require.Equal(t, "model.bot.is_valid.user_id.app_error", err.Id) + }) + + t.Run("invalid username", func(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + + _, err := th.App.ConvertUserToBot(&model.User{ + Username: "invalid username", + Id: th.BasicUser.Id, + }) + require.NotNil(t, err) + require.Equal(t, "model.bot.is_valid.username.app_error", err.Id) + }) + }) + + t.Run("valid user", func(t *testing.T) { + th := Setup(t).InitBasic() + defer th.TearDown() + + bot, err := th.App.ConvertUserToBot(&model.User{ + Username: "username", + Id: th.BasicUser.Id, + }) + require.Nil(t, err) + defer th.App.PermanentDeleteBot(bot.UserId) + assert.Equal(t, "username", bot.Username) + assert.Equal(t, th.BasicUser.Id, bot.OwnerId) + }) +} + func sToP(s string) *string { return &s } diff --git a/cmd/mattermost/commands/user.go b/cmd/mattermost/commands/user.go index 5e3106e1fc..8e430735bc 100644 --- a/cmd/mattermost/commands/user.go +++ b/cmd/mattermost/commands/user.go @@ -45,6 +45,15 @@ var UserCreateCmd = &cobra.Command{ RunE: userCreateCmdF, } +var UserConvertCmd = &cobra.Command{ + Use: "convert [emails, usernames, userIds] --bot", + Short: "Convert users to bots", + Long: "Convert users to bots", + Example: ` user convert user@example.com anotherUser --bot`, + Args: cobra.MinimumNArgs(1), + RunE: userConvertCmdF, +} + var UserInviteCmd = &cobra.Command{ Use: "invite [email] [teams]", Short: "Send user an email invite to a team.", @@ -162,6 +171,8 @@ func init() { UserCreateCmd.Flags().String("locale", "", "Optional. The locale (ex: en, fr) for the new user account.") UserCreateCmd.Flags().Bool("system_admin", false, "Optional. If supplied, the new user will be a system administrator. Defaults to false.") + UserConvertCmd.Flags().Bool("bot", false, "If supplied, convert users to bots.") + DeleteUserCmd.Flags().Bool("confirm", false, "Confirm you really want to delete the user and a DB backup has been performed.") DeleteAllUsersCmd.Flags().Bool("confirm", false, "Confirm you really want to delete the user and a DB backup has been performed.") @@ -234,6 +245,7 @@ Global Flags: UserActivateCmd, UserDeactivateCmd, UserCreateCmd, + UserConvertCmd, UserInviteCmd, ResetUserPasswordCmd, updateUserEmailCmd, @@ -368,6 +380,44 @@ func userCreateCmdF(command *cobra.Command, args []string) error { return nil } +func usersToBots(args []string, a *app.App) { + users := getUsersFromUserArgs(a, args) + for i, user := range users { + if user == nil { + CommandPrintErrorln(fmt.Errorf("Unable to find user \"%s\"", args[i])) + continue + } + + bot, err := a.ConvertUserToBot(user) + if err != nil { + CommandPrintErrorln(err.Error()) + continue + } + + CommandPrettyPrintln(fmt.Sprintf("User %s is converted to bot successfully", bot.UserId)) + } +} + +func userConvertCmdF(command *cobra.Command, args []string) error { + a, err := InitDBCommandContextCobra(command) + if err != nil { + return err + } + defer a.Shutdown() + + toBot, err := command.Flags().GetBool("bot") + if err != nil { + return errors.New("Invalid command. See help text for details.") + } + + if !toBot { + return errors.New("Expect \"bot\" flag to be set. See help text for details.") + } + + usersToBots(args, a) + return nil +} + func userInviteCmdF(command *cobra.Command, args []string) error { a, err := InitDBCommandContextCobra(command) if err != nil { diff --git a/cmd/mattermost/commands/user_test.go b/cmd/mattermost/commands/user_test.go index 632b5efb5b..33920c87ce 100644 --- a/cmd/mattermost/commands/user_test.go +++ b/cmd/mattermost/commands/user_test.go @@ -116,3 +116,12 @@ func TestChangeUserEmail(t *testing.T) { require.Error(t, th.RunCommand(t, "user", "email", th.BasicUser.Username, th.BasicUser2.Email)) } + +func TestConvertUserToBot(t *testing.T) { + th := Setup().InitBasic() + defer th.TearDown() + + th.CheckCommand(t, "user", "convert", th.BasicUser.Username, "anotherinvaliduser", "--bot") + result := <-th.App.Srv.Store.Bot().Get(th.BasicUser.Id, false) + require.Nil(t, result.Err) +} diff --git a/model/bot.go b/model/bot.go index 0311780cae..4ea5379bc5 100644 --- a/model/bot.go +++ b/model/bot.go @@ -172,6 +172,16 @@ func UserFromBot(b *Bot) *User { } } +// BotFromUser returns a bot model given a user model +func BotFromUser(u *User) *Bot { + return &Bot{ + OwnerId: u.Id, + UserId: u.Id, + Username: u.Username, + DisplayName: u.GetDisplayName(SHOW_USERNAME), + } +} + // BotListFromJson deserializes a list of bots from json. func BotListFromJson(data io.Reader) BotList { var bots BotList diff --git a/model/bot_test.go b/model/bot_test.go index 4d49b1d339..e8ea3963af 100644 --- a/model/bot_test.go +++ b/model/bot_test.go @@ -517,6 +517,23 @@ func TestUserFromBot(t *testing.T) { }, UserFromBot(bot2)) } +func TestBotFromUser(t *testing.T) { + user := &User{ + Id: NewId(), + Username: "username", + CreateAt: 1, + UpdateAt: 2, + DeleteAt: 3, + } + + assert.Equal(t, &Bot{ + OwnerId: user.Id, + UserId: user.Id, + Username: "username", + DisplayName: "username", + }, BotFromUser(user)) +} + func TestBotListToAndFromJson(t *testing.T) { testCases := []struct { Description string