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/license.go32
-rw-r--r--api/post.go580
-rw-r--r--api/post_test.go95
-rw-r--r--api/team.go2
-rw-r--r--api/user.go11
-rw-r--r--api/web_team_hub.go3
-rw-r--r--api/webhook.go62
-rw-r--r--api/webhook_test.go84
22 files changed, 2153 insertions, 921 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/license.go b/api/license.go
index 4077c0e46..23e7946c8 100644
--- a/api/license.go
+++ b/api/license.go
@@ -81,9 +81,24 @@ func addLicense(c *Context, w http.ResponseWriter, r *http.Request) {
return
}
- if err := writeFileLocally(data, utils.LicenseLocation()); err != nil {
- c.LogAudit("failed - could not save license file")
- c.Err = model.NewLocAppError("addLicense", "api.license.add_license.save.app_error", nil, "path="+utils.LicenseLocation())
+ record := &model.LicenseRecord{}
+ record.Id = license.Id
+ record.Bytes = string(data)
+ rchan := Srv.Store.License().Save(record)
+
+ sysVar := &model.System{}
+ sysVar.Name = model.SYSTEM_ACTIVE_LICENSE_ID
+ sysVar.Value = license.Id
+ schan := Srv.Store.System().SaveOrUpdate(sysVar)
+
+ if result := <-rchan; result.Err != nil {
+ c.Err = model.NewLocAppError("addLicense", "api.license.add_license.save.app_error", nil, "err="+result.Err.Error())
+ utils.RemoveLicense()
+ return
+ }
+
+ if result := <-schan; result.Err != nil {
+ c.Err = model.NewLocAppError("addLicense", "api.license.add_license.save_active.app_error", nil, "")
utils.RemoveLicense()
return
}
@@ -100,9 +115,14 @@ func addLicense(c *Context, w http.ResponseWriter, r *http.Request) {
func removeLicense(c *Context, w http.ResponseWriter, r *http.Request) {
c.LogAudit("")
- if ok := utils.RemoveLicense(); !ok {
- c.LogAudit("failed - could not remove license file")
- c.Err = model.NewLocAppError("removeLicense", "api.license.remove_license.remove.app_error", nil, "")
+ utils.RemoveLicense()
+
+ sysVar := &model.System{}
+ sysVar.Name = model.SYSTEM_ACTIVE_LICENSE_ID
+ sysVar.Value = ""
+
+ if result := <-Srv.Store.System().Update(sysVar); result.Err != nil {
+ c.Err = model.NewLocAppError("removeLicense", "api.license.remove_license.update.app_error", nil, "")
return
}
diff --git a/api/post.go b/api/post.go
index e8345b5e5..c17da262f 100644
--- a/api/post.go
+++ b/api/post.go
@@ -15,6 +15,7 @@ import (
"net/url"
"path/filepath"
"regexp"
+ "sort"
"strconv"
"strings"
"time"
@@ -231,6 +232,8 @@ func handlePostEventsAndForget(c *Context, post *model.Post, triggerWebhooks boo
tchan := Srv.Store.Team().Get(c.Session.TeamId)
cchan := Srv.Store.Channel().Get(post.ChannelId)
uchan := Srv.Store.User().Get(post.UserId)
+ pchan := Srv.Store.User().GetProfiles(c.Session.TeamId)
+ mchan := Srv.Store.Channel().GetMembers(post.ChannelId)
var team *model.Team
if result := <-tchan; result.Err != nil {
@@ -248,7 +251,24 @@ func handlePostEventsAndForget(c *Context, post *model.Post, triggerWebhooks boo
channel = result.Data.(*model.Channel)
}
- sendNotificationsAndForget(c, post, team, channel)
+ var profiles map[string]*model.User
+ if result := <-pchan; result.Err != nil {
+ l4g.Error(utils.T("api.post.handle_post_events_and_forget.profiles.error"), c.Session.TeamId, result.Err)
+ return
+ } else {
+ profiles = result.Data.(map[string]*model.User)
+ }
+
+ var members []model.ChannelMember
+ if result := <-mchan; result.Err != nil {
+ l4g.Error(utils.T("api.post.handle_post_events_and_forget.members.error"), post.ChannelId, result.Err)
+ return
+ } else {
+ members = result.Data.([]model.ChannelMember)
+ }
+
+ go sendNotifications(c, post, team, channel, profiles, members)
+ go checkForOutOfChannelMentions(c, post, channel, profiles, members)
var user *model.User
if result := <-uchan; result.Err != nil {
@@ -413,311 +433,290 @@ func handleWebhookEventsAndForget(c *Context, post *model.Post, team *model.Team
}
-func sendNotificationsAndForget(c *Context, post *model.Post, team *model.Team, channel *model.Channel) {
+func sendNotifications(c *Context, post *model.Post, team *model.Team, channel *model.Channel, profileMap map[string]*model.User, members []model.ChannelMember) {
+ var channelName string
+ var bodyText string
+ var subjectText string
- go func() {
- // Get a list of user names (to be used as keywords) and ids for the given team
- uchan := Srv.Store.User().GetProfiles(c.Session.TeamId)
- echan := Srv.Store.Channel().GetMembers(post.ChannelId)
+ var mentionedUsers []string
- var channelName string
- var bodyText string
- var subjectText string
+ if _, ok := profileMap[post.UserId]; !ok {
+ l4g.Error(utils.T("api.post.send_notifications_and_forget.user_id.error"), post.UserId)
+ return
+ }
+ senderName := profileMap[post.UserId].Username
- var mentionedUsers []string
+ toEmailMap := make(map[string]bool)
- if result := <-uchan; result.Err != nil {
- l4g.Error(utils.T("api.post.send_notifications_and_forget.retrive_profiles.error"), c.Session.TeamId, result.Err)
- return
+ if channel.Type == model.CHANNEL_DIRECT {
+
+ var otherUserId string
+ if userIds := strings.Split(channel.Name, "__"); userIds[0] == post.UserId {
+ otherUserId = userIds[1]
+ channelName = profileMap[userIds[1]].Username
} else {
- profileMap := result.Data.(map[string]*model.User)
+ otherUserId = userIds[0]
+ channelName = profileMap[userIds[0]].Username
+ }
- if _, ok := profileMap[post.UserId]; !ok {
- l4g.Error(utils.T("api.post.send_notifications_and_forget.user_id.error"), post.UserId)
- return
- }
- senderName := profileMap[post.UserId].Username
+ otherUser := profileMap[otherUserId]
+ sendEmail := true
+ if _, ok := otherUser.NotifyProps["email"]; ok && otherUser.NotifyProps["email"] == "false" {
+ sendEmail = false
+ }
+ if sendEmail && (otherUser.IsOffline() || otherUser.IsAway()) {
+ toEmailMap[otherUserId] = true
+ }
- toEmailMap := make(map[string]bool)
+ } else {
+ // Find out who is a member of the channel, only keep those profiles
+ tempProfileMap := make(map[string]*model.User)
+ for _, member := range members {
+ tempProfileMap[member.UserId] = profileMap[member.UserId]
+ }
- if channel.Type == model.CHANNEL_DIRECT {
+ profileMap = tempProfileMap
- var otherUserId string
- if userIds := strings.Split(channel.Name, "__"); userIds[0] == post.UserId {
- otherUserId = userIds[1]
- channelName = profileMap[userIds[1]].Username
- } else {
- otherUserId = userIds[0]
- channelName = profileMap[userIds[0]].Username
- }
+ // Build map for keywords
+ keywordMap := make(map[string][]string)
+ for _, profile := range profileMap {
+ if len(profile.NotifyProps["mention_keys"]) > 0 {
- otherUser := profileMap[otherUserId]
- sendEmail := true
- if _, ok := otherUser.NotifyProps["email"]; ok && otherUser.NotifyProps["email"] == "false" {
- sendEmail = false
+ // Add all the user's mention keys
+ splitKeys := strings.Split(profile.NotifyProps["mention_keys"], ",")
+ for _, k := range splitKeys {
+ keywordMap[k] = append(keywordMap[strings.ToLower(k)], profile.Id)
}
- if sendEmail && (otherUser.IsOffline() || otherUser.IsAway()) {
- toEmailMap[otherUserId] = true
- }
-
- } else {
-
- // Find out who is a member of the channel, only keep those profiles
- if eResult := <-echan; eResult.Err != nil {
- l4g.Error(utils.T("api.post.send_notifications_and_forget.members.error"), post.ChannelId, eResult.Err.Message)
- return
- } else {
- tempProfileMap := make(map[string]*model.User)
- members := eResult.Data.([]model.ChannelMember)
- for _, member := range members {
- tempProfileMap[member.UserId] = profileMap[member.UserId]
- }
+ }
- profileMap = tempProfileMap
- }
+ // If turned on, add the user's case sensitive first name
+ if profile.NotifyProps["first_name"] == "true" {
+ keywordMap[profile.FirstName] = append(keywordMap[profile.FirstName], profile.Id)
+ }
- // Build map for keywords
- keywordMap := make(map[string][]string)
- for _, profile := range profileMap {
- if len(profile.NotifyProps["mention_keys"]) > 0 {
+ // Add @all to keywords if user has them turned on
+ // if profile.NotifyProps["all"] == "true" {
+ // keywordMap["@all"] = append(keywordMap["@all"], profile.Id)
+ // }
- // Add all the user's mention keys
- splitKeys := strings.Split(profile.NotifyProps["mention_keys"], ",")
- for _, k := range splitKeys {
- keywordMap[k] = append(keywordMap[strings.ToLower(k)], profile.Id)
- }
- }
+ // Add @channel to keywords if user has them turned on
+ if profile.NotifyProps["channel"] == "true" {
+ keywordMap["@channel"] = append(keywordMap["@channel"], profile.Id)
+ }
+ }
- // If turned on, add the user's case sensitive first name
- if profile.NotifyProps["first_name"] == "true" {
- keywordMap[profile.FirstName] = append(keywordMap[profile.FirstName], profile.Id)
- }
+ // Build a map as a list of unique user_ids that are mentioned in this post
+ splitF := func(c rune) bool {
+ return model.SplitRunes[c]
+ }
+ splitMessage := strings.Fields(post.Message)
+ for _, word := range splitMessage {
+ var userIds []string
- // Add @all to keywords if user has them turned on
- // if profile.NotifyProps["all"] == "true" {
- // keywordMap["@all"] = append(keywordMap["@all"], profile.Id)
- // }
+ // Non-case-sensitive check for regular keys
+ if ids, match := keywordMap[strings.ToLower(word)]; match {
+ userIds = append(userIds, ids...)
+ }
- // Add @channel to keywords if user has them turned on
- if profile.NotifyProps["channel"] == "true" {
- keywordMap["@channel"] = append(keywordMap["@channel"], profile.Id)
- }
- }
+ // Case-sensitive check for first name
+ if ids, match := keywordMap[word]; match {
+ userIds = append(userIds, ids...)
+ }
- // Build a map as a list of unique user_ids that are mentioned in this post
- splitF := func(c rune) bool {
- return model.SplitRunes[c]
- }
- splitMessage := strings.Fields(post.Message)
- for _, word := range splitMessage {
- var userIds []string
+ if len(userIds) == 0 {
+ // No matches were found with the string split just on whitespace so try further splitting
+ // the message on punctuation
+ splitWords := strings.FieldsFunc(word, splitF)
+ for _, splitWord := range splitWords {
// Non-case-sensitive check for regular keys
- if ids, match := keywordMap[strings.ToLower(word)]; match {
+ if ids, match := keywordMap[strings.ToLower(splitWord)]; match {
userIds = append(userIds, ids...)
}
// Case-sensitive check for first name
- if ids, match := keywordMap[word]; match {
+ if ids, match := keywordMap[splitWord]; match {
userIds = append(userIds, ids...)
}
-
- if len(userIds) == 0 {
- // No matches were found with the string split just on whitespace so try further splitting
- // the message on punctuation
- splitWords := strings.FieldsFunc(word, splitF)
-
- for _, splitWord := range splitWords {
- // Non-case-sensitive check for regular keys
- if ids, match := keywordMap[strings.ToLower(splitWord)]; match {
- userIds = append(userIds, ids...)
- }
-
- // Case-sensitive check for first name
- if ids, match := keywordMap[splitWord]; match {
- userIds = append(userIds, ids...)
- }
- }
- }
-
- for _, userId := range userIds {
- if post.UserId == userId {
- continue
- }
- sendEmail := true
- if _, ok := profileMap[userId].NotifyProps["email"]; ok && profileMap[userId].NotifyProps["email"] == "false" {
- sendEmail = false
- }
- if sendEmail && (profileMap[userId].IsAway() || profileMap[userId].IsOffline()) {
- toEmailMap[userId] = true
- } else {
- toEmailMap[userId] = false
- }
- }
}
+ }
- for id := range toEmailMap {
- updateMentionCountAndForget(post.ChannelId, id)
+ for _, userId := range userIds {
+ if post.UserId == userId {
+ continue
+ }
+ sendEmail := true
+ if _, ok := profileMap[userId].NotifyProps["email"]; ok && profileMap[userId].NotifyProps["email"] == "false" {
+ sendEmail = false
+ }
+ if sendEmail && (profileMap[userId].IsAway() || profileMap[userId].IsOffline()) {
+ toEmailMap[userId] = true
+ } else {
+ toEmailMap[userId] = false
}
}
+ }
- if len(toEmailMap) != 0 {
- mentionedUsers = make([]string, 0, len(toEmailMap))
- for k := range toEmailMap {
- mentionedUsers = append(mentionedUsers, k)
- }
+ for id := range toEmailMap {
+ updateMentionCountAndForget(post.ChannelId, id)
+ }
+ }
- teamURL := c.GetSiteURL() + "/" + team.Name
+ if len(toEmailMap) != 0 {
+ mentionedUsers = make([]string, 0, len(toEmailMap))
+ for k := range toEmailMap {
+ mentionedUsers = append(mentionedUsers, k)
+ }
- // Build and send the emails
- tm := time.Unix(post.CreateAt/1000, 0)
+ teamURL := c.GetSiteURL() + "/" + team.Name
- for id, doSend := range toEmailMap {
+ // Build and send the emails
+ tm := time.Unix(post.CreateAt/1000, 0)
- if !doSend {
- continue
- }
+ for id, doSend := range toEmailMap {
- // skip if inactive
- if profileMap[id].DeleteAt > 0 {
- continue
- }
+ if !doSend {
+ continue
+ }
- userLocale := utils.GetUserTranslations(profileMap[id].Locale)
+ // skip if inactive
+ if profileMap[id].DeleteAt > 0 {
+ continue
+ }
- if channel.Type == model.CHANNEL_DIRECT {
- bodyText = userLocale("api.post.send_notifications_and_forget.message_body")
- subjectText = userLocale("api.post.send_notifications_and_forget.message_subject")
- } else {
- bodyText = userLocale("api.post.send_notifications_and_forget.mention_body")
- subjectText = userLocale("api.post.send_notifications_and_forget.mention_subject")
- channelName = channel.DisplayName
- }
+ userLocale := utils.GetUserTranslations(profileMap[id].Locale)
- month := userLocale(tm.Month().String())
- day := fmt.Sprintf("%d", tm.Day())
- year := fmt.Sprintf("%d", tm.Year())
- zone, _ := tm.Zone()
-
- subjectPage := NewServerTemplatePage("post_subject", c.Locale)
- subjectPage.Props["Subject"] = userLocale("api.templates.post_subject",
- map[string]interface{}{"SubjectText": subjectText, "TeamDisplayName": team.DisplayName,
- "Month": month[:3], "Day": day, "Year": year})
-
- bodyPage := NewServerTemplatePage("post_body", c.Locale)
- bodyPage.Props["SiteURL"] = c.GetSiteURL()
- bodyPage.Props["PostMessage"] = model.ClearMentionTags(post.Message)
- bodyPage.Props["TeamLink"] = teamURL + "/channels/" + channel.Name
- bodyPage.Props["BodyText"] = bodyText
- bodyPage.Props["Button"] = userLocale("api.templates.post_body.button")
- bodyPage.Html["Info"] = template.HTML(userLocale("api.templates.post_body.info",
- map[string]interface{}{"ChannelName": channelName, "SenderName": senderName,
- "Hour": fmt.Sprintf("%02d", tm.Hour()), "Minute": fmt.Sprintf("%02d", tm.Minute()),
- "TimeZone": zone, "Month": month, "Day": day}))
-
- // attempt to fill in a message body if the post doesn't have any text
- if len(strings.TrimSpace(bodyPage.Props["PostMessage"])) == 0 && len(post.Filenames) > 0 {
- // extract the filenames from their paths and determine what type of files are attached
- filenames := make([]string, len(post.Filenames))
- onlyImages := true
- for i, filename := range post.Filenames {
- var err error
- if filenames[i], err = url.QueryUnescape(filepath.Base(filename)); err != nil {
- // this should never error since filepath was escaped using url.QueryEscape
- filenames[i] = filepath.Base(filename)
- }
+ if channel.Type == model.CHANNEL_DIRECT {
+ bodyText = userLocale("api.post.send_notifications_and_forget.message_body")
+ subjectText = userLocale("api.post.send_notifications_and_forget.message_subject")
+ } else {
+ bodyText = userLocale("api.post.send_notifications_and_forget.mention_body")
+ subjectText = userLocale("api.post.send_notifications_and_forget.mention_subject")
+ channelName = channel.DisplayName
+ }
- ext := filepath.Ext(filename)
- onlyImages = onlyImages && model.IsFileExtImage(ext)
- }
- filenamesString := strings.Join(filenames, ", ")
+ month := userLocale(tm.Month().String())
+ day := fmt.Sprintf("%d", tm.Day())
+ year := fmt.Sprintf("%d", tm.Year())
+ zone, _ := tm.Zone()
+
+ subjectPage := NewServerTemplatePage("post_subject", c.Locale)
+ subjectPage.Props["Subject"] = userLocale("api.templates.post_subject",
+ map[string]interface{}{"SubjectText": subjectText, "TeamDisplayName": team.DisplayName,
+ "Month": month[:3], "Day": day, "Year": year})
+
+ bodyPage := NewServerTemplatePage("post_body", c.Locale)
+ bodyPage.Props["SiteURL"] = c.GetSiteURL()
+ bodyPage.Props["PostMessage"] = model.ClearMentionTags(post.Message)
+ bodyPage.Props["TeamLink"] = teamURL + "/channels/" + channel.Name
+ bodyPage.Props["BodyText"] = bodyText
+ bodyPage.Props["Button"] = userLocale("api.templates.post_body.button")
+ bodyPage.Html["Info"] = template.HTML(userLocale("api.templates.post_body.info",
+ map[string]interface{}{"ChannelName": channelName, "SenderName": senderName,
+ "Hour": fmt.Sprintf("%02d", tm.Hour()), "Minute": fmt.Sprintf("%02d", tm.Minute()),
+ "TimeZone": zone, "Month": month, "Day": day}))
+
+ // attempt to fill in a message body if the post doesn't have any text
+ if len(strings.TrimSpace(bodyPage.Props["PostMessage"])) == 0 && len(post.Filenames) > 0 {
+ // extract the filenames from their paths and determine what type of files are attached
+ filenames := make([]string, len(post.Filenames))
+ onlyImages := true
+ for i, filename := range post.Filenames {
+ var err error
+ if filenames[i], err = url.QueryUnescape(filepath.Base(filename)); err != nil {
+ // this should never error since filepath was escaped using url.QueryEscape
+ filenames[i] = filepath.Base(filename)
+ }
- var attachmentPrefix string
- if onlyImages {
- attachmentPrefix = "Image"
- } else {
- attachmentPrefix = "File"
- }
- if len(post.Filenames) > 1 {
- attachmentPrefix += "s"
- }
+ ext := filepath.Ext(filename)
+ onlyImages = onlyImages && model.IsFileExtImage(ext)
+ }
+ filenamesString := strings.Join(filenames, ", ")
- bodyPage.Props["PostMessage"] = userLocale("api.post.send_notifications_and_forget.sent",
- map[string]interface{}{"Prefix": attachmentPrefix, "Filenames": filenamesString})
- }
+ var attachmentPrefix string
+ if onlyImages {
+ attachmentPrefix = "Image"
+ } else {
+ attachmentPrefix = "File"
+ }
+ if len(post.Filenames) > 1 {
+ attachmentPrefix += "s"
+ }
- if err := utils.SendMail(profileMap[id].Email, subjectPage.Render(), bodyPage.Render()); err != nil {
- l4g.Error(utils.T("api.post.send_notifications_and_forget.send.error"), profileMap[id].Email, err)
- }
+ bodyPage.Props["PostMessage"] = userLocale("api.post.send_notifications_and_forget.sent",
+ map[string]interface{}{"Prefix": attachmentPrefix, "Filenames": filenamesString})
+ }
- if *utils.Cfg.EmailSettings.SendPushNotifications {
- sessionChan := Srv.Store.Session().GetSessions(id)
- if result := <-sessionChan; result.Err != nil {
- l4g.Error(utils.T("api.post.send_notifications_and_forget.sessions.error"), id, result.Err)
- } else {
- sessions := result.Data.([]*model.Session)
- alreadySeen := make(map[string]string)
-
- for _, session := range sessions {
- if len(session.DeviceId) > 0 && alreadySeen[session.DeviceId] == "" &&
- (strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_APPLE+":") || strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":")) {
- alreadySeen[session.DeviceId] = session.DeviceId
-
- msg := model.PushNotification{}
- msg.Badge = 1
- msg.ServerId = utils.CfgDiagnosticId
-
- if strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_APPLE+":") {
- msg.Platform = model.PUSH_NOTIFY_APPLE
- msg.DeviceId = strings.TrimPrefix(session.DeviceId, model.PUSH_NOTIFY_APPLE+":")
- } else if strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":") {
- msg.Platform = model.PUSH_NOTIFY_ANDROID
- msg.DeviceId = strings.TrimPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":")
- }
+ if err := utils.SendMail(profileMap[id].Email, subjectPage.Render(), bodyPage.Render()); err != nil {
+ l4g.Error(utils.T("api.post.send_notifications_and_forget.send.error"), profileMap[id].Email, err)
+ }
- if channel.Type == model.CHANNEL_DIRECT {
- msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_message")
- } else {
- msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_mention") + channelName
- }
+ if *utils.Cfg.EmailSettings.SendPushNotifications {
+ sessionChan := Srv.Store.Session().GetSessions(id)
+ if result := <-sessionChan; result.Err != nil {
+ l4g.Error(utils.T("api.post.send_notifications_and_forget.sessions.error"), id, result.Err)
+ } else {
+ sessions := result.Data.([]*model.Session)
+ alreadySeen := make(map[string]string)
+
+ for _, session := range sessions {
+ if len(session.DeviceId) > 0 && alreadySeen[session.DeviceId] == "" &&
+ (strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_APPLE+":") || strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":")) {
+ alreadySeen[session.DeviceId] = session.DeviceId
+
+ msg := model.PushNotification{}
+ msg.Badge = 1
+ msg.ServerId = utils.CfgDiagnosticId
+
+ if strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_APPLE+":") {
+ msg.Platform = model.PUSH_NOTIFY_APPLE
+ msg.DeviceId = strings.TrimPrefix(session.DeviceId, model.PUSH_NOTIFY_APPLE+":")
+ } else if strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":") {
+ msg.Platform = model.PUSH_NOTIFY_ANDROID
+ msg.DeviceId = strings.TrimPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":")
+ }
- httpClient := http.Client{}
- request, _ := http.NewRequest("POST", *utils.Cfg.EmailSettings.PushNotificationServer+"/api/v1/send_push", strings.NewReader(msg.ToJson()))
+ if channel.Type == model.CHANNEL_DIRECT {
+ msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_message")
+ } else {
+ msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_mention") + channelName
+ }
- l4g.Debug(utils.T("api.post.send_notifications_and_forget.push_notification.debug"), msg.DeviceId, msg.Message)
- if _, err := httpClient.Do(request); err != nil {
- l4g.Error(utils.T("api.post.send_notifications_and_forget.push_notification.error"), id, err)
- }
- }
+ httpClient := http.Client{}
+ request, _ := http.NewRequest("POST", *utils.Cfg.EmailSettings.PushNotificationServer+"/api/v1/send_push", strings.NewReader(msg.ToJson()))
+
+ l4g.Debug(utils.T("api.post.send_notifications_and_forget.push_notification.debug"), msg.DeviceId, msg.Message)
+ if _, err := httpClient.Do(request); err != nil {
+ l4g.Error(utils.T("api.post.send_notifications_and_forget.push_notification.error"), id, err)
}
}
}
}
}
}
+ }
- message := model.NewMessage(c.Session.TeamId, post.ChannelId, post.UserId, model.ACTION_POSTED)
- message.Add("post", post.ToJson())
- message.Add("channel_type", channel.Type)
+ message := model.NewMessage(c.Session.TeamId, post.ChannelId, post.UserId, model.ACTION_POSTED)
+ message.Add("post", post.ToJson())
+ message.Add("channel_type", channel.Type)
- if len(post.Filenames) != 0 {
- message.Add("otherFile", "true")
+ if len(post.Filenames) != 0 {
+ message.Add("otherFile", "true")
- for _, filename := range post.Filenames {
- ext := filepath.Ext(filename)
- if model.IsFileExtImage(ext) {
- message.Add("image", "true")
- break
- }
+ for _, filename := range post.Filenames {
+ ext := filepath.Ext(filename)
+ if model.IsFileExtImage(ext) {
+ message.Add("image", "true")
+ break
}
}
+ }
- if len(mentionedUsers) != 0 {
- message.Add("mentions", model.ArrayToJson(mentionedUsers))
- }
+ if len(mentionedUsers) != 0 {
+ message.Add("mentions", model.ArrayToJson(mentionedUsers))
+ }
- PublishAndForget(message)
- }()
+ PublishAndForget(message)
}
func updateMentionCountAndForget(channelId, userId string) {
@@ -728,6 +727,95 @@ func updateMentionCountAndForget(channelId, userId string) {
}()
}
+func checkForOutOfChannelMentions(c *Context, post *model.Post, channel *model.Channel, allProfiles map[string]*model.User, members []model.ChannelMember) {
+ // don't check for out of channel mentions in direct channels
+ if channel.Type == model.CHANNEL_DIRECT {
+ return
+ }
+
+ mentioned := getOutOfChannelMentions(post, allProfiles, members)
+ if len(mentioned) == 0 {
+ return
+ }
+
+ usernames := make([]string, len(mentioned))
+ for i, user := range mentioned {
+ usernames[i] = user.Username
+ }
+ sort.Strings(usernames)
+
+ var message string
+ if len(usernames) == 1 {
+ message = c.T("api.post.check_for_out_of_channel_mentions.message.one", map[string]interface{}{
+ "Username": usernames[0],
+ })
+ } else {
+ message = c.T("api.post.check_for_out_of_channel_mentions.message.multiple", map[string]interface{}{
+ "Usernames": strings.Join(usernames[:len(usernames)-1], ", "),
+ "LastUsername": usernames[len(usernames)-1],
+ })
+ }
+
+ SendEphemeralPost(
+ c.Session.TeamId,
+ post.UserId,
+ &model.Post{
+ ChannelId: post.ChannelId,
+ Message: message,
+ CreateAt: post.CreateAt + 1,
+ },
+ )
+}
+
+// Gets a list of users that were mentioned in a given post that aren't in the channel that the post was made in
+func getOutOfChannelMentions(post *model.Post, allProfiles map[string]*model.User, members []model.ChannelMember) []*model.User {
+ // copy the profiles map since we'll be removing items from it
+ profiles := make(map[string]*model.User)
+ for id, profile := range allProfiles {
+ profiles[id] = profile
+ }
+
+ // only keep profiles which aren't in the current channel
+ for _, member := range members {
+ delete(profiles, member.UserId)
+ }
+
+ var mentioned []*model.User
+
+ for _, profile := range profiles {
+ if pattern, err := regexp.Compile(`(\W|^)@` + regexp.QuoteMeta(profile.Username) + `(\W|$)`); err != nil {
+ l4g.Error(utils.T("api.post.get_out_of_channel_mentions.regex.error"), profile.Id, err)
+ } else if pattern.MatchString(post.Message) {
+ mentioned = append(mentioned, profile)
+ }
+ }
+
+ return mentioned
+}
+
+func SendEphemeralPost(teamId, userId string, post *model.Post) {
+ post.Type = model.POST_EPHEMERAL
+
+ // fill in fields which haven't been specified which have sensible defaults
+ if post.Id == "" {
+ post.Id = model.NewId()
+ }
+ if post.CreateAt == 0 {
+ post.CreateAt = model.GetMillis()
+ }
+ if post.Props == nil {
+ post.Props = model.StringInterface{}
+ }
+ if post.Filenames == nil {
+ post.Filenames = []string{}
+ }
+
+ message := model.NewMessage(teamId, post.ChannelId, userId, model.ACTION_EPHEMERAL_MESSAGE)
+ message.Add("post", post.ToJson())
+
+ PublishAndForget(message)
+}
+
func updatePost(c *Context, w http.ResponseWriter, r *http.Request) {
post := model.PostFromJson(r.Body)
diff --git a/api/post_test.go b/api/post_test.go
index 1a9fd2579..027043766 100644
--- a/api/post_test.go
+++ b/api/post_test.go
@@ -8,6 +8,7 @@ import (
"github.com/mattermost/platform/store"
"github.com/mattermost/platform/utils"
"net/http"
+ "strings"
"testing"
"time"
)
@@ -857,3 +858,97 @@ func TestMakeDirectChannelVisible(t *testing.T) {
t.Fatal("Failed to set direct channel to be visible for user2")
}
}
+
+func TestGetOutOfChannelMentions(t *testing.T) {
+ Setup()
+
+ team1 := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Type: model.TEAM_OPEN}
+ team1 = Client.Must(Client.CreateTeam(team1)).Data.(*model.Team)
+
+ user1 := &model.User{TeamId: team1.Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd", Username: "user1"}
+ user1 = Client.Must(Client.CreateUser(user1, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user1.Id))
+
+ user2 := &model.User{TeamId: team1.Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd", Username: "user2"}
+ user2 = Client.Must(Client.CreateUser(user2, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user2.Id))
+
+ user3 := &model.User{TeamId: team1.Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd", Username: "user3"}
+ user3 = Client.Must(Client.CreateUser(user3, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user3.Id))
+
+ Client.Must(Client.LoginByEmail(team1.Name, user1.Email, "pwd"))
+
+ channel1 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team1.Id}
+ channel1 = Client.Must(Client.CreateChannel(channel1)).Data.(*model.Channel)
+
+ var allProfiles map[string]*model.User
+ if result := <-Srv.Store.User().GetProfiles(team1.Id); result.Err != nil {
+ t.Fatal(result.Err)
+ } else {
+ allProfiles = result.Data.(map[string]*model.User)
+ }
+
+ var members []model.ChannelMember
+ if result := <-Srv.Store.Channel().GetMembers(channel1.Id); result.Err != nil {
+ t.Fatal(result.Err)
+ } else {
+ members = result.Data.([]model.ChannelMember)
+ }
+
+ // test a post that doesn't @mention anybody
+ post1 := &model.Post{ChannelId: channel1.Id, Message: "user1 user2 user3"}
+ if mentioned := getOutOfChannelMentions(post1, allProfiles, members); len(mentioned) != 0 {
+ t.Fatalf("getOutOfChannelMentions returned %v when no users were mentioned", mentioned)
+ }
+
+ // test a post that @mentions someone in the channel
+ post2 := &model.Post{ChannelId: channel1.Id, Message: "@user1 is user1"}
+ if mentioned := getOutOfChannelMentions(post2, allProfiles, members); len(mentioned) != 0 {
+ t.Fatalf("getOutOfChannelMentions returned %v when only users in the channel were mentioned", mentioned)
+ }
+
+ // test a post that @mentions someone not in the channel
+ post3 := &model.Post{ChannelId: channel1.Id, Message: "@user2 and @user3 aren't in the channel"}
+ if mentioned := getOutOfChannelMentions(post3, allProfiles, members); len(mentioned) != 2 || (mentioned[0].Id != user2.Id && mentioned[0].Id != user3.Id) || (mentioned[1].Id != user2.Id && mentioned[1].Id != user3.Id) {
+ t.Fatalf("getOutOfChannelMentions returned %v when two users outside the channel were mentioned", mentioned)
+ }
+
+ // test a post that @mentions someone not in the channel as well as someone in the channel
+ post4 := &model.Post{ChannelId: channel1.Id, Message: "@user2 and @user1 might be in the channel"}
+ if mentioned := getOutOfChannelMentions(post4, allProfiles, members); len(mentioned) != 1 || mentioned[0].Id != user2.Id {
+ t.Fatalf("getOutOfChannelMentions returned %v when someone in the channel and someone outside the channel were mentioned", mentioned)
+ }
+
+ Client.Must(Client.Logout())
+
+ team2 := &model.Team{DisplayName: "Name", Name: "z-z-" + model.NewId() + "a", Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Type: model.TEAM_OPEN}
+ team2 = Client.Must(Client.CreateTeam(team2)).Data.(*model.Team)
+
+ user4 := &model.User{TeamId: team2.Id, Email: strings.ToLower(model.NewId()) + "success+test@simulator.amazonses.com", Nickname: "Corey Hulen", Password: "pwd", Username: "user4"}
+ user4 = Client.Must(Client.CreateUser(user4, "")).Data.(*model.User)
+ store.Must(Srv.Store.User().VerifyEmail(user4.Id))
+
+ Client.Must(Client.LoginByEmail(team2.Name, user4.Email, "pwd"))
+
+ channel2 := &model.Channel{DisplayName: "Test API Name", Name: "a" + model.NewId() + "a", Type: model.CHANNEL_OPEN, TeamId: team2.Id}
+ channel2 = Client.Must(Client.CreateChannel(channel2)).Data.(*model.Channel)
+
+ if result := <-Srv.Store.User().GetProfiles(team2.Id); result.Err != nil {
+ t.Fatal(result.Err)
+ } else {
+ allProfiles = result.Data.(map[string]*model.User)
+ }
+
+ if result := <-Srv.Store.Channel().GetMembers(channel2.Id); result.Err != nil {
+ t.Fatal(result.Err)
+ } else {
+ members = result.Data.([]model.ChannelMember)
+ }
+
+ // test a post that @mentions someone on a different team
+ post5 := &model.Post{ChannelId: channel2.Id, Message: "@user2 and @user3 might be in the channel"}
+ if mentioned := getOutOfChannelMentions(post5, allProfiles, members); len(mentioned) != 0 {
+ t.Fatalf("getOutOfChannelMentions returned %v when two users on a different team were mentioned", mentioned)
+ }
+}
diff --git a/api/team.go b/api/team.go
index 8b25e3316..6d59e94e9 100644
--- a/api/team.go
+++ b/api/team.go
@@ -66,7 +66,7 @@ func signupTeam(c *Context, w http.ResponseWriter, r *http.Request) {
bodyPage.Props["SiteURL"] = c.GetSiteURL()
bodyPage.Props["Title"] = c.T("api.templates.signup_team_body.title")
bodyPage.Props["Button"] = c.T("api.templates.signup_team_body.button")
- bodyPage.Html["Info"] = template.HTML(c.T("api.templates.signup_team_body.button",
+ bodyPage.Html["Info"] = template.HTML(c.T("api.templates.signup_team_body.info",
map[string]interface{}{"SiteName": utils.ClientCfg["SiteName"]}))
props := make(map[string]string)
diff --git a/api/user.go b/api/user.go
index 91c8c022a..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
}
@@ -2087,13 +2091,16 @@ func switchToSSO(c *Context, w http.ResponseWriter, r *http.Request) {
func CompleteSwitchWithOAuth(c *Context, w http.ResponseWriter, r *http.Request, service string, userData io.ReadCloser, team *model.Team, email string) {
authData := ""
+ ssoEmail := ""
provider := einterfaces.GetOauthProvider(service)
if provider == nil {
c.Err = model.NewLocAppError("CompleteClaimWithOAuth", "api.user.complete_switch_with_oauth.unavailable.app_error",
map[string]interface{}{"Service": service}, "")
return
} else {
- authData = provider.GetAuthDataFromJson(userData)
+ ssoUser := provider.GetUserFromJson(userData)
+ authData = ssoUser.AuthData
+ ssoEmail = ssoUser.Email
}
if len(authData) == 0 {
@@ -2120,7 +2127,7 @@ func CompleteSwitchWithOAuth(c *Context, w http.ResponseWriter, r *http.Request,
return
}
- if result := <-Srv.Store.User().UpdateAuthData(user.Id, service, authData); result.Err != nil {
+ if result := <-Srv.Store.User().UpdateAuthData(user.Id, service, authData, ssoEmail); result.Err != nil {
c.Err = result.Err
return
}
diff --git a/api/web_team_hub.go b/api/web_team_hub.go
index 55300c828..9d1c56f15 100644
--- a/api/web_team_hub.go
+++ b/api/web_team_hub.go
@@ -101,6 +101,9 @@ func ShouldSendEvent(webCon *WebConn, msg *model.Message) bool {
return false
} else if msg.Action == model.ACTION_PREFERENCE_CHANGED {
return false
+ } else if msg.Action == model.ACTION_EPHEMERAL_MESSAGE {
+ // For now, ephemeral messages are sent directly to individual users
+ return false
}
// Only report events to a user who is the subject of the event, or is in the channel of the event
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}