Skip to content

Commit

Permalink
[feat] Add cli method for close sprint with supporting jira client me…
Browse files Browse the repository at this point in the history
…thods (#756)

Co-authored-by: Ankit <oss@ankit.pl>
  • Loading branch information
cworsnup13 and ankitpokhrel authored Sep 20, 2024
1 parent 2c9e6db commit 05564e8
Show file tree
Hide file tree
Showing 4 changed files with 178 additions and 1 deletion.
101 changes: 101 additions & 0 deletions internal/cmd/sprint/close/close.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package close

import (
"fmt"
"strconv"

"github.com/AlecAivazis/survey/v2"
"github.com/ankitpokhrel/jira-cli/api"
"github.com/ankitpokhrel/jira-cli/internal/cmdutil"
"github.com/ankitpokhrel/jira-cli/internal/query"
"github.com/spf13/cobra"
)

const (
helpText = `Close sprint.`
examples = `$ jira sprint close SPRINT_ID`
)

// NewCmdClose is an add command.
func NewCmdClose() *cobra.Command {
return &cobra.Command{
Use: "close SPRINT_ID",
Short: "Close sprint",
Long: helpText,
Example: examples,
Aliases: []string{"complete"},
Annotations: map[string]string{
"help:args": "SPRINT_ID\t\tID of the sprint on which you want to assign issues to, eg: 123\n",
},
Run: closeSprint,
}
}

func closeSprint(cmd *cobra.Command, args []string) {
params := parseFlags(cmd.Flags(), args)
client := api.DefaultClient(params.debug)

qs := getQuestions(params)
if len(qs) > 0 {
ans := struct {
SprintID string
}{}
err := survey.Ask(qs, &ans)
cmdutil.ExitIfError(err)

if params.sprintID == "" {
params.sprintID = ans.SprintID
}
}

err := func() error {
s := cmdutil.Info("Closing sprint...\n")
defer s.Stop()

sprintID, err := strconv.Atoi(params.sprintID)
if err != nil {
return err
}

return client.EndSprint(sprintID)
}()
cmdutil.ExitIfError(err)

cmdutil.Success(fmt.Sprintf("Sprint %s has been closed.", params.sprintID))
}

func parseFlags(flags query.FlagParser, args []string) *addParams {
var sprintID string

nArgs := len(args)
if nArgs > 0 {
sprintID = args[0]
}

debug, err := flags.GetBool("debug")
cmdutil.ExitIfError(err)

return &addParams{
sprintID: sprintID,
debug: debug,
}
}

func getQuestions(params *addParams) []*survey.Question {
var qs []*survey.Question

if params.sprintID == "" {
qs = append(qs, &survey.Question{
Name: "sprintID",
Prompt: &survey.Input{Message: "Sprint ID"},
Validate: survey.Required,
})
}

return qs
}

type addParams struct {
sprintID string
debug bool
}
4 changes: 3 additions & 1 deletion internal/cmd/sprint/sprint.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"github.com/spf13/cobra"

"github.com/ankitpokhrel/jira-cli/internal/cmd/sprint/add"
"github.com/ankitpokhrel/jira-cli/internal/cmd/sprint/close"
"github.com/ankitpokhrel/jira-cli/internal/cmd/sprint/list"
)

Expand All @@ -22,8 +23,9 @@ func NewCmdSprint() *cobra.Command {

lc := list.NewCmdList()
ac := add.NewCmdAdd()
cc := close.NewCmdClose()

cmd.AddCommand(lc, ac)
cmd.AddCommand(lc, ac, cc)

list.SetFlags(lc)

Expand Down
5 changes: 5 additions & 0 deletions pkg/jira/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,11 @@ func (c *Client) PutV2(ctx context.Context, path string, body []byte, headers He
return c.request(ctx, http.MethodPut, c.server+baseURLv2+path, body, headers)
}

// PutV1 sends PUT request to v1 version of the jira api.
func (c *Client) PutV1(ctx context.Context, path string, body []byte, headers Header) (*http.Response, error) {
return c.request(ctx, http.MethodPut, c.server+baseURLv1+path, body, headers)
}

// DeleteV2 sends DELETE request to v2 version of the jira api.
func (c *Client) DeleteV2(ctx context.Context, path string, headers Header) (*http.Response, error) {
return c.request(ctx, http.MethodDelete, c.server+baseURLv2+path, nil, headers)
Expand Down
69 changes: 69 additions & 0 deletions pkg/jira/sprint.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,75 @@ func (c *Client) Sprints(boardID int, qp string, from, limit int) (*SprintResult
return &out, err
}

// GetSprint returns a single sprint given an ID.
func (c *Client) GetSprint(sprintID int) (*Sprint, error) {
res, err := c.GetV1(
context.Background(),
fmt.Sprintf("/sprint/%d", sprintID),
nil,
)
if err != nil {
return nil, err
}
if res == nil {
return nil, ErrEmptyResponse
}
defer func() { _ = res.Body.Close() }()

if res.StatusCode != http.StatusOK {
return nil, formatUnexpectedResponse(res)
}

var s Sprint
err = json.NewDecoder(res.Body).Decode(&s)
if err != nil {
return nil, err
}

return &s, nil
}

// EndSprint queries the existence of the sprint and
// full updates the sprint with new status of closed.
// Default behavior is all open tasks are sent to backlog.
func (c *Client) EndSprint(sprintID int) error {
// get the sprint
sprint, err := c.GetSprint(sprintID)
if err != nil {
return err
}

// update to closed and format for PUT
sprint.Status = SprintStateClosed
body, err := json.Marshal(sprint)
if err != nil {
return err
}

res, err := c.PutV1(
context.Background(),
fmt.Sprintf("/sprint/%d", sprintID),
body,
Header{
"Accept": "application/json",
"Content-Type": "application/json",
},
)
if err != nil {
return err
}
if res == nil {
return ErrEmptyResponse
}
defer func() { _ = res.Body.Close() }()

if res.StatusCode != http.StatusOK {
return formatUnexpectedResponse(res)
}

return nil
}

// SprintsInBoards fetches sprints across given board IDs.
//
// qp is an additional query parameters in key, value pair format, eg: state=closed.
Expand Down

0 comments on commit 05564e8

Please sign in to comment.