Skip to content

Commit 67d5c27

Browse files
committed
feat: create IssueCategory::fromHttpClient()
1 parent 3aebdc8 commit 67d5c27

File tree

13 files changed

+271
-166
lines changed

13 files changed

+271
-166
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1313
- New method `Redmine\Api\CustomField::fromHttpClient()` for creating the class.
1414
- New method `Redmine\Api\Group::fromHttpClient()` for creating the class.
1515
- New method `Redmine\Api\Issue::fromHttpClient()` for creating the class.
16+
- New method `Redmine\Api\IssueCategory::fromHttpClient()` for creating the class.
1617
- Add support for PHP 8.5
1718
- Add support for Redmine 6.1.
1819

@@ -30,6 +31,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3031
- Extending `Redmine\Api\Group` is deprecated and will be set to final in future, create a wrapper class instead.
3132
- `Redmine\Api\Issue::__construct()` is deprecated and will be set to private in future, use `\Redmine\Api\Issue::fromHttpClient()` instead.
3233
- Extending `Redmine\Api\Issue` is deprecated and will be set to final in future, create a wrapper class instead.
34+
- `Redmine\Api\IssueCategory::__construct()` is deprecated and will be set to private in future, use `\Redmine\Api\IssueCategory::fromHttpClient()` instead.
35+
- Extending `Redmine\Api\IssueCategory` is deprecated and will be set to final in future, create a wrapper class instead.
3336

3437
### Removed
3538

src/Redmine/Api/CustomField.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ final public static function fromHttpClient(HttpClient $httpClient): self
3434

3535
/**
3636
* @deprecated v2.9.0 Use fromHttpClient() instead.
37-
* @see Issue::fromHttpClient()
37+
* @see CustomField::fromHttpClient()
3838
*
3939
* @param Client|HttpClient $client
4040
*/

src/Redmine/Api/Group.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ final public static function fromHttpClient(HttpClient $httpClient): self
4040

4141
/**
4242
* @deprecated v2.9.0 Use fromHttpClient() instead.
43-
* @see Issue::fromHttpClient()
43+
* @see Group::fromHttpClient()
4444
*
4545
* @param Client|HttpClient $client
4646
*/

src/Redmine/Api/Issue.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,7 @@ private function getIssueCategoryApi()
522522
/** @var IssueCategory */
523523
$issueCategoryApi = $this->client->getApi('issue_category');
524524
} else {
525-
$issueCategoryApi = new IssueCategory($this->getHttpClient());
525+
$issueCategoryApi = IssueCategory::fromHttpClient($this->getHttpClient());
526526
}
527527

528528
$this->issueCategoryApi = $issueCategoryApi;

src/Redmine/Api/IssueCategory.php

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,13 @@
22

33
namespace Redmine\Api;
44

5+
use Redmine\Client\Client;
56
use Redmine\Exception;
67
use Redmine\Exception\InvalidParameterException;
78
use Redmine\Exception\MissingParameterException;
89
use Redmine\Exception\SerializerException;
910
use Redmine\Exception\UnexpectedResponseException;
11+
use Redmine\Http\HttpClient;
1012
use Redmine\Http\HttpFactory;
1113
use Redmine\Serializer\JsonSerializer;
1214
use Redmine\Serializer\PathSerializer;
@@ -22,6 +24,11 @@
2224
*/
2325
class IssueCategory extends AbstractApi
2426
{
27+
final public static function fromHttpClient(HttpClient $httpClient): self
28+
{
29+
return new self($httpClient, true);
30+
}
31+
2532
/**
2633
* @var null|array<mixed>
2734
*/
@@ -32,6 +39,32 @@ class IssueCategory extends AbstractApi
3239
*/
3340
private array $issueCategoriesNames = [];
3441

42+
/**
43+
* @deprecated v2.9.0 Use fromHttpClient() instead.
44+
* @see IssueCategory::fromHttpClient()
45+
*
46+
* @param Client|HttpClient $client
47+
*/
48+
public function __construct($client/*, bool $privatelyCalled = false*/)
49+
{
50+
$privatelyCalled = (func_num_args() > 1) ? func_get_arg(1) : false;
51+
52+
if ($privatelyCalled === true) {
53+
parent::__construct($client);
54+
55+
return;
56+
}
57+
58+
if (static::class !== self::class) {
59+
$className = (new \ReflectionClass($this))->isAnonymous() ? '' : ' in `' . static::class . '`';
60+
@trigger_error('Class `' . self::class . '` will declared as final in v3.0.0, stop extending it' . $className . '.', E_USER_DEPRECATED);
61+
} else {
62+
@trigger_error('Method `' . __METHOD__ . '()` is deprecated since v2.9.0 and will declared as private in v3.0.0, use `' . self::class . '::fromHttpClient()` instead.', E_USER_DEPRECATED);
63+
}
64+
65+
parent::__construct($client);
66+
}
67+
3568
/**
3669
* List issue categories for a given project.
3770
*

tests/Unit/Api/IssueCategory/CreateTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public function testCreateReturnsCorrectResponse($identifier, array $parameters,
3434
);
3535

3636
// Create the object under test
37-
$api = new IssueCategory($client);
37+
$api = IssueCategory::fromHttpClient($client);
3838

3939
// Perform the tests
4040
$return = $api->create($identifier, $parameters);
@@ -89,7 +89,7 @@ public function testCreateReturnsEmptyString(): void
8989
);
9090

9191
// Create the object under test
92-
$api = new IssueCategory($client);
92+
$api = IssueCategory::fromHttpClient($client);
9393

9494
// Perform the tests
9595
$return = $api->create(5, ['name' => 'Test Category']);
@@ -103,7 +103,7 @@ public function testCreateThrowsExceptionWithEmptyParameters(): void
103103
$client = $this->createMock(HttpClient::class);
104104

105105
// Create the object under test
106-
$api = new IssueCategory($client);
106+
$api = IssueCategory::fromHttpClient($client);
107107

108108
$this->expectException(MissingParameterException::class);
109109
$this->expectExceptionMessage('Theses parameters are mandatory: `name');
@@ -122,7 +122,7 @@ public function testCreateThrowsExceptionIfMandatoyParametersAreMissing(array $p
122122
$client = $this->createMock(HttpClient::class);
123123

124124
// Create the object under test
125-
$api = new IssueCategory($client);
125+
$api = IssueCategory::fromHttpClient($client);
126126

127127
$this->expectException(MissingParameterException::class);
128128
$this->expectExceptionMessage('Theses parameters are mandatory: `name');
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace Redmine\Tests\Unit\Api\IssueCategory;
4+
5+
use PHPUnit\Framework\Attributes\CoversClass;
6+
use PHPUnit\Framework\TestCase;
7+
use Redmine\Api\IssueCategory;
8+
use Redmine\Http\HttpClient;
9+
10+
#[CoversClass(IssueCategory::class)]
11+
class FromHttpClientTest extends TestCase
12+
{
13+
public function testReturnsCorrectObject(): void
14+
{
15+
$httpClient = $this->createStub(HttpClient::class);
16+
17+
$api = IssueCategory::fromHttpClient($httpClient);
18+
19+
$this->assertInstanceOf(IssueCategory::class, $api);
20+
}
21+
}

tests/Unit/Api/IssueCategory/ListByProjectTest.php

Lines changed: 42 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
use PHPUnit\Framework\Attributes\DataProviderExternal;
77
use PHPUnit\Framework\TestCase;
88
use Redmine\Api\IssueCategory;
9-
use Redmine\Client\Client;
109
use Redmine\Exception\InvalidParameterException;
1110
use Redmine\Exception\UnexpectedResponseException;
12-
use Redmine\Tests\Fixtures\MockClient;
11+
use Redmine\Http\HttpClient;
12+
use Redmine\Tests\Fixtures\AssertingHttpClient;
1313
use Redmine\Tests\Fixtures\TestDataProvider;
1414

1515
#[CoversClass(IssueCategory::class)]
@@ -22,21 +22,21 @@ public function testListByProjectWithoutParametersReturnsResponse(): void
2222
$response = '["API Response"]';
2323
$expectedReturn = ['API Response'];
2424

25-
// Create the used mock objects
26-
$client = $this->createMock(Client::class);
27-
$client->expects($this->once())
28-
->method('requestGet')
29-
->with('/projects/5/issue_categories.json')
30-
->willReturn(true);
31-
$client->expects($this->exactly(1))
32-
->method('getLastResponseBody')
33-
->willReturn($response);
34-
$client->expects($this->exactly(1))
35-
->method('getLastResponseContentType')
36-
->willReturn('application/json');
25+
$client = AssertingHttpClient::create(
26+
$this,
27+
[
28+
'GET',
29+
'/projects/5/issue_categories.json',
30+
'application/json',
31+
'',
32+
200,
33+
'application/json',
34+
$response,
35+
],
36+
);
3737

3838
// Create the object under test
39-
$api = new IssueCategory($client);
39+
$api = IssueCategory::fromHttpClient($client);
4040

4141
// Perform the tests
4242
$this->assertSame($expectedReturn, $api->listByProject($projectId));
@@ -50,21 +50,21 @@ public function testListByProjectWithParametersReturnsResponse(): void
5050
$response = '["API Response"]';
5151
$expectedReturn = ['API Response'];
5252

53-
// Create the used mock objects
54-
$client = $this->createMock(Client::class);
55-
$client->expects($this->once())
56-
->method('requestGet')
57-
->with('/projects/project-slug/issue_categories.json?limit=25&offset=0&0=not-used')
58-
->willReturn(true);
59-
$client->expects($this->exactly(1))
60-
->method('getLastResponseBody')
61-
->willReturn($response);
62-
$client->expects($this->exactly(1))
63-
->method('getLastResponseContentType')
64-
->willReturn('application/json');
53+
$client = AssertingHttpClient::create(
54+
$this,
55+
[
56+
'GET',
57+
'/projects/project-slug/issue_categories.json?limit=25&offset=0&0=not-used',
58+
'application/json',
59+
'',
60+
200,
61+
'application/json',
62+
$response,
63+
],
64+
);
6565

6666
// Create the object under test
67-
$api = new IssueCategory($client);
67+
$api = IssueCategory::fromHttpClient($client);
6868

6969
// Perform the tests
7070
$this->assertSame($expectedReturn, $api->listByProject($projectId, $parameters));
@@ -76,7 +76,7 @@ public function testListByProjectWithParametersReturnsResponse(): void
7676
#[DataProviderExternal(TestDataProvider::class, 'getInvalidProjectIdentifiers')]
7777
public function testListByProjectWithWrongProjectIdentifierThrowsException($projectIdentifier): void
7878
{
79-
$api = new IssueCategory(MockClient::create());
79+
$api = IssueCategory::fromHttpClient($this->createStub(HttpClient::class));
8080

8181
$this->expectException(InvalidParameterException::class);
8282
$this->expectExceptionMessage('Redmine\Api\IssueCategory::listByProject(): Argument #1 ($projectIdentifier) must be of type int or string');
@@ -86,21 +86,21 @@ public function testListByProjectWithWrongProjectIdentifierThrowsException($proj
8686

8787
public function testListByProjectThrowsException(): void
8888
{
89-
// Create the used mock objects
90-
$client = $this->createMock(Client::class);
91-
$client->expects($this->exactly(1))
92-
->method('requestGet')
93-
->with('/projects/5/issue_categories.json')
94-
->willReturn(true);
95-
$client->expects($this->exactly(1))
96-
->method('getLastResponseBody')
97-
->willReturn('');
98-
$client->expects($this->exactly(1))
99-
->method('getLastResponseContentType')
100-
->willReturn('application/json');
89+
$client = AssertingHttpClient::create(
90+
$this,
91+
[
92+
'GET',
93+
'/projects/5/issue_categories.json',
94+
'application/json',
95+
'',
96+
200,
97+
'application/json',
98+
'',
99+
],
100+
);
101101

102102
// Create the object under test
103-
$api = new IssueCategory($client);
103+
$api = IssueCategory::fromHttpClient($client);
104104

105105
$this->expectException(UnexpectedResponseException::class);
106106
$this->expectExceptionMessage('The Redmine server replied with an unexpected response.');

tests/Unit/Api/IssueCategory/ListNamesByProjectTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public function testListNamesByProjectReturnsCorrectResponse($projectIdentifier,
3737
);
3838

3939
// Create the object under test
40-
$api = new IssueCategory($client);
40+
$api = IssueCategory::fromHttpClient($client);
4141

4242
// Perform the tests
4343
$this->assertSame($expectedResponse, $api->listNamesByProject($projectIdentifier));
@@ -104,7 +104,7 @@ public function testListNamesByProjectCallsHttpClientOnlyOnce(): void
104104
);
105105

106106
// Create the object under test
107-
$api = new IssueCategory($client);
107+
$api = IssueCategory::fromHttpClient($client);
108108

109109
// Perform the tests
110110
$this->assertSame([1 => 'IssueCategory 1'], $api->listNamesByProject(5));
@@ -118,7 +118,7 @@ public function testListNamesByProjectCallsHttpClientOnlyOnce(): void
118118
#[DataProviderExternal(TestDataProvider::class, 'getInvalidProjectIdentifiers')]
119119
public function testListNamesByProjectWithWrongProjectIdentifierThrowsException($projectIdentifier): void
120120
{
121-
$api = new IssueCategory($this->createMock(HttpClient::class));
121+
$api = IssueCategory::fromHttpClient($this->createMock(HttpClient::class));
122122

123123
$this->expectException(InvalidParameterException::class);
124124
$this->expectExceptionMessage('Redmine\Api\IssueCategory::listNamesByProject(): Argument #1 ($projectIdentifier) must be of type int or string');

tests/Unit/Api/IssueCategory/RemoveTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public function testRemoveReturnsCorrectResponse(int $issueId, array $params, st
3131
);
3232

3333
// Create the object under test
34-
$api = new IssueCategory($client);
34+
$api = IssueCategory::fromHttpClient($client);
3535

3636
// Perform the tests
3737
$this->assertSame($response, $api->remove($issueId, $params));

0 commit comments

Comments
 (0)