diff --git a/features/doctrine/messenger.feature b/features/doctrine/messenger.feature deleted file mode 100644 index 7abecfce529..00000000000 --- a/features/doctrine/messenger.feature +++ /dev/null @@ -1,51 +0,0 @@ -Feature: Combine messenger with doctrine - In order to persist and send a resource - As a client software developer - I need to configure the messenger ApiResource attribute properly - - Background: - Given I add "Accept" header equal to "application/ld+json" - And I add "Content-Type" header equal to "application/ld+json" - - @createSchema - Scenario: Using messenger="persist" should combine doctrine and messenger - When I send a "POST" request to "/messenger_with_persists" with body: - """ - { - "name": "test" - } - """ - 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" - And the JSON should be equal to: - """ - { - "@context": "/contexts/MessengerWithPersist", - "@id": "/messenger_with_persists/1", - "@type": "MessengerWithPersist", - "id": 1, - "name": "test" - } - """ - - Scenario: Using messenger={"persist", "input"} should combine doctrine, messenger and input DTO - When I send a "POST" request to "/messenger_with_arrays" with body: - """ - { - "var": "test" - } - """ - 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" - And the JSON should be equal to: - """ - { - "@context": "/contexts/MessengerWithArray", - "@id": "/messenger_with_arrays/1", - "@type": "MessengerWithArray", - "id": 1, - "name": "test" - } - """ diff --git a/src/Bridge/Symfony/Bundle/DataPersister/TraceableChainDataPersister.php b/src/Bridge/Symfony/Bundle/DataPersister/TraceableChainDataPersister.php index 7b3cbb23c33..801d12e82d7 100644 --- a/src/Bridge/Symfony/Bundle/DataPersister/TraceableChainDataPersister.php +++ b/src/Bridge/Symfony/Bundle/DataPersister/TraceableChainDataPersister.php @@ -16,6 +16,7 @@ use ApiPlatform\Core\DataPersister\ChainDataPersister; use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface; use ApiPlatform\Core\DataPersister\DataPersisterInterface; +use ApiPlatform\Core\DataPersister\ResumableDataPersisterInterface; /** * @author Anthony GRASSIOT @@ -52,9 +53,9 @@ public function supports($data, array $context = []): bool */ public function persist($data, array $context = []) { - if ($match = $this->tracePersisters($data, $context)) { - return $match->persist($data, $context) ?? $data; - } + $this->tracePersisters($data, $context); + + return $this->decorated->persist($data, $context); } /** @@ -62,22 +63,22 @@ public function persist($data, array $context = []) */ public function remove($data, array $context = []) { - if ($match = $this->tracePersisters($data, $context)) { - return $match->remove($data, $context); - } + $this->tracePersisters($data, $context); + + return $this->decorated->remove($data, $context); } private function tracePersisters($data, array $context = []) { - $match = null; + $found = false; foreach ($this->persisters as $persister) { - $this->persistersResponse[\get_class($persister)] = $match ? null : false; - if (!$match && $persister->supports($data, $context)) { - $match = $persister; - $this->persistersResponse[\get_class($persister)] = true; + if ( + ($this->persistersResponse[\get_class($persister)] = $found ? false : $persister->supports($data)) + && + !($persister instanceof ResumableDataPersisterInterface && $persister->resumable()) && !$found + ) { + $found = true; } } - - return $match; } } diff --git a/src/Bridge/Symfony/Bundle/Resources/config/messenger.xml b/src/Bridge/Symfony/Bundle/Resources/config/messenger.xml index 76a4c6d3b51..d50ec0e3d69 100644 --- a/src/Bridge/Symfony/Bundle/Resources/config/messenger.xml +++ b/src/Bridge/Symfony/Bundle/Resources/config/messenger.xml @@ -10,7 +10,6 @@ - diff --git a/src/Bridge/Symfony/Messenger/DataPersister.php b/src/Bridge/Symfony/Messenger/DataPersister.php index c7fb6536fe6..faebcad48e8 100644 --- a/src/Bridge/Symfony/Messenger/DataPersister.php +++ b/src/Bridge/Symfony/Messenger/DataPersister.php @@ -17,7 +17,6 @@ use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface; use ApiPlatform\Core\Exception\ResourceClassNotFoundException; use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface; -use ApiPlatform\Core\Metadata\Resource\ResourceMetadata; use ApiPlatform\Core\Util\ClassInfoTrait; use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\MessageBusInterface; @@ -36,13 +35,11 @@ final class DataPersister implements ContextAwareDataPersisterInterface use DispatchTrait; private $resourceMetadataFactory; - private $dataPersister; - public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, MessageBusInterface $messageBus, ContextAwareDataPersisterInterface $dataPersister) + public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, MessageBusInterface $messageBus) { $this->resourceMetadataFactory = $resourceMetadataFactory; $this->messageBus = $messageBus; - $this->dataPersister = $dataPersister; } /** @@ -50,17 +47,27 @@ public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFa */ public function supports($data, array $context = []): bool { - if (true === ($context['messenger_dispatched'] ?? false)) { - return false; - } - try { $resourceMetadata = $this->resourceMetadataFactory->create($context['resource_class'] ?? $this->getObjectClass($data)); } catch (ResourceClassNotFoundException $e) { return false; } - return false !== $this->getMessengerAttributeValue($resourceMetadata, $context); + if (null !== $operationName = $context['collection_operation_name'] ?? $context['item_operation_name'] ?? null) { + return false !== $resourceMetadata->getTypedOperationAttribute( + $context['collection_operation_name'] ?? false ? OperationType::COLLECTION : OperationType::ITEM, + $operationName, + 'messenger', + false, + true + ); + } + + if (isset($context['graphql_operation_name'])) { + return false !== $resourceMetadata->getGraphqlAttribute($context['graphql_operation_name'], 'messenger', false, true); + } + + return false !== $resourceMetadata->getAttribute('messenger', false); } /** @@ -68,10 +75,6 @@ public function supports($data, array $context = []): bool */ public function persist($data, array $context = []) { - if ($this->handOver($data, $context)) { - $data = $this->dataPersister->persist($data, $context + ['messenger_dispatched' => true]); - } - $envelope = $this->dispatch( (new Envelope($data)) ->with(new ContextStamp($context)) @@ -90,49 +93,9 @@ public function persist($data, array $context = []) */ public function remove($data, array $context = []) { - if ($this->handOver($data, $context)) { - $this->dataPersister->remove($data, $context + ['messenger_dispatched' => true]); - } - $this->dispatch( (new Envelope($data)) ->with(new RemoveStamp()) ); } - - /** - * Should this DataPersister hand over in "persist" mode? - */ - private function handOver($data, array $context = []): bool - { - try { - $value = $this->getMessengerAttributeValue($this->resourceMetadataFactory->create($context['resource_class'] ?? $this->getObjectClass($data)), $context); - } catch (ResourceClassNotFoundException $exception) { - return false; - } - - return 'persist' === $value || (\is_array($value) && (\in_array('persist', $value, true) || (true === $value['persist'] ?? false))); - } - - /** - * @return bool|string|array|null - */ - private function getMessengerAttributeValue(ResourceMetadata $resourceMetadata, array $context = []) - { - if (null !== $operationName = $context['collection_operation_name'] ?? $context['item_operation_name'] ?? null) { - return $resourceMetadata->getTypedOperationAttribute( - $context['collection_operation_name'] ?? false ? OperationType::COLLECTION : OperationType::ITEM, - $operationName, - 'messenger', - false, - true - ); - } - - if (isset($context['graphql_operation_name'])) { - return $resourceMetadata->getGraphqlAttribute($context['graphql_operation_name'], 'messenger', false, true); - } - - return $resourceMetadata->getAttribute('messenger', false); - } } diff --git a/src/Bridge/Symfony/Messenger/DataTransformer.php b/src/Bridge/Symfony/Messenger/DataTransformer.php index bdb01ab10c8..dac49c37d21 100644 --- a/src/Bridge/Symfony/Messenger/DataTransformer.php +++ b/src/Bridge/Symfony/Messenger/DataTransformer.php @@ -55,19 +55,19 @@ public function supportsTransformation($data, string $to, array $context = []): $metadata = $this->resourceMetadataFactory->create($context['resource_class'] ?? $to); if (isset($context['graphql_operation_name'])) { - $attribute = $metadata->getGraphqlAttribute($context['graphql_operation_name'], 'messenger', null, true); - } elseif (!isset($context['operation_type'])) { - $attribute = $metadata->getAttribute('messenger'); - } else { - $attribute = $metadata->getTypedOperationAttribute( - $context['operation_type'], - $context[$context['operation_type'].'_operation_name'] ?? '', - 'messenger', - null, - true - ); + return 'input' === $metadata->getGraphqlAttribute($context['graphql_operation_name'], 'messenger', null, true); } - return 'input' === $attribute || (\is_array($attribute) && \in_array('input', $attribute, true)); + if (!isset($context['operation_type'])) { + return 'input' === $metadata->getAttribute('messenger'); + } + + return 'input' === $metadata->getTypedOperationAttribute( + $context['operation_type'], + $context[$context['operation_type'].'_operation_name'] ?? '', + 'messenger', + null, + true + ); } } diff --git a/src/DataPersister/ChainDataPersister.php b/src/DataPersister/ChainDataPersister.php index 45781f5d848..1d56e5b1dab 100644 --- a/src/DataPersister/ChainDataPersister.php +++ b/src/DataPersister/ChainDataPersister.php @@ -56,9 +56,16 @@ public function persist($data, array $context = []) { foreach ($this->persisters as $persister) { if ($persister->supports($data, $context)) { - return $persister->persist($data, $context) ?? $data; + $data = $persister->persist($data, $context) ?? $data; + if ($persister instanceof ResumableDataPersisterInterface && $persister->resumable($context)) { + continue; + } + + return $data; } } + + return $data; } /** @@ -69,6 +76,9 @@ public function remove($data, array $context = []) foreach ($this->persisters as $persister) { if ($persister->supports($data, $context)) { $persister->remove($data, $context); + if ($persister instanceof ResumableDataPersisterInterface && $persister->resumable($context)) { + continue; + } return; } diff --git a/src/DataPersister/ResumableDataPersisterInterface.php b/src/DataPersister/ResumableDataPersisterInterface.php new file mode 100644 index 00000000000..abfc39c1f84 --- /dev/null +++ b/src/DataPersister/ResumableDataPersisterInterface.php @@ -0,0 +1,26 @@ + + * + * 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\DataPersister; + +/** + * Control the resumability of the data persister chain. + */ +interface ResumableDataPersisterInterface +{ + /** + * Should we continue calling the next DataPersister or stop after this one? + * Defaults to stop the ChainDatapersister if this interface is not implemented. + */ + public function resumable(array $context = []): bool; +} diff --git a/tests/Bridge/Symfony/Bundle/DataPersister/TraceableChainDataPersisterTest.php b/tests/Bridge/Symfony/Bundle/DataPersister/TraceableChainDataPersisterTest.php index ab7b0d2b819..5388e8ea15d 100644 --- a/tests/Bridge/Symfony/Bundle/DataPersister/TraceableChainDataPersisterTest.php +++ b/tests/Bridge/Symfony/Bundle/DataPersister/TraceableChainDataPersisterTest.php @@ -16,6 +16,7 @@ use ApiPlatform\Core\Bridge\Symfony\Bundle\DataPersister\TraceableChainDataPersister; use ApiPlatform\Core\DataPersister\ChainDataPersister; use ApiPlatform\Core\DataPersister\DataPersisterInterface; +use ApiPlatform\Core\DataPersister\ResumableDataPersisterInterface; use PHPUnit\Framework\TestCase; /** @@ -111,7 +112,60 @@ public function remove($data) } }, ]), - [false, true, null], + [false, true, false], + ]; + + yield [ + new ChainDataPersister([ + new class() implements DataPersisterInterface, ResumableDataPersisterInterface { + public function supports($data): bool + { + return true; + } + + public function resumable(array $context = []): bool + { + return true; + } + + public function persist($data) + { + } + + public function remove($data) + { + } + }, + new class() implements DataPersisterInterface { + public function supports($data): bool + { + return false; + } + + public function persist($data) + { + } + + public function remove($data) + { + } + }, + new class() implements DataPersisterInterface { + public function supports($data): bool + { + return true; + } + + public function persist($data) + { + } + + public function remove($data) + { + } + }, + ]), + [true, false, true], ]; } } diff --git a/tests/Bridge/Symfony/Messenger/DataPersisterTest.php b/tests/Bridge/Symfony/Messenger/DataPersisterTest.php index 30b9a9110d0..7f196e4f293 100644 --- a/tests/Bridge/Symfony/Messenger/DataPersisterTest.php +++ b/tests/Bridge/Symfony/Messenger/DataPersisterTest.php @@ -16,7 +16,6 @@ use ApiPlatform\Core\Bridge\Symfony\Messenger\ContextStamp; use ApiPlatform\Core\Bridge\Symfony\Messenger\DataPersister; use ApiPlatform\Core\Bridge\Symfony\Messenger\RemoveStamp; -use ApiPlatform\Core\DataPersister\ContextAwareDataPersisterInterface; use ApiPlatform\Core\Exception\ResourceClassNotFoundException; use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface; use ApiPlatform\Core\Metadata\Resource\ResourceMetadata; @@ -27,6 +26,7 @@ use Prophecy\Argument; use Symfony\Component\Messenger\Envelope; use Symfony\Component\Messenger\MessageBusInterface; +use Symfony\Component\Messenger\Stamp\HandledStamp; /** * @author Kévin Dunglas @@ -40,7 +40,7 @@ public function testSupport() $metadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class); $metadataFactoryProphecy->create(Dummy::class)->willReturn(new ResourceMetadata(null, null, null, null, null, ['messenger' => true])); - $dataPersister = new DataPersister($metadataFactoryProphecy->reveal(), $this->prophesize(MessageBusInterface::class)->reveal(), $this->prophesize(ContextAwareDataPersisterInterface::class)->reveal()); + $dataPersister = new DataPersister($metadataFactoryProphecy->reveal(), $this->prophesize(MessageBusInterface::class)->reveal()); $this->assertTrue($dataPersister->supports(new Dummy())); } @@ -50,39 +50,21 @@ public function testSupportWithContext() $metadataFactoryProphecy->create(Dummy::class)->willReturn(new ResourceMetadata(null, null, null, null, null, ['messenger' => true])); $metadataFactoryProphecy->create(DummyCar::class)->willThrow(new ResourceClassNotFoundException()); - $dataPersister = new DataPersister($metadataFactoryProphecy->reveal(), $this->prophesize(MessageBusInterface::class)->reveal(), $this->prophesize(ContextAwareDataPersisterInterface::class)->reveal()); + $dataPersister = new DataPersister($metadataFactoryProphecy->reveal(), $this->prophesize(MessageBusInterface::class)->reveal()); $this->assertTrue($dataPersister->supports(new DummyCar(), ['resource_class' => Dummy::class])); $this->assertFalse($dataPersister->supports(new DummyCar())); } - public function testSupportWithContextAndMessengerDispatched() - { - $metadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class); - $metadataFactoryProphecy->create(Dummy::class)->willReturn(new ResourceMetadata(null, null, null, null, null, ['messenger' => true])); - $metadataFactoryProphecy->create(DummyCar::class)->willThrow(new ResourceClassNotFoundException()); - - $dataPersister = new DataPersister($metadataFactoryProphecy->reveal(), $this->prophesize(MessageBusInterface::class)->reveal(), $this->prophesize(ContextAwareDataPersisterInterface::class)->reveal()); - $this->assertFalse($dataPersister->supports(new DummyCar(), ['resource_class' => Dummy::class, 'messenger_dispatched' => true])); - } - public function testPersist() { $dummy = new Dummy(); - $metadataFactory = $this->prophesize(ResourceMetadataFactoryInterface::class); - $metadataFactory->create(Dummy::class)->willReturn(new ResourceMetadata(null, null, null, null, null, ['messenger' => true])); - $metadataFactory->create(Dummy::class)->willReturn(new ResourceMetadata(null, null, null, null, null, ['messenger' => 'input'])); - $metadataFactory->create(DummyCar::class)->willThrow(new ResourceClassNotFoundException()); - - $chainDataPersister = $this->prophesize(ContextAwareDataPersisterInterface::class); - $chainDataPersister->persist($dummy, ['messenger_dispatched' => true])->shouldNotBeCalled(); - $messageBus = $this->prophesize(MessageBusInterface::class); $messageBus->dispatch(Argument::that(function (Envelope $envelope) use ($dummy) { return $dummy === $envelope->getMessage() && null !== $envelope->last(ContextStamp::class); }))->willReturn(new Envelope($dummy))->shouldBeCalled(); - $dataPersister = new DataPersister($metadataFactory->reveal(), $messageBus->reveal(), $chainDataPersister->reveal()); + $dataPersister = new DataPersister($this->prophesize(ResourceMetadataFactoryInterface::class)->reveal(), $messageBus->reveal()); $this->assertSame($dummy, $dataPersister->persist($dummy)); } @@ -90,92 +72,35 @@ public function testRemove() { $dummy = new Dummy(); - $metadataFactory = $this->prophesize(ResourceMetadataFactoryInterface::class); - $metadataFactory->create(Dummy::class)->willReturn(new ResourceMetadata(null, null, null, null, null, ['messenger' => true])); - $metadataFactory->create(Dummy::class)->willReturn(new ResourceMetadata(null, null, null, null, null, ['messenger' => 'input'])); - $metadataFactory->create(DummyCar::class)->willThrow(new ResourceClassNotFoundException()); - - $chainDataPersister = $this->prophesize(ContextAwareDataPersisterInterface::class); - $chainDataPersister->remove($dummy, ['messenger_dispatched' => true])->shouldNotBeCalled(); - $messageBus = $this->prophesize(MessageBusInterface::class); + $messageBus->dispatch(Argument::that(function (Envelope $envelope) use ($dummy) { return $dummy === $envelope->getMessage() && null !== $envelope->last(RemoveStamp::class); }))->willReturn(new Envelope($dummy))->shouldBeCalled(); - $dataPersister = new DataPersister($metadataFactory->reveal(), $messageBus->reveal(), $chainDataPersister->reveal()); + $dataPersister = new DataPersister($this->prophesize(ResourceMetadataFactoryInterface::class)->reveal(), $messageBus->reveal()); $dataPersister->remove($dummy); } - public function testPersistWithHandOver() + public function testHandle() { $dummy = new Dummy(); - $metadataFactory = $this->prophesize(ResourceMetadataFactoryInterface::class); - $metadataFactory->create(Dummy::class)->willReturn(new ResourceMetadata(null, null, null, null, null, ['messenger' => 'persist'])); - $metadataFactory->create(Dummy::class)->willReturn(new ResourceMetadata(null, null, null, null, null, ['messenger' => ['persist', 'input']])); - $metadataFactory->create(DummyCar::class)->willThrow(new ResourceClassNotFoundException()); - - $chainDataPersister = $this->prophesize(ContextAwareDataPersisterInterface::class); - $chainDataPersister->persist($dummy, ['messenger_dispatched' => true])->willReturn($dummy)->shouldBeCalled(); - $messageBus = $this->prophesize(MessageBusInterface::class); $messageBus->dispatch(Argument::that(function (Envelope $envelope) use ($dummy) { return $dummy === $envelope->getMessage() && null !== $envelope->last(ContextStamp::class); - }))->willReturn(new Envelope($dummy))->shouldBeCalled(); + }))->willReturn((new Envelope($dummy))->with(new HandledStamp($dummy, 'DummyHandler::__invoke')))->shouldBeCalled(); - $dataPersister = new DataPersister($metadataFactory->reveal(), $messageBus->reveal(), $chainDataPersister->reveal()); + $dataPersister = new DataPersister($this->prophesize(ResourceMetadataFactoryInterface::class)->reveal(), $messageBus->reveal()); $this->assertSame($dummy, $dataPersister->persist($dummy)); } - public function testPersistWithExceptionOnHandOver() - { - $dummy = new Dummy(); - - $metadataFactory = $this->prophesize(ResourceMetadataFactoryInterface::class); - $metadataFactory->create(DummyCar::class)->willThrow(new ResourceClassNotFoundException()); - $metadataFactory->create(Dummy::class)->willThrow(new ResourceClassNotFoundException()); - $metadataFactory->create(Dummy::class)->willThrow(new ResourceClassNotFoundException()); - - $chainDataPersister = $this->prophesize(ContextAwareDataPersisterInterface::class); - $chainDataPersister->persist(Argument::any(), Argument::any())->shouldNotBeCalled(); - - $messageBus = $this->prophesize(MessageBusInterface::class); - $messageBus->dispatch(Argument::that(function (Envelope $envelope) use ($dummy) { - return $dummy === $envelope->getMessage() && null !== $envelope->last(ContextStamp::class); - }))->willReturn(new Envelope($dummy))->shouldBeCalled(); - - $dataPersister = new DataPersister($metadataFactory->reveal(), $messageBus->reveal(), $chainDataPersister->reveal()); - $this->assertSame($dummy, $dataPersister->persist($dummy)); - } - - public function testRemoveWithHandOver() - { - $dummy = new Dummy(); - - $metadataFactory = $this->prophesize(ResourceMetadataFactoryInterface::class); - $metadataFactory->create(Dummy::class)->willReturn(new ResourceMetadata(null, null, null, null, null, ['messenger' => 'persist'])); - $metadataFactory->create(Dummy::class)->willReturn(new ResourceMetadata(null, null, null, null, null, ['messenger' => ['persist', 'input']])); - $metadataFactory->create(DummyCar::class)->willThrow(new ResourceClassNotFoundException()); - - $chainDataPersister = $this->prophesize(ContextAwareDataPersisterInterface::class); - $chainDataPersister->remove($dummy, ['messenger_dispatched' => true])->shouldBeCalled(); - - $messageBus = $this->prophesize(MessageBusInterface::class); - $messageBus->dispatch(Argument::that(function (Envelope $envelope) use ($dummy) { - return $dummy === $envelope->getMessage() && null !== $envelope->last(RemoveStamp::class); - }))->willReturn(new Envelope($dummy))->shouldBeCalled(); - - $dataPersister = new DataPersister($metadataFactory->reveal(), $messageBus->reveal(), $chainDataPersister->reveal()); - $dataPersister->remove($dummy); - } - public function testSupportWithGraphqlContext() { $metadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class); $metadataFactoryProphecy->create(Dummy::class)->willReturn((new ResourceMetadata(null, null, null, null, null, []))->withGraphQl(['create' => ['messenger' => 'input']])); - $dataPersister = new DataPersister($metadataFactoryProphecy->reveal(), $this->prophesize(MessageBusInterface::class)->reveal(), $this->prophesize(ContextAwareDataPersisterInterface::class)->reveal()); + $dataPersister = new DataPersister($metadataFactoryProphecy->reveal(), $this->prophesize(MessageBusInterface::class)->reveal()); $this->assertTrue($dataPersister->supports(new DummyCar(), ['resource_class' => Dummy::class, 'graphql_operation_name' => 'create'])); } } diff --git a/tests/Bridge/Symfony/Messenger/DataTransformerTest.php b/tests/Bridge/Symfony/Messenger/DataTransformerTest.php index 82b529d9188..13e7819130d 100644 --- a/tests/Bridge/Symfony/Messenger/DataTransformerTest.php +++ b/tests/Bridge/Symfony/Messenger/DataTransformerTest.php @@ -28,9 +28,6 @@ class DataTransformerTest extends TestCase { use ProphecyTrait; - /** - * @dataProvider getMessengerAttribute - */ public function testSupport() { $metadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class); @@ -40,9 +37,6 @@ public function testSupport() $this->assertTrue($dataTransformer->supportsTransformation([], Dummy::class, ['input' => ['class' => 'smth']])); } - /** - * @dataProvider getMessengerAttribute - */ public function testSupportWithinRequest() { $metadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class); @@ -70,9 +64,6 @@ public function testNoSupportWithinRequest() $this->assertFalse($dataTransformer->supportsTransformation([], Dummy::class, ['input' => ['class' => 'smth'], 'operation_type' => OperationType::ITEM, 'item_operation_name' => 'foo'])); } - /** - * @dataProvider getMessengerAttribute - */ public function testNoSupportWithoutInput() { $metadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class); @@ -82,9 +73,6 @@ public function testNoSupportWithoutInput() $this->assertFalse($dataTransformer->supportsTransformation([], Dummy::class, [])); } - /** - * @dataProvider getMessengerAttribute - */ public function testNoSupportWithObject() { $metadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class); @@ -102,9 +90,6 @@ public function testTransform() $this->assertSame($dummy, $dataTransformer->transform($dummy, Dummy::class)); } - /** - * @dataProvider getMessengerAttribute - */ public function testSupportWithGraphqlContext() { $metadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class); @@ -112,15 +97,4 @@ public function testSupportWithGraphqlContext() $dataTransformer = new DataTransformer($metadataFactoryProphecy->reveal()); $this->assertTrue($dataTransformer->supportsTransformation([], Dummy::class, ['input' => ['class' => 'smth'], 'graphql_operation_name' => 'create'])); } - - public function getMessengerAttribute() - { - yield [ - 'input', - ]; - - yield [ - ['persist', 'input'], - ]; - } } diff --git a/tests/DataPersister/ChainDataPersisterTest.php b/tests/DataPersister/ChainDataPersisterTest.php index da250f93dba..fb8f73193e9 100644 --- a/tests/DataPersister/ChainDataPersisterTest.php +++ b/tests/DataPersister/ChainDataPersisterTest.php @@ -15,6 +15,7 @@ use ApiPlatform\Core\DataPersister\ChainDataPersister; use ApiPlatform\Core\DataPersister\DataPersisterInterface; +use ApiPlatform\Core\DataPersister\ResumableDataPersisterInterface; use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Dummy; use ApiPlatform\Core\Tests\ProphecyTrait; use PHPUnit\Framework\TestCase; @@ -89,4 +90,33 @@ public function testRemove() (new ChainDataPersister([$fooPersisterProphecy->reveal(), $barPersisterProphecy->reveal(), $foobarPersisterProphecy->reveal()]))->remove($dummy); } + + public function testResumable() + { + $dummy = new Dummy(); + $fooPersisterProphecy = $this->prophesize(DataPersisterInterface::class); + $fooPersisterProphecy->willImplement(ResumableDataPersisterInterface::class); + $fooPersisterProphecy->supports($dummy, Argument::type('array'))->willReturn(true)->shouldBeCalled(); + $fooPersisterProphecy->persist($dummy, Argument::type('array'))->shouldBeCalled(); + $fooPersisterProphecy->remove($dummy, Argument::type('array'))->shouldBeCalled(); + $fooPersisterProphecy->resumable(Argument::type('array'))->willReturn(true)->shouldBeCalled(); + + $foo2PersisterProphecy = $this->prophesize(DataPersisterInterface::class); + $foo2PersisterProphecy->supports($dummy, Argument::type('array'))->willReturn(false)->shouldBeCalled(); + $foo2PersisterProphecy->persist($dummy, Argument::type('array'))->shouldNotBeCalled(); + $foo2PersisterProphecy->remove($dummy, Argument::type('array'))->shouldNotBeCalled(); + + $barPersisterProphecy = $this->prophesize(DataPersisterInterface::class); + $barPersisterProphecy->supports($dummy, Argument::type('array'))->willReturn(true)->shouldBeCalled(); + $barPersisterProphecy->persist($dummy, Argument::type('array'))->shouldBeCalled(); + $barPersisterProphecy->remove($dummy, Argument::type('array'))->shouldBeCalled(); + + $foobarPersisterProphecy = $this->prophesize(DataPersisterInterface::class); + $foobarPersisterProphecy->supports($dummy, Argument::type('array'))->shouldNotBeCalled(); + $foobarPersisterProphecy->persist($dummy, Argument::type('array'))->shouldNotBeCalled(); + $foobarPersisterProphecy->remove($dummy, Argument::type('array'))->shouldNotBeCalled(); + + (new ChainDataPersister([$fooPersisterProphecy->reveal(), $foo2PersisterProphecy->reveal(), $barPersisterProphecy->reveal(), $foobarPersisterProphecy->reveal()]))->persist($dummy); + (new ChainDataPersister([$fooPersisterProphecy->reveal(), $foo2PersisterProphecy->reveal(), $barPersisterProphecy->reveal(), $foobarPersisterProphecy->reveal()]))->remove($dummy); + } } diff --git a/tests/Fixtures/TestBundle/DataTransformer/MessengerWithArrayDtoDataTransformer.php b/tests/Fixtures/TestBundle/DataTransformer/MessengerWithArrayDtoDataTransformer.php deleted file mode 100644 index 0e3f497f981..00000000000 --- a/tests/Fixtures/TestBundle/DataTransformer/MessengerWithArrayDtoDataTransformer.php +++ /dev/null @@ -1,45 +0,0 @@ - - * - * 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\Fixtures\TestBundle\DataTransformer; - -use ApiPlatform\Core\DataTransformer\DataTransformerInterface; -use ApiPlatform\Core\Serializer\AbstractItemNormalizer; -use ApiPlatform\Core\Tests\Fixtures\TestBundle\Document\MessengerWithArray as MessengerWithArrayDocument; -use ApiPlatform\Core\Tests\Fixtures\TestBundle\Dto\MessengerInput; -use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\MessengerWithArray as MessengerWithArrayEntity; - -final class MessengerWithArrayDtoDataTransformer implements DataTransformerInterface -{ - /** - * {@inheritdoc} - */ - public function transform($object, string $to, array $context = []) - { - /** @var MessengerInput */ - $data = $object; - - $resourceObject = $context[AbstractItemNormalizer::OBJECT_TO_POPULATE] ?? new $context['resource_class'](); - $resourceObject->name = $data->var; - - return $resourceObject; - } - - /** - * {@inheritdoc} - */ - public function supportsTransformation($object, string $to, array $context = []): bool - { - return \in_array($to, [MessengerWithArrayEntity::class, MessengerWithArrayDocument::class], true) && null !== ($context['input']['class'] ?? null); - } -} diff --git a/tests/Fixtures/TestBundle/Document/MessengerWithArray.php b/tests/Fixtures/TestBundle/Document/MessengerWithArray.php deleted file mode 100644 index 81dc5482192..00000000000 --- a/tests/Fixtures/TestBundle/Document/MessengerWithArray.php +++ /dev/null @@ -1,42 +0,0 @@ - - * - * 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\Fixtures\TestBundle\Document; - -use ApiPlatform\Core\Annotation\ApiProperty; -use ApiPlatform\Core\Annotation\ApiResource; -use ApiPlatform\Core\Tests\Fixtures\TestBundle\Dto\MessengerInput; -use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; - -/** - * @ApiResource(messenger={"persist", "input"}, input=MessengerInput::class) - * @ODM\Document - */ -class MessengerWithArray -{ - /** - * @var int|null - * - * @ApiProperty(identifier=true) - * - * @ODM\Id(strategy="INCREMENT", type="integer") - */ - public $id; - - /** - * @var string|null - * - * @ODM\Field - */ - public $name; -} diff --git a/tests/Fixtures/TestBundle/Document/MessengerWithPersist.php b/tests/Fixtures/TestBundle/Document/MessengerWithPersist.php deleted file mode 100644 index bc8b317cc60..00000000000 --- a/tests/Fixtures/TestBundle/Document/MessengerWithPersist.php +++ /dev/null @@ -1,41 +0,0 @@ - - * - * 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\Fixtures\TestBundle\Document; - -use ApiPlatform\Core\Annotation\ApiProperty; -use ApiPlatform\Core\Annotation\ApiResource; -use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; - -/** - * @ApiResource(messenger="persist") - * @ODM\Document - */ -class MessengerWithPersist -{ - /** - * @var int|null - * - * @ApiProperty(identifier=true) - * - * @ODM\Id(strategy="INCREMENT", type="integer") - */ - public $id; - - /** - * @var string|null - * - * @ODM\Field - */ - public $name; -} diff --git a/tests/Fixtures/TestBundle/Entity/MessengerWithArray.php b/tests/Fixtures/TestBundle/Entity/MessengerWithArray.php deleted file mode 100644 index 44035beb686..00000000000 --- a/tests/Fixtures/TestBundle/Entity/MessengerWithArray.php +++ /dev/null @@ -1,44 +0,0 @@ - - * - * 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\Fixtures\TestBundle\Entity; - -use ApiPlatform\Core\Annotation\ApiProperty; -use ApiPlatform\Core\Annotation\ApiResource; -use ApiPlatform\Core\Tests\Fixtures\TestBundle\Dto\MessengerInput; -use Doctrine\ORM\Mapping as ORM; - -/** - * @ApiResource(messenger={"persist", "input"}, input=MessengerInput::class) - * @ORM\Entity - */ -class MessengerWithArray -{ - /** - * @var int|null - * - * @ApiProperty(identifier=true) - * - * @ORM\Column(type="integer") - * @ORM\Id - * @ORM\GeneratedValue(strategy="AUTO") - */ - public $id; - - /** - * @var string|null - * - * @ORM\Column(type="string") - */ - public $name; -} diff --git a/tests/Fixtures/TestBundle/Entity/MessengerWithPersist.php b/tests/Fixtures/TestBundle/Entity/MessengerWithPersist.php deleted file mode 100644 index b0c1716d10c..00000000000 --- a/tests/Fixtures/TestBundle/Entity/MessengerWithPersist.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * 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\Fixtures\TestBundle\Entity; - -use ApiPlatform\Core\Annotation\ApiProperty; -use ApiPlatform\Core\Annotation\ApiResource; -use Doctrine\ORM\Mapping as ORM; - -/** - * @ApiResource(messenger="persist") - * @ORM\Entity - */ -class MessengerWithPersist -{ - /** - * @var int|null - * - * @ApiProperty(identifier=true) - * - * @ORM\Column(type="integer") - * @ORM\Id - * @ORM\GeneratedValue(strategy="AUTO") - */ - public $id; - - /** - * @var string|null - * - * @ORM\Column(type="string") - */ - public $name; -} diff --git a/tests/Fixtures/app/config/config_common.yml b/tests/Fixtures/app/config/config_common.yml index 210ecdfad87..4bf5207f6b7 100644 --- a/tests/Fixtures/app/config/config_common.yml +++ b/tests/Fixtures/app/config/config_common.yml @@ -262,12 +262,6 @@ services: tags: - { name: 'api_platform.data_transformer' } - app.data_transformer.messenger_with_array_dto: - class: 'ApiPlatform\Core\Tests\Fixtures\TestBundle\DataTransformer\MessengerWithArrayDtoDataTransformer' - public: false - tags: - - { name: 'api_platform.data_transformer' } - app.data_transformer.custom_output_dto_fallback_same_class: class: 'ApiPlatform\Core\Tests\Fixtures\TestBundle\DataTransformer\OutputDtoSameClassTransformer' public: false