Skip to content

Commit aa39110

Browse files
authored
Merge pull request #11 from nipwaayoni/ensure_dispatchable_events
Ensure dispatchable events
2 parents e5e397c + 6f7bd4e commit aa39110

File tree

5 files changed

+84
-3
lines changed

5 files changed

+84
-3
lines changed

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,17 @@ public function handle(SnsMessageReceived $event)
4141
```
4242
The message content will always be a string. You are responsible for any deserialization or other steps required to interpret the message content.
4343

44+
### Dispatching specific events for specific ARNs
45+
It is possible to map individual event classes to individual ARNs. The map is stored in the sns-handler.php config file (ensure this is published if it does not exist in your project). You can map either subscription events or message events in the following format:
46+
```php
47+
'message-events' => [
48+
SnsMessageAlphaReceived::class => ['arn:aws:sns:us-west-2:123456789012:AlphaTopic'],
49+
SnsMessageBetaReceived::class => ['arn:aws:sns:us-west-2:123456789012:BetaTopic', 'arn:aws:sns:us-west-2:123456789012:GammaTopic'],
50+
SnsMessageReceived::class => ['*'],
51+
];
52+
```
53+
Note that the map is parsed in order, so the first match found in the list will be the class dispatched.
54+
Note that a default event class is not required, but if a matching event cannot be found in the map, a 404 will be returned if a message is sent using that ARN.
4455
## Write a feature test to test the expected SNS feature
4556
The ReceivesSnsMessagesTrait facilitates testing. Use the trait in your test
4657

@@ -73,6 +84,11 @@ You can add your serialized message in the message field. Note that manually POS
7384
This package adds a route to your application for incoming SNS requests. Note that because this is an api route, any API middleware you have applied in your application will affect this route. The route will be `{Your application's base URL}/api/sns/message`.
7485
[Follow these instructions to subscribe your endpoint using HTTPS](https://docs.aws.amazon.com/sns/latest/dg/sns-http-https-endpoint-as-subscriber.html).
7586

87+
This package is configured to automatically respond to all SNS subscription requests sent to its endpoint. This can be disabled by adding the following to your .env file:
88+
```
89+
AUTO_CONFIRM_SUBSCRIPTIONS=false
90+
```
91+
7692
**Note: You will only be able to subscribe your endpoint if it can be reached from the AWS SNS service**
7793

7894
## Development

config/sns-handler.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<?php
22

33
return [
4+
'auto-confirm-subscriptions' => env('AUTO_CONFIRM_SUBSCRIPTIONS', true),
45
'validate-sns-messages' => env('VALIDATE_SNS_MESSAGES', true),
56
'confirmation-events' => [
67
Nipwaayoni\SnsHandler\Events\SnsConfirmationRequestReceived::class => ['*']

src/SnsBroker.php

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ public function handleRequest(SnsHttpRequest $request): void
6161

6262
case SnsMessage::SUBSCRIBE_TYPE:
6363
$className = $this->getSubscriptionEvent($message->topicArn());
64+
if ($this->config->get('sns-handler.auto-confirm-subscriptions', true) === false) {
65+
Log::info(sprintf('Subscription confirmation event not handled for topic %s with %s because auto-confirm-subscriptions disabled', $message->topicArn(), $className));
66+
return;
67+
}
6468
$className::dispatch($message);
6569
Log::info(sprintf('Dispatched confirmation event for subscription to topic %s with %s', $message->topicArn(), $className));
6670
return;
@@ -73,7 +77,7 @@ public function handleRequest(SnsHttpRequest $request): void
7377
/**
7478
* @param string $arn
7579
* @return string
76-
* @throws SnsUnknownTopicArnException
80+
* @throws SnsUnknownTopicArnException|SnsException
7781
*/
7882
private function getSubscriptionEvent(string $arn): string
7983
{
@@ -84,7 +88,7 @@ private function getSubscriptionEvent(string $arn): string
8488
/**
8589
* @param string $arn
8690
* @return string
87-
* @throws SnsUnknownTopicArnException
91+
* @throws SnsUnknownTopicArnException|SnsException
8892
*/
8993
private function getNotificationEvent(string $arn): string
9094
{
@@ -96,7 +100,7 @@ private function getNotificationEvent(string $arn): string
96100
* @param string $arn
97101
* @param array $map
98102
* @return string
99-
* @throws SnsUnknownTopicArnException
103+
* @throws SnsUnknownTopicArnException|SnsException
100104
*/
101105
private function arnMap(string $arn, array $map): string
102106
{
@@ -106,6 +110,7 @@ private function arnMap(string $arn, array $map): string
106110
$default = $className;
107111
}
108112
if (in_array($arn, $arnList)) {
113+
$this->ensureDispatchable($className);
109114
return $className;
110115
}
111116
}
@@ -115,7 +120,20 @@ private function arnMap(string $arn, array $map): string
115120
}
116121

117122
// TODO ensure class is dispatchable
123+
$this->ensureDispatchable($default);
118124

119125
return $default;
120126
}
127+
128+
/**
129+
* @param string $className
130+
* @throws SnsException
131+
*/
132+
private function ensureDispatchable(string $className): void
133+
{
134+
if (method_exists($className, 'dispatch')) {
135+
return;
136+
}
137+
throw new SnsException('Mapped class is not dispatchable:' . $className);
138+
}
121139
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?php
2+
3+
namespace Nipwaayoni\Tests\SnsHandler\Events;
4+
5+
class SnsMessageNotDispatchable
6+
{
7+
}

tests/Unit/SnsBrokerTest.php

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Nipwaayoni\Tests\SnsHandler\Events\SnsConfirmationRequestBetaReceived;
1616
use Nipwaayoni\Tests\SnsHandler\Events\SnsMessageAlphaReceived;
1717
use Nipwaayoni\Tests\SnsHandler\Events\SnsMessageBetaReceived;
18+
use Nipwaayoni\Tests\SnsHandler\Events\SnsMessageNotDispatchable;
1819
use PHPUnit\Framework\MockObject\MockObject;
1920
use Nipwaayoni\Tests\SnsHandler\MakesSnsTests;
2021
use Nipwaayoni\Tests\SnsHandler\TestCase;
@@ -235,4 +236,42 @@ public function testRejectsWithUnhandledTopicArnOnMessage(): void
235236

236237
Event::assertNotDispatched(SnsMessageReceived::class);
237238
}
239+
240+
public function testThrowsExceptionForNonDispatchableMappedClass(): void
241+
{
242+
$this->configValues['message-events'] = [
243+
SnsMessageNotDispatchable::class => ['arn:aws:sns:us-west-2:123456789012:AlphaTopic'],
244+
];
245+
246+
$request = $this->createMock(SnsHttpRequest::class);
247+
$request->expects($this->once())->method('jsonContent')
248+
->willReturn($this->makeSnsMessageJson([
249+
'MessageId' => 'abc123',
250+
'TopicArn' => 'arn:aws:sns:us-west-2:123456789012:AlphaTopic'
251+
]));
252+
253+
$this->expectException(SnsException::class);
254+
$this->expectExceptionMessage('Mapped class is not dispatchable:');
255+
256+
$this->broker->handleRequest($request);
257+
258+
Event::assertNotDispatched(SnsMessageReceived::class);
259+
}
260+
261+
public function testDoesNotAutoConfirmSubscriptionsWhenDisabled(): void
262+
{
263+
$this->configValues['auto-confirm-subscriptions'] = false;
264+
265+
$request = $this->createMock(SnsHttpRequest::class);
266+
$request->expects($this->once())->method('jsonContent')
267+
->willReturn($this->makeSnsMessageJson([
268+
'Type' => SnsMessage::SUBSCRIBE_TYPE,
269+
'SubscribeURL' => 'https://aws.amazon.com/subscribe/123',
270+
'TopicArn' => 'arn:aws:sns:us-west-2:123456789012:AlphaTopic'
271+
]));
272+
273+
$this->broker->handleRequest($request);
274+
275+
Event::assertNotDispatched(SnsConfirmationRequestReceived::class);
276+
}
238277
}

0 commit comments

Comments
 (0)