8000 feature #39141 [Notifier] Add Amazon SNS bridge (adrien-chinour) · YaFou/symfony@ccd3a15 · GitHub
[go: up one dir, main page]

Skip to content

Commit ccd3a15

Browse files
committed
feature symfony#39141 [Notifier] Add Amazon SNS bridge (adrien-chinour)
This PR was squashed before being merged into the 5.4 branch. Discussion ---------- [Notifier] Add Amazon SNS bridge | Q | A | ------------- | --- | Branch? | 5.x | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | | License | MIT | Doc PR | [symfony/symfony-docs#15486](symfony/symfony-docs#15486) | Recipe PR | symfony/recipes#847 Hi, This PR add a bridge on Notifier component for Amazon SNS. This bridge use `async-aws/sns` and only work on actual dev-master version of `asyc-aws/core`. I'm working on recipe and doc PR. Commits ------- 3dc6ad4 [Notifier] Add Amazon SNS bridge
2 parents 01eb18d + 3dc6ad4 commit ccd3a15

19 files changed

+508
-0
lines changed

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@
122122
"amphp/http-tunnel": "^1.0",
123123
"async-aws/ses": "^1.0",
124124
"async-aws/sqs": "^1.0",
125+
"async-aws/sns": "^1.0",
125126
"cache/integration-tests": "dev-master",
126127
"composer/package-versions-deprecated": "^1.8",
127128
"doctrine/annotations": "^1.12",

src/Symfony/Bundle/FrameworkBundle/DependencyInjection/FrameworkExtension.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@
110110
use Symfony\Component\Mime\MimeTypeGuesserInterface;
111111
use Symfony\Component\Mime\MimeTypes;
112112
use Symfony\Component\Notifier\Bridge\AllMySms\AllMySmsTransportFactory;
113+
use Symfony\Component\Notifier\Bridge\AmazonSns\AmazonSnsTransportFactory;
113114
use Symfony\Component\Notifier\Bridge\Clickatell\ClickatellTransportFactory;
114115
use Symfony\Component\Notifier\Bridge\Discord\DiscordTransportFactory;
115116
use Symfony\Component\Notifier\Bridge\Esendex\EsendexTransportFactory;
@@ -2421,6 +2422,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $
24212422

24222423
$classToServices = [
24232424
AllMySmsTransportFactory::class => 'notifier.transport_factory.allmysms',
2425+
AmazonSnsTransportFactory::class => 'notifier.transport_factory.amazonsns',
24242426
ClickatellTransportFactory::class => 'notifier.transport_factory.clickatell',
24252427
DiscordTransportFactory::class => 'notifier.transport_factory.discord',
24262428
EsendexTransportFactory::class => 'notifier.transport_factory.esendex',
@@ -2464,6 +2466,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $
24642466

24652467
foreach ($classToServices as $class => $service) {
24662468
switch ($package = substr($service, \strlen('notifier.transport_factory.'))) {
2469+
case 'amazonsns': $package = 'amazon-sns'; break;
24672470
case 'fakechat': $package = 'fake-chat'; break;
24682471
case 'fakesms': $package = 'fake-sms'; break;
24692472
case 'freemobile': $package = 'free-mobile'; break;

src/Symfony/Bundle/FrameworkBundle/Resources/config/notifier_transports.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
1313

1414
use Symfony\Component\Notifier\Bridge\AllMySms\AllMySmsTransportFactory;
15+
use Symfony\Component\Notifier\Bridge\AmazonSns\AmazonSnsTransportFactory;
1516
use Symfony\Component\Notifier\Bridge\Clickatell\ClickatellTransportFactory;
1617
use Symfony\Component\Notifier\Bridge\Discord\DiscordTransportFactory;
1718
use Symfony\Component\Notifier\Bridge\Esendex\EsendexTransportFactory;
@@ -178,6 +179,11 @@
178179
->parent('notifier.transport_factory.abstract')
179180
->tag('texter.transport_factory')
180181

182+
->set('notifier.transport_factory.amazonsns', AmazonSnsTransportFactory::class)
183+
->parent('notifier.transport_factory.abstract')
184+
->tag('texter.transport_factory')
185+
->tag('chatter.transport_factory')
186+
181187
->set('notifier.transport_factory.null', NullTransportFactory::class)
182188
->parent('notifier.transport_factory.abstract')
183189
->tag('chatter.transport_factory')
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/Tests export-ignore
2+
/phpunit.xml.dist export-ignore
3+
/.gitattributes export-ignore
4+
/.gitignore export-ignore
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
vendor/
2+
composer.lock
3+
phpunit.xml
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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+
namespace Symfony\Component\Notifier\Bridge\AmazonSns;
13+
14+
use AsyncAws\Sns\Input\PublishInput;
15+
use Symfony\Component\Notifier\Message\MessageOptionsInterface;
16+
17+
/**
18+
* @author Adrien Chinour <github@chinour.fr>
19+
*/
20+
final class AmazonSnsOptions implements MessageOptionsInterface
21+
{
22+
private $options = [];
23+
24+
private $recipient;
25+
26+
public function __construct(string $recipient, array $options = [])
27+
{
28+
$this->recipient = $recipient;
29+
$this->options = $options;
30+
}
31+
32+
public function toArray(): array
33+
{
34+
return $this->options;
35+
}
36+
37+
public function getRecipientId(): ?string
38+
{
39+
return $this->recipient;
40+
}
41+
42+
/**
43+
* @param string $topic The Topic ARN for SNS message
44+
*
45+
* @return $this
46+
*/
47+
public function recipient(string $topic): self
48+
{
49+
$this->recipient = $topic;
50+
51+
return $this;
52+
}
53+
54+
/**
55+
* @see PublishInput::$Subject
56+
*/
57+
public function subject(string $subject): self
58+
{
59+
$this->options['Subject'] = $subject;
60+
61+
return $this;
62+
}
63+
64+
/**
65+
* @see PublishInput::$MessageStructure
66+
*/
67+
public function messageStructure(string $messageStructure): self
68+
{
69+
$this->options['MessageStructure'] = $messageStructure;
70+
71+
return $this;
72+
}
73+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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+
namespace Symfony\Component\Notifier\Bridge\AmazonSns;
13+
14+
use AsyncAws\Sns\SnsClient;
15+
use Symfony\Component\Notifier\Exception\TransportException;
16+
use Symfony\Component\Notifier\Exception\UnsupportedMessageTypeException;
17+
use Symfony\Component\Notifier\Message\ChatMessage;
18+
use Symfony\Component\Notifier\Message\MessageInterface;
19+
use Symfony\Component\Notifier\Message\SentMessage;
20+
use Symfony\Component\Notifier\Message\SmsMessage;
21+
use Symfony\Component\Notifier\Transport\AbstractTransport;
22+
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
23+
use Symfony\Contracts\HttpClient\HttpClientInterface;
24+
25+
/**
26+
* @author Adrien Chinour <github@chinour.fr>
27+
*/
28+
final class AmazonSnsTransport extends AbstractTransport
29+
{
30+
private $snsClient;
31+
32+
public function __construct(SnsClient $snsClient, HttpClientInterface $client = null, EventDispatcherInterface $dispatcher = null)
33+
{
34+
$this->snsClient = $snsClient;
35+
parent::__construct($client, $dispatcher);
36+
}
37+
38+
public function __toString(): string
39+
{
40+
$configuration = $this->snsClient->getConfiguration();
41+
42+
return sprintf('sns://%s?region=%s', $this->getEndpoint(), $configuration->get('region'));
43+
}
44+
45+
public function supports(MessageInterface $message): bool
46+
{
47+
return $message instanceof SmsMessage || ($message instanceof ChatMessage && $message->getOptions() instanceof AmazonSnsOptions);
48+
}
49+
50+
protected function doSend(MessageInterface $message): SentMessage
51+
{
52+
if (!$this->supports($message)) {
53+
throw new UnsupportedMessageTypeException(__CLASS__, sprintf('"%s" or "%s"', SmsMessage::class, ChatMessage::class), $message);
54+
}
55+
56+
if ($message instanceof ChatMessage && $message->getOptions() instanceof AmazonSnsOptions) {
57+
$options = $message->getOptions()->toArray();
58+
}
59+
$options['Message'] = $message->getSubject();
60+
$options[($message instanceof ChatMessage) ? 'TopicArn' : 'PhoneNumber'] = $message->getRecipientId();
61+
62+
try {
63+
$response = $this->snsClient->publish($options);
64+
$message = new SentMessage($message, (string) $this);
65+
$message->setMessageId($response->getMessageId());
66+
} catch (\Exception $exception) {
67+
$info = isset($response) ? $response->info() : [];
68+
throw new TransportException('Unable to send the message.', $info['response'] ?? null, $info['status'] ?? 0, $exception);
69+
}
70+
71+
return $message;
72+
}
73+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
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+
namespace Symfony\Component\Notifier\Bridge\AmazonSns;
13+
14+
use AsyncAws\Sns\SnsClient;
15+
use Symfony\Component\Notifier\Exception\UnsupportedSchemeException;
16+
use Symfony\Component\Notifier\Transport\AbstractTransportFactory;
17+
use Symfony\Component\Notifier\Transport\Dsn;
18+
use Symfony\Component\Notifier\Transport\TransportInterface;
19+
20+
/**
21+
* @author Adrien Chinour <github@chinour.fr>
22+
*/
23+
final class AmazonSnsTransportFactory extends AbstractTransportFactory
24+
{
25+
public function create(Dsn $dsn): TransportInterface
26+
{
27+
$scheme = $dsn->getScheme();
28+
29+
if ('sns' !== $scheme) {
30+
throw new UnsupportedSchemeException($dsn, 'sns', $this->getSupportedSchemes());
31+
}
32+
33+
$host = 'default' === $dsn->getHost() ? null : $dsn->getHost();
34+
$port = $dsn->getPort();
35+
36+
$options = null === $host ? [] : ['endpoint' => 'https://'.$host.($port ? ':'.$port : '')];
37+
38+
if ($dsn->getUser()) {
39+
$options += [
40+
'accessKeyId' => $dsn->getUser(),
41+
'accessKeySecret' => $dsn->getPassword(),
42+
];
43+
}
44+
45+
if ($dsn->getOption('region')) {
46+
$options['region'] = $dsn->getOption('region');
47+
}
48+
49+
if ($dsn->getOption('profile')) {
50+
$options['profile'] = $dsn->getOption('profile');
51+
}
52+
53+
return (new AmazonSnsTransport(new SnsClient($options, null, $this->client), $this->client, $this->dispatcher))->setHost($host)->setPort($port);
54+
}
55+
56+
protected function getSupportedSchemes(): array
57+
{
58+
return ['sns'];
59+
}
60+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CHANGELOG
2+
=========
3+
4+
5.4
5+
---
6+
7+
* Add the bridge
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2021 Fabien Potencier
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy
4+
of this software and associated documentation files (the "Software"), to deal
5+
in the Software without restriction, including without limitation the rights
6+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
copies of the Software, and to permit persons to whom the Software is furnished
8+
to do so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
THE SOFTWARE.

0 commit comments

Comments
 (0)
0