PLT-7218: CLI to move slash commands between teams. (#7574)

Этот коммит содержится в:
George Goldberg
2017-10-04 19:08:59 +01:00
коммит произвёл Chris
родитель e16bdf8d1d
Коммит f3fc6d11fa
9 изменённых файлов: 252 добавлений и 1 удалений

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

@@ -336,6 +336,16 @@ func (a *App) UpdateCommand(oldCmd, updatedCmd *model.Command) (*model.Command,
}
}
func (a *App) MoveCommand(team *model.Team, command *model.Command) *model.AppError {
command.TeamId = team.Id
if result := <-a.Srv.Store.Command().Update(command); result.Err != nil {
return result.Err
}
return nil
}
func (a *App) RegenCommandToken(cmd *model.Command) (*model.Command, *model.AppError) {
if !*utils.Cfg.ServiceSettings.EnableCommands {
return nil, model.NewAppError("RegenCommandToken", "api.command.disabled.app_error", nil, "", http.StatusNotImplemented)

46
app/command_test.go Обычный файл
Просмотреть файл

@@ -0,0 +1,46 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package app
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/mattermost/mattermost-server/model"
)
func TestMoveCommand(t *testing.T) {
th := Setup().InitBasic()
sourceTeam := th.CreateTeam()
targetTeam := th.CreateTeam()
command := &model.Command{}
command.CreatorId = model.NewId()
command.Method = model.COMMAND_METHOD_POST
command.TeamId = sourceTeam.Id
command.URL = "http://nowhere.com/"
command.Trigger = "trigger1"
command, err := th.App.CreateCommand(command)
assert.Nil(t, err)
defer func() {
th.App.PermanentDeleteTeam(sourceTeam)
th.App.PermanentDeleteTeam(targetTeam)
}()
// Move a command and check the team is updated.
assert.Nil(t, th.App.MoveCommand(targetTeam, command))
retrievedCommand, err := th.App.GetCommand(command.Id)
assert.Nil(t, err)
assert.EqualValues(t, targetTeam.Id, retrievedCommand.TeamId)
// Move it to the team it's already in. Nothing should change.
assert.Nil(t, th.App.MoveCommand(targetTeam, command))
retrievedCommand, err = th.App.GetCommand(command.Id)
assert.Nil(t, err)
assert.EqualValues(t, targetTeam.Id, retrievedCommand.TeamId)
}

69
cmd/platform/command.go Обычный файл
Просмотреть файл

@@ -0,0 +1,69 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package main
import (
"errors"
"github.com/mattermost/mattermost-server/app"
"github.com/mattermost/mattermost-server/model"
"github.com/spf13/cobra"
)
var commandCmd = &cobra.Command{
Use: "command",
Short: "Management of slash commands",
}
var commandMoveCmd = &cobra.Command{
Use: "move",
Short: "Move a slash command to a different team",
Long: `Move a slash command to a different team. Commands can be specified by [team]:[command-trigger-word]. ie. myteam:trigger or by command ID.`,
Example: ` command move newteam oldteam:command`,
RunE: moveCommandCmdF,
}
func init() {
commandCmd.AddCommand(
commandMoveCmd,
)
}
func moveCommandCmdF(cmd *cobra.Command, args []string) error {
a, err := initDBCommandContextCobra(cmd)
if err != nil {
return err
}
if len(args) < 2 {
return errors.New("Enter the destination team and at least one comamnd to move.")
}
team := getTeamFromTeamArg(a, args[0])
if team == nil {
return errors.New("Unable to find destination team '" + args[0] + "'")
}
commands := getCommandsFromCommandArgs(a, args[1:])
CommandPrintErrorln(commands)
for i, command := range commands {
if command == nil {
CommandPrintErrorln("Unable to find command '" + args[i+1] + "'")
continue
}
if err := moveCommand(a, team, command); err != nil {
CommandPrintErrorln("Unable to move command '" + command.Trigger + "' error: " + err.Error())
} else {
CommandPrettyPrintln("Moved command '" + command.Trigger + "'")
}
}
return nil
}
func moveCommand(a *app.App, team *model.Team, command *model.Command) *model.AppError {
if err := a.MoveCommand(team, command); err != nil {
return err
}
return nil
}

63
cmd/platform/commandargs.go Обычный файл
Просмотреть файл

@@ -0,0 +1,63 @@
// Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
package main
import (
"fmt"
"strings"
"github.com/mattermost/mattermost-server/app"
"github.com/mattermost/mattermost-server/model"
)
const COMMAND_ARGS_SEPARATOR = ":"
func getCommandsFromCommandArgs(a *app.App, commandArgs []string) []*model.Command {
commands := make([]*model.Command, 0, len(commandArgs))
for _, commandArg := range commandArgs {
command := getCommandFromCommandArg(a, commandArg)
commands = append(commands, command)
}
return commands
}
func parseCommandArg(commandArg string) (string, string) {
result := strings.SplitN(commandArg, COMMAND_ARGS_SEPARATOR, 2)
if len(result) == 1 {
return "", commandArg
}
return result[0], result[1]
}
func getCommandFromCommandArg(a *app.App, commandArg string) *model.Command {
teamArg, commandPart := parseCommandArg(commandArg)
if teamArg == "" && commandPart == "" {
return nil
}
var command *model.Command
if teamArg != "" {
team := getTeamFromTeamArg(a, teamArg)
if team == nil {
return nil
}
if result := <-a.Srv.Store.Command().GetByTrigger(team.Id, commandPart); result.Err == nil {
command = result.Data.(*model.Command)
} else {
fmt.Println(result.Err.Error())
}
}
if command == nil {
if result := <-a.Srv.Store.Command().Get(commandPart); result.Err == nil {
command = result.Data.(*model.Command)
}
}
return command
}

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

@@ -40,7 +40,7 @@ func init() {
resetCmd.Flags().Bool("confirm", false, "Confirm you really want to delete everything and a DB backup has been performed.")
rootCmd.AddCommand(serverCmd, versionCmd, userCmd, teamCmd, licenseCmd, importCmd, resetCmd, channelCmd, rolesCmd, testCmd, ldapCmd, configCmd, jobserverCmd)
rootCmd.AddCommand(serverCmd, versionCmd, userCmd, teamCmd, licenseCmd, importCmd, resetCmd, channelCmd, rolesCmd, testCmd, ldapCmd, configCmd, jobserverCmd, commandCmd)
}
var rootCmd = &cobra.Command{

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

@@ -5579,6 +5579,10 @@
"id": "store.sql_command.save.update.app_error",
"translation": "We couldn't update the command"
},
{
"id": "store.sql_command.get_by_trigger.app_error",
"translation": "We couldn't get the command"
},
{
"id": "store.sql_command_webhooks.get.app_error",
"translation": "We couldn't get the webhook"

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

@@ -119,6 +119,27 @@ func (s SqlCommandStore) GetByTeam(teamId string) store.StoreChannel {
return storeChannel
}
func (s SqlCommandStore) GetByTrigger(teamId string, trigger string) store.StoreChannel {
storeChannel := make(store.StoreChannel, 1)
go func() {
result := store.StoreResult{}
var command model.Command
if err := s.GetReplica().SelectOne(&command, "SELECT * FROM Commands WHERE TeamId = :TeamId AND `Trigger` = :Trigger AND DeleteAt = 0", map[string]interface{}{"TeamId": teamId, "Trigger": trigger}); err != nil {
result.Err = model.NewAppError("SqlCommandStore.GetByTrigger", "store.sql_command.get_by_trigger.app_error", nil, "teamId="+teamId+", trigger="+trigger+", err="+err.Error(), http.StatusInternalServerError)
}
result.Data = &command
storeChannel <- result
close(storeChannel)
}()
return storeChannel
}
func (s SqlCommandStore) Delete(commandId string, time int64) store.StoreChannel {
storeChannel := make(store.StoreChannel, 1)

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

@@ -7,6 +7,7 @@ import (
"testing"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/store"
)
func TestCommandStoreSave(t *testing.T) {
@@ -82,6 +83,41 @@ func TestCommandStoreGetByTeam(t *testing.T) {
}
}
func TestCommandStoreGetByTrigger(t *testing.T) {
ss := Setup()
o1 := &model.Command{}
o1.CreatorId = model.NewId()
o1.Method = model.COMMAND_METHOD_POST
o1.TeamId = model.NewId()
o1.URL = "http://nowhere.com/"
o1.Trigger = "trigger1"
o2 := &model.Command{}
o2.CreatorId = model.NewId()
o2.Method = model.COMMAND_METHOD_POST
o2.TeamId = model.NewId()
o2.URL = "http://nowhere.com/"
o2.Trigger = "trigger1"
o1 = (<-ss.Command().Save(o1)).Data.(*model.Command)
o2 = (<-ss.Command().Save(o2)).Data.(*model.Command)
if r1 := <-ss.Command().GetByTrigger(o1.TeamId, o1.Trigger); r1.Err != nil {
t.Fatal(r1.Err)
} else {
if r1.Data.(*model.Command).Id != o1.Id {
t.Fatal("invalid returned command")
}
}
store.Must(ss.Command().Delete(o1.Id, model.GetMillis()))
if result := <-ss.Command().GetByTrigger(o1.TeamId, o1.Trigger); result.Err == nil {
t.Fatal("no commands should have returned")
}
}
func TestCommandStoreDelete(t *testing.T) {
ss := Setup()

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

@@ -7,6 +7,7 @@ import (
"time"
l4g "github.com/alecthomas/log4go"
"github.com/mattermost/mattermost-server/model"
)
@@ -322,6 +323,7 @@ type CommandStore interface {
Save(webhook *model.Command) StoreChannel
Get(id string) StoreChannel
GetByTeam(teamId string) StoreChannel
GetByTrigger(teamId string, trigger string) StoreChannel
Delete(commandId string, time int64) StoreChannel
PermanentDeleteByTeam(teamId string) StoreChannel
PermanentDeleteByUser(userId string) StoreChannel