diff --git a/cmd/jira/main.go b/cmd/jira/main.go index 0640850..9da8ba9 100644 --- a/cmd/jira/main.go +++ b/cmd/jira/main.go @@ -216,6 +216,19 @@ func main() { Entry: cli.CmdLabelsAddRegistry(), Aliases: []string{"rm"}, }, + jiracli.CommandRegistry{ + Command: "take", + Entry: cli.CmdTakeRegistry(), + }, + jiracli.CommandRegistry{ + Command: "assign", + Entry: cli.CmdAssignRegistry(), + Aliases: []string{"give"}, + }, + jiracli.CommandRegistry{ + Command: "unassign", + Entry: cli.CmdUnassignRegistry(), + }, } cli.Register(app, registry) @@ -234,9 +247,6 @@ func main() { } // Usage: - // jira take ISSUE - // jira (assign|give) ISSUE [ASSIGNEE|--default] - // jira unassign ISSUE // jira add component [-p PROJECT] NAME DESCRIPTION LEAD // jira components [-p PROJECT] // jira issuetypes [-p PROJECT] @@ -290,15 +300,12 @@ func main() { // jiraCommands := map[string]string{ // "component": "component", // "components": "components", - // "take": "take", - // "assign": "assign", // "give": "assign", // "issuetypes": "issuetypes", // "export-templates": "export-templates", // "browse": "browse", // "req": "request", // "request": "request", - // "unassign": "unassign", // } // defaults := map[string]interface{}{ @@ -463,25 +470,12 @@ func main() { // case "components": // project := opts["project"].(string) // err = c.CmdComponents(project) - // case "take": - // requireArgs(1) - // err = c.CmdAssign(args[0], opts["user"].(string)) // case "browse": // requireArgs(1) // opts["browse"] = true // err = c.Browse(args[0]) // case "export-templates": // err = c.CmdExportTemplates() - // case "assign": - // requireArgs(1) - // assignee := "" - // if len(args) > 1 { - // assignee = args[1] - // } - // err = c.CmdAssign(args[0], assignee) - // case "unassign": - // requireArgs(1) - // err = c.CmdUnassign(args[0]) // case "request": // requireArgs(1) // data := "" diff --git a/issue.go b/issue.go index 3214793..a78b2a0 100644 --- a/issue.go +++ b/issue.go @@ -424,3 +424,36 @@ func (j *Jira) IssueAddComment(issue string, cp CommentProvider) (*jiradata.Comm } return nil, responseError(resp) } + +type UserProvider interface { + ProvideUser() *jiradata.User +} + +// https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-assign +func (j *Jira) IssueAssign(issue, name string) error { + // this is special, not using the jiradata.User structure + // because we need to be able to send `null` as the name param + // when we want to un-assign the issue + req := struct { + Name *string `json:"name"` + }{&name} + if name == "" { + req.Name = nil + } + + encoded, err := json.Marshal(req) + if err != nil { + return err + } + uri := fmt.Sprintf("%s/rest/api/2/issue/%s/assignee", j.Endpoint, issue) + resp, err := j.UA.Post(uri, "application/json", bytes.NewBuffer(encoded)) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode == 204 { + return nil + } + return responseError(resp) +} diff --git a/jiracli/assign.go b/jiracli/assign.go new file mode 100644 index 0000000..d6a1357 --- /dev/null +++ b/jiracli/assign.go @@ -0,0 +1,56 @@ +package jiracli + +import ( + "fmt" + + kingpin "gopkg.in/alecthomas/kingpin.v2" +) + +type AssignOptions struct { + GlobalOptions + Issue string + Assignee string +} + +func (jc *JiraCli) CmdAssignRegistry() *CommandRegistryEntry { + opts := AssignOptions{} + + return &CommandRegistryEntry{ + "Assign user to issue", + func() error { + return jc.CmdAssign(&opts) + }, + func(cmd *kingpin.CmdClause) error { + return jc.CmdAssignUsage(cmd, &opts) + }, + } +} + +func (jc *JiraCli) CmdAssignUsage(cmd *kingpin.CmdClause, opts *AssignOptions) error { + if err := jc.GlobalUsage(cmd, &opts.GlobalOptions); err != nil { + return err + } + cmd.Flag("default", "use default user for assignee").PreAction(func(ctx *kingpin.ParseContext) error { + if flagValue(ctx, "default") == "true" { + opts.Assignee = "-1" + } + return nil + }).Bool() + cmd.Arg("ISSUE", "issue to assign").Required().StringVar(&opts.Issue) + cmd.Arg("ASSIGNEE", "user to assign to issue").StringVar(&opts.Assignee) + return nil +} + +// CmdAssign will assign an issue to a user +func (jc *JiraCli) CmdAssign(opts *AssignOptions) error { + err := jc.IssueAssign(opts.Issue, opts.Assignee) + if err != nil { + return err + } + + fmt.Printf("OK %s %s/browse/%s\n", opts.Issue, jc.Endpoint, opts.Issue) + + // FIXME implement browse + + return nil +} diff --git a/jiracli/commands.go b/jiracli/commands.go index 1cde4ba..8839708 100644 --- a/jiracli/commands.go +++ b/jiracli/commands.go @@ -160,120 +160,6 @@ func (jc *JiraCli) Register(app *kingpin.Application, reg []CommandRegistry) { // return nil // } -// // CmdLabels will add, remove or set labels on a given issue -// func (c *Cli) CmdLabels(action string, issue string, labels []string) error { -// log.Debugf("label called") - -// if action != "add" && action != "remove" && action != "set" { -// return fmt.Errorf("action must be 'add', 'set' or 'remove': %q is invalid", action) -// } - -// handlePut := func(json string) error { -// uri := fmt.Sprintf("%s/rest/api/2/issue/%s", c.endpoint, issue) -// if c.getOptBool("dryrun", false) { -// log.Debugf("PUT: %s", json) -// log.Debugf("Dryrun mode, skipping POST") -// return nil -// } -// resp, err := c.put(uri, json) -// if err != nil { -// return err -// } - -// if resp.StatusCode == 204 { -// c.Browse(issue) -// if !c.GetOptBool("quiet", false) { -// fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue) -// } -// return nil -// } -// logBuffer := bytes.NewBuffer(make([]byte, 0)) -// resp.Write(logBuffer) -// err = fmt.Errorf("Unexpected Response From PUT") -// log.Errorf("%s:\n%s", err, logBuffer) -// return err -// } - -// var labelsJSON string -// var err error -// if action == "set" { -// labelsActions := make([]map[string][]string, 1) -// labelsActions[0] = map[string][]string{ -// "set": labels, -// } -// labelsJSON, err = jsonEncode(map[string]interface{}{ -// "labels": labelsActions, -// }) -// } else { -// labelsActions := make([]map[string]string, len(labels)) -// for i, label := range labels { -// labelActionMap := map[string]string{ -// action: label, -// } -// labelsActions[i] = labelActionMap -// } -// labelsJSON, err = jsonEncode(map[string]interface{}{ -// "labels": labelsActions, -// }) -// } -// if err != nil { -// return err -// } -// json := fmt.Sprintf("{ \"update\": %s }", labelsJSON) -// return handlePut(json) - -// } - -// // CmdAssign will assign the given user to be the owner of the given issue -// func (c *Cli) CmdAssign(issue string, user string) error { -// log.Debugf("assign called") - -// var userVal interface{} = user -// // https://docs.atlassian.com/jira/REST/cloud/#api/2/issue-assign -// // If the name is "-1" automatic assignee is used. A null name will remove the assignee. -// if user == "" { -// userVal = nil -// } -// if c.GetOptBool("default", false) { -// userVal = "-1" -// } - -// json, err := jsonEncode(map[string]interface{}{ -// "name": userVal, -// }) -// if err != nil { -// return err -// } - -// uri := fmt.Sprintf("%s/rest/api/2/issue/%s/assignee", c.endpoint, issue) -// if c.getOptBool("dryrun", false) { -// log.Debugf("PUT: %s", json) -// log.Debugf("Dryrun mode, skipping PUT") -// return nil -// } -// resp, err := c.put(uri, json) -// if err != nil { -// return err -// } -// if resp.StatusCode == 204 { -// c.Browse(issue) -// if !c.GetOptBool("quiet", false) { -// fmt.Printf("OK %s %s/browse/%s\n", issue, c.endpoint, issue) -// } -// } else { -// logBuffer := bytes.NewBuffer(make([]byte, 0)) -// resp.Write(logBuffer) -// err := fmt.Errorf("Unexpected Response From PUT") -// log.Errorf("%s:\n%s", err, logBuffer) -// return err -// } -// return nil -// } - -// func (c *Cli) CmdUnassign(issue string) error { -// return c.CmdAssign(issue, "") -// } - // // CmdExportTemplates will export the default templates to the template directory. // func (c *Cli) CmdExportTemplates() error { // dir := c.opts["directory"].(string) diff --git a/jiracli/take.go b/jiracli/take.go new file mode 100644 index 0000000..c630acb --- /dev/null +++ b/jiracli/take.go @@ -0,0 +1,26 @@ +package jiracli + +import kingpin "gopkg.in/alecthomas/kingpin.v2" + +func (jc *JiraCli) CmdTakeRegistry() *CommandRegistryEntry { + opts := AssignOptions{} + + return &CommandRegistryEntry{ + "Assign issue to yourself", + func() error { + opts.Assignee = opts.User + return jc.CmdAssign(&opts) + }, + func(cmd *kingpin.CmdClause) error { + return jc.CmdAssignUsage(cmd, &opts) + }, + } +} + +func (jc *JiraCli) CmdTakeUsage(cmd *kingpin.CmdClause, opts *AssignOptions) error { + if err := jc.GlobalUsage(cmd, &opts.GlobalOptions); err != nil { + return err + } + cmd.Arg("ISSUE", "issue to assign").Required().StringVar(&opts.Issue) + return nil +} diff --git a/jiracli/unassign.go b/jiracli/unassign.go new file mode 100644 index 0000000..f0aafce --- /dev/null +++ b/jiracli/unassign.go @@ -0,0 +1,25 @@ +package jiracli + +import kingpin "gopkg.in/alecthomas/kingpin.v2" + +func (jc *JiraCli) CmdUnassignRegistry() *CommandRegistryEntry { + opts := AssignOptions{} + + return &CommandRegistryEntry{ + "Unassign an issue", + func() error { + return jc.CmdAssign(&opts) + }, + func(cmd *kingpin.CmdClause) error { + return jc.CmdAssignUsage(cmd, &opts) + }, + } +} + +func (jc *JiraCli) CmdUnassignUsage(cmd *kingpin.CmdClause, opts *AssignOptions) error { + if err := jc.GlobalUsage(cmd, &opts.GlobalOptions); err != nil { + return err + } + cmd.Arg("ISSUE", "issue to unassign").Required().StringVar(&opts.Issue) + return nil +} diff --git a/jiradata/providers.go b/jiradata/providers.go index a843f9d..dc29c0c 100644 --- a/jiradata/providers.go +++ b/jiradata/providers.go @@ -21,3 +21,7 @@ func (r *RankRequest) ProvideRankRequest() *RankRequest { func (c *Comment) ProvideComment() *Comment { return c } + +func (u *User) ProvideUser() *User { + return u +}