Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[sprint] Add cli method for close sprint with supporting jira client … #756

Merged
merged 3 commits into from
Sep 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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