Skip to content

Commit c06b115

Browse files
authored
Merge pull request #362 from kbsali/192-closing-archiving-project
Add closing and archiving project API
2 parents fddc8f0 + 0652edb commit c06b115

File tree

9 files changed

+628
-1
lines changed

9 files changed

+628
-1
lines changed

CHANGELOG.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### Added
1111

12-
- Added support for updating groups.
12+
- Added support for updating groups with method `Redmine\Api\Group::update()`.
13+
- New method `Redmine\Api\Project::close()` to close a project.
14+
- New method `Redmine\Api\Project::reopen()` to reopen a project.
15+
- New method `Redmine\Api\Project::archive()` to archive a project.
16+
- New method `Redmine\Api\Project::unarchive()` to unarchive a project.
1317

1418
### Changed
1519

docs/usage.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,10 @@ $client->getApi('project')->create([
253253
$client->getApi('project')->update($projectId, [
254254
'name' => 'different name',
255255
]);
256+
$client->getApi('project')->close($projectId);
257+
$client->getApi('project')->reopen($projectId);
258+
$client->getApi('project')->archive($projectId);
259+
$client->getApi('project')->unarchive($projectId);
256260
$client->getApi('project')->remove($projectId);
257261

258262
// ----------------------------

src/Redmine/Api/Project.php

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace Redmine\Api;
44

5+
use InvalidArgumentException;
56
use Redmine\Exception;
67
use Redmine\Exception\MissingParameterException;
78
use Redmine\Exception\SerializerException;
@@ -194,6 +195,146 @@ public function update($id, array $params)
194195
);
195196
}
196197

198+
/**
199+
* Close a project.
200+
*
201+
* @see https://www.redmine.org/issues/35507
202+
*
203+
* @param string|int $projectIdentifier project id or identifier
204+
*
205+
* @throws InvalidArgumentException if $projectIdentifier is not provided as int or string
206+
* @throws UnexpectedResponseException if the Redmine server delivers an unexpected response
207+
*
208+
* @return true if the request was successful
209+
*/
210+
final public function close($projectIdentifier): bool
211+
{
212+
if (! is_int($projectIdentifier) && ! is_string($projectIdentifier)) {
213+
throw new InvalidArgumentException(sprintf(
214+
'%s(): Argument #1 ($projectIdentifier) must be of type int or string',
215+
__METHOD__
216+
));
217+
}
218+
219+
$this->put(
220+
'/projects/' . strval($projectIdentifier) . '/close.xml',
221+
''
222+
);
223+
224+
$lastResponse = $this->getLastResponse();
225+
226+
if ($lastResponse->getStatusCode() !== 204) {
227+
throw new UnexpectedResponseException('The Redmine server replied with the status code ' . $lastResponse->getStatusCode());
228+
}
229+
230+
return true;
231+
}
232+
233+
/**
234+
* Reopen a project.
235+
*
236+
* @see https://www.redmine.org/issues/35507
237+
*
238+
* @param string|int $projectIdentifier project id or identifier
239+
*
240+
* @throws InvalidArgumentException if $projectIdentifier is not provided as int or string
241+
* @throws UnexpectedResponseException if the Redmine server delivers an unexpected response
242+
*
243+
* @return true if the request was successful
244+
*/
245+
final public function reopen($projectIdentifier): bool
246+
{
247+
if (! is_int($projectIdentifier) && ! is_string($projectIdentifier)) {
248+
throw new InvalidArgumentException(sprintf(
249+
'%s(): Argument #1 ($projectIdentifier) must be of type int or string',
250+
__METHOD__
251+
));
252+
}
253+
254+
$this->put(
255+
'/projects/' . strval($projectIdentifier) . '/reopen.xml',
256+
''
257+
);
258+
259+
$lastResponse = $this->getLastResponse();
260+
261+
if ($lastResponse->getStatusCode() !== 204) {
262+
throw new UnexpectedResponseException('The Redmine server replied with the status code ' . $lastResponse->getStatusCode());
263+
}
264+
265+
return true;
266+
}
267+
268+
/**
269+
* Archive a project.
270+
*
271+
* @see https://www.redmine.org/issues/35420
272+
*
273+
* @param string|int $projectIdentifier project id or identifier
274+
*
275+
* @throws InvalidArgumentException if $projectIdentifier is not provided as int or string
276+
* @throws UnexpectedResponseException if the Redmine server delivers an unexpected response
277+
*
278+
* @return true if the request was successful
279+
*/
280+
final public function archive($projectIdentifier): bool
281+
{
282+
if (! is_int($projectIdentifier) && ! is_string($projectIdentifier)) {
283+
throw new InvalidArgumentException(sprintf(
284+
'%s(): Argument #1 ($projectIdentifier) must be of type int or string',
285+
__METHOD__
286+
));
287+
}
288+
289+
$this->put(
290+
'/projects/' . strval($projectIdentifier) . '/archive.xml',
291+
''
292+
);
293+
294+
$lastResponse = $this->getLastResponse();
295+
296+
if ($lastResponse->getStatusCode() !== 204) {
297+
throw new UnexpectedResponseException('The Redmine server replied with the status code ' . $lastResponse->getStatusCode());
298+
}
299+
300+
return true;
301+
}
302+
303+
/**
304+
* Unarchive a project.
305+
*
306+
* @see https://www.redmine.org/issues/35420
307+
*
308+
* @param string|int $projectIdentifier project id or identifier
309+
*
310+
* @throws InvalidArgumentException if $projectIdentifier is not provided as int or string
311+
* @throws UnexpectedResponseException if the Redmine server delivers an unexpected response
312+
*
313+
* @return true if the request was successful
314+
*/
315+
final public function unarchive($projectIdentifier): bool
316+
{
317+
if (! is_int($projectIdentifier) && ! is_string($projectIdentifier)) {
318+
throw new InvalidArgumentException(sprintf(
319+
'%s(): Argument #1 ($projectIdentifier) must be of type int or string',
320+
__METHOD__
321+
));
322+
}
323+
324+
$this->put(
325+
'/projects/' . strval($projectIdentifier) . '/unarchive.xml',
326+
''
327+
);
328+
329+
$lastResponse = $this->getLastResponse();
330+
331+
if ($lastResponse->getStatusCode() !== 204) {
332+
throw new UnexpectedResponseException('The Redmine server replied with the status code ' . $lastResponse->getStatusCode());
333+
}
334+
335+
return true;
336+
}
337+
197338
/**
198339
* @deprecated since v2.3.0, use `\Redmine\Serializer\XmlSerializer::createFromArray()` instead.
199340
*
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Redmine\Tests\End2End\Project;
6+
7+
use Redmine\Api\Project;
8+
use Redmine\Tests\End2End\ClientTestCase;
9+
use Redmine\Tests\RedmineExtension\RedmineVersion;
10+
11+
class ArchivingProjectTest extends ClientTestCase
12+
{
13+
/**
14+
* @dataProvider provideRedmineVersions
15+
*/
16+
public function testInteractionWithProject(RedmineVersion $redmineVersion): void
17+
{
18+
$client = $this->getNativeCurlClient($redmineVersion);
19+
20+
/** @var Project */
21+
$api = $client->getApi('project');
22+
23+
// Create project
24+
$projectName = 'test project';
25+
$projectIdentifier = 'test_project';
26+
27+
$xmlData = $api->create([
28+
'name' => $projectName,
29+
'identifier' => $projectIdentifier,
30+
]);
31+
32+
$projectDataJson = json_encode($xmlData);
33+
$projectData = json_decode($projectDataJson, true);
34+
35+
$this->assertIsArray($projectData, $projectDataJson);
36+
$this->assertArrayHasKey('identifier', $projectData, $projectDataJson);
37+
$this->assertSame($projectIdentifier, $projectData['identifier'], $projectDataJson);
38+
$this->assertArrayHasKey('status', $projectData, $projectDataJson);
39+
$this->assertSame('1', $projectData['status'], $projectDataJson);
40+
41+
// Archive project
42+
$this->assertTrue($api->archive($projectIdentifier));
43+
44+
// Reading an archived project is not possible
45+
$this->assertFalse($api->show($projectIdentifier));
46+
47+
// Unarchive project
48+
$this->assertTrue($api->unarchive($projectIdentifier));
49+
50+
// Read single project
51+
$projectDetails = $api->show($projectIdentifier);
52+
53+
$this->assertArrayHasKey('project', $projectDetails);
54+
$this->assertSame(
55+
[
56+
'id',
57+
'name',
58+
'identifier',
59+
'description',
60+
'homepage',
61+
'status',
62+
'is_public',
63+
'inherit_members',
64+
'trackers',
65+
'issue_categories',
66+
'created_on',
67+
'updated_on',
68+
],
69+
array_keys($projectDetails['project'])
70+
);
71+
$this->assertSame(1, $projectDetails['project']['status']);
72+
}
73+
}
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Redmine\Tests\End2End\Project;
6+
7+
use Redmine\Api\Project;
8+
use Redmine\Tests\End2End\ClientTestCase;
9+
use Redmine\Tests\RedmineExtension\RedmineVersion;
10+
11+
class ClosingProjectTest extends ClientTestCase
12+
{
13+
/**
14+
* @dataProvider provideRedmineVersions
15+
*/
16+
public function testInteractionWithProject(RedmineVersion $redmineVersion): void
17+
{
18+
$client = $this->getNativeCurlClient($redmineVersion);
19+
20+
/** @var Project */
21+
$api = $client->getApi('project');
22+
23+
// Create project
24+
$projectName = 'test project';
25+
$projectIdentifier = 'test_project';
26+
27+
$xmlData = $api->create([
28+
'name' => $projectName,
29+
'identifier' => $projectIdentifier,
30+
]);
31+
32+
$projectDataJson = json_encode($xmlData);
33+
$projectData = json_decode($projectDataJson, true);
34+
35+
$this->assertIsArray($projectData, $projectDataJson);
36+
$this->assertArrayHasKey('identifier', $projectData, $projectDataJson);
37+
$this->assertSame($projectIdentifier, $projectData['identifier'], $projectDataJson);
38+
$this->assertArrayHasKey('status', $projectData, $projectDataJson);
39+
$this->assertSame('1', $projectData['status'], $projectDataJson);
40+
41+
// Close project
42+
$this->assertTrue($api->close($projectIdentifier));
43+
44+
// Read single project
45+
$projectDetails = $api->show($projectIdentifier);
46+
47+
$this->assertArrayHasKey('project', $projectDetails);
48+
$this->assertSame(
49+
[
50+
'id',
51+
'name',
52+
'identifier',
53+
'description',
54+
'homepage',
55+
'status',
56+
'is_public',
57+
'inherit_members',
58+
'trackers',
59+
'issue_categories',
60+
'created_on',
61+
'updated_on',
62+
],
63+
array_keys($projectDetails['project'])
64+
);
65+
$this->assertSame(5, $projectDetails['project']['status']);
66+
67+
// Reopen project
68+
$this->assertTrue($api->reopen($projectIdentifier));
69+
70+
// Read single project
71+
$projectDetails = $api->show($projectIdentifier);
72+
73+
$this->assertArrayHasKey('project', $projectDetails);
74+
$this->assertSame(
75+
[
76+
'id',
77+
'name',
78+
'identifier',
79+
'description',
80+
'homepage',
81+
'status',
82+
'is_public',
83+
'inherit_members',
84+
'trackers',
85+
'issue_categories',
86+
'created_on',
87+
'updated_on',
88+
],
89+
array_keys($projectDetails['project'])
90+
);
91+
$this->assertSame(1, $projectDetails['project']['status']);
92+
}
93+
}

0 commit comments

Comments
 (0)