Skip to content

Commit 378904c

Browse files
authored
Provides an integration with the Symfony Messenger component (#2398)
1 parent 3e12b8d commit 378904c

File tree

13 files changed

+239
-13
lines changed

13 files changed

+239
-13
lines changed

composer.json

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"behat/symfony2-extension": "^2.1.1",
3434
"behatch/contexts": "3.1.0",
3535
"doctrine/annotations": "^1.2",
36+
"doctrine/doctrine-cache-bundle": "^1.3.5",
3637
"doctrine/doctrine-bundle": "^1.8",
3738
"doctrine/orm": "^2.6.3",
3839
"friendsofsymfony/user-bundle": "^2.1",
@@ -42,7 +43,8 @@
4243
"php-mock/php-mock-phpunit": "^2.0",
4344
"phpdocumentor/reflection-docblock": "^3.0 || ^4.0",
4445
"phpdocumentor/type-resolver": "^0.3 || ^0.4",
45-
"phpunit/phpunit": "^6.1",
46+
"phpspec/prophecy": "^1.8",
47+
"phpunit/phpunit": "^7.5.1",
4648
"psr/log": "^1.0",
4749
"ramsey/uuid": "^3.7",
4850
"ramsey/uuid-doctrine": "^1.4",
@@ -57,11 +59,10 @@
5759
"symfony/expression-language": "^3.4 || ^4.0",
5860
"symfony/finder": "^3.4 || ^4.0",
5961
"symfony/form": "^3.4 || ^4.0",
60-
"symfony/framework-bundle": "^3.4 || ^4.0",
61-
"symfony/mercure": "*",
62+
"symfony/framework-bundle": "^4.2",
6263
"symfony/mercure-bundle": "*",
63-
"symfony/messenger": "^4.1",
64-
"symfony/phpunit-bridge": "^3.4 || ^4.0",
64+
"symfony/messenger": "^4.2",
65+
"symfony/phpunit-bridge": "^3.4.5 || ^4.0.5",
6566
"symfony/routing": "^3.4 || ^4.0",
6667
"symfony/security": "^3.4 || ^4.0",
6768
"symfony/security-bundle": "^3.4 || ^4.0",

src/Annotation/ApiResource.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
* @Attribute("itemOperations", type="array"),
4343
* @Attribute("maximumItemsPerPage", type="int"),
4444
* @Attribute("mercure", type="mixed"),
45+
* @Attribute("messenger", type="bool"),
4546
* @Attribute("normalizationContext", type="array"),
4647
* @Attribute("openapiContext", type="array"),
4748
* @Attribute("order", type="array"),
@@ -184,6 +185,13 @@ final class ApiResource
184185
*/
185186
private $mercure;
186187

188+
/**
189+
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
190+
*
191+
* @var bool
192+
*/
193+
private $messenger;
194+
187195
/**
188196
* @see https://github.com/Haehnchen/idea-php-annotation-plugin/issues/112
189197
*

src/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtension.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
use Symfony\Component\ExpressionLanguage\ExpressionLanguage;
3838
use Symfony\Component\Finder\Finder;
3939
use Symfony\Component\HttpKernel\DependencyInjection\Extension;
40+
use Symfony\Component\Messenger\MessageBusInterface;
4041
use Symfony\Component\Validator\Validator\ValidatorInterface;
4142
use Symfony\Component\Yaml\Yaml;
4243

@@ -145,6 +146,10 @@ public function load(array $configs, ContainerBuilder $container)
145146
$this->registerValidatorConfiguration($container, $config);
146147
$this->registerDataCollectorConfiguration($container, $config, $loader);
147148
$this->registerMercureConfiguration($container, $config, $loader, $useDoctrine);
149+
150+
if (interface_exists(MessageBusInterface::class)) {
151+
$loader->load('messenger.xml');
152+
}
148153
}
149154

150155
/**
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" ?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
6+
7+
<services>
8+
<service id="api_platform.message_bus" alias="message_bus" />
9+
10+
<service id="api_platform.messenger.data_persister" class="ApiPlatform\Core\Bridge\Symfony\Messenger\DataPersister" public="false">
11+
<argument type="service" id="api_platform.metadata.resource.metadata_factory" />
12+
<argument type="service" id="api_platform.message_bus" />
13+
14+
<tag name="api_platform.data_persister" priority="-900" />
15+
</service>
16+
</services>
17+
18+
</container>
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Core\Bridge\Symfony\Messenger;
15+
16+
use ApiPlatform\Core\DataPersister\DataPersisterInterface;
17+
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
18+
use ApiPlatform\Core\Util\ClassInfoTrait;
19+
use Symfony\Component\Messenger\Envelope;
20+
use Symfony\Component\Messenger\MessageBusInterface;
21+
22+
/**
23+
* Dispatches the given resource using the message bus of Symfony Messenger.
24+
*
25+
* @experimental
26+
*
27+
* @author Kévin Dunglas <[email protected]>
28+
*/
29+
final class DataPersister implements DataPersisterInterface
30+
{
31+
use ClassInfoTrait;
32+
33+
private $resourceMetadataFactory;
34+
private $messageBus;
35+
36+
public function __construct(ResourceMetadataFactoryInterface $resourceMetadataFactory, MessageBusInterface $messageBus)
37+
{
38+
$this->resourceMetadataFactory = $resourceMetadataFactory;
39+
$this->messageBus = $messageBus;
40+
}
41+
42+
/**
43+
* {@inheritdoc}
44+
*/
45+
public function supports($data): bool
46+
{
47+
return true === $this->resourceMetadataFactory->create($this->getObjectClass($data))->getAttribute('messenger');
48+
}
49+
50+
/**
51+
* {@inheritdoc}
52+
*/
53+
public function persist($data)
54+
{
55+
$this->messageBus->dispatch($data);
56+
57+
return $data;
58+
}
59+
60+
/**
61+
* {@inheritdoc}
62+
*/
63+
public function remove($data)
64+
{
65+
$this->messageBus->dispatch(new Envelope($data, new RemoveStamp()));
66+
}
67+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Core\Bridge\Symfony\Messenger;
15+
16+
use Symfony\Component\Messenger\Stamp\StampInterface;
17+
18+
/**
19+
* Hints that the resource in the envelope must be removed.
20+
*
21+
* @experimental
22+
*
23+
* @author Kévin Dunglas <[email protected]>
24+
*/
25+
final class RemoveStamp implements StampInterface
26+
{
27+
}

tests/Annotation/ApiResourceTest.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,16 @@ public function testConstruct()
3838
'formats' => ['foo', 'bar' => ['application/bar']],
3939
'filters' => ['foo', 'bar'],
4040
'graphql' => ['query' => ['normalization_context' => ['groups' => ['foo', 'bar']]]],
41+
'inputClass' => 'Foo',
4142
'iri' => 'http://example.com/res',
4243
'itemOperations' => ['foo' => ['bar']],
4344
'maximumItemsPerPage' => 42,
4445
'mercure' => '[\'foo\', object.owner]',
46+
'messenger' => true,
4547
'normalizationContext' => ['groups' => ['bar']],
4648
'order' => ['foo', 'bar' => 'ASC'],
4749
'openapiContext' => ['description' => 'foo'],
50+
'outputClass' => 'Bar',
4851
'paginationClientEnabled' => true,
4952
'paginationClientItemsPerPage' => true,
5053
'paginationClientPartial' => true,
@@ -76,11 +79,14 @@ public function testConstruct()
7679
'force_eager' => false,
7780
'formats' => ['foo', 'bar' => ['application/bar']],
7881
'filters' => ['foo', 'bar'],
82+
'input_class' => 'Foo',
7983
'maximum_items_per_page' => 42,
8084
'mercure' => '[\'foo\', object.owner]',
85+
'messenger' => true,
8186
'normalization_context' => ['groups' => ['bar']],
8287
'order' => ['foo', 'bar' => 'ASC'],
8388
'openapi_context' => ['description' => 'foo'],
89+
'output_class' => 'Bar',
8490
'pagination_client_enabled' => true,
8591
'pagination_client_items_per_page' => true,
8692
'pagination_client_partial' => true,

tests/Bridge/Symfony/Bundle/DependencyInjection/ApiPlatformExtensionTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,7 @@ private function getBaseContainerBuilderProphecy()
767767
'api_platform.validator',
768768
'api_platform.mercure.listener.response.add_link_header',
769769
'api_platform.doctrine.listener.mercure.publish',
770+
'api_platform.messenger.data_persister',
770771
];
771772

772773
foreach ($definitions as $definition) {
@@ -775,6 +776,7 @@ private function getBaseContainerBuilderProphecy()
775776

776777
$aliases = [
777778
'api_platform.http_cache.purger' => 'api_platform.http_cache.purger.varnish',
779+
'api_platform.message_bus' => 'message_bus',
778780
EagerLoadingExtension::class => 'api_platform.doctrine.orm.query_extension.eager_loading',
779781
FilterExtension::class => 'api_platform.doctrine.orm.query_extension.filter',
780782
FilterEagerLoadingExtension::class => 'api_platform.doctrine.orm.query_extension.filter_eager_loading',
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Core\Tests\Bridge\Symfony\Messenger;
15+
16+
use ApiPlatform\Core\Bridge\Symfony\Messenger\DataPersister;
17+
use ApiPlatform\Core\Bridge\Symfony\Messenger\RemoveStamp;
18+
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
19+
use ApiPlatform\Core\Metadata\Resource\ResourceMetadata;
20+
use ApiPlatform\Core\Tests\Fixtures\TestBundle\Entity\Dummy;
21+
use PHPUnit\Framework\TestCase;
22+
use Prophecy\Argument;
23+
use Symfony\Component\Messenger\Envelope;
24+
use Symfony\Component\Messenger\MessageBusInterface;
25+
26+
/**
27+
* @author Kévin Dunglas <[email protected]>
28+
*/
29+
class DataPersisterTest extends TestCase
30+
{
31+
public function testSupport()
32+
{
33+
$metadataFactoryProphecy = $this->prophesize(ResourceMetadataFactoryInterface::class);
34+
$metadataFactoryProphecy->create(Dummy::class)->willReturn(new ResourceMetadata(null, null, null, null, null, ['messenger' => true]));
35+
36+
$dataPersister = new DataPersister($metadataFactoryProphecy->reveal(), $this->prophesize(MessageBusInterface::class)->reveal());
37+
$this->assertTrue($dataPersister->supports(new Dummy()));
38+
}
39+
40+
public function testPersist()
41+
{
42+
$dummy = new Dummy();
43+
44+
$messageBus = $this->prophesize(MessageBusInterface::class);
45+
$messageBus->dispatch($dummy)->willReturn(new Envelope(new \stdClass()))->shouldBeCalled();
46+
47+
$dataPersister = new DataPersister($this->prophesize(ResourceMetadataFactoryInterface::class)->reveal(), $messageBus->reveal());
48+
$this->assertSame($dummy, $dataPersister->persist($dummy));
49+
}
50+
51+
public function testRemove()
52+
{
53+
$dummy = new Dummy();
54+
55+
$messageBus = $this->prophesize(MessageBusInterface::class);
56+
$messageBus->dispatch(Argument::that(function (Envelope $envelope) {
57+
return null !== $envelope->last(RemoveStamp::class);
58+
}))->willReturn(new Envelope(new \stdClass()))->shouldBeCalled();
59+
60+
$dataPersister = new DataPersister($this->prophesize(ResourceMetadataFactoryInterface::class)->reveal(), $messageBus->reveal());
61+
$dataPersister->remove($dummy);
62+
}
63+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the API Platform project.
5+
*
6+
* (c) Kévin Dunglas <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace ApiPlatform\Core\Tests\Bridge\Symfony\Messenger;
15+
16+
use ApiPlatform\Core\Bridge\Symfony\Messenger\RemoveStamp;
17+
use PHPUnit\Framework\TestCase;
18+
use Symfony\Component\Messenger\Stamp\StampInterface;
19+
20+
/**
21+
* @author Kévin Dunglas <[email protected]>
22+
*/
23+
class RemoveStampTest extends TestCase
24+
{
25+
public function testConstruct()
26+
{
27+
$this->assertInstanceOf(StampInterface::class, new RemoveStamp());
28+
}
29+
}

tests/Metadata/Property/Factory/AnnotationPropertyMetadataFactoryTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
class AnnotationPropertyMetadataFactoryTest extends TestCase
3131
{
3232
/**
33-
* @dataProvider getDependencies
33+
* @dataProvider dependenciesProvider
3434
*/
3535
public function testCreateProperty(ProphecyInterface $reader, ProphecyInterface $decorated = null, string $description)
3636
{
@@ -48,7 +48,7 @@ public function testCreateProperty(ProphecyInterface $reader, ProphecyInterface
4848
$this->assertEquals(['foo' => 'bar'], $metadata->getAttributes());
4949
}
5050

51-
public function getDependencies()
51+
public function dependenciesProvider(): array
5252
{
5353
$annotation = new ApiProperty();
5454
$annotation->description = 'description';

tests/Metadata/Property/Factory/AnnotationPropertyNameCollectionFactoryTest.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
class AnnotationPropertyNameCollectionFactoryTest extends TestCase
3232
{
3333
/**
34-
* @dataProvider getDependencies
34+
* @dataProvider dependenciesProvider
3535
*/
3636
public function testCreate(PropertyNameCollectionFactoryInterface $decorated = null, array $results)
3737
{
@@ -49,7 +49,7 @@ public function testCreate(PropertyNameCollectionFactoryInterface $decorated = n
4949
$this->assertEquals($results, iterator_to_array($metadata));
5050
}
5151

52-
public function getDependencies()
52+
public function dependenciesProvider(): array
5353
{
5454
$decoratedThrowsNotFound = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
5555
$decoratedThrowsNotFound->create(Dummy::class, [])->willThrow(new ResourceClassNotFoundException())->shouldBeCalled();
@@ -65,7 +65,7 @@ public function getDependencies()
6565
}
6666

6767
/**
68-
* @dataProvider getUpperCaseDependencies
68+
* @dataProvider upperCaseDependenciesProvider
6969
*/
7070
public function testUpperCaseCreate(ObjectProphecy $decorated = null, array $results)
7171
{
@@ -83,7 +83,7 @@ public function testUpperCaseCreate(ObjectProphecy $decorated = null, array $res
8383
$this->assertEquals($results, iterator_to_array($metadata));
8484
}
8585

86-
public function getUpperCaseDependencies()
86+
public function upperCaseDependenciesProvider(): array
8787
{
8888
$decoratedThrowsNotFound = $this->prophesize(PropertyNameCollectionFactoryInterface::class);
8989
$decoratedThrowsNotFound->create(UpperCaseIdentifierDummy::class, [])->willThrow(new ResourceClassNotFoundException())->shouldBeCalled();

0 commit comments

Comments
 (0)