From dd9499ec7a58ac058b52c8258904c2cc6c812c79 Mon Sep 17 00:00:00 2001 From: soyuka Date: Thu, 9 Mar 2023 20:41:32 +0100 Subject: [PATCH 1/2] fix(serializer): find parent class operation fixes #5438 --- features/jsonld/inheritance.feature | 12 ++++++ src/Serializer/AbstractItemNormalizer.php | 1 + .../ApiResource/Issue5438/Contractor.php | 33 ++++++++++++++ .../ApiResource/Issue5438/Employee.php | 33 ++++++++++++++ .../ApiResource/Issue5438/Person.php | 43 +++++++++++++++++++ 5 files changed, 122 insertions(+) create mode 100644 features/jsonld/inheritance.feature create mode 100644 tests/Fixtures/TestBundle/ApiResource/Issue5438/Contractor.php create mode 100644 tests/Fixtures/TestBundle/ApiResource/Issue5438/Employee.php create mode 100644 tests/Fixtures/TestBundle/ApiResource/Issue5438/Person.php diff --git a/features/jsonld/inheritance.feature b/features/jsonld/inheritance.feature new file mode 100644 index 00000000000..0008392cf54 --- /dev/null +++ b/features/jsonld/inheritance.feature @@ -0,0 +1,12 @@ +Feature: Inheritance with correct IRIs + In order to fix (https://github.com/api-platform/core/issues/5438) + + Scenario: Get the collection of people with its employees + When I add "Accept" header equal to "application/json" + And I send a "GET" request to "/people_5438" + Then print last JSON response + + Scenario: Get the collection of people with its employees + When I add "Accept" header equal to "application/ld+json" + And I send a "GET" request to "/people_5438" + Then print last JSON response diff --git a/src/Serializer/AbstractItemNormalizer.php b/src/Serializer/AbstractItemNormalizer.php index b2d80c1b26c..525ec0702d2 100644 --- a/src/Serializer/AbstractItemNormalizer.php +++ b/src/Serializer/AbstractItemNormalizer.php @@ -116,6 +116,7 @@ public function normalize(mixed $object, string $format = null, array $context = } if (isset($context['operation']) && $context['operation'] instanceof CollectionOperationInterface) { + unset($context['operation_name']); unset($context['operation']); unset($context['iri']); } diff --git a/tests/Fixtures/TestBundle/ApiResource/Issue5438/Contractor.php b/tests/Fixtures/TestBundle/ApiResource/Issue5438/Contractor.php new file mode 100644 index 00000000000..429116390a6 --- /dev/null +++ b/tests/Fixtures/TestBundle/ApiResource/Issue5438/Contractor.php @@ -0,0 +1,33 @@ + + * + * 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\ApiResource\Issue5438; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\Operation; + +#[Get( + shortName: 'Contractor5438', + uriTemplate: 'contractor_5438/{id}', + provider: [Contractor::class, 'getContractor'], +)] +class Contractor extends Person +{ + public static function getContractor(Operation $operation, array $uriVariables = []): self + { + return new self( + $uriVariables['id'], + 'a' + ); + } +} diff --git a/tests/Fixtures/TestBundle/ApiResource/Issue5438/Employee.php b/tests/Fixtures/TestBundle/ApiResource/Issue5438/Employee.php new file mode 100644 index 00000000000..ba56d584e30 --- /dev/null +++ b/tests/Fixtures/TestBundle/ApiResource/Issue5438/Employee.php @@ -0,0 +1,33 @@ + + * + * 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\ApiResource\Issue5438; + +use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\Operation; + +#[Get( + shortName: 'Employee5438', + uriTemplate: 'employee_5438/{id}', + provider: [Employee::class, 'getEmployee'] +)] +class Employee extends Person +{ + public static function getEmployee(Operation $operation, array $uriVariables = []): self + { + return new self( + $uriVariables['id'], + 'a' + ); + } +} diff --git a/tests/Fixtures/TestBundle/ApiResource/Issue5438/Person.php b/tests/Fixtures/TestBundle/ApiResource/Issue5438/Person.php new file mode 100644 index 00000000000..0b11e94d42b --- /dev/null +++ b/tests/Fixtures/TestBundle/ApiResource/Issue5438/Person.php @@ -0,0 +1,43 @@ + + * + * 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\ApiResource\Issue5438; + +use ApiPlatform\Metadata\GetCollection; +use ApiPlatform\Metadata\Operation; + +#[GetCollection( + shortName: 'People5438', + uriTemplate: 'people_5438', + provider: [Person::class, 'getData'] +)] +abstract class Person +{ + public function __construct(public readonly ?int $id = null, public readonly ?string $name = null) + { + } + + public static function getData(Operation $operation, array $uriVariables = []): iterable + { + return [ + new Contractor( + 1, + 'a' + ), + new Employee( + 2, + 'b' + ), + ]; + } +} From 124428241ef2ad9572b8097dd94f16d6a1dd04d8 Mon Sep 17 00:00:00 2001 From: soyuka Date: Thu, 9 Mar 2023 21:25:18 +0100 Subject: [PATCH 2/2] fix(metadata): default graphql operations on ApiResource only --- .../AttributesResourceMetadataCollectionFactory.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Metadata/Resource/Factory/AttributesResourceMetadataCollectionFactory.php b/src/Metadata/Resource/Factory/AttributesResourceMetadataCollectionFactory.php index 7c839030cd1..6bdeb4df85e 100644 --- a/src/Metadata/Resource/Factory/AttributesResourceMetadataCollectionFactory.php +++ b/src/Metadata/Resource/Factory/AttributesResourceMetadataCollectionFactory.php @@ -82,9 +82,11 @@ private function buildResourceOperations(array $attributes, string $resourceClas $resources = []; $index = -1; $operationPriority = 0; + $hasApiResource = false; foreach ($attributes as $attribute) { if (is_a($attribute->getName(), ApiResource::class, true)) { + $hasApiResource = true; $resource = $this->getResourceWithDefaults($resourceClass, $shortName, $attribute->newInstance()); $operations = []; foreach ($resource->getOperations() ?? new Operations() as $operation) { @@ -140,6 +142,11 @@ private function buildResourceOperations(array $attributes, string $resourceClas } if (null === $graphQlOperations) { + if (!$hasApiResource) { + $resources[$index] = $resources[$index]->withGraphQlOperations([]); + continue; + } + // Add default GraphQL operations on the first resource if (0 === $index) { $resources[$index] = $this->addDefaultGraphQlOperations($resources[$index]);