From 367040ddfe3ccc6ef8eb4fcb0181130decc634fe Mon Sep 17 00:00:00 2001 From: Krystian Szymukowicz Date: Sun, 27 Jul 2025 20:32:10 +0200 Subject: [PATCH] [BUGFIX] Refine file reference handling for POST requests. Before this fix it was not possible to add sys_file_reference in one go with the main record. It was only possible to add a file reference using PATCH after to existing main record. Additional changes: a) Standardize serialization groups in News model for consistent API access. b) Enhance Postman tests to verify multiple file references in POST requests. c) Update test assertions to validate file reference properties. --- .../t3apinews/Classes/Domain/Model/News.php | 60 +++-- .../Handler/FileReferenceHandler.php | 2 +- README.rst | 2 +- Tests/Postman/t3apinews.crud.json | 221 +++++++++++++++++- 4 files changed, 267 insertions(+), 18 deletions(-) diff --git a/.ddev/test/files/src/t3apinews/Classes/Domain/Model/News.php b/.ddev/test/files/src/t3apinews/Classes/Domain/Model/News.php index 60435e9..ae00bc0 100644 --- a/.ddev/test/files/src/t3apinews/Classes/Domain/Model/News.php +++ b/.ddev/test/files/src/t3apinews/Classes/Domain/Model/News.php @@ -139,6 +139,9 @@ class News extends \GeorgRinger\News\Domain\Model\News * @T3api\Serializer\Groups({ * "api_get_collection_t3apinews_news", * "api_get_item_t3apinews_news", + * "api_post_item_t3apinews_news", + * "api_patch_item_t3apinews_news", + * "api_put_item_t3apinews_news", * }) */ protected $teaser = ''; @@ -148,6 +151,8 @@ class News extends \GeorgRinger\News\Domain\Model\News * @T3api\Serializer\Groups({ * "api_get_item_t3apinews_news", * "api_post_item_t3apinews_news", + * "api_patch_item_t3apinews_news", + * "api_put_item_t3apinews_news", * }) */ protected $bodytext = ''; @@ -156,10 +161,10 @@ class News extends \GeorgRinger\News\Domain\Model\News * @var \DateTime * @T3api\Serializer\Groups({ * "api_get_collection_t3apinews_news", - * "api_get_item_t3apinews_news", - * "api_post_item_t3apinews_news", - * "api_patch_item_t3apinews_news", - * "api_put_item_t3apinews_news", + * "api_get_item_t3apinews_news", + * "api_post_item_t3apinews_news", + * "api_patch_item_t3apinews_news", + * "api_put_item_t3apinews_news", * }) */ protected $datetime; @@ -167,9 +172,9 @@ class News extends \GeorgRinger\News\Domain\Model\News /** * @var \Datetime * @T3api\Serializer\Groups({ - * "api_post_item_t3apinews_news", - * "api_patch_item_t3apinews_news", - * "api_put_item_t3apinews_news", + * "api_post_item_t3apinews_news", + * "api_patch_item_t3apinews_news", + * "api_put_item_t3apinews_news", * }) */ protected $crdate; @@ -179,6 +184,9 @@ class News extends \GeorgRinger\News\Domain\Model\News * @T3api\Serializer\Groups({ * "api_get_collection_t3apinews_news", * "api_get_item_t3apinews_news", + * "api_post_item_t3apinews_news", + * "api_patch_item_t3apinews_news", + * "api_put_item_t3apinews_news", * }) */ protected $author = ''; @@ -188,6 +196,9 @@ class News extends \GeorgRinger\News\Domain\Model\News * @T3api\Serializer\Groups({ * "api_get_collection_t3apinews_news", * "api_get_item_t3apinews_news", + * "api_post_item_t3apinews_news", + * "api_patch_item_t3apinews_news", + * "api_put_item_t3apinews_news", * }) */ protected $authorEmail = ''; @@ -207,10 +218,10 @@ class News extends \GeorgRinger\News\Domain\Model\News /** * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\SourceBroker\T3apinews\Domain\Model\News> * @T3api\Serializer\Groups({ - * "api_get_item_t3apinews_news", - * "api_post_item_t3apinews_news", - * "api_patch_item_t3apinews_news", - * "api_put_item_t3apinews_news", + * "api_get_item_t3apinews_news", + * "api_post_item_t3apinews_news", + * "api_patch_item_t3apinews_news", + * "api_put_item_t3apinews_news", * }) */ protected $related; @@ -218,10 +229,10 @@ class News extends \GeorgRinger\News\Domain\Model\News /** * @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\SourceBroker\T3apinews\Domain\Model\News> * @T3api\Serializer\Groups({ - * "api_get_item_t3apinews_news", - * "api_post_item_t3apinews_news", - * "api_patch_item_t3apinews_news", - * "api_put_item_t3apinews_news", + * "api_get_item_t3apinews_news", + * "api_post_item_t3apinews_news", + * "api_patch_item_t3apinews_news", + * "api_put_item_t3apinews_news", * }) */ protected $relatedFrom; @@ -231,6 +242,9 @@ class News extends \GeorgRinger\News\Domain\Model\News * @T3api\Serializer\Groups({ * "api_get_collection_t3apinews_news", * "api_get_item_t3apinews_news", + * "api_post_item_t3apinews_news", + * "api_patch_item_t3apinews_news", + * "api_put_item_t3apinews_news", * }) */ protected $type = ''; @@ -238,6 +252,8 @@ class News extends \GeorgRinger\News\Domain\Model\News /** * @var string * @T3api\Serializer\Groups({ + * "api_get_collection_t3apinews_news", + * "api_get_item_t3apinews_news", * "api_post_item_t3apinews_news", * "api_patch_item_t3apinews_news", * "api_put_item_t3apinews_news", @@ -249,6 +265,9 @@ class News extends \GeorgRinger\News\Domain\Model\News * @T3api\Serializer\Groups({ * "api_get_collection_t3apinews_news", * "api_get_item_t3apinews_news", + * "api_post_item_t3apinews_news", + * "api_patch_item_t3apinews_news", + * "api_put_item_t3apinews_news", * }) */ protected $internalurl = ''; @@ -258,6 +277,9 @@ class News extends \GeorgRinger\News\Domain\Model\News * @T3api\Serializer\Groups({ * "api_get_collection_t3apinews_news", * "api_get_item_t3apinews_news", + * "api_post_item_t3apinews_news", + * "api_patch_item_t3apinews_news", + * "api_put_item_t3apinews_news", * }) */ protected $externalurl = ''; @@ -267,6 +289,9 @@ class News extends \GeorgRinger\News\Domain\Model\News * @T3api\Serializer\Groups({ * "api_get_collection_t3apinews_news", * "api_get_item_t3apinews_news", + * "api_post_item_t3apinews_news", + * "api_patch_item_t3apinews_news", + * "api_put_item_t3apinews_news", * }) */ protected $istopnews = false; @@ -276,6 +301,9 @@ class News extends \GeorgRinger\News\Domain\Model\News * @T3api\Serializer\Groups({ * "api_get_collection_t3apinews_news", * "api_get_item_t3apinews_news", + * "api_post_item_t3apinews_news", + * "api_patch_item_t3apinews_news", + * "api_put_item_t3apinews_news", * }) */ protected \TYPO3\CMS\Extbase\Persistence\ObjectStorage $tags; @@ -285,7 +313,9 @@ class News extends \GeorgRinger\News\Domain\Model\News * @T3api\Serializer\Groups({ * "api_get_collection_t3apinews_news", * "api_get_item_t3apinews_news", + * "api_post_item_t3apinews_news", * "api_patch_item_t3apinews_news", + * "api_put_item_t3apinews_news", * }) */ protected $falMedia; diff --git a/Classes/Serializer/Handler/FileReferenceHandler.php b/Classes/Serializer/Handler/FileReferenceHandler.php index 7da22fe..ca1fed6 100644 --- a/Classes/Serializer/Handler/FileReferenceHandler.php +++ b/Classes/Serializer/Handler/FileReferenceHandler.php @@ -205,7 +205,7 @@ protected function createSysFileReference( if ($visitor->getCurrentObject() instanceof AbstractDomainObject) { /** @var AbstractDomainObject $currentObject */ $currentObject = $visitor->getCurrentObject(); - $fileReference->setPid($currentObject->getPid()); + $fileReference->setPid($currentObject->getPid() ?? 0); $fileReference->_setProperty('_languageUid', $currentObject->_getProperty('_languageUid')); } diff --git a/README.rst b/README.rst index 8772f1f..063be50 100644 --- a/README.rst +++ b/README.rst @@ -44,7 +44,7 @@ At frontend part you can at once test REST API responses for ext news: * https://13.t3api.ddev.site/_api/news/categories * etc -You can also run Postman test with ``ddev ci:tests:postman`` command or full test suite with ``ddev composer ci``. +You can also run Postman test with ``ddev composer ci:tests:postman`` command or full test suite with ``ddev composer ci``. Postman is doing full CRUD test with category and news (with image). Development diff --git a/Tests/Postman/t3apinews.crud.json b/Tests/Postman/t3apinews.crud.json index ba3d81b..86b4b11 100644 --- a/Tests/Postman/t3apinews.crud.json +++ b/Tests/Postman/t3apinews.crud.json @@ -311,7 +311,6 @@ }, "response": [] }, - { "name": "Edit news", "event": [ @@ -607,6 +606,20 @@ " }", ");", "", + "pm.test(", + " 'Each falMedia item has proper properties',", + " function() {", + " pm.expect(response.falMedia[0].uid).to.be.a('number');", + " pm.expect(response.falMedia[0].url).to.be.a('string');", + " pm.expect(response.falMedia[0].file).to.be.an('object');", + " pm.expect(response.falMedia[0].file.uid).to.equal(Number(pm.environment.get('uploadedFieldUid')));", + "", + " pm.expect(response.falMedia[1].uid).to.be.a('number');", + " pm.expect(response.falMedia[1].url).to.be.a('string');", + " pm.expect(response.falMedia[1].file).to.be.an('object');", + " pm.expect(response.falMedia[1].file.uid).to.equal(Number(pm.environment.get('uploadedFieldUid')));", + " }", + ");", "" ], "type": "text/javascript" @@ -723,6 +736,212 @@ }, "response": [] }, + { + "name": "Create news with file references in the same request", + "event": [ + { + "listen": "test", + "script": { + "id": "7cdf6990-c444-4b88-bd80-12a614d47ed7", + "exec": [ + "const response = pm.response.json();", + "", + "pm.test(", + " 'Status code is 201',", + " function () {", + " pm.response.to.have.status(201);", + " }", + ");", + "", + "pm.environment.set('newsUid', response.uid);", + "pm.environment.set('singleNewsEndpoint', response['@id']);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"title\": \"This is an example news with file reference added at once\",\n\t\"alternativeTitle\": \"Alt title\",\n\t\"pid\": 3,\n\t\"pathSegment\":\"this_is_an_example_news_with_file_reference\",\n\t\"crdate\": \"2019-12-27T16:58:23.063Z\",\n\t\"datetime\": \"2019-12-27T16:58:23.063Z\",\n\t\"categories\": [{{categoryUid}}],\n\t\"bodytext\": \"Here is the body\",\n\t\"falMedia\": [\n\t\t{\n\t\t\t\"uidLocal\": {{uploadedFieldUid}},\n\t\t\t\"showinpreview\": 1\n\t\t},\n\t\t{\n\t\t\t\"uidLocal\": {{uploadedFieldUid}},\n\t\t\t\"showinpreview\": 0\n\t\t}\n\t]\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{baseUrl}}{{newsEndpointPath}}", + "host": [ + "{{baseUrl}}{{newsEndpointPath}}" + ] + } + }, + "response": [] + }, + { + "name": "Check news if has fal media", + "event": [ + { + "listen": "test", + "script": { + "id": "9cb798ce-62c7-494f-a005-747a54caa09c", + "exec": [ + "const response = pm.response.json();", + "", + "pm.test(", + " 'Status code is 200',", + " function () {", + " pm.response.to.have.status(200);", + " }", + ");", + "", + "pm.test(", + " 'Title of the new news is correct',", + " function() {", + " pm.expect(response.title).to.eql('This is an example news with file reference added at once')", + " }", + ");", + "", + "pm.test(", + " 'falMedia is an array with at least two elements',", + " function() {", + " pm.expect(response.falMedia).to.be.an('array');", + " pm.expect(response.falMedia.length).to.be.at.least(2);", + " }", + ");", + "", + "pm.test(", + " 'Each falMedia item has proper properties',", + " function() {", + " pm.expect(response.falMedia[0].uid).to.be.a('number');", + " pm.expect(response.falMedia[0].url).to.be.a('string');", + " pm.expect(response.falMedia[0].file).to.be.an('object');", + " pm.expect(response.falMedia[0].file.uid).to.equal(Number(pm.environment.get('uploadedFieldUid')));", + "", + " pm.expect(response.falMedia[1].uid).to.be.a('number');", + " pm.expect(response.falMedia[1].url).to.be.a('string');", + " pm.expect(response.falMedia[1].file).to.be.an('object');", + " pm.expect(response.falMedia[1].file.uid).to.equal(Number(pm.environment.get('uploadedFieldUid')));", + " }", + ");", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}{{singleNewsEndpoint}}", + "host": [ + "{{baseUrl}}{{singleNewsEndpoint}}" + ] + } + }, + "response": [] + }, + { + "name": "Delete news", + "event": [ + { + "listen": "test", + "script": { + "id": "4de54e1e-64cb-4e41-a7fb-91b37d66a481", + "exec": [ + "pm.test(", + " 'Status code is 200',", + " function () {", + " pm.response.to.have.status(200);", + " }", + ");", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [ + { + "key": "Content-Type", + "value": "application/json", + "type": "text" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "{{baseUrl}}{{singleNewsEndpoint}}", + "host": [ + "{{baseUrl}}{{singleNewsEndpoint}}" + ] + } + }, + "response": [] + }, + { + "name": "Check news after remove", + "event": [ + { + "listen": "test", + "script": { + "id": "37f85fba-b0aa-40d6-95a6-54f03b6b396f", + "exec": [ + "pm.test(", + " 'Status code is 404',", + " function () {", + " pm.response.to.have.status(404);", + " }", + ");", + "", + "pm.environment.set('newsUid', 0);", + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "GET", + "header": [ + { + "key": "Content-Type", + "name": "Content-Type", + "type": "text", + "value": "application/json" + } + ], + "url": { + "raw": "{{baseUrl}}{{singleNewsEndpoint}}", + "host": [ + "{{baseUrl}}{{singleNewsEndpoint}}" + ] + } + }, + "response": [] + }, { "name": "Delete category", "event": [