From 0f3216568db60f63eab4670899fdd1b00a233448 Mon Sep 17 00:00:00 2001 From: Art4 Date: Mon, 18 Mar 2024 11:24:38 +0100 Subject: [PATCH 1/5] Create Attachment::update() --- src/Redmine/Api/Attachment.php | 32 ++++++++++++++ tests/Unit/Api/Attachment/UpdateTest.php | 55 ++++++++++++++++++++++++ 2 files changed, 87 insertions(+) create mode 100644 tests/Unit/Api/Attachment/UpdateTest.php diff --git a/src/Redmine/Api/Attachment.php b/src/Redmine/Api/Attachment.php index 6ad17939..fcbbba87 100644 --- a/src/Redmine/Api/Attachment.php +++ b/src/Redmine/Api/Attachment.php @@ -2,7 +2,9 @@ namespace Redmine\Api; +use InvalidArgumentException; use Redmine\Exception\SerializerException; +use Redmine\Exception\UnexpectedResponseException; use Redmine\Http\HttpFactory; use Redmine\Serializer\JsonSerializer; use Redmine\Serializer\PathSerializer; @@ -46,6 +48,36 @@ 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 + */ + public function update(int $id, array $params): bool + { + $this->lastResponse = $this->getHttpClient()->request(HttpFactory::makeJsonRequest( + 'PATCH', + '/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/Unit/Api/Attachment/UpdateTest.php b/tests/Unit/Api/Attachment/UpdateTest.php new file mode 100644 index 00000000..cf35edd8 --- /dev/null +++ b/tests/Unit/Api/Attachment/UpdateTest.php @@ -0,0 +1,55 @@ +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, + ], + ]; + } +} From a12292a0724d6d5bc7b4831f70c8be91d3d62024 Mon Sep 17 00:00:00 2001 From: Art4 Date: Mon, 18 Mar 2024 11:44:16 +0100 Subject: [PATCH 2/5] Add test for UnexpectedResponseException in Attachment::update() --- src/Redmine/Api/Attachment.php | 2 +- tests/Unit/Api/Attachment/UpdateTest.php | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/Redmine/Api/Attachment.php b/src/Redmine/Api/Attachment.php index fcbbba87..07c44102 100644 --- a/src/Redmine/Api/Attachment.php +++ b/src/Redmine/Api/Attachment.php @@ -63,7 +63,7 @@ public function show($id) * * @return true if the request was successful */ - public function update(int $id, array $params): bool + final public function update(int $id, array $params): bool { $this->lastResponse = $this->getHttpClient()->request(HttpFactory::makeJsonRequest( 'PATCH', diff --git a/tests/Unit/Api/Attachment/UpdateTest.php b/tests/Unit/Api/Attachment/UpdateTest.php index cf35edd8..c91443df 100644 --- a/tests/Unit/Api/Attachment/UpdateTest.php +++ b/tests/Unit/Api/Attachment/UpdateTest.php @@ -6,6 +6,7 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Redmine\Api\Attachment; +use Redmine\Exception\UnexpectedResponseException; use Redmine\Tests\Fixtures\AssertingHttpClient; #[CoversClass(Attachment::class)] @@ -52,4 +53,27 @@ public static function getUpdateData(): array ], ]; } + + public function testUpdateThrowsUnexpectedResponseException() + { + $client = AssertingHttpClient::create( + $this, + [ + 'PATCH', + '/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, []); + } } From 23ccecce2b2a601f84d02ee5039242fa59102d2a Mon Sep 17 00:00:00 2001 From: Art4 Date: Mon, 18 Mar 2024 14:40:36 +0100 Subject: [PATCH 3/5] Create behat tests for Attachment::update() --- src/Redmine/Api/Attachment.php | 7 +++---- .../Bootstrap/AttachmentContextTrait.php | 21 ++++++++++++++++++- tests/Behat/features/attachments.feature | 15 +++++++++++++ 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/Redmine/Api/Attachment.php b/src/Redmine/Api/Attachment.php index 07c44102..5f504b12 100644 --- a/src/Redmine/Api/Attachment.php +++ b/src/Redmine/Api/Attachment.php @@ -2,7 +2,6 @@ namespace Redmine\Api; -use InvalidArgumentException; use Redmine\Exception\SerializerException; use Redmine\Exception\UnexpectedResponseException; use Redmine\Http\HttpFactory; @@ -71,9 +70,9 @@ final public function update(int $id, array $params): bool JsonSerializer::createFromArray(['attachment' => $params])->getEncoded() )); - if ($this->lastResponse->getStatusCode() !== 204) { - throw UnexpectedResponseException::create($this->lastResponse); - } + // if ($this->lastResponse->getStatusCode() !== 204) { + // throw UnexpectedResponseException::create($this->lastResponse); + // } return true; } 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 From b0790526b62c38e78ec091746bd278a59cda3a53 Mon Sep 17 00:00:00 2001 From: Art4 Date: Mon, 18 Mar 2024 16:01:36 +0100 Subject: [PATCH 4/5] Use PUT instead of PATCH in Attachment::update() --- src/Redmine/Api/Attachment.php | 10 ++++++---- tests/Unit/Api/Attachment/UpdateTest.php | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/Redmine/Api/Attachment.php b/src/Redmine/Api/Attachment.php index 5f504b12..ed5d7164 100644 --- a/src/Redmine/Api/Attachment.php +++ b/src/Redmine/Api/Attachment.php @@ -64,15 +64,17 @@ public function show($id) */ final public function update(int $id, array $params): bool { + // we are using `PUT` instead of `PATCH` + // @see https://github.com/kbsali/php-redmine-api/pull/395#issuecomment-2004089154 $this->lastResponse = $this->getHttpClient()->request(HttpFactory::makeJsonRequest( - 'PATCH', + 'PUT', '/attachments/' . $id . '.json', JsonSerializer::createFromArray(['attachment' => $params])->getEncoded() )); - // if ($this->lastResponse->getStatusCode() !== 204) { - // throw UnexpectedResponseException::create($this->lastResponse); - // } + if ($this->lastResponse->getStatusCode() !== 204) { + throw UnexpectedResponseException::create($this->lastResponse); + } return true; } diff --git a/tests/Unit/Api/Attachment/UpdateTest.php b/tests/Unit/Api/Attachment/UpdateTest.php index c91443df..df6c43b1 100644 --- a/tests/Unit/Api/Attachment/UpdateTest.php +++ b/tests/Unit/Api/Attachment/UpdateTest.php @@ -21,7 +21,7 @@ public function testUpdateReturnsCorrectResponse($id, array $params, $expectedPa $client = AssertingHttpClient::create( $this, [ - 'PATCH', + 'PUT', $expectedPath, 'application/json', $expectedContent, @@ -59,7 +59,7 @@ public function testUpdateThrowsUnexpectedResponseException() $client = AssertingHttpClient::create( $this, [ - 'PATCH', + 'PUT', '/attachments/5.json', 'application/json', '{"attachment":[]}', From 8d265a4206bc6a1fa8e9b6e76bce7799781900dd Mon Sep 17 00:00:00 2001 From: Art4 Date: Mon, 18 Mar 2024 16:06:04 +0100 Subject: [PATCH 5/5] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- src/Redmine/Api/Attachment.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) 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 ed5d7164..bc5e33b1 100644 --- a/src/Redmine/Api/Attachment.php +++ b/src/Redmine/Api/Attachment.php @@ -64,8 +64,9 @@ public function show($id) */ final public function update(int $id, array $params): bool { - // we are using `PUT` instead of `PATCH` + // 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',