From adc40a68aa583cb141fab2d9d019db744a3bc8d6 Mon Sep 17 00:00:00 2001 From: Beno!t POLASZEK Date: Tue, 3 May 2022 18:48:45 +0200 Subject: [PATCH] feat: subscription events (WIP) --- src/Hub/Controller/SubscribeController.php | 14 ++++++++- src/Hub/Hub.php | 23 +++++++++++++++ src/Subscription/Subscription.php | 34 +++++++++++++++++++++- 3 files changed, 69 insertions(+), 2 deletions(-) diff --git a/src/Hub/Controller/SubscribeController.php b/src/Hub/Controller/SubscribeController.php index 1cffadb..38fe6fc 100644 --- a/src/Hub/Controller/SubscribeController.php +++ b/src/Hub/Controller/SubscribeController.php @@ -56,9 +56,10 @@ public function __invoke( ): ResponseInterface { $subscribedTopics = $this->extractSubscribedTopics($request); $allowedTopics = $this->extractAllowedTopics($request); + $payload = $this->extractPayload($request); $lastEventId = extract_last_event_id($request); - $subscriber = new Subscriber($subscribedTopics); + $subscriber = new Subscriber($subscribedTopics, $payload); if (null !== $lastEventId) { async( @@ -138,4 +139,15 @@ private function extractAllowedTopics(ServerRequestInterface $request): ?array return $jwt->claims()->get('mercure')['subscribe'] ?? null; } + + private function extractPayload(ServerRequestInterface $request): mixed + { + /** @var UnencryptedToken|null $jwt */ + $jwt = $request->getAttribute('token'); + if (null === $jwt) { + return null; + } + + return $jwt->claims()->get('mercure')['payload'] ?? null; + } } diff --git a/src/Hub/Hub.php b/src/Hub/Hub.php index 0cadef0..5906a91 100644 --- a/src/Hub/Hub.php +++ b/src/Hub/Hub.php @@ -8,6 +8,7 @@ use Freddie\Hub\Middleware\HttpExceptionConverterMiddleware; use Freddie\Hub\Transport\PHP\PHPTransport; use Freddie\Hub\Transport\TransportInterface; +use Freddie\Message\Message; use Freddie\Message\Update; use Freddie\Subscription\Subscriber; use Generator; @@ -17,12 +18,14 @@ use Symfony\Component\OptionsResolver\OptionsResolver; use function array_key_exists; +use function json_encode; use function sprintf; final class Hub implements HubInterface { public const DEFAULT_OPTIONS = [ 'allow_anonymous' => true, + 'enable_subscription_events' => true, ]; /** @@ -46,6 +49,7 @@ public function __construct( $resolver = new OptionsResolver(); $resolver->setDefaults(self::DEFAULT_OPTIONS); $resolver->setAllowedTypes('allow_anonymous', 'bool'); + $resolver->setAllowedTypes('enable_subscription_events', 'bool'); $this->options = $resolver->resolve($options); foreach ($controllers as $controller) { $controller->setHub($this); @@ -79,11 +83,30 @@ public function publish(Update $update): PromiseInterface public function subscribe(Subscriber $subscriber): void { $this->transport->subscribe($subscriber); + if (true === $this->getOption('enable_subscription_events')) { + foreach ($subscriber->subscriptions as $subscription) { + $update = new Update( + $subscription->id, + new Message(data: (string) json_encode($subscription), private: true) + ); + Loop::futureTick(fn () => $this->publish($update)); + } + } } public function unsubscribe(Subscriber $subscriber): void { $this->transport->unsubscribe($subscriber); + if (true === $this->getOption('enable_subscription_events')) { + $subscriber->active = false; + foreach ($subscriber->subscriptions as $subscription) { + $update = new Update( + $subscription->id, + new Message(data: (string) json_encode($subscription), private: true) + ); + Loop::futureTick(fn () => $this->publish($update)); + } + } } public function reconciliate(string $lastEventID): Generator diff --git a/src/Subscription/Subscription.php b/src/Subscription/Subscription.php index f3d0cd7..040b985 100644 --- a/src/Subscription/Subscription.php +++ b/src/Subscription/Subscription.php @@ -4,11 +4,43 @@ namespace Freddie\Subscription; -final class Subscription +use JsonSerializable; + +use function sprintf; +use function urlencode; + +final class Subscription implements JsonSerializable { + public readonly string $id; + public function __construct( public Subscriber $subscriber, public string $topic, ) { + $this->id = sprintf( + '/.well-known/mercure/subscriptions/%s/%s', + urlencode($this->topic), + urlencode((string) $subscriber->id) + ); + } + + /** + * @return array + */ + public function jsonSerialize(): array + { + $output = [ + 'id' => $this->id, + 'type' => 'Subscription', + 'subscriber' => (string) $this->subscriber->id, + 'topic' => $this->topic, + 'active' => $this->subscriber->active, + ]; + + if (null !== $this->subscriber->payload) { + $output['payload'] = $this->subscriber->payload; + } + + return $output; } }