Skip to content

Commit

Permalink
Fixed /jira subcommand parsing consistency (#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
levb authored May 8, 2019
1 parent 33b0a3a commit 96bee47
Showing 1 changed file with 101 additions and 95 deletions.
196 changes: 101 additions & 95 deletions server/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,72 +22,73 @@ const helpText = "###### Mattermost Jira Plugin - Slash Command Help\n" +
" * `delete <number or URL>` - Delete a known instance, select the first remaining as the current\n" +
""

func getCommand() *model.Command {
return &model.Command{
Trigger: "jira",
DisplayName: "Jira",
Description: "Integration with Jira.",
AutoComplete: true,
AutoCompleteDesc: "Available commands: connect, disconnect, transition, instance, help",
AutoCompleteHint: "[command]",
type CommandHandlerFunc func(p *Plugin, c *plugin.Context, header *model.CommandArgs, args ...string) *model.CommandResponse

type CommandHandler struct {
handlers map[string]CommandHandlerFunc
defaultHandler CommandHandlerFunc
}

var jiraCommandHandler = CommandHandler{
handlers: map[string]CommandHandlerFunc{
"instance/add/server": executeInstanceAddServer,
"instance/add/cloud": executeInstanceAddCloud,
"instance/list": executeInstanceList,
"instance/select": executeInstanceSelect,
"instance/delete": executeInstanceDelete,
"transition": executeTransition,
"connect": executeConnect,
"disconnect": executeDisconnect,
},
defaultHandler: commandHelp,
}

func (ch CommandHandler) Handle(p *Plugin, c *plugin.Context, header *model.CommandArgs, args ...string) *model.CommandResponse {
for n := len(args); n > 0; n-- {
h := ch.handlers[strings.Join(args[:n], "/")]
if h != nil {
return h(p, c, header, args[n:]...)
}
}
return ch.defaultHandler(p, c, header, args...)
}

func commandHelp(p *Plugin, c *plugin.Context, header *model.CommandArgs, args ...string) *model.CommandResponse {
return help()
}

func help() *model.CommandResponse {
return responsef(helpText)
}

func (p *Plugin) ExecuteCommand(c *plugin.Context, commandArgs *model.CommandArgs) (*model.CommandResponse, *model.AppError) {
args := strings.Fields(commandArgs.Command)
if len(args) < 2 {
return responsef("Invalid syntax. Must be at least /jira action."), nil
}
action := args[1]
args = args[2:]

switch action {
case "help":
return responsef(helpText), nil
case "connect":
return executeConnect(p, c, args), nil
case "disconnect":
return executeDisconnect(p, c, args), nil
case "instance":
return executeInstance(p, c, args), nil
case "transition":
return executeTransition(p, c, args, commandArgs.UserId), nil
if len(args) == 0 || args[0] != "/jira" {
return help(), nil
}

return responsef("Action %v is not supported.", action), nil
return jiraCommandHandler.Handle(p, c, commandArgs, args[1:]...), nil
}

func executeConnect(p *Plugin, c *plugin.Context, args []string) *model.CommandResponse {
func executeConnect(p *Plugin, c *plugin.Context, header *model.CommandArgs, args ...string) *model.CommandResponse {
if len(args) != 0 {
return help()
}
return responsef("[Click here to link your Jira account.](%s/%s)",
p.GetPluginURL(), routeUserConnect)
}

func executeDisconnect(p *Plugin, c *plugin.Context, args []string) *model.CommandResponse {
func executeDisconnect(p *Plugin, c *plugin.Context, header *model.CommandArgs, args ...string) *model.CommandResponse {
if len(args) != 0 {
return help()
}
return responsef("[Click here to unlink your Jira account.](%s/%s)",
p.GetPluginURL(), routeUserDisconnect)
}

func executeInstance(p *Plugin, c *plugin.Context, args []string) *model.CommandResponse {
if len(args) < 1 {
return responsef("Please specify a parameter in the form `/jira instance [add,list,select]")
func executeInstanceList(p *Plugin, c *plugin.Context, header *model.CommandArgs, args ...string) *model.CommandResponse {
if len(args) != 0 {
return help()
}
action := args[0]
args = args[1:]

switch action {
case "list":
return executeInstanceList(p, c)
case "add":
return executeInstanceAdd(p, c, args...)
case "select":
return executeInstanceSelect(p, c, args...)
case "delete":
return executeInstanceDelete(p, c, args...)
}
return responsef("Please specify a parameter in the form `/jira instance [add,list,select]")
}

func executeInstanceList(p *Plugin, c *plugin.Context) *model.CommandResponse {
known, err := p.LoadKnownJIRAInstances()
if err != nil {
return responsef("Failed to load known Jira instances: %v", err)
Expand Down Expand Up @@ -131,7 +132,14 @@ func executeInstanceList(p *Plugin, c *plugin.Context) *model.CommandResponse {
return responsef(text)
}

const addResponseFormat = `Instance has been added. You need to add an Application Link to it in Jira now.
func executeInstanceAddServer(p *Plugin, c *plugin.Context, header *model.CommandArgs, args ...string) *model.CommandResponse {
if len(args) != 1 {
return help()
}
jiraURL := args[0]

const addResponseFormat = `` +
`Instance has been added. You need to add an Application Link to it in Jira now.
1. Click %s, login as an admin.
2. Navigate to (Jira) Settings > Applications > Application Links.
3. Enter %s, anc click "Create new link".
Expand All @@ -145,48 +153,35 @@ const addResponseFormat = `Instance has been added. You need to add an Applicati
- Consumer Name: Mattermost
- Public Key: %s
`

func executeInstanceAdd(p *Plugin, c *plugin.Context, args ...string) *model.CommandResponse {
if len(args) < 1 {
return responsef("Please specify a parameter in the form `/jira instance add server {URL}` or `/jira instance add cloud`")
ji := NewJIRAServerInstance(p, jiraURL)
err := p.StoreJIRAInstance(ji)
if err != nil {
return responsef(err.Error())
}
err = p.StoreCurrentJIRAInstance(ji)
if err != nil {
return responsef(err.Error())
}
typ := args[0]

switch typ {
case JIRATypeServer:
if len(args) < 2 {
return responsef("Please specify the server URL in the form `/jira instance add server {URL}`")
}
jiraURL := args[1]

ji := NewJIRAServerInstance(p, jiraURL)
err := p.StoreJIRAInstance(ji)
if err != nil {
return responsef(err.Error())
}
err = p.StoreCurrentJIRAInstance(ji)
if err != nil {
return responsef(err.Error())
}

pkey, err := publicKeyString(p)
if err != nil {
return responsef("Failed to load public key: %v", err)
}
return responsef(addResponseFormat, ji.GetURL(), p.GetSiteURL(), ji.GetMattermostKey(), pkey)

case JIRATypeCloud:
// TODO the exact group membership in Jira?
return responsef(`As an admin, upload an application from %s/%s. The link can be found in **Jira Settings > Applications > Manage**`,
p.GetPluginURL(), routeACJSON)
pkey, err := publicKeyString(p)
if err != nil {
return responsef("Failed to load public key: %v", err)
}
return responsef(addResponseFormat, ji.GetURL(), p.GetSiteURL(), ji.GetMattermostKey(), pkey)
}

return responsef("Please specify a parameter in the form `/jira instance add server {URL}` or `/jira instance add cloud`")
func executeInstanceAddCloud(p *Plugin, c *plugin.Context, header *model.CommandArgs, args ...string) *model.CommandResponse {
if len(args) != 0 {
return help()
}
// TODO What is the exact group membership in Jira required? Site-admins?
return responsef(`As an admin, upload an application from %s/%s. The link can be found in **Jira Settings > Applications > Manage**`,
p.GetPluginURL(), routeACJSON)
}

func executeInstanceSelect(p *Plugin, c *plugin.Context, args ...string) *model.CommandResponse {
if len(args) < 1 {
return responsef("/jira instance select {URL|#} ")
func executeInstanceSelect(p *Plugin, c *plugin.Context, header *model.CommandArgs, args ...string) *model.CommandResponse {
if len(args) != 1 {
return help()
}
instanceKey := args[0]
num, err := strconv.ParseUint(instanceKey, 10, 8)
Expand Down Expand Up @@ -216,12 +211,12 @@ func executeInstanceSelect(p *Plugin, c *plugin.Context, args ...string) *model.
return responsef(err.Error())
}

return executeInstanceList(p, c)
return executeInstanceList(p, c, header)
}

func executeInstanceDelete(p *Plugin, c *plugin.Context, args ...string) *model.CommandResponse {
if len(args) < 1 {
return responsef("/jira instance delete {URL|#}")
func executeInstanceDelete(p *Plugin, c *plugin.Context, header *model.CommandArgs, args ...string) *model.CommandResponse {
if len(args) != 1 {
return help()
}
instanceKey := args[0]

Expand Down Expand Up @@ -250,23 +245,34 @@ func executeInstanceDelete(p *Plugin, c *plugin.Context, args ...string) *model.
return responsef("failed to delete Jira instance %s: %v", instanceKey, err)
}

return executeInstanceSelect(p, c, "1")
return executeInstanceSelect(p, c, header, "1")
}

func executeTransition(p *Plugin, c *plugin.Context, args []string, userId string) *model.CommandResponse {
if len(args) < 2 {
return responsef("Please specify both an issue key and state in the form `/jira transition <issue-key> <state>`")
func executeTransition(p *Plugin, c *plugin.Context, header *model.CommandArgs, args ...string) *model.CommandResponse {
if len(args) != 2 {
return help()
}
issueKey := args[0]
toState := strings.Join(args[1:], " ")

if err := p.transitionJiraIssue(userId, issueKey, toState); err != nil {
if err := p.transitionJiraIssue(header.UserId, issueKey, toState); err != nil {
return responsef("%v", err)
}

return responsef("Transition completed.")
}

func getCommand() *model.Command {
return &model.Command{
Trigger: "jira",
DisplayName: "Jira",
Description: "Integration with Jira.",
AutoComplete: true,
AutoCompleteDesc: "Available commands: connect, disconnect, transition, instance, help",
AutoCompleteHint: "[command]",
}
}

func responsef(format string, args ...interface{}) *model.CommandResponse {
return &model.CommandResponse{
ResponseType: model.COMMAND_RESPONSE_TYPE_EPHEMERAL,
Expand Down

0 comments on commit 96bee47

Please sign in to comment.