mirror of
https://github.com/interviewstreet/go-jira.git
synced 2025-02-03 13:11:49 +02:00
Merge branch 'develop' of https://github.com/EvgenKostenko/go-jira into EvgenKostenko-develop
* 'develop' of https://github.com/EvgenKostenko/go-jira: cosmetic fix in boards imports, tests in projects add delete board with tests + go fmt add board create with tests go fmt boards Implement BoardService and get boards list with parameters remove old project Add boards and fix some bugs in project add boards service
This commit is contained in:
commit
ce47602482
124
board.go
Normal file
124
board.go
Normal file
@ -0,0 +1,124 @@
|
||||
package jira
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type BoardService struct {
|
||||
client *Client
|
||||
}
|
||||
|
||||
//Type for boards list
|
||||
type BoardsList struct {
|
||||
MaxResults int `json:"maxResults"`
|
||||
StartAt int `json:"startAt"`
|
||||
Total int `json:"total"`
|
||||
IsLast bool `json:"isLast"`
|
||||
Values []Board `json:"values"`
|
||||
}
|
||||
|
||||
// Board represents a JIRA board
|
||||
type Board struct {
|
||||
ID int `json:"id",omitempty"`
|
||||
Self string `json:"self",omitempty"`
|
||||
Name string `json:"name",omitempty"`
|
||||
Type string `json:"type",omitempty"`
|
||||
FilterId int `omitempty`
|
||||
}
|
||||
|
||||
// BoardListOptions specifies the optional parameters to the BoardService.GetList
|
||||
type BoardListOptions struct {
|
||||
// Filters results to boards of the specified type.
|
||||
// Valid values: scrum, kanban.
|
||||
BoardType string `url:"boardType,omitempty"`
|
||||
// Filters results to boards that match or partially match the specified name.
|
||||
Name string `url:"name,omitempty"`
|
||||
// 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"`
|
||||
// ListOptions specifies the optional parameters to various List methods that
|
||||
// support pagination.
|
||||
// Pagination is used for the JIRA REST APIs to conserve server resources and limit
|
||||
// response size for resources that return potentially large collection of items.
|
||||
// A request to a pages API will result in a values array wrapped in a JSON object with some paging metadata
|
||||
// Default Pagination options
|
||||
// The starting index of the returned projects. Base index: 0.
|
||||
StartAt int `url:"startAt,omitempty"`
|
||||
// The maximum number of projects to return per page. Default: 50.
|
||||
MaxResults int `url:"maxResults,omitempty"`
|
||||
}
|
||||
|
||||
// Get all boards form jira
|
||||
//
|
||||
// JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/project-getAllProjects
|
||||
func (s *BoardService) GetList(opt *BoardListOptions) (*BoardsList, *http.Response, error) {
|
||||
apiEndpoint := "rest/agile/1.0/board"
|
||||
url, err := addOptions(apiEndpoint, opt)
|
||||
req, err := s.client.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
boards := new(BoardsList)
|
||||
resp, err := s.client.Do(req, boards)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
|
||||
return boards, resp, err
|
||||
}
|
||||
|
||||
// Returns the board for the given board Id. This board will only be returned if the user has permission to view it.
|
||||
func (s *BoardService) Get(boardID int) (*Board, *http.Response, error) {
|
||||
apiEndpoint := fmt.Sprintf("rest/agile/1.0/board/%v", boardID)
|
||||
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
board := new(Board)
|
||||
resp, err := s.client.Do(req, board)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return board, resp, nil
|
||||
}
|
||||
|
||||
// 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) Create(board *Board) (*Board, *http.Response, error) {
|
||||
|
||||
apiEndpoint := "rest/agile/1.0/board"
|
||||
req, err := s.client.NewRequest("POST", apiEndpoint, board)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
responseBoard := new(Board)
|
||||
resp, err := s.client.Do(req, responseBoard)
|
||||
if err != nil {
|
||||
return nil, resp, err
|
||||
}
|
||||
return responseBoard, resp, nil
|
||||
}
|
||||
|
||||
// Deletes the board.
|
||||
//
|
||||
// https://docs.atlassian.com/jira-software/REST/cloud/#agile/1.0/board-deleteBoard
|
||||
func (s *BoardService) Delete(boardID int) (*Board, *http.Response, error) {
|
||||
apiEndpoint := fmt.Sprintf("rest/agile/1.0/board/%v", boardID)
|
||||
req, err := s.client.NewRequest("DELETE", apiEndpoint, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
resp, err := s.client.Do(req, nil)
|
||||
return nil, resp, err
|
||||
}
|
154
board_test.go
Normal file
154
board_test.go
Normal file
@ -0,0 +1,154 @@
|
||||
package jira
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBoardsGetAll(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
testAPIEdpoint := "/rest/agile/1.0/board"
|
||||
|
||||
raw, err := ioutil.ReadFile("./mocks/all_boards.json")
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
testMux.HandleFunc(testAPIEdpoint, func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, "GET")
|
||||
testRequestURL(t, r, testAPIEdpoint)
|
||||
fmt.Fprint(w, string(raw))
|
||||
})
|
||||
|
||||
projects, _, err := testClient.Board.GetList(nil)
|
||||
if projects == nil {
|
||||
t.Error("Expected boards list. Boards list is nil")
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("Error given: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Test with params
|
||||
func TestBoardsGetFiltered(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
testAPIEdpoint := "/rest/agile/1.0/board"
|
||||
|
||||
raw, err := ioutil.ReadFile("./mocks/all_boards_filtered.json")
|
||||
if err != nil {
|
||||
t.Error(err.Error())
|
||||
}
|
||||
testMux.HandleFunc(testAPIEdpoint, func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, "GET")
|
||||
testRequestURL(t, r, testAPIEdpoint)
|
||||
fmt.Fprint(w, string(raw))
|
||||
})
|
||||
|
||||
boardsListOptions := &BoardListOptions{
|
||||
BoardType: "scrum",
|
||||
Name: "Test",
|
||||
ProjectKeyOrId: "TE",
|
||||
StartAt: 1,
|
||||
MaxResults: 10,
|
||||
}
|
||||
|
||||
projects, _, err := testClient.Board.GetList(boardsListOptions)
|
||||
if projects == nil {
|
||||
t.Error("Expected boards list. Boards list is nil")
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("Error given: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoardGet(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
testAPIEdpoint := "/rest/agile/1.0/board/1"
|
||||
|
||||
testMux.HandleFunc(testAPIEdpoint, func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, "GET")
|
||||
testRequestURL(t, r, testAPIEdpoint)
|
||||
fmt.Fprint(w, `{"id":4,"self":"https://test.jira.org/rest/agile/1.0/board/1","name":"Test Weekly","type":"scrum"}`)
|
||||
})
|
||||
|
||||
board, _, err := testClient.Board.Get(1)
|
||||
if board == nil {
|
||||
t.Error("Expected board list. Board list is nil")
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("Error given: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoardGet_NoBoard(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
testAPIEndpoint := "/rest/api/2/board/99999999"
|
||||
|
||||
testMux.HandleFunc(testAPIEndpoint, func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, "GET")
|
||||
testRequestURL(t, r, testAPIEndpoint)
|
||||
fmt.Fprint(w, nil)
|
||||
})
|
||||
|
||||
board, resp, err := testClient.Board.Get(99999999)
|
||||
if board != nil {
|
||||
t.Errorf("Expected nil. Got %s", err)
|
||||
}
|
||||
|
||||
if resp.Status == "404" {
|
||||
t.Errorf("Expected status 404. Got %s", resp.Status)
|
||||
}
|
||||
if err == nil {
|
||||
t.Errorf("Error given: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoardCreate(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
testMux.HandleFunc("/rest/agile/1.0/board", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, "POST")
|
||||
testRequestURL(t, r, "/rest/agile/1.0/board")
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
fmt.Fprint(w, `{"id":17,"self":"https://test.jira.org/rest/agile/1.0/board/17","name":"Test","type":"kanban"}`)
|
||||
})
|
||||
|
||||
b := &Board{
|
||||
Name: "Test",
|
||||
Type: "kanban",
|
||||
FilterId: 17,
|
||||
}
|
||||
issue, _, err := testClient.Board.Create(b)
|
||||
if issue == nil {
|
||||
t.Error("Expected board. Board is nil")
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("Error given: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBoardDelete(t *testing.T) {
|
||||
setup()
|
||||
defer teardown()
|
||||
testMux.HandleFunc("/rest/agile/1.0/board/1", func(w http.ResponseWriter, r *http.Request) {
|
||||
testMethod(t, r, "DELETE")
|
||||
testRequestURL(t, r, "/rest/agile/1.0/board/1")
|
||||
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
fmt.Fprint(w, `{}`)
|
||||
})
|
||||
|
||||
_, resp, err := testClient.Board.Delete(1)
|
||||
if resp.StatusCode != 204 {
|
||||
t.Error("Expected board not deleted.")
|
||||
}
|
||||
if err != nil {
|
||||
t.Errorf("Error given: %s", err)
|
||||
}
|
||||
}
|
27
jira.go
27
jira.go
@ -7,6 +7,9 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"reflect"
|
||||
|
||||
"github.com/google/go-querystring/query"
|
||||
)
|
||||
|
||||
// A Client manages communication with the JIRA API.
|
||||
@ -24,6 +27,7 @@ type Client struct {
|
||||
Authentication *AuthenticationService
|
||||
Issue *IssueService
|
||||
Project *ProjectService
|
||||
Board *BoardService
|
||||
}
|
||||
|
||||
// NewClient returns a new JIRA API client.
|
||||
@ -50,6 +54,7 @@ func NewClient(httpClient *http.Client, baseURL string) (*Client, error) {
|
||||
c.Authentication = &AuthenticationService{client: c}
|
||||
c.Issue = &IssueService{client: c}
|
||||
c.Project = &ProjectService{client: c}
|
||||
c.Board = &BoardService{client: c}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
@ -92,6 +97,28 @@ func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Requ
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// addOptions adds the parameters in opt as URL query parameters to s. opt
|
||||
// must be a struct whose fields may contain "url" tags.
|
||||
func addOptions(s string, opt interface{}) (string, error) {
|
||||
v := reflect.ValueOf(opt)
|
||||
if v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
return s, nil
|
||||
}
|
||||
|
||||
u, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return s, err
|
||||
}
|
||||
|
||||
qs, err := query.Values(opt)
|
||||
if err != nil {
|
||||
return s, err
|
||||
}
|
||||
|
||||
u.RawQuery = qs.Encode()
|
||||
return u.String(), nil
|
||||
}
|
||||
|
||||
// NewMultiPartRequest creates an API request including a multi-part file.
|
||||
// A relative URL can be provided in urlStr, in which case it is resolved relative to the baseURL of the Client.
|
||||
// Relative URLs should always be specified without a preceding slash.
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
@ -52,7 +53,7 @@ func testMethod(t *testing.T, r *http.Request, want string) {
|
||||
}
|
||||
|
||||
func testRequestURL(t *testing.T, r *http.Request, want string) {
|
||||
if got := r.URL.String(); got != want {
|
||||
if got := r.URL.String(); !strings.HasPrefix(got, want) {
|
||||
t.Errorf("Request URL: %v, want %v", got, want)
|
||||
}
|
||||
}
|
||||
|
43
mocks/all_boards.json
Normal file
43
mocks/all_boards.json
Normal file
@ -0,0 +1,43 @@
|
||||
{
|
||||
"maxResults": 50,
|
||||
"startAt": 0,
|
||||
"isLast": true,
|
||||
"values": [
|
||||
{
|
||||
"id": 4,
|
||||
"self": "https://test.jira.org/rest/agile/1.0/board/4",
|
||||
"name": "Test Weekly",
|
||||
"type": "scrum"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"self": "https://test.jira.org/rest/agile/1.0/board/5",
|
||||
"name": "Test Production Support",
|
||||
"type": "kanban"
|
||||
},
|
||||
{
|
||||
"id": 6,
|
||||
"self": "https://test.jira.org/rest/agile/1.0/board/6",
|
||||
"name": "Test To Give",
|
||||
"type": "kanban"
|
||||
},
|
||||
{
|
||||
"id": 7,
|
||||
"self": "https://test.jira.org/rest/agile/1.0/board/7",
|
||||
"name": "Test Journey App",
|
||||
"type": "kanban"
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"self": "https://test.jira.org/rest/agile/1.0/board/9",
|
||||
"name": "Testix",
|
||||
"type": "scrum"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"self": "https://test.jira.org/rest/agile/1.0/board/1",
|
||||
"name": "Test Mobile",
|
||||
"type": "scrum"
|
||||
}
|
||||
]
|
||||
}
|
25
mocks/all_boards_filtered.json
Normal file
25
mocks/all_boards_filtered.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"maxResults": 10,
|
||||
"startAt": 1,
|
||||
"isLast": true,
|
||||
"values": [
|
||||
{
|
||||
"id": 4,
|
||||
"self": "https://test.jira.org/rest/agile/1.0/board/4",
|
||||
"name": "Test Weekly",
|
||||
"type": "scrum"
|
||||
},
|
||||
{
|
||||
"id": 9,
|
||||
"self": "https://test.jira.org/rest/agile/1.0/board/9",
|
||||
"name": "Testix",
|
||||
"type": "scrum"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"self": "https://test.jira.org/rest/agile/1.0/board/1",
|
||||
"name": "Test Mobile",
|
||||
"type": "scrum"
|
||||
}
|
||||
]
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user