Skip to content

Commit 9d1a1dc

Browse files
committed
Add SearchV3 to fix deprecation of JQL search and evaluate expression endpoints
1 parent 47d27a7 commit 9d1a1dc

File tree

2 files changed

+152
-2
lines changed

2 files changed

+152
-2
lines changed

issue.go

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,59 @@ type SearchOptions struct {
527527
ValidateQuery string `url:"validateQuery,omitempty"`
528528
}
529529

530+
type SearchOptionsV3 struct {
531+
// NextPageToken: The token for a page to fetch that is not the first page.
532+
// The first page has a nextPageToken of null.
533+
// Use the nextPageToken to fetch the next page of issues.
534+
// Note: The nextPageToken field is not included in the response for the last page,
535+
// indicating there is no next page.
536+
NextPageToken string `url:"nextPageToken,omitempty"`
537+
538+
// MaxResults: The maximum number of items to return per page.
539+
// To manage page size, API may return fewer items per page where a large number of fields or properties are requested.
540+
// The greatest number of items returned per page is achieved when requesting id or key only.
541+
// It returns max 5000 issues.
542+
// Default: 50
543+
MaxResults int `url:"maxResults,omitempty"`
544+
545+
// Fields: A list of fields to return for each issue
546+
547+
// Fields: A list of fields to return for each issue, use it to retrieve a subset of fields.
548+
// This parameter accepts a comma-separated list. Expand options include:
549+
//
550+
// `*all` Returns all fields.
551+
// `*navigable` Returns navigable fields.
552+
// `id` Returns only issue IDs.
553+
// Any issue field, prefixed with a minus to exclude.
554+
//
555+
// The default is id.
556+
//
557+
// Examples:
558+
//
559+
// `summary,comment` Returns only the summary and comments fields only.
560+
// `-description` Returns all navigable (default) fields except description.
561+
// `*all,-comment` Returns all fields except comments.
562+
//
563+
// Multiple `fields` parameters can be included in a request.
564+
//
565+
// Note: By default, this resource returns IDs only. This differs from GET issue where the default is all fields.
566+
Fields []string
567+
568+
// Expand: Use expand to include additional information about issues in the response.
569+
// TODO add proper docs, see https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-search/#api-rest-api-3-search-jql-get
570+
Expand string `url:"expand,omitempty"`
571+
// A list of up to 5 issue properties to include in the results
572+
Properties []string `url:"properties,omitempty"`
573+
// FieldsByKeys: Reference fields by their key (rather than ID).
574+
// The default is false.
575+
FieldsByKeys bool `url:"fieldsByKeys,omitempty"`
576+
// FailFast: Fail this request early if we can't retrieve all field data.
577+
// Default false.
578+
FailFast bool `url:"failFast,omitempty"`
579+
// ReconcileIssues: Strong consistency issue ids to be reconciled with search results. Accepts max 50 ids
580+
ReconcileIssues []int `url:"reconcileIssues,omitempty"`
581+
}
582+
530583
// searchResult is only a small wrapper around the Search (with JQL) method
531584
// to be able to parse the results
532585
type searchResult struct {
@@ -536,6 +589,22 @@ type searchResult struct {
536589
Total int `json:"total" structs:"total"`
537590
}
538591

592+
type searchResultV3 struct {
593+
// IsLast: Indicates whether this is the last page of the paginated response.
594+
IsLast bool `json:"isLast" structs:"isLast"`
595+
// Issues: The list of issues found by the search or reconsiliation.
596+
Issues []Issue `json:"issues" structs:"issues"`
597+
598+
// TODO Missing
599+
// Field names object
600+
// Field schema object
601+
602+
// NextPageToken: Continuation token to fetch the next page.
603+
// If this result represents the last or the only page this token will be null.
604+
// This token will expire in 7 days.
605+
NextPageToken string `json:"nextPageToken" structs:"nextPageToken"`
606+
}
607+
539608
// GetQueryOptions specifies the optional parameters for the Get Issue methods
540609
type GetQueryOptions struct {
541610
// Fields is the list of fields to return for the issue. By default, all fields are returned.
@@ -1134,6 +1203,79 @@ func (s *IssueService) Search(jql string, options *SearchOptions) ([]Issue, *Res
11341203
return s.SearchWithContext(context.Background(), jql, options)
11351204
}
11361205

1206+
// SearchV3JQL will search for tickets according to the jql for Jira Cloud
1207+
//
1208+
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-issue-search/#api-rest-api-3-search-jql-get
1209+
func (s *IssueService) SearchV3JQL(jql string, options *SearchOptions) ([]Issue, *Response, error) {
1210+
return s.SearchWithContext(context.Background(), jql, options)
1211+
}
1212+
1213+
func (s *IssueService) SearchV3JQLWithContext(ctx context.Context, jql string, options *SearchOptionsV3) ([]Issue, *Response, error) {
1214+
u := url.URL{
1215+
Path: "rest/api/3/search/jql",
1216+
}
1217+
uv := url.Values{}
1218+
if jql != "" {
1219+
uv.Add("jql", jql)
1220+
}
1221+
1222+
// TODO Check this out if this works with addOptions as well
1223+
if options != nil {
1224+
if options.NextPageToken != "" {
1225+
uv.Add("nextPageToken", options.NextPageToken)
1226+
}
1227+
if options.MaxResults != 0 {
1228+
uv.Add("maxResults", strconv.Itoa(options.MaxResults))
1229+
}
1230+
if strings.Join(options.Fields, ",") != "" {
1231+
uv.Add("fields", strings.Join(options.Fields, ","))
1232+
}
1233+
if options.Expand != "" {
1234+
uv.Add("expand", options.Expand)
1235+
}
1236+
if len(options.Properties) > 5 {
1237+
return nil, nil, fmt.Errorf("Search option Properties accepts maximum five entries")
1238+
}
1239+
if strings.Join(options.Properties, ",") != "" {
1240+
uv.Add("properties", strings.Join(options.Properties, ","))
1241+
}
1242+
if options.FieldsByKeys {
1243+
uv.Add("fieldsByKeys", "true")
1244+
}
1245+
if options.FailFast {
1246+
uv.Add("failFast", "true")
1247+
}
1248+
if len(options.ReconcileIssues) > 50 {
1249+
return nil, nil, fmt.Errorf("Search option ReconcileIssue accepts maximum 50 entries")
1250+
}
1251+
if len(options.ReconcileIssues) > 0 {
1252+
// TODO Extract this
1253+
// Convert []int to []string for strings.Join
1254+
reconcileIssuesStr := make([]string, len(options.ReconcileIssues))
1255+
for i, v := range options.ReconcileIssues {
1256+
reconcileIssuesStr[i] = strconv.Itoa(v)
1257+
}
1258+
uv.Add("reconcileIssues", strings.Join(reconcileIssuesStr, ","))
1259+
}
1260+
}
1261+
1262+
u.RawQuery = uv.Encode()
1263+
1264+
req, err := s.client.NewRequest(ctx, http.MethodGet, u.String(), nil)
1265+
if err != nil {
1266+
return []Issue{}, nil, err
1267+
}
1268+
1269+
v := new(searchResultV3)
1270+
resp, err := s.client.Do(req, v)
1271+
if err != nil {
1272+
err = NewJiraError(resp, err)
1273+
}
1274+
1275+
return v.Issues, resp, err
1276+
}
1277+
1278+
11371279
// SearchPagesWithContext will get issues from all pages in a search
11381280
//
11391281
// Jira API docs: https://developer.atlassian.com/jiradev/jira-apis/jira-rest-apis/jira-rest-api-tutorials/jira-rest-api-example-query-issues

jira.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,10 @@ type Response struct {
327327
StartAt int
328328
MaxResults int
329329
Total int
330+
331+
// *searchResultV3
332+
IsLast bool
333+
NextPageToken string
330334
}
331335

332336
func newResponse(r *http.Response, v interface{}) *Response {
@@ -343,6 +347,9 @@ func (r *Response) populatePageValues(v interface{}) {
343347
r.StartAt = value.StartAt
344348
r.MaxResults = value.MaxResults
345349
r.Total = value.Total
350+
case *searchResultV3:
351+
r.IsLast = value.IsLast
352+
r.NextPageToken = value.NextPageToken
346353
case *groupMembersResult:
347354
r.StartAt = value.StartAt
348355
r.MaxResults = value.MaxResults
@@ -561,8 +568,9 @@ func (t *CookieAuthTransport) transport() http.RoundTripper {
561568
//
562569
// Jira docs: https://developer.atlassian.com/cloud/jira/platform/understanding-jwt
563570
// Examples in other languages:
564-
// https://bitbucket.org/atlassian/atlassian-jwt-ruby/src/d44a8e7a4649e4f23edaa784402655fda7c816ea/lib/atlassian/jwt.rb
565-
// https://bitbucket.org/atlassian/atlassian-jwt-py/src/master/atlassian_jwt/url_utils.py
571+
//
572+
// https://bitbucket.org/atlassian/atlassian-jwt-ruby/src/d44a8e7a4649e4f23edaa784402655fda7c816ea/lib/atlassian/jwt.rb
573+
// https://bitbucket.org/atlassian/atlassian-jwt-py/src/master/atlassian_jwt/url_utils.py
566574
type JWTAuthTransport struct {
567575
Secret []byte
568576
Issuer string

0 commit comments

Comments
 (0)