diff --git a/features/main/crud_uri_variables.feature b/features/main/crud_uri_variables.feature index c8122f9d28..279a85ba01 100644 --- a/features/main/crud_uri_variables.feature +++ b/features/main/crud_uri_variables.feature @@ -112,16 +112,22 @@ Feature: Uri Variables { "@id": "/companies/2/employees/2", "@type": "Employee", - "id": 2, "name": "foo2", - "company": "/companies/2" + "company": { + "@id": "/companies/2", + "@type": "Company", + "name": "Foo Company 2" + } }, { "@id": "/companies/2/employees/3", "@type": "Employee", - "id": 3, "name": "foo3", - "company": "/companies/2" + "company": { + "@id": "/companies/2", + "@type": "Company", + "name": "Foo Company 2" + } } ], "hydra:totalItems": 2 diff --git a/src/Core/GraphQl/Serializer/ItemNormalizer.php b/src/Core/GraphQl/Serializer/ItemNormalizer.php index 567af0a08a..f574cd8ff7 100644 --- a/src/Core/GraphQl/Serializer/ItemNormalizer.php +++ b/src/Core/GraphQl/Serializer/ItemNormalizer.php @@ -56,7 +56,13 @@ public function __construct(PropertyNameCollectionFactoryInterface $propertyName */ public function supportsNormalization($data, $format = null, array $context = []): bool { - return self::FORMAT === $format && parent::supportsNormalization($data, $format, $context); + if (!\is_object($data) || is_iterable($data)) { + return false; + } + + $class = $this->getObjectClass($data); + + return self::FORMAT === $format && $this->resourceClassResolver->isResourceClass($class); } /** diff --git a/src/JsonSchema/SchemaFactory.php b/src/JsonSchema/SchemaFactory.php index 0c6c51986a..81c7b82400 100644 --- a/src/JsonSchema/SchemaFactory.php +++ b/src/JsonSchema/SchemaFactory.php @@ -295,7 +295,7 @@ private function getMetadata(string $className, string $type = Schema::TYPE_OUTP } if (!$operation) { - $operation = $op; + $operation = new HttpOperation(); } } } diff --git a/src/Serializer/AbstractItemNormalizer.php b/src/Serializer/AbstractItemNormalizer.php index c51c29d50a..12734a5e28 100644 --- a/src/Serializer/AbstractItemNormalizer.php +++ b/src/Serializer/AbstractItemNormalizer.php @@ -202,14 +202,19 @@ public function normalize($object, $format = null, array $context = []) unset($context[self::IS_TRANSFORMED_TO_SAME_CLASS]); } + $iri = null; if ($this->resourceClassResolver->isResourceClass($resourceClass)) { $context = $this->initContext($resourceClass, $context); + + if ($this->iriConverter instanceof LegacyIriConverterInterface) { + $iri = $this->iriConverter->getIriFromItem($object); + } } if (isset($context['iri'])) { $iri = $context['iri']; - } else { - $iri = $this->iriConverter instanceof LegacyIriConverterInterface ? $this->iriConverter->getIriFromItem($object) : $this->iriConverter->getIriFromResource($object, UrlGeneratorInterface::ABS_URL, $context['operation'] ?? null, $context); + } elseif ($this->iriConverter instanceof IriConverterInterface) { + $iri = $this->iriConverter->getIriFromResource($object, UrlGeneratorInterface::ABS_URL, $context['operation'] ?? null, $context); } $context['iri'] = $iri; diff --git a/tests/Api/QueryParameterValidator/QueryParameterValidatorTest.php b/tests/Api/QueryParameterValidator/QueryParameterValidatorTest.php index c076153b81..361d6f3981 100644 --- a/tests/Api/QueryParameterValidator/QueryParameterValidatorTest.php +++ b/tests/Api/QueryParameterValidator/QueryParameterValidatorTest.php @@ -58,12 +58,14 @@ public function testOnKernelRequestWithUnsafeMethod() } /** - * If the tested filter is non-existant, then nothing should append. + * If the tested filter is non-existent, then nothing should append. */ public function testOnKernelRequestWithWrongFilter() { $request = []; + $this->filterLocatorProphecy->has('some_inexistent_filter')->willReturn(false); + $this->assertNull( $this->testedInstance->validateFilters(Dummy::class, ['some_inexistent_filter'], $request) ); diff --git a/tests/Fixtures/TestBundle/Document/Company.php b/tests/Fixtures/TestBundle/Document/Company.php index ea00a67ded..15f885f65a 100644 --- a/tests/Fixtures/TestBundle/Document/Company.php +++ b/tests/Fixtures/TestBundle/Document/Company.php @@ -15,14 +15,17 @@ use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; use ApiPlatform\Metadata\Link; use ApiPlatform\Metadata\Post; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; +use Symfony\Component\Serializer\Annotation\Groups; /** * @ODM\Document */ #[ApiResource] +#[GetCollection] #[Get] #[Post] #[ApiResource(uriTemplate: '/employees/{employeeId}/rooms/{roomId}/company/{companyId}', uriVariables: ['employeeId' => ['from_class' => Employee::class, 'from_property' => 'company']])] @@ -42,6 +45,7 @@ class Company * * @ODM\Field */ + #[Groups(['company_employees_read'])] public $name; /** @var Employee[] */ diff --git a/tests/Fixtures/TestBundle/Document/Employee.php b/tests/Fixtures/TestBundle/Document/Employee.php index 3ca2d5a499..f7923f8194 100644 --- a/tests/Fixtures/TestBundle/Document/Employee.php +++ b/tests/Fixtures/TestBundle/Document/Employee.php @@ -18,6 +18,7 @@ use ApiPlatform\Metadata\GetCollection; use ApiPlatform\Metadata\Post; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; +use Symfony\Component\Serializer\Annotation\Groups; /** * @ODM\Document @@ -26,7 +27,7 @@ #[Post] #[ApiResource(uriTemplate: '/companies/{companyId}/employees/{id}', uriVariables: ['companyId' => ['from_class' => Company::class, 'to_property' => 'company'], 'id' => ['from_class' => Employee::class]])] #[Get] -#[ApiResource(uriTemplate: '/companies/{companyId}/employees', uriVariables: ['companyId' => ['from_class' => Company::class, 'to_property' => 'company']])] +#[ApiResource(uriTemplate: '/companies/{companyId}/employees', uriVariables: ['companyId' => ['from_class' => Company::class, 'to_property' => 'company']], normalizationContext: ['groups' => ['company_employees_read']])] #[GetCollection] class Employee { @@ -42,6 +43,7 @@ class Employee * * @ODM\Field */ + #[Groups(['company_employees_read'])] public $name; /** @@ -49,6 +51,7 @@ class Employee * * @ODM\ReferenceOne(targetDocument=Company::class, storeAs="id") */ + #[Groups(['company_employees_read'])] public $company; public function getId() diff --git a/tests/Fixtures/TestBundle/Entity/Company.php b/tests/Fixtures/TestBundle/Entity/Company.php index 690797d433..f514c3dd57 100644 --- a/tests/Fixtures/TestBundle/Entity/Company.php +++ b/tests/Fixtures/TestBundle/Entity/Company.php @@ -15,14 +15,17 @@ use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\Get; +use ApiPlatform\Metadata\GetCollection; use ApiPlatform\Metadata\Link; use ApiPlatform\Metadata\Post; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation\Groups; /** * @ORM\Entity */ #[ApiResource] +#[GetCollection] #[Get] #[Post] #[ApiResource( @@ -54,6 +57,7 @@ class Company * * @ORM\Column */ + #[Groups(['company_employees_read'])] public string $name; /** @var Employee[] */ diff --git a/tests/Fixtures/TestBundle/Entity/Employee.php b/tests/Fixtures/TestBundle/Entity/Employee.php index ca970b747f..f9147ff87b 100644 --- a/tests/Fixtures/TestBundle/Entity/Employee.php +++ b/tests/Fixtures/TestBundle/Entity/Employee.php @@ -18,6 +18,7 @@ use ApiPlatform\Metadata\GetCollection; use ApiPlatform\Metadata\Post; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Serializer\Annotation\Groups; /** * @ORM\Entity @@ -36,7 +37,8 @@ uriTemplate: '/companies/{companyId}/employees', uriVariables: [ 'companyId' => ['from_class' => Company::class, 'to_property' => 'company'], - ] + ], + normalizationContext: ['groups' => ['company_employees_read']] )] #[GetCollection] class Employee @@ -55,11 +57,13 @@ class Employee * * @ORM\Column */ + #[Groups(['company_employees_read'])] public string $name; /** * @ORM\ManyToOne(targetEntity="ApiPlatform\Tests\Fixtures\TestBundle\Entity\Company") */ + #[Groups(['company_employees_read'])] public ?Company $company; public function getId() diff --git a/tests/Hydra/Serializer/CollectionFiltersNormalizerTest.php b/tests/Hydra/Serializer/CollectionFiltersNormalizerTest.php index a42f9ec0a4..f8fa29cbaf 100644 --- a/tests/Hydra/Serializer/CollectionFiltersNormalizerTest.php +++ b/tests/Hydra/Serializer/CollectionFiltersNormalizerTest.php @@ -239,11 +239,14 @@ public function testDoNothingIfNoRequestUri() $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class); $resourceClassResolverProphecy->getResourceClass($dummy, Dummy::class)->willReturn(Dummy::class); + $filterLocatorProphecy = $this->prophesize(ContainerInterface::class); + $filterLocatorProphecy->has('foo')->willReturn(false); + $normalizer = new CollectionFiltersNormalizer( $decoratedProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), $resourceClassResolverProphecy->reveal(), - $this->prophesize(ContainerInterface::class)->reveal() + $filterLocatorProphecy->reveal() ); $this->assertEquals(['name' => 'foo'], $normalizer->normalize($dummy, CollectionNormalizer::FORMAT, [ diff --git a/tests/JsonSchema/SchemaFactoryTest.php b/tests/JsonSchema/SchemaFactoryTest.php index d6d94f8fac..df4e4088ad 100644 --- a/tests/JsonSchema/SchemaFactoryTest.php +++ b/tests/JsonSchema/SchemaFactoryTest.php @@ -92,7 +92,7 @@ public function testBuildSchemaForNonResourceClass(): void $this->assertSame('example_bar', $definitions[$rootDefinitionKey]['properties']['bar']['example']); } - public function testBuildSchemaForOperationWithOverriddenSerializerGroups(): void + public function testBuildSchemaWithSerializerGroups(): void { $typeFactoryProphecy = $this->prophesize(TypeFactoryInterface::class); $typeFactoryProphecy->getType(Argument::allOf( @@ -115,30 +115,20 @@ public function testBuildSchemaForOperationWithOverriddenSerializerGroups(): voi ]) ); - $serializerGroup = 'overridden_operation_dummy_put'; - $validationGroups = 'validation_groups_dummy_put'; + $serializerGroup = 'custom_operation_dummy'; $propertyNameCollectionFactoryProphecy = $this->prophesize(PropertyNameCollectionFactoryInterface::class); - $propertyNameCollectionFactoryProphecy->create(OverriddenOperationDummy::class, Argument::allOf( - Argument::type('array'), - Argument::allOf(Argument::withEntry('serializer_groups', [$serializerGroup]), Argument::withEntry('validation_groups', [$validationGroups])) - ))->willReturn(new PropertyNameCollection(['alias', 'description'])); + $propertyNameCollectionFactoryProphecy->create(OverriddenOperationDummy::class, Argument::type('array'))->willReturn(new PropertyNameCollection(['alias', 'description'])); $propertyMetadataFactoryProphecy = $this->prophesize(PropertyMetadataFactoryInterface::class); - $propertyMetadataFactoryProphecy->create(OverriddenOperationDummy::class, 'alias', Argument::allOf( - Argument::type('array'), - Argument::allOf(Argument::withEntry('serializer_groups', [$serializerGroup]), Argument::withEntry('validation_groups', [$validationGroups])) - ))->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withReadable(true)); - $propertyMetadataFactoryProphecy->create(OverriddenOperationDummy::class, 'description', Argument::allOf( - Argument::type('array'), - Argument::allOf(Argument::withEntry('serializer_groups', [$serializerGroup]), Argument::withEntry('validation_groups', [$validationGroups])) - ))->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withReadable(true)); + $propertyMetadataFactoryProphecy->create(OverriddenOperationDummy::class, 'alias', Argument::type('array'))->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withReadable(true)); + $propertyMetadataFactoryProphecy->create(OverriddenOperationDummy::class, 'description', Argument::type('array'))->willReturn((new ApiProperty())->withBuiltinTypes([new Type(Type::BUILTIN_TYPE_STRING)])->withReadable(true)); $resourceClassResolverProphecy = $this->prophesize(ResourceClassResolverInterface::class); $resourceClassResolverProphecy->isResourceClass(OverriddenOperationDummy::class)->willReturn(true); $schemaFactory = new SchemaFactory($typeFactoryProphecy->reveal(), $resourceMetadataFactoryProphecy->reveal(), $propertyNameCollectionFactoryProphecy->reveal(), $propertyMetadataFactoryProphecy->reveal(), null, $resourceClassResolverProphecy->reveal()); - $resultSchema = $schemaFactory->buildSchema(OverriddenOperationDummy::class, 'json', Schema::TYPE_OUTPUT); + $resultSchema = $schemaFactory->buildSchema(OverriddenOperationDummy::class, 'json', Schema::TYPE_OUTPUT, null, null, ['groups' => $serializerGroup, AbstractNormalizer::ALLOW_EXTRA_ATTRIBUTES => false]); $rootDefinitionKey = $resultSchema->getRootDefinitionKey(); $definitions = $resultSchema->getDefinitions(); diff --git a/tests/Symfony/Bundle/DataCollector/RequestDataCollectorTest.php b/tests/Symfony/Bundle/DataCollector/RequestDataCollectorTest.php index d2a2692315..40d1948532 100644 --- a/tests/Symfony/Bundle/DataCollector/RequestDataCollectorTest.php +++ b/tests/Symfony/Bundle/DataCollector/RequestDataCollectorTest.php @@ -167,6 +167,9 @@ public function testWithResourceWithTraceables() { $this->apiResourceClassWillReturn(DummyEntity::class); + $this->filterLocator->has('a_filter')->willReturn(false); + $this->filterLocator->has('foo')->willReturn(false); + $dataCollector = new RequestDataCollector( $this->metadataFactory->reveal(), $this->filterLocator->reveal(), @@ -220,6 +223,9 @@ public function testVersionCollection() { $this->apiResourceClassWillReturn(DummyEntity::class); + $this->filterLocator->has('a_filter')->willReturn(false); + $this->filterLocator->has('foo')->willReturn(false); + $dataCollector = new RequestDataCollector( $this->metadataFactory->reveal(), $this->filterLocator->reveal(),