Skip to content

Commit 229e7bc

Browse files
authored
Merge pull request #78 from ericfernance/add-support-for-fifo-sns
Adds SnsFifoStamp class based of the Symfony base stamp for SQS and a…
2 parents c3f00f1 + 669bba8 commit 229e7bc

File tree

3 files changed

+107
-1
lines changed

3 files changed

+107
-1
lines changed

src/Service/Sns/SnsFifoStamp.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace Bref\Symfony\Messenger\Service\Sns;
4+
5+
use Symfony\Component\Messenger\Stamp\NonSendableStampInterface;
6+
7+
class SnsFifoStamp implements NonSendableStampInterface {
8+
private ?string $messageGroupId;
9+
private ?string $messageDeduplicationId;
10+
11+
public function __construct(string $messageGroupId = null, string $messageDeduplicationId = null)
12+
{
13+
$this->messageGroupId = $messageGroupId;
14+
$this->messageDeduplicationId = $messageDeduplicationId;
15+
}
16+
17+
public function getMessageGroupId(): ?string
18+
{
19+
return $this->messageGroupId;
20+
}
21+
22+
public function getMessageDeduplicationId(): ?string
23+
{
24+
return $this->messageDeduplicationId;
25+
}
26+
}

src/Service/Sns/SnsTransport.php

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,21 @@ public function send(Envelope $envelope): Envelope
3939
'Message' => $encodedMessage['body'],
4040
'TopicArn' => $this->topic,
4141
];
42-
42+
if (str_contains($this->topic, ".fifo")) {
43+
$stamps = $envelope->all();
44+
$dedupeStamp = $stamps[SnsFifoStamp::class][0] ?? false;
45+
if (!$dedupeStamp) {
46+
throw new Exception("SnsFifoStamp required for fifo topic");
47+
}
48+
$messageGroupId = $dedupeStamp->getMessageGroupId() ?? false;
49+
$messageDeDuplicationId = $dedupeStamp->getMessageDeduplicationId() ?? false;
50+
if ($messageDeDuplicationId) {
51+
$arguments['MessageDeduplicationId'] = $messageDeDuplicationId;
52+
}
53+
if ($messageGroupId) {
54+
$arguments['MessageGroupId'] = $messageGroupId;
55+
}
56+
}
4357
try {
4458
$result = $this->sns->publish($arguments);
4559
$messageId = $result->getMessageId();

tests/Functional/Service/Sns/SnsTransportTest.php

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,15 @@
55
use AsyncAws\Core\Test\ResultMockFactory;
66
use AsyncAws\Sns\Result\PublishResponse;
77
use AsyncAws\Sns\SnsClient;
8+
use Bref\Symfony\Messenger\Service\Sns\SnsFifoStamp;
89
use Bref\Symfony\Messenger\Service\Sns\SnsTransport;
910
use Bref\Symfony\Messenger\Service\Sns\SnsTransportFactory;
1011
use Bref\Symfony\Messenger\Test\Functional\BaseFunctionalTest;
1112
use Bref\Symfony\Messenger\Test\Resources\TestMessage\TestMessage;
13+
use Symfony\Component\Messenger\Envelope;
1214
use Symfony\Component\Messenger\MessageBusInterface;
1315
use Symfony\Component\Messenger\Transport\Serialization\PhpSerializer;
16+
use Symfony\Component\Messenger\Transport\Serialization\SerializerInterface;
1417

1518
class SnsTransportTest extends BaseFunctionalTest
1619
{
@@ -53,4 +56,67 @@ public function test send message(): void
5356
$bus = $this->container->get(MessageBusInterface::class);
5457
$bus->dispatch(new TestMessage('hello'));
5558
}
59+
60+
public function testRejectsMessageWhenQueueIsFifoWithoutStamp()
61+
{
62+
$snsClient = $this->getMockBuilder(SnsClient::class)->disableOriginalConstructor()->getMock();
63+
$serializer = $this->container->get(SerializerInterface::class);
64+
$snsTransport = new SnsTransport($snsClient, $serializer, "arn:aws:sns:us-east-1:1234567890:test.fifo'"); // fifo suffix designates fifo queue
65+
$msg = new TestMessage("hello");
66+
$envelope = new Envelope($msg);
67+
$this->expectExceptionMessage("SnsFifoStamp required for fifo topic");
68+
$snsTransport->send($envelope);
69+
}
70+
public function testAcceptsMessageWhenQueueIsFifoWithStamp(){
71+
$snsClient = $this->getMockBuilder(SnsClient::class)->disableOriginalConstructor()->getMock();
72+
$snsClient->expects($this->once())->method("publish")->willReturn(ResultMockFactory::create(PublishResponse::class, ['MessageId' => 4711]));
73+
$serializer = $this->container->get(SerializerInterface::class);
74+
$snsTransport = new SnsTransport($snsClient, $serializer, "arn:aws:sns:us-east-1:1234567890:test.fifo'"); // fifo suffix designates fifo queue
75+
$msg = new TestMessage("hello");
76+
$envelope = new Envelope($msg, [new SnsFifoStamp("123","456")]);
77+
$resp = $snsTransport->send($envelope);
78+
$this->assertInstanceOf(Envelope::class, $resp);
79+
}
80+
public function testAttachingSnsFifoStampToMessageAppliesMessageGroupId(){
81+
$snsClient = $this->getMockBuilder(SnsClient::class)->disableOriginalConstructor()->getMock();
82+
$snsClient->expects($this->once())->method("publish")
83+
->with($this->callback(function($params){
84+
$this->assertEquals("123", $params["MessageGroupId"]);
85+
return true;
86+
}))
87+
->willReturn(ResultMockFactory::create(PublishResponse::class, ['MessageId' => 4711]));
88+
$serializer = $this->container->get(SerializerInterface::class);
89+
$snsTransport = new SnsTransport($snsClient, $serializer, "arn:aws:sns:us-east-1:1234567890:test.fifo'"); // fifo suffix designates fifo queue
90+
$msg = new TestMessage("hello");
91+
$envelope = new Envelope($msg, [new SnsFifoStamp("123","456")]);
92+
$resp = $snsTransport->send($envelope);
93+
$this->assertInstanceOf(Envelope::class, $resp);
94+
}
95+
public function testAttachingSnsFifoStampToMessageAppliesMessageDeDeuplicatId(){
96+
$snsClient = $this->getMockBuilder(SnsClient::class)->disableOriginalConstructor()->getMock();
97+
$snsClient->expects($this->once())->method("publish")
98+
->with($this->callback(function($params){
99+
$this->assertEquals("456", $params["MessageDeduplicationId"]);
100+
return true;
101+
}))
102+
->willReturn(ResultMockFactory::create(PublishResponse::class, ['MessageId' => 4711]));
103+
$serializer = $this->container->get(SerializerInterface::class);
104+
$snsTransport = new SnsTransport($snsClient, $serializer, "arn:aws:sns:us-east-1:1234567890:test.fifo'"); // fifo suffix designates fifo queue
105+
$msg = new TestMessage("hello");
106+
$envelope = new Envelope($msg, [new SnsFifoStamp("123","456")]);
107+
$resp = $snsTransport->send($envelope);
108+
$this->assertInstanceOf(Envelope::class, $resp);
109+
}
110+
public function testAttachingSnsFifoStampToMessageAllowsNullMessageGroupId(){
111+
// in fifo queues message group id can be null when the de-dupe scope is the entire queue.
112+
$snsClient = $this->getMockBuilder(SnsClient::class)->disableOriginalConstructor()->getMock();
113+
$snsClient->expects($this->once())->method("publish")
114+
->willReturn(ResultMockFactory::create(PublishResponse::class, ['MessageId' => 4711]));
115+
$serializer = $this->container->get(SerializerInterface::class);
116+
$snsTransport = new SnsTransport($snsClient, $serializer, "arn:aws:sns:us-east-1:1234567890:test.fifo'"); // fifo suffix designates fifo queue
117+
$msg = new TestMessage("hello");
118+
$envelope = new Envelope($msg, [new SnsFifoStamp(null,"456")]);
119+
$resp = $snsTransport->send($envelope);
120+
$this->assertInstanceOf(Envelope::class, $resp);
121+
}
56122
}

0 commit comments

Comments
 (0)