Skip to content

Commit 3940781

Browse files
authored
Merge pull request #395 from kbsali/320-add-support-for-updating-attachments-over-rest-api
Add support for updating attachments
2 parents 2b4c451 + 8d265a4 commit 3940781

File tree

5 files changed

+150
-2
lines changed

5 files changed

+150
-2
lines changed

CHANGELOG.md

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

1010
### Added
1111

12+
- New method `Redmine\Api\Attachment::update()` for updating attachments.
1213
- New interface `Redmine\Http\HttpClient` for new minimalistic HTTP clients.
1314
- New interface `Redmine\Http\Request` for sending data with new minimalistic HTTP clients.
1415
- New method `Redmine\Api\...::getLastResponse()` to get the last response made by the API class.
15-
- Add support for custom arrays in `Redmine\Serializer\XmlSerializer`
16+
- Add support for custom arrays in `Redmine\Serializer\XmlSerializer`.
1617

1718
### Changed
1819

src/Redmine/Api/Attachment.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Redmine\Api;
44

55
use Redmine\Exception\SerializerException;
6+
use Redmine\Exception\UnexpectedResponseException;
67
use Redmine\Http\HttpFactory;
78
use Redmine\Serializer\JsonSerializer;
89
use Redmine\Serializer\PathSerializer;
@@ -46,6 +47,39 @@ public function show($id)
4647
}
4748
}
4849

50+
/**
51+
* Update information about an attachment.
52+
*
53+
* @see https://www.redmine.org/projects/redmine/wiki/Rest_Attachments#PATCH
54+
*
55+
* @param int $id the attachment id
56+
* @param array $params available $params:
57+
* - filename: filename of the attachment
58+
* - description: new description of the attachment
59+
*
60+
* @throws SerializerException if $params contains invalid values
61+
* @throws UnexpectedResponseException if the Redmine server delivers an unexpected response
62+
*
63+
* @return true if the request was successful
64+
*/
65+
final public function update(int $id, array $params): bool
66+
{
67+
// we are using `PUT` instead of documented `PATCH`
68+
// @see https://github.com/kbsali/php-redmine-api/pull/395#issuecomment-2004089154
69+
// @see https://www.redmine.org/projects/redmine/wiki/Rest_Attachments#PATCH
70+
$this->lastResponse = $this->getHttpClient()->request(HttpFactory::makeJsonRequest(
71+
'PUT',
72+
'/attachments/' . $id . '.json',
73+
JsonSerializer::createFromArray(['attachment' => $params])->getEncoded()
74+
));
75+
76+
if ($this->lastResponse->getStatusCode() !== 204) {
77+
throw UnexpectedResponseException::create($this->lastResponse);
78+
}
79+
80+
return true;
81+
}
82+
4983
/**
5084
* Get attachment content as a binary file.
5185
*

tests/Behat/Bootstrap/AttachmentContextTrait.php

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
namespace Redmine\Tests\Behat\Bootstrap;
66

7-
use Behat\Behat\Tester\Exception\PendingException;
87
use Behat\Gherkin\Node\TableNode;
98
use Redmine\Api\Attachment;
109

@@ -32,6 +31,26 @@ public function iUploadTheContentOfTheFileWithTheFollowingData(string $filepath,
3231
);
3332
}
3433

34+
/**
35+
* @When I update the attachment with the id :attachmentId with the following data
36+
*/
37+
public function iUpdateTheAttachmentWithTheIdWithTheFollowingData(int $attachmentId, TableNode $table)
38+
{
39+
$data = [];
40+
41+
foreach ($table as $row) {
42+
$data[$row['property']] = $row['value'];
43+
}
44+
45+
/** @var Attachment */
46+
$api = $this->getNativeCurlClient()->getApi('attachment');
47+
48+
$this->registerClientResponse(
49+
$api->update($attachmentId, $data),
50+
$api->getLastResponse()
51+
);
52+
}
53+
3554
/**
3655
* @When I show the attachment with the id :attachmentId
3756
*/

tests/Behat/features/attachments.feature

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,21 @@ Feature: Interacting with the REST API for attachments
2626
| id | 1 |
2727
| token | 1.7b962f8af22e26802b87abfa0b07b21dbd03b984ec8d6888dabd3f69cff162f8 |
2828

29+
@attachment
30+
Scenario: Updating the details of an attachment
31+
Given I have a "NativeCurlClient" client
32+
And I create a project with name "Test Project" and identifier "test-project"
33+
And I upload the content of the file "%tests_dir%/Fixtures/testfile_01.txt" with the following data
34+
| property | value |
35+
| filename | testfile.txt |
36+
When I update the attachment with the id "1" with the following data
37+
| property | value |
38+
| filename | testfile2.txt |
39+
Then the response has the status code "204"
40+
And the response has an empty content type
41+
And the response has the content ""
42+
And the returned data is true
43+
2944
@attachment
3045
Scenario: Showing the details of an attachment
3146
Given I have a "NativeCurlClient" client
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<?php
2+
3+
namespace Redmine\Tests\Unit\Api\Attachment;
4+
5+
use PHPUnit\Framework\Attributes\CoversClass;
6+
use PHPUnit\Framework\Attributes\DataProvider;
7+
use PHPUnit\Framework\TestCase;
8+
use Redmine\Api\Attachment;
9+
use Redmine\Exception\UnexpectedResponseException;
10+
use Redmine\Tests\Fixtures\AssertingHttpClient;
11+
12+
#[CoversClass(Attachment::class)]
13+
class UpdateTest extends TestCase
14+
{
15+
/**
16+
* @dataProvider getUpdateData
17+
*/
18+
#[DataProvider('getUpdateData')]
19+
public function testUpdateReturnsCorrectResponse($id, array $params, $expectedPath, $expectedContent, $expectedReturn)
20+
{
21+
$client = AssertingHttpClient::create(
22+
$this,
23+
[
24+
'PUT',
25+
$expectedPath,
26+
'application/json',
27+
$expectedContent,
28+
204,
29+
'',
30+
''
31+
]
32+
);
33+
34+
// Create the object under test
35+
$api = new Attachment($client);
36+
37+
// Perform the tests
38+
$this->assertSame($expectedReturn, $api->update($id, $params));
39+
}
40+
41+
public static function getUpdateData(): array
42+
{
43+
return [
44+
'test with all params' => [
45+
5,
46+
[
47+
'filename' => 'renamed.zip',
48+
'description' => 'updated',
49+
],
50+
'/attachments/5.json',
51+
'{"attachment":{"filename":"renamed.zip","description":"updated"}}',
52+
true,
53+
],
54+
];
55+
}
56+
57+
public function testUpdateThrowsUnexpectedResponseException()
58+
{
59+
$client = AssertingHttpClient::create(
60+
$this,
61+
[
62+
'PUT',
63+
'/attachments/5.json',
64+
'application/json',
65+
'{"attachment":[]}',
66+
403,
67+
'',
68+
'',
69+
]
70+
);
71+
72+
$api = new Attachment($client);
73+
74+
$this->expectException(UnexpectedResponseException::class);
75+
$this->expectExceptionMessage('The Redmine server replied with an unexpected response.');
76+
77+
$api->update(5, []);
78+
}
79+
}

0 commit comments

Comments
 (0)