@@ -159,20 +159,19 @@ service, including controllers::
159
159
namespace App\Controller;
160
160
161
161
use Symfony\Component\HttpFoundation\Response;
162
- use Symfony\Component\Mercure\PublisherInterface ;
162
+ use Symfony\Component\Mercure\HubInterface ;
163
163
use Symfony\Component\Mercure\Update;
164
164
165
165
class PublishController
166
166
{
167
- public function __invoke(PublisherInterface $publisher ): Response
167
+ public function __invoke(HubInterface $hub ): Response
168
168
{
169
169
$update = new Update(
170
170
'http://example.com/books/1',
171
171
json_encode(['status' => 'OutOfStock'])
172
172
);
173
173
174
- // The Publisher service is an invokable object
175
- $publisher($update);
174
+ $hub->publish($update);
176
175
177
176
return new Response('published!');
178
177
}
@@ -297,17 +296,14 @@ by using the ``AbstractController::addLink`` helper method::
297
296
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
298
297
use Symfony\Component\HttpFoundation\JsonResponse;
299
298
use Symfony\Component\HttpFoundation\Request;
300
- use Symfony\Component\WebLink\Link ;
299
+ use Symfony\Component\Mercure\Discovery ;
301
300
302
301
class DiscoverController extends AbstractController
303
302
{
304
- public function __invoke(Request $request): JsonResponse
303
+ public function __invoke(Request $request, Discovery $discovery ): JsonResponse
305
304
{
306
- // This parameter is automatically created by the MercureBundle
307
- $hubUrl = $this->getParameter('mercure.default_hub');
308
-
309
305
// Link: <http://localhost:3000/.well-known/mercure>; rel="mercure"
310
- $this ->addLink($request, new Link('mercure', $hubUrl) );
306
+ $discovery ->addLink($request);
311
307
312
308
return $this->json([
313
309
'@id' => '/books/1',
@@ -346,13 +342,13 @@ of the ``Update`` constructor to ``true``::
346
342
// src/Controller/Publish.php
347
343
namespace App\Controller;
348
344
345
+ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
349
346
use Symfony\Component\HttpFoundation\Response;
350
- use Symfony\Component\Mercure\PublisherInterface;
351
347
use Symfony\Component\Mercure\Update;
352
348
353
- class PublishController
349
+ class PublishController extends AbstractController
354
350
{
355
- public function __invoke(PublisherInterface $publisher ): Response
351
+ public function __invoke(HubInterface $hub ): Response
356
352
{
357
353
$update = new Update(
358
354
'http://example.com/books/1',
@@ -362,7 +358,7 @@ of the ``Update`` constructor to ``true``::
362
358
363
359
// Publisher's JWT must contain this topic, a URI template it matches or * in mercure.publish or you'll get a 401
364
360
// Subscriber's JWT must contain this topic, a URI template it matches or * in mercure.subscribe to receive the update
365
- $publisher ($update);
361
+ $hub->publish ($update);
366
362
367
363
return new Response('private update published!');
368
364
}
@@ -406,50 +402,73 @@ This cookie will be automatically sent by the web browser when connecting to the
406
402
Then, the Hub will verify the validity of the provided JWT, and extract the topic selectors
407
403
from it.
408
404
409
- To generate the JWT, we'll use the `` lcobucci/jwt `` library. Install it :
405
+ add your JWT secret to the configuration as follow : :
410
406
411
- .. code-block :: terminal
407
+ .. configuration-block ::
408
+
409
+ .. code-block :: yaml
410
+
411
+ # config/packages/mercure.yaml
412
+ mercure :
413
+ hubs :
414
+ default :
415
+ url : https://mercure-hub.example.com/.well-known/mercure
416
+ jwt :
417
+ secret : ' !ChangeMe!'
412
418
413
- $ composer require lcobucci/jwt
419
+ .. code-block :: xml
420
+
421
+ <!-- config/packages/mercure.xml -->
422
+ <?xml version =" 1.0" encoding =" UTF-8" ?>
423
+ <config >
424
+ <hub
425
+ name =" default"
426
+ url =" https://mercure-hub.example.com/.well-known/mercure"
427
+ >
428
+ <jwt secret =" !ChangeMe!" />
429
+ </hub >
430
+ </config >
431
+
432
+ .. code-block :: php
433
+
434
+ // config/packages/mercure.php
435
+ $container->loadFromExtension('mercure', [
436
+ 'hubs' => [
437
+ 'default' => [
438
+ 'url' => 'https://mercure-hub.example.com/.well-known/mercure',
439
+ 'jwt' => [
440
+ 'secret' => '!ChangeMe!',
441
+ ]
442
+ ],
443
+ ],
444
+ ]);
414
445
415
446
And here is the controller::
416
447
417
448
// src/Controller/DiscoverController.php
418
449
namespace App\Controller;
419
450
420
- use Lcobucci\JWT\Configuration;
421
- use Lcobucci\JWT\Signer\Hmac\Sha256;
422
- use Lcobucci\JWT\Signer\Key;
423
451
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
424
452
use Symfony\Component\HttpFoundation\Cookie;
425
453
use Symfony\Component\HttpFoundation\Request;
426
454
use Symfony\Component\HttpFoundation\Response;
427
- use Symfony\Component\WebLink\Link;
455
+ use Symfony\Component\Mercure\Authorization;
456
+ use Symfony\Component\Mercure\Discovery;
428
457
429
458
class DiscoverController extends AbstractController
430
459
{
431
- public function __invoke(Request $request): Response
460
+ public function __invoke(Request $request, Discovery $discovery, Authorization $authorization ): Response
432
461
{
433
- $hubUrl = $this->getParameter('mercure.default_hub');
434
- $this->addLink($request, new Link('mercure', $hubUrl));
435
-
436
- $key = Key\InMemory::plainText('mercure_secret_key'); // don't forget to set this parameter! Test value: !ChangeMe!
437
- $configuration = Configuration::forSymmetricSigner(new Sha256(), $key);
438
-
439
- $token = $configuration->builder()
440
- ->withClaim('mercure', ['subscribe' => ["http://example.com/books/1"]]) // can also be a URI template, or *
441
- ->getToken($configuration->signer(), $configuration->signingKey())
442
- ->toString();
443
-
444
- $response = $this->json(['@id' => '/demo/books/1', 'availability' => 'https://schema.org/InStock']);
445
- $cookie = Cookie::create('mercureAuthorization')
446
- ->withValue($token)
447
- ->withPath('/.well-known/mercure')
448
- ->withSecure(true)
449
- ->withHttpOnly(true)
450
- ->withSameSite('strict')
451
- ;
452
- $response->headers->setCookie($cookie);
462
+ $discovery->addLink($request);
463
+
464
+ $response = new JsonResponse([
465
+ '@id' => '/demo/books/1',
466
+ 'availability' => 'https://schema.org/InStock'
467
+ ]);
468
+
469
+ $response->headers->setCookie(
470
+ $authorization->createCookie($request, ["http://example.com/books/1"])
471
+ );
453
472
454
473
return $response;
455
474
}
@@ -464,15 +483,17 @@ Programmatically Generating The JWT Used to Publish
464
483
---------------------------------------------------
465
484
466
485
Instead of directly storing a JWT in the configuration,
467
- you can create a service that will return the token used by
468
- the ``Publisher `` object::
486
+ you can create a token provider that will return the token used by
487
+ the ``HubInterface `` object::
469
488
470
- // src/Mercure/MyJwtProvider .php
489
+ // src/Mercure/MyTokenProvider .php
471
490
namespace App\Mercure;
472
491
473
- final class MyJwtProvider
492
+ use Symfony\Component\Mercure\JWT\TokenProviderInterface;
493
+
494
+ final class MyTokenProvider implements TokenProviderInterface
474
495
{
475
- public function __invoke (): string
496
+ public function getToken (): string
476
497
{
477
498
return 'the-JWT';
478
499
}
@@ -489,7 +510,8 @@ Then, reference this service in the bundle configuration:
489
510
hubs :
490
511
default :
491
512
url : https://mercure-hub.example.com/.well-known/mercure
492
- jwt_provider : App\Mercure\MyJwtProvider
513
+ jwt :
514
+ provider : App\Mercure\MyTokenProvider
493
515
494
516
.. code-block :: xml
495
517
@@ -499,8 +521,9 @@ Then, reference this service in the bundle configuration:
499
521
<hub
500
522
name =" default"
501
523
url =" https://mercure-hub.example.com/.well-known/mercure"
502
- jwt-provider =" App\Mercure\MyJwtProvider"
503
- />
524
+ >
525
+ <jwt provider =" App\Mercure\MyTokenProvider" />
526
+ </hub >
504
527
</config >
505
528
506
529
.. code-block :: php
@@ -512,7 +535,9 @@ Then, reference this service in the bundle configuration:
512
535
'hubs' => [
513
536
'default' => [
514
537
'url' => 'https://mercure-hub.example.com/.well-known/mercure',
515
- 'jwt_provider' => MyJwtProvider::class,
538
+ 'jwt' => [
539
+ 'provider' => MyJwtProvider::class,
540
+ ]
516
541
],
517
542
],
518
543
]);
@@ -573,29 +598,59 @@ its Mercure support.
573
598
Testing
574
599
--------
575
600
576
- During functional testing there is no need to send updates to Mercure. They will
577
- be handled by a stub publisher::
601
+ During unit testing there is not need to send updates to Mercure.
602
+
603
+ You can instead make use of the `MockHub `::
578
604
579
- // tests/Functional/Fixtures/PublisherStub.php
605
+ // tests/Functional/.php
606
+ namespace App\Tests\Unit\Controller;
607
+
608
+ use App\Controller\MessageController;
609
+ use Symfony\Component\Mercure\HubInterface;
610
+ use Symfony\Component\Mercure\JWT\StaticTokenProvider;
611
+ use Symfony\Component\Mercure\MockHub;
612
+ use Symfony\Component\Mercure\Update;
613
+
614
+ class MessageControllerTest extends TestCase
615
+ {
616
+ public function testPublishing()
617
+ {
618
+ $hub = new MockHub('default', 'https://internal/.well-known/mercure', new StaticTokenProvider('foo'), function(Update $update): string {
619
+ // $this->assertTrue($update->isPrivate());
620
+
621
+ return 'id';
622
+ });
623
+
624
+ $controller = new MessageController($hub);
625
+
626
+ ...
627
+ }
628
+ }
629
+
630
+ During functional testing you can instead decorate the Hub::
631
+
632
+ // tests/Functional/Fixtures/HubStub.php
580
633
namespace App\Tests\Functional\Fixtures;
581
634
582
- use Symfony\Component\Mercure\PublisherInterface ;
635
+ use Symfony\Component\Mercure\HubInterface ;
583
636
use Symfony\Component\Mercure\Update;
584
637
585
- class PublisherStub implements PublisherInterface
638
+ class HubStub implements HubInterface
586
639
{
587
- public function __invoke (Update $update): string
640
+ public function publish (Update $update): string
588
641
{
589
- return '';
642
+ return 'id ';
590
643
}
644
+
645
+ // implement rest of HubInterface methods here
591
646
}
592
647
593
- PublisherStub decorates the default publisher service so no updates are actually
594
- sent. Here is the PublisherStub implementation::
648
+ HubStub decorates the default hub service so no updates are actually
649
+ sent. Here is the HubStub implementation::
595
650
596
651
# config/services_test.yaml
597
- App\Tests\Functional\Fixtures\PublisherStub :
598
- decorates: mercure.hub.default.publisher
652
+ App\Tests\Functional\Fixtures\HubStub :
653
+ decorates: mercure.hub.default
599
654
600
655
601
656
Debugging
0 commit comments