Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 148 additions & 0 deletions issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,63 @@ type SearchOptions struct {
ValidateQuery string `url:"validateQuery,omitempty"`
}

// SearchOptionsV2 specifies the parameters for the Jira Cloud-specific
// paramaters to List methods that support pagination
//
// Docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issue-search/#api-rest-api-2-search-jql-get
type SearchOptionsV2 struct {
// NextPageToken: The token for a page to fetch that is not the first page.
// The first page has a nextPageToken of null.
// Use the nextPageToken to fetch the next page of issues.
// Note: The nextPageToken field is not included in the response for the last page,
// indicating there is no next page.
NextPageToken string `url:"nextPageToken,omitempty"`

// MaxResults: The maximum number of items to return per page.
// To manage page size, API may return fewer items per page where a large number of fields or properties are requested.
// The greatest number of items returned per page is achieved when requesting id or key only.
// It returns max 5000 issues.
// Default: 50
MaxResults int `url:"maxResults,omitempty"`

// Fields: A list of fields to return for each issue

// Fields: A list of fields to return for each issue, use it to retrieve a subset of fields.
// This parameter accepts a comma-separated list. Expand options include:
//
// `*all` Returns all fields.
// `*navigable` Returns navigable fields.
// `id` Returns only issue IDs.
// Any issue field, prefixed with a minus to exclude.
//
// The default is id.
//
// Examples:
//
// `summary,comment` Returns only the summary and comments fields only.
// `-description` Returns all navigable (default) fields except description.
// `*all,-comment` Returns all fields except comments.
//
// Multiple `fields` parameters can be included in a request.
//
// Note: By default, this resource returns IDs only. This differs from GET issue where the default is all fields.
Fields []string

// Expand: Use expand to include additional information about issues in the response.
// TODO add proper docs, see https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issue-search/#api-rest-api-2-search-jql-get
Expand string `url:"expand,omitempty"`
// A list of up to 5 issue properties to include in the results
Properties []string `url:"properties,omitempty"`
// FieldsByKeys: Reference fields by their key (rather than ID).
// The default is false.
FieldsByKeys bool `url:"fieldsByKeys,omitempty"`
// FailFast: Fail this request early if we can't retrieve all field data.
// Default false.
FailFast bool `url:"failFast,omitempty"`
// ReconcileIssues: Strong consistency issue ids to be reconciled with search results. Accepts max 50 ids
ReconcileIssues []int `url:"reconcileIssues,omitempty"`
}

// searchResult is only a small wrapper around the Search (with JQL) method
// to be able to parse the results
type searchResult struct {
Expand All @@ -536,6 +593,24 @@ type searchResult struct {
Total int `json:"total" structs:"total"`
}

// searchResultV2 is only a small wrapper around the Jira Cloud-specific SearchV2 (with JQL) method
// to be able to parse the results
type searchResultV2 struct {
// IsLast: Indicates whether this is the last page of the paginated response.
IsLast bool `json:"isLast" structs:"isLast"`
// Issues: The list of issues found by the search or reconsiliation.
Issues []Issue `json:"issues" structs:"issues"`

// TODO Missing
// Field names object
// Field schema object

// NextPageToken: Continuation token to fetch the next page.
// If this result represents the last or the only page this token will be null.
// This token will expire in 7 days.
NextPageToken string `json:"nextPageToken" structs:"nextPageToken"`
}

// GetQueryOptions specifies the optional parameters for the Get Issue methods
type GetQueryOptions struct {
// Fields is the list of fields to return for the issue. By default, all fields are returned.
Expand Down Expand Up @@ -1134,6 +1209,79 @@ func (s *IssueService) Search(jql string, options *SearchOptions) ([]Issue, *Res
return s.SearchWithContext(context.Background(), jql, options)
}

// SearchV2JQL will search for tickets according to the jql for Jira Cloud
//
// Jira API docs: https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issue-search/#api-rest-api-2-search-jql-get
func (s *IssueService) SearchV2JQL(jql string, options *SearchOptionsV2) ([]Issue, *Response, error) {
return s.SearchV2JQLWithContext(context.Background(), jql, options)
}

func (s *IssueService) SearchV2JQLWithContext(ctx context.Context, jql string, options *SearchOptionsV2) ([]Issue, *Response, error) {
u := url.URL{
Path: "rest/api/2/search/jql",
}
uv := url.Values{}
if jql != "" {
uv.Add("jql", jql)
}

// TODO Check this out if this works with addOptions as well
if options != nil {
if options.NextPageToken != "" {
uv.Add("nextPageToken", options.NextPageToken)
}
if options.MaxResults != 0 {
uv.Add("maxResults", strconv.Itoa(options.MaxResults))
}
if strings.Join(options.Fields, ",") != "" {
uv.Add("fields", strings.Join(options.Fields, ","))
}
if options.Expand != "" {
uv.Add("expand", options.Expand)
}
if len(options.Properties) > 5 {
return nil, nil, fmt.Errorf("Search option Properties accepts maximum five entries")
}
if strings.Join(options.Properties, ",") != "" {
uv.Add("properties", strings.Join(options.Properties, ","))
}
if options.FieldsByKeys {
uv.Add("fieldsByKeys", "true")
}
if options.FailFast {
uv.Add("failFast", "true")
}
if len(options.ReconcileIssues) > 50 {
return nil, nil, fmt.Errorf("Search option ReconcileIssue accepts maximum 50 entries")
}
if len(options.ReconcileIssues) > 0 {
// TODO Extract this
// Convert []int to []string for strings.Join
reconcileIssuesStr := make([]string, len(options.ReconcileIssues))
for i, v := range options.ReconcileIssues {
reconcileIssuesStr[i] = strconv.Itoa(v)
}
uv.Add("reconcileIssues", strings.Join(reconcileIssuesStr, ","))
}
}

u.RawQuery = uv.Encode()

req, err := s.client.NewRequestWithContext(ctx, http.MethodGet, u.String(), nil)
if err != nil {
return []Issue{}, nil, err
}

v := new(searchResultV2)
resp, err := s.client.Do(req, v)
if err != nil {
err = NewJiraError(resp, err)
}

return v.Issues, resp, err
}


// SearchPagesWithContext will get issues from all pages in a search
//
// Jira API docs: https://developer.atlassian.com/jiradev/jira-apis/jira-rest-apis/jira-rest-api-tutorials/jira-rest-api-example-query-issues
Expand Down
12 changes: 10 additions & 2 deletions jira.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,10 @@ type Response struct {
StartAt int
MaxResults int
Total int

// *searchResultV2
IsLast bool
NextPageToken string
}

func newResponse(r *http.Response, v interface{}) *Response {
Expand All @@ -343,6 +347,9 @@ func (r *Response) populatePageValues(v interface{}) {
r.StartAt = value.StartAt
r.MaxResults = value.MaxResults
r.Total = value.Total
case *searchResultV2:
r.IsLast = value.IsLast
r.NextPageToken = value.NextPageToken
case *groupMembersResult:
r.StartAt = value.StartAt
r.MaxResults = value.MaxResults
Expand Down Expand Up @@ -561,8 +568,9 @@ func (t *CookieAuthTransport) transport() http.RoundTripper {
//
// Jira docs: https://developer.atlassian.com/cloud/jira/platform/understanding-jwt
// Examples in other languages:
// https://bitbucket.org/atlassian/atlassian-jwt-ruby/src/d44a8e7a4649e4f23edaa784402655fda7c816ea/lib/atlassian/jwt.rb
// https://bitbucket.org/atlassian/atlassian-jwt-py/src/master/atlassian_jwt/url_utils.py
//
// https://bitbucket.org/atlassian/atlassian-jwt-ruby/src/d44a8e7a4649e4f23edaa784402655fda7c816ea/lib/atlassian/jwt.rb
// https://bitbucket.org/atlassian/atlassian-jwt-py/src/master/atlassian_jwt/url_utils.py
type JWTAuthTransport struct {
Secret []byte
Issuer string
Expand Down
Loading