2015-06-14 23:53:32 -08:00
// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved.
// See License.txt for license information.
package api
import (
l4g "code.google.com/p/log4go"
"github.com/gorilla/mux"
"github.com/mattermost/platform/model"
"github.com/mattermost/platform/utils"
"net/http"
2015-06-18 12:13:56 -04:00
"reflect"
"runtime"
2015-06-14 23:53:32 -08:00
"strconv"
"strings"
)
type commandHandler func ( c * Context , command * model . Command ) bool
var commands = [ ] commandHandler {
logoutCommand ,
joinCommand ,
loadTestCommand ,
2015-06-18 12:13:56 -04:00
echoCommand ,
2015-06-14 23:53:32 -08:00
}
func InitCommand ( r * mux . Router ) {
l4g . Debug ( "Initializing command api routes" )
r . Handle ( "/command" , ApiUserRequired ( command ) ) . Methods ( "POST" )
2015-06-15 08:45:02 -04:00
2015-06-14 23:53:32 -08:00
hub . Start ( )
}
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 ) ,
}
checkCommand ( c , command )
if c . Err != nil {
return
} else {
w . Write ( [ ] byte ( command . ToJson ( ) ) )
}
}
func checkCommand ( c * Context , command * model . Command ) bool {
if len ( command . Command ) == 0 || strings . Index ( command . Command , "/" ) != 0 {
c . Err = model . NewAppError ( "checkCommand" , "Command must start with /" , "" )
return false
}
2015-06-17 12:07:35 -04:00
tchan := Srv . Store . Team ( ) . Get ( c . Session . TeamId )
2015-06-14 23:53:32 -08:00
if len ( command . ChannelId ) > 0 {
cchan := Srv . Store . Channel ( ) . CheckPermissionsTo ( c . Session . TeamId , command . ChannelId , c . Session . UserId )
if ! c . HasPermissionsToChannel ( cchan , "checkCommand" ) {
return true
}
}
2015-06-17 12:07:35 -04:00
allowValet := false
if tResult := <- tchan ; tResult . Err != nil {
c . Err = model . NewAppError ( "checkCommand" , "Could not find the team for this session, team_id=" + c . Session . TeamId , "" )
return false
} else {
allowValet = tResult . Data . ( * model . Team ) . AllowValet
}
2015-06-18 12:13:56 -04:00
ec := runtime . FuncForPC ( reflect . ValueOf ( echoCommand ) . Pointer ( ) ) . Name ( )
2015-06-14 23:53:32 -08:00
for _ , v := range commands {
2015-06-18 12:13:56 -04:00
if ! allowValet && ec == runtime . FuncForPC ( reflect . ValueOf ( v ) . Pointer ( ) ) . Name ( ) {
2015-06-17 12:07:35 -04:00
continue
}
2015-06-14 23:53:32 -08:00
if v ( c , command ) {
return true
} else if c . Err != nil {
return true
}
}
return false
}
func logoutCommand ( c * Context , command * model . Command ) bool {
cmd := "/logout"
if strings . Index ( command . Command , cmd ) == 0 {
command . AddSuggestion ( & model . SuggestCommand { Suggestion : cmd , Description : "Logout" } )
if ! command . Suggest {
command . GotoLocation = "/logout"
command . Response = model . RESP_EXECUTED
return true
}
} else if strings . Index ( cmd , command . Command ) == 0 {
command . AddSuggestion ( & model . SuggestCommand { Suggestion : cmd , Description : "Logout" } )
}
return false
}
func echoCommand ( c * Context , command * model . Command ) bool {
cmd := "/echo"
if strings . Index ( command . Command , cmd ) == 0 {
parts := strings . SplitN ( command . Command , " " , 3 )
channelName := ""
if len ( parts ) >= 2 {
channelName = parts [ 1 ]
}
message := ""
if len ( parts ) >= 3 {
message = parts [ 2 ]
}
if result := <- Srv . Store . Channel ( ) . GetChannels ( c . Session . TeamId , c . Session . UserId ) ; result . Err != nil {
c . Err = result . Err
return false
} else {
channels := result . Data . ( * model . ChannelList )
for _ , v := range channels . Channels {
if v . Type == model . CHANNEL_DIRECT {
continue
}
if v . Name == channelName && ! command . Suggest {
post := & model . Post { }
post . ChannelId = v . Id
post . Message = message
if _ , err := CreateValetPost ( c , post ) ; err != nil {
c . Err = err
return false
}
command . Response = model . RESP_EXECUTED
return true
}
if len ( channelName ) == 0 || ( strings . Index ( v . Name , channelName ) == 0 && len ( parts ) < 3 ) {
command . AddSuggestion ( & model . SuggestCommand { Suggestion : cmd + " " + v . Name , Description : "Echo a message using Valet in a channel" } )
}
}
}
} else if strings . Index ( cmd , command . Command ) == 0 {
command . AddSuggestion ( & model . SuggestCommand { Suggestion : cmd , Description : "Echo a message using Valet in a channel" } )
}
return false
}
func joinCommand ( c * Context , command * model . Command ) bool {
// looks for "/join channel-name"
cmd := "/join"
if strings . Index ( command . Command , cmd ) == 0 {
parts := strings . Split ( command . Command , " " )
startsWith := ""
if len ( parts ) == 2 {
startsWith = parts [ 1 ]
}
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 )
for _ , v := range channels . Channels {
if v . Name == startsWith && ! command . Suggest {
if v . Type == model . CHANNEL_DIRECT {
return false
}
2015-06-29 10:24:45 -04:00
JoinChannel ( c , v . Id , "" )
2015-06-14 23:53:32 -08:00
if c . Err != nil {
return false
}
command . GotoLocation = "/channels/" + v . Name
command . Response = model . RESP_EXECUTED
return true
}
if len ( startsWith ) == 0 || strings . Index ( v . Name , startsWith ) == 0 {
command . AddSuggestion ( & model . SuggestCommand { Suggestion : cmd + " " + v . Name , Description : "Join the open channel" } )
}
}
}
} else if strings . Index ( cmd , command . Command ) == 0 {
command . AddSuggestion ( & model . SuggestCommand { Suggestion : cmd , Description : "Join an open channel" } )
}
return false
}
func loadTestCommand ( c * Context , command * model . Command ) bool {
cmd := "/loadtest"
// This command is only available when AllowTesting is true
if ! utils . Cfg . ServiceSettings . AllowTesting {
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
}
} else if strings . Index ( cmd , command . Command ) == 0 {
command . AddSuggestion ( & model . SuggestCommand { Suggestion : cmd , Description : "Debug Load Testing" } )
}
return false
}
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
}
func loadTestSetupCommand ( c * Context , command * model . Command ) bool {
cmd := "/loadtest setup"
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" )
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 . TeamUrl + "/api/v1" )
if doTeams {
if err := CreateBasicUser ( client ) ; err != nil {
l4g . Error ( "Failed to create testing enviroment" )
return true
}
client . LoginByEmail ( BTEST_TEAM_DOMAIN_NAME , BTEST_USER_EMAIL , BTEST_USER_PASSWORD )
enviroment , err := CreateTestEnviromentWithTeams (
client ,
utils . Range { numTeams , numTeams } ,
utils . Range { numChannels , numChannels } ,
utils . Range { numUsers , numUsers } ,
utils . Range { numPosts , numPosts } ,
doFuzz )
if err != true {
l4g . Error ( "Failed to create testing enviroment" )
return true
} else {
l4g . Info ( "Testing enviroment created" )
for i := 0 ; i < len ( enviroment . Teams ) ; i ++ {
l4g . Info ( "Team Created: " + enviroment . Teams [ i ] . Domain )
l4g . Info ( "\t User to login: " + enviroment . Enviroments [ i ] . Users [ 0 ] . Email + ", " + USER_PASSWORD )
}
}
} else {
client . MockSession ( c . Session . Id )
CreateTestEnviromentInTeam (
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 : "Creates a testing enviroment in current team. [teams] [fuzz] <Num Channels> <Num Users> <NumPosts>" } )
}
return false
}
func loadTestUsersCommand ( c * Context , command * model . Command ) bool {
cmd1 := "/loadtest users"
cmd2 := "/loadtest users fuzz"
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 }
}
client := model . NewClient ( c . TeamUrl + "/api/v1" )
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 : "Add a specified number of random users to current team <Min Users> <Max Users>" } )
command . AddSuggestion ( & model . SuggestCommand { Suggestion : cmd2 , Description : "Add a specified number of random users with fuzz text to current team <Min Users> <Max Users>" } )
} else if strings . Index ( cmd2 , command . Command ) == 0 {
command . AddSuggestion ( & model . SuggestCommand { Suggestion : cmd2 , Description : "Add a specified number of random users with fuzz text to current team <Min Users> <Max Users>" } )
}
return false
}
func loadTestChannelsCommand ( c * Context , command * model . Command ) bool {
cmd1 := "/loadtest channels"
cmd2 := "/loadtest channels fuzz"
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 }
}
client := model . NewClient ( c . TeamUrl + "/api/v1" )
client . MockSession ( c . Session . Id )
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 : "Add a specified number of random channels to current team <MinChannels> <MaxChannels>" } )
command . AddSuggestion ( & model . SuggestCommand { Suggestion : cmd2 , Description : "Add a specified number of random channels with fuzz text to current team <Min Channels> <Max Channels>" } )
} else if strings . Index ( cmd2 , command . Command ) == 0 {
command . AddSuggestion ( & model . SuggestCommand { Suggestion : cmd2 , Description : "Add a specified number of random channels with fuzz text to current team <Min Channels> <Max Channels>" } )
}
return false
}
func loadTestPostsCommand ( c * Context , command * model . Command ) bool {
cmd1 := "/loadtest posts"
cmd2 := "/loadtest posts fuzz"
if strings . Index ( command . Command , cmd1 ) == 0 && ! command . Suggest {
cmd := cmd1
doFuzz := false
if strings . Index ( command . Command , cmd2 ) == 0 {
cmd = cmd2
doFuzz = true
}
postsr , err := parseRange ( command . Command , cmd )
if err == false {
postsr = utils . Range { 20 , 30 }
}
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 }
}
}
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 . TeamUrl + "/api/v1" )
client . MockSession ( c . Session . Id )
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 : "Add some random posts to current channel <Min Posts> <Max Posts> <Min Images> <Max Images>" } )
command . AddSuggestion ( & model . SuggestCommand { Suggestion : cmd2 , Description : "Add some random posts with fuzz text to current channel <Min Posts> <Max Posts> <Min Images> <Max Images>" } )
} else if strings . Index ( cmd2 , command . Command ) == 0 {
command . AddSuggestion ( & model . SuggestCommand { Suggestion : cmd2 , Description : "Add some random posts with fuzz text to current channel <Min Posts> <Max Posts> <Min Images> <Max Images>" } )
}
return false
}