Skip to content

Commit afcca88

Browse files
JamesHemeryOskarStark
authored andcommitted
[Notifier] [SpotHit] Add the bridge
1 parent 72a82c3 commit afcca88

File tree

11 files changed

+348
-0
lines changed

11 files changed

+348
-0
lines changed

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

+2
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@
126126
use Symfony\Component\Notifier\Bridge\Sinch\SinchTransportFactory;
127127
use Symfony\Component\Notifier\Bridge\Slack\SlackTransportFactory;
128128
use Symfony\Component\Notifier\Bridge\Smsapi\SmsapiTransportFactory;
129+
use Symfony\Component\Notifier\Bridge\SpotHit\SpotHitTransportFactory;
129130
use Symfony\Component\Notifier\Bridge\Telegram\TelegramTransportFactory;
130131
use Symfony\Component\Notifier\Bridge\Twilio\TwilioTransportFactory;
131132
use Symfony\Component\Notifier\Bridge\Zulip\ZulipTransportFactory;
@@ -2236,6 +2237,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $
22362237
AllMySmsTransportFactory::class => 'notifier.transport_factory.allmysms',
22372238
FirebaseTransportFactory::class => 'notifier.transport_factory.firebase',
22382239
FreeMobileTransportFactory::class => 'notifier.transport_factory.freemobile',
2240+
SpotHitTransportFactory::class => 'notifier.transport_factory.spothit',
22392241
OvhCloudTransportFactory::class => 'notifier.transport_factory.ovhcloud',
22402242
SinchTransportFactory::class => 'notifier.transport_factory.sinch',
22412243
ZulipTransportFactory::class => 'notifier.transport_factory.zulip',

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

+5
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
use Symfony\Component\Notifier\Bridge\Sinch\SinchTransportFactory;
3434
use Symfony\Component\Notifier\Bridge\Slack\SlackTransportFactory;
3535
use Symfony\Component\Notifier\Bridge\Smsapi\SmsapiTransportFactory;
36+
use Symfony\Component\Notifier\Bridge\SpotHit\SpotHitTransportFactory;
3637
use Symfony\Component\Notifier\Bridge\Telegram\TelegramTransportFactory;
3738
use Symfony\Component\Notifier\Bridge\Twilio\TwilioTransportFactory;
3839
use Symfony\Component\Notifier\Bridge\Zulip\ZulipTransportFactory;
@@ -89,6 +90,10 @@
8990
->parent('notifier.transport_factory.abstract')
9091
->tag('texter.transport_factory')
9192

93+
->set('notifier.transport_factory.spothit', SpotHitTransportFactory::class)
94+
->parent('notifier.transport_factory.abstract')
95+
->tag('texter.transport_factory')
96+
9297
->set('notifier.transport_factory.ovhcloud', OvhCloudTransportFactory::class)
9398
->parent('notifier.transport_factory.abstract')
9499
->tag('texter.transport_factory')
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CHANGELOG
2+
=========
3+
4+
5.3
5+
---
6+
7+
* Add the bridge
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
Spot-Hit Notifier
2+
=================
3+
4+
Provides [Spot-Hit](https://www.spot-hit.fr/) integration for Symfony Notifier.
5+
6+
#### DSN example
7+
8+
```
9+
SPOTHIT_DSN=spothit://TOKEN@default?from=FROM
10+
```
11+
12+
where:
13+
- `TOKEN` is your Spot-Hit API key
14+
- `FROM` is the custom sender (3-11 letters, default is a 5 digits phone number)
15+
16+
Resources
17+
---------
18+
19+
* [Spot-Hit API doc](https://www.spot-hit.fr/documentation-api).
20+
* [Contributing](https://symfony.com/doc/current/contributing/index.html)
21+
* [Report issues](https://github.com/symfony/symfony/issues) and
22+
[send Pull Requests](https://github.com/symfony/symfony/pulls)
23+
in the [main Symfony repository](https://github.com/symfony/symfony)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
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\SpotHit;
13+
14+
use Symfony\Component\Notifier\Exception\TransportException;
15+
use Symfony\Component\Notifier\Exception\UnsupportedMessageTypeException;
16+
use Symfony\Component\Notifier\Message\MessageInterface;
17+
use Symfony\Component\Notifier\Message\SentMessage;
18+
use Symfony\Component\Notifier\Message\SmsMessage;
19+
use Symfony\Component\Notifier\Transport\AbstractTransport;
20+
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
21+
use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface;
22+
use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface;
23+
use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface;
24+
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
25+
use Symfony\Contracts\HttpClient\HttpClientInterface;
26+
27+
/**
28+
* @author James Hemery <james@yieldstudio.fr>
29+
*/
30+
final class SpotHitTransport extends AbstractTransport
31+
{
32+
protected const HOST = 'spot-hit.fr';
33+
34+
private $token;
35+
private $from;
36+
37+
public function __construct(string $token, ?string $from = null, HttpClientInterface $client = null, EventDispatcherInterface $dispatcher = null)
38+
{
39+
$this->token = $token;
40+
$this->from = $from;
41+
42+
parent::__construct($client, $dispatcher);
43+
}
44+
45+
public function __toString(): string
46+
{
47+
if (!$this->from) {
48+
return sprintf('spothit://%s', $this->getEndpoint());
49+
}
50+
51+
return sprintf('spothit://%s?from=%s', $this->getEndpoint(), $this->from);
52+
}
53+
54+
public function supports(MessageInterface $message): bool
55+
{
56+
return $message instanceof SmsMessage;
57+
}
58+
59+
/**
60+
* @param MessageInterface|SmsMessage $message
61+
*
62+
* @throws TransportExceptionInterface
63+
* @throws ClientExceptionInterface
64+
* @throws RedirectionExceptionInterface
65+
* @throws ServerExceptionInterface
66+
*/
67+
protected function doSend(MessageInterface $message): SentMessage
68+
{
69+
if (!$this->supports($message)) {
70+
throw new UnsupportedMessageTypeException(__CLASS__, SmsMessage::class, $message);
71+
}
72+
73+
$endpoint = sprintf('https://www.%s/api/envoyer/sms', $this->getEndpoint());
74+
$response = $this->client->request('POST', $endpoint, [
75+
'body' => [
76+
'key' => $this->token,
77+
'destinataires' => $message->getPhone(),
78+
'type' => 'premium',
79+
'message' => $message->getSubject(),
80+
'expediteur' => $this->from,
81+
],
82+
]);
83+
84+
$data = json_decode($response->getContent(), true);
85+
86+
if (!$data['resultat']) {
87+
$errors = \is_array($data['erreurs']) ? implode(',', $data['erreurs']) : $data['erreurs'];
88+
throw new TransportException(sprintf('[HTTP %d] Unable to send the SMS: error(s) "%s".', $response->getStatusCode(), $errors), $response);
89+
}
90+
91+
$sentMessage = new SentMessage($message, (string) $this);
92+
$sentMessage->setMessageId($data['id']);
93+
94+
return $sentMessage;
95+
}
96+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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\SpotHit;
13+
14+
use Symfony\Component\Notifier\Exception\UnsupportedSchemeException;
15+
use Symfony\Component\Notifier\Transport\AbstractTransportFactory;
16+
use Symfony\Component\Notifier\Transport\Dsn;
17+
use Symfony\Component\Notifier\Transport\TransportInterface;
18+
19+
/**
20+
* @author James Hemery <james@yieldstudio.fr>
21+
*/
22+
final class SpotHitTransportFactory extends AbstractTransportFactory
23+
{
24+
/**
25+
* @return SpotHitTransport
26+
*/
27+
public function create(Dsn $dsn): TransportInterface
28+
{
29+
$scheme = $dsn->getScheme();
30+
31+
if ('spothit' !== $scheme) {
32+
throw new UnsupportedSchemeException($dsn, 'spothit', $this->getSupportedSchemes());
33+
}
34+
35+
$token = $this->getUser($dsn);
36+
$from = $dsn->getOption('from');
37+
$host = 'default' === $dsn->getHost() ? null : $dsn->getHost();
38+
$port = $dsn->getPort();
39+
40+
return (new SpotHitTransport($token, $from, $this->client, $this->dispatcher))->setHost($host)->setPort($port);
41+
}
42+
43+
protected function getSupportedSchemes(): array
44+
{
45+
return ['spothit'];
46+
}
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
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\SpotHit\Tests;
13+
14+
use Symfony\Component\Notifier\Bridge\SpotHit\SpotHitTransportFactory;
15+
use Symfony\Component\Notifier\Tests\TransportFactoryTestCase;
16+
use Symfony\Component\Notifier\Transport\TransportFactoryInterface;
17+
18+
final class SpotHitTransportFactoryTest extends TransportFactoryTestCase
19+
{
20+
/**
21+
* @return SpotHitTransportFactory
22+
*/
23+
public function createFactory(): TransportFactoryInterface
24+
{
25+
return new SpotHitTransportFactory();
26+
}
27+
28+
public function createProvider(): iterable
29+
{
30+
yield [
31+
'spothit://spot-hit.fr',
32+
'spothit://api_token@default',
33+
];
34+
yield [
35+
'spothit://spot-hit.fr?from=MyCompany',
36+
'spothit://api_token@default?from=MyCompany',
37+
];
38+
}
39+
40+
public function supportsProvider(): iterable
41+
{
42+
yield [true, 'spothit://api_token@default?from=MyCompany'];
43+
yield [false, 'somethingElse://api_token@default?from=MyCompany'];
44+
}
45+
46+
public function unsupportedSchemeProvider(): iterable
47+
{
48+
yield ['foobar://api_token@default?from=MyCompany'];
49+
yield ['foobar://api_token@default'];
50+
}
51+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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\SpotHit\Tests;
13+
14+
use Symfony\Component\Notifier\Bridge\SpotHit\SpotHitTransport;
15+
use Symfony\Component\Notifier\Message\ChatMessage;
16+
use Symfony\Component\Notifier\Message\MessageInterface;
17+
use Symfony\Component\Notifier\Message\SmsMessage;
18+
use Symfony\Component\Notifier\Tests\TransportTestCase;
19+
use Symfony\Component\Notifier\Transport\TransportInterface;
20+
use Symfony\Contracts\HttpClient\HttpClientInterface;
21+
22+
final class SpotHitTransportTest extends TransportTestCase
23+
{
24+
/**
25+
* @return SpotHitTransport
26+
*/
27+
public function createTransport(?HttpClientInterface $client = null): TransportInterface
28+
{
29+
return (new SpotHitTransport('api_token', 'MyCompany', $client ?: $this->createMock(HttpClientInterface::class)))->setHost('host.test');
30+
}
31+
32+
public function toStringProvider(): iterable
33+
{
34+
yield ['spothit://host.test?from=MyCompany', $this->createTransport()];
35+
}
36+
37+
public function supportedMessagesProvider(): iterable
38+
{
39+
yield [new SmsMessage('0611223344', 'Hello!')];
40+
yield [new SmsMessage('+33611223344', 'Hello!')];
41+
}
42+
43+
public function unsupportedMessagesProvider(): iterable
44+
{
45+
yield [new ChatMessage('Hello!')];
46+
yield [$this->createMock(MessageInterface::class)];
47+
}
48+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
{
2+
"name": "symfony/spothit-notifier",
3+
"type": "symfony-bridge",
4+
"description": "Symfony Spot-Hit Notifier Bridge",
5+
"keywords": ["sms", "spot-hit", "notifier", "symfony"],
6+
"homepage": "https://symfony.com",
7+
"license": "MIT",
8+
"authors": [
9+
{
10+
"name": "James Hemery",
11+
"homepage": "https://github.com/JamesHemery"
12+
},
13+
{
14+
"name": "Yield Studio",
15+
"homepage": "https://github.com/YieldStudio"
16+
},
17+
{
18+
"name": "Symfony Community",
19+
"homepage": "https://symfony.com/contributors"
20+
}
21+
],
22+
"require": {
23+
"php": ">=7.2.5",
24+
"symfony/http-client": "^4.3|^5.1",
25+
"symfony/notifier": "^5.3"
26+
},
27+
"autoload": {
28+
"psr-4": { "Symfony\\Component\\Notifier\\Bridge\\SpotHit\\": "" },
29+
"exclude-from-classmap": [
30+
"/Tests/"
31+
]
32+
},
33+
"minimum-stability": "dev"
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
3+
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/5.2/phpunit.xsd"
5+
backupGlobals="false"
6+
colors="true"
7+
bootstrap="vendor/autoload.php"
8+
failOnRisky="true"
9+
failOnWarning="true"
10+
>
11+
<php>
12+
<ini name="error_reporting" value="-1" />
13+
</php>
14+
15+
<testsuites>
16+
<testsuite name="Symfony Spot-Hit Notifier Test Suite">
17+
<directory>./Tests/</directory>
18+
</testsuite>
19+
</testsuites>
20+
21+
<filter>
22+
<whitelist>
23+
<directory>./</directory>
24+
<exclude>
25+
<directory>./Resources</directory>
26+
<directory>./Tests</directory>
27+
<directory>./vendor</directory>
28+
</exclude>
29+
</whitelist>
30+
</filter>
31+
</phpunit>

src/Symfony/Component/Notifier/Exception/UnsupportedSchemeException.php

+4
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ class UnsupportedSchemeException extends LogicException
6868
'class' => Bridge\FreeMobile\FreeMobileTransportFactory::class,
6969
'package' => 'symfony/free-mobile-notifier',
7070
],
71+
'spothit' => [
72+
'class' => Bridge\SpotHit\SpotHitTransportFactory::class,
73+
'package' => 'symfony/spothit-notifier',
74+
],
7175
'ovhcloud' => [
7276
'class' => Bridge\OvhCloud\OvhCloudTransportFactory::class,
7377
'package' => 'symfony/ovh-cloud-notifier',

0 commit comments

Comments
 (0)