Skip to content

Commit e5e397c

Browse files
authored
Merge pull request #6 from nipwaayoni/topic-dispatcher
Topic dispatcher
2 parents fa2b228 + 26f54ff commit e5e397c

20 files changed

+632
-81
lines changed

.github/workflows/run-tests.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@ jobs:
99
strategy:
1010
matrix:
1111
php: [7.4, 7.3]
12-
laravel: [8.*]
12+
laravel: [6.*, 8.*]
1313
include:
14+
- laravel: 6.*
15+
testbench: 4.*
1416
- laravel: 8.*
1517
testbench: 6.*
1618

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44
.idea
55
.php_cs.cache
66
.phpunit.result.cache
7+
composer.lock

README.md

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Introduction
1+
# Laravel SNS Handler
22
This package provides an easy way of adding AWS SNS message handling to your Laravel application as a REST endpoint. The package can automatically confirm subscription requests and dispatches events when a message is received.
33

44
## How to create an SNS topic
@@ -75,3 +75,14 @@ This package adds a route to your application for incoming SNS requests. Note th
7575

7676
**Note: You will only be able to subscribe your endpoint if it can be reached from the AWS SNS service**
7777

78+
## Development
79+
This package is expected to work with supported versions of Laravel, including LTS releases. During development, you should be sure to run tests and validate expected behaviors under different releases. Since we use the `orchestra/testbench` package, you can easily switch between installed Laravel framework releases using `composer`:
80+
81+
```bash
82+
# Laravel 6
83+
composer require --dev orchestra/testbench:^4.0 -W
84+
# Laravel 8
85+
composer require --dev orchestra/testbench:^6.0 -W
86+
```
87+
88+
New releases of Laravel should be added to the GitHub workflow matrix in `.github/workflows/run-tests.yml`.

composer.json

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,16 @@
1616
"require": {
1717
"php": ">=7.3",
1818
"ext-json": "*",
19-
"aws/aws-php-sns-message-validator": "^1.6"
19+
"aws/aws-php-sns-message-validator": "^1.6",
20+
"php-http/discovery": "^1.14",
21+
"psr/http-factory": "^1.0",
22+
"psr/http-client": "^1.0"
2023
},
2124
"require-dev" : {
2225
"roave/security-advisories": "dev-latest",
23-
"guzzlehttp/guzzle": "^7.2",
24-
"orchestra/testbench": "^6.0"
26+
"orchestra/testbench": "^6.0",
27+
"http-interop/http-factory-guzzle": "^1.2",
28+
"php-http/guzzle6-adapter": "^2.0"
2529
},
2630
"autoload": {
2731
"psr-4": {

config/sns-handler.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,10 @@
22

33
return [
44
'validate-sns-messages' => env('VALIDATE_SNS_MESSAGES', true),
5-
'sns-class-map' => []
5+
'confirmation-events' => [
6+
Nipwaayoni\SnsHandler\Events\SnsConfirmationRequestReceived::class => ['*']
7+
],
8+
'message-events' => [
9+
Nipwaayoni\SnsHandler\Events\SnsMessageReceived::class => ['*']
10+
],
611
];

src/Listeners/SnsConfirmationRequestListener.php

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,67 @@
33

44
namespace Nipwaayoni\SnsHandler\Listeners;
55

6-
use Illuminate\Support\Facades\Http;
6+
use Http\Discovery\HttpClientDiscovery;
7+
use Http\Discovery\Psr17FactoryDiscovery;
78
use Illuminate\Support\Facades\Log;
89
use Nipwaayoni\SnsHandler\Events\SnsConfirmationRequestReceived;
910
use Nipwaayoni\SnsHandler\SnsConfirmSubscriptionException;
11+
use Nipwaayoni\SnsHandler\SnsException;
12+
use Nipwaayoni\SnsHandler\SnsMessage;
13+
use Psr\Http\Client\ClientExceptionInterface;
14+
use Psr\Http\Client\ClientInterface;
15+
use Psr\Http\Message\RequestFactoryInterface;
16+
use Psr\Http\Message\ResponseInterface;
1017

1118
class SnsConfirmationRequestListener
1219
{
20+
/**
21+
* @var ClientInterface
22+
*/
23+
private $client;
24+
25+
/**
26+
* @var RequestFactoryInterface
27+
*/
28+
private $requestFactory;
29+
30+
/**
31+
* @param ClientInterface|null $client
32+
* @param RequestFactoryInterface|null $requestFactory
33+
*/
34+
public function __construct(
35+
ClientInterface $client = null,
36+
RequestFactoryInterface $requestFactory = null
37+
) {
38+
$this->client = $client ?? HttpClientDiscovery::find();
39+
$this->requestFactory = $requestFactory ?? Psr17FactoryDiscovery::findRequestFactory();
40+
}
41+
1342
public function handle(SnsConfirmationRequestReceived $event)
1443
{
1544
$message = $event->message();
16-
//TODO Make this work with Laravel 6, as the Http facade was introduced in Laravel 7
17-
$response = Http::get($message->subscribeUrl());
18-
if ($response->successful()) {
19-
$info = sprintf('Subscription confirmation for %s succeeded with status %s', $message->topicArn(), $response->status());
20-
Log::info($info);
21-
return;
45+
46+
$response = $this->getResponse($message);
47+
48+
Log::info(sprintf('Subscription confirmation for %s succeeded with status %s', $message->topicArn(), $response->getStatusCode()));
49+
}
50+
51+
/**
52+
* @param SnsMessage $message
53+
* @return ResponseInterface
54+
* @throws SnsConfirmSubscriptionException
55+
* @throws SnsException
56+
*/
57+
private function getResponse(SnsMessage $message): ResponseInterface
58+
{
59+
try {
60+
return $this->client->sendRequest(
61+
$this->requestFactory->createRequest('GET', $message->subscribeUrl())
62+
);
63+
} catch (ClientExceptionInterface $e) {
64+
throw new SnsConfirmSubscriptionException(
65+
sprintf('Subscription confirmation for %s failed with status %s', $message->topicArn(), $e->getCode())
66+
);
2267
}
23-
$error = sprintf('Subscription confirmation for %s failed with status %s', $message->topicArn(), $response->status());
24-
Log::error($error);
25-
throw new SnsConfirmSubscriptionException($error);
2668
}
2769
}

src/SnsBroker.php

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@
55

66
use Aws\Sns\Message;
77
use Aws\Sns\MessageValidator;
8+
use Illuminate\Config\Repository as Config;
89
use Illuminate\Support\Facades\Log;
9-
use Nipwaayoni\SnsHandler\Events\SnsConfirmationRequestReceived;
10-
use Nipwaayoni\SnsHandler\Events\SnsMessageReceived;
1110

1211
class SnsBroker
1312
{
@@ -19,10 +18,15 @@ class SnsBroker
1918
* @var Log
2019
*/
2120
private $log;
21+
/**
22+
* @var Config
23+
*/
24+
private $config;
2225

23-
public function __construct(MessageValidator $validator)
26+
public function __construct(MessageValidator $validator, Config $config)
2427
{
2528
$this->validator = $validator;
29+
$this->config = $config;
2630
}
2731

2832
/**
@@ -66,19 +70,35 @@ public function handleRequest(SnsHttpRequest $request): void
6670
throw new SnsException(sprintf('Unknown message type: %s', $message->type()));
6771
}
6872

69-
private function getSubscriptionEvent(string $arn)
73+
/**
74+
* @param string $arn
75+
* @return string
76+
* @throws SnsUnknownTopicArnException
77+
*/
78+
private function getSubscriptionEvent(string $arn): string
7079
{
71-
$map = [SnsConfirmationRequestReceived::class => ['*']];
80+
$map = $this->config->get('sns-handler.confirmation-events', []);
7281
return $this->arnMap($arn, $map);
7382
}
7483

75-
private function getNotificationEvent(string $arn)
84+
/**
85+
* @param string $arn
86+
* @return string
87+
* @throws SnsUnknownTopicArnException
88+
*/
89+
private function getNotificationEvent(string $arn): string
7690
{
77-
$map = [SnsMessageReceived::class => ['*']];
91+
$map = $this->config->get('sns-handler.message-events', []);
7892
return $this->arnMap($arn, $map);
7993
}
8094

81-
private function arnMap(string $arn, array $map)
95+
/**
96+
* @param string $arn
97+
* @param array $map
98+
* @return string
99+
* @throws SnsUnknownTopicArnException
100+
*/
101+
private function arnMap(string $arn, array $map): string
82102
{
83103
$default = null;
84104
foreach ($map as $className => $arnList) {
@@ -90,6 +110,12 @@ private function arnMap(string $arn, array $map)
90110
}
91111
}
92112

113+
if (null === $default) {
114+
throw new SnsUnknownTopicArnException(sprintf('Unmappable TopicArn: %s', $arn));
115+
}
116+
117+
// TODO ensure class is dispatchable
118+
93119
return $default;
94120
}
95121
}

src/SnsUnknownTopicArnException.php

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,6 @@
33

44
namespace Nipwaayoni\SnsHandler;
55

6-
use Throwable;
7-
86
class SnsUnknownTopicArnException extends SnsException
97
{
10-
public function __construct(string $topicArn = '', $code = 0, Throwable $previous = null)
11-
{
12-
parent::__construct(sprintf('No handler registered for TopicArn %s', $topicArn), $code, $previous);
13-
}
148
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
4+
namespace Nipwaayoni\Tests\SnsHandler\Events;
5+
6+
use Illuminate\Broadcasting\InteractsWithSockets;
7+
use Illuminate\Foundation\Events\Dispatchable;
8+
use Nipwaayoni\SnsHandler\SnsMessage;
9+
10+
class SnsConfirmationRequestAlphaReceived
11+
{
12+
use Dispatchable, InteractsWithSockets;
13+
14+
private $message;
15+
16+
public function __construct(SnsMessage $message)
17+
{
18+
$this->message = $message;
19+
}
20+
21+
public function message(): SnsMessage
22+
{
23+
return $this->message;
24+
}
25+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
4+
namespace Nipwaayoni\Tests\SnsHandler\Events;
5+
6+
use Illuminate\Broadcasting\InteractsWithSockets;
7+
use Illuminate\Foundation\Events\Dispatchable;
8+
use Nipwaayoni\SnsHandler\SnsMessage;
9+
10+
class SnsConfirmationRequestBetaReceived
11+
{
12+
use Dispatchable, InteractsWithSockets;
13+
14+
private $message;
15+
16+
public function __construct(SnsMessage $message)
17+
{
18+
$this->message = $message;
19+
}
20+
21+
public function message(): SnsMessage
22+
{
23+
return $this->message;
24+
}
25+
}

0 commit comments

Comments
 (0)