From 6a34ec5b8ae2822dadfe4d2533e64b3ca251b8c3 Mon Sep 17 00:00:00 2001 From: Kyle Bradford Date: Tue, 31 Mar 2020 16:31:00 -0600 Subject: [PATCH] feat(IssueService): allow empty JQL --- issue.go | 29 ++++++++++++++++++----------- issue_test.go | 42 ++++++++++++++++++++++++++++++++++++------ 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/issue.go b/issue.go index f53cb136..a0637346 100644 --- a/issue.go +++ b/issue.go @@ -10,6 +10,7 @@ import ( "net/http" "net/url" "reflect" + "strconv" "strings" "time" @@ -956,29 +957,35 @@ func (s *IssueService) AddLink(issueLink *IssueLink) (*Response, error) { // // JIRA API docs: https://developer.atlassian.com/jiradev/jira-apis/jira-rest-apis/jira-rest-api-tutorials/jira-rest-api-example-query-issues func (s *IssueService) Search(jql string, options *SearchOptions) ([]Issue, *Response, error) { - var u string - if options == nil { - u = fmt.Sprintf("rest/api/2/search?jql=%s", url.QueryEscape(jql)) - } else { - u = "rest/api/2/search?jql=" + url.QueryEscape(jql) + u := url.URL{ + Path: "rest/api/2/search", + } + uv := url.Values{} + if jql != "" { + uv.Add("jql", url.QueryEscape(jql)) + } + + if options != nil { if options.StartAt != 0 { - u += fmt.Sprintf("&startAt=%d", options.StartAt) + uv.Add("startAt", strconv.Itoa(options.StartAt)) } if options.MaxResults != 0 { - u += fmt.Sprintf("&maxResults=%d", options.MaxResults) + uv.Add("maxResults", strconv.Itoa(options.MaxResults)) } if options.Expand != "" { - u += fmt.Sprintf("&expand=%s", options.Expand) + uv.Add("expand", options.Expand) } if strings.Join(options.Fields, ",") != "" { - u += fmt.Sprintf("&fields=%s", strings.Join(options.Fields, ",")) + uv.Add("fields", strings.Join(options.Fields, ",")) } if options.ValidateQuery != "" { - u += fmt.Sprintf("&validateQuery=%s", options.ValidateQuery) + uv.Add("validateQuery", options.ValidateQuery) } } - req, err := s.client.NewRequest("GET", u, nil) + u.RawQuery = uv.Encode() + + req, err := s.client.NewRequest("GET", u.String(), nil) if err != nil { return []Issue{}, nil, err } diff --git a/issue_test.go b/issue_test.go index 83f720fe..cc8e9d68 100644 --- a/issue_test.go +++ b/issue_test.go @@ -597,7 +597,7 @@ func TestIssueService_Search(t *testing.T) { defer teardown() testMux.HandleFunc("/rest/api/2/search", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - testRequestURL(t, r, "/rest/api/2/search?jql=something&startAt=1&maxResults=40&expand=foo") + testRequestURL(t, r, "/rest/api/2/search?expand=foo&jql=something&maxResults=40&startAt=1") w.WriteHeader(http.StatusOK) fmt.Fprint(w, `{"expand": "schema,names","startAt": 1,"maxResults": 40,"total": 6,"issues": [{"expand": "html","id": "10230","self": "http://kelpie9:8081/rest/api/2/issue/BULK-62","key": "BULK-62","fields": {"summary": "testing","timetracking": null,"issuetype": {"self": "http://kelpie9:8081/rest/api/2/issuetype/5","id": "5","description": "The sub-task of the issue","iconUrl": "http://kelpie9:8081/images/icons/issue_subtask.gif","name": "Sub-task","subtask": true},"customfield_10071": null}},{"expand": "html","id": "10004","self": "http://kelpie9:8081/rest/api/2/issue/BULK-47","key": "BULK-47","fields": {"summary": "Cheese v1 2.0 issue","timetracking": null,"issuetype": {"self": "http://kelpie9:8081/rest/api/2/issuetype/3","id": "3","description": "A task that needs to be done.","iconUrl": "http://kelpie9:8081/images/icons/task.gif","name": "Task","subtask": false}}}]}`) }) @@ -623,6 +623,37 @@ func TestIssueService_Search(t *testing.T) { } } +func TestIssueService_SearchEmptyJQL(t *testing.T) { + setup() + defer teardown() + testMux.HandleFunc("/rest/api/2/search", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + testRequestURL(t, r, "/rest/api/2/search?expand=foo&maxResults=40&startAt=1") + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, `{"expand": "schema,names","startAt": 1,"maxResults": 40,"total": 6,"issues": [{"expand": "html","id": "10230","self": "http://kelpie9:8081/rest/api/2/issue/BULK-62","key": "BULK-62","fields": {"summary": "testing","timetracking": null,"issuetype": {"self": "http://kelpie9:8081/rest/api/2/issuetype/5","id": "5","description": "The sub-task of the issue","iconUrl": "http://kelpie9:8081/images/icons/issue_subtask.gif","name": "Sub-task","subtask": true},"customfield_10071": null}},{"expand": "html","id": "10004","self": "http://kelpie9:8081/rest/api/2/issue/BULK-47","key": "BULK-47","fields": {"summary": "Cheese v1 2.0 issue","timetracking": null,"issuetype": {"self": "http://kelpie9:8081/rest/api/2/issuetype/3","id": "3","description": "A task that needs to be done.","iconUrl": "http://kelpie9:8081/images/icons/task.gif","name": "Task","subtask": false}}}]}`) + }) + + opt := &SearchOptions{StartAt: 1, MaxResults: 40, Expand: "foo"} + _, resp, err := testClient.Issue.Search("", opt) + + if resp == nil { + t.Errorf("Response given: %+v", resp) + } + if err != nil { + t.Errorf("Error given: %s", err) + } + + if resp.StartAt != 1 { + t.Errorf("StartAt should populate with 1, %v given", resp.StartAt) + } + if resp.MaxResults != 40 { + t.Errorf("StartAt should populate with 40, %v given", resp.MaxResults) + } + if resp.Total != 6 { + t.Errorf("StartAt should populate with 6, %v given", resp.Total) + } +} + func TestIssueService_Search_WithoutPaging(t *testing.T) { setup() defer teardown() @@ -632,7 +663,6 @@ func TestIssueService_Search_WithoutPaging(t *testing.T) { w.WriteHeader(http.StatusOK) fmt.Fprint(w, `{"expand": "schema,names","startAt": 0,"maxResults": 50,"total": 6,"issues": [{"expand": "html","id": "10230","self": "http://kelpie9:8081/rest/api/2/issue/BULK-62","key": "BULK-62","fields": {"summary": "testing","timetracking": null,"issuetype": {"self": "http://kelpie9:8081/rest/api/2/issuetype/5","id": "5","description": "The sub-task of the issue","iconUrl": "http://kelpie9:8081/images/icons/issue_subtask.gif","name": "Sub-task","subtask": true},"customfield_10071": null}},{"expand": "html","id": "10004","self": "http://kelpie9:8081/rest/api/2/issue/BULK-47","key": "BULK-47","fields": {"summary": "Cheese v1 2.0 issue","timetracking": null,"issuetype": {"self": "http://kelpie9:8081/rest/api/2/issuetype/3","id": "3","description": "A task that needs to be done.","iconUrl": "http://kelpie9:8081/images/icons/task.gif","name": "Task","subtask": false}}}]}`) }) - _, resp, err := testClient.Issue.Search("something", nil) if resp == nil { @@ -658,15 +688,15 @@ func TestIssueService_SearchPages(t *testing.T) { defer teardown() testMux.HandleFunc("/rest/api/2/search", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - if r.URL.String() == "/rest/api/2/search?jql=something&startAt=1&maxResults=2&expand=foo&validateQuery=warn" { + if r.URL.String() == "/rest/api/2/search?expand=foo&jql=something&maxResults=2&startAt=1&validateQuery=warn" { w.WriteHeader(http.StatusOK) fmt.Fprint(w, `{"expand": "schema,names","startAt": 1,"maxResults": 2,"total": 6,"issues": [{"expand": "html","id": "10230","self": "http://kelpie9:8081/rest/api/2/issue/BULK-62","key": "BULK-62","fields": {"summary": "testing","timetracking": null,"issuetype": {"self": "http://kelpie9:8081/rest/api/2/issuetype/5","id": "5","description": "The sub-task of the issue","iconUrl": "http://kelpie9:8081/images/icons/issue_subtask.gif","name": "Sub-task","subtask": true},"customfield_10071": null}},{"expand": "html","id": "10004","self": "http://kelpie9:8081/rest/api/2/issue/BULK-47","key": "BULK-47","fields": {"summary": "Cheese v1 2.0 issue","timetracking": null,"issuetype": {"self": "http://kelpie9:8081/rest/api/2/issuetype/3","id": "3","description": "A task that needs to be done.","iconUrl": "http://kelpie9:8081/images/icons/task.gif","name": "Task","subtask": false}}}]}`) return - } else if r.URL.String() == "/rest/api/2/search?jql=something&startAt=3&maxResults=2&expand=foo&validateQuery=warn" { + } else if r.URL.String() == "/rest/api/2/search?expand=foo&jql=something&maxResults=2&startAt=3&validateQuery=warn" { w.WriteHeader(http.StatusOK) fmt.Fprint(w, `{"expand": "schema,names","startAt": 3,"maxResults": 2,"total": 6,"issues": [{"expand": "html","id": "10230","self": "http://kelpie9:8081/rest/api/2/issue/BULK-62","key": "BULK-62","fields": {"summary": "testing","timetracking": null,"issuetype": {"self": "http://kelpie9:8081/rest/api/2/issuetype/5","id": "5","description": "The sub-task of the issue","iconUrl": "http://kelpie9:8081/images/icons/issue_subtask.gif","name": "Sub-task","subtask": true},"customfield_10071": null}},{"expand": "html","id": "10004","self": "http://kelpie9:8081/rest/api/2/issue/BULK-47","key": "BULK-47","fields": {"summary": "Cheese v1 2.0 issue","timetracking": null,"issuetype": {"self": "http://kelpie9:8081/rest/api/2/issuetype/3","id": "3","description": "A task that needs to be done.","iconUrl": "http://kelpie9:8081/images/icons/task.gif","name": "Task","subtask": false}}}]}`) return - } else if r.URL.String() == "/rest/api/2/search?jql=something&startAt=5&maxResults=2&expand=foo&validateQuery=warn" { + } else if r.URL.String() == "/rest/api/2/search?expand=foo&jql=something&maxResults=2&startAt=5&validateQuery=warn" { w.WriteHeader(http.StatusOK) fmt.Fprint(w, `{"expand": "schema,names","startAt": 5,"maxResults": 2,"total": 6,"issues": [{"expand": "html","id": "10230","self": "http://kelpie9:8081/rest/api/2/issue/BULK-62","key": "BULK-62","fields": {"summary": "testing","timetracking": null,"issuetype": {"self": "http://kelpie9:8081/rest/api/2/issuetype/5","id": "5","description": "The sub-task of the issue","iconUrl": "http://kelpie9:8081/images/icons/issue_subtask.gif","name": "Sub-task","subtask": true},"customfield_10071": null}}]}`) return @@ -696,7 +726,7 @@ func TestIssueService_SearchPages_EmptyResult(t *testing.T) { defer teardown() testMux.HandleFunc("/rest/api/2/search", func(w http.ResponseWriter, r *http.Request) { testMethod(t, r, "GET") - if r.URL.String() == "/rest/api/2/search?jql=something&startAt=1&maxResults=50&expand=foo&validateQuery=warn" { + if r.URL.String() == "/rest/api/2/search?expand=foo&jql=something&maxResults=50&startAt=1&validateQuery=warn" { w.WriteHeader(http.StatusOK) // This is what Jira outputs when the &maxResult= issue occurs. It used to cause SearchPages to go into an endless loop. fmt.Fprint(w, `{"expand": "schema,names","startAt": 0,"maxResults": 0,"total": 6,"issues": []}`)