Skip to content

Commit b15a479

Browse files
committed
feature #53927 [Notifier] Add new Pushy notifier bridge (stloyd)
This PR was merged into the 7.1 branch. Discussion ---------- [Notifier] Add new Pushy notifier bridge | Q | A | ------------- | --- | Branch? | 7.1 | Bug fix? | no | New feature? | yes <!-- please update src/**/CHANGELOG.md files --> | Deprecations? | no <!-- please update UPGRADE-*.md and src/**/CHANGELOG.md files --> | License | MIT This PR adds a new bridge for [Pushy](https://pushy.me). Based on documentation available: https://pushy.me/docs/api/send-notifications <!-- Replace this notice by a description of 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 ------- b8f666d Add new Pushy notifier bridge
2 parents fd53091 + b8f666d commit b15a479

19 files changed

+706
-0
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2804,6 +2804,7 @@ private function registerNotifierConfiguration(array $config, ContainerBuilder $
28042804
NotifierBridge\PagerDuty\PagerDutyTransportFactory::class => 'notifier.transport_factory.pager-duty',
28052805
NotifierBridge\Plivo\PlivoTransportFactory::class => 'notifier.transport_factory.plivo',
28062806
NotifierBridge\Pushover\PushoverTransportFactory::class => 'notifier.transport_factory.pushover',
2807+
NotifierBridge\Pushy\PushyTransportFactory::class => 'notifier.transport_factory.pushy',
28072808
NotifierBridge\Redlink\RedlinkTransportFactory::class => 'notifier.transport_factory.redlink',
28082809
NotifierBridge\RingCentral\RingCentralTransportFactory::class => 'notifier.transport_factory.ring-central',
28092810
NotifierBridge\RocketChat\RocketChatTransportFactory::class => 'notifier.transport_factory.rocket-chat',

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
'ovh-cloud' => Bridge\OvhCloud\OvhCloudTransportFactory::class,
8888
'plivo' => Bridge\Plivo\PlivoTransportFactory::class,
8989
'pushover' => Bridge\Pushover\PushoverTransportFactory::class,
90+
'pushy' => Bridge\Pushy\PushyTransportFactory::class,
9091
'redlink' => Bridge\Redlink\RedlinkTransportFactory::class,
9192
'ring-central' => Bridge\RingCentral\RingCentralTransportFactory::class,
9293
'sendberry' => Bridge\Sendberry\SendberryTransportFactory::class,
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+
7.1
5+
---
6+
7+
* Add the bridge
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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\Pushy\Enum;
13+
14+
/**
15+
* @author Joseph Bielawski <stloyd@gmail.com>
16+
*/
17+
enum InterruptionLevel: string
18+
{
19+
case ACTIVE = 'active';
20+
case CRITICAL = 'critical';
21+
case PASSIVE = 'passive';
22+
case TIME_SENSITIVE = 'time-sensitive';
23+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright (c) 2024-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: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
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\Pushy;
13+
14+
use Symfony\Component\Notifier\Bridge\Pushy\Enum\InterruptionLevel;
15+
use Symfony\Component\Notifier\Exception\InvalidArgumentException;
16+
use Symfony\Component\Notifier\Message\MessageOptionsInterface;
17+
use Symfony\Component\Notifier\Notification\Notification;
18+
19+
/**
20+
* @author Joseph Bielawski <stloyd@gmail.com>
21+
*
22+
* @see https://pushy.me/docs/api/send-notifications
23+
*/
24+
final class PushyOptions implements MessageOptionsInterface
25+
{
26+
public function __construct(
27+
private array $options = [],
28+
) {
29+
}
30+
31+
public static function fromNotification(Notification $notification): self
32+
{
33+
$options = new self();
34+
$options->interruptionLevel(
35+
match ($notification->getImportance()) {
36+
Notification::IMPORTANCE_URGENT => InterruptionLevel::CRITICAL,
37+
Notification::IMPORTANCE_HIGH => InterruptionLevel::TIME_SENSITIVE,
38+
Notification::IMPORTANCE_MEDIUM => InterruptionLevel::ACTIVE,
39+
Notification::IMPORTANCE_LOW => InterruptionLevel::PASSIVE,
40+
}
41+
);
42+
43+
return $options;
44+
}
45+
46+
public function toArray(): array
47+
{
48+
return $this->options;
49+
}
50+
51+
public function getRecipientId(): ?string
52+
{
53+
return $this->options['to'] ?? null;
54+
}
55+
56+
/**
57+
* @see https://pushy.me/docs/api/send-notifications#request-schema
58+
*
59+
* @param string|string[] $to
60+
*
61+
* @return $this
62+
*/
63+
public function to(string|array $to): static
64+
{
65+
$this->options['to'] = $to;
66+
67+
return $this;
68+
}
69+
70+
/**
71+
* @see https://pushy.me/docs/api/send-notifications#request-schema
72+
*
73+
* @return $this
74+
*/
75+
public function contentAvailable(bool $bool): static
76+
{
77+
$this->options['content_available'] = $bool;
78+
79+
return $this;
80+
}
81+
82+
/**
83+
* @see https://pushy.me/docs/api/send-notifications#request-schema
84+
*
85+
* @return $this
86+
*/
87+
public function mutableContent(bool $bool): static
88+
{
89+
$this->options['mutable_content'] = $bool;
90+
91+
return $this;
92+
}
93+
94+
/**
95+
* @see https://pushy.me/docs/api/send-notifications#request-schema
96+
*
97+
* @return $this
98+
*/
99+
public function ttl(int $seconds): static
100+
{
101+
if ($seconds > (86400 * 365)) {
102+
throw new InvalidArgumentException('Pushy notification time to live cannot exceed 365 days.');
103+
}
104+
105+
$this->options['time_to_live'] = $seconds;
106+
107+
return $this;
108+
}
109+
110+
/**
111+
* @see https://pushy.me/docs/api/send-notifications#request-schema
112+
*
113+
* @return $this
114+
*/
115+
public function schedule(int $seconds): static
116+
{
117+
if (false === \DateTime::createFromFormat('U', $seconds)) {
118+
throw new InvalidArgumentException('Pushy notification schedule time must be correct Unix timestamp.');
119+
}
120+
121+
if (\DateTime::createFromFormat('U', $seconds) >= new \DateTime('+1 year')) {
122+
throw new InvalidArgumentException('Pushy notification schedule time cannot exceed 1 year.');
123+
}
124+
125+
$this->options['schedule'] = $seconds;
126+
127+
return $this;
128+
}
129+
130+
/**
131+
* @see https://pushy.me/docs/api/send-notifications#request-schema
132+
*
133+
* @return $this
134+
*/
135+
public function collapseKey(string $collapseKey): static
136+
{
137+
if (32 < \strlen($collapseKey)) {
138+
throw new InvalidArgumentException('Pushy notification collapse key cannot be longer than 32 characters.');
139+
}
140+
141+
$this->options['collapse_key'] = $collapseKey;
142+
143+
return $this;
144+
}
145+
146+
/**
147+
* @return $this
148+
*/
149+
public function body(string $body): static
150+
{
151+
$this->options['notification']['body'] = $body;
152+
153+
return $this;
154+
}
155+
156+
/**
157+
* @see https://pushy.me/docs/api/send-notifications#request-schema
158+
*
159+
* @return $this
160+
*/
161+
public function badge(int $badge): static
162+
{
163+
$this->options['notification']['badge'] = $badge;
164+
165+
return $this;
166+
}
167+
168+
/**
169+
* @see https://pushy.me/docs/api/send-notifications#request-schema
170+
*
171+
* @return $this
172+
*/
173+
public function threadId(int $threadId): static
174+
{
175+
$this->options['notification']['thread_id'] = $threadId;
176+
177+
return $this;
178+
}
179+
180+
/**
181+
* @see https://pushy.me/docs/api/send-notifications#request-schema
182+
*
183+
* @return $this
184+
*/
185+
public function interruptionLevel(InterruptionLevel $interruptionLevel): static
186+
{
187+
$this->options['notification']['interruption_level'] = $interruptionLevel->value;
188+
189+
return $this;
190+
}
191+
}
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
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\Pushy;
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\PushMessage;
19+
use Symfony\Component\Notifier\Message\SentMessage;
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 Joseph Bielawski <stloyd@gmail.com>
27+
*/
28+
final class PushyTransport extends AbstractTransport
29+
{
30+
protected const HOST = 'api.pushy.me';
31+
32+
public function __construct(
33+
#[\SensitiveParameter] private readonly string $apiKey,
34+
?HttpClientInterface $client = null,
35+
?EventDispatcherInterface $dispatcher = null,
36+
) {
37+
parent::__construct($client, $dispatcher);
38+
}
39+
40+
public function supports(MessageInterface $message): bool
41+
{
42+
return $message instanceof PushMessage && (null === $message->getOptions() || $message->getOptions() instanceof PushyOptions);
43+
}
44+
45+
public function __toString(): string
46+
{
47+
return sprintf('pushy://%s', $this->getEndpoint());
48+
}
49+
50+
protected function doSend(MessageInterface $message): SentMessage
51+
{
52+
if (!$message instanceof PushMessage) {
53+
throw new UnsupportedMessageTypeException(__CLASS__, PushMessage::class, $message);
54+
}
55+
56+
$options = $message->getOptions()?->toArray() ?? [];
57+
$options['data'] = $message->getContent();
58+
$options['notification']['title'] = $message->getSubject();
59+
$options['to'] ??= $message->getRecipientId();
60+
61+
if (!$options['to']) {
62+
throw new InvalidArgumentException(sprintf('The "%s" transport required the "to" option to be set.', __CLASS__));
63+
}
64+
65+
$endpoint = sprintf('https://%s?api_key=%s', $this->getEndpoint(), $this->apiKey);
66+
$response = $this->client->request('POST', $endpoint, [
67+
'headers' => [
68+
'Accept' => 'application/json',
69+
'Content-Type' => 'application/json',
70+
],
71+
'json' => array_filter($options),
72+
]);
73+
74+
try {
75+
$statusCode = $response->getStatusCode();
76+
} catch (TransportExceptionInterface $e) {
77+
throw new TransportException('Could not reach the remote Pushy server.', $response, 0, $e);
78+
}
79+
80+
if (200 !== $statusCode) {
81+
throw new TransportException(sprintf('Unable to send the Pushy push notification: "%s".', $response->getContent(false)), $response);
82+
}
83+
84+
$result = $response->toArray(false);
85+
86+
if (!isset($result['id'])) {
87+
throw new TransportException(sprintf('Unable to find the message ID within the Pushy response: "%s".', $response->getContent(false)), $response);
88+
}
89+
90+
$sentMessage = new SentMessage($message, (string) $this);
91+
$sentMessage->setMessageId($result['id']);
92+
93+
return $sentMessage;
94+
}
95+
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy