diff --git a/CHANGELOG.md b/CHANGELOG.md index 14d56264..eb26d5f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,12 +12,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - New method `Redmine\Api\CustomField::listNames()` for listing the ids and names of all custom fields. - New method `Redmine\Api\Group::listNames()` for listing the ids and names of all groups. - New method `Redmine\Api\IssueCategory::listNamesByProject()` for listing the ids and names of all issue categories of a project. +- New method `Redmine\Api\IssueStatus::listNames()` for listing the ids and names of all issue statuses. ### Deprecated - `Redmine\Api\CustomField::listing()` is deprecated, use `\Redmine\Api\CustomField::listNames()` instead. - `Redmine\Api\Group::listing()` is deprecated, use `\Redmine\Api\Group::listNames()` instead. - `Redmine\Api\IssueCategory::listing()` is deprecated, use `\Redmine\Api\IssueCategory::listNamesByProject()` instead. +- `Redmine\Api\IssueStatus::listing()` is deprecated, use `\Redmine\Api\IssueStatus::listNamesByProject()` instead. ## [v2.6.0](https://github.com/kbsali/php-redmine-api/compare/v2.5.0...v2.6.0) - 2024-03-25 diff --git a/src/Redmine/Api/IssueStatus.php b/src/Redmine/Api/IssueStatus.php index a7c076a1..022b1e43 100644 --- a/src/Redmine/Api/IssueStatus.php +++ b/src/Redmine/Api/IssueStatus.php @@ -17,6 +17,8 @@ class IssueStatus extends AbstractApi { private $issueStatuses = []; + private $issueStatusNames = null; + /** * List issue statuses. * @@ -37,6 +39,30 @@ final public function list(array $params = []): array } } + /** + * Returns an array of all issue statuses with id/name pairs. + * + * @return array list of issue statuses (id => name) + */ + final public function listNames(): array + { + if ($this->issueStatusNames !== null) { + return $this->issueStatusNames; + } + + $this->issueStatusNames = []; + + $list = $this->list(); + + if (array_key_exists('issue_statuses', $list)) { + foreach ($list['issue_statuses'] as $issueStatus) { + $this->issueStatusNames[(int) $issueStatus['id']] = (string) $issueStatus['name']; + } + } + + return $this->issueStatusNames; + } + /** * List issue statuses. * @@ -73,12 +99,17 @@ public function all(array $params = []) /** * Returns an array of issue statuses with name/id pairs. * + * @deprecated v2.7.0 Use listNames() instead. + * @see IssueStatus::listNames() + * * @param bool $forceUpdate to force the update of the statuses var * * @return array list of issue statuses (id => name) */ public function listing($forceUpdate = false) { + @trigger_error('`' . __METHOD__ . '()` is deprecated since v2.7.0, use `' . __CLASS__ . '::listNames()` instead.', E_USER_DEPRECATED); + if (empty($this->issueStatuses) || $forceUpdate) { $this->issueStatuses = $this->list(); } diff --git a/tests/Behat/Bootstrap/IssueStatusContextTrait.php b/tests/Behat/Bootstrap/IssueStatusContextTrait.php index f8c8d16f..d0a5f401 100644 --- a/tests/Behat/Bootstrap/IssueStatusContextTrait.php +++ b/tests/Behat/Bootstrap/IssueStatusContextTrait.php @@ -4,6 +4,8 @@ namespace Redmine\Tests\Behat\Bootstrap; +use Redmine\Api\IssueStatus; + trait IssueStatusContextTrait { /** @@ -22,4 +24,32 @@ public function iHaveAnIssueStatusWithTheName($issueStatusName) ], ); } + + /** + * @When I list all issue statuses + */ + public function iListAllIssueStatuses() + { + /** @var IssueStatus */ + $api = $this->getNativeCurlClient()->getApi('issue_status'); + + $this->registerClientResponse( + $api->list(), + $api->getLastResponse(), + ); + } + + /** + * @When I list all issue status names + */ + public function iListAllIssueStatusNames() + { + /** @var IssueStatus */ + $api = $this->getNativeCurlClient()->getApi('issue_status'); + + $this->registerClientResponse( + $api->listNames(), + $api->getLastResponse(), + ); + } } diff --git a/tests/Behat/features/issue_status.feature b/tests/Behat/features/issue_status.feature new file mode 100644 index 00000000..540cd4ff --- /dev/null +++ b/tests/Behat/features/issue_status.feature @@ -0,0 +1,69 @@ +@issue_status +Feature: Interacting with the REST API for issue statuses + In order to interact with REST API for issue statuses + As a user + I want to make sure the Redmine server replies with the correct response + + Scenario: Listing of zero issue statuses + Given I have a "NativeCurlClient" client + When I list all issue statuses + Then the response has the status code "200" + And the response has the content type "application/json" + And the returned data has only the following properties + """ + issue_statuses + """ + And the returned data "issue_statuses" property is an array + And the returned data "issue_statuses" property contains "0" items + + Scenario: Listing of multiple issue statuses + Given I have a "NativeCurlClient" client + And I have an issue status with the name "New" + And I have an issue status with the name "Done" + When I list all issue statuses + Then the response has the status code "200" + And the response has the content type "application/json" + And the returned data has only the following properties + """ + issue_statuses + """ + And the returned data "issue_statuses" property is an array + And the returned data "issue_statuses" property contains "2" items + # field 'description' was added in Redmine 5.1.0, see https://www.redmine.org/issues/2568 + And the returned data "issue_statuses.0" property contains the following data with Redmine version ">= 5.1.0" + | property | value | + | id | 1 | + | name | New | + | is_closed | false | + | description | null | + But the returned data "issue_statuses.0" property contains the following data with Redmine version "< 5.1.0" + | property | value | + | id | 1 | + | name | New | + | is_closed | false | + # field 'description' was added in Redmine 5.1.0, see https://www.redmine.org/issues/2568 + And the returned data "issue_statuses.1" property contains the following data with Redmine version ">= 5.1.0" + | property | value | + | id | 2 | + | name | Done | + | is_closed | false | + | description | null | + But the returned data "issue_statuses.1" property contains the following data with Redmine version "< 5.1.0" + | property | value | + | id | 2 | + | name | Done | + | is_closed | false | + + Scenario: Listing of multiple issue status names + Given I have a "NativeCurlClient" client + And I have an issue status with the name "New" + And I have an issue status with the name "Done" + When I list all issue status names + Then the response has the status code "200" + And the response has the content type "application/json" + And the returned data is an array + And the returned data contains "2" items + And the returned data contains the following data + | property | value | + | 1 | New | + | 2 | Done | diff --git a/tests/Unit/Api/IssueStatus/ListNamesTest.php b/tests/Unit/Api/IssueStatus/ListNamesTest.php new file mode 100644 index 00000000..231e4fc5 --- /dev/null +++ b/tests/Unit/Api/IssueStatus/ListNamesTest.php @@ -0,0 +1,108 @@ +assertSame($expectedResponse, $api->listNames()); + } + + public static function getListNamesData(): array + { + return [ + 'test without issue statuses' => [ + '/issue_statuses.json', + 201, + << [ + '/issue_statuses.json', + 201, + << "IssueStatus C", + 8 => "IssueStatus B", + 9 => "IssueStatus A", + ], + ], + ]; + } + + public function testListNamesCallsHttpClientOnlyOnce() + { + $client = AssertingHttpClient::create( + $this, + [ + 'GET', + '/issue_statuses.json', + 'application/json', + '', + 200, + 'application/json', + <<assertSame([1 => 'IssueStatus 1'], $api->listNames()); + $this->assertSame([1 => 'IssueStatus 1'], $api->listNames()); + $this->assertSame([1 => 'IssueStatus 1'], $api->listNames()); + } +} diff --git a/tests/Unit/Api/IssueStatusTest.php b/tests/Unit/Api/IssueStatusTest.php index 18eeb651..e292ee0b 100644 --- a/tests/Unit/Api/IssueStatusTest.php +++ b/tests/Unit/Api/IssueStatusTest.php @@ -215,6 +215,38 @@ public function testListingCallsGetEveryTimeWithForceUpdate() $this->assertSame($expectedReturn, $api->listing(true)); } + /** + * Test listing(). + */ + public function testListingTriggersDeprecationWarning() + { + $client = $this->createMock(Client::class); + $client->method('requestGet') + ->willReturn(true); + $client->method('getLastResponseBody') + ->willReturn('{"issue_statuses":[{"id":1,"name":"IssueStatus 1"},{"id":5,"name":"IssueStatus 5"}]}'); + $client->method('getLastResponseContentType') + ->willReturn('application/json'); + + $api = new IssueStatus($client); + + // PHPUnit 10 compatible way to test trigger_error(). + set_error_handler( + function ($errno, $errstr): bool { + $this->assertSame( + '`Redmine\Api\IssueStatus::listing()` is deprecated since v2.7.0, use `Redmine\Api\IssueStatus::listNames()` instead.', + $errstr, + ); + + restore_error_handler(); + return true; + }, + E_USER_DEPRECATED, + ); + + $api->listing(); + } + /** * Test getIdByName(). */