diff --git a/CHANGELOG.md b/CHANGELOG.md index 50e6982..084758d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ This project adheres to [Semantic Versioning](https://semver.org/). ### Changed - The `Api::downloadAttachment` method now throws an exception, when attempting to download from a non-Jira website by [@aik099] (#240). +- The `$params` argument of the `Api::getWorklogs` method is now optional by [@aik099] (#244). +- The `$params` argument of the `Api::getTransitions` method is now optional by [@aik099] (#244). ### Removed ... diff --git a/src/Jira/Api.php b/src/Jira/Api.php index f894716..35f5c44 100644 --- a/src/Jira/Api.php +++ b/src/Jira/Api.php @@ -178,20 +178,20 @@ protected function clearLocalCaches() * Get fields definitions. * * @return array + * @link https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issue-fields/#api-rest-api-2-field-get */ public function getFields() { // Fetch fields when the method is called for the first time. if ( $this->fields === null ) { - $fields = array(); - $result = $this->api(self::REQUEST_GET, '/rest/api/2/field', array(), true); + $ret = array(); + $fields = $this->api(self::REQUEST_GET, '/rest/api/2/field', array(), true); - /* set hash key as custom field id */ - foreach ( $result as $field ) { - $fields[$field['id']] = $field; + foreach ( $fields as $field_data ) { + $ret[$field_data['id']] = $field_data; } - $this->fields = $fields; + $this->fields = $ret; } return $this->fields; @@ -203,11 +203,18 @@ public function getFields() * @param string $issue_key Issue key should be "YOURPROJ-221". * @param string $expand Expand. * - * @return Result|false + * @return Result + * @link https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issues/#api-rest-api-2-issue-issueidorkey-get */ public function getIssue($issue_key, $expand = '') { - return $this->api(self::REQUEST_GET, sprintf('/rest/api/2/issue/%s', $issue_key), array('expand' => $expand)); + $params = array(); + + if ( $expand !== '' ) { + $params['expand'] = $expand; + } + + return $this->api(self::REQUEST_GET, sprintf('/rest/api/2/issue/%s', $issue_key), $params); } /** @@ -217,6 +224,7 @@ public function getIssue($issue_key, $expand = '') * @param array $params Params. * * @return Result|false + * @link https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issues/#api-rest-api-2-issue-issueidorkey-put */ public function editIssue($issue_key, array $params) { @@ -273,7 +281,8 @@ public function getProject($project_key) * * @param string $project_key Project key. * - * @return array|false + * @return array + * @link https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-project-roles/#api-rest-api-2-role-get */ public function getRoles($project_key) { @@ -283,8 +292,8 @@ public function getRoles($project_key) /** * Returns role details. * - * @param string $project_key Project key. - * @param string $role_id Role ID. + * @param string $project_key Project key. + * @param integer $role_id Role ID. * * @return array|false */ @@ -322,7 +331,8 @@ public function getRoleDetails($project_key, $role_id) * an issue type that does not exist is not an error. * @param array $expand Optional list of entities to expand in the response. * - * @return array|false + * @return array + * @link https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issues/#api-rest-api-2-issue-createmeta-get */ public function getCreateMeta( array $project_ids = null, @@ -331,7 +341,7 @@ public function getCreateMeta( array $issue_type_names = null, array $expand = null ) { - // Create comma separated query parameters for the supplied filters. + // Create comma-separated query parameters for the supplied filters. $data = array(); if ( $project_ids !== null ) { @@ -413,7 +423,7 @@ public function addWorklog($issue_key, $time_spent, array $params = array()) * @return Result|false * @since 2.0.0 */ - public function getWorklogs($issue_key, array $params) + public function getWorklogs($issue_key, array $params = array()) { return $this->api(self::REQUEST_GET, sprintf('/rest/api/2/issue/%s/worklog', $issue_key), $params); } @@ -445,8 +455,9 @@ public function deleteWorklog($issue_key, $worklog_id, array $params = array()) * @param array $params Params. * * @return Result|false + * @link https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issues/#api-rest-api-2-issue-issueidorkey-transitions-get */ - public function getTransitions($issue_key, array $params) + public function getTransitions($issue_key, array $params = array()) { return $this->api(self::REQUEST_GET, sprintf('/rest/api/2/issue/%s/transitions', $issue_key), $params); } @@ -468,17 +479,18 @@ public function transition($issue_key, array $params) * Get available issue types. * * @return IssueType[] + * @link https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issue-types/#api-rest-api-2-issuetype-get */ public function getIssueTypes() { - $result = array(); - $types = $this->api(self::REQUEST_GET, '/rest/api/2/issuetype', array(), true); + $ret = array(); + $issue_types = $this->api(self::REQUEST_GET, '/rest/api/2/issuetype', array(), true); - foreach ( $types as $issue_type ) { - $result[] = new IssueType($issue_type); + foreach ( $issue_types as $issue_type_data ) { + $ret[] = new IssueType($issue_type_data); } - return $result; + return $ret; } /** @@ -486,7 +498,8 @@ public function getIssueTypes() * * @param string $project_key Project key. * - * @return array|false + * @return array + * @link https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-project-versions/#api-rest-api-2-project-projectidorkey-versions-get */ public function getVersions($project_key) { @@ -507,6 +520,11 @@ public function findVersionByName($project_key, $name) // Fetch all versions of this project. $versions = $this->getVersions($project_key); + // Don't iterate versions, when API call ended with an error. + if ( array_key_exists('errorMessages', $versions) || array_key_exists('errors', $versions) ) { + return null; + } + // Filter results on the name. $matching_versions = array_filter($versions, function (array $version) use ($name) { return $version['name'] == $name; @@ -517,7 +535,7 @@ public function findVersionByName($project_key, $name) return null; } - // Multiple results should not happen since name is unique. + // Multiple results should not happen since the name is unique. return reset($matching_versions); } @@ -525,21 +543,21 @@ public function findVersionByName($project_key, $name) * Get available priorities. * * @return array + * @link https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issue-priorities/#api-rest-api-2-priority-get * @since 2.0.0 */ public function getPriorities() { // Fetch priorities when the method is called for the first time. if ( $this->priorities === null ) { - $priorities = array(); - $result = $this->api(self::REQUEST_GET, '/rest/api/2/priority', array(), true); + $ret = array(); + $priorities = $this->api(self::REQUEST_GET, '/rest/api/2/priority', array(), true); - /* set hash key as custom field id */ - foreach ( $result as $priority ) { - $priorities[$priority['id']] = $priority; + foreach ( $priorities as $priority_data ) { + $ret[$priority_data['id']] = $priority_data; } - $this->priorities = $priorities; + $this->priorities = $ret; } return $this->priorities; @@ -549,20 +567,20 @@ public function getPriorities() * Get available statuses. * * @return array + * @link https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-status/#api-rest-api-2-statuses-get */ public function getStatuses() { // Fetch statuses when the method is called for the first time. if ( $this->statuses === null ) { - $statuses = array(); - $result = $this->api(self::REQUEST_GET, '/rest/api/2/status', array(), true); + $ret = array(); + $statuses = $this->api(self::REQUEST_GET, '/rest/api/2/status', array(), true); - /* set hash key as custom field id */ - foreach ( $result as $status ) { - $statuses[$status['id']] = $status; + foreach ( $statuses as $status_data ) { + $ret[$status_data['id']] = $status_data; } - $this->statuses = $statuses; + $this->statuses = $ret; } return $this->statuses; @@ -571,16 +589,17 @@ public function getStatuses() /** * Creates an issue. * - * @param string $project_key Project key. - * @param string $summary Summary. - * @param string $issue_type Issue type. - * @param array $options Options. + * @param string $project_key Project key. + * @param string $summary Summary. + * @param string $issue_type Issue type. + * @param array $other_fields Other fields. * * @return Result|false + * @link https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issues/#api-rest-api-2-issue-post */ - public function createIssue($project_key, $summary, $issue_type, array $options = array()) + public function createIssue($project_key, $summary, $issue_type, array $other_fields = array()) { - $default = array( + $default_fields = array( 'project' => array( 'key' => $project_key, ), @@ -590,9 +609,9 @@ public function createIssue($project_key, $summary, $issue_type, array $options ), ); - $default = array_merge($default, $options); + $fields = array_merge($default_fields, $other_fields); - return $this->api(self::REQUEST_POST, '/rest/api/2/issue/', array('fields' => $default)); + return $this->api(self::REQUEST_POST, '/rest/api/2/issue', array('fields' => $fields)); } /** @@ -603,7 +622,8 @@ public function createIssue($project_key, $summary, $issue_type, array $options * @param integer $max_results Max results. * @param string $fields Fields. * - * @return Result|false + * @return Result + * @link https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issue-search/#api-rest-api-2-search-get */ public function search($jql, $start_at = 0, $max_results = 20, $fields = '*navigable') { @@ -626,26 +646,23 @@ public function search($jql, $start_at = 0, $max_results = 20, $fields = '*navig * * @param string $project_key Project key. * @param string $version Version. - * @param array $options Options. + * @param array $params Params. * - * @return Result|false + * @return Result + * @link https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-project-versions/#api-rest-api-2-version-post */ - public function createVersion($project_key, $version, array $options = array()) + public function createVersion($project_key, $version, array $params = array()) { - $options = array_merge( + $params = array_merge( array( 'name' => $version, - 'description' => '', 'project' => $project_key, - // 'userReleaseDate' => '', - // 'releaseDate' => '', - 'released' => false, 'archived' => false, ), - $options + $params ); - return $this->api(self::REQUEST_POST, '/rest/api/2/version', $options); + return $this->api(self::REQUEST_POST, '/rest/api/2/version', $params); } /** @@ -654,9 +671,9 @@ public function createVersion($project_key, $version, array $options = array()) * @param integer $version_id Version ID. * @param array $params Key->Value list to update the version with. * - * @return false + * @return Result * @since 2.0.0 - * @link https://docs.atlassian.com/jira/REST/latest/#api/2/version-updateVersion + * @link https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-project-versions/#api-rest-api-2-version-id-put */ public function updateVersion($version_id, array $params = array()) { @@ -670,7 +687,7 @@ public function updateVersion($version_id, array $params = array()) * @param string|null $release_date Date in Y-m-d format (defaults to today). * @param array $params Optionally extra parameters. * - * @return false + * @return Result * @since 2.0.0 */ public function releaseVersion($version_id, $release_date = null, array $params = array()) @@ -698,18 +715,19 @@ public function releaseVersion($version_id, $release_date = null, array $params * @param string $name Name. * * @return Result|false + * @link https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issue-attachments/#api-rest-api-2-issue-issueidorkey-attachments-post */ public function createAttachment($issue_key, $filename, $name = null) { - $options = array( + $params = array( 'file' => '@' . $filename, - 'name' => $name, + 'name' => $name, // The NULL value is handled in the "ClientInterface" implementing class. ); return $this->api( self::REQUEST_POST, sprintf('/rest/api/2/issue/%s/attachments', $issue_key), - $options, + $params, false, true ); @@ -861,7 +879,7 @@ protected function automapFields(array $issue) * @param string $issue_key Issue key. * @param array $watchers Watchers. * - * @return Result|false + * @return (Result|false)[] */ public function setWatchers($issue_key, array $watchers) { @@ -884,28 +902,26 @@ public function setWatchers($issue_key, array $watchers) */ public function closeIssue($issue_key) { - $result = array(); - // Get available transitions. - $tmp_transitions = $this->getTransitions($issue_key, array()); - $tmp_transitions_result = $tmp_transitions->getResult(); + $tmp_transitions_result = $this->getTransitions($issue_key)->getResult(); $transitions = $tmp_transitions_result['transitions']; // Look for "Close Issue" transition in issue transitions. - foreach ( $transitions as $v ) { - // Close issue if required id was found. - if ( $v['name'] == 'Close Issue' ) { - $result = $this->transition( - $issue_key, - array( - 'transition' => array('id' => $v['id']), - ) - ); - break; + foreach ( $transitions as $transition_data ) { + if ( $transition_data['name'] !== 'Close Issue' ) { + continue; } + + // Close issue if required id was found. + return $this->transition( + $issue_key, + array( + 'transition' => array('id' => $transition_data['id']), + ) + ); } - return $result; + return array(); } /** @@ -914,6 +930,7 @@ public function closeIssue($issue_key) * @param string $project_key Project key. * * @return array + * @link https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-project-components/#api-rest-api-2-project-projectidorkey-components-get * @since 2.0.0 */ public function getProjectComponents($project_key) @@ -927,6 +944,7 @@ public function getProjectComponents($project_key) * @param string $project_key Project key. * * @return array + * @link https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-projects/#api-rest-api-2-project-projectidorkey-statuses-get * @since 2.0.0 */ public function getProjectIssueTypes($project_key) @@ -938,20 +956,21 @@ public function getProjectIssueTypes($project_key) * Returns a list of all resolutions. * * @return array + * @link https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issue-resolutions/#api-rest-api-2-resolution-get * @since 2.0.0 */ public function getResolutions() { // Fetch resolutions when the method is called for the first time. if ( $this->resolutions === null ) { - $resolutions = array(); - $result = $this->api(self::REQUEST_GET, '/rest/api/2/resolution', array(), true); + $ret = array(); + $resolutions = $this->api(self::REQUEST_GET, '/rest/api/2/resolution', array(), true); - foreach ( $result as $resolution ) { - $resolutions[$resolution['id']] = $resolution; + foreach ( $resolutions as $resolution_data ) { + $ret[$resolution_data['id']] = $resolution_data; } - $this->resolutions = $resolutions; + $this->resolutions = $ret; } return $this->resolutions; diff --git a/tests/Jira/AbstractApiTestCase.php b/tests/Jira/AbstractApiTestCase.php new file mode 100644 index 0000000..e97bc59 --- /dev/null +++ b/tests/Jira/AbstractApiTestCase.php @@ -0,0 +1,96 @@ +credential = $this->prophesize(AuthenticationInterface::class)->reveal(); + $this->client = $this->prophesize(ClientInterface::class); + + $this->api = new Api(self::ENDPOINT, $this->credential, $this->client->reveal()); + $this->api->setOptions(0); // Disable automapping. + } + + /** + * Checks, that response is correct. + * + * @param string $expected_raw_response Expected raw response. + * @param Result|false $actual_response Actual response. + * + * @return void + */ + protected function assertApiResponse($expected_raw_response, $actual_response) + { + $expected = new Result(json_decode($expected_raw_response, true)); + + // You'll get "false", when unexpected API call was made. + if ( $actual_response !== false ) { + $this->assertEquals($expected, $actual_response); + } + } + + /** + * Expects a particular client call. + * + * @param string $method Request method. + * @param string $url URL. + * @param array|string $data Request data. + * @param string $return_value Return value. + * @param boolean $is_file This is a file upload request. + * @param boolean $debug Debug this request. + * + * @return MethodProphecy + */ + protected function expectClientCall( + $method, + $url, + $data = array(), + $return_value, + $is_file = false, + $debug = false + ) { + return $this->client + ->sendRequest($method, $url, $data, self::ENDPOINT, $this->credential, $is_file, $debug) + ->willReturn($return_value) + ->shouldBeCalled(); + } + +} diff --git a/tests/Jira/ApiTest.php b/tests/Jira/ApiTest.php index 8116238..64ad8e7 100644 --- a/tests/Jira/ApiTest.php +++ b/tests/Jira/ApiTest.php @@ -4,55 +4,17 @@ use chobie\Jira\Api; -use chobie\Jira\Api\Authentication\AuthenticationInterface; -use chobie\Jira\Api\Exception; use chobie\Jira\Api\Result; use chobie\Jira\IssueType; -use Prophecy\Prophecy\ObjectProphecy; -use chobie\Jira\Api\Client\ClientInterface; /** * Class ApiTest * * @package Tests\chobie\Jira */ -class ApiTest extends AbstractTestCase +class ApiTest extends AbstractApiTestCase { - const ENDPOINT = 'http://jira.company.com'; - - /** - * Api. - * - * @var Api - */ - protected $api; - - /** - * Credential. - * - * @var AuthenticationInterface - */ - protected $credential; - - /** - * Client. - * - * @var ObjectProphecy - */ - protected $client; - - /** - * @before - */ - protected function setUpTest() - { - $this->credential = $this->prophesize(AuthenticationInterface::class)->reveal(); - $this->client = $this->prophesize(ClientInterface::class); - - $this->api = new Api(self::ENDPOINT, $this->credential, $this->client->reveal()); - } - /** * @dataProvider setEndpointDataProvider */ @@ -79,105 +41,44 @@ public function testSearch() '/rest/api/2/search', array( 'jql' => 'test', - 'startAt' => 0, + 'startAt' => 5, 'maxResults' => 2, 'fields' => 'description', ), $response ); - $response_decoded = json_decode($response, true); - - // Field auto-expanding would trigger this call. - $this->expectClientCall( - Api::REQUEST_GET, - '/rest/api/2/field', - array(), - file_get_contents(__DIR__ . '/resources/api_field.json') - ); - - $this->assertEquals(new Result($response_decoded), $this->api->search('test', 0, 2, 'description')); - } - - public function testUpdateVersion() - { - $params = array( - 'overdue' => true, - 'description' => 'new description', - ); - - $this->expectClientCall( - Api::REQUEST_PUT, - '/rest/api/2/version/111000', - $params, - '' + $this->assertApiResponse( + $response, + $this->api->search('test', 5, 2, 'description') ); - - $this->assertFalse($this->api->updateVersion(111000, $params)); } - public function testReleaseVersionAutomaticReleaseDate() + public function testSetWatchers() { - $params = array( - 'released' => true, - 'releaseDate' => date('Y-m-d'), - ); + $errored_response = '{"errorMessages":[],"errors":{}}'; $this->expectClientCall( - Api::REQUEST_PUT, - '/rest/api/2/version/111000', - $params, - '' - ); - - $this->assertFalse($this->api->releaseVersion(111000)); - } - - public function testReleaseVersionParameterMerging() - { - $release_date = '2010-07-06'; - - $expected_params = array( - 'released' => true, - 'releaseDate' => $release_date, - 'test' => 'extra', + Api::REQUEST_POST, + '/rest/api/2/issue/JRE-123/watchers', + 'account-id-one', + '' // For successful operation an empty string is returned. ); - $this->expectClientCall( - Api::REQUEST_PUT, - '/rest/api/2/version/111000', - $expected_params, - '' + Api::REQUEST_POST, + '/rest/api/2/issue/JRE-123/watchers', + 'account-id-two', + $errored_response // For a failed operation an error list is returned. ); - $this->assertFalse($this->api->releaseVersion(111000, $release_date, array('test' => 'extra'))); - } - - public function testDownloadAttachmentSuccessful() - { - $expected = 'file content'; - - $this->expectClientCall( - Api::REQUEST_GET, - '/rest/api/2/attachment/content/12345', - array(), - $expected, - true + // Can't use "assertSame" due to objected, but "assertEquals" would consider "false" and "" the same. + $this->assertEquals( + array( + false, + new Result(json_decode($errored_response, true)), + ), + $this->api->setWatchers('JRE-123', array('account-id-one', 'account-id-two')) ); - - $actual = $this->api->downloadAttachment(self::ENDPOINT . '/rest/api/2/attachment/content/12345'); - - if ( $actual !== null ) { - $this->assertEquals($expected, $actual); - } - } - - public function testDownloadAttachmentWithException() - { - $this->expectException(Exception::class); - $this->expectExceptionMessage('The download url is coming from the different Jira instance.'); - - $this->api->downloadAttachment('https://other.jira-instance.com/rest/api/2/attachment/content/12345'); } /** @@ -283,33 +184,145 @@ public static function createRemoteLinkDataProvider() ); } - public function testFindVersionByName() + public function testFalseOnEmptyResponse() { - $project_key = 'POR'; - $version_id = '14206'; - $version_name = '3.36.0'; - - $versions = array( - array('id' => '14205', 'name' => '3.62.0'), - array('id' => $version_id, 'name' => $version_name), - array('id' => '14207', 'name' => '3.66.0'), + $this->expectClientCall( + Api::REQUEST_GET, + '/rest/api/2/something', + array(), + '' ); + $this->assertFalse($this->api->api(api::REQUEST_GET, '/rest/api/2/something')); + } + + public function testResponseIsJsonDecodedIntoArray() + { $this->expectClientCall( Api::REQUEST_GET, - '/rest/api/2/project/' . $project_key . '/versions', + '/rest/api/2/something', array(), - json_encode($versions) + '{"key":"value"}' ); $this->assertEquals( - array('id' => $version_id, 'name' => $version_name), - $this->api->findVersionByName($project_key, $version_name), - 'Version found' + array('key' => 'value'), + $this->api->api(api::REQUEST_GET, '/rest/api/2/something', array(), true) ); + } - $this->assertNull( - $this->api->findVersionByName($project_key, 'i_do_not_exist') + public function testResponseIsJsonDecodedIntoResultObject() + { + $this->expectClientCall( + Api::REQUEST_GET, + '/rest/api/2/something', + array(), + '{"key":"value"}' + ); + + $this->assertEquals( + new Result(array('key' => 'value')), + $this->api->api(api::REQUEST_GET, '/rest/api/2/something') + ); + } + + /** + * @dataProvider responseAutomappingDataProvider + */ + public function testResponseAutomapping($options, $jira_response, array $expected_response) + { + $this->expectClientCall( + Api::REQUEST_GET, + '/rest/api/2/something', + array(), + $jira_response + ); + + // Field auto-expanding would trigger this call. + if ( $options === Api::AUTOMAP_FIELDS ) { + $decoded_field_response = array( + array( + 'id' => 'title', + 'name' => 'Заголовок', + ), + array( + 'id' => 'description', + 'name' => 'Описание', + ), + ); + $this->expectClientCall( + Api::REQUEST_GET, + '/rest/api/2/field', + array(), + json_encode($decoded_field_response) + ); + } + + $this->api->setOptions($options); + + $this->assertEquals( + $expected_response, + $this->api->api(api::REQUEST_GET, '/rest/api/2/something', array(), true) + ); + } + + public static function responseAutomappingDataProvider() + { + $decoded_issues_response = array( + 'issues' => array( + array( + 'fields' => array( + 'title' => 'sample title 1', + 'description' => 'sample description 1', + 'issuetype' => array( + 'self' => 'https://test.atlassian.net/rest/api/2/issuetype/10034', + ), + ), + ), + array( + 'fields' => array( + 'title' => 'sample title 2', + 'description' => 'sample description 2', + 'issuetype' => array( + 'self' => 'https://test.atlassian.net/rest/api/2/issuetype/10035', + ), + ), + ), + ), + ); + + return array( + 'auto-map' => array( + Api::AUTOMAP_FIELDS, + json_encode($decoded_issues_response), + array( + 'issues' => array( + array( + 'fields' => array( + 'Заголовок' => 'sample title 1', + 'Описание' => 'sample description 1', + 'issuetype' => array( + 'self' => 'https://test.atlassian.net/rest/api/2/issuetype/10034', + ), + ), + ), + array( + 'fields' => array( + 'Заголовок' => 'sample title 2', + 'Описание' => 'sample description 2', + 'issuetype' => array( + 'self' => 'https://test.atlassian.net/rest/api/2/issuetype/10035', + ), + ), + ), + ), + ), + ), + 'don\'t auto-map' => array( + 0, + json_encode($decoded_issues_response), + $decoded_issues_response, + ), ); } @@ -322,21 +335,17 @@ public function testGetResolutions() '/rest/api/2/resolution', array(), $response - ); - - $actual = $this->api->getResolutions(); + )->shouldBeCalledOnce(); + // Perform the 1st call (uncached). $response_decoded = json_decode($response, true); - $expected = array( '1' => $response_decoded[0], '10000' => $response_decoded[1], ); - $this->assertEquals($expected, $actual); + $this->assertEquals($expected, $this->api->getResolutions()); - // Second time we call the method the results should be cached and not trigger an API Request. - $this->client->sendRequest(Api::REQUEST_GET, '/rest/api/2/resolution', array(), self::ENDPOINT, $this->credential) - ->shouldNotBeCalled(); + // Perform the 2nd call (cached). $this->assertEquals($expected, $this->api->getResolutions(), 'Calling twice did not yield the same results'); } @@ -349,21 +358,17 @@ public function testGetFields() '/rest/api/2/field', array(), $response - ); - - $actual = $this->api->getFields(); + )->shouldBeCalledOnce(); + // Perform the 1st call (uncached). $response_decoded = json_decode($response, true); - $expected = array( 'issuetype' => $response_decoded[0], 'timespent' => $response_decoded[1], ); - $this->assertEquals($expected, $actual); + $this->assertEquals($expected, $this->api->getFields()); - // Second time we call the method the results should be cached and not trigger an API Request. - $this->client->sendRequest(Api::REQUEST_GET, '/rest/api/2/field', array(), self::ENDPOINT, $this->credential) - ->shouldNotBeCalled(); + // Perform the 2nd call (cached). $this->assertEquals($expected, $this->api->getFields(), 'Calling twice did not yield the same results'); } @@ -376,21 +381,17 @@ public function testGetStatuses() '/rest/api/2/status', array(), $response - ); - - $actual = $this->api->getStatuses(); + )->shouldBeCalledOnce(); + // Perform the 1st call (uncached). $response_decoded = json_decode($response, true); - $expected = array( '1' => $response_decoded[0], '3' => $response_decoded[1], ); - $this->assertEquals($expected, $actual); + $this->assertEquals($expected, $this->api->getStatuses()); - // Second time we call the method the results should be cached and not trigger an API Request. - $this->client->sendRequest(Api::REQUEST_GET, '/rest/api/2/status', array(), self::ENDPOINT, $this->credential) - ->shouldNotBeCalled(); + // Perform the 2nd call (cached). $this->assertEquals($expected, $this->api->getStatuses(), 'Calling twice did not yield the same results'); } @@ -403,21 +404,17 @@ public function testGetPriorities() '/rest/api/2/priority', array(), $response - ); - - $actual = $this->api->getPriorities(); + )->shouldBeCalledOnce(); + // Perform the 1st call (uncached). $response_decoded = json_decode($response, true); - $expected = array( '1' => $response_decoded[0], '5' => $response_decoded[1], ); - $this->assertEquals($expected, $actual); + $this->assertEquals($expected, $this->api->getPriorities()); - // Second time we call the method the results should be cached and not trigger an API Request. - $this->client->sendRequest(Api::REQUEST_GET, '/rest/api/2/priority', array(), self::ENDPOINT, $this->credential) - ->shouldNotBeCalled(); + // Perform the 2nd call (cached). $this->assertEquals($expected, $this->api->getPriorities(), 'Calling twice did not yield the same results'); } @@ -443,110 +440,108 @@ public function testGetIssueTypes() $this->assertEquals($expected, $actual); } - /** - * @param string|integer $time_spent Time spent. - * @param array $expected_rest_params Expected rest params. - * - * @return void - * @dataProvider addWorkLogWithoutCustomParamsDataProvider - */ - public function testAddWorkLogWithoutCustomParams($time_spent, array $expected_rest_params) - { - $response = '{}'; - - $this->expectClientCall( - Api::REQUEST_POST, - '/rest/api/2/issue/JRA-15/worklog', - $expected_rest_params, - $response - ); - - $actual = $this->api->addWorklog('JRA-15', $time_spent); - - $this->assertEquals(json_decode($response, true), $actual, 'The response is json-decoded.'); - } - - public static function addWorkLogWithoutCustomParamsDataProvider() + public function testGetAttachmentsMetaInformation() { - return array( - 'integer time spent' => array(12, array('timeSpentSeconds' => 12)), - 'string time spent' => array('12m', array('timeSpent' => '12m')), - ); - } - - public function testAddWorklogWithCustomParams() - { - $response = '{}'; + $response = file_get_contents(__DIR__ . '/resources/api_get_attachments_meta.json'); - $started = date(Api::DATE_TIME_FORMAT, 1621026000); $this->expectClientCall( - Api::REQUEST_POST, - '/rest/api/2/issue/JRA-15/worklog', - array('timeSpent' => '12m', 'started' => $started), + Api::REQUEST_GET, + '/rest/api/2/attachment/meta', + array(), $response ); - $actual = $this->api->addWorklog('JRA-15', '12m', array('started' => $started)); - - $this->assertEquals(json_decode($response, true), $actual, 'The response is json-decoded.'); + $this->assertApiResponse($response, $this->api->getAttachmentsMetaInformation()); } - public function testDeleteWorkLogWithoutCustomParams() - { - $response = '{}'; + /** + * @dataProvider getCreateMetaDataProvider + */ + public function testGetCreateMeta( + array $project_ids = null, + array $project_keys = null, + array $issue_type_ids = null, + array $issue_type_names = null, + array $expand = null, + array $params = array() + ) { + $response = file_get_contents(__DIR__ . '/resources/api_get_create_meta.json'); $this->expectClientCall( - Api::REQUEST_DELETE, - '/rest/api/2/issue/JRA-15/worklog/11256', - array(), + Api::REQUEST_GET, + '/rest/api/2/issue/createmeta', + $params, $response ); - $actual = $this->api->deleteWorklog('JRA-15', 11256); + // Perform the API call. + $actual = $this->api->getCreateMeta($project_ids, $project_keys, $issue_type_ids, $issue_type_names, $expand); + $response_decoded = json_decode($response, true); - $this->assertEquals(json_decode($response, true), $actual, 'The response is json-decoded.'); + $this->assertEquals($response_decoded, $actual, 'The decoded response does not match the actual result.'); } - public function testDeleteWorkLogWithCustomParams() + public static function getCreateMetaDataProvider() { - $response = '{}'; + return array( + 'project_ids' => array( + array(123, 456), + null, + null, + null, + null, + array('projectIds' => '123,456'), + ), + 'project_names' => array( + null, + array('abc', 'def'), + null, + null, + null, + array('projectKeys' => 'abc,def'), + ), + 'project_ids+project_names' => array( + array(123, 456), + array('abc', 'def'), + null, + null, + null, + array('projectIds' => '123,456', 'projectKeys' => 'abc,def'), + ), - $this->expectClientCall( - Api::REQUEST_DELETE, - '/rest/api/2/issue/JRA-15/worklog/11256', - array('custom' => 'param'), - $response + 'issue_type_ids' => array( + null, + null, + array(123, 456), + null, + null, + array('issuetypeIds' => '123,456'), + ), + 'issue_type_names' => array( + null, + null, + null, + array('abc', 'def'), + null, + array('issuetypeNames' => 'abc,def'), + ), + 'issue_type_ids+issue_type_names' => array( + null, + null, + array(123, 456), + array('abc', 'def'), + null, + array('issuetypeIds' => '123,456', 'issuetypeNames' => 'abc,def'), + ), + 'expand' => array( + null, + null, + null, + null, + array('aa', 'bb'), + array('expand' => 'aa,bb'), + ), ); - - $actual = $this->api->deleteWorklog('JRA-15', 11256, array('custom' => 'param')); - - $this->assertEquals(json_decode($response, true), $actual, 'The response is json-decoded.'); - } - - /** - * Expects a particular client call. - * - * @param string $method Request method. - * @param string $url URL. - * @param array|string $data Request data. - * @param string $return_value Return value. - * @param boolean $is_file This is a file upload request. - * @param boolean $debug Debug this request. - * - * @return void - */ - protected function expectClientCall( - $method, - $url, - $data = array(), - $return_value, - $is_file = false, - $debug = false - ) { - $this->client - ->sendRequest($method, $url, $data, self::ENDPOINT, $this->credential, $is_file, $debug) - ->willReturn($return_value) - ->shouldBeCalled(); } } diff --git a/tests/Jira/IssueAttachmentsApiTest.php b/tests/Jira/IssueAttachmentsApiTest.php new file mode 100644 index 0000000..eb8d405 --- /dev/null +++ b/tests/Jira/IssueAttachmentsApiTest.php @@ -0,0 +1,98 @@ +expectClientCall( + Api::REQUEST_POST, + '/rest/api/2/issue/JRE-123/attachments', + array( + 'file' => '@' . __DIR__ . '/resources/api_field.json', + 'name' => null, + ), + $response, + true + ); + + $this->assertApiResponse( + $response, + $this->api->createAttachment('JRE-123', __DIR__ . '/resources/api_field.json') + ); + } + + public function testCreateAttachmentWithManualAttachmentName() + { + $response = file_get_contents(__DIR__ . '/resources/api_create_attachment.json'); + + $this->expectClientCall( + Api::REQUEST_POST, + '/rest/api/2/issue/JRE-123/attachments', + array( + 'file' => '@' . __DIR__ . '/resources/api_field.json', + 'name' => 'manual.txt', + ), + $response, + true + ); + + $this->assertApiResponse( + $response, + $this->api->createAttachment('JRE-123', __DIR__ . '/resources/api_field.json', 'manual.txt') + ); + } + + public function testDownloadAttachmentSuccessful() + { + $expected = 'file content'; + + $this->expectClientCall( + Api::REQUEST_GET, + '/rest/api/2/attachment/content/12345', + array(), + $expected, + true + ); + + $actual = $this->api->downloadAttachment(self::ENDPOINT . '/rest/api/2/attachment/content/12345'); + + if ( $actual !== null ) { + $this->assertEquals($expected, $actual); + } + } + + public function testDownloadAttachmentWithException() + { + $this->expectException(Exception::class); + $this->expectExceptionMessage('The download url is coming from the different Jira instance.'); + + $this->api->downloadAttachment('https://other.jira-instance.com/rest/api/2/attachment/content/12345'); + } + + public function testGetAttachment() + { + $response = file_get_contents(__DIR__ . '/resources/api_get_attachment.json'); + $this->expectClientCall( + Api::REQUEST_GET, + '/rest/api/2/attachment/18700', + array(), + $response + ); + + $this->assertEquals( + json_decode($response, true), + $this->api->getAttachment('18700') + ); + } + +} diff --git a/tests/Jira/IssueCommentsApiTest.php b/tests/Jira/IssueCommentsApiTest.php new file mode 100644 index 0000000..c5c0540 --- /dev/null +++ b/tests/Jira/IssueCommentsApiTest.php @@ -0,0 +1,59 @@ +expectClientCall( + Api::REQUEST_POST, + '/rest/api/2/issue/POR-1/comment', + $api_params, + $response + ); + + $this->assertApiResponse($response, $this->api->addComment('POR-1', $input_param)); + } + + public static function addCommentDataProvider() + { + return array( + 'data-structure' => array( + array( + 'body' => 'testdesc', + 'visibility' => array( + 'identifier' => 'Administrators', + 'type' => 'role', + 'value' => 'Administrators', + ), + ), + array( + 'body' => 'testdesc', + 'visibility' => array( + 'identifier' => 'Administrators', + 'type' => 'role', + 'value' => 'Administrators', + ), + ), + ), + 'comment-text-only' => array( + 'comment text', + array( + 'body' => 'comment text', + ), + ), + ); + } + +} diff --git a/tests/Jira/IssueWorklogsApiTest.php b/tests/Jira/IssueWorklogsApiTest.php new file mode 100644 index 0000000..98a567c --- /dev/null +++ b/tests/Jira/IssueWorklogsApiTest.php @@ -0,0 +1,108 @@ +expectClientCall( + Api::REQUEST_GET, + '/rest/api/2/issue/POR-1/worklog', + array(), + $response + ); + + $this->assertApiResponse( + $response, + $this->api->getWorklogs('POR-1') + ); + } + + /** + * @param string|integer $time_spent Time spent. + * @param array $expected_rest_params Expected rest params. + * + * @return void + * @dataProvider addWorkLogWithoutCustomParamsDataProvider + */ + public function testAddWorkLogWithoutCustomParams($time_spent, array $expected_rest_params) + { + $response = '{}'; + + $this->expectClientCall( + Api::REQUEST_POST, + '/rest/api/2/issue/JRA-15/worklog', + $expected_rest_params, + $response + ); + + $actual = $this->api->addWorklog('JRA-15', $time_spent); + + $this->assertEquals(json_decode($response, true), $actual, 'The response is json-decoded.'); + } + + public static function addWorkLogWithoutCustomParamsDataProvider() + { + return array( + 'integer time spent' => array(12, array('timeSpentSeconds' => 12)), + 'string time spent' => array('12m', array('timeSpent' => '12m')), + ); + } + + public function testAddWorklogWithCustomParams() + { + $response = '{}'; + + $started = date(Api::DATE_TIME_FORMAT, 1621026000); + $this->expectClientCall( + Api::REQUEST_POST, + '/rest/api/2/issue/JRA-15/worklog', + array('timeSpent' => '12m', 'started' => $started), + $response + ); + + $actual = $this->api->addWorklog('JRA-15', '12m', array('started' => $started)); + + $this->assertEquals(json_decode($response, true), $actual, 'The response is json-decoded.'); + } + + public function testDeleteWorkLogWithoutCustomParams() + { + $response = '{}'; + + $this->expectClientCall( + Api::REQUEST_DELETE, + '/rest/api/2/issue/JRA-15/worklog/11256', + array(), + $response + ); + + $actual = $this->api->deleteWorklog('JRA-15', 11256); + + $this->assertEquals(json_decode($response, true), $actual, 'The response is json-decoded.'); + } + + public function testDeleteWorkLogWithCustomParams() + { + $response = '{}'; + + $this->expectClientCall( + Api::REQUEST_DELETE, + '/rest/api/2/issue/JRA-15/worklog/11256', + array('custom' => 'param'), + $response + ); + + $actual = $this->api->deleteWorklog('JRA-15', 11256, array('custom' => 'param')); + + $this->assertEquals(json_decode($response, true), $actual, 'The response is json-decoded.'); + } + +} diff --git a/tests/Jira/IssuesApiTest.php b/tests/Jira/IssuesApiTest.php new file mode 100644 index 0000000..4a44c10 --- /dev/null +++ b/tests/Jira/IssuesApiTest.php @@ -0,0 +1,235 @@ +expectClientCall( + Api::REQUEST_GET, + '/rest/api/2/issue/' . $issue_key, + array(), + $response + ); + + $this->assertApiResponse($response, $this->api->getIssue($issue_key)); + } + + public function testGetIssueWithExpand() + { + $response = file_get_contents(__DIR__ . '/resources/api_get_issue.json'); + + $issue_key = 'POR-1'; + + $this->expectClientCall( + Api::REQUEST_GET, + '/rest/api/2/issue/' . $issue_key, + array('expand' => 'changelog'), + $response + ); + + $this->assertApiResponse($response, $this->api->getIssue($issue_key, 'changelog')); + } + + public function testEditIssue() + { + $issue_key = 'POR-1'; + $params = array( + 'update' => array( + 'summary' => array( + array('set' => 'Bug in business logic'), + ), + ), + ); + $this->expectClientCall( + Api::REQUEST_PUT, + '/rest/api/2/issue/' . $issue_key, + $params, + false // False is returned because there is no content (204). + ); + + $this->assertFalse($this->api->editIssue($issue_key, $params)); + } + + public function testCreateIssueWithoutOtherFields() + { + $params = array( + 'fields' => array( + 'project' => array( + 'key' => 'POR-1', + ), + 'summary' => 'New issue summary', + 'issuetype' => array( + 'id' => '10034', + ), + ), + ); + + $response = file_get_contents(__DIR__ . '/resources/api_create_issue.json'); + + $this->expectClientCall( + Api::REQUEST_POST, + '/rest/api/2/issue', + $params, + $response + ); + + $this->assertApiResponse( + $response, + $this->api->createIssue('POR-1', 'New issue summary', '10034') + ); + } + + public function testCreateIssueWithOtherFields() + { + $params = array( + 'fields' => array( + 'project' => array( + 'key' => 'POR-1', + ), + 'summary' => 'New issue summary', + 'issuetype' => array( + 'name' => 'Bug', // Replaced. + ), + 'description' => 'New issue description', // Added. + ), + ); + + $response = file_get_contents(__DIR__ . '/resources/api_create_issue.json'); + + $this->expectClientCall( + Api::REQUEST_POST, + '/rest/api/2/issue', + $params, + $response + ); + + $this->assertApiResponse( + $response, + $this->api->createIssue( + 'POR-1', + 'New issue summary', + '10034', + array('description' => 'New issue description', 'issuetype' => array('name' => 'Bug')) + ) + ); + } + + public function testGetTransitionsWithoutExtraParams() + { + $issue_key = 'POR-1'; + + $response = file_get_contents(__DIR__ . '/resources/api_get_transitions.json'); + + $this->expectClientCall( + Api::REQUEST_GET, + '/rest/api/2/issue/' . $issue_key . '/transitions', + array(), + $response + ); + + $this->assertApiResponse( + $response, + $this->api->getTransitions('POR-1') + ); + } + + public function testGetTransitionsWithExtraParams() + { + $issue_key = 'POR-1'; + + $response = file_get_contents(__DIR__ . '/resources/api_get_transitions.json'); + + $this->expectClientCall( + Api::REQUEST_GET, + '/rest/api/2/issue/' . $issue_key . '/transitions', + array('sortByOpsBarAndStatus' => true), + $response + ); + + $this->assertApiResponse( + $response, + $this->api->getTransitions('POR-1', array('sortByOpsBarAndStatus' => true)) + ); + } + + public function testTransition() + { + $params = array( + 'transition' => array( + 'id' => 123, + ), + ); + + $this->expectClientCall( + Api::REQUEST_POST, + '/rest/api/2/issue/POR-1/transitions', + $params, + false // False is returned because there is no content (204). + ); + + $this->assertFalse( + $this->api->transition('POR-1', $params) + ); + } + + /** + * @depends testGetTransitionsWithoutExtraParams + * @depends testTransition + */ + public function testCloseIssue() + { + $issue_key = 'POR-1'; + + $this->expectClientCall( + Api::REQUEST_GET, + '/rest/api/2/issue/' . $issue_key . '/transitions', + array(), + file_get_contents(__DIR__ . '/resources/api_get_transitions.json') + ); + + $this->expectClientCall( + Api::REQUEST_POST, + '/rest/api/2/issue/POR-1/transitions', + array( + 'transition' => array('id' => 171), // The "171" is transition ID for "Close Issue" transition. + ), + false // False is returned because there is no content (204). + ); + + $this->api->closeIssue($issue_key); + } + + /** + * @depends testGetTransitionsWithoutExtraParams + * @depends testTransition + */ + public function testCloseIssueFailure() + { + $issue_key = 'POR-1'; + + $this->expectClientCall( + Api::REQUEST_GET, + '/rest/api/2/issue/' . $issue_key . '/transitions', + array(), + json_encode(array( + 'transitions' => array( + array('name' => 'Schedule Issue'), + ), + )) + ); + + $this->assertSame(array(), $this->api->closeIssue($issue_key)); + } + +} diff --git a/tests/Jira/ProjectRolesApiTest.php b/tests/Jira/ProjectRolesApiTest.php new file mode 100644 index 0000000..80cb5b0 --- /dev/null +++ b/tests/Jira/ProjectRolesApiTest.php @@ -0,0 +1,49 @@ +expectClientCall( + Api::REQUEST_GET, + '/rest/api/2/project/' . $project_id . '/role', + array(), + $response + ); + + $actual = $this->api->getRoles($project_id); + + $expected = json_decode($response, true); + $this->assertEquals($expected, $actual); + } + + public function testGetProjectRoleDetails() + { + $response = file_get_contents(__DIR__ . '/resources/api_get_project_role.json'); + $project_id = '10500'; + $role_id = '10200'; + + $this->expectClientCall( + Api::REQUEST_GET, + '/rest/api/2/project/' . $project_id . '/role/' . $role_id, + array(), + $response + ); + + $actual = $this->api->getRoleDetails($project_id, $role_id); + + $expected = json_decode($response, true); + $this->assertEquals($expected, $actual); + } + +} diff --git a/tests/Jira/ProjectVersionsApiTest.php b/tests/Jira/ProjectVersionsApiTest.php new file mode 100644 index 0000000..e05f5b4 --- /dev/null +++ b/tests/Jira/ProjectVersionsApiTest.php @@ -0,0 +1,184 @@ +expectClientCall( + Api::REQUEST_GET, + '/rest/api/2/project/' . $project_key . '/versions', + array(), + $response + ); + + $this->assertEquals( + json_decode($response, true), + $this->api->getVersions($project_key) + ); + } + + public function testCreateVersionWithoutCustomParams() + { + $response = file_get_contents(__DIR__ . '/resources/api_create_version.json'); + + $this->expectClientCall( + Api::REQUEST_POST, + '/rest/api/2/version', + array( + 'name' => '1.2.3', + 'project' => 'TST', + 'archived' => false, + ), + $response + ); + + $this->assertApiResponse( + $response, + $this->api->createVersion('TST', '1.2.3') + ); + } + + public function testCreateVersionWithCustomParams() + { + $response = file_get_contents(__DIR__ . '/resources/api_create_version.json'); + + $this->expectClientCall( + Api::REQUEST_POST, + '/rest/api/2/version', + array( + 'name' => '1.2.3', + 'project' => 'TST', + 'archived' => true, + 'description' => 'test', + ), + $response + ); + + $this->assertApiResponse( + $response, + $this->api->createVersion('TST', '1.2.3', array('archived' => true, 'description' => 'test')) + ); + } + + public function testUpdateVersion() + { + $response = file_get_contents(__DIR__ . '/resources/api_create_version.json'); + + $params = array( + 'overdue' => true, + 'description' => 'new description', + ); + + $this->expectClientCall( + Api::REQUEST_PUT, + '/rest/api/2/version/111000', + $params, + $response + ); + + $this->assertApiResponse( + $response, + $this->api->updateVersion(111000, $params) + ); + } + + public function testReleaseVersionAutomaticReleaseDate() + { + $response = file_get_contents(__DIR__ . '/resources/api_create_version.json'); + + $params = array( + 'released' => true, + 'releaseDate' => date('Y-m-d'), + ); + + $this->expectClientCall( + Api::REQUEST_PUT, + '/rest/api/2/version/111000', + $params, + $response + ); + + $this->assertApiResponse( + $response, + $this->api->releaseVersion(111000) + ); + } + + public function testReleaseVersionParameterMerging() + { + $response = file_get_contents(__DIR__ . '/resources/api_create_version.json'); + + $release_date = '2010-07-06'; + + $expected_params = array( + 'released' => true, + 'releaseDate' => $release_date, + 'test' => 'extra', + ); + + $this->expectClientCall( + Api::REQUEST_PUT, + '/rest/api/2/version/111000', + $expected_params, + $response + ); + + $this->assertApiResponse( + $response, + $this->api->releaseVersion(111000, $release_date, array('test' => 'extra')) + ); + } + + public function testFindVersionByName() + { + $versions = array( + array('id' => '14205', 'name' => '3.62.0'), + array('id' => '14206', 'name' => '3.36.0'), + array('id' => '14207', 'name' => '3.66.0'), + ); + + $this->expectClientCall( + Api::REQUEST_GET, + '/rest/api/2/project/POR/versions', + array(), + json_encode($versions) + ); + + $this->assertEquals( + array('id' => '14206', 'name' => '3.36.0'), + $this->api->findVersionByName('POR', '3.36.0'), + 'Version found' + ); + + $this->assertNull( + $this->api->findVersionByName('POR', 'i_do_not_exist'), + 'Version not found' + ); + } + + public function testFindVersionByNameError() + { + $this->expectClientCall( + Api::REQUEST_GET, + '/rest/api/2/project/POR/versions', + array(), + '{"errorMessages":["No project could be found with key \'POR\'."],"errors":{}}' + ); + + $this->assertNull( + $this->api->findVersionByName('POR', 'any-version'), + 'Project not found' + ); + } + +} diff --git a/tests/Jira/ProjectsApiTest.php b/tests/Jira/ProjectsApiTest.php new file mode 100644 index 0000000..c53399b --- /dev/null +++ b/tests/Jira/ProjectsApiTest.php @@ -0,0 +1,74 @@ +expectClientCall( + Api::REQUEST_GET, + '/rest/api/2/project', + array(), + $response + ); + + $this->assertApiResponse($response, $this->api->getProjects()); + } + + public function testGetProject() + { + $response = file_get_contents(__DIR__ . '/resources/api_get_project.json'); + $this->expectClientCall( + Api::REQUEST_GET, + '/rest/api/2/project/TST', + array(), + $response + ); + + $this->assertEquals( + json_decode($response, true), + $this->api->getProject('TST') + ); + } + + public function testGetProjectComponents() + { + $response = file_get_contents(__DIR__ . '/resources/api_get_project_components.json'); + $this->expectClientCall( + Api::REQUEST_GET, + '/rest/api/2/project/TST/components', + array(), + $response + ); + + $this->assertEquals( + json_decode($response, true), + $this->api->getProjectComponents('TST') + ); + } + + public function testGetProjectIssueTypes() + { + $response = file_get_contents(__DIR__ . '/resources/api_get_project_issue_types.json'); + $this->expectClientCall( + Api::REQUEST_GET, + '/rest/api/2/project/TST/statuses', + array(), + $response + ); + + $this->assertEquals( + json_decode($response, true), + $this->api->getProjectIssueTypes('TST') + ); + } + +} diff --git a/tests/Jira/resources/api_add_comment.json b/tests/Jira/resources/api_add_comment.json new file mode 100644 index 0000000..538540e --- /dev/null +++ b/tests/Jira/resources/api_add_comment.json @@ -0,0 +1,23 @@ +{ + "self": "http://www.example.com/jira/rest/api/2/issue/10010/comment/10000", + "id": "10000", + "author": { + "self": "http://www.example.com/jira/rest/api/2/user?username=fred", + "name": "fred", + "displayName": "Fred F. User", + "active": false + }, + "body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque eget venenatis elit. Duis eu justo eget augue iaculis fermentum. Sed semper quam laoreet nisi egestas at posuere augue semper.", + "updateAuthor": { + "self": "http://www.example.com/jira/rest/api/2/user?username=fred", + "name": "fred", + "displayName": "Fred F. User", + "active": false + }, + "created": "2016-08-17T12:37:37.987+0000", + "updated": "2016-08-17T12:37:37.987+0000", + "visibility": { + "type": "role", + "value": "Administrators" + } +} diff --git a/tests/Jira/resources/api_create_attachment.json b/tests/Jira/resources/api_create_attachment.json new file mode 100644 index 0000000..7bfabd9 --- /dev/null +++ b/tests/Jira/resources/api_create_attachment.json @@ -0,0 +1,25 @@ +[ + { + "self": "https://test.atlassian.net/rest/api/2/attachment/12211", + "id": "12211", + "filename": "api_get_attachments_meta.json", + "author": { + "accountId": "5b10a2844c20165700ede21g", + "active": true, + "avatarUrls": { + "16x16": "https://avatar-management--avatars.server-location.prod.public.atl-paas.net/initials/MK-5.png?size=16&s=16", + "24x24": "https://avatar-management--avatars.server-location.prod.public.atl-paas.net/initials/MK-5.png?size=24&s=24", + "32x32": "https://avatar-management--avatars.server-location.prod.public.atl-paas.net/initials/MK-5.png?size=32&s=32", + "48x48": "https://avatar-management--avatars.server-location.prod.public.atl-paas.net/initials/MK-5.png?size=48&s=48" + }, + "displayName": "Mia Krystof", + "emailAddress": "mia@example.com", + "self": "https://your-domain.atlassian.net/rest/api/2/user?accountId=5b10a2844c20165700ede21g", + "timeZone": "Australia/Sydney" + }, + "created": "2025-01-05T21:34:14.433+0200", + "size": 49, + "mimeType": "application/json", + "content": "https://test.atlassian.net/rest/api/2/attachment/content/12211" + } +] diff --git a/tests/Jira/resources/api_create_issue.json b/tests/Jira/resources/api_create_issue.json new file mode 100644 index 0000000..0ea43a6 --- /dev/null +++ b/tests/Jira/resources/api_create_issue.json @@ -0,0 +1,5 @@ +{ + "id": "13004", + "key": "PRJ-1", + "self": "https://test.atlassian.net/rest/api/2/issue/13004" +} diff --git a/tests/Jira/resources/api_create_version.json b/tests/Jira/resources/api_create_version.json new file mode 100644 index 0000000..a4bc8c2 --- /dev/null +++ b/tests/Jira/resources/api_create_version.json @@ -0,0 +1,8 @@ +{ + "self": "https://test.atlassian.net/rest/api/2/version/10528", + "id": "10528", + "name": "9.8.7", + "archived": false, + "released": false, + "projectId": 10047 +} diff --git a/tests/Jira/resources/api_get_attachment.json b/tests/Jira/resources/api_get_attachment.json new file mode 100644 index 0000000..1fa9302 --- /dev/null +++ b/tests/Jira/resources/api_get_attachment.json @@ -0,0 +1,23 @@ +{ + "self": "https://test.atlassian.net/rest/api/2/attachment/18700", + "filename": "2016-04-04 10.02.45.jpg", + "author": { + "self": "https://test.atlassian.net/rest/api/2/user?username=joost.pastoor", + "key": "joost.pastoor", + "name": "joost.pastoor", + "avatarUrls": { + "16x16": "https://test.atlassian.net/secure/useravatar?size=xsmall&ownerId=joost.pastoor&avatarId=10502", + "24x24": "https://test.atlassian.net/secure/useravatar?size=small&ownerId=joost.pastoor&avatarId=10502", + "32x32": "https://test.atlassian.net/secure/useravatar?size=medium&ownerId=joost.pastoor&avatarId=10502", + "48x48": "https://test.atlassian.net/secure/useravatar?ownerId=joost.pastoor&avatarId=10502" + }, + "displayName": "Ernst van Rijn", + "active": true + }, + "created": "2016-04-04T10:04:07.537+0200", + "size": 4097662, + "mimeType": "image/jpeg", + "properties": {}, + "content": "https://test.atlassian.net/secure/attachment/18700/2016-04-04+10.02.45.jpg", + "thumbnail": "https://test.atlassian.net/secure/thumbnail/18700/_thumb_18700.png" +} diff --git a/tests/Jira/resources/api_get_attachments_meta.json b/tests/Jira/resources/api_get_attachments_meta.json new file mode 100644 index 0000000..ae3f08c --- /dev/null +++ b/tests/Jira/resources/api_get_attachments_meta.json @@ -0,0 +1,4 @@ +{ + "enabled": true, + "uploadLimit": 10485760 +} diff --git a/tests/Jira/resources/api_get_create_meta.json b/tests/Jira/resources/api_get_create_meta.json new file mode 100644 index 0000000..f4b8096 --- /dev/null +++ b/tests/Jira/resources/api_get_create_meta.json @@ -0,0 +1,63 @@ +{ + "expand": "projects", + "projects": [ + { + "self": "https://test.atlassian.net/rest/api/2/project/10034", + "id": "10034", + "key": "PRJ1", + "name": "Advanced", + "avatarUrls": { + "48x48": "https://test.atlassian.net/rest/api/2/universal_avatar/view/type/project/avatar/10400", + "24x24": "https://test.atlassian.net/rest/api/2/universal_avatar/view/type/project/avatar/10400?size=small", + "16x16": "https://test.atlassian.net/rest/api/2/universal_avatar/view/type/project/avatar/10400?size=xsmall", + "32x32": "https://test.atlassian.net/rest/api/2/universal_avatar/view/type/project/avatar/10400?size=medium" + }, + "issuetypes": [ + { + "self": "https://test.atlassian.net/rest/api/2/issuetype/10034", + "id": "10034", + "description": "A problem which impairs or prevents the functions of the product.", + "iconUrl": "https://test.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10303?size=medium", + "name": "Bug Report", + "untranslatedName": "Bug Report", + "subtask": false, + "hierarchyLevel": 0 + }, + { + "self": "https://test.atlassian.net/rest/api/2/issuetype/10035", + "id": "10035", + "description": "", + "iconUrl": "https://test.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10310?size=medium", + "name": "Refactoring", + "untranslatedName": "Refactoring", + "subtask": false, + "hierarchyLevel": 0 + } + ] + }, + { + "self": "https://test.atlassian.net/rest/api/2/project/10045", + "id": "10045", + "key": "PRJ2", + "name": "Bootstrap", + "avatarUrls": { + "48x48": "https://test.atlassian.net/rest/api/2/universal_avatar/view/type/project/avatar/10418", + "24x24": "https://test.atlassian.net/rest/api/2/universal_avatar/view/type/project/avatar/10418?size=small", + "16x16": "https://test.atlassian.net/rest/api/2/universal_avatar/view/type/project/avatar/10418?size=xsmall", + "32x32": "https://test.atlassian.net/rest/api/2/universal_avatar/view/type/project/avatar/10418?size=medium" + }, + "issuetypes": [ + { + "self": "https://test.atlassian.net/rest/api/2/issuetype/10034", + "id": "10034", + "description": "A problem which impairs or prevents the functions of the product.", + "iconUrl": "https://test.atlassian.net/rest/api/2/universal_avatar/view/type/issuetype/avatar/10303?size=medium", + "name": "Bug Report", + "untranslatedName": "Bug Report", + "subtask": false, + "hierarchyLevel": 0 + } + ] + } + ] +} diff --git a/tests/Jira/resources/api_get_issue.json b/tests/Jira/resources/api_get_issue.json new file mode 100644 index 0000000..45b14b7 --- /dev/null +++ b/tests/Jira/resources/api_get_issue.json @@ -0,0 +1,322 @@ +{ + "expand": "renderedFields,names,schema,operations,editmeta,changelog,versionedRepresentations", + "id": "10100", + "self": "https://test.atlassian.net/rest/api/2/issue/10100", + "key": "POR-1", + "fields": { + "issuetype": { + "self": "https://test.atlassian.net/rest/api/2/issuetype/4", + "id": "4", + "description": "An improvement or enhancement to an existing feature or task.", + "iconUrl": "https://test.atlassian.net/secure/viewavatar?size=xsmall&avatarId=11110&avatarType=issuetype", + "name": "Improvement", + "subtask": false, + "avatarId": 11110 + }, + "timespent": 316800, + "project": { + "self": "https://test.atlassian.net/rest/api/2/project/10100", + "id": "10100", + "key": "POR", + "name": "Portal", + "avatarUrls": { + "48x48": "https://test.atlassian.net/secure/projectavatar?pid=10100&avatarId=10401", + "24x24": "https://test.atlassian.net/secure/projectavatar?size=small&pid=10100&avatarId=10401", + "16x16": "https://test.atlassian.net/secure/projectavatar?size=xsmall&pid=10100&avatarId=10401", + "32x32": "https://test.atlassian.net/secure/projectavatar?size=medium&pid=10100&avatarId=10401" + } + }, + "fixVersions": [], + "aggregatetimespent": 316800, + "resolution": { + "self": "https://test.atlassian.net/rest/api/2/resolution/1", + "id": "1", + "description": "A fix for this issue is checked into the tree and tested.", + "name": "Fixed" + }, + "customfield_10500": null, + "customfield_10501": [], + "customfield_10502": null, + "resolutiondate": "2013-05-13T15:53:48.236+0200", + "workratio": 1100, + "lastViewed": null, + "watches": { + "self": "https://test.atlassian.net/rest/api/2/issue/POR-1/watchers", + "watchCount": 1, + "isWatching": true + }, + "created": "2013-05-13T08:57:21.520+0200", + "customfield_10020": null, + "customfield_10021": "Not started", + "priority": { + "self": "https://test.atlassian.net/rest/api/2/priority/3", + "iconUrl": "https://test.atlassian.net/images/icons/priorities/major.svg", + "name": "Major", + "id": "3" + }, + "customfield_10024": null, + "customfield_10300": null, + "labels": [ + "esee-wp5.4" + ], + "customfield_10016": null, + "customfield_10017": null, + "customfield_10018": null, + "customfield_10019": null, + "timeestimate": 0, + "aggregatetimeoriginalestimate": 28800, + "versions": [], + "issuelinks": [ + { + "id": "10000", + "self": "https://test.atlassian.net/rest/api/2/issueLink/10000", + "type": { + "id": "10003", + "name": "Relates", + "inward": "relates to", + "outward": "relates to", + "self": "https://test.atlassian.net/rest/api/2/issueLinkType/10003" + }, + "outwardIssue": { + "id": "10101", + "key": "POR-2", + "self": "https://test.atlassian.net/rest/api/2/issue/10101", + "fields": { + "summary": "Speedup Precalced Props with Gearman", + "status": { + "self": "https://test.atlassian.net/rest/api/2/status/6", + "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", + "iconUrl": "https://test.atlassian.net/images/icons/statuses/closed.png", + "name": "Closed", + "id": "6", + "statusCategory": { + "self": "https://test.atlassian.net/rest/api/2/statuscategory/3", + "id": 3, + "key": "done", + "colorName": "green", + "name": "Done" + } + }, + "priority": { + "self": "https://test.atlassian.net/rest/api/2/priority/3", + "iconUrl": "https://test.atlassian.net/images/icons/priorities/major.svg", + "name": "Major", + "id": "3" + }, + "issuetype": { + "self": "https://test.atlassian.net/rest/api/2/issuetype/4", + "id": "4", + "description": "An improvement or enhancement to an existing feature or task.", + "iconUrl": "https://test.atlassian.net/secure/viewavatar?size=xsmall&avatarId=11110&avatarType=issuetype", + "name": "Improvement", + "subtask": false, + "avatarId": 11110 + } + } + } + } + ], + "assignee": { + "self": "https://test.atlassian.net/rest/api/2/user?username=joost", + "name": "joost", + "key": "joost", + "emailAddress": "joost.pastoor@test.com", + "avatarUrls": { + "48x48": "https://test.atlassian.net/secure/useravatar?ownerId=joost&avatarId=10500", + "24x24": "https://test.atlassian.net/secure/useravatar?size=small&ownerId=joost&avatarId=10500", + "16x16": "https://test.atlassian.net/secure/useravatar?size=xsmall&ownerId=joost&avatarId=10500", + "32x32": "https://test.atlassian.net/secure/useravatar?size=medium&ownerId=joost&avatarId=10500" + }, + "displayName": "Joost Pastoor", + "active": true, + "timeZone": "Europe/Berlin" + }, + "updated": "2016-08-03T16:57:31.232+0200", + "status": { + "self": "https://test.atlassian.net/rest/api/2/status/5", + "description": "A resolution has been taken, and it is awaiting verification by reporter. From here issues are either reopened, or are closed.", + "iconUrl": "https://test.atlassian.net/images/icons/statuses/resolved.png", + "name": "Resolved", + "id": "5", + "statusCategory": { + "self": "https://test.atlassian.net/rest/api/2/statuscategory/3", + "id": 3, + "key": "done", + "colorName": "green", + "name": "Done" + } + }, + "components": [], + "timeoriginalestimate": 28800, + "description": "- Both hourly reports and passings", + "customfield_10012": null, + "customfield_10013": null, + "customfield_10014": null, + "timetracking": { + "originalEstimate": "1d", + "remainingEstimate": "0m", + "timeSpent": "2w 1d", + "originalEstimateSeconds": 28800, + "remainingEstimateSeconds": 0, + "timeSpentSeconds": 316800 + }, + "customfield_10015": null, + "customfield_10401": null, + "customfield_10006": "22", + "customfield_10600": "com.atlassian.servicedesk.plugins.approvals.internal.customfield.ApprovalsCFValue@1956b56", + "customfield_10007": null, + "customfield_10008": null, + "attachment": [], + "aggregatetimeestimate": 0, + "summary": "Convert TrainMeasurement passings to Precalculated Property", + "creator": { + "self": "https://test.atlassian.net/rest/api/2/user?username=joost", + "name": "joost", + "key": "joost", + "emailAddress": "joost.pastoor@test.com", + "avatarUrls": { + "48x48": "https://test.atlassian.net/secure/useravatar?ownerId=joost&avatarId=10500", + "24x24": "https://test.atlassian.net/secure/useravatar?size=small&ownerId=joost&avatarId=10500", + "16x16": "https://test.atlassian.net/secure/useravatar?size=xsmall&ownerId=joost&avatarId=10500", + "32x32": "https://test.atlassian.net/secure/useravatar?size=medium&ownerId=joost&avatarId=10500" + }, + "displayName": "Joost Pastoor", + "active": true, + "timeZone": "Europe/Berlin" + }, + "subtasks": [], + "reporter": { + "self": "https://test.atlassian.net/rest/api/2/user?username=joost", + "name": "joost", + "key": "joost", + "emailAddress": "joost.pastoor@test.com", + "avatarUrls": { + "48x48": "https://test.atlassian.net/secure/useravatar?ownerId=joost&avatarId=10500", + "24x24": "https://test.atlassian.net/secure/useravatar?size=small&ownerId=joost&avatarId=10500", + "16x16": "https://test.atlassian.net/secure/useravatar?size=xsmall&ownerId=joost&avatarId=10500", + "32x32": "https://test.atlassian.net/secure/useravatar?size=medium&ownerId=joost&avatarId=10500" + }, + "displayName": "Joost Pastoor", + "active": true, + "timeZone": "Europe/Berlin" + }, + "customfield_10000": "3_*:*_1_*:*_0_*|*_1_*:*_1_*:*_307391", + "aggregateprogress": { + "progress": 316800, + "total": 316800, + "percent": 100 + }, + "customfield_10001": null, + "customfield_10200": "0|10004g:", + "customfield_10002": null, + "customfield_10003": null, + "customfield_10400": null, + "environment": null, + "duedate": null, + "progress": { + "progress": 316800, + "total": 316800, + "percent": 100 + }, + "comment": { + "comments": [], + "maxResults": 0, + "total": 0, + "startAt": 0 + }, + "votes": { + "self": "https://test.atlassian.net/rest/api/2/issue/POR-1/votes", + "votes": 0, + "hasVoted": false + }, + "worklog": { + "startAt": 0, + "maxResults": 20, + "total": 2, + "worklogs": [ + { + "self": "https://test.atlassian.net/rest/api/2/issue/10100/worklog/10001", + "author": { + "self": "https://test.atlassian.net/rest/api/2/user?username=joost", + "name": "joost", + "key": "joost", + "emailAddress": "joost.pastoor@test.com", + "avatarUrls": { + "48x48": "https://test.atlassian.net/secure/useravatar?ownerId=joost&avatarId=10500", + "24x24": "https://test.atlassian.net/secure/useravatar?size=small&ownerId=joost&avatarId=10500", + "16x16": "https://test.atlassian.net/secure/useravatar?size=xsmall&ownerId=joost&avatarId=10500", + "32x32": "https://test.atlassian.net/secure/useravatar?size=medium&ownerId=joost&avatarId=10500" + }, + "displayName": "Joost Pastoor", + "active": true, + "timeZone": "Europe/Berlin" + }, + "updateAuthor": { + "self": "https://test.atlassian.net/rest/api/2/user?username=joost", + "name": "joost", + "key": "joost", + "emailAddress": "joost.pastoor@test.com", + "avatarUrls": { + "48x48": "https://test.atlassian.net/secure/useravatar?ownerId=joost&avatarId=10500", + "24x24": "https://test.atlassian.net/secure/useravatar?size=small&ownerId=joost&avatarId=10500", + "16x16": "https://test.atlassian.net/secure/useravatar?size=xsmall&ownerId=joost&avatarId=10500", + "32x32": "https://test.atlassian.net/secure/useravatar?size=medium&ownerId=joost&avatarId=10500" + }, + "displayName": "Joost Pastoor", + "active": true, + "timeZone": "Europe/Berlin" + }, + "comment": "", + "created": "2013-05-13T15:53:48.236+0200", + "updated": "2013-05-13T15:53:48.236+0200", + "started": "2013-05-13T15:53:00.000+0200", + "timeSpent": "1d", + "timeSpentSeconds": 28800, + "id": "10001", + "issueId": "10100" + }, + { + "self": "https://test.atlassian.net/rest/api/2/issue/10100/worklog/31214", + "author": { + "self": "https://test.atlassian.net/rest/api/2/user?username=joost", + "name": "joost", + "key": "joost", + "emailAddress": "joost.pastoor@test.com", + "avatarUrls": { + "48x48": "https://test.atlassian.net/secure/useravatar?ownerId=joost&avatarId=10500", + "24x24": "https://test.atlassian.net/secure/useravatar?size=small&ownerId=joost&avatarId=10500", + "16x16": "https://test.atlassian.net/secure/useravatar?size=xsmall&ownerId=joost&avatarId=10500", + "32x32": "https://test.atlassian.net/secure/useravatar?size=medium&ownerId=joost&avatarId=10500" + }, + "displayName": "Joost Pastoor", + "active": true, + "timeZone": "Europe/Berlin" + }, + "updateAuthor": { + "self": "https://test.atlassian.net/rest/api/2/user?username=joost", + "name": "joost", + "key": "joost", + "emailAddress": "joost.pastoor@test.com", + "avatarUrls": { + "48x48": "https://test.atlassian.net/secure/useravatar?ownerId=joost&avatarId=10500", + "24x24": "https://test.atlassian.net/secure/useravatar?size=small&ownerId=joost&avatarId=10500", + "16x16": "https://test.atlassian.net/secure/useravatar?size=xsmall&ownerId=joost&avatarId=10500", + "32x32": "https://test.atlassian.net/secure/useravatar?size=medium&ownerId=joost&avatarId=10500" + }, + "displayName": "Joost Pastoor", + "active": true, + "timeZone": "Europe/Berlin" + }, + "comment": "", + "created": "2016-08-03T16:57:15.840+0200", + "updated": "2016-08-03T16:57:15.840+0200", + "started": "2013-05-01T16:56:00.000+0200", + "timeSpent": "2w", + "timeSpentSeconds": 288000, + "id": "31214", + "issueId": "10100" + } + ] + } + } +} diff --git a/tests/Jira/resources/api_get_project.json b/tests/Jira/resources/api_get_project.json new file mode 100644 index 0000000..d0b4a8e --- /dev/null +++ b/tests/Jira/resources/api_get_project.json @@ -0,0 +1,52 @@ +{ + "expand": "description,lead,url,projectKeys", + "self": "https://test.atlassian.net/rest/api/2/project/10500", + "id": "10500", + "key": "ONEDS", + "description": "", + "lead": { + "self": "https://test.atlassian.net/rest/api/2/user?username=joost.pastoor", + "key": "joost.pastoor", + "name": "joost.pastoor", + "avatarUrls": { + "16x16": "https://test.atlassian.net/secure/useravatar?size=xsmall&ownerId=joost.pastoor&avatarId=10607", + "24x24": "https://test.atlassian.net/secure/useravatar?size=small&ownerId=joost.pastoor&avatarId=10607", + "32x32": "https://test.atlassian.net/secure/useravatar?size=medium&ownerId=joost.pastoor&avatarId=10607", + "48x48": "https://test.atlassian.net/secure/useravatar?ownerId=joost.pastoor&avatarId=10607" + }, + "displayName": "Joost Pastoor", + "active": true + }, + "components": [], + "issueTypes": [ + { + "self": "https://test.atlassian.net/rest/api/2/issuetype/1", + "id": "1", + "description": "A problem which impairs or prevents the functions of the product.", + "iconUrl": "https://test.atlassian.net/secure/viewavatar?size=xsmall&avatarId=11103&avatarType=issuetype", + "name": "Bug", + "subtask": false, + "avatarId": 11103 + } + ], + "assigneeType": "UNASSIGNED", + "versions": [], + "name": "1D Sound", + "roles": { + "atlassian-addons-project-access": "https://test.atlassian.net/rest/api/2/project/10500/role/10200", + "Service Desk Team": "https://test.atlassian.net/rest/api/2/project/10500/role/10301", + "Developers": "https://test.atlassian.net/rest/api/2/project/10500/role/10001", + "Service Desk Customers": "https://test.atlassian.net/rest/api/2/project/10500/role/10300", + "Service Desk Collaborators": "https://test.atlassian.net/rest/api/2/project/10500/role/10302", + "Administrators": "https://test.atlassian.net/rest/api/2/project/10500/role/10002", + "Users": "https://test.atlassian.net/rest/api/2/project/10500/role/10000", + "Tempo Project Managers": "https://test.atlassian.net/rest/api/2/project/10500/role/10100" + }, + "avatarUrls": { + "48x48": "https://test.atlassian.net/secure/projectavatar?pid=10500&avatarId=10011", + "24x24": "https://test.atlassian.net/secure/projectavatar?size=small&pid=10500&avatarId=10011", + "16x16": "https://test.atlassian.net/secure/projectavatar?size=xsmall&pid=10500&avatarId=10011", + "32x32": "https://test.atlassian.net/secure/projectavatar?size=medium&pid=10500&avatarId=10011" + }, + "projectTypeKey": "software" +} diff --git a/tests/Jira/resources/api_get_project_components.json b/tests/Jira/resources/api_get_project_components.json new file mode 100644 index 0000000..34cd65c --- /dev/null +++ b/tests/Jira/resources/api_get_project_components.json @@ -0,0 +1,32 @@ +[ + { + "self": "https://test.atlassian.net/rest/api/2/component/10068", + "id": "10068", + "name": "Admin Interfaces", + "assigneeType": "PROJECT_DEFAULT", + "realAssigneeType": "PROJECT_DEFAULT", + "isAssigneeTypeValid": false, + "project": "TST", + "projectId": 10047 + }, + { + "self": "https://test.atlassian.net/rest/api/2/component/10069", + "id": "10069", + "name": "Build", + "assigneeType": "PROJECT_DEFAULT", + "realAssigneeType": "PROJECT_DEFAULT", + "isAssigneeTypeValid": false, + "project": "TST", + "projectId": 10047 + }, + { + "self": "https://test.atlassian.net/rest/api/2/component/10070", + "id": "10070", + "name": "Caching", + "assigneeType": "PROJECT_DEFAULT", + "realAssigneeType": "PROJECT_DEFAULT", + "isAssigneeTypeValid": false, + "project": "TST", + "projectId": 10047 + } +] diff --git a/tests/Jira/resources/api_get_project_issue_types.json b/tests/Jira/resources/api_get_project_issue_types.json new file mode 100644 index 0000000..5154ecc --- /dev/null +++ b/tests/Jira/resources/api_get_project_issue_types.json @@ -0,0 +1,78 @@ +[ + { + "self": "https://test.atlassian.net/rest/api/2/issuetype/10034", + "id": "10034", + "name": "Bug Report", + "subtask": false, + "statuses": [ + { + "self": "https://test.atlassian.net/rest/api/2/status/10036", + "description": "Associated \"Differential Revision\" is reviewed and ready to be committed.", + "iconUrl": "https://test.atlassian.net/images/icons/statuses/generic.png", + "name": "Accepted", + "untranslatedName": "Accepted", + "id": "10036", + "statusCategory": { + "self": "https://test.atlassian.net/rest/api/2/statuscategory/2", + "id": 2, + "key": "new", + "colorName": "blue-gray", + "name": "To Do" + } + }, + { + "self": "https://test.atlassian.net/rest/api/2/status/10037", + "description": "Work on the issue can be started.", + "iconUrl": "https://test.atlassian.net/images/icons/statuses/generic.png", + "name": "Scheduled", + "untranslatedName": "Scheduled", + "id": "10037", + "statusCategory": { + "self": "https://test.atlassian.net/rest/api/2/statuscategory/2", + "id": 2, + "key": "new", + "colorName": "blue-gray", + "name": "To Do" + } + } + ] + }, + { + "self": "https://test.atlassian.net/rest/api/2/issuetype/10035", + "id": "10035", + "name": "Refactoring", + "subtask": false, + "statuses": [ + { + "self": "https://test.atlassian.net/rest/api/2/status/10036", + "description": "Associated \"Differential Revision\" is reviewed and ready to be committed.", + "iconUrl": "https://test.atlassian.net/images/icons/statuses/generic.png", + "name": "Accepted", + "untranslatedName": "Accepted", + "id": "10036", + "statusCategory": { + "self": "https://test.atlassian.net/rest/api/2/statuscategory/2", + "id": 2, + "key": "new", + "colorName": "blue-gray", + "name": "To Do" + } + }, + { + "self": "https://test.atlassian.net/rest/api/2/status/10037", + "description": "Work on the issue can be started.", + "iconUrl": "https://test.atlassian.net/images/icons/statuses/generic.png", + "name": "Scheduled", + "untranslatedName": "Scheduled", + "id": "10037", + "statusCategory": { + "self": "https://test.atlassian.net/rest/api/2/statuscategory/2", + "id": 2, + "key": "new", + "colorName": "blue-gray", + "name": "To Do" + } + } + ] + } +] diff --git a/tests/Jira/resources/api_get_project_role.json b/tests/Jira/resources/api_get_project_role.json new file mode 100644 index 0000000..c361c8d --- /dev/null +++ b/tests/Jira/resources/api_get_project_role.json @@ -0,0 +1,29 @@ +{ + "self": "https://test.atlassian.net/rest/api/2/project/10500/role/10200", + "name": "atlassian-addons-project-access", + "id": 10200, + "description": "A project role that represents Connect add-ons declaring a scope that requires more than read issue permissions", + "actors": [ + { + "id": 15301, + "displayName": "Epic Sum Up - Free", + "type": "atlassian-user-role-actor", + "name": "addon_aptis.plugins.epicSumUpFree", + "avatarUrl": "https://test.atlassian.net/secure/useravatar?size=xsmall&avatarId=10122" + }, + { + "id": 11701, + "displayName": "Gantt Cloud", + "type": "atlassian-user-role-actor", + "name": "addon_eu.wisoft.gantt-ondemand", + "avatarUrl": "https://test.atlassian.net/secure/useravatar?size=xsmall&avatarId=10122" + }, + { + "id": 14601, + "displayName": "Zendesk for JIRA", + "type": "atlassian-user-role-actor", + "name": "addon_zendesk_for_jira", + "avatarUrl": "https://test.atlassian.net/secure/useravatar?size=xsmall&avatarId=10122" + } + ] +} diff --git a/tests/Jira/resources/api_get_project_roles.json b/tests/Jira/resources/api_get_project_roles.json new file mode 100644 index 0000000..7c0d9e4 --- /dev/null +++ b/tests/Jira/resources/api_get_project_roles.json @@ -0,0 +1,6 @@ +{ + "Service Desk Team": "https://test.atlassian.net/rest/api/2/project/10500/role/10301", + "Developers": "https://test.atlassian.net/rest/api/2/project/10500/role/10001", + "Administrators": "https://test.atlassian.net/rest/api/2/project/10500/role/10002", + "Users": "https://test.atlassian.net/rest/api/2/project/10500/role/10000" +} diff --git a/tests/Jira/resources/api_get_projects.json b/tests/Jira/resources/api_get_projects.json new file mode 100644 index 0000000..694d819 --- /dev/null +++ b/tests/Jira/resources/api_get_projects.json @@ -0,0 +1,30 @@ +[ + { + "expand": "description,lead,url,projectKeys", + "self": "https://test.atlassian.net/rest/api/2/project/10500", + "id": "10500", + "key": "ONEDS", + "name": "1D Sound", + "avatarUrls": { + "48x48": "https://test.atlassian.net/secure/projectavatar?pid=10500&avatarId=10011", + "24x24": "https://test.atlassian.net/secure/projectavatar?size=small&pid=10500&avatarId=10011", + "16x16": "https://test.atlassian.net/secure/projectavatar?size=xsmall&pid=10500&avatarId=10011", + "32x32": "https://test.atlassian.net/secure/projectavatar?size=medium&pid=10500&avatarId=10011" + }, + "projectTypeKey": "software" + }, + { + "expand": "description,lead,url,projectKeys", + "self": "https://test.atlassian.net/rest/api/2/project/10201", + "id": "10201", + "key": "SNDDIR", + "name": "3D Sound", + "avatarUrls": { + "48x48": "https://test.atlassian.net/secure/projectavatar?pid=10201&avatarId=10605", + "24x24": "https://test.atlassian.net/secure/projectavatar?size=small&pid=10201&avatarId=10605", + "16x16": "https://test.atlassian.net/secure/projectavatar?size=xsmall&pid=10201&avatarId=10605", + "32x32": "https://test.atlassian.net/secure/projectavatar?size=medium&pid=10201&avatarId=10605" + }, + "projectTypeKey": "software" + } +] diff --git a/tests/Jira/resources/api_get_transitions.json b/tests/Jira/resources/api_get_transitions.json new file mode 100644 index 0000000..88baeca --- /dev/null +++ b/tests/Jira/resources/api_get_transitions.json @@ -0,0 +1,53 @@ +{ + "expand": "transitions", + "transitions": [ + { + "id": "251", + "name": "Schedule Issue", + "to": { + "self": "https://test.atlassian.net/rest/api/2/status/10037", + "description": "Work on the issue can be started.", + "iconUrl": "https://test.atlassian.net/images/icons/statuses/generic.png", + "name": "Scheduled", + "id": "10037", + "statusCategory": { + "self": "https://test.atlassian.net/rest/api/2/statuscategory/2", + "id": 2, + "key": "new", + "colorName": "blue-gray", + "name": "To Do" + } + }, + "hasScreen": true, + "isGlobal": false, + "isInitial": false, + "isAvailable": true, + "isConditional": true, + "isLooped": false + }, + { + "id": "171", + "name": "Close Issue", + "to": { + "self": "https://test.atlassian.net/rest/api/2/status/6", + "description": "The issue is considered finished, the resolution is correct. Issues which are closed can be reopened.", + "iconUrl": "https://test.atlassian.net/images/icons/statuses/closed.png", + "name": "Closed", + "id": "6", + "statusCategory": { + "self": "https://test.atlassian.net/rest/api/2/statuscategory/3", + "id": 3, + "key": "done", + "colorName": "green", + "name": "Done" + } + }, + "hasScreen": true, + "isGlobal": false, + "isInitial": false, + "isAvailable": true, + "isConditional": true, + "isLooped": false + } + ] +} diff --git a/tests/Jira/resources/api_get_versions.json b/tests/Jira/resources/api_get_versions.json new file mode 100644 index 0000000..33ca401 --- /dev/null +++ b/tests/Jira/resources/api_get_versions.json @@ -0,0 +1,34 @@ +[ + { + "self": "https://test.atlassian.net/rest/api/2/version/10404", + "id": "10404", + "name": "4.3.9", + "archived": false, + "released": true, + "releaseDate": "2008-10-20", + "userReleaseDate": "20/Oct/08", + "projectId": 10047 + }, + { + "self": "https://test.atlassian.net/rest/api/2/version/10381", + "id": "10381", + "description": "First Open Source release", + "name": "5.0.0", + "archived": false, + "released": true, + "releaseDate": "2009-07-25", + "userReleaseDate": "25/Jul/09", + "projectId": 10047 + }, + { + "self": "https://test.atlassian.net/rest/api/2/version/10382", + "id": "10382", + "description": "Bug fixes and some visual features.", + "name": "5.0.1", + "archived": false, + "released": true, + "releaseDate": "2009-09-26", + "userReleaseDate": "26/Sep/09", + "projectId": 10047 + } +] diff --git a/tests/Jira/resources/api_get_worklog_of_issue.json b/tests/Jira/resources/api_get_worklog_of_issue.json new file mode 100644 index 0000000..88c6fa5 --- /dev/null +++ b/tests/Jira/resources/api_get_worklog_of_issue.json @@ -0,0 +1,89 @@ +{ + "startAt": 0, + "maxResults": 2, + "total": 2, + "worklogs": [ + { + "self": "https://test.atlassian.net/rest/api/2/issue/10100/worklog/10001", + "author": { + "self": "https://test.atlassian.net/rest/api/2/user?username=joost", + "name": "joost", + "key": "joost", + "emailAddress": "joost.pastoor@test.com", + "avatarUrls": { + "48x48": "https://test.atlassian.net/secure/useravatar?ownerId=joost&avatarId=10500", + "24x24": "https://test.atlassian.net/secure/useravatar?size=small&ownerId=joost&avatarId=10500", + "16x16": "https://test.atlassian.net/secure/useravatar?size=xsmall&ownerId=joost&avatarId=10500", + "32x32": "https://test.atlassian.net/secure/useravatar?size=medium&ownerId=joost&avatarId=10500" + }, + "displayName": "Joost Pastoor", + "active": true, + "timeZone": "Europe/Berlin" + }, + "updateAuthor": { + "self": "https://test.atlassian.net/rest/api/2/user?username=joost", + "name": "joost", + "key": "joost", + "emailAddress": "joost.pastoor@test.com", + "avatarUrls": { + "48x48": "https://test.atlassian.net/secure/useravatar?ownerId=joost&avatarId=10500", + "24x24": "https://test.atlassian.net/secure/useravatar?size=small&ownerId=joost&avatarId=10500", + "16x16": "https://test.atlassian.net/secure/useravatar?size=xsmall&ownerId=joost&avatarId=10500", + "32x32": "https://test.atlassian.net/secure/useravatar?size=medium&ownerId=joost&avatarId=10500" + }, + "displayName": "Joost Pastoor", + "active": true, + "timeZone": "Europe/Berlin" + }, + "comment": "", + "created": "2013-05-13T15:53:48.236+0200", + "updated": "2013-05-13T15:53:48.236+0200", + "started": "2013-05-13T15:53:00.000+0200", + "timeSpent": "1d", + "timeSpentSeconds": 28800, + "id": "10001", + "issueId": "10100" + }, + { + "self": "https://test.atlassian.net/rest/api/2/issue/10100/worklog/31214", + "author": { + "self": "https://test.atlassian.net/rest/api/2/user?username=joost", + "name": "joost", + "key": "joost", + "emailAddress": "joost.pastoor@test.com", + "avatarUrls": { + "48x48": "https://test.atlassian.net/secure/useravatar?ownerId=joost&avatarId=10500", + "24x24": "https://test.atlassian.net/secure/useravatar?size=small&ownerId=joost&avatarId=10500", + "16x16": "https://test.atlassian.net/secure/useravatar?size=xsmall&ownerId=joost&avatarId=10500", + "32x32": "https://test.atlassian.net/secure/useravatar?size=medium&ownerId=joost&avatarId=10500" + }, + "displayName": "Joost Pastoor", + "active": true, + "timeZone": "Europe/Berlin" + }, + "updateAuthor": { + "self": "https://test.atlassian.net/rest/api/2/user?username=joost", + "name": "joost", + "key": "joost", + "emailAddress": "joost.pastoor@test.com", + "avatarUrls": { + "48x48": "https://test.atlassian.net/secure/useravatar?ownerId=joost&avatarId=10500", + "24x24": "https://test.atlassian.net/secure/useravatar?size=small&ownerId=joost&avatarId=10500", + "16x16": "https://test.atlassian.net/secure/useravatar?size=xsmall&ownerId=joost&avatarId=10500", + "32x32": "https://test.atlassian.net/secure/useravatar?size=medium&ownerId=joost&avatarId=10500" + }, + "displayName": "Joost Pastoor", + "active": true, + "timeZone": "Europe/Berlin" + }, + "comment": "", + "created": "2016-08-03T16:57:15.840+0200", + "updated": "2016-08-03T16:57:15.840+0200", + "started": "2013-05-01T16:56:00.000+0200", + "timeSpent": "2w", + "timeSpentSeconds": 288000, + "id": "31214", + "issueId": "10100" + } + ] +}