From fb1ce2269961b9f5417019536bc486b694217b49 Mon Sep 17 00:00:00 2001 From: Thibaut Rousseau Date: Mon, 25 Jun 2018 17:48:53 +0200 Subject: [PATCH 1/4] feat: Add ResolutionService to retrieve resolutions --- issue.go | 9 --- jira.go | 2 + jira_test.go | 3 + mocks/all_resolutions.json | 140 +++++++++++++++++++++++++++++++++++++ resolution.go | 35 ++++++++++ resolution_test.go | 32 +++++++++ 6 files changed, 212 insertions(+), 9 deletions(-) create mode 100644 mocks/all_resolutions.json create mode 100644 resolution.go create mode 100644 resolution_test.go diff --git a/issue.go b/issue.go index a9e5f738..f51af5bb 100644 --- a/issue.go +++ b/issue.go @@ -226,15 +226,6 @@ type IssueType struct { AvatarID int `json:"avatarId,omitempty" structs:"avatarId,omitempty"` } -// Resolution represents a resolution of a JIRA issue. -// Typical types are "Fixed", "Suspended", "Won't Fix", ... -type Resolution struct { - Self string `json:"self" structs:"self"` - ID string `json:"id" structs:"id"` - Description string `json:"description" structs:"description"` - Name string `json:"name" structs:"name"` -} - // Watches represents a type of how many and which user are "observing" a JIRA issue to track the status / updates. type Watches struct { Self string `json:"self,omitempty" structs:"self,omitempty"` diff --git a/jira.go b/jira.go index b6e6b6f5..1317bd3e 100644 --- a/jira.go +++ b/jira.go @@ -37,6 +37,7 @@ type Client struct { Version *VersionService Priority *PriorityService Field *FieldService + Resolution *ResolutionService } // NewClient returns a new JIRA API client. @@ -75,6 +76,7 @@ func NewClient(httpClient *http.Client, baseURL string) (*Client, error) { c.Version = &VersionService{client: c} c.Priority = &PriorityService{client: c} c.Field = &FieldService{client: c} + c.Resolution = &ResolutionService{client: c} return c, nil } diff --git a/jira_test.go b/jira_test.go index bb138711..43a888a6 100644 --- a/jira_test.go +++ b/jira_test.go @@ -118,6 +118,9 @@ func TestNewClient_WithServices(t *testing.T) { if c.Priority == nil { t.Error("No PriorityService provided") } + if c.Resolution == nil { + t.Error("No ResolutionService provided") + } } func TestCheckResponse(t *testing.T) { diff --git a/mocks/all_resolutions.json b/mocks/all_resolutions.json new file mode 100644 index 00000000..8d3d96b4 --- /dev/null +++ b/mocks/all_resolutions.json @@ -0,0 +1,140 @@ +[ + { + "self": "https://issues.apache.org/jira/rest/api/2/resolution/1", + "id": "1", + "description": "A fix for this issue is checked into the tree and tested.", + "name": "Fixed" + }, + { + "self": "https://issues.apache.org/jira/rest/api/2/resolution/2", + "id": "2", + "description": "The problem described is an issue which will never be fixed.", + "name": "Won't Fix" + }, + { + "self": "https://issues.apache.org/jira/rest/api/2/resolution/3", + "id": "3", + "description": "The problem is a duplicate of an existing issue.", + "name": "Duplicate" + }, + { + "self": "https://issues.apache.org/jira/rest/api/2/resolution/6", + "id": "6", + "description": "The problem isn't valid and it can't be fixed.", + "name": "Invalid" + }, + { + "self": "https://issues.apache.org/jira/rest/api/2/resolution/4", + "id": "4", + "description": "The problem is not completely described.", + "name": "Incomplete" + }, + { + "self": "https://issues.apache.org/jira/rest/api/2/resolution/5", + "id": "5", + "description": "All attempts at reproducing this issue failed, or not enough information was available to reproduce the issue. Reading the code produces no clues as to why this behavior would occur. If more information appears later, please reopen the issue.", + "name": "Cannot Reproduce" + }, + { + "self": "https://issues.apache.org/jira/rest/api/2/resolution/7", + "id": "7", + "description": "Later", + "name": "Later" + }, + { + "self": "https://issues.apache.org/jira/rest/api/2/resolution/8", + "id": "8", + "description": "The described issue is not actually a problem - it is as designed.", + "name": "Not A Problem" + }, + { + "self": "https://issues.apache.org/jira/rest/api/2/resolution/9", + "id": "9", + "description": "Unresolved", + "name": "Unresolved" + }, + { + "self": "https://issues.apache.org/jira/rest/api/2/resolution/10", + "id": "10", + "description": "As the name suggests", + "name": "Implemented" + }, + { + "self": "https://issues.apache.org/jira/rest/api/2/resolution/11", + "id": "11", + "description": "", + "name": "Done" + }, + { + "self": "https://issues.apache.org/jira/rest/api/2/resolution/10000", + "id": "10000", + "description": "This issue was automatically closed", + "name": "Auto Closed" + }, + { + "self": "https://issues.apache.org/jira/rest/api/2/resolution/10001", + "id": "10001", + "description": "", + "name": "Pending Closed" + }, + { + "self": "https://issues.apache.org/jira/rest/api/2/resolution/10002", + "id": "10002", + "description": "REMIND", + "name": "REMIND" + }, + { + "self": "https://issues.apache.org/jira/rest/api/2/resolution/10003", + "id": "10003", + "description": "Resolved", + "name": "Resolved" + }, + { + "self": "https://issues.apache.org/jira/rest/api/2/resolution/10004", + "id": "10004", + "description": "Not A Bug", + "name": "Not A Bug" + }, + { + "self": "https://issues.apache.org/jira/rest/api/2/resolution/10005", + "id": "10005", + "description": "Workaround", + "name": "Workaround" + }, + { + "self": "https://issues.apache.org/jira/rest/api/2/resolution/10006", + "id": "10006", + "description": "Staged", + "name": "Staged" + }, + { + "self": "https://issues.apache.org/jira/rest/api/2/resolution/10007", + "id": "10007", + "description": "Delivered", + "name": "Delivered" + }, + { + "self": "https://issues.apache.org/jira/rest/api/2/resolution/10008", + "id": "10008", + "description": "Information Provided", + "name": "Information Provided" + }, + { + "self": "https://issues.apache.org/jira/rest/api/2/resolution/10009", + "id": "10009", + "description": "Works for Me", + "name": "Works for Me" + }, + { + "self": "https://issues.apache.org/jira/rest/api/2/resolution/10010", + "id": "10010", + "description": "Feedback Received", + "name": "Feedback Received" + }, + { + "self": "https://issues.apache.org/jira/rest/api/2/resolution/10011", + "id": "10011", + "description": "Won't Do", + "name": "Won't Do" + } +] diff --git a/resolution.go b/resolution.go new file mode 100644 index 00000000..36a651fb --- /dev/null +++ b/resolution.go @@ -0,0 +1,35 @@ +package jira + +// ResolutionService handles resolutions for the JIRA instance / API. +// +// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-Resolution +type ResolutionService struct { + client *Client +} + +// Resolution represents a resolution of a JIRA issue. +// Typical types are "Fixed", "Suspended", "Won't Fix", ... +type Resolution struct { + Self string `json:"self" structs:"self"` + ID string `json:"id" structs:"id"` + Description string `json:"description" structs:"description"` + Name string `json:"name" structs:"name"` +} + +// GetList gets all resolutions from JIRA +// +// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-resolution-get +func (s *ResolutionService) GetList() ([]Resolution, *Response, error) { + apiEndpoint := "rest/api/2/resolution" + req, err := s.client.NewRequest("GET", apiEndpoint, nil) + if err != nil { + return nil, nil, err + } + + resolutionList := []Resolution{} + resp, err := s.client.Do(req, &resolutionList) + if err != nil { + return nil, resp, NewJiraError(resp, err) + } + return resolutionList, resp, nil +} diff --git a/resolution_test.go b/resolution_test.go new file mode 100644 index 00000000..7c01e751 --- /dev/null +++ b/resolution_test.go @@ -0,0 +1,32 @@ +package jira + +import ( + "fmt" + "io/ioutil" + "net/http" + "testing" +) + +func TestResolutionService_GetList(t *testing.T) { + setup() + defer teardown() + testAPIEdpoint := "/rest/api/2/resolution" + + raw, err := ioutil.ReadFile("./mocks/all_resolutions.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)) + }) + + resolution, _, err := testClient.Resolution.GetList() + if resolution == nil { + t.Error("Expected resolution list. Resolution list is nil") + } + if err != nil { + t.Errorf("Error given: %s", err) + } +} From 52e8877e24695e359cedbf8ec3f482e386e7e416 Mon Sep 17 00:00:00 2001 From: Thibaut Rousseau Date: Mon, 25 Jun 2018 17:52:57 +0200 Subject: [PATCH 2/4] doc: Fix comments --- field.go | 9 ++++----- field_test.go | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/field.go b/field.go index 25550922..257d4f99 100644 --- a/field.go +++ b/field.go @@ -1,14 +1,13 @@ package jira -// PriorityService handles priorities for the JIRA instance / API. +// FieldService handles fields for the JIRA instance / API. // -// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-Priority +// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-Field type FieldService struct { client *Client } -// Priority represents a priority of a JIRA issue. -// Typical types are "Normal", "Moderate", "Urgent", ... +// Field represents a field of a JIRA issue. type Field struct { ID string `json:"id,omitempty" structs:"id,omitempty"` Key string `json:"key,omitempty" structs:"key,omitempty"` @@ -27,7 +26,7 @@ type FieldSchema struct { // GetList gets all fields from JIRA // -// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-priority-get +// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-field-get func (s *FieldService) GetList() ([]Field, *Response, error) { apiEndpoint := "rest/api/2/field" req, err := s.client.NewRequest("GET", apiEndpoint, nil) diff --git a/field_test.go b/field_test.go index 3121061b..a2deb533 100644 --- a/field_test.go +++ b/field_test.go @@ -24,7 +24,7 @@ func TestFieldService_GetList(t *testing.T) { fields, _, err := testClient.Field.GetList() if fields == nil { - t.Error("Expected priority list. Priority list is nil") + t.Error("Expected field list. Field list is nil") } if err != nil { t.Errorf("Error given: %s", err) From 6223ddd83345e2a560daa90994b4bf8fdd7c0129 Mon Sep 17 00:00:00 2001 From: Thibaut Rousseau Date: Mon, 25 Jun 2018 18:35:15 +0200 Subject: [PATCH 3/4] feat: Add status category constants --- issue.go | 10 ---------- statuscategory.go | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 10 deletions(-) create mode 100644 statuscategory.go diff --git a/issue.go b/issue.go index f51af5bb..0949ea12 100644 --- a/issue.go +++ b/issue.go @@ -270,16 +270,6 @@ type Status struct { StatusCategory StatusCategory `json:"statusCategory" structs:"statusCategory"` } -// StatusCategory represents the category a status belongs to. -// Those categories can be user defined in every JIRA instance. -type StatusCategory struct { - Self string `json:"self" structs:"self"` - ID int `json:"id" structs:"id"` - Name string `json:"name" structs:"name"` - Key string `json:"key" structs:"key"` - ColorName string `json:"colorName" structs:"colorName"` -} - // Progress represents the progress of a JIRA issue. type Progress struct { Progress int `json:"progress" structs:"progress"` diff --git a/statuscategory.go b/statuscategory.go new file mode 100644 index 00000000..c5365568 --- /dev/null +++ b/statuscategory.go @@ -0,0 +1,19 @@ +package jira + +// StatusCategory represents the category a status belongs to. +// Those categories can be user defined in every JIRA instance. +type StatusCategory struct { + Self string `json:"self" structs:"self"` + ID int `json:"id" structs:"id"` + Name string `json:"name" structs:"name"` + Key string `json:"key" structs:"key"` + ColorName string `json:"colorName" structs:"colorName"` +} + +// These constants are the keys of the default JIRA status categories +const ( + StatusCategoryComplete = "done" + StatusCategoryInProgress = "indeterminate" + StatusCategoryToDo = "new" + StatusCategoryUndefined = "undefined" +) From 049a756bbe95d83e783ffd6d4d46455e0ab672f1 Mon Sep 17 00:00:00 2001 From: Thibaut Rousseau Date: Tue, 26 Jun 2018 09:41:46 +0200 Subject: [PATCH 4/4] feat: Add StatusCategory GetList --- jira.go | 2 ++ jira_test.go | 3 +++ mocks/all_statuscategories.json | 30 ++++++++++++++++++++++++++++++ statuscategory.go | 25 +++++++++++++++++++++++++ statuscategory_test.go | 32 ++++++++++++++++++++++++++++++++ 5 files changed, 92 insertions(+) create mode 100644 mocks/all_statuscategories.json create mode 100644 statuscategory_test.go diff --git a/jira.go b/jira.go index 1317bd3e..6548c484 100644 --- a/jira.go +++ b/jira.go @@ -38,6 +38,7 @@ type Client struct { Priority *PriorityService Field *FieldService Resolution *ResolutionService + StatusCategory *StatusCategoryService } // NewClient returns a new JIRA API client. @@ -77,6 +78,7 @@ func NewClient(httpClient *http.Client, baseURL string) (*Client, error) { c.Priority = &PriorityService{client: c} c.Field = &FieldService{client: c} c.Resolution = &ResolutionService{client: c} + c.StatusCategory = &StatusCategoryService{client: c} return c, nil } diff --git a/jira_test.go b/jira_test.go index 43a888a6..32c6ea3e 100644 --- a/jira_test.go +++ b/jira_test.go @@ -121,6 +121,9 @@ func TestNewClient_WithServices(t *testing.T) { if c.Resolution == nil { t.Error("No ResolutionService provided") } + if c.StatusCategory == nil { + t.Error("No StatusCategoryService provided") + } } func TestCheckResponse(t *testing.T) { diff --git a/mocks/all_statuscategories.json b/mocks/all_statuscategories.json new file mode 100644 index 00000000..ec597113 --- /dev/null +++ b/mocks/all_statuscategories.json @@ -0,0 +1,30 @@ +[ + { + "self": "https://issues.apache.org/jira/rest/api/2/statuscategory/1", + "id": 1, + "key": "undefined", + "colorName": "medium-gray", + "name": "No Category" + }, + { + "self": "https://issues.apache.org/jira/rest/api/2/statuscategory/2", + "id": 2, + "key": "new", + "colorName": "blue-gray", + "name": "To Do" + }, + { + "self": "https://issues.apache.org/jira/rest/api/2/statuscategory/4", + "id": 4, + "key": "indeterminate", + "colorName": "yellow", + "name": "In Progress" + }, + { + "self": "https://issues.apache.org/jira/rest/api/2/statuscategory/3", + "id": 3, + "key": "done", + "colorName": "green", + "name": "Done" + } +] diff --git a/statuscategory.go b/statuscategory.go index c5365568..05db4207 100644 --- a/statuscategory.go +++ b/statuscategory.go @@ -1,5 +1,12 @@ package jira +// StatusCategoryService handles status categories for the JIRA instance / API. +// +// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-Statuscategory +type StatusCategoryService struct { + client *Client +} + // StatusCategory represents the category a status belongs to. // Those categories can be user defined in every JIRA instance. type StatusCategory struct { @@ -17,3 +24,21 @@ const ( StatusCategoryToDo = "new" StatusCategoryUndefined = "undefined" ) + +// GetList gets all status categories from JIRA +// +// JIRA API docs: https://developer.atlassian.com/cloud/jira/platform/rest/#api-api-2-statuscategory-get +func (s *StatusCategoryService) GetList() ([]StatusCategory, *Response, error) { + apiEndpoint := "rest/api/2/statuscategory" + req, err := s.client.NewRequest("GET", apiEndpoint, nil) + if err != nil { + return nil, nil, err + } + + statusCategoryList := []StatusCategory{} + resp, err := s.client.Do(req, &statusCategoryList) + if err != nil { + return nil, resp, NewJiraError(resp, err) + } + return statusCategoryList, resp, nil +} diff --git a/statuscategory_test.go b/statuscategory_test.go new file mode 100644 index 00000000..3f8b7ecb --- /dev/null +++ b/statuscategory_test.go @@ -0,0 +1,32 @@ +package jira + +import ( + "fmt" + "io/ioutil" + "net/http" + "testing" +) + +func TestStatusCategoryService_GetList(t *testing.T) { + setup() + defer teardown() + testAPIEdpoint := "/rest/api/2/statuscategory" + + raw, err := ioutil.ReadFile("./mocks/all_statuscategories.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)) + }) + + statusCategory, _, err := testClient.StatusCategory.GetList() + if statusCategory == nil { + t.Error("Expected statusCategory list. StatusCategory list is nil") + } + if err != nil { + t.Errorf("Error given: %s", err) + } +}