Skip to content

Commit 3bd9db6

Browse files
committed
implement IssueCategory::listNamesByProject()
1 parent 030ae2b commit 3bd9db6

File tree

4 files changed

+207
-0
lines changed

4 files changed

+207
-0
lines changed

src/Redmine/Api/IssueCategory.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ class IssueCategory extends AbstractApi
2424
{
2525
private $issueCategories = [];
2626

27+
private $issueCategoriesNames = [];
28+
2729
/**
2830
* List issue categories for a given project.
2931
*
@@ -53,6 +55,41 @@ final public function listByProject($projectIdentifier, array $params = []): arr
5355
}
5456
}
5557

58+
/**
59+
* Returns an array of all issue categories by a project with id/name pairs.
60+
*
61+
* @param string|int $projectIdentifier project id or literal identifier
62+
*
63+
* @throws InvalidParameterException if $projectIdentifier is not of type int or string
64+
*
65+
* @return array<int,string> list of issue category names (id => name)
66+
*/
67+
final public function listNamesByProject($projectIdentifier): array
68+
{
69+
if (! is_int($projectIdentifier) && ! is_string($projectIdentifier)) {
70+
throw new InvalidParameterException(sprintf(
71+
'%s(): Argument #1 ($projectIdentifier) must be of type int or string',
72+
__METHOD__,
73+
));
74+
}
75+
76+
if (array_key_exists($projectIdentifier, $this->issueCategoriesNames)) {
77+
return $this->issueCategoriesNames[$projectIdentifier];
78+
}
79+
80+
$this->issueCategoriesNames[$projectIdentifier] = [];
81+
82+
$list = $this->listByProject($projectIdentifier);
83+
84+
if (array_key_exists('issue_categories', $list)) {
85+
foreach ($list['issue_categories'] as $category) {
86+
$this->issueCategoriesNames[$projectIdentifier][(int) $category['id']] = $category['name'];
87+
}
88+
}
89+
90+
return $this->issueCategoriesNames[$projectIdentifier];
91+
}
92+
5693
/**
5794
* List issue categories.
5895
*

tests/Behat/Bootstrap/IssueCategoryContextTrait.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,20 @@ public function iListAllIssueCategoriesForProjectIdentifier($identifier)
4343
);
4444
}
4545

46+
/**
47+
* @When I list all issue category names for project identifier :identifier
48+
*/
49+
public function iListAllIssueCategoryNamesForProjectIdentifier($identifier)
50+
{
51+
/** @var IssueCategory */
52+
$api = $this->getNativeCurlClient()->getApi('issue_category');
53+
54+
$this->registerClientResponse(
55+
$api->listNamesByProject($identifier),
56+
$api->getLastResponse(),
57+
);
58+
}
59+
4660
/**
4761
* @When I update the issue category with id :id and the following data
4862
*/

tests/Behat/features/issue_category.feature

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,23 @@ Feature: Interacting with the REST API for issue categories
152152
| id | 1 |
153153
| name | Test Project |
154154

155+
Scenario: Listing of multiple issue category names
156+
Given I have a "NativeCurlClient" client
157+
And I create a project with name "Test Project" and identifier "test-project"
158+
And I create an issue category for project identifier "test-project" and with the following data
159+
| property | value |
160+
| name | Category name B |
161+
And I create an issue category for project identifier "test-project" and with the following data
162+
| property | value |
163+
| name | Category name A |
164+
When I list all issue category names for project identifier "test-project"
165+
Then the response has the status code "200"
166+
And the response has the content type "application/json"
167+
And the returned data contains the following data
168+
| property | value |
169+
| 1 | Category name B |
170+
| 2 | Category name A |
171+
155172
@issue_category
156173
Scenario: Updating an issue category with all data
157174
Given I have a "NativeCurlClient" client
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Redmine\Tests\Unit\Api\IssueCategory;
6+
7+
use PHPUnit\Framework\Attributes\CoversClass;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use PHPUnit\Framework\TestCase;
10+
use Redmine\Api\IssueCategory;
11+
use Redmine\Exception\InvalidParameterException;
12+
use Redmine\Http\HttpClient;
13+
use Redmine\Tests\Fixtures\AssertingHttpClient;
14+
use stdClass;
15+
16+
#[CoversClass(IssueCategory::class)]
17+
class ListNamesByProjectTest extends TestCase
18+
{
19+
/**
20+
* @dataProvider getListNamesByProjectData
21+
*/
22+
#[DataProvider('getListNamesByProjectData')]
23+
public function testListNamesByProjectReturnsCorrectResponse($projectIdentifier, $expectedPath, $responseCode, $response, $expectedResponse)
24+
{
25+
$client = AssertingHttpClient::create(
26+
$this,
27+
[
28+
'GET',
29+
$expectedPath,
30+
'application/json',
31+
'',
32+
$responseCode,
33+
'application/json',
34+
$response,
35+
],
36+
);
37+
38+
// Create the object under test
39+
$api = new IssueCategory($client);
40+
41+
// Perform the tests
42+
$this->assertSame($expectedResponse, $api->listNamesByProject($projectIdentifier));
43+
}
44+
45+
public static function getListNamesByProjectData(): array
46+
{
47+
return [
48+
'test without issue categories' => [
49+
5,
50+
'/projects/5/issue_categories.json',
51+
201,
52+
<<<JSON
53+
{
54+
"issue_categories": []
55+
}
56+
JSON,
57+
[],
58+
],
59+
'test with multiple categories' => [
60+
'test-project',
61+
'/projects/test-project/issue_categories.json',
62+
201,
63+
<<<JSON
64+
{
65+
"issue_categories": [
66+
{"id": 9, "name": "IssueCategory 1"},
67+
{"id": 8, "name": "IssueCategory 2"},
68+
{"id": 7, "name": "IssueCategory 3"}
69+
]
70+
}
71+
JSON,
72+
[
73+
9 => "IssueCategory 1",
74+
8 => "IssueCategory 2",
75+
7 => "IssueCategory 3",
76+
],
77+
],
78+
];
79+
}
80+
81+
public function testListNamesByProjectCallsHttpClientOnlyOnce()
82+
{
83+
$client = AssertingHttpClient::create(
84+
$this,
85+
[
86+
'GET',
87+
'/projects/5/issue_categories.json',
88+
'application/json',
89+
'',
90+
200,
91+
'application/json',
92+
<<<JSON
93+
{
94+
"issue_categories": [
95+
{
96+
"id": 1,
97+
"name": "IssueCategory 1"
98+
}
99+
]
100+
}
101+
JSON,
102+
],
103+
);
104+
105+
// Create the object under test
106+
$api = new IssueCategory($client);
107+
108+
// Perform the tests
109+
$this->assertSame([1 => 'IssueCategory 1'], $api->listNamesByProject(5));
110+
$this->assertSame([1 => 'IssueCategory 1'], $api->listNamesByProject(5));
111+
$this->assertSame([1 => 'IssueCategory 1'], $api->listNamesByProject(5));
112+
}
113+
114+
/**
115+
* @dataProvider getInvalidProjectIdentifiers
116+
*/
117+
#[DataProvider('getInvalidProjectIdentifiers')]
118+
public function testListNamesByProjectWithWrongProjectIdentifierThrowsException($projectIdentifier)
119+
{
120+
$api = new IssueCategory($this->createMock(HttpClient::class));
121+
122+
$this->expectException(InvalidParameterException::class);
123+
$this->expectExceptionMessage('Redmine\Api\IssueCategory::listNamesByProject(): Argument #1 ($projectIdentifier) must be of type int or string');
124+
125+
$api->listNamesByProject($projectIdentifier);
126+
}
127+
128+
public static function getInvalidProjectIdentifiers(): array
129+
{
130+
return [
131+
'null' => [null],
132+
'true' => [true],
133+
'false' => [false],
134+
'float' => [0.0],
135+
'array' => [[]],
136+
'object' => [new stdClass()],
137+
];
138+
}
139+
}

0 commit comments

Comments
 (0)