package jira import ( "context" "fmt" "strconv" "time" ) // BoardService handles Agile Boards for the Jira instance / API. // // Jira API docs: https://docs.atlassian.com/jira-software/REST/server/ type BoardService struct { client *Client } // BoardsList reflects a list of agile boards type BoardsList 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 []Board `json:"values" structs:"values"` } // Board represents a Jira agile board type Board struct { 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"` } // BoardListOptions specifies the optional parameters to the BoardService.GetList type BoardListOptions struct { // BoardType filters results to boards of the specified type. // Valid values: scrum, kanban. BoardType string `url:"type,omitempty"` // Name filters results to boards that match or partially match the specified name. Name string `url:"name,omitempty"` // 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 } // 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"` } // Sprint represents a sprint on Jira agile board type Sprint struct { 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"` } // 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"` } // GetAllBoardsWithContext will returns all boards. This only includes boards that the user has permission to view. // // Jira API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-getAllBoards func (s *BoardService) GetAllBoardsWithContext(ctx context.Context, opt *BoardListOptions) (*BoardsList, *Response, error) { apiEndpoint := "rest/agile/1.0/board" url, err := addOptions(apiEndpoint, opt) if err != nil { return nil, nil, err } req, err := s.client.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { return nil, nil, err } boards := new(BoardsList) resp, err := s.client.Do(req, boards) if err != nil { jerr := NewJiraError(resp, err) return nil, resp, jerr } return boards, resp, err } // 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. // 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 func (s *BoardService) GetBoardWithContext(ctx context.Context, boardID int) (*Board, *Response, error) { apiEndpoint := fmt.Sprintf("rest/agile/1.0/board/%v", boardID) req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil) if err != nil { return nil, nil, err } board := new(Board) resp, err := s.client.Do(req, board) if err != nil { jerr := NewJiraError(resp, err) return nil, resp, jerr } return board, resp, nil } // 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. // 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). // // Jira API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-createBoard func (s *BoardService) CreateBoardWithContext(ctx context.Context, board *Board) (*Board, *Response, error) { apiEndpoint := "rest/agile/1.0/board" req, err := s.client.NewRequestWithContext(ctx, "POST", apiEndpoint, board) if err != nil { return nil, nil, err } responseBoard := new(Board) resp, err := s.client.Do(req, responseBoard) if err != nil { jerr := NewJiraError(resp, err) return nil, resp, jerr } return responseBoard, resp, nil } // 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. // // Jira API docs: https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-deleteBoard func (s *BoardService) DeleteBoardWithContext(ctx context.Context, boardID int) (*Board, *Response, error) { apiEndpoint := fmt.Sprintf("rest/agile/1.0/board/%v", boardID) req, err := s.client.NewRequestWithContext(ctx, "DELETE", apiEndpoint, nil) if err != nil { return nil, nil, err } resp, err := s.client.Do(req, nil) if err != nil { err = NewJiraError(resp, err) } return nil, resp, err } // 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. // 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 func (s *BoardService) GetAllSprintsWithContext(ctx context.Context, boardID string) ([]Sprint, *Response, error) { 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 } // 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 // 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 func (s *BoardService) GetAllSprintsWithOptionsWithContext(ctx context.Context, boardID int, options *GetAllSprintsOptions) (*SprintsList, *Response, error) { apiEndpoint := fmt.Sprintf("rest/agile/1.0/board/%d/sprint", boardID) url, err := addOptions(apiEndpoint, options) if err != nil { return nil, nil, err } req, err := s.client.NewRequestWithContext(ctx, "GET", url, nil) if err != nil { return nil, nil, err } result := new(SprintsList) resp, err := s.client.Do(req, result) if err != nil { err = NewJiraError(resp, err) } return result, resp, err } // 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 // Jira API docs:https://developer.atlassian.com/cloud/jira/software/rest/#api-rest-agile-1-0-board-boardId-configuration-get func (s *BoardService) GetBoardConfigurationWithContext(ctx context.Context, boardID int) (*BoardConfiguration, *Response, error) { apiEndpoint := fmt.Sprintf("rest/agile/1.0/board/%d/configuration", boardID) req, err := s.client.NewRequestWithContext(ctx, "GET", apiEndpoint, nil) 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 } // GetBoardConfiguration wraps GetBoardConfigurationWithContext using the background context. func (s *BoardService) GetBoardConfiguration(boardID int) (*BoardConfiguration, *Response, error) { return s.GetBoardConfigurationWithContext(context.Background(), boardID) }