diff --git a/internal/cmd/sprint/list/list.go b/internal/cmd/sprint/list/list.go index 0486a511..43b95c09 100644 --- a/internal/cmd/sprint/list/list.go +++ b/internal/cmd/sprint/list/list.go @@ -72,7 +72,7 @@ func SetFlags(cmd *cobra.Command) { func sprintList(cmd *cobra.Command, args []string) { server := viper.GetString("server") project := viper.GetString("project.key") - boardID := viper.GetInt("board.id") + boardID := getBoardID(cmd) debug, err := cmd.Flags().GetBool("debug") cmdutil.ExitIfError(err) @@ -203,6 +203,8 @@ func sprintExplorerView(sprintQuery *query.Sprint, flags query.FlagParser, board return } + boardName := getBoardName(client, boardID) + if sprintQuery.Params().Current || sprintQuery.Params().Prev || sprintQuery.Params().Next { sprint := sprints[0] if sprintQuery.Params().Next { @@ -226,7 +228,7 @@ func sprintExplorerView(sprintQuery *query.Sprint, flags query.FlagParser, board v := view.SprintList{ Project: project, - Board: viper.GetString("board.name"), + Board: boardName, Server: server, Data: sprints, Issues: func(boardID, sprintID int) []*jira.Issue { @@ -277,6 +279,7 @@ func getIssueQuery(project string, flags query.FlagParser, showAll bool) (string } func setFlags(cmd *cobra.Command) { + cmd.Flags().String("board", "", "Board ID to use (overrides board.id from config)") cmd.Flags().String("state", "", "Filter sprint by its state (comma separated).\n"+ "Valid values are future, active and closed.\n"+ `Defaults to "active,closed"`) @@ -291,6 +294,32 @@ func setFlags(cmd *cobra.Command) { cmd.Flags().Bool("next", false, "List issues in next planned sprint") } +func getBoardID(cmd *cobra.Command) int { + boardFlag, err := cmd.Flags().GetString("board") + cmdutil.ExitIfError(err) + + if boardFlag != "" { + boardID, err := strconv.Atoi(boardFlag) + cmdutil.ExitIfError(err) + return boardID + } + + return viper.GetInt("board.id") +} + +func getBoardName(client *jira.Client, boardID int) string { + if boardID == 0 { + return viper.GetString("board.name") + } + + board, err := client.BoardByID(boardID) + if err != nil { + return viper.GetString("board.name") + } + + return board.Name +} + func hideFlags(cmd *cobra.Command) { cmdutil.ExitIfError(cmd.Flags().MarkHidden("history")) cmdutil.ExitIfError(cmd.Flags().MarkHidden("watching")) diff --git a/pkg/jira/board.go b/pkg/jira/board.go index 4805005e..a18c9a37 100644 --- a/pkg/jira/board.go +++ b/pkg/jira/board.go @@ -38,6 +38,30 @@ func (c *Client) BoardSearch(project, name string) (*BoardResult, error) { return c.board(path) } +// BoardByID fetches a single board by its ID. +func (c *Client) BoardByID(boardID int) (*Board, error) { + path := fmt.Sprintf("/board/%d", boardID) + + res, err := c.GetV1(context.Background(), path, 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 out Board + + err = json.NewDecoder(res.Body).Decode(&out) + + return &out, err +} + func (c *Client) board(path string) (*BoardResult, error) { res, err := c.GetV1(context.Background(), path, nil) if err != nil { diff --git a/pkg/jira/board_test.go b/pkg/jira/board_test.go index 4b229294..5745f38f 100644 --- a/pkg/jira/board_test.go +++ b/pkg/jira/board_test.go @@ -13,37 +13,47 @@ import ( func TestBoards(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, "/rest/agile/1.0/board", r.URL.Path) + switch r.URL.Path { + case "/rest/agile/1.0/board": + qs := r.URL.Query() - qs := r.URL.Query() + switch qs.Get("projectKeyOrId") { + case "BAD": + w.WriteHeader(400) + case "TEST": + assert.Equal(t, url.Values{ + "projectKeyOrId": []string{"TEST"}, + "type": []string{"scrum"}, + }, qs) - switch qs.Get("projectKeyOrId") { - case "BAD": - w.WriteHeader(400) - case "TEST": - assert.Equal(t, url.Values{ - "projectKeyOrId": []string{"TEST"}, - "type": []string{"scrum"}, - }, qs) + resp, err := os.ReadFile("./testdata/boards.json") + assert.NoError(t, err) - resp, err := os.ReadFile("./testdata/boards.json") - assert.NoError(t, err) + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + _, _ = w.Write(resp) + case "SEARCH": + assert.Equal(t, url.Values{ + "projectKeyOrId": []string{"SEARCH"}, + "name": []string{"board"}, + }, qs) - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(200) - _, _ = w.Write(resp) - case "SEARCH": - assert.Equal(t, url.Values{ - "projectKeyOrId": []string{"SEARCH"}, - "name": []string{"board"}, - }, qs) + resp, err := os.ReadFile("./testdata/boards.json") + assert.NoError(t, err) - resp, err := os.ReadFile("./testdata/boards.json") + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + _, _ = w.Write(resp) + } + case "/rest/agile/1.0/board/1": + resp, err := os.ReadFile("./testdata/board.json") assert.NoError(t, err) w.Header().Set("Content-Type", "application/json") w.WriteHeader(200) _, _ = w.Write(resp) + case "/rest/agile/1.0/board/999": + w.WriteHeader(404) } })) defer server.Close() @@ -81,3 +91,35 @@ func TestBoards(t *testing.T) { assert.NoError(t, err) assert.Equal(t, expected, actual) } + +func TestBoardByID(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case "/rest/agile/1.0/board/1": + resp, err := os.ReadFile("./testdata/board.json") + assert.NoError(t, err) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + _, _ = w.Write(resp) + case "/rest/agile/1.0/board/999": + w.WriteHeader(404) + } + })) + defer server.Close() + + client := NewClient(Config{Server: server.URL}, WithTimeout(3*time.Second)) + + actual, err := client.BoardByID(1) + assert.NoError(t, err) + + expected := &Board{ + ID: 1, + Name: "Board 1", + Type: "scrum", + } + assert.Equal(t, expected, actual) + + _, err = client.BoardByID(999) + assert.Error(t, &ErrUnexpectedResponse{}, err) +} diff --git a/pkg/jira/testdata/board.json b/pkg/jira/testdata/board.json new file mode 100644 index 00000000..0ec9c630 --- /dev/null +++ b/pkg/jira/testdata/board.json @@ -0,0 +1,5 @@ +{ + "id": 1, + "name": "Board 1", + "type": "scrum" +}