github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/app/slashcommands/command_loadtest.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package slashcommands
     5  
     6  import (
     7  	"io"
     8  	"io/ioutil"
     9  	"net/http"
    10  	"path"
    11  	"regexp"
    12  	"strconv"
    13  	"strings"
    14  
    15  	"github.com/pkg/errors"
    16  
    17  	"github.com/masterhung0112/hk_server/v5/app"
    18  	"github.com/masterhung0112/hk_server/v5/app/request"
    19  	"github.com/masterhung0112/hk_server/v5/model"
    20  	"github.com/masterhung0112/hk_server/v5/shared/i18n"
    21  	"github.com/masterhung0112/hk_server/v5/shared/mlog"
    22  	"github.com/masterhung0112/hk_server/v5/utils"
    23  )
    24  
    25  var usage = `Mattermost testing commands to help configure the system
    26  
    27  	COMMANDS:
    28  
    29  	Setup - Creates a testing environment in current team.
    30  		/test setup [teams] [fuzz] <Num Channels> <Num Users> <NumPosts>
    31  
    32  		Example:
    33  		/test setup teams fuzz 10 20 50
    34  
    35  	Users - Add a specified number of random users with fuzz text to current team.
    36  		/test users [fuzz] <Min Users> <Max Users>
    37  
    38  		Example:
    39  			/test users fuzz 5 10
    40  
    41  	Channels - Add a specified number of random channels with fuzz text to current team.
    42  		/test channels [fuzz] <Min Channels> <Max Channels>
    43  
    44  		Example:
    45  			/test channels fuzz 5 10
    46  
    47  	ThreadedPost - create a large threaded post
    48          /test threaded_post
    49  
    50  	Posts - Add some random posts with fuzz text to current channel.
    51  		/test posts [fuzz] <Min Posts> <Max Posts> <Max Images>
    52  
    53  		Example:
    54  			/test posts fuzz 5 10 3
    55  
    56  	Post - Add post to a channel as another user.
    57  		/test post u=@username p=passwd c=~channelname t=teamname "message"
    58  
    59  		Example:
    60  			/test post u=@user-1 p=user-1 c=~town-square t=ad-1 "message"
    61  
    62  	Url - Add a post containing the text from a given url to current channel.
    63  		/test url
    64  
    65  		Example:
    66  			/test http://www.example.com/sample_file.md
    67  
    68  	Json - Add a post using the JSON file as payload to the current channel.
    69  	        /test json url
    70  
    71  		Example
    72  		/test json http://www.example.com/sample_body.json
    73  
    74  `
    75  
    76  const (
    77  	CmdTest = "test"
    78  )
    79  
    80  var (
    81  	userRE    = regexp.MustCompile(`u=@([^\s]+)`)
    82  	passwdRE  = regexp.MustCompile(`p=([^\s]+)`)
    83  	teamRE    = regexp.MustCompile(`t=([^\s]+)`)
    84  	channelRE = regexp.MustCompile(`c=~([^\s]+)`)
    85  	messageRE = regexp.MustCompile(`"(.*)"`)
    86  )
    87  
    88  type LoadTestProvider struct {
    89  }
    90  
    91  func init() {
    92  	app.RegisterCommandProvider(&LoadTestProvider{})
    93  }
    94  
    95  func (*LoadTestProvider) GetTrigger() string {
    96  	return CmdTest
    97  }
    98  
    99  func (*LoadTestProvider) GetCommand(a *app.App, T i18n.TranslateFunc) *model.Command {
   100  	if !*a.Config().ServiceSettings.EnableTesting {
   101  		return nil
   102  	}
   103  	return &model.Command{
   104  		Trigger:          CmdTest,
   105  		AutoComplete:     false,
   106  		AutoCompleteDesc: "Debug Load Testing",
   107  		AutoCompleteHint: "help",
   108  		DisplayName:      "test",
   109  	}
   110  }
   111  
   112  func (lt *LoadTestProvider) DoCommand(a *app.App, c *request.Context, args *model.CommandArgs, message string) *model.CommandResponse {
   113  	commandResponse, err := lt.doCommand(a, c, args, message)
   114  	if err != nil {
   115  		mlog.Error("failed command /"+CmdTest, mlog.Err(err))
   116  	}
   117  
   118  	return commandResponse
   119  }
   120  
   121  func (lt *LoadTestProvider) doCommand(a *app.App, c *request.Context, args *model.CommandArgs, message string) (*model.CommandResponse, error) {
   122  	//This command is only available when EnableTesting is true
   123  	if !*a.Config().ServiceSettings.EnableTesting {
   124  		return &model.CommandResponse{}, nil
   125  	}
   126  
   127  	if strings.HasPrefix(message, "setup") {
   128  		return lt.SetupCommand(a, c, args, message)
   129  	}
   130  
   131  	if strings.HasPrefix(message, "users") {
   132  		return lt.UsersCommand(a, c, args, message)
   133  	}
   134  
   135  	if strings.HasPrefix(message, "activate_user") {
   136  		return lt.ActivateUserCommand(a, c, args, message)
   137  	}
   138  
   139  	if strings.HasPrefix(message, "deactivate_user") {
   140  		return lt.DeActivateUserCommand(a, c, args, message)
   141  	}
   142  
   143  	if strings.HasPrefix(message, "channels") {
   144  		return lt.ChannelsCommand(a, c, args, message)
   145  	}
   146  
   147  	if strings.HasPrefix(message, "posts") {
   148  		return lt.PostsCommand(a, c, args, message)
   149  	}
   150  
   151  	if strings.HasPrefix(message, "post") {
   152  		return lt.PostCommand(a, args, message)
   153  	}
   154  
   155  	if strings.HasPrefix(message, "threaded_post") {
   156  		return lt.ThreadedPostCommand(a, c, args, message)
   157  	}
   158  
   159  	if strings.HasPrefix(message, "url") {
   160  		return lt.UrlCommand(a, c, args, message)
   161  	}
   162  
   163  	if strings.HasPrefix(message, "json") {
   164  		return lt.JsonCommand(a, c, args, message)
   165  	}
   166  
   167  	return lt.HelpCommand(args, message), nil
   168  }
   169  
   170  func (*LoadTestProvider) HelpCommand(args *model.CommandArgs, message string) *model.CommandResponse {
   171  	return &model.CommandResponse{Text: usage, ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}
   172  }
   173  
   174  func (*LoadTestProvider) SetupCommand(a *app.App, c *request.Context, args *model.CommandArgs, message string) (*model.CommandResponse, error) {
   175  	tokens := strings.Fields(strings.TrimPrefix(message, "setup"))
   176  	doTeams := contains(tokens, "teams")
   177  	doFuzz := contains(tokens, "fuzz")
   178  
   179  	numArgs := 0
   180  	if doTeams {
   181  		numArgs++
   182  	}
   183  	if doFuzz {
   184  		numArgs++
   185  	}
   186  
   187  	var numTeams int
   188  	var numChannels int
   189  	var numUsers int
   190  	var numPosts int
   191  
   192  	// Defaults
   193  	numTeams = 10
   194  	numChannels = 10
   195  	numUsers = 10
   196  	numPosts = 10
   197  
   198  	if doTeams {
   199  		if (len(tokens) - numArgs) >= 4 {
   200  			numTeams, _ = strconv.Atoi(tokens[numArgs+0])
   201  			numChannels, _ = strconv.Atoi(tokens[numArgs+1])
   202  			numUsers, _ = strconv.Atoi(tokens[numArgs+2])
   203  			numPosts, _ = strconv.Atoi(tokens[numArgs+3])
   204  		}
   205  	} else {
   206  		if (len(tokens) - numArgs) >= 3 {
   207  			numChannels, _ = strconv.Atoi(tokens[numArgs+0])
   208  			numUsers, _ = strconv.Atoi(tokens[numArgs+1])
   209  			numPosts, _ = strconv.Atoi(tokens[numArgs+2])
   210  		}
   211  	}
   212  	client := model.NewAPIv4Client(args.SiteURL)
   213  
   214  	if doTeams {
   215  		if err := CreateBasicUser(a, client); err != nil {
   216  			return &model.CommandResponse{Text: "Failed to create testing environment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, err
   217  		}
   218  		_, resp := client.Login(BTestUserEmail, BTestUserPassword)
   219  		if resp.Error != nil {
   220  			return &model.CommandResponse{Text: "Failed to create testing environment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, resp.Error
   221  		}
   222  		environment, err := CreateTestEnvironmentWithTeams(
   223  			a,
   224  			c,
   225  			client,
   226  			utils.Range{Begin: numTeams, End: numTeams},
   227  			utils.Range{Begin: numChannels, End: numChannels},
   228  			utils.Range{Begin: numUsers, End: numUsers},
   229  			utils.Range{Begin: numPosts, End: numPosts},
   230  			doFuzz)
   231  		if err != nil {
   232  			return &model.CommandResponse{Text: "Failed to create testing environment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, err
   233  		}
   234  
   235  		mlog.Info("Testing environment created")
   236  		for i := 0; i < len(environment.Teams); i++ {
   237  			mlog.Info("Team Created: " + environment.Teams[i].Name)
   238  			mlog.Info("\t User to login: " + environment.Environments[i].Users[0].Email + ", " + UserPassword)
   239  		}
   240  	} else {
   241  		team, err := a.Srv().Store.Team().Get(args.TeamId)
   242  		if err != nil {
   243  			return &model.CommandResponse{Text: "Failed to create testing environment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, err
   244  		}
   245  
   246  		CreateTestEnvironmentInTeam(
   247  			a,
   248  			c,
   249  			client,
   250  			team,
   251  			utils.Range{Begin: numChannels, End: numChannels},
   252  			utils.Range{Begin: numUsers, End: numUsers},
   253  			utils.Range{Begin: numPosts, End: numPosts},
   254  			doFuzz)
   255  	}
   256  
   257  	return &model.CommandResponse{Text: "Created environment", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, nil
   258  }
   259  
   260  func (*LoadTestProvider) ActivateUserCommand(a *app.App, c *request.Context, args *model.CommandArgs, message string) (*model.CommandResponse, error) {
   261  	user_id := strings.TrimSpace(strings.TrimPrefix(message, "activate_user"))
   262  	if err := a.UpdateUserActive(c, user_id, true); err != nil {
   263  		return &model.CommandResponse{Text: "Failed to activate user", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, err
   264  	}
   265  
   266  	return &model.CommandResponse{Text: "Activated user", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, nil
   267  }
   268  
   269  func (*LoadTestProvider) DeActivateUserCommand(a *app.App, c *request.Context, args *model.CommandArgs, message string) (*model.CommandResponse, error) {
   270  	user_id := strings.TrimSpace(strings.TrimPrefix(message, "deactivate_user"))
   271  	if err := a.UpdateUserActive(c, user_id, false); err != nil {
   272  		return &model.CommandResponse{Text: "Failed to deactivate user", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, err
   273  	}
   274  
   275  	return &model.CommandResponse{Text: "DeActivated user", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, nil
   276  }
   277  
   278  func (*LoadTestProvider) UsersCommand(a *app.App, c *request.Context, args *model.CommandArgs, message string) (*model.CommandResponse, error) {
   279  	cmd := strings.TrimSpace(strings.TrimPrefix(message, "users"))
   280  
   281  	doFuzz := false
   282  	if strings.Index(cmd, "fuzz") == 0 {
   283  		doFuzz = true
   284  		cmd = strings.TrimSpace(strings.TrimPrefix(cmd, "fuzz"))
   285  	}
   286  
   287  	usersr, ok := parseRange(cmd, "")
   288  	if !ok {
   289  		usersr = utils.Range{Begin: 2, End: 5}
   290  	}
   291  
   292  	team, err := a.Srv().Store.Team().Get(args.TeamId)
   293  	if err != nil {
   294  		return &model.CommandResponse{Text: "Failed to add users", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, err
   295  	}
   296  
   297  	client := model.NewAPIv4Client(args.SiteURL)
   298  	userCreator := NewAutoUserCreator(a, client, team)
   299  	userCreator.Fuzzy = doFuzz
   300  	if _, err := userCreator.CreateTestUsers(c, usersr); err != nil {
   301  		return &model.CommandResponse{Text: "Failed to add users: " + err.Error(), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, err
   302  	}
   303  
   304  	return &model.CommandResponse{Text: "Added users", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, nil
   305  }
   306  
   307  func (*LoadTestProvider) ChannelsCommand(a *app.App, c *request.Context, args *model.CommandArgs, message string) (*model.CommandResponse, error) {
   308  	cmd := strings.TrimSpace(strings.TrimPrefix(message, "channels"))
   309  
   310  	doFuzz := false
   311  	if strings.Index(cmd, "fuzz") == 0 {
   312  		doFuzz = true
   313  		cmd = strings.TrimSpace(strings.TrimPrefix(cmd, "fuzz"))
   314  	}
   315  
   316  	channelsr, ok := parseRange(cmd, "")
   317  	if !ok {
   318  		channelsr = utils.Range{Begin: 2, End: 5}
   319  	}
   320  
   321  	team, err := a.Srv().Store.Team().Get(args.TeamId)
   322  	if err != nil {
   323  		return &model.CommandResponse{Text: "Failed to add channels", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, err
   324  	}
   325  
   326  	channelCreator := NewAutoChannelCreator(a, team, args.UserId)
   327  	channelCreator.Fuzzy = doFuzz
   328  	if _, err := channelCreator.CreateTestChannels(c, channelsr); err != nil {
   329  		return &model.CommandResponse{Text: "Failed to create test channels: " + err.Error(), ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, err
   330  	}
   331  
   332  	return &model.CommandResponse{Text: "Added channels", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, nil
   333  }
   334  
   335  func (*LoadTestProvider) ThreadedPostCommand(a *app.App, c *request.Context, args *model.CommandArgs, message string) (*model.CommandResponse, error) {
   336  	var usernames []string
   337  	options := &model.UserGetOptions{InTeamId: args.TeamId, Page: 0, PerPage: 1000}
   338  	if profileUsers, err := a.Srv().Store.User().GetProfiles(options); err == nil {
   339  		usernames = make([]string, len(profileUsers))
   340  		i := 0
   341  		for _, userprof := range profileUsers {
   342  			usernames[i] = userprof.Username
   343  			i++
   344  		}
   345  	}
   346  
   347  	testPoster := NewAutoPostCreator(a, args.ChannelId, args.UserId)
   348  	testPoster.Fuzzy = true
   349  	testPoster.Users = usernames
   350  	rpost, err2 := testPoster.CreateRandomPost(c)
   351  	if err2 != nil {
   352  		return &model.CommandResponse{Text: "Failed to create a post", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, err2
   353  	}
   354  	for i := 0; i < 1000; i++ {
   355  		testPoster.CreateRandomPostNested(c, rpost.Id, rpost.Id)
   356  	}
   357  
   358  	return &model.CommandResponse{Text: "Added threaded post", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, nil
   359  }
   360  
   361  func (*LoadTestProvider) PostsCommand(a *app.App, c *request.Context, args *model.CommandArgs, message string) (*model.CommandResponse, error) {
   362  	cmd := strings.TrimSpace(strings.TrimPrefix(message, "posts"))
   363  
   364  	doFuzz := false
   365  	if strings.Index(cmd, "fuzz") == 0 {
   366  		doFuzz = true
   367  		cmd = strings.TrimSpace(strings.TrimPrefix(cmd, "fuzz"))
   368  	}
   369  
   370  	postsr, ok := parseRange(cmd, "")
   371  	if !ok {
   372  		postsr = utils.Range{Begin: 20, End: 30}
   373  	}
   374  
   375  	tokens := strings.Fields(cmd)
   376  	rimages := utils.Range{Begin: 0, End: 0}
   377  	if len(tokens) >= 3 {
   378  		if numImages, err := strconv.Atoi(tokens[2]); err == nil {
   379  			rimages = utils.Range{Begin: numImages, End: numImages}
   380  		}
   381  	}
   382  
   383  	var usernames []string
   384  	options := &model.UserGetOptions{InTeamId: args.TeamId, Page: 0, PerPage: 1000}
   385  	if profileUsers, err := a.Srv().Store.User().GetProfiles(options); err == nil {
   386  		usernames = make([]string, len(profileUsers))
   387  		i := 0
   388  		for _, userprof := range profileUsers {
   389  			usernames[i] = userprof.Username
   390  			i++
   391  		}
   392  	}
   393  
   394  	testPoster := NewAutoPostCreator(a, args.ChannelId, args.UserId)
   395  	testPoster.Fuzzy = doFuzz
   396  	testPoster.Users = usernames
   397  
   398  	numImages := utils.RandIntFromRange(rimages)
   399  	numPosts := utils.RandIntFromRange(postsr)
   400  	for i := 0; i < numPosts; i++ {
   401  		testPoster.HasImage = (i < numImages)
   402  		_, err := testPoster.CreateRandomPost(c)
   403  		if err != nil {
   404  			return &model.CommandResponse{Text: "Failed to add posts", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, err
   405  		}
   406  
   407  	}
   408  
   409  	return &model.CommandResponse{Text: "Added posts", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, nil
   410  }
   411  
   412  func getMatch(re *regexp.Regexp, text string) string {
   413  	if match := re.FindStringSubmatch(text); match != nil {
   414  		return match[1]
   415  	}
   416  
   417  	return ""
   418  }
   419  
   420  func (*LoadTestProvider) PostCommand(a *app.App, args *model.CommandArgs, message string) (*model.CommandResponse, error) {
   421  	textMessage := getMatch(messageRE, message)
   422  	if textMessage == "" {
   423  		return &model.CommandResponse{Text: "No message to post", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, nil
   424  	}
   425  
   426  	teamName := getMatch(teamRE, message)
   427  	team, err := a.GetTeamByName(teamName)
   428  	if err != nil {
   429  		return &model.CommandResponse{Text: "Failed to get a team", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, err
   430  	}
   431  
   432  	channelName := getMatch(channelRE, message)
   433  	channel, err := a.GetChannelByName(channelName, team.Id, true)
   434  	if err != nil {
   435  		return &model.CommandResponse{Text: "Failed to get a channel", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, err
   436  	}
   437  
   438  	passwd := getMatch(passwdRE, message)
   439  	username := getMatch(userRE, message)
   440  	user, err := a.GetUserByUsername(username)
   441  	if err != nil {
   442  		return &model.CommandResponse{Text: "Failed to get a user", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, err
   443  	}
   444  
   445  	client := model.NewAPIv4Client(args.SiteURL)
   446  	_, resp := client.LoginById(user.Id, passwd)
   447  	if resp.Error != nil {
   448  		return &model.CommandResponse{Text: "Failed to login a user", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, resp.Error
   449  	}
   450  
   451  	post := &model.Post{
   452  		ChannelId: channel.Id,
   453  		Message:   textMessage,
   454  	}
   455  	_, resp = client.CreatePost(post)
   456  	if resp.Error != nil {
   457  		return &model.CommandResponse{Text: "Failed to create a post", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, resp.Error
   458  	}
   459  
   460  	return &model.CommandResponse{Text: "Added a post to " + channel.DisplayName, ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, nil
   461  }
   462  
   463  func (*LoadTestProvider) UrlCommand(a *app.App, c *request.Context, args *model.CommandArgs, message string) (*model.CommandResponse, error) {
   464  	url := strings.TrimSpace(strings.TrimPrefix(message, "url"))
   465  	if url == "" {
   466  		return &model.CommandResponse{Text: "Command must contain a url", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, nil
   467  	}
   468  
   469  	// provide a shortcut to easily access tests stored in doc/developer/tests
   470  	if !strings.HasPrefix(url, "http") {
   471  		url = "https://raw.githubusercontent.com/mattermost/hk_server/master/tests/" + url
   472  
   473  		if path.Ext(url) == "" {
   474  			url += ".md"
   475  		}
   476  	}
   477  
   478  	r, err := http.Get(url)
   479  	if err != nil {
   480  		return &model.CommandResponse{Text: "Unable to get file", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, err
   481  	}
   482  	defer func() {
   483  		io.Copy(ioutil.Discard, r.Body)
   484  		r.Body.Close()
   485  	}()
   486  
   487  	if r.StatusCode > 400 {
   488  		return &model.CommandResponse{Text: "Unable to get file", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, errors.Errorf("unexpected status code %d", r.StatusCode)
   489  	}
   490  
   491  	bytes := make([]byte, 4000)
   492  
   493  	// break contents into 4000 byte posts
   494  	for {
   495  		length, err := r.Body.Read(bytes)
   496  		if err != nil && err != io.EOF {
   497  			return &model.CommandResponse{Text: "Encountered error reading file", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, err
   498  		}
   499  
   500  		if length == 0 {
   501  			break
   502  		}
   503  
   504  		post := &model.Post{}
   505  		post.Message = string(bytes[:length])
   506  		post.ChannelId = args.ChannelId
   507  		post.UserId = args.UserId
   508  
   509  		if _, err := a.CreatePostMissingChannel(c, post, false); err != nil {
   510  			return &model.CommandResponse{Text: "Unable to create post", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, err
   511  		}
   512  	}
   513  
   514  	return &model.CommandResponse{Text: "Loaded data", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, nil
   515  }
   516  
   517  func (*LoadTestProvider) JsonCommand(a *app.App, c *request.Context, args *model.CommandArgs, message string) (*model.CommandResponse, error) {
   518  	url := strings.TrimSpace(strings.TrimPrefix(message, "json"))
   519  	if url == "" {
   520  		return &model.CommandResponse{Text: "Command must contain a url", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, nil
   521  	}
   522  
   523  	// provide a shortcut to easily access tests stored in doc/developer/tests
   524  	if !strings.HasPrefix(url, "http") {
   525  		url = "https://raw.githubusercontent.com/mattermost/hk_server/master/tests/" + url
   526  
   527  		if path.Ext(url) == "" {
   528  			url += ".json"
   529  		}
   530  	}
   531  
   532  	r, err := http.Get(url)
   533  	if err != nil {
   534  		return &model.CommandResponse{Text: "Unable to get file", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, err
   535  	}
   536  
   537  	if r.StatusCode > 400 {
   538  		return &model.CommandResponse{Text: "Unable to get file", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, errors.Errorf("unexpected status code %d", r.StatusCode)
   539  	}
   540  	defer func() {
   541  		io.Copy(ioutil.Discard, r.Body)
   542  		r.Body.Close()
   543  	}()
   544  
   545  	post := model.PostFromJson(r.Body)
   546  	if post == nil {
   547  		return &model.CommandResponse{Text: "Unable to decode post", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, errors.Errorf("could not decode post from json")
   548  	}
   549  	post.ChannelId = args.ChannelId
   550  	post.UserId = args.UserId
   551  	if post.Message == "" {
   552  		post.Message = message
   553  	}
   554  
   555  	if _, err := a.CreatePostMissingChannel(c, post, false); err != nil {
   556  		return &model.CommandResponse{Text: "Unable to create post", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, err
   557  	}
   558  
   559  	return &model.CommandResponse{Text: "Loaded data", ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL}, nil
   560  }
   561  
   562  func parseRange(command string, cmd string) (utils.Range, bool) {
   563  	tokens := strings.Fields(strings.TrimPrefix(command, cmd))
   564  	var begin int
   565  	var end int
   566  	var err1 error
   567  	var err2 error
   568  	switch {
   569  	case len(tokens) == 1:
   570  		begin, err1 = strconv.Atoi(tokens[0])
   571  		end = begin
   572  		if err1 != nil {
   573  			return utils.Range{Begin: 0, End: 0}, false
   574  		}
   575  	case len(tokens) >= 2:
   576  		begin, err1 = strconv.Atoi(tokens[0])
   577  		end, err2 = strconv.Atoi(tokens[1])
   578  		if err1 != nil || err2 != nil {
   579  			return utils.Range{Begin: 0, End: 0}, false
   580  		}
   581  	default:
   582  		return utils.Range{Begin: 0, End: 0}, false
   583  	}
   584  	return utils.Range{Begin: begin, End: end}, true
   585  }
   586  
   587  func contains(items []string, token string) bool {
   588  	for _, elem := range items {
   589  		if elem == token {
   590  			return true
   591  		}
   592  	}
   593  	return false
   594  }