1
0
mirror of https://github.com/interviewstreet/go-jira.git synced 2025-02-01 13:07:50 +02:00

Add Transition API handling

Added TransitionService with 2 methods:
 * GetList for retrieving possible transitions for an issue
 * Create for creating transition and changing issue status in the
   process
This commit is contained in:
Maciej Kwiek 2016-06-20 17:14:55 +02:00
parent 6c73f2f575
commit 1fd364aea2
5 changed files with 255 additions and 0 deletions

View File

@ -13,6 +13,7 @@
* Authentication (HTTP Basic, OAuth, Session Cookie)
* Create and receive issues
* Create and retrieve issue transitions (status updates)
* Call every API endpoint of the JIRA, even it is not directly implemented in this library
This package is not JIRA API complete (yet), but you can call every API endpoint you want. See [Call a not implemented API endpoint](#call-a-not-implemented-api-endpoint) how to do this. For all possible API endpoints of JRIA have a look at [latest JIRA REST API documentation](https://docs.atlassian.com/jira/REST/latest/).

View File

@ -28,6 +28,7 @@ type Client struct {
Issue *IssueService
Project *ProjectService
Board *BoardService
Transition *TransitionService
}
// NewClient returns a new JIRA API client.
@ -55,6 +56,7 @@ func NewClient(httpClient *http.Client, baseURL string) (*Client, error) {
c.Issue = &IssueService{client: c}
c.Project = &ProjectService{client: c}
c.Board = &BoardService{client: c}
c.Transition = &TransitionService{client: c}
return c, nil
}

101
mocks/transitions.json Normal file
View File

@ -0,0 +1,101 @@
{
"expand": "transitions",
"transitions": [
{
"id": "2",
"name": "Close Issue",
"to": {
"self": "http://localhost:8090/jira/rest/api/2.0/status/10000",
"description": "The issue is currently being worked on.",
"iconUrl": "http://localhost:8090/jira/images/icons/progress.gif",
"name": "In Progress",
"id": "10000",
"statusCategory": {
"self": "http://localhost:8090/jira/rest/api/2.0/statuscategory/1",
"id": 1,
"key": "in-flight",
"colorName": "yellow",
"name": "In Progress"
}
},
"fields": {
"summary": {
"required": false,
"schema": {
"type": "array",
"items": "option",
"custom": "com.atlassian.jira.plugin.system.customfieldtypes:multiselect",
"customId": 10001
},
"name": "My Multi Select",
"hasDefaultValue": false,
"operations": [
"set",
"add"
],
"allowedValues": [
"red",
"blue"
]
}
}
},
{
"id": "711",
"name": "QA Review",
"to": {
"self": "http://localhost:8090/jira/rest/api/2.0/status/5",
"description": "The issue is closed.",
"iconUrl": "http://localhost:8090/jira/images/icons/closed.gif",
"name": "Closed",
"id": "5",
"statusCategory": {
"self": "http://localhost:8090/jira/rest/api/2.0/statuscategory/9",
"id": 9,
"key": "completed",
"colorName": "green"
}
},
"fields": {
"summary": {
"required": false,
"schema": {
"type": "array",
"items": "option",
"custom": "com.atlassian.jira.plugin.system.customfieldtypes:multiselect",
"customId": 10001
},
"name": "My Multi Select",
"hasDefaultValue": false,
"operations": [
"set",
"add"
],
"allowedValues": [
"red",
"blue"
]
},
"colour": {
"required": false,
"schema": {
"type": "array",
"items": "option",
"custom": "com.atlassian.jira.plugin.system.customfieldtypes:multiselect",
"customId": 10001
},
"name": "My Multi Select",
"hasDefaultValue": false,
"operations": [
"set",
"add"
],
"allowedValues": [
"red",
"blue"
]
}
}
}
]
}

75
transition.go Normal file
View File

@ -0,0 +1,75 @@
package jira
import (
"fmt"
)
// TransitionService handles transitions for JIRA issue.
type TransitionService struct {
client *Client
}
// Wrapper struct for search result
type transitionResult struct {
Transitions []Transition `json:transitions`
}
// Transition represents an issue transition in JIRA
type Transition struct {
ID string `json:"id"`
Name string `json:"name"`
Fields map[string]TransitionField `json:"fields"`
}
type TransitionField struct {
Required bool `json:"required"`
}
// CreatePayload is used for creating new issue transitions
type CreateTransitionPayload struct {
Transition TransitionPayload `json:"transition"`
}
type TransitionPayload struct {
ID string `json:"id"`
}
// GetList gets transitions available for given issue
//
// JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-getTransitions
func (s *TransitionService) GetList(id string) ([]Transition, *Response, error) {
apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/transitions?expand=transitions.fields", id)
req, err := s.client.NewRequest("GET", apiEndpoint, nil)
if err != nil {
return nil, nil, err
}
result := new(transitionResult)
resp, err := s.client.Do(req, result)
return result.Transitions, resp, err
}
// Basic transition creation. This simply creates transition with given ID for issue
// with given ID. It doesn't yet support anything else.
//
// JIRA API docs: https://docs.atlassian.com/jira/REST/latest/#api/2/issue-doTransition
func (s *TransitionService) Create(ticketID, transitionID string) (*Response, error) {
apiEndpoint := fmt.Sprintf("rest/api/2/issue/%s/transitions", ticketID)
payload := CreateTransitionPayload{
Transition: TransitionPayload{
ID: transitionID,
},
}
req, err := s.client.NewRequest("POST", apiEndpoint, payload)
if err != nil {
return nil, err
}
resp, err := s.client.Do(req, nil)
if err != nil {
return nil, err
}
return resp, nil
}

76
transition_test.go Normal file
View File

@ -0,0 +1,76 @@
package jira
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"testing"
)
func TestTransitionGetList(t *testing.T) {
setup()
defer teardown()
testAPIEndpoint := "/rest/api/2/issue/123/transitions"
raw, err := ioutil.ReadFile("./mocks/transitions.json")
if err != nil {
t.Error(err.Error())
}
testMux.HandleFunc(testAPIEndpoint, func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "GET")
testRequestURL(t, r, testAPIEndpoint)
fmt.Fprint(w, string(raw))
})
transitions, _, err := testClient.Transition.GetList("123")
if err != nil {
t.Errorf("Got error: %v", err)
}
if transitions == nil {
t.Error("Expected transition list. Got nil.")
}
if len(transitions) != 2 {
t.Errorf("Expected 2 transitions. Got %d", len(transitions))
}
if transitions[0].Fields["summary"].Required != false {
t.Errorf("First transition summary field should not be required")
}
}
func TestTransitionCreate(t *testing.T) {
setup()
defer teardown()
testAPIEndpoint := "/rest/api/2/issue/123/transitions"
transitionID := "22"
testMux.HandleFunc(testAPIEndpoint, func(w http.ResponseWriter, r *http.Request) {
testMethod(t, r, "POST")
testRequestURL(t, r, testAPIEndpoint)
decoder := json.NewDecoder(r.Body)
var payload CreateTransitionPayload
err := decoder.Decode(&payload)
if err != nil {
t.Error("Got error: %v", err)
}
if strings.Compare(payload.Transition.ID, transitionID) != 0 {
t.Errorf("Expected %s to be in payload, got %s instead", transitionID, payload.Transition.ID)
}
})
_, err := testClient.Transition.Create("123", transitionID)
if err != nil {
t.Error("Got error: %v", err)
}
}