diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a81b22f..30a2fae6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,10 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- New method `Redmine\Api\Attachment::update()` for updating attachments. - New interface `Redmine\Http\HttpClient` for new minimalistic HTTP clients. - New interface `Redmine\Http\Request` for sending data with new minimalistic HTTP clients. - New method `Redmine\Api\...::getLastResponse()` to get the last response made by the API class. -- Add support for custom arrays in `Redmine\Serializer\XmlSerializer` +- Add support for custom arrays in `Redmine\Serializer\XmlSerializer`. ### Changed diff --git a/src/Redmine/Api/Attachment.php b/src/Redmine/Api/Attachment.php index 6ad17939..bc5e33b1 100644 --- a/src/Redmine/Api/Attachment.php +++ b/src/Redmine/Api/Attachment.php @@ -3,6 +3,7 @@ namespace Redmine\Api; use Redmine\Exception\SerializerException; +use Redmine\Exception\UnexpectedResponseException; use Redmine\Http\HttpFactory; use Redmine\Serializer\JsonSerializer; use Redmine\Serializer\PathSerializer; @@ -46,6 +47,39 @@ public function show($id) } } + /** + * Update information about an attachment. + * + * @see https://www.redmine.org/projects/redmine/wiki/Rest_Attachments#PATCH + * + * @param int $id the attachment id + * @param array $params available $params: + * - filename: filename of the attachment + * - description: new description of the attachment + * + * @throws SerializerException if $params contains invalid values + * @throws UnexpectedResponseException if the Redmine server delivers an unexpected response + * + * @return true if the request was successful + */ + final public function update(int $id, array $params): bool + { + // we are using `PUT` instead of documented `PATCH` + // @see https://github.com/kbsali/php-redmine-api/pull/395#issuecomment-2004089154 + // @see https://www.redmine.org/projects/redmine/wiki/Rest_Attachments#PATCH + $this->lastResponse = $this->getHttpClient()->request(HttpFactory::makeJsonRequest( + 'PUT', + '/attachments/' . $id . '.json', + JsonSerializer::createFromArray(['attachment' => $params])->getEncoded() + )); + + if ($this->lastResponse->getStatusCode() !== 204) { + throw UnexpectedResponseException::create($this->lastResponse); + } + + return true; + } + /** * Get attachment content as a binary file. * diff --git a/tests/Behat/Bootstrap/AttachmentContextTrait.php b/tests/Behat/Bootstrap/AttachmentContextTrait.php index 63a86b75..be47608d 100644 --- a/tests/Behat/Bootstrap/AttachmentContextTrait.php +++ b/tests/Behat/Bootstrap/AttachmentContextTrait.php @@ -4,7 +4,6 @@ namespace Redmine\Tests\Behat\Bootstrap; -use Behat\Behat\Tester\Exception\PendingException; use Behat\Gherkin\Node\TableNode; use Redmine\Api\Attachment; @@ -32,6 +31,26 @@ public function iUploadTheContentOfTheFileWithTheFollowingData(string $filepath, ); } + /** + * @When I update the attachment with the id :attachmentId with the following data + */ + public function iUpdateTheAttachmentWithTheIdWithTheFollowingData(int $attachmentId, TableNode $table) + { + $data = []; + + foreach ($table as $row) { + $data[$row['property']] = $row['value']; + } + + /** @var Attachment */ + $api = $this->getNativeCurlClient()->getApi('attachment'); + + $this->registerClientResponse( + $api->update($attachmentId, $data), + $api->getLastResponse() + ); + } + /** * @When I show the attachment with the id :attachmentId */ diff --git a/tests/Behat/features/attachments.feature b/tests/Behat/features/attachments.feature index da303c56..aef9a26b 100644 --- a/tests/Behat/features/attachments.feature +++ b/tests/Behat/features/attachments.feature @@ -26,6 +26,21 @@ Feature: Interacting with the REST API for attachments | id | 1 | | token | 1.7b962f8af22e26802b87abfa0b07b21dbd03b984ec8d6888dabd3f69cff162f8 | + @attachment + Scenario: Updating the details of an attachment + Given I have a "NativeCurlClient" client + And I create a project with name "Test Project" and identifier "test-project" + And I upload the content of the file "%tests_dir%/Fixtures/testfile_01.txt" with the following data + | property | value | + | filename | testfile.txt | + When I update the attachment with the id "1" with the following data + | property | value | + | filename | testfile2.txt | + Then the response has the status code "204" + And the response has an empty content type + And the response has the content "" + And the returned data is true + @attachment Scenario: Showing the details of an attachment Given I have a "NativeCurlClient" client diff --git a/tests/Unit/Api/Attachment/UpdateTest.php b/tests/Unit/Api/Attachment/UpdateTest.php new file mode 100644 index 00000000..df6c43b1 --- /dev/null +++ b/tests/Unit/Api/Attachment/UpdateTest.php @@ -0,0 +1,79 @@ +assertSame($expectedReturn, $api->update($id, $params)); + } + + public static function getUpdateData(): array + { + return [ + 'test with all params' => [ + 5, + [ + 'filename' => 'renamed.zip', + 'description' => 'updated', + ], + '/attachments/5.json', + '{"attachment":{"filename":"renamed.zip","description":"updated"}}', + true, + ], + ]; + } + + public function testUpdateThrowsUnexpectedResponseException() + { + $client = AssertingHttpClient::create( + $this, + [ + 'PUT', + '/attachments/5.json', + 'application/json', + '{"attachment":[]}', + 403, + '', + '', + ] + ); + + $api = new Attachment($client); + + $this->expectException(UnexpectedResponseException::class); + $this->expectExceptionMessage('The Redmine server replied with an unexpected response.'); + + $api->update(5, []); + } +}