From f6fc760e31ace01afc9529ddb9636102d943d7c3 Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 29 Oct 2024 21:35:17 +0200 Subject: [PATCH] Added support for "entityId", "hierarchyLevel" and "untranslatedName" to the "IssueType" class --- CHANGELOG.md | 1 + src/Jira/IssueType.php | 151 +++++++--------------- tests/Jira/ApiTest.php | 23 ++++ tests/Jira/IssueTypeTest.php | 79 ++++++++--- tests/Jira/resources/api_issue_types.json | 23 ++++ 5 files changed, 156 insertions(+), 121 deletions(-) create mode 100644 tests/Jira/resources/api_issue_types.json diff --git a/CHANGELOG.md b/CHANGELOG.md index a177124..62fd651 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,7 @@ This project adheres to [Semantic Versioning](https://semver.org/). - Error details from failed API calls were not available back from `Api::api method` call by [@betterphp]. - Warning about `count()` function usage on PHP 7.2, when searching for issues by [@aik099]. - Capitalize `globalId` properly in `createRemotelink` [@glensc]. +- The `Api::getIssueTypes` was always returning an error by [@aik099]. ## [1.0.0] - 2014-07-27 ### Added diff --git a/src/Jira/IssueType.php b/src/Jira/IssueType.php index 75f7bfe..c1360d3 100644 --- a/src/Jira/IssueType.php +++ b/src/Jira/IssueType.php @@ -25,57 +25,29 @@ namespace chobie\Jira; +/** + * The issue type. + * + * @method integer getAvatarId() Gets avatar ID. + * @method string getDescription() Gets description. + * @method string getEntityId() Gets Unique ID for next-gen projects. + * @method integer getHierarchyLevel() Gets hierarchy level. + * @method string getIconUrl() Gets icon url. + * @method string getId() Gets ID. + * @method string getName() Gets name. + * @method string getUntranslatedName() Gets untranslated name. + * @method array getScope() Gets details of the next-gen projects the issue type is available in. + * @method string getSelf() Gets the URL of these issue type details. + */ class IssueType { /** - * Self. - * - * @var string - */ - protected $self; - - /** - * ID. - * - * @var string - */ - protected $id; - - /** - * Description. - * - * @var string - */ - protected $description; - - /** - * Icon URL. + * Data. * - * @var string - */ - protected $iconUrl; - - /** - * Name. - * - * @var string - */ - protected $name; - - /** - * Sub-task. - * - * @var string - */ - protected $subTask; - - /** - * Avatar ID. - * - * @var string + * @var array */ - protected $avatarId; + private $_data; /** * Acceptable keys. @@ -83,93 +55,70 @@ class IssueType * @var array */ private $_acceptableKeys = array( - 'self', - 'id', + 'avatarId', 'description', + 'entityId', + 'hierarchyLevel', 'iconUrl', + 'id', 'name', - 'subtask', - 'avatarId', 'scope', + 'self', + 'subtask', + + 'untranslatedName', ); /** * Creates issue instance. * - * @param array $types Types. + * @param array $data Data. * - * @throws \Exception When unknown type is given. + * @throws \Exception When an unknown data field is given. */ - public function __construct(array $types) + public function __construct(array $data) { - foreach ( $types as $key => $value ) { - if ( in_array($key, $this->_acceptableKeys) ) { - $this->$key = $value; - } - else { - throw new \Exception('the key ' . $key . ' does not support'); - } + $unknown_fields = array_diff(array_keys($data), $this->_acceptableKeys); + + if ( $unknown_fields ) { + throw new \Exception( + 'The "' . implode('", "', $unknown_fields) . '" issue type keys are not supported.' + ); } - } - /** - * Gets name. - * - * @return string - */ - public function getName() - { - return $this->name; + $this->_data = $data; } /** * Gets sub-task. * - * @return string + * @return boolean */ public function isSubtask() { - return $this->subTask; + return $this->_data['subtask']; } /** - * Gets ID. + * Allows accessing issue type properties. * - * @return string - */ - public function getId() - { - return $this->id; - } - - /** - * Gets description. + * @param string $method Method name. + * @param array $params Params. * - * @return string + * @return mixed + * @throws \Exception When requested method wasn't found. */ - public function getDescription() + public function __call($method, array $params) { - return $this->description; - } + if ( preg_match('/^get(.+)$/', $method, $regs) ) { + $data_key = lcfirst($regs[1]); - /** - * Gets icon url. - * - * @return string - */ - public function getIconUrl() - { - return $this->iconUrl; - } + if ( in_array($data_key, $this->_acceptableKeys) ) { + return array_key_exists($data_key, $this->_data) ? $this->_data[$data_key] : null; + } + } - /** - * Gets avatar id. - * - * @return string - */ - public function getAvatarId() - { - return $this->avatarId; + throw new \Exception('The "' . __CLASS__ . '::' . $method . '" method does not exist.'); } } diff --git a/tests/Jira/ApiTest.php b/tests/Jira/ApiTest.php index 7f1d862..31c69a4 100644 --- a/tests/Jira/ApiTest.php +++ b/tests/Jira/ApiTest.php @@ -6,6 +6,7 @@ use chobie\Jira\Api; use chobie\Jira\Api\Authentication\AuthenticationInterface; use chobie\Jira\Api\Result; +use chobie\Jira\IssueType; use PHPUnit\Framework\TestCase; use Prophecy\Prophecy\ObjectProphecy; @@ -289,6 +290,28 @@ public function testGetPriorities() $this->assertEquals($expected, $this->api->getPriorities(), 'Calling twice did not yield the same results'); } + public function testGetIssueTypes() + { + $response = file_get_contents(__DIR__ . '/resources/api_issue_types.json'); + + $this->expectClientCall( + Api::REQUEST_GET, + '/rest/api/2/issuetype', + array(), + $response + ); + + $actual = $this->api->getIssueTypes(); + + $response_decoded = json_decode($response, true); + + $expected = array( + new IssueType($response_decoded[0]), + new IssueType($response_decoded[1]), + ); + $this->assertEquals($expected, $actual); + } + /** * Expects a particular client call. * diff --git a/tests/Jira/IssueTypeTest.php b/tests/Jira/IssueTypeTest.php index 474659b..ad32ca5 100644 --- a/tests/Jira/IssueTypeTest.php +++ b/tests/Jira/IssueTypeTest.php @@ -5,32 +5,47 @@ use chobie\Jira\IssueType; use PHPUnit\Framework\TestCase; +use Yoast\PHPUnitPolyfills\Polyfills\ExpectException; class IssueTypeTest extends TestCase { + use ExpectException; public function testHandlesSingleIssueTypeWithAvatarId() { - $issueTypeSource = array( - 'self' => 'https://hosted.atlassian.net/rest/api/2/issuetype/4', - 'id' => '4', - 'description' => 'An improvement or enhancement to an existing feature or task.', - 'iconUrl' => 'https://hosted.atlassian.net/secure/viewavatar?size=xsmall&avatarId=1&avatarType=issuetype', - 'name' => 'Improvement', - 'subtask' => false, - 'avatarId' => 1, + $issue_type_source = array( + 'self' => 'https://hosted.atlassian.net/rest/api/2/issuetype/4', + 'id' => '4', + 'description' => 'An improvement or enhancement to an existing feature or task.', + 'entityId' => '9d7dd6f7-e8b6-4247-954b-7b2c9b2a5ba2', + 'iconUrl' => 'https://hosted.atlassian.net/secure/viewavatar?size=xsmall&avatarId=1&avatarType=issuetype', + 'name' => 'Improvement', + 'scope' => array( + 'project' => array('id' => '10000'), + 'type' => 'PROJECT', + ), + 'untranslatedName' => 'Epic', + 'subtask' => false, + 'hierarchyLevel' => 1, + 'avatarId' => 1, ); - $issueType = new IssueType($issueTypeSource); - $this->assertEquals($issueType->getId(), $issueTypeSource['id']); - $this->assertEquals($issueType->getDescription(), $issueTypeSource['description']); - $this->assertEquals($issueType->getIconUrl(), $issueTypeSource['iconUrl']); - $this->assertEquals($issueType->getName(), $issueTypeSource['name']); - $this->assertEquals($issueType->getAvatarId(), $issueTypeSource['avatarId']); + $issue_type = new IssueType($issue_type_source); + $this->assertEquals($issue_type->getSelf(), $issue_type_source['self']); + $this->assertEquals($issue_type->getId(), $issue_type_source['id']); + $this->assertEquals($issue_type->getDescription(), $issue_type_source['description']); + $this->assertEquals($issue_type->getEntityId(), $issue_type_source['entityId']); + $this->assertEquals($issue_type->getIconUrl(), $issue_type_source['iconUrl']); + $this->assertEquals($issue_type->getName(), $issue_type_source['name']); + $this->assertEquals($issue_type->getScope(), $issue_type_source['scope']); + $this->assertEquals($issue_type->isSubtask(), $issue_type_source['subtask']); + $this->assertEquals($issue_type->getUntranslatedName(), $issue_type_source['untranslatedName']); + $this->assertEquals($issue_type->getHierarchyLevel(), $issue_type_source['hierarchyLevel']); + $this->assertEquals($issue_type->getAvatarId(), $issue_type_source['avatarId']); } public function testHandlesSingleIssueTypeWithoutAvatarId() { - $issueTypeSource = array( + $issue_type_source = array( 'self' => 'https://hosted.atlassian.net/rest/api/2/issuetype/4', 'id' => '4', 'description' => 'An improvement or enhancement to an existing feature or task.', @@ -38,11 +53,35 @@ public function testHandlesSingleIssueTypeWithoutAvatarId() 'name' => 'Improvement', 'subtask' => false, ); - $issueType = new IssueType($issueTypeSource); - $this->assertEquals($issueType->getId(), $issueTypeSource['id']); - $this->assertEquals($issueType->getDescription(), $issueTypeSource['description']); - $this->assertEquals($issueType->getIconUrl(), $issueTypeSource['iconUrl']); - $this->assertEquals($issueType->getName(), $issueTypeSource['name']); + $issue_type = new IssueType($issue_type_source); + $this->assertEquals($issue_type->getId(), $issue_type_source['id']); + $this->assertEquals($issue_type->getDescription(), $issue_type_source['description']); + $this->assertEquals($issue_type->getIconUrl(), $issue_type_source['iconUrl']); + $this->assertEquals($issue_type->getName(), $issue_type_source['name']); + } + + public function testGettingUnknownProperty() + { + $issue_type_source = array( + 'self' => 'https://hosted.atlassian.net/rest/api/2/issuetype/4', + ); + $issue_type = new IssueType($issue_type_source); + + $this->expectException(\Exception::class); + $this->expectExceptionMessage('The "chobie\Jira\IssueType::getUnknown" method does not exist.'); + $issue_type->getUnknown(); + } + + public function testCreatingWithUnknownField() + { + $this->expectException(\Exception::class); + $this->expectExceptionMessage('The "unknown" issue type keys are not supported.'); + + $issue_type_source = array( + 'self' => 'https://hosted.atlassian.net/rest/api/2/issuetype/4', + 'unknown' => '4', + ); + new IssueType($issue_type_source); } } diff --git a/tests/Jira/resources/api_issue_types.json b/tests/Jira/resources/api_issue_types.json new file mode 100644 index 0000000..303025d --- /dev/null +++ b/tests/Jira/resources/api_issue_types.json @@ -0,0 +1,23 @@ +[ + { + "self": "https://test.atlassian.net/rest/api/2/issuetype/10000", + "id": "10000", + "description": "A big user story that needs to be broken down. Created by Jira Software - do not edit or delete.", + "iconUrl": "https://test.atlassian.net/images/icons/issuetypes/epic.svg", + "name": "Epic", + "untranslatedName": "Epic", + "subtask": false, + "hierarchyLevel": 1 + }, + { + "self": "https://test.atlassian.net/rest/api/2/issuetype/10034", + "id": "10034", + "description": "A problem which impairs or prevents the functions of the product. (Migrated on 31 Aug 2024 07:01 UTC)", + "iconUrl": "https://test.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10300?size=medium", + "name": "Bug Report", + "untranslatedName": "Bug Report", + "subtask": false, + "avatarId": 10300, + "hierarchyLevel": 0 + } +]