Skip to content

Commit 829724f

Browse files
jpastoorAlexander Obuhovich
authored andcommitted
Added client-side cache to Api::getResolutions (#131)
Added client-side cache to "Api::getResolutions" method. Clear local caches on endpoint url change.
1 parent 42bab70 commit 829724f

File tree

7 files changed

+266
-25
lines changed

7 files changed

+266
-25
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
2121
- Minimal supported PHP version changed from 5.2 to 5.3 by [@chobie].
2222
- The `Api::getPriorties` renamed into `Api::getPriorities` by [@josevh].
2323
- Remove trailing slash from endpoint url by [@Procta].
24+
- Added local cache to getResolutions [@jpastoor].
2425

2526
### Removed
2627
...
@@ -34,6 +35,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
3435
- Fixed PHP deprecation notice, when creating issue attachments via `CurlClient` on PHP 5.5+ by [@DerMika].
3536
- The `Api::getRoles` call was always retuning an error by [@aik099].
3637
- Attempt to make a `DELETE` API call using `CurlClient` wasn't working by [@aik099].
38+
- Clearing local caches (statuses, priorities, fields and resolutions) on endpoint change [@jpastoor].
3739

3840
## [1.0.0] - 2014-07-27
3941
### Added

src/Jira/Api.php

Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -68,26 +68,33 @@ class Api
6868
protected $options = self::AUTOMAP_FIELDS;
6969

7070
/**
71-
* Fields.
71+
* Client-side cache of fields. List of fields when loaded, null when nothing is fetched yet.
7272
*
73-
* @var array
73+
* @var array|null
7474
*/
7575
protected $fields;
7676

7777
/**
78-
* Priorities.
78+
* Client-side cache of priorities. List of priorities when loaded, null when nothing is fetched yet.
7979
*
80-
* @var array
80+
* @var array|null
8181
*/
8282
protected $priorities;
8383

8484
/**
85-
* Statuses.
85+
* Client-side cache of statuses. List of statuses when loaded, null when nothing is fetched yet.
8686
*
87-
* @var array
87+
* @var array|null
8888
*/
8989
protected $statuses;
9090

91+
/**
92+
* Client-side cache of resolutions. List of resolutions when loaded, null when nothing is fetched yet.
93+
*
94+
* @var array|null
95+
*/
96+
protected $resolutions;
97+
9198
/**
9299
* Create a JIRA API client.
93100
*
@@ -141,12 +148,26 @@ public function getEndpoint()
141148
*/
142149
public function setEndpoint($url)
143150
{
144-
$this->fields = array();
145-
146151
// Remove trailing slash in the url.
147152
$url = rtrim($url, '/');
148153

149-
$this->endpoint = $url;
154+
if ( $url !== $this->endpoint ) {
155+
$this->endpoint = $url;
156+
$this->clearLocalCaches();
157+
}
158+
}
159+
160+
/**
161+
* Helper method to clear the local caches. Is called when switching endpoints
162+
*
163+
* @return void
164+
*/
165+
protected function clearLocalCaches()
166+
{
167+
$this->fields = null;
168+
$this->priorities = null;
169+
$this->statuses = null;
170+
$this->resolutions = null;
150171
}
151172

152173
/**
@@ -156,13 +177,14 @@ public function setEndpoint($url)
156177
*/
157178
public function getFields()
158179
{
159-
if ( !count($this->fields) ) {
180+
// Fetch fields when the method is called for the first time.
181+
if ( $this->fields === null ) {
160182
$fields = array();
161-
$_fields = $this->api(self::REQUEST_GET, '/rest/api/2/field', array());
183+
$result = $this->api(self::REQUEST_GET, '/rest/api/2/field', array(), true);
162184

163185
/* set hash key as custom field id */
164-
foreach ( $_fields->getResult() as $k => $v ) {
165-
$fields[$v['id']] = $v;
186+
foreach ( $result as $field ) {
187+
$fields[$field['id']] = $field;
166188
}
167189

168190
$this->fields = $fields;
@@ -456,13 +478,14 @@ public function findVersionByName($project_key, $name)
456478
*/
457479
public function getPriorities()
458480
{
459-
if ( !count($this->priorities) ) {
481+
// Fetch priorities when the method is called for the first time.
482+
if ( $this->priorities === null ) {
460483
$priorities = array();
461-
$result = $this->api(self::REQUEST_GET, '/rest/api/2/priority', array());
484+
$result = $this->api(self::REQUEST_GET, '/rest/api/2/priority', array(), true);
462485

463486
/* set hash key as custom field id */
464-
foreach ( $result->getResult() as $k => $v ) {
465-
$priorities[$v['id']] = $v;
487+
foreach ( $result as $priority ) {
488+
$priorities[$priority['id']] = $priority;
466489
}
467490

468491
$this->priorities = $priorities;
@@ -478,13 +501,14 @@ public function getPriorities()
478501
*/
479502
public function getStatuses()
480503
{
481-
if ( !count($this->statuses) ) {
504+
// Fetch statuses when the method is called for the first time.
505+
if ( $this->statuses === null ) {
482506
$statuses = array();
483-
$result = $this->api(self::REQUEST_GET, '/rest/api/2/status', array());
507+
$result = $this->api(self::REQUEST_GET, '/rest/api/2/status', array(), true);
484508

485509
/* set hash key as custom field id */
486-
foreach ( $result->getResult() as $k => $v ) {
487-
$statuses[$v['id']] = $v;
510+
foreach ( $result as $status ) {
511+
$statuses[$status['id']] = $status;
488512
}
489513

490514
$this->statuses = $statuses;
@@ -862,14 +886,24 @@ public function getProjectIssueTypes($project_key)
862886
/**
863887
* Returns a list of all resolutions.
864888
*
865-
* @param string $project_key Project key.
866-
*
867-
* @return array|false
889+
* @return array
868890
* @since 2.0.0
869891
*/
870892
public function getResolutions()
871893
{
872-
return $this->api(self::REQUEST_GET, '/rest/api/2/resolution', array(), true);
894+
// Fetch resolutions when the method is called for the first time.
895+
if ( $this->resolutions === null ) {
896+
$resolutions = array();
897+
$result = $this->api(self::REQUEST_GET, '/rest/api/2/resolution', array(), true);
898+
899+
foreach ( $result as $resolution ) {
900+
$resolutions[$resolution['id']] = $resolution;
901+
}
902+
903+
$this->resolutions = $resolutions;
904+
}
905+
906+
return $this->resolutions;
873907
}
874908

875909
}

tests/Jira/ApiTest.php

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,114 @@ public function testFindVersionByName()
149149
);
150150
}
151151

152+
public function testGetResolutions()
153+
{
154+
$response = file_get_contents(__DIR__ . '/resources/api_resolution.json');
155+
156+
$this->expectClientCall(
157+
Api::REQUEST_GET,
158+
'/rest/api/2/resolution',
159+
array(),
160+
$response
161+
);
162+
163+
$actual = $this->api->getResolutions();
164+
165+
$response_decoded = json_decode($response, true);
166+
167+
$expected = array(
168+
'1' => $response_decoded[0],
169+
'10000' => $response_decoded[1],
170+
);
171+
$this->assertEquals($expected, $actual);
172+
173+
// Second time we call the method the results should be cached and not trigger an API Request.
174+
$this->client->sendRequest(Api::REQUEST_GET, '/rest/api/2/resolution', array(), self::ENDPOINT, $this->credential)
175+
->shouldNotBeCalled();
176+
$this->assertEquals($expected, $this->api->getResolutions(), 'Calling twice did not yield the same results');
177+
}
178+
179+
public function testGetFields()
180+
{
181+
$response = file_get_contents(__DIR__ . '/resources/api_field.json');
182+
183+
$this->expectClientCall(
184+
Api::REQUEST_GET,
185+
'/rest/api/2/field',
186+
array(),
187+
$response
188+
);
189+
190+
$actual = $this->api->getFields();
191+
192+
$response_decoded = json_decode($response, true);
193+
194+
$expected = array(
195+
'issuetype' => $response_decoded[0],
196+
'timespent' => $response_decoded[1],
197+
);
198+
$this->assertEquals($expected, $actual);
199+
200+
// Second time we call the method the results should be cached and not trigger an API Request.
201+
$this->client->sendRequest(Api::REQUEST_GET, '/rest/api/2/field', array(), self::ENDPOINT, $this->credential)
202+
->shouldNotBeCalled();
203+
$this->assertEquals($expected, $this->api->getFields(), 'Calling twice did not yield the same results');
204+
}
205+
206+
public function testGetStatuses()
207+
{
208+
$response = file_get_contents(__DIR__ . '/resources/api_status.json');
209+
210+
$this->expectClientCall(
211+
Api::REQUEST_GET,
212+
'/rest/api/2/status',
213+
array(),
214+
$response
215+
);
216+
217+
$actual = $this->api->getStatuses();
218+
219+
$response_decoded = json_decode($response, true);
220+
221+
$expected = array(
222+
'1' => $response_decoded[0],
223+
'3' => $response_decoded[1],
224+
);
225+
$this->assertEquals($expected, $actual);
226+
227+
// Second time we call the method the results should be cached and not trigger an API Request.
228+
$this->client->sendRequest(Api::REQUEST_GET, '/rest/api/2/status', array(), self::ENDPOINT, $this->credential)
229+
->shouldNotBeCalled();
230+
$this->assertEquals($expected, $this->api->getStatuses(), 'Calling twice did not yield the same results');
231+
}
232+
233+
public function testGetPriorities()
234+
{
235+
$response = file_get_contents(__DIR__ . '/resources/api_priority.json');
236+
237+
$this->expectClientCall(
238+
Api::REQUEST_GET,
239+
'/rest/api/2/priority',
240+
array(),
241+
$response
242+
);
243+
244+
$actual = $this->api->getPriorities();
245+
246+
$response_decoded = json_decode($response, true);
247+
248+
$expected = array(
249+
'1' => $response_decoded[0],
250+
'5' => $response_decoded[1],
251+
);
252+
$this->assertEquals($expected, $actual);
253+
254+
// Second time we call the method the results should be cached and not trigger an API Request.
255+
$this->client->sendRequest(Api::REQUEST_GET, '/rest/api/2/priority', array(), self::ENDPOINT, $this->credential)
256+
->shouldNotBeCalled();
257+
$this->assertEquals($expected, $this->api->getPriorities(), 'Calling twice did not yield the same results');
258+
}
259+
152260
/**
153261
* Expects a particular client call.
154262
*
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
[
2+
{
3+
"id": "issuetype",
4+
"key": "issuetype",
5+
"name": "Issue Type",
6+
"custom": false,
7+
"orderable": true,
8+
"navigable": true,
9+
"searchable": true,
10+
"clauseNames": [
11+
"issuetype",
12+
"type"
13+
],
14+
"schema": {
15+
"type": "issuetype",
16+
"system": "issuetype"
17+
}
18+
},
19+
{
20+
"id": "timespent",
21+
"key": "timespent",
22+
"name": "Time Spent",
23+
"custom": false,
24+
"orderable": false,
25+
"navigable": true,
26+
"searchable": false,
27+
"clauseNames": [
28+
"timespent"
29+
],
30+
"schema": {
31+
"type": "number",
32+
"system": "timespent"
33+
}
34+
}
35+
]
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
[
2+
{
3+
"self": "https://test.atlassian.net/rest/api/2/priority/1",
4+
"statusColor": "#cc0000",
5+
"description": "Blocks development and/or testing work, production could not run.",
6+
"iconUrl": "https://test.atlassian.net/images/icons/priorities/blocker.svg",
7+
"name": "Blocker",
8+
"id": "1"
9+
},
10+
{
11+
"self": "https://test.atlassian.net/rest/api/2/priority/5",
12+
"statusColor": "#003300",
13+
"description": "Cosmetic problem like misspelt words or misaligned text.",
14+
"iconUrl": "https://test.atlassian.net/images/icons/priorities/trivial.svg",
15+
"name": "Trivial",
16+
"id": "5"
17+
}
18+
]
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[
2+
{
3+
"self": "https://test.atlassian.net/rest/api/2/resolution/1",
4+
"id": "1",
5+
"description": "A fix for this issue is checked into the tree and tested.",
6+
"name": "Fixed"
7+
},
8+
{
9+
"self": "https://test.atlassian.net/rest/api/2/resolution/10000",
10+
"id": "10000",
11+
"description": "This issue won't be actioned.",
12+
"name": "Won't Do"
13+
}
14+
]
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
[
2+
{
3+
"self": "https://test.atlassian.net/rest/api/2/status/1",
4+
"description": "The issue is open and ready for the assignee to start work on it.",
5+
"iconUrl": "https://test.atlassian.net/images/icons/statuses/open.png",
6+
"name": "Open",
7+
"id": "1",
8+
"statusCategory": {
9+
"self": "https://test.atlassian.net/rest/api/2/statuscategory/2",
10+
"id": 2,
11+
"key": "new",
12+
"colorName": "blue-gray",
13+
"name": "To Do"
14+
}
15+
},
16+
{
17+
"self": "https://test.atlassian.net/rest/api/2/status/3",
18+
"description": "This issue is being actively worked on at the moment by the assignee.",
19+
"iconUrl": "https://test.atlassian.net/images/icons/statuses/inprogress.png",
20+
"name": "In Progress",
21+
"id": "3",
22+
"statusCategory": {
23+
"self": "https://test.atlassian.net/rest/api/2/statuscategory/4",
24+
"id": 4,
25+
"key": "indeterminate",
26+
"colorName": "yellow",
27+
"name": "In Progress"
28+
}
29+
}
30+
]

0 commit comments

Comments
 (0)