8000 feature #48496 [Notifier] Add Smsmode bridge (gnito-org) · symfony/symfony@725fac4 · GitHub
[go: up one dir, main page]

Skip to content

Commit 725fac4

Browse files
feature #48496 [Notifier] Add Smsmode bridge (gnito-org)
This PR was squashed before being merged into the 6.3 branch. Discussion ---------- [Notifier] Add Smsmode bridge | Q | A | ------------- | --- | Branch? | 6.3 | Bug fix? | no | New feature? | yes | Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files --> | Tickets | | License | MIT | Doc PR | symfony/symfony-docs#17511 | Recipe | symfony/recipes#1161 <!-- Replace this notice by a short README for your feature/bugfix. This will help reviewers and should be a good start for the documentation. Additionally (see https://symfony.com/releases): - Always add tests and ensure they pass. - Bug fixes must be submitted against the lowest maintained branch where they apply (lowest branches are regularly merged to upper ones so they get the fixes too). - Features and deprecations must be submitted against the latest branch. - For new features, provide some code snippets to help understand usage. - Changelog entry should follow https://symfony.com/doc/current/contributing/code/conventions.html#writing-a-changelog-entry - Never break backward compatibility (see https://symfony.com/bc). --> Commits ------- f5e7500 [Notifier] Add Smsmode bridge
2 parents 6ec3943 + f5e7500 commit 725fac4

18 files changed

+568
-0
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2752,6 +2752,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $
27522752
NotifierBridge\SmsBiuras\SmsBiurasTransportFactory::class => 'notifier.transport_factory.sms-biuras',
27532753
NotifierBridge\Smsc\SmscTransportFactory::class => 'notifier.transport_factory.smsc',
27542754
NotifierBridge\SmsFactor\SmsFactorTransportFactory::class => 'notifier.transport_factory.sms-factor',
2755+
NotifierBridge\Smsmode\SmsmodeTransportFactory::class => 'notifier.transport_factory.smsmode',
27552756
NotifierBridge\SpotHit\SpotHitTransportFactory::class => 'notifier.transport_factory.spot-hit',
27562757
NotifierBridge\Telegram\TelegramTransportFactory::class => 'notifier.transport_factory.telegram',
27572758
NotifierBridge\Telnyx\TelnyxTransportFactory::class => 'notifier.transport_factory.telnyx',

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,5 +283,9 @@
283283
->set('notifier.transport_factory.click-send', Bridge\ClickSend\ClickSendTransportFactory::class)
284284
->parent('notifier.transport_factory.abstract')
285285
->tag('texter.transport_factory')
286+
287+
->set('notifier.transport_factory.smsmode', Bridge\Smsmode\SmsmodeTransportFactory::class)
288+
->parent('notifier.transport_factory.abstract')
289+
->tag('texter.transport_factory')
286290
;
287291
};
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: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CHANGELOG
2+
=========
3+
4+
6.3
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) 2022-present 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.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
Smsmode Notifier
2+
================
3+
4+
Provides [Smsmode](https://www.smsmode.com/) integration for Symfony Notifier.
5+
6+
DSN example
7+
-----------
8+
9+
```
10+
SMSMODE_DSN=smsmode://API_KEY@default?from=FROM
11+
```
12+
13+
where:
14+
15+
- `API_KEY` is your Smsmode API key
16+
- `FROM` is your sender ID
17+
18+
Resources
19+
---------
20+
21+
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
22+
* [Report issues](https://github.com/symfony/symfony/issues) and
23+
[send Pull Requests](https://github.com/symfony/symfony/pulls)
24+
in the [main Symfony repository](https://github.com/symfony/symfony)
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
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\Smsmode;
13+
14+
use Symfony\Component\Notifier\Message\MessageOptionsInterface;
15+
16+
/**
17+
* @author gnito-org <https://github.com/gnito-org>
18+
*/
19+
final class SmsmodeOptions implements MessageOptionsInterface
20+
{
21+
private array $options;
22+
23+
public function __construct(array $options = [])
24+
{
25+
$this->options = $options;
26+
}
27+
28+
public function getFrom(): ?string
29+
{
30+
return $this->options['from'] ?? null;
31+
}
32+
33+
public function getRecipientId(): ?string
34+
{
35+
return $this->options['recipient_id'] ?? null;
36+
}
37+
38+
public function getRefClient(): ?string
39+
{
40+
return $this->options['ref_client'] ?? null;
41+
}
42+
43+
public function getSentDate(): ?string
44+
{
45+
return $this->options['sent_date'] ?? null;
46+
}
47+
48+
public function setFrom(string $from): self
49+
{
50+
$this->options['from'] = $from;
51+
52+
return $this;
53+
}
54+
55+
public function setRecipientId(string $id): self
56+
{
57+
$this->options['recipient_id'] = $id;
58+
59+
return $this;
60+
}
61+
62+
public function setRefClient(string $refClient): self
63+
{
64+
$this->options['ref_client'] = $refClient;
65+
66+
return $this;
67+
}
68+
69+
public function setSentDate(string $sentDate): self
70+
{
71+
$this->options['sent_date'] = $sentDate;
72+
73+
return $this;
74+
}
75+
76+
public function toArray(): array
77+
{
78+
$options = $this->options;
79+
if (isset($options['recipient_id'])) {
80+
unset($options['recipient_id']);
81+
}
82+
83+
return $options;
84+
}
85+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
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\Smsmode;
13+
14+
use Symfony\Component\Notifier\Exception\InvalidArgumentException;
15+
use Symfony\Component\Notifier\Exception\TransportException;
16+
use Symfony\Component\Notifier\Exception\UnsupportedMessageTypeException;
17+
use Symfony\Component\Notifier\Message\MessageInterface;
18+
use Symfony\Component\Notifier\Message\SentMessage;
19+
use Symfony\Component\Notifier\Message\SmsMessage;
20+
use Symfony\Component\Notifier\Transport\AbstractTransport;
21+
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
22+
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
23+
use Symfony\Contracts\HttpClient\HttpClientInterface;
24+
25+
/**
26+
* @author gnito-org <https://github.com/gnito-org>
27+
*/
28+
final class SmsmodeTransport extends AbstractTransport
29+
{
30+
protected const HOST = 'rest.smsmode.com';
31+
32+
public function __construct(
33+
#[\SensitiveParameter] private readonly string $apiKey,
34+
private readonly ?string $from = null,
35+
HttpClientInterface $client = null,
36+
EventDispatcherInterface $dispatcher = null
37+
) {
38+
parent::__construct($client, $dispatcher);
39+
}
40+
41+
public function __toString(): string
42+
{
43+
$queryParameters = [];
44+
if ($this->from) {
45+
$queryParameters['from'] = $this->from;
46+
}
47+
48+
return sprintf('smsmode://%s', $this->getEndpoint()).($queryParameters ? '?'.http_build_query($queryParameters) : null);
49+
}
50+
51+
public function supports(MessageInterface $message): bool
52+
{
53+
return $message instanceof SmsMessage && (null === $message->getOptions() || $message->getOptions() instanceof SmsmodeOptions);
54+
}
55+
56+
/**
57+
* https://dev.smsmode.com/sms/v1/message.
58+
*/
59+
protected function doSend(MessageInterface $message): SentMessage
60+
{
61+
if (!$message instanceof SmsMessage) {
62+
throw new UnsupportedMessageTypeException(__CLASS__, SmsMessage::class, $message);
63+
}
64+
65+
$endpoint = sprintf('https://%s/sms/v1/messages', $this->getEndpoint());
66+
67+
$opts = $message->getOptions();
68+
$options = $opts ? $opts->toArray() : [];
69+
$options['body']['text'] = $message->getSubject();
70+
$options['recipient']['to'] = $message->getPhone();
71+
72+
if (!isset($options['from'])) {
73+
$options['from'] = $this->from;
74+
}
75+
76+
if (!preg_match('/^[a-zA-Z0-9\s]{1,11}$/', $options['from'] ?? '')) {
77+
throw new InvalidArgumentException(sprintf('The "From" value "%s" is not a valid sender ID.', $this->from));
78+
}
79+
80+
if (isset($options['sent_date'])) {
81+
$options['sentDate'] = $options['sent_date'];
82+
unset($options['sent_date']);
83+
}
84+
85+
if (isset($options['ref_client'])) {
86+
$options['refClient'] = $options['ref_client'];
87+
unset($options['ref_client']);
88+
}
89+
90+
$response = $this->client->request('POST', $endpoint, [
91+
'headers' => ['X-Api-Key' => $this->apiKey],
92+
'json' => array_filter($options),
93+
]);
94+
95+
try {
96+
$statusCode = $response->getStatusCode();
97+
} catch (TransportExceptionInterface $e) {
98+
throw new TransportException('Could not reach the remote Smsmode server.', $response, 0, $e);
99+
}
100+
101+
if (201 !== $statusCode) {
102+
$error = $response->getContent(false);
103+
throw new TransportException(sprintf('Unable to send the SMS - "%s".', $error ?: 'unknown failure'), $response);
104+
}
105+
106+
$success = $response->toArray(false);
107+
108+
$sentMessage = new SentMessage($message, (string) $this);
109+
$sentMessage->setMessageId($success['messageId']);
110+
111+
return $sentMessage;
112+
}
113+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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\Smsmode;
13+
14+
use Symfony\Component\Notifier\Exception\UnsupportedSchemeException;
15+
use Symfony\Component\Notifier\Transport\AbstractTransportFactory;
16+
use Symfony\Component\Notifier\Transport\Dsn;
17+
18+
/**
19+
* @author gnito-org <https://github.com/gnito-org>
20+
*/
21+
final class SmsmodeTransportFactory extends AbstractTransportFactory
22+
{
23+
private const TRANSPORT_SCHEME = 'smsmode';
24+
25+
public function create(Dsn $dsn): SmsmodeTransport
26+
{
27+
$scheme = $dsn->getScheme();
28+
29+
if (self::TRANSPORT_SCHEME !== $scheme) {
30+
throw new UnsupportedSchemeException($dsn, self::TRANSPORT_SCHEME, $this->getSupportedSchemes());
31+
}
32+
33+
$apiKey = $this->getUser($dsn);
34+
$from = $dsn->getRequiredOption('from');
35+
$host = 'default' === $dsn->getHost() ? null : $dsn->getHost();
36+
$port = $dsn->getPort();
37+
38+
return (new SmsmodeTransport($apiKey, $from, $this->client, $this->dispatcher))->setHost($host)->setPort($port);
39+
}
40+
41+
protected function getSupportedSchemes(): array
42+
{
43+
return [self::TRANSPORT_SCHEME];
44+
}
45+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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\Smsmode\Tests;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\Notifier\Bridge\Smsmode\SmsmodeOptions;
16+
17+
class SmsmodeOptionsTest extends TestCase
18+
{
19+
public function testSmsmodeOptions()
20+
{
21+
$smsmodeOptions = (new SmsmodeOptions())->setFrom('test_from')->setRecipientId('test_recipient')->setRefClient('test_ref_client')->setSentDate('test_sent_date');
22+
23+
self::assertSame([
24+
'from' => 'test_from',
25+
'ref_client' => 'test_ref_client',
26+
'sent_date' => 'test_sent_date',
27+
], $smsmodeOptions->toArray());
28+
}
29+
}

0 commit comments

Comments
 (0)
0