Skip to content

Commit abcc723

Browse files
committed
Add options to turbo stream
1 parent dec3eb3 commit abcc723

11 files changed

+115
-34
lines changed

src/Turbo/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# CHANGELOG
22

3+
## 2.23.0
4+
5+
- Add support for providing options to the EventSource via `turbo_stream_listen`
6+
37
## 2.22.0
48

59
- Add `<twig:Turbo:Stream>` component

src/Turbo/assets/dist/turbo_stream_controller.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ export default class extends Controller {
44
topic: StringConstructor;
55
topics: ArrayConstructor;
66
hub: StringConstructor;
7+
withCredentials: BooleanConstructor;
78
};
89
es: EventSource | undefined;
910
url: string | undefined;
1011
readonly topicValue: string;
1112
readonly topicsValue: string[];
13+
readonly withCredentialsValue: boolean;
1214
readonly hubValue: string;
1315
readonly hasHubValue: boolean;
1416
readonly hasTopicValue: boolean;

src/Turbo/assets/dist/turbo_stream_controller.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class default_1 extends Controller {
2323
}
2424
connect() {
2525
if (this.url) {
26-
this.es = new EventSource(this.url);
26+
this.es = new EventSource(this.url, { withCredentials: this.withCredentialsValue });
2727
connectStreamSource(this.es);
2828
}
2929
}
@@ -38,6 +38,7 @@ default_1.values = {
3838
topic: String,
3939
topics: Array,
4040
hub: String,
41+
withCredentials: Boolean,
4142
};
4243

4344
export { default_1 as default };

src/Turbo/assets/src/turbo_stream_controller.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@ export default class extends Controller {
1818
topic: String,
1919
topics: Array,
2020
hub: String,
21+
withCredentials: Boolean,
2122
};
2223
es: EventSource | undefined;
2324
url: string | undefined;
2425

2526
declare readonly topicValue: string;
2627
declare readonly topicsValue: string[];
28+
declare readonly withCredentialsValue: boolean;
2729
declare readonly hubValue: string;
2830
declare readonly hasHubValue: boolean;
2931
declare readonly hasTopicValue: boolean;
@@ -50,7 +52,7 @@ export default class extends Controller {
5052

5153
connect() {
5254
if (this.url) {
53-
this.es = new EventSource(this.url);
55+
this.es = new EventSource(this.url, { withCredentials: this.withCredentialsValue });
5456
connectStreamSource(this.es);
5557
}
5658
}

src/Turbo/config/services.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\DependencyInjection\Loader\Configurator;
1313

14+
use Symfony\Component\Mercure\Authorization;
15+
use Symfony\UX\Turbo\Twig\TurboRuntime;
1416
use Symfony\UX\Turbo\Broadcaster\BroadcasterInterface;
1517
use Symfony\UX\Turbo\Broadcaster\IdAccessor;
1618
use Symfony\UX\Turbo\Broadcaster\ImuxBroadcaster;
@@ -45,9 +47,17 @@
4547
->decorate('turbo.broadcaster.imux')
4648

4749
->set('turbo.twig.extension', TwigExtension::class)
48-
->args([tagged_locator('turbo.renderer.stream_listen', 'transport'), abstract_arg('default')])
4950
->tag('twig.extension')
5051

52+
->set('turbo.twig.runtime', TurboRuntime::class)
53+
->args([
54+
tagged_locator('turbo.renderer.stream_listen', 'transport'),
55+
abstract_arg('default'),
56+
service(Authorization::class)->nullOnInvalid(),
57+
service('request_stack')->nullOnInvalid(),
58+
])
59+
->tag('twig.runtime')
60+
5161
->set('turbo.doctrine.event_listener', BroadcastListener::class)
5262
->args([
5363
service('turbo.broadcaster.imux'),

src/Turbo/src/Bridge/Mercure/TurboStreamListenRenderer.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,12 @@ public function __construct(
4242
$this->stimulusHelper = $stimulus;
4343
}
4444

45-
public function renderTurboStreamListen(Environment $env, $topic): string
45+
public function renderTurboStreamListen(Environment $env, $topic /* array $eventSourceOptions = [] */): string
4646
{
47+
if (\func_num_args() > 2) {
48+
$eventSourceOptions = func_get_arg(2);
49+
}
50+
4751
$topics = $topic instanceof TopicSet
4852
? array_map($this->resolveTopic(...), $topic->getTopics())
4953
: [$this->resolveTopic($topic)];
@@ -55,6 +59,10 @@ public function renderTurboStreamListen(Environment $env, $topic): string
5559
$controllerAttributes['topic'] = current($topics);
5660
}
5761

62+
if (isset($eventSourceOptions, $eventSourceOptions['withCredentials'])) {
63+
$controllerAttributes['withCredentials'] = $eventSourceOptions['withCredentials'];
64+
}
65+
5866
$stimulusAttributes = $this->stimulusHelper->createStimulusAttributes();
5967
$stimulusAttributes->addController(
6068
'symfony/ux-turbo/mercure-turbo-stream',

src/Turbo/src/DependencyInjection/TurboExtension.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public function load(array $configs, ContainerBuilder $container): void
3737

3838
$loader = (new PhpFileLoader($container, new FileLocator(\dirname(__DIR__).'/../config')));
3939
$loader->load('services.php');
40-
$container->getDefinition('turbo.twig.extension')->replaceArgument(1, $config['default_transport']);
40+
$container->getDefinition('turbo.twig.runtime')->replaceArgument(1, $config['default_transport']);
4141

4242
$this->registerTwig($config, $container);
4343
$this->registerBroadcast($config, $container, $loader);

src/Turbo/src/Twig/TurboRuntime.php

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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\UX\Turbo\Twig;
13+
14+
use Psr\Container\ContainerInterface;
15+
use Symfony\Component\HttpFoundation\RequestStack;
16+
use Symfony\Component\Mercure\Authorization;
17+
use Symfony\UX\Turbo\Bridge\Mercure\TopicSet;
18+
use Twig\Extension\RuntimeExtensionInterface;
19+
use Twig\Environment;
20+
21+
/**
22+
* @author Pierre Ambroise <pierre27.ambroise@gmail.com>
23+
*
24+
* @internal
25+
*/
26+
class TurboRuntime implements RuntimeExtensionInterface
27+
{
28+
public function __construct(
29+
private ContainerInterface $turboStreamListenRenderers,
30+
private string $default,
31+
private ?Authorization $authorization = null,
32+
private ?RequestStack $requestStack = null,
33+
) {
34+
}
35+
36+
/**
37+
* @param object|string|array<object|string> $topic
38+
* @param array<string, mixed> $options
39+
*/
40+
public function renderTurboStreamListen(Environment $env, $topic, ?string $transport = null, array $options = []): string
41+
{
42+
$transport ??= $this->default;
43+
44+
if (!$this->turboStreamListenRenderers->has($transport)) {
45+
throw new \InvalidArgumentException(\sprintf('The Turbo stream transport "%s" does not exist.', $transport));
46+
}
47+
48+
if (\is_array($topic)) {
49+
$topic = new TopicSet($topic);
50+
}
51+
52+
if (
53+
null !== $this->authorization
54+
&& null !== $this->requestStack
55+
&& (isset($options['subscribe']) || isset($options['publish']) || isset($options['additionalClaims']))
56+
&& null !== $request = $this->requestStack->getMainRequest()
57+
) {
58+
$this->authorization->setCookie(
59+
$request,
60+
$options['subscribe'] ?? [],
61+
$options['publish'] ?? [],
62+
$options['additionalClaims'] ?? [],
63+
$transport,
64+
);
65+
66+
unset($options['subscribe'], $options['publish'], $options['additionalClaims']);
67+
}
68+
69+
return $this->turboStreamListenRenderers->get($transport)->renderTurboStreamListen($env, $topic, $options);
70+
}
71+
}

src/Turbo/src/Twig/TurboStreamListenRendererInterface.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@ interface TurboStreamListenRendererInterface
2323
/**
2424
* @param string|object $topic
2525
*/
26-
public function renderTurboStreamListen(Environment $env, $topic): string;
26+
public function renderTurboStreamListen(Environment $env, $topic /* , array $eventSourceOptions = [] */): string;
2727
}

src/Turbo/src/Twig/TwigExtension.php

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,45 +11,20 @@
1111

1212
namespace Symfony\UX\Turbo\Twig;
1313

14-
use Psr\Container\ContainerInterface;
15-
use Symfony\UX\Turbo\Bridge\Mercure\TopicSet;
16-
use Twig\Environment;
14+
use Symfony\UX\Turbo\Twig\TurboRuntime;
1715
use Twig\Extension\AbstractExtension;
1816
use Twig\TwigFunction;
1917

2018
/**
2119
* @author Kévin Dunglas <kevin@dunglas.fr>
20+
* @author Pierre Ambroise <pierre27.ambroise@gmail.com>
2221
*/
2322
final class TwigExtension extends AbstractExtension
2423
{
25-
public function __construct(
26-
private ContainerInterface $turboStreamListenRenderers,
27-
private string $default,
28-
) {
29-
}
30-
3124
public function getFunctions(): array
3225
{
3326
return [
34-
new TwigFunction('turbo_stream_listen', $this->turboStreamListen(...), ['needs_environment' => true, 'is_safe' => ['html']]),
27+
new TwigFunction('turbo_stream_listen', [TurboRuntime::class, 'renderTurboStreamListen'], ['needs_environment' => true, 'is_safe' => ['html']]),
3528
];
3629
}
37-
38-
/**
39-
* @param object|string|array<object|string> $topic
40-
*/
41-
public function turboStreamListen(Environment $env, $topic, ?string $transport = null): string
42-
{
43-
$transport ??= $this->default;
44-
45-
if (!$this->turboStreamListenRenderers->has($transport)) {
46-
throw new \InvalidArgumentException(\sprintf('The Turbo stream transport "%s" does not exist.', $transport));
47-
}
48-
49-
if (\is_array($topic)) {
50-
$topic = new TopicSet($topic);
51-
}
52-
53-
return $this->turboStreamListenRenderers->get($transport)->renderTurboStreamListen($env, $topic);
54-
}
5530
}

src/Turbo/tests/Bridge/Mercure/TurboStreamListenRendererTest.php

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,5 +71,13 @@ public static function provideTestCases(): iterable
7171
? 'data-controller="symfony--ux-turbo--mercure-turbo-stream" data-symfony--ux-turbo--mercure-turbo-stream-hub-value="http://127.0.0.1:3000/.well-known/mercure" data-symfony--ux-turbo--mercure-turbo-stream-topics-value="[&quot;a_topic&quot;,&quot;AppEntityBook&quot;,&quot;https:\/\/symfony.com\/ux-turbo\/App%5CEntity%5CBook\/123&quot;]"'
7272
: 'data-controller="symfony--ux-turbo--mercure-turbo-stream" data-symfony--ux-turbo--mercure-turbo-stream-hub-value="http&#x3A;&#x2F;&#x2F;127.0.0.1&#x3A;3000&#x2F;.well-known&#x2F;mercure" data-symfony--ux-turbo--mercure-turbo-stream-topics-value="&#x5B;&quot;a_topic&quot;,&quot;AppEntityBook&quot;,&quot;https&#x3A;&#x5C;&#x2F;&#x5C;&#x2F;symfony.com&#x5C;&#x2F;ux-turbo&#x5C;&#x2F;App&#x25;5CEntity&#x25;5CBook&#x5C;&#x2F;123&quot;&#x5D;"',
7373
];
74+
75+
yield [
76+
"{{ turbo_stream_listen('a_topic', 'default', { withCredentials: true }) }}",
77+
[],
78+
$newEscape
79+
? 'data-controller="symfony--ux-turbo--mercure-turbo-stream" data-symfony--ux-turbo--mercure-turbo-stream-hub-value="http://127.0.0.1:3000/.well-known/mercure" data-symfony--ux-turbo--mercure-turbo-stream-topic-value="a_topic" data-symfony--ux-turbo--mercure-turbo-stream-with-credentials-value="true"'
80+
: 'data-controller="symfony--ux-turbo--mercure-turbo-stream" data-symfony--ux-turbo--mercure-turbo-stream-hub-value="http&#x3A;&#x2F;&#x2F;127.0.0.1&#x3A;3000&#x2F;.well-known&#x2F;mercure" data-symfony--ux-turbo--mercure-turbo-stream-topic-value="a_topic" data-symfony--ux-turbo--mercure-turbo-stream-with-credentials-value="true"',
81+
];
7482
}
7583
}

0 commit comments

Comments
 (0)