Skip to content

Commit 70d9b95

Browse files
authored
Merge pull request #248 from aik099/searching-fix
Use "/rest/api/2/search/jql" instead of "/rest/api/2/search" API call internally
2 parents 7328538 + 8d5520f commit 70d9b95

7 files changed

Lines changed: 143 additions & 271 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@ This project adheres to [Semantic Versioning](https://semver.org/).
1010
- The `Api::downloadAttachment` method now throws an exception, when attempting to download from a non-Jira website by [@aik099] (#240).
1111
- The `$params` argument of the `Api::getWorklogs` method is now optional by [@aik099] (#244).
1212
- The `$params` argument of the `Api::getTransitions` method is now optional by [@aik099] (#244).
13+
- The `$start_at = 0` argument of `Api::search` method was renamed into the `$next_page_token = null` to reflect underlying Jira API changes by [@aik099] (#248).
14+
- Improved performance of `Result::getIssuesCount` method by [@aik099] (#248).
1315

1416
### Removed
15-
...
17+
- The `Api::search` method and the `Walker` class no longer return total issue count, because underlaying Jira API doesn't support this [@aik099] (#248).
1618

1719
### Fixed
1820
- The `CurlClient` in combination with cURL version < 7.33.0 was getting `426 Upgrade Required` error on any request to the Atlassian Cloud servers by [@aik099] (#239).

src/Jira/Api.php

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -617,28 +617,39 @@ public function createIssue($project_key, $summary, $issue_type, array $other_fi
617617
/**
618618
* Query issues.
619619
*
620-
* @param string $jql JQL.
621-
* @param integer $start_at Start at.
622-
* @param integer $max_results Max results.
623-
* @param string $fields Fields.
620+
* @param string $jql JQL.
621+
* @param string|null $next_page_token Next page token.
622+
* @param integer $max_results Max results.
623+
* @param string $fields Fields.
624624
*
625625
* @return Result
626+
* @throws Exception When the "$next_page_token" parameter has an invalid value.
626627
* @link https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issue-search/#api-rest-api-2-search-get
627628
*/
628-
public function search($jql, $start_at = 0, $max_results = 20, $fields = '*navigable')
629+
public function search($jql, $next_page_token = null, $max_results = 20, $fields = '*navigable')
629630
{
630-
$result = $this->api(
631-
self::REQUEST_GET,
632-
'/rest/api/2/search',
633-
array(
634-
'jql' => $jql,
635-
'startAt' => $start_at,
636-
'maxResults' => $max_results,
637-
'fields' => $fields,
638-
)
631+
// Polyfill for users who don't paginate themselves.
632+
if ( $next_page_token === 0 ) {
633+
$next_page_token = null;
634+
}
635+
636+
if ( !is_string($next_page_token) && $next_page_token !== null ) {
637+
$error_msg = 'The "$next_page_token" value must be either null (for the first page) ';
638+
$error_msg .= 'or come from the "nextPageToken" key of the previous search response.';
639+
throw new Exception($error_msg);
640+
}
641+
642+
$params = array(
643+
'jql' => $jql,
644+
'maxResults' => $max_results,
645+
'fields' => $fields,
639646
);
640647

641-
return $result;
648+
if ( $next_page_token !== null ) {
649+
$params['nextPageToken'] = $next_page_token;
650+
}
651+
652+
return $this->api(self::REQUEST_GET, '/rest/api/2/search/jql', $params);
642653
}
643654

644655
/**

src/Jira/Api/Result.php

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public function getTotal()
108108
*/
109109
public function getIssuesCount()
110110
{
111-
return count($this->getIssues());
111+
return isset($this->result['issues']) ? count($this->result['issues']) : 0;
112112
}
113113

114114
/**
@@ -118,17 +118,17 @@ public function getIssuesCount()
118118
*/
119119
public function getIssues()
120120
{
121-
if ( isset($this->result['issues']) ) {
122-
$result = array();
121+
if ( !isset($this->result['issues']) ) {
122+
return array();
123+
}
123124

124-
foreach ( $this->result['issues'] as $issue ) {
125-
$result[] = new Issue($issue);
126-
}
125+
$result = array();
127126

128-
return $result;
127+
foreach ( $this->result['issues'] as $issue ) {
128+
$result[] = new Issue($issue);
129129
}
130130

131-
return array();
131+
return $result;
132132
}
133133

134134
/**

src/Jira/Issues/Walker.php

Lines changed: 28 additions & 183 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828
use chobie\Jira\Api;
2929

30-
class Walker implements \Iterator, \Countable
30+
class Walker implements \IteratorAggregate
3131
{
3232

3333
/**
@@ -44,62 +44,13 @@ class Walker implements \Iterator, \Countable
4444
*/
4545
protected $jql = null;
4646

47-
/**
48-
* Offset.
49-
*
50-
* @var integer
51-
*/
52-
protected $offset = 0;
53-
54-
/**
55-
* Current record index.
56-
*
57-
* @var integer
58-
*/
59-
protected $current = 0;
60-
61-
/**
62-
* Total issue count.
63-
*
64-
* @var integer
65-
*/
66-
protected $total = null;
67-
68-
/**
69-
* Issue count on current page.
70-
*
71-
* @var integer
72-
*/
73-
protected $max = 0;
74-
75-
/**
76-
* Index of issue in issue list (across all issue pages).
77-
*
78-
* @var integer
79-
*/
80-
protected $startAt = 0;
81-
8247
/**
8348
* Issues per page.
8449
*
8550
* @var integer
8651
*/
8752
protected $perPage = 50;
8853

89-
/**
90-
* Was JQL executed.
91-
*
92-
* @var boolean
93-
*/
94-
protected $executed = false;
95-
96-
/**
97-
* Result.
98-
*
99-
* @var array
100-
*/
101-
protected $issues = array();
102-
10354
/**
10455
* List of fields to query.
10556
*
@@ -144,143 +95,53 @@ public function push($jql, $fields = null)
14495
}
14596

14697
/**
147-
* Return the current element.
148-
*
149-
* @return mixed Can return any type.
150-
* @link http://php.net/manual/en/iterator.current.php
151-
*/
152-
public function current()
153-
{
154-
if ( is_callable($this->callback) ) {
155-
$tmp = $this->issues[$this->offset];
156-
$callback = $this->callback;
157-
158-
return $callback($tmp);
159-
}
160-
161-
return $this->issues[$this->offset];
162-
}
163-
164-
/**
165-
* Move forward to next element.
98+
* @inheritDoc
16699
*
167-
* @return void Any returned value is ignored.
168-
* @link http://php.net/manual/en/iterator.next.php
169-
*/
170-
public function next()
171-
{
172-
$this->offset++;
173-
}
174-
175-
/**
176-
* Return the key of the current element.
177-
*
178-
* @return mixed scalar on success, or null on failure.
179-
* @link http://php.net/manual/en/iterator.key.php
180-
*/
181-
public function key()
182-
{
183-
if ( $this->startAt > 0 ) {
184-
return $this->offset + (($this->startAt - 1) * $this->perPage);
185-
}
186-
187-
return 0;
188-
}
189-
190-
/**
191-
* Checks if current position is valid.
192-
*
193-
* @return boolean The return value will be casted to boolean and then evaluated.
194-
* Returns true on success or false on failure.
195100
* @throws \Exception When "Walker::push" method wasn't called.
196101
* @throws Api\UnauthorizedException When it happens.
197-
* @link http://php.net/manual/en/iterator.valid.php
198102
*/
199-
public function valid()
103+
public function getIterator()
200104
{
201105
if ( $this->jql === null ) {
202106
throw new \Exception('you have to call Jira_Walker::push($jql, $fields) at first');
203107
}
204108

205-
if ( !$this->executed ) {
206-
try {
207-
$result = $this->api->search($this->getQuery(), $this->key(), $this->perPage, $this->fields);
109+
$next_page_token = null;
110+
$jql = $this->getQuery();
208111

209-
$this->setResult($result);
210-
$this->executed = true;
112+
try {
113+
while ( true ) {
114+
$result = $this->api->search($jql, $next_page_token, $this->perPage, $this->fields);
211115

212-
if ( $result->getTotal() == 0 ) {
213-
return false;
116+
if ( $result->getIssuesCount() === 0 ) {
117+
return;
214118
}
215119

216-
return true;
217-
}
218-
catch ( Api\UnauthorizedException $e ) {
219-
throw $e;
220-
}
221-
catch ( \Exception $e ) {
222-
error_log($e->getMessage());
223-
224-
return false;
225-
}
226-
}
227-
else {
228-
if ( $this->offset >= $this->max && $this->key() < $this->total ) {
229-
try {
230-
$result = $this->api->search($this->getQuery(), $this->key(), $this->perPage, $this->fields);
231-
$this->setResult($result);
120+
foreach ( $result->getIssues() as $issue ) {
121+
if ( is_callable($this->callback) ) {
122+
$callback = $this->callback;
232123

233-
return true;
124+
yield $callback($issue);
125+
}
126+
else {
127+
yield $issue;
128+
}
234129
}
235-
catch ( Api\UnauthorizedException $e ) {
236-
throw $e;
237-
}
238-
catch ( \Exception $e ) {
239-
error_log($e->getMessage());
240130

241-
return false;
242-
}
243-
}
244-
else {
245-
if ( ($this->startAt - 1) * $this->perPage + $this->offset < $this->total ) {
246-
return true;
247-
}
131+
$raw_result = $result->getResult();
132+
$next_page_token = array_key_exists('nextPageToken', $raw_result) ? $raw_result['nextPageToken'] : null;
248133

249-
return false;
134+
if ( empty($next_page_token) ) {
135+
break;
136+
}
250137
}
251138
}
252-
}
253-
254-
/**
255-
* Rewind the Iterator to the first element.
256-
*
257-
* @return void Any returned value is ignored.
258-
* @link http://php.net/manual/en/iterator.rewind.php
259-
*/
260-
public function rewind()
261-
{
262-
$this->offset = 0;
263-
$this->startAt = 0;
264-
$this->current = 0;
265-
$this->max = 0;
266-
$this->total = null;
267-
$this->executed = false;
268-
$this->issues = array();
269-
}
270-
271-
/**
272-
* Count elements of an object.
273-
*
274-
* @return integer The custom count as an integer.
275-
* @link http://php.net/manual/en/countable.count.php
276-
*/
277-
public function count()
278-
{
279-
if ( $this->total === null ) {
280-
$this->valid();
139+
catch ( Api\UnauthorizedException $e ) {
140+
throw $e;
141+
}
142+
catch ( \Exception $e ) {
143+
error_log($e->getMessage());
281144
}
282-
283-
return $this->total;
284145
}
285146

286147
/**
@@ -301,22 +162,6 @@ public function setDelegate($callable)
301162
}
302163
}
303164

304-
/**
305-
* Sets result.
306-
*
307-
* @param Api\Result $result Result.
308-
*
309-
* @return void
310-
*/
311-
protected function setResult(Api\Result $result)
312-
{
313-
$this->total = $result->getTotal();
314-
$this->offset = 0;
315-
$this->max = $result->getIssuesCount();
316-
$this->issues = $result->getIssues();
317-
$this->startAt++;
318-
}
319-
320165
/**
321166
* Returns JQL.
322167
*

0 commit comments

Comments
 (0)