Skip to content

[Messenger][DX] Allow stamps to be passed directly to MessageBusInterface::dispatch() #30707

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 31, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/Symfony/Component/Messenger/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ CHANGELOG
4.3.0
-----

* [BC BREAK] The `Envelope::__construct()` signature changed:
you can no longer pass an unlimited number of stamps as the second,
third, fourth, arguments etc: stamps are now an array passed to the
second argument.
* [BC BREAK] The `MessageBusInterface::dispatch()` signature changed:
a second argument `array $stamps = []` was added.
* [BC BREAK] The `TransportFactoryInterface::createTransport()` signature
changed: a required 3rd `SerializerInterface` argument was added.
* Added a new `SyncTransport` along with `ForceCallHandlersStamp` to
Expand Down
18 changes: 16 additions & 2 deletions src/Symfony/Component/Messenger/Envelope.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,10 @@ final class Envelope
private $message;

/**
* @param object $message
* @param object $message
* @param StampInterface[] $stamps
*/
public function __construct($message, StampInterface ...$stamps)
public function __construct($message, array $stamps = [])
{
if (!\is_object($message)) {
throw new \TypeError(sprintf('Invalid argument provided to "%s()": expected object but got %s.', __METHOD__, \gettype($message)));
Expand All @@ -40,6 +41,19 @@ public function __construct($message, StampInterface ...$stamps)
}
}

/**
* Makes sure the message is in an Envelope and adds the given stamps.
*
* @param object|Envelope $message
* @param StampInterface[] $stamps
*/
public static function wrap($message, array $stamps = []): self
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@weaverryan wouldnt stamp() be the better name? In case of explicit public usage ...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to clarify; if wrapping means "put it in an envelop" this method doesnt always "wrap", whereas new Envelope($anything) does.

This method wraps if needed, but always stamps.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, are you suggesting a change? Or is it clear for you now?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It stamps only if stamps are provided.
The main use case is removing the "if" that the method contains from userland boilerplate.
Tldr, wrap() fits my mind :)

Copy link
Contributor

@ro0NL ro0NL May 11, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah it might fit; "we wrap it with stamps" :/ but to me that's "stamping", not "wrapping" (stamping requires an envelope wrap)

Just curious if wrap() and __construct() could be considered confusing...

Copy link
Contributor

@ro0NL ro0NL May 11, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It stamps only if stamps are provided.

hm indeed, missed this detail :} OK. these technical details are hard to convey with naming :) let's keep as is 👍

{
$envelope = $message instanceof self ? $message : new self($message);

return $envelope->with(...$stamps);
}

/**
* @return Envelope a new Envelope instance with additional stamp
*/
Expand Down
4 changes: 2 additions & 2 deletions src/Symfony/Component/Messenger/MessageBus.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,12 @@ public function getIterator()
/**
* {@inheritdoc}
*/
public function dispatch($message): Envelope
public function dispatch($message, array $stamps = []): Envelope
{
if (!\is_object($message)) {
throw new \TypeError(sprintf('Invalid argument provided to "%s()": expected object, but got %s.', __METHOD__, \gettype($message)));
}
$envelope = $message instanceof Envelope ? $message : new Envelope($message);
$envelope = Envelope::wrap($message, $stamps);
$middlewareIterator = $this->middlewareAggregate->getIterator();

while ($middlewareIterator instanceof \IteratorAggregate) {
Expand Down
7 changes: 5 additions & 2 deletions src/Symfony/Component/Messenger/MessageBusInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

namespace Symfony\Component\Messenger;

use Symfony\Component\Messenger\Stamp\StampInterface;

/**
* @author Samuel Roze <samuel.roze@gmail.com>
*
Expand All @@ -21,7 +23,8 @@ interface MessageBusInterface
/**
* Dispatches the given message.
*
* @param object|Envelope $message The message or the message pre-wrapped in an envelope
* @param object|Envelope $message The message or the message pre-wrapped in an envelope
* @param StampInterface[] $stamps
*/
public function dispatch($message): Envelope;
public function dispatch($message, array $stamps = []): Envelope;
}
4 changes: 2 additions & 2 deletions src/Symfony/Component/Messenger/RoutableMessageBus.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public function __construct(ContainerInterface $busLocator)
$this->busLocator = $busLocator;
}

public function dispatch($envelope): Envelope
public function dispatch($envelope, array $stamps = []): Envelope
{
if (!$envelope instanceof Envelope) {
throw new InvalidArgumentException('Messages passed to RoutableMessageBus::dispatch() must be inside an Envelope');
Expand All @@ -53,6 +53,6 @@ public function dispatch($envelope): Envelope
throw new InvalidArgumentException(sprintf('Invalid bus name "%s" on BusNameStamp.', $busNameStamp->getBusName()));
}

return $this->busLocator->get($busNameStamp->getBusName())->dispatch($envelope);
return $this->busLocator->get($busNameStamp->getBusName())->dispatch($envelope, $stamps);
}
}
25 changes: 22 additions & 3 deletions src/Symfony/Component/Messenger/Tests/EnvelopeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class EnvelopeTest extends TestCase
public function testConstruct()
{
$receivedStamp = new ReceivedStamp();
$envelope = new Envelope($dummy = new DummyMessage('dummy'), $receivedStamp);
$envelope = new Envelope($dummy = new DummyMessage('dummy'), [$receivedStamp]);

$this->assertSame($dummy, $envelope->getMessage());
$this->assertArrayHasKey(ReceivedStamp::class, $stamps = $envelope->all());
Expand All @@ -42,7 +42,7 @@ public function testWithReturnsNewInstance()

public function testWithoutAll()
{
$envelope = new Envelope(new DummyMessage('dummy'), new ReceivedStamp(), new ReceivedStamp(), new DelayStamp(5000));
$envelope = new Envelope(new DummyMessage('dummy'), [new ReceivedStamp(), new ReceivedStamp(), new DelayStamp(5000)]);

$envelope = $envelope->withoutAll(ReceivedStamp::class);

Expand All @@ -53,7 +53,7 @@ public function testWithoutAll()
public function testLast()
{
$receivedStamp = new ReceivedStamp();
$envelope = new Envelope($dummy = new DummyMessage('dummy'), $receivedStamp);
$envelope = new Envelope($dummy = new DummyMessage('dummy'), [$receivedStamp]);

$this->assertSame($receivedStamp, $envelope->last(ReceivedStamp::class));
$this->assertNull($envelope->last(ValidationStamp::class));
Expand All @@ -72,4 +72,23 @@ public function testAll()
$this->assertArrayHasKey(ValidationStamp::class, $stamps);
$this->assertSame($validationStamp, $stamps[ValidationStamp::class][0]);
}

public function testWrapWithMessage()
{
$message = new \stdClass();
$stamp = new ReceivedStamp();
$envelope = Envelope::wrap($message, [$stamp]);

$this->assertSame($message, $envelope->getMessage());
$this->assertSame([ReceivedStamp::class => [$stamp]], $envelope->all());
}

public function testWrapWithEnvelope()
{
$envelope = new Envelope(new \stdClass(), [new DelayStamp(5)]);
$envelope = Envelope::wrap($envelope, [new ReceivedStamp()]);

$this->assertCount(1, $envelope->all(DelayStamp::class));
$this->assertCount(1, $envelope->all(ReceivedStamp::class));
}
}
6 changes: 3 additions & 3 deletions src/Symfony/Component/Messenger/Tests/HandleTraitTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public function testHandleReturnsHandledStampResult()

$query = new DummyMessage('Hello');
$bus->expects($this->once())->method('dispatch')->willReturn(
new Envelope($query, new HandledStamp('result', 'DummyHandler::__invoke'))
new Envelope($query, [new HandledStamp('result', 'DummyHandler::__invoke')])
);

$this->assertSame('result', $queryBus->query($query));
Expand All @@ -42,7 +42,7 @@ public function testHandleAcceptsEnvelopes()
$bus = $this->createMock(MessageBus::class);
$queryBus = new TestQueryBus($bus);

$envelope = new Envelope(new DummyMessage('Hello'), new HandledStamp('result', 'DummyHandler::__invoke'));
$envelope = new Envelope(new DummyMessage('Hello'), [new HandledStamp('result', 'DummyHandler::__invoke')]);
$bus->expects($this->once())->method('dispatch')->willReturn($envelope);

$this->assertSame('result', $queryBus->query($envelope));
Expand Down Expand Up @@ -74,7 +74,7 @@ public function testHandleThrowsOnMultipleHandledStamps()

$query = new DummyMessage('Hello');
$bus->expects($this->once())->method('dispatch')->willReturn(
new Envelope($query, new HandledStamp('first_result', 'FirstDummyHandler::__invoke'), new HandledStamp('second_result', 'SecondDummyHandler::__invoke', 'dummy_2'))
new Envelope($query, [new HandledStamp('first_result', 'FirstDummyHandler::__invoke'), new HandledStamp('second_result', 'SecondDummyHandler::__invoke', 'dummy_2')])
);

$queryBus->query($query);
Expand Down
20 changes: 17 additions & 3 deletions src/Symfony/Component/Messenger/Tests/MessageBusTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
use Symfony\Component\Messenger\MessageBus;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
use Symfony\Component\Messenger\Stamp\BusNameStamp;
use Symfony\Component\Messenger\Stamp\DelayStamp;
use Symfony\Component\Messenger\Stamp\ReceivedStamp;
use Symfony\Component\Messenger\Tests\Fixtures\AnEnvelopeStamp;
use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage;
Expand Down Expand Up @@ -69,7 +71,7 @@ public function testItCallsMiddleware()
public function testThatAMiddlewareCanAddSomeStampsToTheEnvelope()
{
$message = new DummyMessage('Hello');
$envelope = new Envelope($message, new ReceivedStamp());
$envelope = new Envelope($message, [new ReceivedStamp()]);
$envelopeWithAnotherStamp = $envelope->with(new AnEnvelopeStamp());

$firstMiddleware = $this->getMockBuilder(MiddlewareInterface::class)->getMock();
Expand Down Expand Up @@ -107,10 +109,10 @@ public function testThatAMiddlewareCanAddSomeStampsToTheEnvelope()
public function testThatAMiddlewareCanUpdateTheMessageWhileKeepingTheEnvelopeStamps()
{
$message = new DummyMessage('Hello');
$envelope = new Envelope($message, ...$stamps = [new ReceivedStamp()]);
$envelope = new Envelope($message, $stamps = [new ReceivedStamp()]);

$changedMessage = new DummyMessage('Changed');
$expectedEnvelope = new Envelope($changedMessage, ...$stamps);
$expectedEnvelope = new Envelope($changedMessage, $stamps);

$firstMiddleware = $this->getMockBuilder(MiddlewareInterface::class)->getMock();
$firstMiddleware->expects($this->once())
Expand All @@ -134,4 +136,16 @@ public function testThatAMiddlewareCanUpdateTheMessageWhileKeepingTheEnvelopeSta

$bus->dispatch($envelope);
}

public function testItAddsTheStamps()
{
$finalEnvelope = (new MessageBus())->dispatch(new \stdClass(), [new DelayStamp(5), new BusNameStamp('bar')]);
$this->assertCount(2, $finalEnvelope->all());
}

public function testItAddsTheStampsToEnvelope()
{
$finalEnvelope = (new MessageBus())->dispatch(new Envelope(new \stdClass()), [new DelayStamp(5), new BusNameStamp('bar')]);
$this->assertCount(2, $finalEnvelope->all());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ public function testEventsInNewTransactionAreHandledAfterMainMessage()
$messageBus = new MessageBus([
$middleware,
new DispatchingMiddleware($eventBus, [
new Envelope($firstEvent, new DispatchAfterCurrentBusStamp()),
new Envelope($secondEvent, new DispatchAfterCurrentBusStamp()),
new Envelope($firstEvent, [new DispatchAfterCurrentBusStamp()]),
new Envelope($secondEvent, [new DispatchAfterCurrentBusStamp()]),
$thirdEvent, // Not in a new transaction
]),
$handlingMiddleware,
Expand Down Expand Up @@ -80,8 +80,8 @@ public function testThrowingEventsHandlingWontStopExecution()
$messageBus = new MessageBus([
$middleware,
new DispatchingMiddleware($eventBus, [
new Envelope($firstEvent, new DispatchAfterCurrentBusStamp()),
new Envelope($secondEvent, new DispatchAfterCurrentBusStamp()),
new Envelope($firstEvent, [new DispatchAfterCurrentBusStamp()]),
new Envelope($secondEvent, [new DispatchAfterCurrentBusStamp()]),
]),
$handlingMiddleware,
]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public function testItSendsTheMessageToMultipleSenders()

public function testItSendsToOnlyOneSenderOnRedelivery()
{
$envelope = new Envelope(new DummyMessage('Hey'), new RedeliveryStamp(5, 'bar'));
$envelope = new Envelope(new DummyMessage('Hey'), [new RedeliveryStamp(5, 'bar')]);
// even with a ForceCallHandlersStamp, the next middleware won't be called
$envelope = $envelope->with(new ForceCallHandlersStamp());
$sender = $this->getMockBuilder(SenderInterface::class)->getMock();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ class MultiplierRetryStrategyTest extends TestCase
public function testIsRetryable()
{
$strategy = new MultiplierRetryStrategy(3);
$envelope = new Envelope(new \stdClass(), new RedeliveryStamp(0, 'sender_alias'));
$envelope = new Envelope(new \stdClass(), [new RedeliveryStamp(0, 'sender_alias')]);

$this->assertTrue($strategy->isRetryable($envelope));
}

public function testIsNotRetryable()
{
$strategy = new MultiplierRetryStrategy(3);
$envelope = new Envelope(new \stdClass(), new RedeliveryStamp(3, 'sender_alias'));
$envelope = new Envelope(new \stdClass(), [new RedeliveryStamp(3, 'sender_alias')]);

$this->assertFalse($strategy->isRetryable($envelope));
}
Expand All @@ -48,7 +48,7 @@ public function testIsRetryableWithNoStamp()
public function testGetWaitTime(int $delay, int $multiplier, int $maxDelay, int $previousRetries, int $expectedDelay)
{
$strategy = new MultiplierRetryStrategy(10, $delay, $multiplier, $maxDelay);
$envelope = new Envelope(new \stdClass(), new RedeliveryStamp($previousRetries, 'sender_alias'));
$envelope = new Envelope(new \stdClass(), [new RedeliveryStamp($previousRetries, 'sender_alias')]);

$this->assertSame($expectedDelay, $strategy->getWaitingTime($envelope));
}
Expand Down
10 changes: 6 additions & 4 deletions src/Symfony/Component/Messenger/Tests/RoutableMessageBusTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Messenger\RoutableMessageBus;
use Symfony\Component\Messenger\Stamp\BusNameStamp;
use Symfony\Component\Messenger\Stamp\DelayStamp;

class RoutableMessageBusTest extends TestCase
{
public function testItRoutesToTheCorrectBus()
{
$envelope = new Envelope(new \stdClass(), new BusNameStamp('foo_bus'));
$envelope = new Envelope(new \stdClass(), [new BusNameStamp('foo_bus')]);

$bus1 = $this->createMock(MessageBusInterface::class);
$bus2 = $this->createMock(MessageBusInterface::class);
Expand All @@ -32,11 +33,12 @@ public function testItRoutesToTheCorrectBus()
$container->expects($this->once())->method('has')->with('foo_bus')->willReturn(true);
$container->expects($this->once())->method('get')->will($this->returnValue($bus2));

$stamp = new DelayStamp(5);
$bus1->expects($this->never())->method('dispatch');
$bus2->expects($this->once())->method('dispatch')->with($envelope)->willReturn($envelope);
$bus2->expects($this->once())->method('dispatch')->with($envelope, [$stamp])->willReturn($envelope);

$routableBus = new RoutableMessageBus($container);
$this->assertSame($envelope, $routableBus->dispatch($envelope));
$this->assertSame($envelope, $routableBus->dispatch($envelope, [$stamp]));
}

public function testItExceptionOnMissingStamp()
Expand All @@ -58,7 +60,7 @@ public function testItExceptionOnBusNotFound()
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Invalid bus name');

$envelope = new Envelope(new \stdClass(), new BusNameStamp('foo_bus'));
$envelope = new Envelope(new \stdClass(), [new BusNameStamp('foo_bus')]);

$container = $this->createMock(ContainerInterface::class);
$container->expects($this->once())->method('has')->willReturn(false);
Expand Down
26 changes: 17 additions & 9 deletions src/Symfony/Component/Messenger/Tests/TraceableMessageBusTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use PHPUnit\Framework\TestCase;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\MessageBusInterface;
use Symfony\Component\Messenger\Stamp\DelayStamp;
use Symfony\Component\Messenger\Tests\Fixtures\AnEnvelopeStamp;
use Symfony\Component\Messenger\Tests\Fixtures\DummyMessage;
use Symfony\Component\Messenger\TraceableMessageBus;
Expand All @@ -24,22 +25,25 @@ public function testItTracesDispatch()
{
$message = new DummyMessage('Hello');

$stamp = new DelayStamp(5);
$bus = $this->getMockBuilder(MessageBusInterface::class)->getMock();
$bus->expects($this->once())->method('dispatch')->with($message)->willReturn(new Envelope($message));
$bus->expects($this->once())->method('dispatch')->with($message, [$stamp])->willReturn(new Envelope($message));

$traceableBus = new TraceableMessageBus($bus);
$line = __LINE__ + 1;
$traceableBus->dispatch($message);
$traceableBus->dispatch($message, [$stamp]);
$this->assertCount(1, $tracedMessages = $traceableBus->getDispatchedMessages());
$this->assertArraySubset([
$actualTracedMessage = $tracedMessages[0];
unset($actualTracedMessage['callTime']); // don't check, too variable
$this->assertEquals([
'message' => $message,
'stamps' => [],
'stamps' => [[$stamp]],
'caller' => [
'name' => 'TraceableMessageBusTest.php',
'file' => __FILE__,
'line' => $line,
],
], $tracedMessages[0], true);
], $actualTracedMessage);
}

public function testItTracesDispatchWithEnvelope()
Expand All @@ -54,15 +58,17 @@ public function testItTracesDispatchWithEnvelope()
$line = __LINE__ + 1;
$traceableBus->dispatch($envelope);
$this->assertCount(1, $tracedMessages = $traceableBus->getDispatchedMessages());
$this->assertArraySubset([
$actualTracedMessage = $tracedMessages[0];
unset($actualTracedMessage['callTime']); // don't check, too variable
$this->assertEquals([
'message' => $message,
'stamps' => [[$stamp]],
'caller' => [
'name' => 'TraceableMessageBusTest.php',
'file' => __FILE__,
'line' => $line,
],
], $tracedMessages[0], true);
], $actualTracedMessage);
}

public function testItTracesExceptions()
Expand All @@ -82,7 +88,9 @@ public function testItTracesExceptions()
}

$this->assertCount(1, $tracedMessages = $traceableBus->getDispatchedMessages());
$this->assertArraySubset([
$actualTracedMessage = $tracedMessages[0];
unset($actualTracedMessage['callTime']); // don't check, too variable
$this->assertEquals([
'message' => $message,
'exception' => $exception,
'stamps' => [],
Expand All @@ -91,6 +99,6 @@ public function testItTracesExceptions()
'file' => __FILE__,
'line' => $line,
],
], $tracedMessages[0], true);
], $actualTracedMessage);
}
}
Loading