diff --git a/CHANGELOG.md b/CHANGELOG.md index 755b7057403..b117d191050 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## 2.6.5 * OpenAPI: Fix notice/warning for `response` without `content` in the `openapi_context` (#4210) +* Serializer: Convert internal error to HTTP 400 in Ramsey uuid denormalization from invalid body string (#4200) ## 2.6.4 diff --git a/features/main/uuid.feature b/features/main/uuid.feature index 9278cb15ee5..a2b0b3024db 100644 --- a/features/main/uuid.feature +++ b/features/main/uuid.feature @@ -111,8 +111,7 @@ Feature: Using uuid identifier on resource @createSchema Scenario: Retrieve a resource identified by Ramsey\Uuid\Uuid Given there is a ramsey identified resource with uuid "41B29566-144B-11E6-A148-3E1D05DEFE78" - When I add "Content-Type" header equal to "application/ld+json" - And I send a "GET" request to "/ramsey_uuid_dummies/41B29566-144B-11E6-A148-3E1D05DEFE78" + When I send a "GET" request to "/ramsey_uuid_dummies/41B29566-144B-11E6-A148-3E1D05DEFE78" Then the response status code should be 200 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" @@ -125,8 +124,7 @@ Feature: Using uuid identifier on resource @!mongodb Scenario: Retrieve a resource identified by a bad Ramsey\Uuid\Uuid - When I add "Content-Type" header equal to "application/ld+json" - And I send a "GET" request to "/ramsey_uuid_dummies/41B29566-144B-E1D05DEFE78" + When I send a "GET" request to "/ramsey_uuid_dummies/41B29566-144B-E1D05DEFE78" Then the response status code should be 404 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" @@ -144,3 +142,55 @@ Feature: Using uuid identifier on resource Then the response status code should be 201 And the response should be in JSON And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" + + @!mongodb + Scenario: Create a resource with a Ramsey\Uuid\Uuid non-id field + When I add "Content-Type" header equal to "application/ld+json" + And I send a "POST" request to "/ramsey_uuid_dummies" with body: + """ + { + "other": "51b29566-144b-11e6-a148-3e1d05defe78" + } + """ + Then the response status code should be 201 + And the response should be in JSON + And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" + + @!mongodb + Scenario: Update a resource with a Ramsey\Uuid\Uuid non-id field + When I add "Content-Type" header equal to "application/ld+json" + And I send a "PUT" request to "/ramsey_uuid_dummies/41b29566-144b-11e6-a148-3e1d05defe78" with body: + """ + { + "other": "61b29566-144b-11e6-a148-3e1d05defe78" + } + """ + Then the response status code should be 200 + And the response should be in JSON + And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" + + @!mongodb + Scenario: Create a resource identified by a bad Ramsey\Uuid\Uuid + When I add "Content-Type" header equal to "application/ld+json" + And I send a "POST" request to "/ramsey_uuid_dummies" with body: + """ + { + "id": "41b29566-144b-e1d05defe78" + } + """ + Then the response status code should be 400 + And the response should be in JSON + And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" + + @!mongodb + Scenario: Update a resource with a bad Ramsey\Uuid\Uuid non-id field + When I add "Content-Type" header equal to "application/ld+json" + And I send a "PUT" request to "/ramsey_uuid_dummies/41b29566-144b-11e6-a148-3e1d05defe78" with body: + """ + { + "other": "61b29566-144b-e1d05defe78" + } + """ + Then the response status code should be 400 + And the response should be in JSON + And the header "Content-Type" should be equal to "application/ld+json; charset=utf-8" diff --git a/src/Bridge/RamseyUuid/Serializer/UuidDenormalizer.php b/src/Bridge/RamseyUuid/Serializer/UuidDenormalizer.php index 87090190a85..8a8ff7abe63 100644 --- a/src/Bridge/RamseyUuid/Serializer/UuidDenormalizer.php +++ b/src/Bridge/RamseyUuid/Serializer/UuidDenormalizer.php @@ -13,15 +13,21 @@ namespace ApiPlatform\Core\Bridge\RamseyUuid\Serializer; +use Ramsey\Uuid\Exception\InvalidUuidStringException; use Ramsey\Uuid\Uuid; use Ramsey\Uuid\UuidInterface; +use Symfony\Component\Serializer\Exception\NotNormalizableValueException; use Symfony\Component\Serializer\Normalizer\DenormalizerInterface; final class UuidDenormalizer implements DenormalizerInterface { public function denormalize($data, $type, $format = null, array $context = []) { - return Uuid::fromString($data); + try { + return Uuid::fromString($data); + } catch (InvalidUuidStringException $e) { + throw new NotNormalizableValueException($e->getMessage(), $e->getCode(), $e); + } } public function supportsDenormalization($data, $type, $format = null) diff --git a/tests/Behat/DoctrineContext.php b/tests/Behat/DoctrineContext.php index ab42b22f050..0f03df48c7f 100644 --- a/tests/Behat/DoctrineContext.php +++ b/tests/Behat/DoctrineContext.php @@ -167,6 +167,7 @@ use Doctrine\ORM\Tools\SchemaTool; use Doctrine\Persistence\ManagerRegistry; use Doctrine\Persistence\ObjectManager; +use Ramsey\Uuid\Uuid; use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; /** @@ -1365,8 +1366,7 @@ public function thereAreDummyWithDifferentGraphQlSerializationGroupsObjects(int */ public function thereIsARamseyIdentifiedResource(string $uuid) { - $dummy = new RamseyUuidDummy(); - $dummy->setId($uuid); + $dummy = new RamseyUuidDummy(Uuid::fromString($uuid)); $this->manager->persist($dummy); $this->manager->flush(); diff --git a/tests/Bridge/RamseyUuid/Normalizer/UuidNormalizerTest.php b/tests/Bridge/RamseyUuid/Identifier/Normalizer/UuidNormalizerTest.php similarity index 95% rename from tests/Bridge/RamseyUuid/Normalizer/UuidNormalizerTest.php rename to tests/Bridge/RamseyUuid/Identifier/Normalizer/UuidNormalizerTest.php index cbf158da679..680f95c63ba 100644 --- a/tests/Bridge/RamseyUuid/Normalizer/UuidNormalizerTest.php +++ b/tests/Bridge/RamseyUuid/Identifier/Normalizer/UuidNormalizerTest.php @@ -11,7 +11,7 @@ declare(strict_types=1); -namespace ApiPlatform\Core\Tests\Bridge\RamseyUuid\Normalizer; +namespace ApiPlatform\Core\Tests\Bridge\RamseyUuid\Identifier\Normalizer; use ApiPlatform\Core\Bridge\RamseyUuid\Identifier\Normalizer\UuidNormalizer; use ApiPlatform\Core\Exception\InvalidIdentifierException; diff --git a/tests/Bridge/RamseyUuid/Serializer/UuidDenormalizerTest.php b/tests/Bridge/RamseyUuid/Serializer/UuidDenormalizerTest.php new file mode 100644 index 00000000000..1525c624389 --- /dev/null +++ b/tests/Bridge/RamseyUuid/Serializer/UuidDenormalizerTest.php @@ -0,0 +1,53 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +declare(strict_types=1); + +namespace ApiPlatform\Core\Tests\Bridge\RamseyUuid\Serializer; + +use ApiPlatform\Core\Bridge\RamseyUuid\Serializer\UuidDenormalizer; +use PHPUnit\Framework\TestCase; +use Ramsey\Uuid\Uuid; +use Symfony\Component\Serializer\Exception\NotNormalizableValueException; + +class UuidDenormalizerTest extends TestCase +{ + public function testDenormalizeUuid(): void + { + $uuid = Uuid::uuid4(); + $normalizer = new UuidDenormalizer(); + self::assertTrue($normalizer->supportsDenormalization($uuid->toString(), Uuid::class)); + self::assertEquals($uuid, $normalizer->denormalize($uuid->toString(), Uuid::class)); + } + + public function testNoSupportDenormalizeUuid(): void + { + $uuid = 'notanuuid'; + $normalizer = new UuidDenormalizer(); + self::assertFalse($normalizer->supportsDenormalization($uuid, '')); + } + + public function testFailDenormalizeUuid(): void + { + $this->expectException(NotNormalizableValueException::class); + + $uuid = 'notanuuid'; + $normalizer = new UuidDenormalizer(); + $normalizer->denormalize($uuid, Uuid::class); + } + + public function testDoNotSupportNotString(): void + { + $uuid = Uuid::uuid4(); + $normalizer = new UuidDenormalizer(); + self::assertFalse($normalizer->supportsDenormalization($uuid, Uuid::class)); + } +} diff --git a/tests/Fixtures/TestBundle/Entity/RamseyUuidDummy.php b/tests/Fixtures/TestBundle/Entity/RamseyUuidDummy.php index 0316dfa9611..15b72be3242 100644 --- a/tests/Fixtures/TestBundle/Entity/RamseyUuidDummy.php +++ b/tests/Fixtures/TestBundle/Entity/RamseyUuidDummy.php @@ -32,13 +32,30 @@ class RamseyUuidDummy */ private $id; + /** + * @var \Ramsey\Uuid\UuidInterface|null + * + * @ORM\Column(type="uuid", nullable=true) + */ + private $other; + + public function __construct(?UuidInterface $id = null) + { + $this->id = $id ?? Uuid::uuid4(); + } + public function getId(): UuidInterface { return $this->id; } - public function setId(string $uuid) + public function getOther(): ?UuidInterface + { + return $this->other; + } + + public function setOther(UuidInterface $other) { - $this->id = Uuid::fromString($uuid); + $this->other = $other; } }