2016-06-01 20:18:33 +02:00
|
|
|
package jira
|
2016-06-01 20:23:27 +02:00
|
|
|
|
2016-07-17 11:17:29 +02:00
|
|
|
import (
|
2020-05-03 15:38:32 +02:00
|
|
|
"context"
|
2016-07-17 11:17:29 +02:00
|
|
|
"fmt"
|
2018-06-08 01:08:02 +02:00
|
|
|
"strconv"
|
2016-07-17 11:17:29 +02:00
|
|
|
"time"
|
|
|
|
)
|
2016-06-01 20:23:27 +02:00
|
|
|
|
2016-06-19 15:08:53 +02:00
|
|
|
// BoardService handles Agile Boards for the JIRA instance / API.
|
|
|
|
//
|
|
|
|
// JIRA API docs: https://docs.atlassian.com/jira-software/REST/server/
|
2016-06-01 20:23:27 +02:00
|
|
|
type BoardService struct {
|
|
|
|
client *Client
|
|
|
|
}
|
|
|
|
|
2016-06-19 15:08:53 +02:00
|
|
|
// BoardsList reflects a list of agile boards
|
2016-06-01 20:23:27 +02:00
|
|
|
type BoardsList struct {
|
2016-09-23 16:19:07 +02:00
|
|
|
MaxResults int `json:"maxResults" structs:"maxResults"`
|
|
|
|
StartAt int `json:"startAt" structs:"startAt"`
|
|
|
|
Total int `json:"total" structs:"total"`
|
|
|
|
IsLast bool `json:"isLast" structs:"isLast"`
|
|
|
|
Values []Board `json:"values" structs:"values"`
|
2016-06-01 20:23:27 +02:00
|
|
|
}
|
|
|
|
|
2016-06-19 15:08:53 +02:00
|
|
|
// Board represents a JIRA agile board
|
2016-06-16 09:52:16 +02:00
|
|
|
type Board struct {
|
2016-09-23 16:19:07 +02:00
|
|
|
ID int `json:"id,omitempty" structs:"id,omitempty"`
|
|
|
|
Self string `json:"self,omitempty" structs:"self,omitempty"`
|
|
|
|
Name string `json:"name,omitempty" structs:"name,omitemtpy"`
|
|
|
|
Type string `json:"type,omitempty" structs:"type,omitempty"`
|
|
|
|
FilterID int `json:"filterId,omitempty" structs:"filterId,omitempty"`
|
2016-06-15 19:08:15 +02:00
|
|
|
}
|
2016-06-01 20:23:27 +02:00
|
|
|
|
2016-06-15 11:20:37 +02:00
|
|
|
// BoardListOptions specifies the optional parameters to the BoardService.GetList
|
|
|
|
type BoardListOptions struct {
|
2016-06-19 15:08:53 +02:00
|
|
|
// BoardType filters results to boards of the specified type.
|
2016-06-15 11:20:37 +02:00
|
|
|
// Valid values: scrum, kanban.
|
2019-05-12 10:54:48 +02:00
|
|
|
BoardType string `url:"type,omitempty"`
|
2016-06-19 15:08:53 +02:00
|
|
|
// Name filters results to boards that match or partially match the specified name.
|
2016-06-15 19:08:15 +02:00
|
|
|
Name string `url:"name,omitempty"`
|
2016-06-19 15:08:53 +02:00
|
|
|
// ProjectKeyOrID filters results to boards that are relevant to a project.
|
|
|
|
// Relevance meaning that the JQL filter defined in board contains a reference to a project.
|
|
|
|
ProjectKeyOrID string `url:"projectKeyOrId,omitempty"`
|
|
|
|
|
|
|
|
SearchOptions
|
2016-06-15 11:20:37 +02:00
|
|
|
}
|
2016-06-01 20:23:27 +02:00
|
|
|
|
2018-06-08 01:08:02 +02:00
|
|
|
// GetAllSprintsOptions specifies the optional parameters to the BoardService.GetList
|
|
|
|
type GetAllSprintsOptions struct {
|
|
|
|
// State filters results to sprints in the specified states, comma-separate list
|
|
|
|
State string `url:"state,omitempty"`
|
|
|
|
|
|
|
|
SearchOptions
|
|
|
|
}
|
|
|
|
|
|
|
|
// SprintsList reflects a list of agile sprints
|
|
|
|
type SprintsList struct {
|
|
|
|
MaxResults int `json:"maxResults" structs:"maxResults"`
|
|
|
|
StartAt int `json:"startAt" structs:"startAt"`
|
|
|
|
Total int `json:"total" structs:"total"`
|
|
|
|
IsLast bool `json:"isLast" structs:"isLast"`
|
|
|
|
Values []Sprint `json:"values" structs:"values"`
|
2016-07-17 11:17:29 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Sprint represents a sprint on JIRA agile board
|
|
|
|
type Sprint struct {
|
2016-09-23 16:19:07 +02:00
|
|
|
ID int `json:"id" structs:"id"`
|
|
|
|
Name string `json:"name" structs:"name"`
|
|
|
|
CompleteDate *time.Time `json:"completeDate" structs:"completeDate"`
|
|
|
|
EndDate *time.Time `json:"endDate" structs:"endDate"`
|
|
|
|
StartDate *time.Time `json:"startDate" structs:"startDate"`
|
|
|
|
OriginBoardID int `json:"originBoardId" structs:"originBoardId"`
|
|
|
|
Self string `json:"self" structs:"self"`
|
|
|
|
State string `json:"state" structs:"state"`
|
2016-07-17 11:17:29 +02:00
|
|
|
}
|
|
|
|
|
2019-08-18 16:17:16 +02:00
|
|
|
// BoardConfiguration represents a boardConfiguration of a jira board
|
|
|
|
type BoardConfiguration struct {
|
|
|
|
ID int `json:"id"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
Self string `json:"self"`
|
|
|
|
Location BoardConfigurationLocation `json:"location"`
|
|
|
|
Filter BoardConfigurationFilter `json:"filter"`
|
|
|
|
SubQuery BoardConfigurationSubQuery `json:"subQuery"`
|
|
|
|
ColumnConfig BoardConfigurationColumnConfig `json:"columnConfig"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// BoardConfigurationFilter reference to the filter used by the given board.
|
|
|
|
type BoardConfigurationFilter struct {
|
|
|
|
ID string `json:"id"`
|
|
|
|
Self string `json:"self"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// BoardConfigurationSubQuery (Kanban only) - JQL subquery used by the given board.
|
|
|
|
type BoardConfigurationSubQuery struct {
|
|
|
|
Query string `json:"query"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// BoardConfigurationLocation reference to the container that the board is located in
|
|
|
|
type BoardConfigurationLocation struct {
|
|
|
|
Type string `json:"type"`
|
|
|
|
Key string `json:"key"`
|
|
|
|
ID string `json:"id"`
|
|
|
|
Self string `json:"self"`
|
|
|
|
Name string `json:"name"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// BoardConfigurationColumnConfig lists the columns for a given board in the order defined in the column configuration
|
|
|
|
// with constrainttype (none, issueCount, issueCountExclSubs)
|
|
|
|
type BoardConfigurationColumnConfig struct {
|
|
|
|
Columns []BoardConfigurationColumn `json:"columns"`
|
|
|
|
ConstraintType string `json:"constraintType"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// BoardConfigurationColumn lists the name of the board with the statuses that maps to a particular column
|
|
|
|
type BoardConfigurationColumn struct {
|
|
|
|
Name string `json:"name"`
|
|
|
|
Status []BoardConfigurationColumnStatus `json:"statuses"`
|
|
|
|
}
|
|
|
|
|
|
|
|
// BoardConfigurationColumnStatus represents a status in the column configuration
|
|
|
|
type BoardConfigurationColumnStatus struct {
|
|
|
|
ID string `json:"id"`
|
|
|
|
Self string `json:"self"`
|
|
|
|
}
|
|
|
|
|
2020-05-03 15:38:32 +02:00
|
|
|
// GetAllBoardsWithContext will returns all boards. This only includes boards that the user has permission to view.
|
2016-06-01 20:23:27 +02:00
|
|
|
//
|
2016-06-19 15:08:53 +02:00
|
|
|
// JIRA API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-getAllBoards
|
2020-05-03 15:38:32 +02:00
|
|
|
func (s *BoardService) GetAllBoardsWithContext(ctx context.Context, opt *BoardListOptions) (*BoardsList, *Response, error) {
|
2016-06-16 09:52:16 +02:00
|
|
|
apiEndpoint := "rest/agile/1.0/board"
|
2016-06-15 11:20:37 +02:00
|
|
|
url, err := addOptions(apiEndpoint, opt)
|
2018-06-08 01:08:02 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2020-05-03 15:38:32 +02:00
|
|
|
req, err := s.client.NewRequestWithContext(ctx, "GET", url, nil)
|
2016-06-01 20:23:27 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
boards := new(BoardsList)
|
|
|
|
resp, err := s.client.Do(req, boards)
|
|
|
|
if err != nil {
|
2017-11-04 00:22:32 +02:00
|
|
|
jerr := NewJiraError(resp, err)
|
|
|
|
return nil, resp, jerr
|
2016-06-01 20:23:27 +02:00
|
|
|
}
|
2016-06-15 11:20:37 +02:00
|
|
|
|
|
|
|
return boards, resp, err
|
2016-06-01 20:23:27 +02:00
|
|
|
}
|
2016-06-16 09:52:16 +02:00
|
|
|
|
2020-05-03 15:38:32 +02:00
|
|
|
// GetAllBoards wraps GetAllBoardsWithContext using the background context.
|
|
|
|
func (s *BoardService) GetAllBoards(opt *BoardListOptions) (*BoardsList, *Response, error) {
|
|
|
|
return s.GetAllBoardsWithContext(context.Background(), opt)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetBoardWithContext will returns the board for the given boardID.
|
2016-06-19 15:08:53 +02:00
|
|
|
// This board will only be returned if the user has permission to view it.
|
|
|
|
//
|
|
|
|
// JIRA API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-getBoard
|
2020-05-03 15:38:32 +02:00
|
|
|
func (s *BoardService) GetBoardWithContext(ctx context.Context, boardID int) (*Board, *Response, error) {
|
2016-06-16 11:20:47 +02:00
|
|
|
apiEndpoint := fmt.Sprintf("rest/agile/1.0/board/%v", boardID)
|
2020-05-03 15:38:32 +02:00
|
|
|
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
|
2016-06-16 09:52:16 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
board := new(Board)
|
|
|
|
resp, err := s.client.Do(req, board)
|
|
|
|
if err != nil {
|
2017-11-04 00:22:32 +02:00
|
|
|
jerr := NewJiraError(resp, err)
|
|
|
|
return nil, resp, jerr
|
2016-06-16 09:52:16 +02:00
|
|
|
}
|
2017-11-04 00:22:32 +02:00
|
|
|
|
2016-06-16 09:52:16 +02:00
|
|
|
return board, resp, nil
|
|
|
|
}
|
|
|
|
|
2020-05-03 15:38:32 +02:00
|
|
|
// GetBoard wraps GetBoardWithContext using the background context.
|
|
|
|
func (s *BoardService) GetBoard(boardID int) (*Board, *Response, error) {
|
|
|
|
return s.GetBoardWithContext(context.Background(), boardID)
|
|
|
|
}
|
|
|
|
|
|
|
|
// CreateBoardWithContext creates a new board. Board name, type and filter Id is required.
|
2016-06-16 09:52:16 +02:00
|
|
|
// name - Must be less than 255 characters.
|
|
|
|
// type - Valid values: scrum, kanban
|
|
|
|
// filterId - Id of a filter that the user has permissions to view.
|
|
|
|
// Note, if the user does not have the 'Create shared objects' permission and tries to create a shared board, a private
|
|
|
|
// board will be created instead (remember that board sharing depends on the filter sharing).
|
2016-06-16 11:20:47 +02:00
|
|
|
//
|
2016-06-16 09:52:16 +02:00
|
|
|
// JIRA API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-createBoard
|
2020-05-03 15:38:32 +02:00
|
|
|
func (s *BoardService) CreateBoardWithContext(ctx context.Context, board *Board) (*Board, *Response, error) {
|
2016-06-16 09:52:16 +02:00
|
|
|
apiEndpoint := "rest/agile/1.0/board"
|
2020-05-03 15:38:32 +02:00
|
|
|
req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, board)
|
2016-06-16 09:52:16 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
responseBoard := new(Board)
|
|
|
|
resp, err := s.client.Do(req, responseBoard)
|
|
|
|
if err != nil {
|
2017-11-04 00:22:32 +02:00
|
|
|
jerr := NewJiraError(resp, err)
|
|
|
|
return nil, resp, jerr
|
2016-06-16 09:52:16 +02:00
|
|
|
}
|
2016-06-19 15:08:53 +02:00
|
|
|
|
2016-06-16 09:52:16 +02:00
|
|
|
return responseBoard, resp, nil
|
|
|
|
}
|
2016-06-16 11:20:47 +02:00
|
|
|
|
2020-05-03 15:38:32 +02:00
|
|
|
// CreateBoard wraps CreateBoardWithContext using the background context.
|
|
|
|
func (s *BoardService) CreateBoard(board *Board) (*Board, *Response, error) {
|
|
|
|
return s.CreateBoardWithContext(context.Background(), board)
|
|
|
|
}
|
|
|
|
|
|
|
|
// DeleteBoardWithContext will delete an agile board.
|
2016-06-16 11:20:47 +02:00
|
|
|
//
|
2016-07-17 11:08:07 +02:00
|
|
|
// JIRA API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-deleteBoard
|
2020-05-03 15:38:32 +02:00
|
|
|
func (s *BoardService) DeleteBoardWithContext(ctx context.Context, boardID int) (*Board, *Response, error) {
|
2016-06-16 11:20:47 +02:00
|
|
|
apiEndpoint := fmt.Sprintf("rest/agile/1.0/board/%v", boardID)
|
2020-05-03 15:38:32 +02:00
|
|
|
req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil)
|
2016-06-16 11:20:47 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
resp, err := s.client.Do(req, nil)
|
2017-11-04 00:22:32 +02:00
|
|
|
if err != nil {
|
|
|
|
err = NewJiraError(resp, err)
|
|
|
|
}
|
2016-06-16 11:20:47 +02:00
|
|
|
return nil, resp, err
|
|
|
|
}
|
2016-07-17 11:17:29 +02:00
|
|
|
|
2020-05-03 15:38:32 +02:00
|
|
|
// DeleteBoard wraps DeleteBoardWithContext using the background context.
|
|
|
|
func (s *BoardService) DeleteBoard(boardID int) (*Board, *Response, error) {
|
|
|
|
return s.DeleteBoardWithContext(context.Background(), boardID)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetAllSprintsWithContext will return all sprints from a board, for a given board Id.
|
2016-07-17 11:17:29 +02:00
|
|
|
// This only includes sprints that the user has permission to view.
|
|
|
|
//
|
|
|
|
// JIRA API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board/{boardId}/sprint
|
2020-05-03 15:38:32 +02:00
|
|
|
func (s *BoardService) GetAllSprintsWithContext(ctx context.Context, boardID string) ([]Sprint, *Response, error) {
|
2018-06-08 01:08:02 +02:00
|
|
|
id, err := strconv.Atoi(boardID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
result, response, err := s.GetAllSprintsWithOptions(id, &GetAllSprintsOptions{})
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return result.Values, response, nil
|
|
|
|
}
|
|
|
|
|
2020-05-03 15:38:32 +02:00
|
|
|
// GetAllSprints wraps GetAllSprintsWithContext using the background context.
|
|
|
|
func (s *BoardService) GetAllSprints(boardID string) ([]Sprint, *Response, error) {
|
|
|
|
return s.GetAllSprintsWithContext(context.Background(), boardID)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetAllSprintsWithOptionsWithContext will return sprints from a board, for a given board Id and filtering options
|
2018-06-08 01:08:02 +02:00
|
|
|
// This only includes sprints that the user has permission to view.
|
|
|
|
//
|
|
|
|
// JIRA API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board/{boardId}/sprint
|
2020-05-03 15:38:32 +02:00
|
|
|
func (s *BoardService) GetAllSprintsWithOptionsWithContext(ctx context.Context, boardID int, options *GetAllSprintsOptions) (*SprintsList, *Response, error) {
|
2018-06-08 01:08:02 +02:00
|
|
|
apiEndpoint := fmt.Sprintf("rest/agile/1.0/board/%d/sprint", boardID)
|
|
|
|
url, err := addOptions(apiEndpoint, options)
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
2020-05-03 15:38:32 +02:00
|
|
|
req, err := s.client.NewRequestWithContext(ctx, "GET", url, nil)
|
2016-07-17 11:17:29 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
2018-06-08 01:08:02 +02:00
|
|
|
result := new(SprintsList)
|
2016-07-17 11:17:29 +02:00
|
|
|
resp, err := s.client.Do(req, result)
|
2017-11-04 00:22:32 +02:00
|
|
|
if err != nil {
|
|
|
|
err = NewJiraError(resp, err)
|
|
|
|
}
|
|
|
|
|
2018-06-08 01:08:02 +02:00
|
|
|
return result, resp, err
|
2016-07-17 11:24:23 +02:00
|
|
|
}
|
2019-08-18 16:17:16 +02:00
|
|
|
|
2020-05-03 15:38:32 +02:00
|
|
|
// GetAllSprintsWithOptions wraps GetAllSprintsWithOptionsWithContext using the background context.
|
|
|
|
func (s *BoardService) GetAllSprintsWithOptions(boardID int, options *GetAllSprintsOptions) (*SprintsList, *Response, error) {
|
|
|
|
return s.GetAllSprintsWithOptionsWithContext(context.Background(), boardID, options)
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetBoardConfigurationWithContext will return a board configuration for a given board Id
|
2019-08-18 16:17:16 +02:00
|
|
|
// Jira API docs:https://developer.atlassian.com/cloud/jira/software/rest/#api-rest-agile-1-0-board-boardId-configuration-get
|
2020-05-03 15:38:32 +02:00
|
|
|
func (s *BoardService) GetBoardConfigurationWithContext(ctx context.Context, boardID int) (*BoardConfiguration, *Response, error) {
|
2019-08-18 16:17:16 +02:00
|
|
|
apiEndpoint := fmt.Sprintf("rest/agile/1.0/board/%d/configuration", boardID)
|
|
|
|
|
2020-05-03 15:38:32 +02:00
|
|
|
req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil)
|
2019-08-18 16:17:16 +02:00
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
return nil, nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
result := new(BoardConfiguration)
|
|
|
|
resp, err := s.client.Do(req, result)
|
|
|
|
if err != nil {
|
|
|
|
err = NewJiraError(resp, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return result, resp, err
|
|
|
|
|
|
|
|
}
|
2020-05-03 15:38:32 +02:00
|
|
|
|
|
|
|
// GetBoardConfiguration wraps GetBoardConfigurationWithContext using the background context.
|
|
|
|
func (s *BoardService) GetBoardConfiguration(boardID int) (*BoardConfiguration, *Response, error) {
|
|
|
|
return s.GetBoardConfigurationWithContext(context.Background(), boardID)
|
|
|
|
}
|