From e93e028fed4707e0f9fab24b9ab614b2c3793efa Mon Sep 17 00:00:00 2001 From: soyuka Date: Mon, 3 Oct 2022 14:07:55 +0200 Subject: [PATCH 1/3] fix(metadata): no skolem iri by default --- src/JsonLd/ContextBuilder.php | 4 ++++ src/JsonLd/Serializer/JsonLdContextTrait.php | 2 +- src/Metadata/ApiProperty.php | 19 +++++++++++++++++++ src/Serializer/AbstractItemNormalizer.php | 4 ++-- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/JsonLd/ContextBuilder.php b/src/JsonLd/ContextBuilder.php index ea04f293d3f..6f8c6c2089a 100644 --- a/src/JsonLd/ContextBuilder.php +++ b/src/JsonLd/ContextBuilder.php @@ -200,6 +200,10 @@ public function getAnonymousResourceContext($object, array $context = [], int $r } } + if (false === ($context['iri'] ?? null)) { + trigger_deprecation('api-platform/core', '2.7', 'An anonymous resource will use a Skolem IRI in API Platform 3.0. Use #[ApiProperty(skolemIri: false)] to keep this behavior in 3.0.'); + } + if ($context['has_context'] ?? false) { unset($jsonLdContext['@context']); } diff --git a/src/JsonLd/Serializer/JsonLdContextTrait.php b/src/JsonLd/Serializer/JsonLdContextTrait.php index 296c6476143..43612ccc785 100644 --- a/src/JsonLd/Serializer/JsonLdContextTrait.php +++ b/src/JsonLd/Serializer/JsonLdContextTrait.php @@ -51,7 +51,7 @@ private function createJsonLdContext(AnonymousContextBuilderInterface $contextBu { // We're in a collection, don't add the @context part if (isset($context['jsonld_has_context'])) { - return $contextBuilder->getAnonymousResourceContext($object, ($context['output'] ?? []) + ['api_resource' => $context['api_resource'] ?? null, 'has_context' => true]); + return $contextBuilder->getAnonymousResourceContext($object, ($context['output'] ?? []) + ['api_resource' => $context['api_resource'] ?? null, 'has_context' => true, 'iri' => false]); } $context['jsonld_has_context'] = true; diff --git a/src/Metadata/ApiProperty.php b/src/Metadata/ApiProperty.php index a9641a931c1..c6ac721c29e 100644 --- a/src/Metadata/ApiProperty.php +++ b/src/Metadata/ApiProperty.php @@ -92,6 +92,7 @@ final class ApiProperty private $schema; private $initializable; + private $skolemIri; /** * @var string[] @@ -146,6 +147,7 @@ public function __construct( ?array $builtinTypes = null, ?array $schema = null, ?bool $initializable = null, + ?bool $skolemIri = null, $iris = null, @@ -175,6 +177,7 @@ public function __construct( $this->builtinTypes = $builtinTypes; $this->schema = $schema; $this->initializable = $initializable; + $this->skolemIri = $skolemIri; $this->iris = $iris; $this->extraProperties = $extraProperties; } @@ -507,4 +510,20 @@ public function withIris($iris): self return $metadata; } + + /** + * Whether to generate a skolem iri on anonymous resources. + */ + public function getSkolemIri() + { + return $this->skolemIri; + } + + public function withSkolemIri($skolemIri): self + { + $metadata = clone $this; + $metadata->skolemIri = $skolemIri; + + return $metadata; + } } diff --git a/src/Serializer/AbstractItemNormalizer.php b/src/Serializer/AbstractItemNormalizer.php index 279edf8fd3a..0a8c4b0c2ba 100644 --- a/src/Serializer/AbstractItemNormalizer.php +++ b/src/Serializer/AbstractItemNormalizer.php @@ -831,10 +831,10 @@ protected function getAttributeValue($object, $attribute, $format = null, array unset($childContext['iri'], $childContext['uri_variables']); if ($propertyMetadata instanceof PropertyMetadata) { - $childContext['output']['iri'] = $propertyMetadata->getIri(); + $childContext['output']['iri'] = $propertyMetadata->getIri() ?? false; } else { if (null !== ($propertyIris = $propertyMetadata->getIris())) { - $childContext['output']['iri'] = 1 === \count($propertyIris) ? $propertyIris[0] : $propertyIris; + $childContext['output']['iri'] = 1 === \count($propertyIris) ? ($propertyIris[0] ?? false) : $propertyIris; } } From aef862dbfb71f755611280be4d1ac3e62697ec86 Mon Sep 17 00:00:00 2001 From: soyuka Date: Wed, 5 Oct 2022 11:25:22 +0200 Subject: [PATCH 2/3] temp --- src/Metadata/Extractor/schema/properties.xsd | 1 + tests/Metadata/Extractor/Adapter/XmlPropertyAdapter.php | 1 + tests/Metadata/Extractor/PropertyMetadataCompatibilityTest.php | 1 + 3 files changed, 3 insertions(+) diff --git a/src/Metadata/Extractor/schema/properties.xsd b/src/Metadata/Extractor/schema/properties.xsd index 86422068b7f..6f74f9fdc95 100644 --- a/src/Metadata/Extractor/schema/properties.xsd +++ b/src/Metadata/Extractor/schema/properties.xsd @@ -43,6 +43,7 @@ + diff --git a/tests/Metadata/Extractor/Adapter/XmlPropertyAdapter.php b/tests/Metadata/Extractor/Adapter/XmlPropertyAdapter.php index 52248aed1ad..fc31e786d05 100644 --- a/tests/Metadata/Extractor/Adapter/XmlPropertyAdapter.php +++ b/tests/Metadata/Extractor/Adapter/XmlPropertyAdapter.php @@ -42,6 +42,7 @@ final class XmlPropertyAdapter implements PropertyAdapterInterface 'securityPostDenormalize', 'initializable', 'iris', + 'skolemIri' ]; /** diff --git a/tests/Metadata/Extractor/PropertyMetadataCompatibilityTest.php b/tests/Metadata/Extractor/PropertyMetadataCompatibilityTest.php index 84677a9912d..2c0502a8b1a 100644 --- a/tests/Metadata/Extractor/PropertyMetadataCompatibilityTest.php +++ b/tests/Metadata/Extractor/PropertyMetadataCompatibilityTest.php @@ -75,6 +75,7 @@ final class PropertyMetadataCompatibilityTest extends TestCase 'custom_property' => 'Lorem ipsum dolor sit amet', ], 'iris' => ['https://schema.org/totalPrice'], + 'skolemIri' => true ]; /** From 68fe207a5a21edb82f592edc8455d223e31c0fe5 Mon Sep 17 00:00:00 2001 From: soyuka Date: Fri, 7 Oct 2022 08:53:46 +0200 Subject: [PATCH 3/3] genid --- features/jsonld/non_resource.feature | 9 +++++ src/Hydra/Serializer/CollectionNormalizer.php | 1 + src/JsonLd/ContextBuilder.php | 6 +++- src/JsonLd/Serializer/ItemNormalizer.php | 6 ++++ src/Metadata/ApiProperty.php | 14 ++++---- .../Extractor/XmlPropertyExtractor.php | 1 + .../Extractor/YamlPropertyExtractor.php | 1 + src/Metadata/Extractor/schema/properties.xsd | 2 +- src/Serializer/AbstractItemNormalizer.php | 4 +-- tests/Fixtures/TestBundle/Model/GenId.php | 35 +++++++++++++++++++ .../TestBundle/Model/MonetaryAmount.php | 21 +++++++++++ .../Serializer/CollectionNormalizerTest.php | 1 + .../Extractor/Adapter/XmlPropertyAdapter.php | 2 +- .../PropertyMetadataCompatibilityTest.php | 2 +- 14 files changed, 91 insertions(+), 14 deletions(-) create mode 100644 tests/Fixtures/TestBundle/Model/GenId.php create mode 100644 tests/Fixtures/TestBundle/Model/MonetaryAmount.php diff --git a/features/jsonld/non_resource.feature b/features/jsonld/non_resource.feature index 654d90d35bf..fefa71f8609 100644 --- a/features/jsonld/non_resource.feature +++ b/features/jsonld/non_resource.feature @@ -38,6 +38,7 @@ Feature: JSON-LD non-resource handling } } """ + And the JSON node "notAResource.@id" should not exist Scenario: Get a resource containing a raw object with selected properties Given there are 1 dummy objects with relatedDummy and its thirdLevel @@ -123,3 +124,11 @@ Feature: JSON-LD non-resource handling "id": 1 } """ + + @php8 + Scenario: Get a generated id + When I send a "GET" request to "/genids/1" + 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" + And the JSON node "totalPrice.@id" should exist diff --git a/src/Hydra/Serializer/CollectionNormalizer.php b/src/Hydra/Serializer/CollectionNormalizer.php index 91a2903edca..af5791decb6 100644 --- a/src/Hydra/Serializer/CollectionNormalizer.php +++ b/src/Hydra/Serializer/CollectionNormalizer.php @@ -87,6 +87,7 @@ public function normalize($object, $format = null, array $context = []): array $resourceClass = $this->resourceClassResolver->getResourceClass($object, $context['resource_class']); $context = $this->initContext($resourceClass, $context); + $context['api_collection_sub_level'] = true; $data = $this->addJsonLdContext($this->contextBuilder, $resourceClass, $context); if ($this->iriConverter instanceof LegacyIriConverterInterface) { diff --git a/src/JsonLd/ContextBuilder.php b/src/JsonLd/ContextBuilder.php index 6f8c6c2089a..c7e2e3c3608 100644 --- a/src/JsonLd/ContextBuilder.php +++ b/src/JsonLd/ContextBuilder.php @@ -200,8 +200,12 @@ public function getAnonymousResourceContext($object, array $context = [], int $r } } + if ($this->iriConverter && isset($context['gen_id']) && true === $context['gen_id']) { + $jsonLdContext['@id'] = $this->iriConverter->getIriFromResource($object); + } + if (false === ($context['iri'] ?? null)) { - trigger_deprecation('api-platform/core', '2.7', 'An anonymous resource will use a Skolem IRI in API Platform 3.0. Use #[ApiProperty(skolemIri: false)] to keep this behavior in 3.0.'); + trigger_deprecation('api-platform/core', '2.7', 'An anonymous resource will use a Skolem IRI in API Platform 3.0. Use #[ApiProperty(genId: false)] to keep this behavior in 3.0.'); } if ($context['has_context'] ?? false) { diff --git a/src/JsonLd/Serializer/ItemNormalizer.php b/src/JsonLd/Serializer/ItemNormalizer.php index 8482f236a5b..1102f7b48ba 100644 --- a/src/JsonLd/Serializer/ItemNormalizer.php +++ b/src/JsonLd/Serializer/ItemNormalizer.php @@ -89,6 +89,12 @@ public function normalize($object, $format = null, array $context = []) $context = $this->initContext($resourceClass, $context); $metadata = $this->addJsonLdContext($this->contextBuilder, $resourceClass, $context); } elseif ($this->contextBuilder instanceof AnonymousContextBuilderInterface) { + if ($context['api_collection_sub_level'] ?? false) { + unset($context['api_collection_sub_level']); + $context['output']['genid'] = true; + $context['output']['iri'] = null; + } + // We should improve what's behind the context creation, its probably more complicated then it should $metadata = $this->createJsonLdContext($this->contextBuilder, $object, $context); } diff --git a/src/Metadata/ApiProperty.php b/src/Metadata/ApiProperty.php index c6ac721c29e..cd62a5656ca 100644 --- a/src/Metadata/ApiProperty.php +++ b/src/Metadata/ApiProperty.php @@ -92,7 +92,7 @@ final class ApiProperty private $schema; private $initializable; - private $skolemIri; + private $genId; /** * @var string[] @@ -147,7 +147,7 @@ public function __construct( ?array $builtinTypes = null, ?array $schema = null, ?bool $initializable = null, - ?bool $skolemIri = null, + ?bool $genId = null, $iris = null, @@ -177,7 +177,7 @@ public function __construct( $this->builtinTypes = $builtinTypes; $this->schema = $schema; $this->initializable = $initializable; - $this->skolemIri = $skolemIri; + $this->genId = $genId; $this->iris = $iris; $this->extraProperties = $extraProperties; } @@ -514,15 +514,15 @@ public function withIris($iris): self /** * Whether to generate a skolem iri on anonymous resources. */ - public function getSkolemIri() + public function getGenId() { - return $this->skolemIri; + return $this->genId; } - public function withSkolemIri($skolemIri): self + public function withGenId(bool $genId): self { $metadata = clone $this; - $metadata->skolemIri = $skolemIri; + $metadata->genId = $genId; return $metadata; } diff --git a/src/Metadata/Extractor/XmlPropertyExtractor.php b/src/Metadata/Extractor/XmlPropertyExtractor.php index f5fcabf023c..2da5d1dd5dc 100644 --- a/src/Metadata/Extractor/XmlPropertyExtractor.php +++ b/src/Metadata/Extractor/XmlPropertyExtractor.php @@ -71,6 +71,7 @@ protected function extractPath(string $path) 'initializable' => $this->phpize($property, 'initializable', 'bool'), 'extraProperties' => $this->buildExtraProperties($property, 'extraProperties'), 'iris' => $this->buildArrayValue($property, 'iri'), + 'genId' => $this->phpize($property, 'genId', 'bool'), ]; } } diff --git a/src/Metadata/Extractor/YamlPropertyExtractor.php b/src/Metadata/Extractor/YamlPropertyExtractor.php index ca14895a0e6..dd1231ca2aa 100644 --- a/src/Metadata/Extractor/YamlPropertyExtractor.php +++ b/src/Metadata/Extractor/YamlPropertyExtractor.php @@ -92,6 +92,7 @@ private function buildProperties(array $resourcesYaml): void 'example' => $propertyValues['example'] ?? null, 'builtinTypes' => $this->buildAttribute($propertyValues, 'builtinTypes'), 'schema' => $this->buildAttribute($propertyValues, 'schema'), + 'genId' => $this->phpize($propertyValues, 'genId', 'bool'), ]; } } diff --git a/src/Metadata/Extractor/schema/properties.xsd b/src/Metadata/Extractor/schema/properties.xsd index 6f74f9fdc95..f25266eba22 100644 --- a/src/Metadata/Extractor/schema/properties.xsd +++ b/src/Metadata/Extractor/schema/properties.xsd @@ -43,7 +43,7 @@ - + diff --git a/src/Serializer/AbstractItemNormalizer.php b/src/Serializer/AbstractItemNormalizer.php index 0a8c4b0c2ba..3fc9b19d7c0 100644 --- a/src/Serializer/AbstractItemNormalizer.php +++ b/src/Serializer/AbstractItemNormalizer.php @@ -833,9 +833,7 @@ protected function getAttributeValue($object, $attribute, $format = null, array if ($propertyMetadata instanceof PropertyMetadata) { $childContext['output']['iri'] = $propertyMetadata->getIri() ?? false; } else { - if (null !== ($propertyIris = $propertyMetadata->getIris())) { - $childContext['output']['iri'] = 1 === \count($propertyIris) ? ($propertyIris[0] ?? false) : $propertyIris; - } + $childContext['output']['gen_id'] = $propertyMetadata->getGenId() ?? false; } return $this->serializer->normalize($attributeValue, $format, $childContext); diff --git a/tests/Fixtures/TestBundle/Model/GenId.php b/tests/Fixtures/TestBundle/Model/GenId.php new file mode 100644 index 00000000000..58f40916dfd --- /dev/null +++ b/tests/Fixtures/TestBundle/Model/GenId.php @@ -0,0 +1,35 @@ + + * + * 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\Tests\Fixtures\TestBundle\Model; + +use ApiPlatform\Metadata\ApiProperty; +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\Operation; + +#[Get('/genids/{id}', provider: [GenId::class, 'getData'])] +class GenId +{ + #[ApiProperty(genId: true)] + public MonetaryAmount $totalPrice; + + public function __construct(public int $id) + { + $this->totalPrice = new MonetaryAmount(1000.01); + } + + public static function getData(Operation $operation, array $uriVariables = [], array $context = []): self + { + return new self($uriVariables['id']); + } +} diff --git a/tests/Fixtures/TestBundle/Model/MonetaryAmount.php b/tests/Fixtures/TestBundle/Model/MonetaryAmount.php new file mode 100644 index 00000000000..52adef125f1 --- /dev/null +++ b/tests/Fixtures/TestBundle/Model/MonetaryAmount.php @@ -0,0 +1,21 @@ + + * + * 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\Tests\Fixtures\TestBundle\Model; + +class MonetaryAmount +{ + public function __construct(public float $value = 0.0, public string $currency = 'EUR', public float $minValue = 0.0) + { + } +} diff --git a/tests/Hydra/Serializer/CollectionNormalizerTest.php b/tests/Hydra/Serializer/CollectionNormalizerTest.php index b394f295ec8..428fd791ba7 100644 --- a/tests/Hydra/Serializer/CollectionNormalizerTest.php +++ b/tests/Hydra/Serializer/CollectionNormalizerTest.php @@ -327,6 +327,7 @@ private function normalizePaginator($partial = false) 'jsonld_has_context' => true, 'api_sub_level' => true, 'resource_class' => 'Foo', + 'api_collection_sub_level' => true, ])->willReturn(['name' => 'Kévin', 'friend' => 'Smail']); $normalizer = new CollectionNormalizer($contextBuilder->reveal(), $resourceClassResolverProphecy->reveal(), $iriConvert->reveal()); diff --git a/tests/Metadata/Extractor/Adapter/XmlPropertyAdapter.php b/tests/Metadata/Extractor/Adapter/XmlPropertyAdapter.php index fc31e786d05..9aee0b5896f 100644 --- a/tests/Metadata/Extractor/Adapter/XmlPropertyAdapter.php +++ b/tests/Metadata/Extractor/Adapter/XmlPropertyAdapter.php @@ -42,7 +42,7 @@ final class XmlPropertyAdapter implements PropertyAdapterInterface 'securityPostDenormalize', 'initializable', 'iris', - 'skolemIri' + 'genId', ]; /** diff --git a/tests/Metadata/Extractor/PropertyMetadataCompatibilityTest.php b/tests/Metadata/Extractor/PropertyMetadataCompatibilityTest.php index 2c0502a8b1a..e310c802468 100644 --- a/tests/Metadata/Extractor/PropertyMetadataCompatibilityTest.php +++ b/tests/Metadata/Extractor/PropertyMetadataCompatibilityTest.php @@ -75,7 +75,7 @@ final class PropertyMetadataCompatibilityTest extends TestCase 'custom_property' => 'Lorem ipsum dolor sit amet', ], 'iris' => ['https://schema.org/totalPrice'], - 'skolemIri' => true + 'genId' => true, ]; /**