github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/app/command.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package app 5 6 import ( 7 "errors" 8 "io" 9 "io/ioutil" 10 "net/http" 11 "net/url" 12 "strings" 13 "sync" 14 "unicode" 15 16 goi18n "github.com/mattermost/go-i18n/i18n" 17 "github.com/mattermost/mattermost-server/v5/mlog" 18 "github.com/mattermost/mattermost-server/v5/model" 19 "github.com/mattermost/mattermost-server/v5/store" 20 "github.com/mattermost/mattermost-server/v5/utils" 21 ) 22 23 type CommandProvider interface { 24 GetTrigger() string 25 GetCommand(a *App, T goi18n.TranslateFunc) *model.Command 26 DoCommand(a *App, args *model.CommandArgs, message string) *model.CommandResponse 27 } 28 29 var commandProviders = make(map[string]CommandProvider) 30 31 func RegisterCommandProvider(newProvider CommandProvider) { 32 commandProviders[newProvider.GetTrigger()] = newProvider 33 } 34 35 func GetCommandProvider(name string) CommandProvider { 36 provider, ok := commandProviders[name] 37 if ok { 38 return provider 39 } 40 41 return nil 42 } 43 44 // @openTracingParams teamId, skipSlackParsing 45 func (a *App) CreateCommandPost(post *model.Post, teamId string, response *model.CommandResponse, skipSlackParsing bool) (*model.Post, *model.AppError) { 46 if skipSlackParsing { 47 post.Message = response.Text 48 } else { 49 post.Message = model.ParseSlackLinksToMarkdown(response.Text) 50 } 51 52 post.CreateAt = model.GetMillis() 53 54 if strings.HasPrefix(post.Type, model.POST_SYSTEM_MESSAGE_PREFIX) { 55 err := model.NewAppError("CreateCommandPost", "api.context.invalid_param.app_error", map[string]interface{}{"Name": "post.type"}, "", http.StatusBadRequest) 56 return nil, err 57 } 58 59 if response.Attachments != nil { 60 model.ParseSlackAttachment(post, response.Attachments) 61 } 62 63 if response.ResponseType == model.COMMAND_RESPONSE_TYPE_IN_CHANNEL { 64 return a.CreatePostMissingChannel(post, true) 65 } 66 67 if (response.ResponseType == "" || response.ResponseType == model.COMMAND_RESPONSE_TYPE_EPHEMERAL) && (response.Text != "" || response.Attachments != nil) { 68 post.ParentId = "" 69 a.SendEphemeralPost(post.UserId, post) 70 } 71 72 return post, nil 73 } 74 75 // @openTracingParams teamId 76 // previous ListCommands now ListAutocompleteCommands 77 func (a *App) ListAutocompleteCommands(teamId string, T goi18n.TranslateFunc) ([]*model.Command, *model.AppError) { 78 commands := make([]*model.Command, 0, 32) 79 seen := make(map[string]bool) 80 81 for _, cmd := range a.PluginCommandsForTeam(teamId) { 82 if cmd.AutoComplete && !seen[cmd.Trigger] { 83 seen[cmd.Trigger] = true 84 commands = append(commands, cmd) 85 } 86 } 87 88 if *a.Config().ServiceSettings.EnableCommands { 89 teamCmds, err := a.Srv().Store.Command().GetByTeam(teamId) 90 if err != nil { 91 return nil, model.NewAppError("ListAutocompleteCommands", "app.command.listautocompletecommands.internal_error", nil, err.Error(), http.StatusInternalServerError) 92 } 93 94 for _, cmd := range teamCmds { 95 if cmd.AutoComplete && !seen[cmd.Trigger] { 96 cmd.Sanitize() 97 seen[cmd.Trigger] = true 98 commands = append(commands, cmd) 99 } 100 } 101 } 102 103 for _, value := range commandProviders { 104 if cmd := value.GetCommand(a, T); cmd != nil { 105 cpy := *cmd 106 if cpy.AutoComplete && !seen[cpy.Trigger] { 107 cpy.Sanitize() 108 seen[cpy.Trigger] = true 109 commands = append(commands, &cpy) 110 } 111 } 112 } 113 114 return commands, nil 115 } 116 117 func (a *App) ListTeamCommands(teamId string) ([]*model.Command, *model.AppError) { 118 if !*a.Config().ServiceSettings.EnableCommands { 119 return nil, model.NewAppError("ListTeamCommands", "api.command.disabled.app_error", nil, "", http.StatusNotImplemented) 120 } 121 122 teamCmds, err := a.Srv().Store.Command().GetByTeam(teamId) 123 if err != nil { 124 return nil, model.NewAppError("ListTeamCommands", "app.command.listteamcommands.internal_error", nil, err.Error(), http.StatusInternalServerError) 125 } 126 127 return teamCmds, nil 128 } 129 130 func (a *App) ListAllCommands(teamId string, T goi18n.TranslateFunc) ([]*model.Command, *model.AppError) { 131 commands := make([]*model.Command, 0, 32) 132 seen := make(map[string]bool) 133 for _, value := range commandProviders { 134 if cmd := value.GetCommand(a, T); cmd != nil { 135 cpy := *cmd 136 if cpy.AutoComplete && !seen[cpy.Trigger] { 137 cpy.Sanitize() 138 seen[cpy.Trigger] = true 139 commands = append(commands, &cpy) 140 } 141 } 142 } 143 144 for _, cmd := range a.PluginCommandsForTeam(teamId) { 145 if !seen[cmd.Trigger] { 146 seen[cmd.Trigger] = true 147 commands = append(commands, cmd) 148 } 149 } 150 151 if *a.Config().ServiceSettings.EnableCommands { 152 teamCmds, err := a.Srv().Store.Command().GetByTeam(teamId) 153 if err != nil { 154 return nil, model.NewAppError("ListAllCommands", "app.command.listallcommands.internal_error", nil, err.Error(), http.StatusInternalServerError) 155 } 156 for _, cmd := range teamCmds { 157 if !seen[cmd.Trigger] { 158 cmd.Sanitize() 159 seen[cmd.Trigger] = true 160 commands = append(commands, cmd) 161 } 162 } 163 } 164 165 return commands, nil 166 } 167 168 // @openTracingParams args 169 func (a *App) ExecuteCommand(args *model.CommandArgs) (*model.CommandResponse, *model.AppError) { 170 trigger := "" 171 message := "" 172 index := strings.IndexFunc(args.Command, unicode.IsSpace) 173 if index != -1 { 174 trigger = args.Command[:index] 175 message = args.Command[index+1:] 176 } else { 177 trigger = args.Command 178 } 179 trigger = strings.ToLower(trigger) 180 if !strings.HasPrefix(trigger, "/") { 181 return nil, model.NewAppError("command", "api.command.execute_command.format.app_error", map[string]interface{}{"Trigger": trigger}, "", http.StatusBadRequest) 182 } 183 trigger = strings.TrimPrefix(trigger, "/") 184 185 clientTriggerId, triggerId, appErr := model.GenerateTriggerId(args.UserId, a.AsymmetricSigningKey()) 186 if appErr != nil { 187 mlog.Error("error occurred in generating trigger Id for a user ", mlog.Err(appErr)) 188 } 189 190 args.TriggerId = triggerId 191 192 // Plugins can override built in and custom commands 193 cmd, response, appErr := a.tryExecutePluginCommand(args) 194 if appErr != nil { 195 return nil, appErr 196 } else if cmd != nil && response != nil { 197 response.TriggerId = clientTriggerId 198 return a.HandleCommandResponse(cmd, args, response, true) 199 } 200 201 // Custom commands can override built ins 202 cmd, response, appErr = a.tryExecuteCustomCommand(args, trigger, message) 203 if appErr != nil { 204 return nil, appErr 205 } else if cmd != nil && response != nil { 206 response.TriggerId = clientTriggerId 207 return a.HandleCommandResponse(cmd, args, response, false) 208 } 209 210 cmd, response = a.tryExecuteBuiltInCommand(args, trigger, message) 211 if cmd != nil && response != nil { 212 return a.HandleCommandResponse(cmd, args, response, true) 213 } 214 215 return nil, model.NewAppError("command", "api.command.execute_command.not_found.app_error", map[string]interface{}{"Trigger": trigger}, "", http.StatusNotFound) 216 } 217 218 // mentionsToTeamMembers returns all the @ mentions found in message that 219 // belong to users in the specified team, linking them to their users 220 func (a *App) mentionsToTeamMembers(message, teamId string) model.UserMentionMap { 221 type mentionMapItem struct { 222 Name string 223 Id string 224 } 225 226 possibleMentions := model.PossibleAtMentions(message) 227 mentionChan := make(chan *mentionMapItem, len(possibleMentions)) 228 229 var wg sync.WaitGroup 230 for _, mention := range possibleMentions { 231 wg.Add(1) 232 go func(mention string) { 233 defer wg.Done() 234 user, err := a.Srv().Store.User().GetByUsername(mention) 235 236 if err != nil && err.StatusCode != http.StatusNotFound { 237 mlog.Warn("Failed to retrieve user @"+mention, mlog.Err(err)) 238 return 239 } 240 241 // If it's a http.StatusNotFound error, check for usernames in substrings 242 // without trailing punctuation 243 if err != nil { 244 trimmed, ok := model.TrimUsernameSpecialChar(mention) 245 for ; ok; trimmed, ok = model.TrimUsernameSpecialChar(trimmed) { 246 userFromTrimmed, userErr := a.Srv().Store.User().GetByUsername(trimmed) 247 if userErr != nil && err.StatusCode != http.StatusNotFound { 248 return 249 } 250 251 if userErr != nil { 252 continue 253 } 254 255 _, err = a.GetTeamMember(teamId, userFromTrimmed.Id) 256 if err != nil { 257 // The user is not in the team, so we should ignore it 258 return 259 } 260 261 mentionChan <- &mentionMapItem{trimmed, userFromTrimmed.Id} 262 return 263 } 264 265 return 266 } 267 268 _, err = a.GetTeamMember(teamId, user.Id) 269 if err != nil { 270 // The user is not in the team, so we should ignore it 271 return 272 } 273 274 mentionChan <- &mentionMapItem{mention, user.Id} 275 }(mention) 276 } 277 278 wg.Wait() 279 close(mentionChan) 280 281 atMentionMap := make(model.UserMentionMap) 282 for mention := range mentionChan { 283 atMentionMap[mention.Name] = mention.Id 284 } 285 286 return atMentionMap 287 } 288 289 // mentionsToPublicChannels returns all the mentions to public channels, 290 // linking them to their channels 291 func (a *App) mentionsToPublicChannels(message, teamId string) model.ChannelMentionMap { 292 type mentionMapItem struct { 293 Name string 294 Id string 295 } 296 297 channelMentions := model.ChannelMentions(message) 298 mentionChan := make(chan *mentionMapItem, len(channelMentions)) 299 300 var wg sync.WaitGroup 301 for _, channelName := range channelMentions { 302 wg.Add(1) 303 go func(channelName string) { 304 defer wg.Done() 305 channel, err := a.GetChannelByName(channelName, teamId, false) 306 if err != nil { 307 return 308 } 309 310 if !channel.IsOpen() { 311 return 312 } 313 314 mentionChan <- &mentionMapItem{channelName, channel.Id} 315 }(channelName) 316 } 317 318 wg.Wait() 319 close(mentionChan) 320 321 channelMentionMap := make(model.ChannelMentionMap) 322 for mention := range mentionChan { 323 channelMentionMap[mention.Name] = mention.Id 324 } 325 326 return channelMentionMap 327 } 328 329 // tryExecuteBuiltInCommand attempts to run a built in command based on the given arguments. If no such command can be 330 // found, returns nil for all arguments. 331 func (a *App) tryExecuteBuiltInCommand(args *model.CommandArgs, trigger string, message string) (*model.Command, *model.CommandResponse) { 332 provider := GetCommandProvider(trigger) 333 if provider == nil { 334 return nil, nil 335 } 336 337 cmd := provider.GetCommand(a, args.T) 338 if cmd == nil { 339 return nil, nil 340 } 341 342 return cmd, provider.DoCommand(a, args, message) 343 } 344 345 // tryExecuteCustomCommand attempts to run a custom command based on the given arguments. If no such command can be 346 // found, returns nil for all arguments. 347 func (a *App) tryExecuteCustomCommand(args *model.CommandArgs, trigger string, message string) (*model.Command, *model.CommandResponse, *model.AppError) { 348 // Handle custom commands 349 if !*a.Config().ServiceSettings.EnableCommands { 350 return nil, nil, model.NewAppError("ExecuteCommand", "api.command.disabled.app_error", nil, "", http.StatusNotImplemented) 351 } 352 353 chanChan := make(chan store.StoreResult, 1) 354 go func() { 355 channel, err := a.Srv().Store.Channel().Get(args.ChannelId, true) 356 chanChan <- store.StoreResult{Data: channel, NErr: err} 357 close(chanChan) 358 }() 359 360 teamChan := make(chan store.StoreResult, 1) 361 go func() { 362 team, err := a.Srv().Store.Team().Get(args.TeamId) 363 teamChan <- store.StoreResult{Data: team, Err: err} 364 close(teamChan) 365 }() 366 367 userChan := make(chan store.StoreResult, 1) 368 go func() { 369 user, err := a.Srv().Store.User().Get(args.UserId) 370 userChan <- store.StoreResult{Data: user, Err: err} 371 close(userChan) 372 }() 373 374 teamCmds, err := a.Srv().Store.Command().GetByTeam(args.TeamId) 375 if err != nil { 376 return nil, nil, model.NewAppError("tryExecuteCustomCommand", "app.command.tryexecutecustomcommand.internal_error", nil, err.Error(), http.StatusInternalServerError) 377 } 378 379 tr := <-teamChan 380 if tr.Err != nil { 381 return nil, nil, tr.Err 382 } 383 team := tr.Data.(*model.Team) 384 385 ur := <-userChan 386 if ur.Err != nil { 387 return nil, nil, ur.Err 388 } 389 user := ur.Data.(*model.User) 390 391 cr := <-chanChan 392 if cr.NErr != nil { 393 var nfErr *store.ErrNotFound 394 switch { 395 case errors.As(cr.NErr, &nfErr): 396 return nil, nil, model.NewAppError("tryExecuteCustomCommand", "app.channel.get.existing.app_error", nil, nfErr.Error(), http.StatusNotFound) 397 default: 398 return nil, nil, model.NewAppError("tryExecuteCustomCommand", "app.channel.get.find.app_error", nil, cr.NErr.Error(), http.StatusInternalServerError) 399 } 400 } 401 channel := cr.Data.(*model.Channel) 402 403 var cmd *model.Command 404 405 for _, teamCmd := range teamCmds { 406 if trigger == teamCmd.Trigger { 407 cmd = teamCmd 408 } 409 } 410 411 if cmd == nil { 412 return nil, nil, nil 413 } 414 415 mlog.Debug("Executing command", mlog.String("command", trigger), mlog.String("user_id", args.UserId)) 416 417 p := url.Values{} 418 p.Set("token", cmd.Token) 419 420 p.Set("team_id", cmd.TeamId) 421 p.Set("team_domain", team.Name) 422 423 p.Set("channel_id", args.ChannelId) 424 p.Set("channel_name", channel.Name) 425 426 p.Set("user_id", args.UserId) 427 p.Set("user_name", user.Username) 428 429 p.Set("command", "/"+trigger) 430 p.Set("text", message) 431 432 p.Set("trigger_id", args.TriggerId) 433 434 userMentionMap := a.mentionsToTeamMembers(message, team.Id) 435 for key, values := range userMentionMap.ToURLValues() { 436 p[key] = values 437 } 438 439 channelMentionMap := a.mentionsToPublicChannels(message, team.Id) 440 for key, values := range channelMentionMap.ToURLValues() { 441 p[key] = values 442 } 443 444 hook, appErr := a.CreateCommandWebhook(cmd.Id, args) 445 if appErr != nil { 446 return cmd, nil, model.NewAppError("command", "api.command.execute_command.failed.app_error", map[string]interface{}{"Trigger": trigger}, appErr.Error(), http.StatusInternalServerError) 447 } 448 p.Set("response_url", args.SiteURL+"/hooks/commands/"+hook.Id) 449 450 return a.doCommandRequest(cmd, p) 451 } 452 453 func (a *App) doCommandRequest(cmd *model.Command, p url.Values) (*model.Command, *model.CommandResponse, *model.AppError) { 454 // Prepare the request 455 var req *http.Request 456 var err error 457 if cmd.Method == model.COMMAND_METHOD_GET { 458 req, err = http.NewRequest(http.MethodGet, cmd.URL, nil) 459 } else { 460 req, err = http.NewRequest(http.MethodPost, cmd.URL, strings.NewReader(p.Encode())) 461 } 462 463 if err != nil { 464 return cmd, nil, model.NewAppError("command", "api.command.execute_command.failed.app_error", map[string]interface{}{"Trigger": cmd.Trigger}, err.Error(), http.StatusInternalServerError) 465 } 466 467 if cmd.Method == model.COMMAND_METHOD_GET { 468 if req.URL.RawQuery != "" { 469 req.URL.RawQuery += "&" 470 } 471 req.URL.RawQuery += p.Encode() 472 } 473 474 req.Header.Set("Accept", "application/json") 475 req.Header.Set("Authorization", "Token "+cmd.Token) 476 if cmd.Method == model.COMMAND_METHOD_POST { 477 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") 478 } 479 480 // Send the request 481 resp, err := a.HTTPService().MakeClient(false).Do(req) 482 if err != nil { 483 return cmd, nil, model.NewAppError("command", "api.command.execute_command.failed.app_error", map[string]interface{}{"Trigger": cmd.Trigger}, err.Error(), http.StatusInternalServerError) 484 } 485 486 defer resp.Body.Close() 487 488 // Handle the response 489 body := io.LimitReader(resp.Body, MaxIntegrationResponseSize) 490 491 if resp.StatusCode != http.StatusOK { 492 // Ignore the error below because the resulting string will just be the empty string if bodyBytes is nil 493 bodyBytes, _ := ioutil.ReadAll(body) 494 495 return cmd, nil, model.NewAppError("command", "api.command.execute_command.failed_resp.app_error", map[string]interface{}{"Trigger": cmd.Trigger, "Status": resp.Status}, string(bodyBytes), http.StatusInternalServerError) 496 } 497 498 response, err := model.CommandResponseFromHTTPBody(resp.Header.Get("Content-Type"), body) 499 if err != nil { 500 return cmd, nil, model.NewAppError("command", "api.command.execute_command.failed.app_error", map[string]interface{}{"Trigger": cmd.Trigger}, err.Error(), http.StatusInternalServerError) 501 } else if response == nil { 502 return cmd, nil, model.NewAppError("command", "api.command.execute_command.failed_empty.app_error", map[string]interface{}{"Trigger": cmd.Trigger}, "", http.StatusInternalServerError) 503 } 504 505 return cmd, response, nil 506 } 507 508 func (a *App) HandleCommandResponse(command *model.Command, args *model.CommandArgs, response *model.CommandResponse, builtIn bool) (*model.CommandResponse, *model.AppError) { 509 trigger := "" 510 if len(args.Command) != 0 { 511 parts := strings.Split(args.Command, " ") 512 trigger = parts[0][1:] 513 trigger = strings.ToLower(trigger) 514 } 515 516 var lastError *model.AppError 517 _, err := a.HandleCommandResponsePost(command, args, response, builtIn) 518 519 if err != nil { 520 mlog.Error("error occurred in handling command response post", mlog.Err(err)) 521 lastError = err 522 } 523 524 if response.ExtraResponses != nil { 525 for _, resp := range response.ExtraResponses { 526 _, err := a.HandleCommandResponsePost(command, args, resp, builtIn) 527 528 if err != nil { 529 mlog.Error("error occurred in handling command response post", mlog.Err(err)) 530 lastError = err 531 } 532 } 533 } 534 535 if lastError != nil { 536 return response, model.NewAppError("command", "api.command.execute_command.create_post_failed.app_error", map[string]interface{}{"Trigger": trigger}, "", http.StatusInternalServerError) 537 } 538 539 return response, nil 540 } 541 542 func (a *App) HandleCommandResponsePost(command *model.Command, args *model.CommandArgs, response *model.CommandResponse, builtIn bool) (*model.Post, *model.AppError) { 543 post := &model.Post{} 544 post.ChannelId = args.ChannelId 545 post.RootId = args.RootId 546 post.ParentId = args.ParentId 547 post.UserId = args.UserId 548 post.Type = response.Type 549 post.SetProps(response.Props) 550 551 if len(response.ChannelId) != 0 { 552 _, err := a.GetChannelMember(response.ChannelId, args.UserId) 553 if err != nil { 554 err = model.NewAppError("HandleCommandResponsePost", "api.command.command_post.forbidden.app_error", nil, err.Error(), http.StatusForbidden) 555 return nil, err 556 } 557 post.ChannelId = response.ChannelId 558 } 559 560 isBotPost := !builtIn 561 562 if *a.Config().ServiceSettings.EnablePostUsernameOverride { 563 if len(command.Username) != 0 { 564 post.AddProp("override_username", command.Username) 565 isBotPost = true 566 } else if len(response.Username) != 0 { 567 post.AddProp("override_username", response.Username) 568 isBotPost = true 569 } 570 } 571 572 if *a.Config().ServiceSettings.EnablePostIconOverride { 573 if len(command.IconURL) != 0 { 574 post.AddProp("override_icon_url", command.IconURL) 575 isBotPost = true 576 } else if len(response.IconURL) != 0 { 577 post.AddProp("override_icon_url", response.IconURL) 578 isBotPost = true 579 } else { 580 post.AddProp("override_icon_url", "") 581 } 582 } 583 584 if isBotPost { 585 post.AddProp("from_webhook", "true") 586 } 587 588 // Process Slack text replacements if the response does not contain "skip_slack_parsing": true. 589 if !response.SkipSlackParsing { 590 response.Text = a.ProcessSlackText(response.Text) 591 response.Attachments = a.ProcessSlackAttachments(response.Attachments) 592 } 593 594 if _, err := a.CreateCommandPost(post, args.TeamId, response, response.SkipSlackParsing); err != nil { 595 return post, err 596 } 597 598 return post, nil 599 } 600 601 func (a *App) CreateCommand(cmd *model.Command) (*model.Command, *model.AppError) { 602 if !*a.Config().ServiceSettings.EnableCommands { 603 return nil, model.NewAppError("CreateCommand", "api.command.disabled.app_error", nil, "", http.StatusNotImplemented) 604 } 605 606 return a.createCommand(cmd) 607 } 608 609 func (a *App) createCommand(cmd *model.Command) (*model.Command, *model.AppError) { 610 cmd.Trigger = strings.ToLower(cmd.Trigger) 611 612 teamCmds, err := a.Srv().Store.Command().GetByTeam(cmd.TeamId) 613 if err != nil { 614 return nil, model.NewAppError("CreateCommand", "app.command.createcommand.internal_error", nil, err.Error(), http.StatusInternalServerError) 615 } 616 617 for _, existingCommand := range teamCmds { 618 if cmd.Trigger == existingCommand.Trigger { 619 return nil, model.NewAppError("CreateCommand", "api.command.duplicate_trigger.app_error", nil, "", http.StatusBadRequest) 620 } 621 } 622 623 for _, builtInProvider := range commandProviders { 624 builtInCommand := builtInProvider.GetCommand(a, utils.T) 625 if builtInCommand != nil && cmd.Trigger == builtInCommand.Trigger { 626 return nil, model.NewAppError("CreateCommand", "api.command.duplicate_trigger.app_error", nil, "", http.StatusBadRequest) 627 } 628 } 629 630 command, nErr := a.Srv().Store.Command().Save(cmd) 631 if nErr != nil { 632 var appErr *model.AppError 633 switch { 634 case errors.As(nErr, &appErr): 635 return nil, appErr 636 default: 637 return nil, model.NewAppError("CreateCommand", "app.command.createcommand.internal_error", nil, nErr.Error(), http.StatusInternalServerError) 638 } 639 } 640 641 return command, nil 642 } 643 644 func (a *App) GetCommand(commandId string) (*model.Command, *model.AppError) { 645 if !*a.Config().ServiceSettings.EnableCommands { 646 return nil, model.NewAppError("GetCommand", "api.command.disabled.app_error", nil, "", http.StatusNotImplemented) 647 } 648 649 command, err := a.Srv().Store.Command().Get(commandId) 650 if err != nil { 651 var nfErr *store.ErrNotFound 652 switch { 653 case errors.As(err, &nfErr): 654 return nil, model.NewAppError("SqlCommandStore.Get", "store.sql_command.get.missing.app_error", map[string]interface{}{"command_id": commandId}, "", http.StatusNotFound) 655 default: 656 return nil, model.NewAppError("GetCommand", "app.command.getcommand.internal_error", nil, err.Error(), http.StatusInternalServerError) 657 } 658 } 659 return command, nil 660 } 661 662 func (a *App) UpdateCommand(oldCmd, updatedCmd *model.Command) (*model.Command, *model.AppError) { 663 if !*a.Config().ServiceSettings.EnableCommands { 664 return nil, model.NewAppError("UpdateCommand", "api.command.disabled.app_error", nil, "", http.StatusNotImplemented) 665 } 666 667 updatedCmd.Trigger = strings.ToLower(updatedCmd.Trigger) 668 updatedCmd.Id = oldCmd.Id 669 updatedCmd.Token = oldCmd.Token 670 updatedCmd.CreateAt = oldCmd.CreateAt 671 updatedCmd.UpdateAt = model.GetMillis() 672 updatedCmd.DeleteAt = oldCmd.DeleteAt 673 updatedCmd.CreatorId = oldCmd.CreatorId 674 updatedCmd.PluginId = oldCmd.PluginId 675 updatedCmd.TeamId = oldCmd.TeamId 676 677 command, err := a.Srv().Store.Command().Update(updatedCmd) 678 if err != nil { 679 var nfErr *store.ErrNotFound 680 var appErr *model.AppError 681 switch { 682 case errors.As(err, &nfErr): 683 return nil, model.NewAppError("SqlCommandStore.Update", "store.sql_command.update.missing.app_error", map[string]interface{}{"command_id": updatedCmd.Id}, "", http.StatusNotFound) 684 case errors.As(err, &appErr): 685 return nil, appErr 686 default: 687 return nil, model.NewAppError("UpdateCommand", "app.command.updatecommand.internal_error", nil, err.Error(), http.StatusInternalServerError) 688 } 689 } 690 691 return command, nil 692 } 693 694 func (a *App) MoveCommand(team *model.Team, command *model.Command) *model.AppError { 695 command.TeamId = team.Id 696 697 _, err := a.Srv().Store.Command().Update(command) 698 if err != nil { 699 var nfErr *store.ErrNotFound 700 var appErr *model.AppError 701 switch { 702 case errors.As(err, &nfErr): 703 return model.NewAppError("SqlCommandStore.Update", "store.sql_command.update.missing.app_error", map[string]interface{}{"command_id": command.Id}, "", http.StatusNotFound) 704 case errors.As(err, &appErr): 705 return appErr 706 default: 707 return model.NewAppError("MoveCommand", "app.command.movecommand.internal_error", nil, err.Error(), http.StatusInternalServerError) 708 } 709 } 710 711 return nil 712 } 713 714 func (a *App) RegenCommandToken(cmd *model.Command) (*model.Command, *model.AppError) { 715 if !*a.Config().ServiceSettings.EnableCommands { 716 return nil, model.NewAppError("RegenCommandToken", "api.command.disabled.app_error", nil, "", http.StatusNotImplemented) 717 } 718 719 cmd.Token = model.NewId() 720 721 command, err := a.Srv().Store.Command().Update(cmd) 722 if err != nil { 723 var nfErr *store.ErrNotFound 724 var appErr *model.AppError 725 switch { 726 case errors.As(err, &nfErr): 727 return nil, model.NewAppError("SqlCommandStore.Update", "store.sql_command.update.missing.app_error", map[string]interface{}{"command_id": cmd.Id}, "", http.StatusNotFound) 728 case errors.As(err, &appErr): 729 return nil, appErr 730 default: 731 return nil, model.NewAppError("RegenCommandToken", "app.command.regencommandtoken.internal_error", nil, err.Error(), http.StatusInternalServerError) 732 } 733 } 734 735 return command, nil 736 } 737 738 func (a *App) DeleteCommand(commandId string) *model.AppError { 739 if !*a.Config().ServiceSettings.EnableCommands { 740 return model.NewAppError("DeleteCommand", "api.command.disabled.app_error", nil, "", http.StatusNotImplemented) 741 } 742 743 err := a.Srv().Store.Command().Delete(commandId, model.GetMillis()) 744 if err != nil { 745 return model.NewAppError("DeleteCommand", "app.command.deletecommand.internal_error", nil, err.Error(), http.StatusInternalServerError) 746 } 747 748 return nil 749 }