summaryrefslogtreecommitdiffstats
path: root/api
diff options
context:
space:
mode:
Diffstat (limited to 'api')
-rw-r--r--api/command.go807
-rw-r--r--api/command_echo.go89
-rw-r--r--api/command_echo_test.go42
-rw-r--r--api/command_join.go62
-rw-r--r--api/command_join_test.go71
-rw-r--r--api/command_loadtest.go365
-rw-r--r--api/command_loadtest_test.go221
-rw-r--r--api/command_logout.go37
-rw-r--r--api/command_logout_test.go32
-rw-r--r--api/command_me.go37
-rw-r--r--api/command_me_test.go47
-rw-r--r--api/command_shrug.go42
-rw-r--r--api/command_shrug_test.go47
-rw-r--r--api/command_test.go306
-rw-r--r--api/user.go4
-rw-r--r--api/webhook.go62
-rw-r--r--api/webhook_test.go84
17 files changed, 1689 insertions, 666 deletions
diff --git a/api/command.go b/api/command.go
index ab63a15a7..a8573cdcc 100644
--- a/api/command.go
+++ b/api/command.go
@@ -4,12 +4,11 @@
package api
import (
- "io"
+ "fmt"
+ "io/ioutil"
"net/http"
- "path"
- "strconv"
+ "net/url"
"strings"
- "time"
l4g "github.com/alecthomas/log4go"
"github.com/gorilla/mux"
@@ -17,630 +16,386 @@ import (
"github.com/mattermost/platform/utils"
)
-type commandHandler func(c *Context, command *model.Command) bool
-
-var (
- cmds = map[string]string{
- "logoutCommand": "/logout",
- "joinCommand": "/join",
- "loadTestCommand": "/loadtest",
- "echoCommand": "/echo",
- "shrugCommand": "/shrug",
- "meCommand": "/me",
- }
- commands = []commandHandler{
- logoutCommand,
- joinCommand,
- loadTestCommand,
- echoCommand,
- shrugCommand,
- meCommand,
- }
- commandNotImplementedErr = model.NewLocAppError("checkCommand", "api.command.no_implemented.app_error", nil, "")
-)
-var echoSem chan bool
-
-func InitCommand(r *mux.Router) {
- l4g.Debug(utils.T("api.command.init.debug"))
- r.Handle("/command", ApiUserRequired(command)).Methods("POST")
+type CommandProvider interface {
+ GetTrigger() string
+ GetCommand(c *Context) *model.Command
+ DoCommand(c *Context, channelId string, message string) *model.CommandResponse
}
-func command(c *Context, w http.ResponseWriter, r *http.Request) {
-
- props := model.MapFromJson(r.Body)
-
- command := &model.Command{
- Command: strings.TrimSpace(props["command"]),
- ChannelId: strings.TrimSpace(props["channelId"]),
- Suggest: props["suggest"] == "true",
- Suggestions: make([]*model.SuggestCommand, 0, 128),
- }
+var commandProviders = make(map[string]CommandProvider)
- checkCommand(c, command)
- if c.Err != nil {
- if c.Err != commandNotImplementedErr {
- return
- } else {
- c.Err = nil
- command.Response = model.RESP_NOT_IMPLEMENTED
- w.Write([]byte(command.ToJson()))
- return
- }
- } else {
- w.Write([]byte(command.ToJson()))
- }
+func RegisterCommandProvider(newProvider CommandProvider) {
+ commandProviders[newProvider.GetTrigger()] = newProvider
}
-func checkCommand(c *Context, command *model.Command) bool {
-
- if len(command.Command) == 0 || strings.Index(command.Command, "/") != 0 {
- c.Err = model.NewLocAppError("checkCommand", "api.command.check_command.start.app_error", nil, "")
- return false
+func GetCommandProvider(name string) CommandProvider {
+ provider, ok := commandProviders[name]
+ if ok {
+ return provider
}
- if len(command.ChannelId) > 0 {
- cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, command.ChannelId, c.Session.UserId)
+ return nil
+}
- if !c.HasPermissionsToChannel(cchan, "checkCommand") {
- return true
- }
- }
+func InitCommand(r *mux.Router) {
+ l4g.Debug(utils.T("api.command.init.debug"))
- if !command.Suggest {
- implemented := false
- for _, cmd := range cmds {
- bounds := len(cmd)
- if len(command.Command) < bounds {
- continue
- }
- if command.Command[:bounds] == cmd {
- implemented = true
- }
- }
- if !implemented {
- c.Err = commandNotImplementedErr
- return false
- }
- }
+ sr := r.PathPrefix("/commands").Subrouter()
- for _, v := range commands {
+ sr.Handle("/execute", ApiUserRequired(executeCommand)).Methods("POST")
+ sr.Handle("/list", ApiUserRequired(listCommands)).Methods("GET")
- if v(c, command) || c.Err != nil {
- return true
- }
- }
+ sr.Handle("/create", ApiUserRequired(createCommand)).Methods("POST")
+ sr.Handle("/list_team_commands", ApiUserRequired(listTeamCommands)).Methods("GET")
+ sr.Handle("/regen_token", ApiUserRequired(regenCommandToken)).Methods("POST")
+ sr.Handle("/delete", ApiUserRequired(deleteCommand)).Methods("POST")
- return false
+ sr.Handle("/test", ApiAppHandler(testCommand)).Methods("POST")
+ sr.Handle("/test", ApiAppHandler(testCommand)).Methods("GET")
}
-func logoutCommand(c *Context, command *model.Command) bool {
-
- cmd := cmds["logoutCommand"]
-
- if strings.Index(command.Command, cmd) == 0 {
- command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: c.T("api.command.logout_command.description")})
-
- if !command.Suggest {
- command.GotoLocation = "/logout"
- command.Response = model.RESP_EXECUTED
- return true
+func listCommands(c *Context, w http.ResponseWriter, r *http.Request) {
+ commands := make([]*model.Command, 0, 32)
+ seen := make(map[string]bool)
+ for _, value := range commandProviders {
+ cpy := *value.GetCommand(c)
+ if cpy.AutoComplete && !seen[cpy.Id] {
+ cpy.Sanitize()
+ seen[cpy.Trigger] = true
+ commands = append(commands, &cpy)
}
-
- } else if strings.Index(cmd, command.Command) == 0 {
- command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: c.T("api.command.logout_command.description")})
}
- return false
-}
-
-func echoCommand(c *Context, command *model.Command) bool {
- cmd := cmds["echoCommand"]
- maxThreads := 100
-
- if !command.Suggest && strings.Index(command.Command, cmd) == 0 {
- parameters := strings.SplitN(command.Command, " ", 2)
- if len(parameters) != 2 || len(parameters[1]) == 0 {
- return false
- }
- message := strings.Trim(parameters[1], " ")
- delay := 0
- if endMsg := strings.LastIndex(message, "\""); string(message[0]) == "\"" && endMsg > 1 {
- if checkDelay, err := strconv.Atoi(strings.Trim(message[endMsg:], " \"")); err == nil {
- delay = checkDelay
- }
- message = message[1:endMsg]
- } else if strings.Index(message, " ") > -1 {
- delayIdx := strings.LastIndex(message, " ")
- delayStr := strings.Trim(message[delayIdx:], " ")
-
- if checkDelay, err := strconv.Atoi(delayStr); err == nil {
- delay = checkDelay
- message = message[:delayIdx]
+ if result := <-Srv.Store.Command().GetByTeam(c.Session.TeamId); result.Err != nil {
+ c.Err = result.Err
+ return
+ } else {
+ teamCmds := result.Data.([]*model.Command)
+ for _, cmd := range teamCmds {
+ if cmd.AutoComplete && !seen[cmd.Id] {
+ cmd.Sanitize()
+ seen[cmd.Trigger] = true
+ commands = append(commands, cmd)
}
}
-
- if delay > 10000 {
- c.Err = model.NewLocAppError("echoCommand", "api.command.echo_command.under.app_error", nil, "")
- return false
- }
-
- if echoSem == nil {
- // We want one additional thread allowed so we never reach channel lockup
- echoSem = make(chan bool, maxThreads+1)
- }
-
- if len(echoSem) >= maxThreads {
- c.Err = model.NewLocAppError("echoCommand", "api.command.echo_command.high_volume.app_error", nil, "")
- return false
- }
-
- echoSem <- true
- go func() {
- defer func() { <-echoSem }()
- post := &model.Post{}
- post.ChannelId = command.ChannelId
- post.Message = message
-
- time.Sleep(time.Duration(delay) * time.Second)
-
- if _, err := CreatePost(c, post, true); err != nil {
- l4g.Error(utils.T("api.command.echo_command.create.error"), err)
- }
- }()
-
- command.Response = model.RESP_EXECUTED
- return true
-
- } else if strings.Index(cmd, command.Command) == 0 {
- command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: c.T("api.command.echo_command.description")})
}
- return false
+ w.Write([]byte(model.CommandListToJson(commands)))
}
-func meCommand(c *Context, command *model.Command) bool {
- cmd := cmds["meCommand"]
+func executeCommand(c *Context, w http.ResponseWriter, r *http.Request) {
+ props := model.MapFromJson(r.Body)
+ command := strings.TrimSpace(props["command"])
+ channelId := strings.TrimSpace(props["channelId"])
- if !command.Suggest && strings.Index(command.Command, cmd) == 0 {
- message := ""
+ if len(command) <= 1 || strings.Index(command, "/") != 0 {
+ c.Err = model.NewLocAppError("executeCommand", "api.command.execute_command.start.app_error", nil, "")
+ return
+ }
- parameters := strings.SplitN(command.Command, " ", 2)
- if len(parameters) > 1 {
- message += "*" + parameters[1] + "*"
- }
+ if len(channelId) > 0 {
+ cchan := Srv.Store.Channel().CheckPermissionsTo(c.Session.TeamId, channelId, c.Session.UserId)
- post := &model.Post{}
- post.Message = message
- post.ChannelId = command.ChannelId
- if _, err := CreatePost(c, post, false); err != nil {
- l4g.Error(utils.T("api.command.me_command.create.error"), err)
- return false
+ if !c.HasPermissionsToChannel(cchan, "checkCommand") {
+ return
}
- command.Response = model.RESP_EXECUTED
- return true
-
- } else if strings.Index(cmd, command.Command) == 0 {
- command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: c.T("api.command.me_command.description")})
}
- return false
-}
-
-func shrugCommand(c *Context, command *model.Command) bool {
- cmd := cmds["shrugCommand"]
+ parts := strings.Split(command, " ")
+ trigger := parts[0][1:]
+ message := strings.Join(parts[1:], " ")
+ provider := GetCommandProvider(trigger)
- if !command.Suggest && strings.Index(command.Command, cmd) == 0 {
- message := `¯\\\_(ツ)_/¯`
+ if provider != nil {
- parameters := strings.SplitN(command.Command, " ", 2)
- if len(parameters) > 1 {
- message += " " + parameters[1]
- }
-
- post := &model.Post{}
- post.Message = message
- post.ChannelId = command.ChannelId
- if _, err := CreatePost(c, post, false); err != nil {
- l4g.Error(utils.T("api.command.shrug_command.create.error"), err)
- return false
- }
- command.Response = model.RESP_EXECUTED
- return true
+ response := provider.DoCommand(c, channelId, message)
+ handleResponse(c, w, response, channelId)
+ return
+ } else {
+ chanChan := Srv.Store.Channel().Get(channelId)
+ teamChan := Srv.Store.Team().Get(c.Session.TeamId)
+ userChan := Srv.Store.User().Get(c.Session.UserId)
- } else if strings.Index(cmd, command.Command) == 0 {
- command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: c.T("api.command.shrug_command.description")})
- }
+ if result := <-Srv.Store.Command().GetByTeam(c.Session.TeamId); result.Err != nil {
+ c.Err = result.Err
+ return
+ } else {
- return false
-}
+ var team *model.Team
+ if tr := <-teamChan; tr.Err != nil {
+ c.Err = tr.Err
+ return
+ } else {
+ team = tr.Data.(*model.Team)
-func joinCommand(c *Context, command *model.Command) bool {
+ }
- // looks for "/join channel-name"
- cmd := cmds["joinCommand"]
+ var user *model.User
+ if ur := <-userChan; ur.Err != nil {
+ c.Err = ur.Err
+ return
+ } else {
+ user = ur.Data.(*model.User)
+ }
- if strings.Index(command.Command, cmd) == 0 {
+ var channel *model.Channel
+ if cr := <-chanChan; cr.Err != nil {
+ c.Err = cr.Err
+ return
+ } else {
+ channel = cr.Data.(*model.Channel)
+ }
- parts := strings.Split(command.Command, " ")
+ teamCmds := result.Data.([]*model.Command)
+ for _, cmd := range teamCmds {
+ if trigger == cmd.Trigger {
+ l4g.Debug(fmt.Sprintf(utils.T("api.command.execute_command.debug"), trigger, c.Session.UserId))
- startsWith := ""
+ p := url.Values{}
+ p.Set("token", cmd.Token)
- if len(parts) == 2 {
- startsWith = parts[1]
- }
+ p.Set("team_id", cmd.TeamId)
+ p.Set("team_domain", team.Name)
- if result := <-Srv.Store.Channel().GetMoreChannels(c.Session.TeamId, c.Session.UserId); result.Err != nil {
- c.Err = result.Err
- return false
- } else {
- channels := result.Data.(*model.ChannelList)
+ p.Set("channel_id", channelId)
+ p.Set("channel_name", channel.Name)
- for _, v := range channels.Channels {
+ p.Set("user_id", c.Session.UserId)
+ p.Set("user_name", user.Username)
- if v.Name == startsWith && !command.Suggest {
+ p.Set("command", "/"+trigger)
+ p.Set("text", message)
+ p.Set("response_url", "not supported yet")
- if v.Type == model.CHANNEL_DIRECT {
- return false
+ method := "POST"
+ if cmd.Method == model.COMMAND_METHOD_GET {
+ method = "GET"
}
- JoinChannel(c, v.Id, "")
-
- if c.Err != nil {
- return false
+ client := &http.Client{}
+ req, _ := http.NewRequest(method, cmd.URL, strings.NewReader(p.Encode()))
+ req.Header.Set("Accept", "application/json")
+ if cmd.Method == model.COMMAND_METHOD_POST {
+ req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
}
- command.GotoLocation = c.GetTeamURL() + "/channels/" + v.Name
- command.Response = model.RESP_EXECUTED
- return true
- }
+ if resp, err := client.Do(req); err != nil {
+ c.Err = model.NewLocAppError("command", "api.command.execute_command.failed.app_error", map[string]interface{}{"Trigger": trigger}, err.Error())
+ } else {
+ if resp.StatusCode == http.StatusOK {
+ response := model.CommandResponseFromJson(resp.Body)
+ if response == nil {
+ c.Err = model.NewLocAppError("command", "api.command.execute_command.failed_empty.app_error", map[string]interface{}{"Trigger": trigger}, "")
+ } else {
+ handleResponse(c, w, response, channelId)
+ }
+ } else {
+ body, _ := ioutil.ReadAll(resp.Body)
+ c.Err = model.NewLocAppError("command", "api.command.execute_command.failed_resp.app_error", map[string]interface{}{"Trigger": trigger, "Status": resp.Status}, string(body))
+ }
+ }
- if len(startsWith) == 0 || strings.Index(v.Name, startsWith) == 0 {
- command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd + " " + v.Name, Description: c.T("api.commmand.join_command.description")})
+ return
}
}
+
}
- } else if strings.Index(cmd, command.Command) == 0 {
- command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: c.T("api.commmand.join_command.description")})
}
- return false
+ c.Err = model.NewLocAppError("command", "api.command.execute_command.not_found.app_error", map[string]interface{}{"Trigger": trigger}, "")
}
-func loadTestCommand(c *Context, command *model.Command) bool {
- cmd := cmds["loadTestCommand"]
-
- // This command is only available when EnableTesting is true
- if !utils.Cfg.ServiceSettings.EnableTesting {
- return false
- }
-
- if strings.Index(command.Command, cmd) == 0 {
- if loadTestSetupCommand(c, command) {
- return true
- }
- if loadTestUsersCommand(c, command) {
- return true
- }
- if loadTestChannelsCommand(c, command) {
- return true
- }
- if loadTestPostsCommand(c, command) {
- return true
- }
- if loadTestUrlCommand(c, command) {
- return true
- }
- } else if strings.Index(cmd, command.Command) == 0 {
- command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: c.T("api.command.load_test_command.description")})
+func handleResponse(c *Context, w http.ResponseWriter, response *model.CommandResponse, channelId string) {
+ if response.ResponseType == model.COMMAND_RESPONSE_TYPE_IN_CHANNEL {
+ post := &model.Post{}
+ post.ChannelId = channelId
+ post.Message = response.Text
+ if _, err := CreatePost(c, post, true); err != nil {
+ c.Err = model.NewLocAppError("command", "api.command.execute_command.save.app_error", nil, "")
+ }
+ } else if response.ResponseType == model.COMMAND_RESPONSE_TYPE_EPHEMERAL {
+ // post := &model.Post{}
+ // post.ChannelId = channelId
+ // post.Message = "TODO_EPHEMERAL: " + response.Text
+ // if _, err := CreatePost(c, post, true); err != nil {
+ // c.Err = model.NewLocAppError("command", "api.command.execute_command.save.app_error", nil, "")
+ // }
}
- return false
+ w.Write([]byte(response.ToJson()))
}
-func parseRange(command string, cmd string) (utils.Range, bool) {
- tokens := strings.Fields(strings.TrimPrefix(command, cmd))
- var begin int
- var end int
- var err1 error
- var err2 error
- switch {
- case len(tokens) == 1:
- begin, err1 = strconv.Atoi(tokens[0])
- end = begin
- if err1 != nil {
- return utils.Range{0, 0}, false
- }
- case len(tokens) >= 2:
- begin, err1 = strconv.Atoi(tokens[0])
- end, err2 = strconv.Atoi(tokens[1])
- if err1 != nil || err2 != nil {
- return utils.Range{0, 0}, false
- }
- default:
- return utils.Range{0, 0}, false
+func createCommand(c *Context, w http.ResponseWriter, r *http.Request) {
+ if !*utils.Cfg.ServiceSettings.EnableCommands {
+ c.Err = model.NewLocAppError("createCommand", "api.command.disabled.app_error", nil, "")
+ c.Err.StatusCode = http.StatusNotImplemented
+ return
}
- return utils.Range{begin, end}, true
-}
-func contains(items []string, token string) bool {
- for _, elem := range items {
- if elem == token {
- return true
+ if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
+ if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
+ c.Err = model.NewLocAppError("createCommand", "api.command.admin_only.app_error", nil, "")
+ c.Err.StatusCode = http.StatusForbidden
+ return
}
}
- return false
-}
-func loadTestSetupCommand(c *Context, command *model.Command) bool {
- cmd := cmds["loadTestCommand"] + " setup"
+ c.LogAudit("attempt")
- if strings.Index(command.Command, cmd) == 0 && !command.Suggest {
- tokens := strings.Fields(strings.TrimPrefix(command.Command, cmd))
- doTeams := contains(tokens, "teams")
- doFuzz := contains(tokens, "fuzz")
+ cmd := model.CommandFromJson(r.Body)
- numArgs := 0
- if doTeams {
- numArgs++
- }
- if doFuzz {
- numArgs++
- }
+ if cmd == nil {
+ c.SetInvalidParam("createCommand", "command")
+ return
+ }
- var numTeams int
- var numChannels int
- var numUsers int
- var numPosts int
-
- // Defaults
- numTeams = 10
- numChannels = 10
- numUsers = 10
- numPosts = 10
-
- if doTeams {
- if (len(tokens) - numArgs) >= 4 {
- numTeams, _ = strconv.Atoi(tokens[numArgs+0])
- numChannels, _ = strconv.Atoi(tokens[numArgs+1])
- numUsers, _ = strconv.Atoi(tokens[numArgs+2])
- numPosts, _ = strconv.Atoi(tokens[numArgs+3])
- }
- } else {
- if (len(tokens) - numArgs) >= 3 {
- numChannels, _ = strconv.Atoi(tokens[numArgs+0])
- numUsers, _ = strconv.Atoi(tokens[numArgs+1])
- numPosts, _ = strconv.Atoi(tokens[numArgs+2])
- }
- }
- client := model.NewClient(c.GetSiteURL())
+ cmd.CreatorId = c.Session.UserId
+ cmd.TeamId = c.Session.TeamId
- if doTeams {
- if err := CreateBasicUser(client); err != nil {
- l4g.Error(utils.T("api.command.load_test_setup_command.create.error"))
- return true
- }
- client.LoginByEmail(BTEST_TEAM_NAME, BTEST_USER_EMAIL, BTEST_USER_PASSWORD)
- environment, err := CreateTestEnvironmentWithTeams(
- client,
- utils.Range{numTeams, numTeams},
- utils.Range{numChannels, numChannels},
- utils.Range{numUsers, numUsers},
- utils.Range{numPosts, numPosts},
- doFuzz)
- if err != true {
- l4g.Error(utils.T("api.command.load_test_setup_command.create.error"))
- return true
- } else {
- l4g.Info("Testing environment created")
- for i := 0; i < len(environment.Teams); i++ {
- l4g.Info(utils.T("api.command.load_test_setup_command.created.info"), environment.Teams[i].Name)
- l4g.Info(utils.T("api.command.load_test_setup_command.login.info"), environment.Environments[i].Users[0].Email, USER_PASSWORD)
- }
- }
- } else {
- client.MockSession(c.Session.Token)
- CreateTestEnvironmentInTeam(
- client,
- c.Session.TeamId,
- utils.Range{numChannels, numChannels},
- utils.Range{numUsers, numUsers},
- utils.Range{numPosts, numPosts},
- doFuzz)
- }
- return true
- } else if strings.Index(cmd, command.Command) == 0 {
- command.AddSuggestion(&model.SuggestCommand{
- Suggestion: cmd,
- Description: c.T("api.command.load_test_setup_command.description")})
+ if result := <-Srv.Store.Command().Save(cmd); result.Err != nil {
+ c.Err = result.Err
+ return
+ } else {
+ c.LogAudit("success")
+ rcmd := result.Data.(*model.Command)
+ w.Write([]byte(rcmd.ToJson()))
}
-
- return false
}
-func loadTestUsersCommand(c *Context, command *model.Command) bool {
- cmd1 := cmds["loadTestCommand"] + " users"
- cmd2 := cmds["loadTestCommand"] + " users fuzz"
+func listTeamCommands(c *Context, w http.ResponseWriter, r *http.Request) {
+ if !*utils.Cfg.ServiceSettings.EnableCommands {
+ c.Err = model.NewLocAppError("listTeamCommands", "api.command.disabled.app_error", nil, "")
+ c.Err.StatusCode = http.StatusNotImplemented
+ return
+ }
- if strings.Index(command.Command, cmd1) == 0 && !command.Suggest {
- cmd := cmd1
- doFuzz := false
- if strings.Index(command.Command, cmd2) == 0 {
- doFuzz = true
- cmd = cmd2
- }
- usersr, err := parseRange(command.Command, cmd)
- if err == false {
- usersr = utils.Range{10, 15}
+ if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
+ if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
+ c.Err = model.NewLocAppError("listTeamCommands", "api.command.admin_only.app_error", nil, "")
+ c.Err.StatusCode = http.StatusForbidden
+ return
}
- client := model.NewClient(c.GetSiteURL())
- userCreator := NewAutoUserCreator(client, c.Session.TeamId)
- userCreator.Fuzzy = doFuzz
- userCreator.CreateTestUsers(usersr)
- return true
- } else if strings.Index(cmd1, command.Command) == 0 {
- command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd1, Description: c.T("api.command.load_test_users_command.users.description")})
- command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd2, Description: c.T("api.command.load_test_users_command.fuzz.description")})
- } else if strings.Index(cmd2, command.Command) == 0 {
- command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd2, Description: c.T("api.command.load_test_users_command.fuzz.description")})
}
- return false
+ if result := <-Srv.Store.Command().GetByTeam(c.Session.TeamId); result.Err != nil {
+ c.Err = result.Err
+ return
+ } else {
+ cmds := result.Data.([]*model.Command)
+ w.Write([]byte(model.CommandListToJson(cmds)))
+ }
}
-func loadTestChannelsCommand(c *Context, command *model.Command) bool {
- cmd1 := cmds["loadTestCommand"] + " channels"
- cmd2 := cmds["loadTestCommand"] + " channels fuzz"
+func regenCommandToken(c *Context, w http.ResponseWriter, r *http.Request) {
+ if !*utils.Cfg.ServiceSettings.EnableCommands {
+ c.Err = model.NewLocAppError("regenCommandToken", "api.command.disabled.app_error", nil, "")
+ c.Err.StatusCode = http.StatusNotImplemented
+ return
+ }
- if strings.Index(command.Command, cmd1) == 0 && !command.Suggest {
- cmd := cmd1
- doFuzz := false
- if strings.Index(command.Command, cmd2) == 0 {
- doFuzz = true
- cmd = cmd2
- }
- channelsr, err := parseRange(command.Command, cmd)
- if err == false {
- channelsr = utils.Range{20, 30}
+ if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
+ if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
+ c.Err = model.NewLocAppError("regenCommandToken", "api.command.admin_only.app_error", nil, "")
+ c.Err.StatusCode = http.StatusForbidden
+ return
}
- client := model.NewClient(c.GetSiteURL())
- client.MockSession(c.Session.Token)
- channelCreator := NewAutoChannelCreator(client, c.Session.TeamId)
- channelCreator.Fuzzy = doFuzz
- channelCreator.CreateTestChannels(channelsr)
- return true
- } else if strings.Index(cmd1, command.Command) == 0 {
- command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd1, Description: c.T("api.command.load_test_channels_command.channel.description")})
- command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd2, Description: c.T("api.command.load_test_channels_command.fuzz.description")})
- } else if strings.Index(cmd2, command.Command) == 0 {
- command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd2, Description: c.T("api.command.load_test_channels_command.fuzz.description")})
}
- return false
-}
+ c.LogAudit("attempt")
-func loadTestPostsCommand(c *Context, command *model.Command) bool {
- cmd1 := cmds["loadTestCommand"] + " posts"
- cmd2 := cmds["loadTestCommand"] + " posts fuzz"
+ props := model.MapFromJson(r.Body)
- if strings.Index(command.Command, cmd1) == 0 && !command.Suggest {
- cmd := cmd1
- doFuzz := false
- if strings.Index(command.Command, cmd2) == 0 {
- cmd = cmd2
- doFuzz = true
- }
+ id := props["id"]
+ if len(id) == 0 {
+ c.SetInvalidParam("regenCommandToken", "id")
+ return
+ }
- postsr, err := parseRange(command.Command, cmd)
- if err == false {
- postsr = utils.Range{20, 30}
- }
+ var cmd *model.Command
+ if result := <-Srv.Store.Command().Get(id); result.Err != nil {
+ c.Err = result.Err
+ return
+ } else {
+ cmd = result.Data.(*model.Command)
- tokens := strings.Fields(strings.TrimPrefix(command.Command, cmd))
- rimages := utils.Range{0, 0}
- if len(tokens) >= 3 {
- if numImages, err := strconv.Atoi(tokens[2]); err == nil {
- rimages = utils.Range{numImages, numImages}
- }
+ if c.Session.TeamId != cmd.TeamId || (c.Session.UserId != cmd.CreatorId && !c.IsTeamAdmin()) {
+ c.LogAudit("fail - inappropriate permissions")
+ c.Err = model.NewLocAppError("regenToken", "api.command.regen.app_error", nil, "user_id="+c.Session.UserId)
+ return
}
+ }
- var usernames []string
- if result := <-Srv.Store.User().GetProfiles(c.Session.TeamId); result.Err == nil {
- profileUsers := result.Data.(map[string]*model.User)
- usernames = make([]string, len(profileUsers))
- i := 0
- for _, userprof := range profileUsers {
- usernames[i] = userprof.Username
- i++
- }
- }
+ cmd.Token = model.NewId()
- client := model.NewClient(c.GetSiteURL())
- client.MockSession(c.Session.Token)
- testPoster := NewAutoPostCreator(client, command.ChannelId)
- testPoster.Fuzzy = doFuzz
- testPoster.Users = usernames
-
- numImages := utils.RandIntFromRange(rimages)
- numPosts := utils.RandIntFromRange(postsr)
- for i := 0; i < numPosts; i++ {
- testPoster.HasImage = (i < numImages)
- testPoster.CreateRandomPost()
- }
- return true
- } else if strings.Index(cmd1, command.Command) == 0 {
- command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd1, Description: c.T("api.command.load_test_posts_command.posts.description")})
- command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd2, Description: c.T("api.command.load_test_posts_command.fuzz.description")})
- } else if strings.Index(cmd2, command.Command) == 0 {
- command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd2, Description: c.T("api.command.load_test_posts_command.fuzz.description")})
+ if result := <-Srv.Store.Command().Update(cmd); result.Err != nil {
+ c.Err = result.Err
+ return
+ } else {
+ w.Write([]byte(result.Data.(*model.Command).ToJson()))
}
-
- return false
}
-func loadTestUrlCommand(c *Context, command *model.Command) bool {
- cmd := cmds["loadTestCommand"] + " url"
-
- if strings.Index(command.Command, cmd) == 0 && !command.Suggest {
- url := ""
+func deleteCommand(c *Context, w http.ResponseWriter, r *http.Request) {
+ if !*utils.Cfg.ServiceSettings.EnableCommands {
+ c.Err = model.NewLocAppError("deleteCommand", "api.command.disabled.app_error", nil, "")
+ c.Err.StatusCode = http.StatusNotImplemented
+ return
+ }
- parameters := strings.SplitN(command.Command, " ", 3)
- if len(parameters) != 3 {
- c.Err = model.NewLocAppError("loadTestUrlCommand", "api.command.load_test_url_command.url.app_error", nil, "")
- return true
- } else {
- url = parameters[2]
+ if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
+ if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
+ c.Err = model.NewLocAppError("deleteCommand", "api.command.admin_only.app_error", nil, "")
+ c.Err.StatusCode = http.StatusForbidden
+ return
}
+ }
- // provide a shortcut to easily access tests stored in doc/developer/tests
- if !strings.HasPrefix(url, "http") {
- url = "https://raw.githubusercontent.com/mattermost/platform/master/doc/developer/tests/" + url
+ c.LogAudit("attempt")
- if path.Ext(url) == "" {
- url += ".md"
- }
- }
-
- var contents io.ReadCloser
- if r, err := http.Get(url); err != nil {
- c.Err = model.NewLocAppError("loadTestUrlCommand", "api.command.load_test_url_command.file.app_error", nil, err.Error())
- return false
- } else if r.StatusCode > 400 {
- c.Err = model.NewLocAppError("loadTestUrlCommand", "api.command.load_test_url_command.file.app_error", nil, r.Status)
- return false
- } else {
- contents = r.Body
- }
+ props := model.MapFromJson(r.Body)
- bytes := make([]byte, 4000)
+ id := props["id"]
+ if len(id) == 0 {
+ c.SetInvalidParam("deleteCommand", "id")
+ return
+ }
- // break contents into 4000 byte posts
- for {
- length, err := contents.Read(bytes)
- if err != nil && err != io.EOF {
- c.Err = model.NewLocAppError("loadTestUrlCommand", "api.command.load_test_url_command.reading.app_error", nil, err.Error())
- return false
- }
+ if result := <-Srv.Store.Command().Get(id); result.Err != nil {
+ c.Err = result.Err
+ return
+ } else {
+ if c.Session.TeamId != result.Data.(*model.Command).TeamId || (c.Session.UserId != result.Data.(*model.Command).CreatorId && !c.IsTeamAdmin()) {
+ c.LogAudit("fail - inappropriate permissions")
+ c.Err = model.NewLocAppError("deleteCommand", "api.command.delete.app_error", nil, "user_id="+c.Session.UserId)
+ return
+ }
+ }
- if length == 0 {
- break
- }
+ if err := (<-Srv.Store.Command().Delete(id, model.GetMillis())).Err; err != nil {
+ c.Err = err
+ return
+ }
- post := &model.Post{}
- post.Message = string(bytes[:length])
- post.ChannelId = command.ChannelId
+ c.LogAudit("success")
+ w.Write([]byte(model.MapToJson(props)))
+}
- if _, err := CreatePost(c, post, false); err != nil {
- l4g.Error(utils.T("api.command.load_test_url_command.create.error"), err)
- return false
- }
- }
+func testCommand(c *Context, w http.ResponseWriter, r *http.Request) {
+ r.ParseForm()
- command.Response = model.RESP_EXECUTED
+ msg := ""
+ if r.Method == "POST" {
+ msg = msg + "\ntoken=" + r.FormValue("token")
+ msg = msg + "\nteam_domain=" + r.FormValue("team_domain")
+ } else {
+ body, _ := ioutil.ReadAll(r.Body)
+ msg = string(body)
+ }
- return true
- } else if strings.Index(cmd, command.Command) == 0 && strings.Index(command.Command, "/loadtest posts") != 0 {
- command.AddSuggestion(&model.SuggestCommand{Suggestion: cmd, Description: c.T("api.command.load_test_url_command.description")})
+ rc := &model.CommandResponse{
+ Text: "test command response " + msg,
+ ResponseType: model.COMMAND_RESPONSE_TYPE_IN_CHANNEL,
}
- return false
+ w.Write([]byte(rc.ToJson()))
}
diff --git a/api/command_echo.go b/api/command_echo.go
new file mode 100644
index 000000000..805db7ad2
--- /dev/null
+++ b/api/command_echo.go
@@ -0,0 +1,89 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package api
+
+import (
+ "strconv"
+ "strings"
+ "time"
+
+ l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/platform/model"
+)
+
+var echoSem chan bool
+
+type EchoProvider struct {
+}
+
+const (
+ CMD_ECHO = "echo"
+)
+
+func init() {
+ RegisterCommandProvider(&EchoProvider{})
+}
+
+func (me *EchoProvider) GetTrigger() string {
+ return CMD_ECHO
+}
+
+func (me *EchoProvider) GetCommand(c *Context) *model.Command {
+ return &model.Command{
+ Trigger: CMD_ECHO,
+ AutoComplete: true,
+ AutoCompleteDesc: c.T("api.command_echo.desc"),
+ AutoCompleteHint: c.T("api.command_echo.hint"),
+ DisplayName: c.T("api.command_echo.name"),
+ }
+}
+
+func (me *EchoProvider) DoCommand(c *Context, channelId string, message string) *model.CommandResponse {
+ maxThreads := 100
+
+ delay := 0
+ if endMsg := strings.LastIndex(message, "\""); string(message[0]) == "\"" && endMsg > 1 {
+ if checkDelay, err := strconv.Atoi(strings.Trim(message[endMsg:], " \"")); err == nil {
+ delay = checkDelay
+ }
+ message = message[1:endMsg]
+ } else if strings.Index(message, " ") > -1 {
+ delayIdx := strings.LastIndex(message, " ")
+ delayStr := strings.Trim(message[delayIdx:], " ")
+
+ if checkDelay, err := strconv.Atoi(delayStr); err == nil {
+ delay = checkDelay
+ message = message[:delayIdx]
+ }
+ }
+
+ if delay > 10000 {
+ return &model.CommandResponse{Text: c.T("api.command_echo.delay.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ }
+
+ if echoSem == nil {
+ // We want one additional thread allowed so we never reach channel lockup
+ echoSem = make(chan bool, maxThreads+1)
+ }
+
+ if len(echoSem) >= maxThreads {
+ return &model.CommandResponse{Text: c.T("api.command_echo.high_volume.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ }
+
+ echoSem <- true
+ go func() {
+ defer func() { <-echoSem }()
+ post := &model.Post{}
+ post.ChannelId = channelId
+ post.Message = message
+
+ time.Sleep(time.Duration(delay) * time.Second)
+
+ if _, err := CreatePost(c, post, true); err != nil {
+ l4g.Error(c.T("api.command_echo.create.app_error"), err)
+ }
+ }()
+
+ return &model.CommandResponse{}
+}
diff --git a/api/command_echo_test.go b/api/command_echo_test.go
new file mode 100644
index 000000000..3bfaa0279
--- /dev/null
+++ b/api/command_echo_test.go
@@ -0,0 +1,42 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package api
+
+import (
+ "testing"
+ "time"
+
+ "github.com/mattermost/platform/model"
+ "github.com/mattermost/platform/store"
+)
+
+func TestEchoCommand(t *testing.T) {
+ Setup()
+
+ team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
+
+ user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
+ user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user1.Id))
+
+ Client.LoginByEmail(team.Name, user1.Email, "pwd")
+
+ channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
+ channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
+
+ echoTestString := "/echo test"
+
+ r1 := Client.Must(Client.Command(channel1.Id, echoTestString, false)).Data.(*model.CommandResponse)
+ if r1 == nil {
+ t.Fatal("Echo command failed to execute")
+ }
+
+ time.Sleep(100 * time.Millisecond)
+
+ p1 := Client.Must(Client.GetPosts(channel1.Id, 0, 2, "")).Data.(*model.PostList)
+ if len(p1.Order) != 1 {
+ t.Fatal("Echo command failed to send")
+ }
+}
diff --git a/api/command_join.go b/api/command_join.go
new file mode 100644
index 000000000..ba3b0041e
--- /dev/null
+++ b/api/command_join.go
@@ -0,0 +1,62 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package api
+
+import (
+ "github.com/mattermost/platform/model"
+)
+
+type JoinProvider struct {
+}
+
+const (
+ CMD_JOIN = "join"
+)
+
+func init() {
+ RegisterCommandProvider(&JoinProvider{})
+}
+
+func (me *JoinProvider) GetTrigger() string {
+ return CMD_JOIN
+}
+
+func (me *JoinProvider) GetCommand(c *Context) *model.Command {
+ return &model.Command{
+ Trigger: CMD_JOIN,
+ AutoComplete: true,
+ AutoCompleteDesc: c.T("api.command_join.desc"),
+ AutoCompleteHint: c.T("api.command_join.hint"),
+ DisplayName: c.T("api.command_join.name"),
+ }
+}
+
+func (me *JoinProvider) DoCommand(c *Context, channelId string, message string) *model.CommandResponse {
+ if result := <-Srv.Store.Channel().GetMoreChannels(c.Session.TeamId, c.Session.UserId); result.Err != nil {
+ return &model.CommandResponse{Text: c.T("api.command_join.list.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ } else {
+ channels := result.Data.(*model.ChannelList)
+
+ for _, v := range channels.Channels {
+
+ if v.Name == message {
+
+ if v.Type == model.CHANNEL_DIRECT {
+ return &model.CommandResponse{Text: c.T("api.command_join.fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ }
+
+ JoinChannel(c, v.Id, "")
+
+ if c.Err != nil {
+ c.Err = nil
+ return &model.CommandResponse{Text: c.T("api.command_join.fail.app_error"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ }
+
+ return &model.CommandResponse{GotoLocation: c.GetTeamURL() + "/channels/" + v.Name, Text: c.T("api.command_join.success"), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ }
+ }
+ }
+
+ return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: c.T("api.command_join.missing.app_error")}
+}
diff --git a/api/command_join_test.go b/api/command_join_test.go
new file mode 100644
index 000000000..7260915a6
--- /dev/null
+++ b/api/command_join_test.go
@@ -0,0 +1,71 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package api
+
+import (
+ "strings"
+ "testing"
+
+ "github.com/mattermost/platform/model"
+ "github.com/mattermost/platform/store"
+)
+
+func TestJoinCommands(t *testing.T) {
+ Setup()
+
+ team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
+
+ user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
+ user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user1.Id))
+
+ Client.LoginByEmail(team.Name, user1.Email, "pwd")
+
+ channel0 := &model.Channel{DisplayName: "00", Name: "00" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
+ channel0 = Client.Must(Client.CreateChannel(channel0)).Data.(*model.Channel)
+
+ channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
+ channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
+ Client.Must(Client.LeaveChannel(channel1.Id))
+
+ channel2 := &model.Channel{DisplayName: "BB", Name: "bb" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
+ channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
+ Client.Must(Client.LeaveChannel(channel2.Id))
+
+ user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
+ user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user1.Id))
+
+ data := make(map[string]string)
+ data["user_id"] = user2.Id
+ channel3 := Client.Must(Client.CreateDirectChannel(data)).Data.(*model.Channel)
+
+ rs5 := Client.Must(Client.Command(channel0.Id, "/join "+channel2.Name, false)).Data.(*model.CommandResponse)
+ if !strings.HasSuffix(rs5.GotoLocation, "/"+team.Name+"/channels/"+channel2.Name) {
+ t.Fatal("failed to join channel")
+ }
+
+ rs6 := Client.Must(Client.Command(channel0.Id, "/join "+channel3.Name, false)).Data.(*model.CommandResponse)
+ if strings.HasSuffix(rs6.GotoLocation, "/"+team.Name+"/channels/"+channel3.Name) {
+ t.Fatal("should not have joined direct message channel")
+ }
+
+ c1 := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList)
+
+ if len(c1.Channels) != 5 { // 4 because of town-square, off-topic and direct
+ t.Fatal("didn't join channel")
+ }
+
+ found := false
+ for _, c := range c1.Channels {
+ if c.Name == channel2.Name {
+ found = true
+ break
+ }
+ }
+ if !found {
+ t.Fatal("didn't join channel")
+ }
+}
diff --git a/api/command_loadtest.go b/api/command_loadtest.go
new file mode 100644
index 000000000..c7c4f98f5
--- /dev/null
+++ b/api/command_loadtest.go
@@ -0,0 +1,365 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package api
+
+import (
+ "io"
+ "net/http"
+ "path"
+ "strconv"
+ "strings"
+
+ l4g "github.com/alecthomas/log4go"
+ "github.com/mattermost/platform/model"
+ "github.com/mattermost/platform/utils"
+)
+
+var usage = `Mattermost load testing commands to help configure the system
+
+ COMMANDS:
+
+ Setup - Creates a testing environment in current team.
+ /loadtest setup [teams] [fuzz] <Num Channels> <Num Users> <NumPosts>
+
+ Example:
+ /loadtest setup teams fuzz 10 20 50
+
+ Users - Add a specified number of random users with fuzz text to current team.
+ /loadtest users [fuzz] <Min Users> <Max Users>
+
+ Example:
+ /loadtest users fuzz 5 10
+
+ Channels - Add a specified number of random channels with fuzz text to current team.
+ /loadtest channels [fuzz] <Min Channels> <Max Channels>
+
+ Example:
+ /loadtest channels fuzz 5 10
+
+ Posts - Add some random posts with fuzz text to current channel.
+ /loadtest posts [fuzz] <Min Posts> <Max Posts> <Max Images>
+
+ Example:
+ /loadtest posts fuzz 5 10 3
+
+ Url - Add a post containing the text from a given url to current channel.
+ /loadtest url
+
+ Example:
+ /loadtest http://www.example.com/sample_file.md
+
+
+`
+
+const (
+ CMD_LOADTEST = "loadtest"
+)
+
+type LoadTestProvider struct {
+}
+
+func init() {
+ if !utils.Cfg.ServiceSettings.EnableTesting {
+ RegisterCommandProvider(&LoadTestProvider{})
+ }
+}
+
+func (me *LoadTestProvider) GetTrigger() string {
+ return CMD_LOADTEST
+}
+
+func (me *LoadTestProvider) GetCommand(c *Context) *model.Command {
+ return &model.Command{
+ Trigger: CMD_LOADTEST,
+ AutoComplete: false,
+ AutoCompleteDesc: "Debug Load Testing",
+ AutoCompleteHint: "help",
+ DisplayName: "loadtest",
+ }
+}
+
+func (me *LoadTestProvider) DoCommand(c *Context, channelId string, message string) *model.CommandResponse {
+
+ //This command is only available when EnableTesting is true
+ if !utils.Cfg.ServiceSettings.EnableTesting {
+ return &model.CommandResponse{}
+ }
+
+ if strings.HasPrefix(message, "setup") {
+ return me.SetupCommand(c, channelId, message)
+ }
+
+ if strings.HasPrefix(message, "users") {
+ return me.UsersCommand(c, channelId, message)
+ }
+
+ if strings.HasPrefix(message, "channels") {
+ return me.ChannelsCommand(c, channelId, message)
+ }
+
+ if strings.HasPrefix(message, "posts") {
+ return me.PostsCommand(c, channelId, message)
+ }
+
+ if strings.HasPrefix(message, "url") {
+ return me.UrlCommand(c, channelId, message)
+ }
+
+ return me.HelpCommand(c, channelId, message)
+}
+
+func (me *LoadTestProvider) HelpCommand(c *Context, channelId string, message string) *model.CommandResponse {
+ return &model.CommandResponse{Text: usage, ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+}
+
+func (me *LoadTestProvider) SetupCommand(c *Context, channelId string, message string) *model.CommandResponse {
+ tokens := strings.Fields(strings.TrimPrefix(message, "setup"))
+ doTeams := contains(tokens, "teams")
+ doFuzz := contains(tokens, "fuzz")
+
+ numArgs := 0
+ if doTeams {
+ numArgs++
+ }
+ if doFuzz {
+ numArgs++
+ }
+
+ var numTeams int
+ var numChannels int
+ var numUsers int
+ var numPosts int
+
+ // Defaults
+ numTeams = 10
+ numChannels = 10
+ numUsers = 10
+ numPosts = 10
+
+ if doTeams {
+ if (len(tokens) - numArgs) >= 4 {
+ numTeams, _ = strconv.Atoi(tokens[numArgs+0])
+ numChannels, _ = strconv.Atoi(tokens[numArgs+1])
+ numUsers, _ = strconv.Atoi(tokens[numArgs+2])
+ numPosts, _ = strconv.Atoi(tokens[numArgs+3])
+ }
+ } else {
+ if (len(tokens) - numArgs) >= 3 {
+ numChannels, _ = strconv.Atoi(tokens[numArgs+0])
+ numUsers, _ = strconv.Atoi(tokens[numArgs+1])
+ numPosts, _ = strconv.Atoi(tokens[numArgs+2])
+ }
+ }
+ client := model.NewClient(c.GetSiteURL())
+
+ if doTeams {
+ if err := CreateBasicUser(client); err != nil {
+ return &model.CommandResponse{Text: "Failed to create testing environment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ }
+ client.LoginByEmail(BTEST_TEAM_NAME, BTEST_USER_EMAIL, BTEST_USER_PASSWORD)
+ environment, err := CreateTestEnvironmentWithTeams(
+ client,
+ utils.Range{numTeams, numTeams},
+ utils.Range{numChannels, numChannels},
+ utils.Range{numUsers, numUsers},
+ utils.Range{numPosts, numPosts},
+ doFuzz)
+ if err != true {
+ return &model.CommandResponse{Text: "Failed to create testing environment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ } else {
+ l4g.Info("Testing environment created")
+ for i := 0; i < len(environment.Teams); i++ {
+ l4g.Info("Team Created: " + environment.Teams[i].Name)
+ l4g.Info("\t User to login: " + environment.Environments[i].Users[0].Email + ", " + USER_PASSWORD)
+ }
+ }
+ } else {
+ client.MockSession(c.Session.Token)
+ CreateTestEnvironmentInTeam(
+ client,
+ c.Session.TeamId,
+ utils.Range{numChannels, numChannels},
+ utils.Range{numUsers, numUsers},
+ utils.Range{numPosts, numPosts},
+ doFuzz)
+ }
+
+ return &model.CommandResponse{Text: "Creating enviroment...", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+}
+
+func (me *LoadTestProvider) UsersCommand(c *Context, channelId string, message string) *model.CommandResponse {
+ cmd := strings.TrimSpace(strings.TrimPrefix(message, "users"))
+
+ doFuzz := false
+ if strings.Index(cmd, "fuzz") == 0 {
+ doFuzz = true
+ cmd = strings.TrimSpace(strings.TrimPrefix(cmd, "fuzz"))
+ }
+
+ usersr, err := parseRange(cmd, "")
+ if err == false {
+ usersr = utils.Range{2, 5}
+ }
+
+ client := model.NewClient(c.GetSiteURL())
+ userCreator := NewAutoUserCreator(client, c.Session.TeamId)
+ userCreator.Fuzzy = doFuzz
+ userCreator.CreateTestUsers(usersr)
+
+ return &model.CommandResponse{Text: "Adding users...", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+}
+
+func (me *LoadTestProvider) ChannelsCommand(c *Context, channelId string, message string) *model.CommandResponse {
+ cmd := strings.TrimSpace(strings.TrimPrefix(message, "channels"))
+
+ doFuzz := false
+ if strings.Index(cmd, "fuzz") == 0 {
+ doFuzz = true
+ cmd = strings.TrimSpace(strings.TrimPrefix(cmd, "fuzz"))
+ }
+
+ channelsr, err := parseRange(cmd, "")
+ if err == false {
+ channelsr = utils.Range{2, 5}
+ }
+ client := model.NewClient(c.GetSiteURL())
+ client.MockSession(c.Session.Token)
+ channelCreator := NewAutoChannelCreator(client, c.Session.TeamId)
+ channelCreator.Fuzzy = doFuzz
+ channelCreator.CreateTestChannels(channelsr)
+
+ return &model.CommandResponse{Text: "Adding channels...", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+}
+
+func (me *LoadTestProvider) PostsCommand(c *Context, channelId string, message string) *model.CommandResponse {
+ cmd := strings.TrimSpace(strings.TrimPrefix(message, "posts"))
+
+ doFuzz := false
+ if strings.Index(cmd, "fuzz") == 0 {
+ doFuzz = true
+ cmd = strings.TrimSpace(strings.TrimPrefix(cmd, "fuzz"))
+ }
+
+ postsr, err := parseRange(cmd, "")
+ if err == false {
+ postsr = utils.Range{20, 30}
+ }
+
+ tokens := strings.Fields(cmd)
+ rimages := utils.Range{0, 0}
+ if len(tokens) >= 3 {
+ if numImages, err := strconv.Atoi(tokens[2]); err == nil {
+ rimages = utils.Range{numImages, numImages}
+ }
+ }
+
+ var usernames []string
+ if result := <-Srv.Store.User().GetProfiles(c.Session.TeamId); result.Err == nil {
+ profileUsers := result.Data.(map[string]*model.User)
+ usernames = make([]string, len(profileUsers))
+ i := 0
+ for _, userprof := range profileUsers {
+ usernames[i] = userprof.Username
+ i++
+ }
+ }
+
+ client := model.NewClient(c.GetSiteURL())
+ client.MockSession(c.Session.Token)
+ testPoster := NewAutoPostCreator(client, channelId)
+ testPoster.Fuzzy = doFuzz
+ testPoster.Users = usernames
+
+ numImages := utils.RandIntFromRange(rimages)
+ numPosts := utils.RandIntFromRange(postsr)
+ for i := 0; i < numPosts; i++ {
+ testPoster.HasImage = (i < numImages)
+ testPoster.CreateRandomPost()
+ }
+
+ return &model.CommandResponse{Text: "Adding posts...", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+}
+
+func (me *LoadTestProvider) UrlCommand(c *Context, channelId string, message string) *model.CommandResponse {
+ url := strings.TrimSpace(strings.TrimPrefix(message, "url"))
+ if len(url) == 0 {
+ return &model.CommandResponse{Text: "Command must contain a url", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ }
+
+ // provide a shortcut to easily access tests stored in doc/developer/tests
+ if !strings.HasPrefix(url, "http") {
+ url = "https://raw.githubusercontent.com/mattermost/platform/master/doc/developer/tests/" + url
+
+ if path.Ext(url) == "" {
+ url += ".md"
+ }
+ }
+
+ var contents io.ReadCloser
+ if r, err := http.Get(url); err != nil {
+ return &model.CommandResponse{Text: "Unable to get file", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ } else if r.StatusCode > 400 {
+ return &model.CommandResponse{Text: "Unable to get file", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ } else {
+ contents = r.Body
+ }
+
+ bytes := make([]byte, 4000)
+
+ // break contents into 4000 byte posts
+ for {
+ length, err := contents.Read(bytes)
+ if err != nil && err != io.EOF {
+ return &model.CommandResponse{Text: "Encountered error reading file", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ }
+
+ if length == 0 {
+ break
+ }
+
+ post := &model.Post{}
+ post.Message = string(bytes[:length])
+ post.ChannelId = channelId
+
+ if _, err := CreatePost(c, post, false); err != nil {
+ return &model.CommandResponse{Text: "Unable to create post", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+ }
+ }
+
+ return &model.CommandResponse{Text: "Loading data...", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
+}
+
+func parseRange(command string, cmd string) (utils.Range, bool) {
+ tokens := strings.Fields(strings.TrimPrefix(command, cmd))
+ var begin int
+ var end int
+ var err1 error
+ var err2 error
+ switch {
+ case len(tokens) == 1:
+ begin, err1 = strconv.Atoi(tokens[0])
+ end = begin
+ if err1 != nil {
+ return utils.Range{0, 0}, false
+ }
+ case len(tokens) >= 2:
+ begin, err1 = strconv.Atoi(tokens[0])
+ end, err2 = strconv.Atoi(tokens[1])
+ if err1 != nil || err2 != nil {
+ return utils.Range{0, 0}, false
+ }
+ default:
+ return utils.Range{0, 0}, false
+ }
+ return utils.Range{begin, end}, true
+}
+
+func contains(items []string, token string) bool {
+ for _, elem := range items {
+ if elem == token {
+ return true
+ }
+ }
+ return false
+}
diff --git a/api/command_loadtest_test.go b/api/command_loadtest_test.go
new file mode 100644
index 000000000..7cb77cf18
--- /dev/null
+++ b/api/command_loadtest_test.go
@@ -0,0 +1,221 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package api
+
+import (
+ "strings"
+ "testing"
+ "time"
+
+ "github.com/mattermost/platform/model"
+ "github.com/mattermost/platform/store"
+ "github.com/mattermost/platform/utils"
+)
+
+func TestLoadTestHelpCommands(t *testing.T) {
+ Setup()
+ // enable testing to use /loadtest but don't save it since we don't want to overwrite config.json
+ enableTesting := utils.Cfg.ServiceSettings.EnableTesting
+ defer func() {
+ utils.Cfg.ServiceSettings.EnableTesting = enableTesting
+ }()
+
+ utils.Cfg.ServiceSettings.EnableTesting = true
+
+ team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
+
+ user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
+ user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user1.Id))
+
+ Client.LoginByEmail(team.Name, user1.Email, "pwd")
+
+ channel := &model.Channel{DisplayName: "00", Name: "00" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
+ channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel)
+
+ rs := Client.Must(Client.Command(channel.Id, "/loadtest help", false)).Data.(*model.CommandResponse)
+ if !strings.Contains(rs.Text, "Mattermost load testing commands to help") {
+ t.Fatal(rs.Text)
+ }
+
+ time.Sleep(2 * time.Second)
+}
+
+func TestLoadTestSetupCommands(t *testing.T) {
+ Setup()
+ // enable testing to use /loadtest but don't save it since we don't want to overwrite config.json
+ enableTesting := utils.Cfg.ServiceSettings.EnableTesting
+ defer func() {
+ utils.Cfg.ServiceSettings.EnableTesting = enableTesting
+ }()
+
+ utils.Cfg.ServiceSettings.EnableTesting = true
+
+ team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
+
+ user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
+ user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user1.Id))
+
+ Client.LoginByEmail(team.Name, user1.Email, "pwd")
+
+ channel := &model.Channel{DisplayName: "00", Name: "00" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
+ channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel)
+
+ rs := Client.Must(Client.Command(channel.Id, "/loadtest setup fuzz 1 1 1", false)).Data.(*model.CommandResponse)
+ if rs.Text != "Creating enviroment..." {
+ t.Fatal(rs.Text)
+ }
+
+ time.Sleep(2 * time.Second)
+}
+
+func TestLoadTestUsersCommands(t *testing.T) {
+ Setup()
+ // enable testing to use /loadtest but don't save it since we don't want to overwrite config.json
+ enableTesting := utils.Cfg.ServiceSettings.EnableTesting
+ defer func() {
+ utils.Cfg.ServiceSettings.EnableTesting = enableTesting
+ }()
+
+ utils.Cfg.ServiceSettings.EnableTesting = true
+
+ team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
+
+ user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
+ user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user1.Id))
+
+ Client.LoginByEmail(team.Name, user1.Email, "pwd")
+
+ channel := &model.Channel{DisplayName: "00", Name: "00" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
+ channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel)
+
+ rs := Client.Must(Client.Command(channel.Id, "/loadtest users fuzz 1 2", false)).Data.(*model.CommandResponse)
+ if rs.Text != "Adding users..." {
+ t.Fatal(rs.Text)
+ }
+
+ time.Sleep(2 * time.Second)
+}
+
+func TestLoadTestChannelsCommands(t *testing.T) {
+ Setup()
+ // enable testing to use /loadtest but don't save it since we don't want to overwrite config.json
+ enableTesting := utils.Cfg.ServiceSettings.EnableTesting
+ defer func() {
+ utils.Cfg.ServiceSettings.EnableTesting = enableTesting
+ }()
+
+ utils.Cfg.ServiceSettings.EnableTesting = true
+
+ team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
+
+ user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
+ user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user1.Id))
+
+ Client.LoginByEmail(team.Name, user1.Email, "pwd")
+
+ channel := &model.Channel{DisplayName: "00", Name: "00" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
+ channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel)
+
+ rs := Client.Must(Client.Command(channel.Id, "/loadtest channels fuzz 1 2", false)).Data.(*model.CommandResponse)
+ if rs.Text != "Adding channels..." {
+ t.Fatal(rs.Text)
+ }
+
+ time.Sleep(2 * time.Second)
+}
+
+func TestLoadTestPostsCommands(t *testing.T) {
+ Setup()
+ // enable testing to use /loadtest but don't save it since we don't want to overwrite config.json
+ enableTesting := utils.Cfg.ServiceSettings.EnableTesting
+ defer func() {
+ utils.Cfg.ServiceSettings.EnableTesting = enableTesting
+ }()
+
+ utils.Cfg.ServiceSettings.EnableTesting = true
+
+ team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
+
+ user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
+ user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user1.Id))
+
+ Client.LoginByEmail(team.Name, user1.Email, "pwd")
+
+ channel := &model.Channel{DisplayName: "00", Name: "00" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
+ channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel)
+
+ rs := Client.Must(Client.Command(channel.Id, "/loadtest posts fuzz 2 3 2", false)).Data.(*model.CommandResponse)
+ if rs.Text != "Adding posts..." {
+ t.Fatal(rs.Text)
+ }
+
+ time.Sleep(2 * time.Second)
+}
+
+func TestLoadTestUrlCommands(t *testing.T) {
+ Setup()
+ // enable testing to use /loadtest but don't save it since we don't want to overwrite config.json
+ enableTesting := utils.Cfg.ServiceSettings.EnableTesting
+ defer func() {
+ utils.Cfg.ServiceSettings.EnableTesting = enableTesting
+ }()
+
+ utils.Cfg.ServiceSettings.EnableTesting = true
+
+ team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
+
+ user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
+ user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user1.Id))
+
+ Client.LoginByEmail(team.Name, user1.Email, "pwd")
+
+ channel := &model.Channel{DisplayName: "00", Name: "00" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
+ channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel)
+
+ command := "/loadtest url "
+ if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.CommandResponse); r.Text != "Command must contain a url" {
+ t.Fatal("/loadtest url with no url should've failed")
+ }
+
+ command = "/loadtest url http://missingfiletonwhere/path/asdf/qwerty"
+ if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.CommandResponse); r.Text != "Unable to get file" {
+ t.Log(r.Text)
+ t.Fatal("/loadtest url with invalid url should've failed")
+ }
+
+ command = "/loadtest url https://raw.githubusercontent.com/mattermost/platform/master/README.md"
+ if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.CommandResponse); r.Text != "Loading data..." {
+ t.Fatal("/loadtest url for README.md should've executed")
+ }
+
+ command = "/loadtest url test-emoticons.md"
+ if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.CommandResponse); r.Text != "Loading data..." {
+ t.Fatal("/loadtest url for test-emoticons.md should've executed")
+ }
+
+ command = "/loadtest url test-emoticons"
+ if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.CommandResponse); r.Text != "Loading data..." {
+ t.Fatal("/loadtest url for test-emoticons should've executed")
+ }
+
+ posts := Client.Must(Client.GetPosts(channel.Id, 0, 5, "")).Data.(*model.PostList)
+ // note that this may make more than 3 posts if files are too long to fit in an individual post
+ if len(posts.Order) < 3 {
+ t.Fatal("/loadtest url made too few posts, perhaps there needs to be a delay before GetPosts in the test?")
+ }
+
+ time.Sleep(2 * time.Second)
+}
diff --git a/api/command_logout.go b/api/command_logout.go
new file mode 100644
index 000000000..fb69b4f85
--- /dev/null
+++ b/api/command_logout.go
@@ -0,0 +1,37 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package api
+
+import (
+ "github.com/mattermost/platform/model"
+)
+
+type LogoutProvider struct {
+}
+
+const (
+ CMD_LOGOUT = "logout"
+)
+
+func init() {
+ RegisterCommandProvider(&LogoutProvider{})
+}
+
+func (me *LogoutProvider) GetTrigger() string {
+ return CMD_LOGOUT
+}
+
+func (me *LogoutProvider) GetCommand(c *Context) *model.Command {
+ return &model.Command{
+ Trigger: CMD_LOGOUT,
+ AutoComplete: true,
+ AutoCompleteDesc: c.T("api.command_logout.desc"),
+ AutoCompleteHint: "",
+ DisplayName: c.T("api.command_logout.name"),
+ }
+}
+
+func (me *LogoutProvider) DoCommand(c *Context, channelId string, message string) *model.CommandResponse {
+ return &model.CommandResponse{GotoLocation: "/logout", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL, Text: c.T("api.command_logout.success_message")}
+}
diff --git a/api/command_logout_test.go b/api/command_logout_test.go
new file mode 100644
index 000000000..86979316b
--- /dev/null
+++ b/api/command_logout_test.go
@@ -0,0 +1,32 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package api
+
+import (
+ "testing"
+
+ "github.com/mattermost/platform/model"
+ "github.com/mattermost/platform/store"
+)
+
+func TestLogoutTestCommand(t *testing.T) {
+ Setup()
+
+ team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
+
+ user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
+ user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user1.Id))
+
+ Client.LoginByEmail(team.Name, user1.Email, "pwd")
+
+ channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
+ channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
+
+ rs1 := Client.Must(Client.Command(channel1.Id, "/logout", false)).Data.(*model.CommandResponse)
+ if rs1.GotoLocation != "/logout" {
+ t.Fatal("failed to logout")
+ }
+}
diff --git a/api/command_me.go b/api/command_me.go
new file mode 100644
index 000000000..c6147278b
--- /dev/null
+++ b/api/command_me.go
@@ -0,0 +1,37 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package api
+
+import (
+ "github.com/mattermost/platform/model"
+)
+
+type MeProvider struct {
+}
+
+const (
+ CMD_ME = "me"
+)
+
+func init() {
+ RegisterCommandProvider(&MeProvider{})
+}
+
+func (me *MeProvider) GetTrigger() string {
+ return CMD_ME
+}
+
+func (me *MeProvider) GetCommand(c *Context) *model.Command {
+ return &model.Command{
+ Trigger: CMD_ME,
+ AutoComplete: true,
+ AutoCompleteDesc: c.T("api.command_me.desc"),
+ AutoCompleteHint: c.T("api.command_me.hint"),
+ DisplayName: c.T("api.command_me.name"),
+ }
+}
+
+func (me *MeProvider) DoCommand(c *Context, channelId string, message string) *model.CommandResponse {
+ return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_IN_CHANNEL, Text: "*" + message + "*"}
+}
diff --git a/api/command_me_test.go b/api/command_me_test.go
new file mode 100644
index 000000000..d55a15b2c
--- /dev/null
+++ b/api/command_me_test.go
@@ -0,0 +1,47 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package api
+
+import (
+ "testing"
+ "time"
+
+ "github.com/mattermost/platform/model"
+ "github.com/mattermost/platform/store"
+)
+
+func TestMeCommand(t *testing.T) {
+ Setup()
+
+ team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
+
+ user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
+ user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user1.Id))
+
+ Client.LoginByEmail(team.Name, user1.Email, "pwd")
+
+ channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
+ channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
+
+ testString := "/me hello"
+
+ r1 := Client.Must(Client.Command(channel1.Id, testString, false)).Data.(*model.CommandResponse)
+ if r1 == nil {
+ t.Fatal("Command failed to execute")
+ }
+
+ time.Sleep(100 * time.Millisecond)
+
+ p1 := Client.Must(Client.GetPosts(channel1.Id, 0, 2, "")).Data.(*model.PostList)
+ if len(p1.Order) != 1 {
+ t.Fatal("Command failed to send")
+ } else {
+ if p1.Posts[p1.Order[0]].Message != `*hello*` {
+ t.Log(p1.Posts[p1.Order[0]].Message)
+ t.Fatal("invalid shrug reponse")
+ }
+ }
+}
diff --git a/api/command_shrug.go b/api/command_shrug.go
new file mode 100644
index 000000000..8fb5bc200
--- /dev/null
+++ b/api/command_shrug.go
@@ -0,0 +1,42 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package api
+
+import (
+ "github.com/mattermost/platform/model"
+)
+
+type ShrugProvider struct {
+}
+
+const (
+ CMD_SHRUG = "shrug"
+)
+
+func init() {
+ RegisterCommandProvider(&ShrugProvider{})
+}
+
+func (me *ShrugProvider) GetTrigger() string {
+ return CMD_SHRUG
+}
+
+func (me *ShrugProvider) GetCommand(c *Context) *model.Command {
+ return &model.Command{
+ Trigger: CMD_SHRUG,
+ AutoComplete: true,
+ AutoCompleteDesc: c.T("api.command_shrug.desc"),
+ AutoCompleteHint: c.T("api.command_shrug.hint"),
+ DisplayName: c.T("api.command_shrug.name"),
+ }
+}
+
+func (me *ShrugProvider) DoCommand(c *Context, channelId string, message string) *model.CommandResponse {
+ rmsg := `¯\\\_(ツ)\_/¯`
+ if len(message) > 0 {
+ rmsg = message + " " + rmsg
+ }
+
+ return &model.CommandResponse{ResponseType: model.COMMAND_RESPONSE_TYPE_IN_CHANNEL, Text: rmsg}
+}
diff --git a/api/command_shrug_test.go b/api/command_shrug_test.go
new file mode 100644
index 000000000..92cecf664
--- /dev/null
+++ b/api/command_shrug_test.go
@@ -0,0 +1,47 @@
+// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
+// See License.txt for license information.
+
+package api
+
+import (
+ "testing"
+ "time"
+
+ "github.com/mattermost/platform/model"
+ "github.com/mattermost/platform/store"
+)
+
+func TestShrugCommand(t *testing.T) {
+ Setup()
+
+ team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
+
+ user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
+ user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user1.Id))
+
+ Client.LoginByEmail(team.Name, user1.Email, "pwd")
+
+ channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
+ channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
+
+ testString := "/shrug"
+
+ r1 := Client.Must(Client.Command(channel1.Id, testString, false)).Data.(*model.CommandResponse)
+ if r1 == nil {
+ t.Fatal("Command failed to execute")
+ }
+
+ time.Sleep(100 * time.Millisecond)
+
+ p1 := Client.Must(Client.GetPosts(channel1.Id, 0, 2, "")).Data.(*model.PostList)
+ if len(p1.Order) != 1 {
+ t.Fatal("Command failed to send")
+ } else {
+ if p1.Posts[p1.Order[0]].Message != `¯\\\_(ツ)\_/¯` {
+ t.Log(p1.Posts[p1.Order[0]].Message)
+ t.Fatal("invalid shrug reponse")
+ }
+ }
+}
diff --git a/api/command_test.go b/api/command_test.go
index 86eb297d5..22e2bd666 100644
--- a/api/command_test.go
+++ b/api/command_test.go
@@ -4,7 +4,6 @@
package api
import (
- "strings"
"testing"
"time"
@@ -13,7 +12,7 @@ import (
"github.com/mattermost/platform/utils"
)
-func TestSuggestRootCommands(t *testing.T) {
+func TestListCommands(t *testing.T) {
Setup()
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
@@ -25,177 +24,197 @@ func TestSuggestRootCommands(t *testing.T) {
Client.LoginByEmail(team.Name, user1.Email, "pwd")
- if _, err := Client.Command("", "", true); err == nil {
- t.Fatal("Should fail")
- }
+ if results, err := Client.ListCommands(); err != nil {
+ t.Fatal(err)
+ } else {
+ commands := results.Data.([]*model.Command)
+ foundEcho := false
- rs1 := Client.Must(Client.Command("", "/", true)).Data.(*model.Command)
+ for _, command := range commands {
+ if command.Trigger == "echo" {
+ foundEcho = true
+ }
+ }
- hasLogout := false
- for _, v := range rs1.Suggestions {
- if v.Suggestion == "/logout" {
- hasLogout = true
+ if !foundEcho {
+ t.Fatal("Couldn't find echo command")
}
}
+}
- if !hasLogout {
- t.Log(rs1.Suggestions)
- t.Fatal("should have logout cmd")
- }
+func TestCreateCommand(t *testing.T) {
+ Setup()
- rs2 := Client.Must(Client.Command("", "/log", true)).Data.(*model.Command)
+ enableCommands := *utils.Cfg.ServiceSettings.EnableCommands
+ defer func() {
+ utils.Cfg.ServiceSettings.EnableCommands = &enableCommands
+ }()
+ *utils.Cfg.ServiceSettings.EnableCommands = true
- if rs2.Suggestions[0].Suggestion != "/logout" {
- t.Fatal("should have logout cmd")
- }
+ team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
- rs3 := Client.Must(Client.Command("", "/joi", true)).Data.(*model.Command)
+ user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
+ user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user.Id))
- if rs3.Suggestions[0].Suggestion != "/join" {
- t.Fatal("should have join cmd")
- }
+ Client.LoginByEmail(team.Name, user.Email, "pwd")
- rs4 := Client.Must(Client.Command("", "/ech", true)).Data.(*model.Command)
+ cmd := &model.Command{URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST}
- if rs4.Suggestions[0].Suggestion != "/echo" {
- t.Fatal("should have echo cmd")
+ if _, err := Client.CreateCommand(cmd); err == nil {
+ t.Fatal("should have failed because not admin")
}
-}
-func TestLogoutCommands(t *testing.T) {
- Setup()
+ c := &Context{}
+ c.RequestId = model.NewId()
+ c.IpAddress = "cmd_line"
+ UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
+ Client.LoginByEmail(team.Name, user.Email, "pwd")
- team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
- team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
+ var rcmd *model.Command
+ if result, err := Client.CreateCommand(cmd); err != nil {
+ t.Fatal(err)
+ } else {
+ rcmd = result.Data.(*model.Command)
+ }
- user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"}
- user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
- store.Must(Srv.Store.User().VerifyEmail(user1.Id))
+ if rcmd.CreatorId != user.Id {
+ t.Fatal("user ids didn't match")
+ }
- Client.LoginByEmail(team.Name, user1.Email, "pwd")
+ if rcmd.TeamId != team.Id {
+ t.Fatal("team ids didn't match")
+ }
- rs1 := Client.Must(Client.Command("", "/logout", false)).Data.(*model.Command)
- if rs1.GotoLocation != "/logout" {
- t.Fatal("failed to logout")
+ cmd = &model.Command{CreatorId: "123", TeamId: "456", URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST}
+ if result, err := Client.CreateCommand(cmd); err != nil {
+ t.Fatal(err)
+ } else {
+ if result.Data.(*model.Command).CreatorId != user.Id {
+ t.Fatal("bad user id wasn't overwritten")
+ }
+ if result.Data.(*model.Command).TeamId != team.Id {
+ t.Fatal("bad team id wasn't overwritten")
+ }
}
}
-func TestJoinCommands(t *testing.T) {
+func TestListTeamCommands(t *testing.T) {
Setup()
+ enableCommands := *utils.Cfg.ServiceSettings.EnableCommands
+ defer func() {
+ utils.Cfg.ServiceSettings.EnableCommands = &enableCommands
+ }()
+ *utils.Cfg.ServiceSettings.EnableCommands = true
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
- user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"}
- user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
- store.Must(Srv.Store.User().VerifyEmail(user1.Id))
-
- Client.LoginByEmail(team.Name, user1.Email, "pwd")
-
- channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
- channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
- Client.Must(Client.LeaveChannel(channel1.Id))
-
- channel2 := &model.Channel{DisplayName: "BB", Name: "bb" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
- channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
- Client.Must(Client.LeaveChannel(channel2.Id))
+ user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"}
+ user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user.Id))
- user2 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"}
- user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
- store.Must(Srv.Store.User().VerifyEmail(user1.Id))
+ c := &Context{}
+ c.RequestId = model.NewId()
+ c.IpAddress = "cmd_line"
+ UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
+ Client.LoginByEmail(team.Name, user.Email, "pwd")
- data := make(map[string]string)
- data["user_id"] = user2.Id
- channel3 := Client.Must(Client.CreateDirectChannel(data)).Data.(*model.Channel)
+ cmd1 := &model.Command{URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST}
+ cmd1 = Client.Must(Client.CreateCommand(cmd1)).Data.(*model.Command)
- rs1 := Client.Must(Client.Command("", "/join aa", true)).Data.(*model.Command)
- if rs1.Suggestions[0].Suggestion != "/join "+channel1.Name {
- t.Fatal("should have join cmd")
- }
+ if result, err := Client.ListTeamCommands(); err != nil {
+ t.Fatal(err)
+ } else {
+ cmds := result.Data.([]*model.Command)
- rs2 := Client.Must(Client.Command("", "/join bb", true)).Data.(*model.Command)
- if rs2.Suggestions[0].Suggestion != "/join "+channel2.Name {
- t.Fatal("should have join cmd")
+ if len(cmds) != 1 {
+ t.Fatal("incorrect number of cmd")
+ }
}
+}
- rs3 := Client.Must(Client.Command("", "/join", true)).Data.(*model.Command)
- if len(rs3.Suggestions) != 2 {
- t.Fatal("should have 2 join cmd")
- }
+func TestRegenToken(t *testing.T) {
+ Setup()
+ enableCommands := *utils.Cfg.ServiceSettings.EnableCommands
+ defer func() {
+ utils.Cfg.ServiceSettings.EnableCommands = &enableCommands
+ }()
+ *utils.Cfg.ServiceSettings.EnableCommands = true
- rs4 := Client.Must(Client.Command("", "/join ", true)).Data.(*model.Command)
- if len(rs4.Suggestions) != 2 {
- t.Fatal("should have 2 join cmd")
- }
+ team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
+ team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
- rs5 := Client.Must(Client.Command("", "/join "+channel2.Name, false)).Data.(*model.Command)
- if !strings.HasSuffix(rs5.GotoLocation, "/"+team.Name+"/channels/"+channel2.Name) {
- t.Fatal("failed to join channel")
- }
+ user := &model.User{TeamId: team.Id, Email: model.NewId() + "corey+test@test.com", Nickname: "Corey Hulen", Password: "pwd"}
+ user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user.Id))
- rs6 := Client.Must(Client.Command("", "/join "+channel3.Name, false)).Data.(*model.Command)
- if strings.HasSuffix(rs6.GotoLocation, "/"+team.Name+"/channels/"+channel3.Name) {
- t.Fatal("should not have joined direct message channel")
- }
+ c := &Context{}
+ c.RequestId = model.NewId()
+ c.IpAddress = "cmd_line"
+ UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
+ Client.LoginByEmail(team.Name, user.Email, "pwd")
- c1 := Client.Must(Client.GetChannels("")).Data.(*model.ChannelList)
+ cmd := &model.Command{URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST}
+ cmd = Client.Must(Client.CreateCommand(cmd)).Data.(*model.Command)
- if len(c1.Channels) != 4 { // 4 because of town-square, off-topic and direct
- t.Fatal("didn't join channel")
- }
+ data := make(map[string]string)
+ data["id"] = cmd.Id
- found := false
- for _, c := range c1.Channels {
- if c.Name == channel2.Name {
- found = true
- break
+ if result, err := Client.RegenCommandToken(data); err != nil {
+ t.Fatal(err)
+ } else {
+ if result.Data.(*model.Command).Token == cmd.Token {
+ t.Fatal("regen didn't work properly")
}
}
- if !found {
- t.Fatal("didn't join channel")
- }
}
-func TestEchoCommand(t *testing.T) {
+func TestDeleteCommand(t *testing.T) {
Setup()
+ enableCommands := *utils.Cfg.ServiceSettings.EnableCommands
+ defer func() {
+ utils.Cfg.ServiceSettings.EnableCommands = &enableCommands
+ }()
+ *utils.Cfg.ServiceSettings.EnableCommands = true
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
- user1 := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"}
- user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
- store.Must(Srv.Store.User().VerifyEmail(user1.Id))
+ user := &model.User{TeamId: team.Id, Email: model.NewId() + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd"}
+ user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user.Id))
- Client.LoginByEmail(team.Name, user1.Email, "pwd")
+ c := &Context{}
+ c.RequestId = model.NewId()
+ c.IpAddress = "cmd_line"
+ UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
+ Client.LoginByEmail(team.Name, user.Email, "pwd")
- channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
- channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
+ cmd := &model.Command{URL: "http://nowhere.com", Method: model.COMMAND_METHOD_POST}
+ cmd = Client.Must(Client.CreateCommand(cmd)).Data.(*model.Command)
- echoTestString := "/echo test"
+ data := make(map[string]string)
+ data["id"] = cmd.Id
- r1 := Client.Must(Client.Command(channel1.Id, echoTestString, false)).Data.(*model.Command)
- if r1.Response != model.RESP_EXECUTED {
- t.Fatal("Echo command failed to execute")
+ if _, err := Client.DeleteCommand(data); err != nil {
+ t.Fatal(err)
}
- time.Sleep(100 * time.Millisecond)
-
- p1 := Client.Must(Client.GetPosts(channel1.Id, 0, 2, "")).Data.(*model.PostList)
- if len(p1.Order) != 1 {
- t.Fatal("Echo command failed to send")
+ cmds := Client.Must(Client.ListTeamCommands()).Data.([]*model.Command)
+ if len(cmds) != 0 {
+ t.Fatal("delete didn't work properly")
}
}
-func TestLoadTestUrlCommand(t *testing.T) {
+func TestTestCommand(t *testing.T) {
Setup()
-
- // enable testing to use /loadtest but don't save it since we don't want to overwrite config.json
- enableTesting := utils.Cfg.ServiceSettings.EnableTesting
+ enableCommands := *utils.Cfg.ServiceSettings.EnableCommands
defer func() {
- utils.Cfg.ServiceSettings.EnableTesting = enableTesting
+ utils.Cfg.ServiceSettings.EnableCommands = &enableCommands
}()
-
- utils.Cfg.ServiceSettings.EnableTesting = true
+ *utils.Cfg.ServiceSettings.EnableCommands = true
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
@@ -204,39 +223,52 @@ func TestLoadTestUrlCommand(t *testing.T) {
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id))
+ c := &Context{}
+ c.RequestId = model.NewId()
+ c.IpAddress = "cmd_line"
+ UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
Client.LoginByEmail(team.Name, user.Email, "pwd")
- channel := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
- channel = Client.Must(Client.CreateChannel(channel)).Data.(*model.Channel)
+ channel1 := &model.Channel{DisplayName: "AA", Name: "aa" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
+ channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
- command := "/loadtest url "
- if _, err := Client.Command(channel.Id, command, false); err == nil {
- t.Fatal("/loadtest url with no url should've failed")
+ cmd1 := &model.Command{
+ URL: "http://localhost" + utils.Cfg.ServiceSettings.ListenAddress + "/api/v1/commands/test",
+ Method: model.COMMAND_METHOD_POST,
+ Trigger: "test",
}
- // command = "/loadtest url http://www.hopefullynonexistent.file/path/asdf/qwerty"
- // if _, err := Client.Command(channel.Id, command, false); err == nil {
- // t.Fatal("/loadtest url with invalid url should've failed")
- // }
+ cmd1 = Client.Must(Client.CreateCommand(cmd1)).Data.(*model.Command)
- command = "/loadtest url https://raw.githubusercontent.com/mattermost/platform/master/README.md"
- if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.Command); r.Response != model.RESP_EXECUTED {
- t.Fatal("/loadtest url for README.md should've executed")
+ r1 := Client.Must(Client.Command(channel1.Id, "/test", false)).Data.(*model.CommandResponse)
+ if r1 == nil {
+ t.Fatal("Test command failed to execute")
}
- command = "/loadtest url test-emoticons.md"
- if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.Command); r.Response != model.RESP_EXECUTED {
- t.Fatal("/loadtest url for test-emoticons.md should've executed")
+ time.Sleep(100 * time.Millisecond)
+
+ p1 := Client.Must(Client.GetPosts(channel1.Id, 0, 2, "")).Data.(*model.PostList)
+ if len(p1.Order) != 1 {
+ t.Fatal("Test command failed to send")
}
- command = "/loadtest url test-emoticons"
- if r := Client.Must(Client.Command(channel.Id, command, false)).Data.(*model.Command); r.Response != model.RESP_EXECUTED {
- t.Fatal("/loadtest url for test-emoticons should've executed")
+ cmd2 := &model.Command{
+ URL: "http://localhost" + utils.Cfg.ServiceSettings.ListenAddress + "/api/v1/commands/test",
+ Method: model.COMMAND_METHOD_GET,
+ Trigger: "test2",
}
- posts := Client.Must(Client.GetPosts(channel.Id, 0, 5, "")).Data.(*model.PostList)
- // note that this may make more than 3 posts if files are too long to fit in an individual post
- if len(posts.Order) < 3 {
- t.Fatal("/loadtest url made too few posts, perhaps there needs to be a delay before GetPosts in the test?")
+ cmd2 = Client.Must(Client.CreateCommand(cmd2)).Data.(*model.Command)
+
+ r2 := Client.Must(Client.Command(channel1.Id, "/test2", false)).Data.(*model.CommandResponse)
+ if r2 == nil {
+ t.Fatal("Test2 command failed to execute")
+ }
+
+ time.Sleep(100 * time.Millisecond)
+
+ p2 := Client.Must(Client.GetPosts(channel1.Id, 0, 2, "")).Data.(*model.PostList)
+ if len(p2.Order) != 2 {
+ t.Fatal("Test command failed to send")
}
}
diff --git a/api/user.go b/api/user.go
index 8b2df7143..9926f3ff3 100644
--- a/api/user.go
+++ b/api/user.go
@@ -1542,6 +1542,10 @@ func PermanentDeleteUser(c *Context, user *model.User) *model.AppError {
return result.Err
}
+ if result := <-Srv.Store.Command().PermanentDeleteByUser(user.Id); result.Err != nil {
+ return result.Err
+ }
+
if result := <-Srv.Store.Preference().PermanentDeleteByUser(user.Id); result.Err != nil {
return result.Err
}
diff --git a/api/webhook.go b/api/webhook.go
index 1372fe335..3906d09be 100644
--- a/api/webhook.go
+++ b/api/webhook.go
@@ -32,6 +32,14 @@ func createIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
+ if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
+ c.Err = model.NewLocAppError("createIncomingHook", "api.command.admin_only.app_error", nil, "")
+ c.Err.StatusCode = http.StatusForbidden
+ return
+ }
+ }
+
c.LogAudit("attempt")
hook := model.IncomingWebhookFromJson(r.Body)
@@ -79,6 +87,14 @@ func deleteIncomingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
+ if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
+ c.Err = model.NewLocAppError("deleteIncomingHook", "api.command.admin_only.app_error", nil, "")
+ c.Err.StatusCode = http.StatusForbidden
+ return
+ }
+ }
+
c.LogAudit("attempt")
props := model.MapFromJson(r.Body)
@@ -116,7 +132,15 @@ func getIncomingHooks(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- if result := <-Srv.Store.Webhook().GetIncomingByUser(c.Session.UserId); result.Err != nil {
+ if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
+ if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
+ c.Err = model.NewLocAppError("getIncomingHooks", "api.command.admin_only.app_error", nil, "")
+ c.Err.StatusCode = http.StatusForbidden
+ return
+ }
+ }
+
+ if result := <-Srv.Store.Webhook().GetIncomingByTeam(c.Session.TeamId); result.Err != nil {
c.Err = result.Err
return
} else {
@@ -132,6 +156,14 @@ func createOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
+ if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
+ c.Err = model.NewLocAppError("createOutgoingHook", "api.command.admin_only.app_error", nil, "")
+ c.Err.StatusCode = http.StatusForbidden
+ return
+ }
+ }
+
c.LogAudit("attempt")
hook := model.OutgoingWebhookFromJson(r.Body)
@@ -188,7 +220,15 @@ func getOutgoingHooks(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- if result := <-Srv.Store.Webhook().GetOutgoingByCreator(c.Session.UserId); result.Err != nil {
+ if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
+ if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
+ c.Err = model.NewLocAppError("getOutgoingHooks", "api.command.admin_only.app_error", nil, "")
+ c.Err.StatusCode = http.StatusForbidden
+ return
+ }
+ }
+
+ if result := <-Srv.Store.Webhook().GetOutgoingByTeam(c.Session.TeamId); result.Err != nil {
c.Err = result.Err
return
} else {
@@ -204,6 +244,14 @@ func deleteOutgoingHook(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
+ if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
+ if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
+ c.Err = model.NewLocAppError("deleteOutgoingHook", "api.command.admin_only.app_error", nil, "")
+ c.Err.StatusCode = http.StatusForbidden
+ return
+ }
+ }
+
c.LogAudit("attempt")
props := model.MapFromJson(r.Body)
@@ -241,6 +289,14 @@ func regenOutgoingHookToken(c *Context, w http.ResponseWriter, r *http.Request)
return
}
+ if *utils.Cfg.ServiceSettings.EnableOnlyAdminIntegrations {
+ if !(c.IsSystemAdmin() || c.IsTeamAdmin()) {
+ c.Err = model.NewLocAppError("regenOutgoingHookToken", "api.command.admin_only.app_error", nil, "")
+ c.Err.StatusCode = http.StatusForbidden
+ return
+ }
+ }
+
c.LogAudit("attempt")
props := model.MapFromJson(r.Body)
@@ -258,7 +314,7 @@ func regenOutgoingHookToken(c *Context, w http.ResponseWriter, r *http.Request)
} else {
hook = result.Data.(*model.OutgoingWebhook)
- if c.Session.UserId != hook.CreatorId && !c.IsTeamAdmin() {
+ if c.Session.TeamId != hook.TeamId && c.Session.UserId != hook.CreatorId && !c.IsTeamAdmin() {
c.LogAudit("fail - inappropriate permissions")
c.Err = model.NewLocAppError("regenOutgoingHookToken", "api.webhook.regen_outgoing_token.permissions.app_error", nil, "user_id="+c.Session.UserId)
return
diff --git a/api/webhook_test.go b/api/webhook_test.go
index 0a464656b..4f85d178d 100644
--- a/api/webhook_test.go
+++ b/api/webhook_test.go
@@ -13,6 +13,14 @@ import (
func TestCreateIncomingHook(t *testing.T) {
Setup()
+ enableIncomingHooks := utils.Cfg.ServiceSettings.EnableIncomingWebhooks
+ enableOutgoingHooks := utils.Cfg.ServiceSettings.EnableOutgoingWebhooks
+ defer func() {
+ utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks
+ utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks
+ }()
+ utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true
+ utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
@@ -21,6 +29,10 @@ func TestCreateIncomingHook(t *testing.T) {
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id))
+ c := &Context{}
+ c.RequestId = model.NewId()
+ c.IpAddress = "cmd_line"
+ UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
Client.LoginByEmail(team.Name, user.Email, "pwd")
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
@@ -76,6 +88,14 @@ func TestCreateIncomingHook(t *testing.T) {
func TestListIncomingHooks(t *testing.T) {
Setup()
+ enableIncomingHooks := utils.Cfg.ServiceSettings.EnableIncomingWebhooks
+ enableOutgoingHooks := utils.Cfg.ServiceSettings.EnableOutgoingWebhooks
+ defer func() {
+ utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks
+ utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks
+ }()
+ utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true
+ utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
@@ -84,6 +104,10 @@ func TestListIncomingHooks(t *testing.T) {
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id))
+ c := &Context{}
+ c.RequestId = model.NewId()
+ c.IpAddress = "cmd_line"
+ UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
Client.LoginByEmail(team.Name, user.Email, "pwd")
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
@@ -114,6 +138,14 @@ func TestListIncomingHooks(t *testing.T) {
func TestDeleteIncomingHook(t *testing.T) {
Setup()
+ enableIncomingHooks := utils.Cfg.ServiceSettings.EnableIncomingWebhooks
+ enableOutgoingHooks := utils.Cfg.ServiceSettings.EnableOutgoingWebhooks
+ defer func() {
+ utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks
+ utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks
+ }()
+ utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true
+ utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
@@ -122,6 +154,10 @@ func TestDeleteIncomingHook(t *testing.T) {
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id))
+ c := &Context{}
+ c.RequestId = model.NewId()
+ c.IpAddress = "cmd_line"
+ UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
Client.LoginByEmail(team.Name, user.Email, "pwd")
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
@@ -154,6 +190,14 @@ func TestDeleteIncomingHook(t *testing.T) {
func TestCreateOutgoingHook(t *testing.T) {
Setup()
+ enableIncomingHooks := utils.Cfg.ServiceSettings.EnableIncomingWebhooks
+ enableOutgoingHooks := utils.Cfg.ServiceSettings.EnableOutgoingWebhooks
+ defer func() {
+ utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks
+ utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks
+ }()
+ utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true
+ utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
@@ -162,6 +206,10 @@ func TestCreateOutgoingHook(t *testing.T) {
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id))
+ c := &Context{}
+ c.RequestId = model.NewId()
+ c.IpAddress = "cmd_line"
+ UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
Client.LoginByEmail(team.Name, user.Email, "pwd")
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
@@ -217,6 +265,14 @@ func TestCreateOutgoingHook(t *testing.T) {
func TestListOutgoingHooks(t *testing.T) {
Setup()
+ enableIncomingHooks := utils.Cfg.ServiceSettings.EnableIncomingWebhooks
+ enableOutgoingHooks := utils.Cfg.ServiceSettings.EnableOutgoingWebhooks
+ defer func() {
+ utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks
+ utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks
+ }()
+ utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true
+ utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
@@ -225,6 +281,10 @@ func TestListOutgoingHooks(t *testing.T) {
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id))
+ c := &Context{}
+ c.RequestId = model.NewId()
+ c.IpAddress = "cmd_line"
+ UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
Client.LoginByEmail(team.Name, user.Email, "pwd")
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
@@ -255,6 +315,14 @@ func TestListOutgoingHooks(t *testing.T) {
func TestDeleteOutgoingHook(t *testing.T) {
Setup()
+ enableIncomingHooks := utils.Cfg.ServiceSettings.EnableIncomingWebhooks
+ enableOutgoingHooks := utils.Cfg.ServiceSettings.EnableOutgoingWebhooks
+ defer func() {
+ utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks
+ utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks
+ }()
+ utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true
+ utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
@@ -263,6 +331,10 @@ func TestDeleteOutgoingHook(t *testing.T) {
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id))
+ c := &Context{}
+ c.RequestId = model.NewId()
+ c.IpAddress = "cmd_line"
+ UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
Client.LoginByEmail(team.Name, user.Email, "pwd")
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}
@@ -295,6 +367,14 @@ func TestDeleteOutgoingHook(t *testing.T) {
func TestRegenOutgoingHookToken(t *testing.T) {
Setup()
+ enableIncomingHooks := utils.Cfg.ServiceSettings.EnableIncomingWebhooks
+ enableOutgoingHooks := utils.Cfg.ServiceSettings.EnableOutgoingWebhooks
+ defer func() {
+ utils.Cfg.ServiceSettings.EnableIncomingWebhooks = enableIncomingHooks
+ utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = enableOutgoingHooks
+ }()
+ utils.Cfg.ServiceSettings.EnableIncomingWebhooks = true
+ utils.Cfg.ServiceSettings.EnableOutgoingWebhooks = true
team := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: "test@nowhere.com", Type: model.TEAM_OPEN}
team = Client.Must(Client.CreateTeam(team)).Data.(*model.Team)
@@ -303,6 +383,10 @@ func TestRegenOutgoingHookToken(t *testing.T) {
user = Client.Must(Client.CreateUser(user, "")).Data.(*model.User)
store.Must(Srv.Store.User().VerifyEmail(user.Id))
+ c := &Context{}
+ c.RequestId = model.NewId()
+ c.IpAddress = "cmd_line"
+ UpdateRoles(c, user, model.ROLE_SYSTEM_ADMIN)
Client.LoginByEmail(team.Name, user.Email, "pwd")
channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team.Id}