@@ -159,20 +159,19 @@ service, including controllers::
159159 namespace App\Controller;
160160
161161 use Symfony\Component\HttpFoundation\Response;
162- use Symfony\Component\Mercure\PublisherInterface ;
162+ use Symfony\Component\Mercure\HubInterface ;
163163 use Symfony\Component\Mercure\Update;
164164
165165 class PublishController
166166 {
167- public function __invoke(PublisherInterface $publisher ): Response
167+ public function __invoke(HubInterface $hub ): Response
168168 {
169169 $update = new Update(
170170 'http://example.com/books/1',
171171 json_encode(['status' => 'OutOfStock'])
172172 );
173173
174- // The Publisher service is an invokable object
175- $publisher($update);
174+ $hub->publish($update);
176175
177176 return new Response('published!');
178177 }
@@ -297,17 +296,14 @@ by using the ``AbstractController::addLink`` helper method::
297296 use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
298297 use Symfony\Component\HttpFoundation\JsonResponse;
299298 use Symfony\Component\HttpFoundation\Request;
300- use Symfony\Component\WebLink\Link ;
299+ use Symfony\Component\Mercure\Discovery ;
301300
302301 class DiscoverController extends AbstractController
303302 {
304- public function __invoke(Request $request): JsonResponse
303+ public function __invoke(Request $request, Discovery $discovery ): JsonResponse
305304 {
306- // This parameter is automatically created by the MercureBundle
307- $hubUrl = $this->getParameter('mercure.default_hub');
308-
309305 // Link: <http://localhost:3000/.well-known/mercure>; rel="mercure"
310- $this ->addLink($request, new Link('mercure', $hubUrl) );
306+ $discovery ->addLink($request);
311307
312308 return $this->json([
313309 '@id' => '/books/1',
@@ -346,13 +342,13 @@ of the ``Update`` constructor to ``true``::
346342 // src/Controller/Publish.php
347343 namespace App\Controller;
348344
345+ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
349346 use Symfony\Component\HttpFoundation\Response;
350- use Symfony\Component\Mercure\PublisherInterface;
351347 use Symfony\Component\Mercure\Update;
352348
353- class PublishController
349+ class PublishController extends AbstractController
354350 {
355- public function __invoke(PublisherInterface $publisher ): Response
351+ public function __invoke(HubInterface $hub ): Response
356352 {
357353 $update = new Update(
358354 'http://example.com/books/1',
@@ -362,7 +358,7 @@ of the ``Update`` constructor to ``true``::
362358
363359 // Publisher's JWT must contain this topic, a URI template it matches or * in mercure.publish or you'll get a 401
364360 // 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);
366362
367363 return new Response('private update published!');
368364 }
@@ -406,50 +402,72 @@ This cookie will be automatically sent by the web browser when connecting to the
406402Then, the Hub will verify the validity of the provided JWT, and extract the topic selectors
407403from it.
408404
409- To generate the JWT, we'll use the `` lcobucci/jwt `` library. Install it :
405+ add your JWT secret to the configuration as follow : :
410406
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!'
412418
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+ ]);
414445
415446 And here is the controller::
416447
417448 // src/Controller/DiscoverController.php
418449 namespace App\Controller;
419450
420- use Lcobucci\JWT\Configuration;
421- use Lcobucci\JWT\Signer\Hmac\Sha256;
422- use Lcobucci\JWT\Signer\Key;
423451 use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
424- use Symfony\Component\HttpFoundation\Cookie;
425452 use Symfony\Component\HttpFoundation\Request;
426453 use Symfony\Component\HttpFoundation\Response;
427- use Symfony\Component\WebLink\Link;
454+ use Symfony\Component\Mercure\Authorization;
455+ use Symfony\Component\Mercure\Discovery;
428456
429457 class DiscoverController extends AbstractController
430458 {
431- public function __invoke(Request $request): Response
459+ public function __invoke(Request $request, Discovery $discovery, Authorization $authorization ): Response
432460 {
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);
461+ $discovery->addLink($request);
462+
463+ $response = new JsonResponse([
464+ '@id' => '/demo/books/1',
465+ 'availability' => 'https://schema.org/InStock'
466+ ]);
467+
468+ $response->headers->setCookie(
469+ $authorization->createCookie($request, ["http://example.com/books/1"])
470+ );
453471
454472 return $response;
455473 }
@@ -464,15 +482,17 @@ Programmatically Generating The JWT Used to Publish
464482---------------------------------------------------
465483
466484Instead 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::
485+ you can create a token provider that will return the token used by
486+ the ``HubInterface `` object::
469487
470- // src/Mercure/MyJwtProvider .php
488+ // src/Mercure/MyTokenProvider .php
471489 namespace App\Mercure;
472490
473- final class MyJwtProvider
491+ use Symfony\Component\Mercure\JWT\TokenProviderInterface;
492+
493+ final class MyTokenProvider implements TokenProviderInterface
474494 {
475- public function __invoke (): string
495+ public function getToken (): string
476496 {
477497 return 'the-JWT';
478498 }
@@ -489,7 +509,8 @@ Then, reference this service in the bundle configuration:
489509 hubs :
490510 default :
491511 url : https://mercure-hub.example.com/.well-known/mercure
492- jwt_provider : App\Mercure\MyJwtProvider
512+ jwt :
513+ provider : App\Mercure\MyTokenProvider
493514
494515 .. code-block :: xml
495516
@@ -499,8 +520,9 @@ Then, reference this service in the bundle configuration:
499520 <hub
500521 name =" default"
501522 url =" https://mercure-hub.example.com/.well-known/mercure"
502- jwt-provider =" App\Mercure\MyJwtProvider"
503- />
523+ >
524+ <jwt provider =" App\Mercure\MyTokenProvider" />
525+ </hub >
504526 </config >
505527
506528 .. code-block :: php
@@ -512,7 +534,9 @@ Then, reference this service in the bundle configuration:
512534 'hubs' => [
513535 'default' => [
514536 'url' => 'https://mercure-hub.example.com/.well-known/mercure',
515- 'jwt_provider' => MyJwtProvider::class,
537+ 'jwt' => [
538+ 'provider' => MyJwtProvider::class,
539+ ]
516540 ],
517541 ],
518542 ]);
@@ -573,29 +597,59 @@ its Mercure support.
573597Testing
574598--------
575599
576- During functional testing there is no need to send updates to Mercure. They will
577- be handled by a stub publisher::
600+ During unit testing there is not need to send updates to Mercure.
601+
602+ You can instead make use of the `MockHub `::
578603
579- // tests/Functional/Fixtures/PublisherStub.php
604+ // tests/Functional/.php
605+ namespace App\Tests\Unit\Controller;
606+
607+ use App\Controller\MessageController;
608+ use Symfony\Component\Mercure\HubInterface;
609+ use Symfony\Component\Mercure\JWT\StaticTokenProvider;
610+ use Symfony\Component\Mercure\MockHub;
611+ use Symfony\Component\Mercure\Update;
612+
613+ class MessageControllerTest extends TestCase
614+ {
615+ public function testPublishing()
616+ {
617+ $hub = new MockHub('default', 'https://internal/.well-known/mercure', new StaticTokenProvider('foo'), function(Update $update): string {
618+ // $this->assertTrue($update->isPrivate());
619+
620+ return 'id';
621+ });
622+
623+ $controller = new MessageController($hub);
624+
625+ ...
626+ }
627+ }
628+
629+ During functional testing you can instead decorate the Hub::
630+
631+ // tests/Functional/Fixtures/HubStub.php
580632 namespace App\Tests\Functional\Fixtures;
581633
582- use Symfony\Component\Mercure\PublisherInterface ;
634+ use Symfony\Component\Mercure\HubInterface ;
583635 use Symfony\Component\Mercure\Update;
584636
585- class PublisherStub implements PublisherInterface
637+ class HubStub implements HubInterface
586638 {
587- public function __invoke (Update $update): string
639+ public function publish (Update $update): string
588640 {
589- return '';
641+ return 'id ';
590642 }
643+
644+ // implement rest of HubInterface methods here
591645 }
592646
593- PublisherStub decorates the default publisher service so no updates are actually
594- sent. Here is the PublisherStub implementation::
647+ HubStub decorates the default hub service so no updates are actually
648+ sent. Here is the HubStub implementation::
595649
596650 # config/services_test.yaml
597- App\Tests\Functional\Fixtures\PublisherStub :
598- decorates: mercure.hub.default.publisher
651+ App\Tests\Functional\Fixtures\HubStub :
652+ decorates: mercure.hub.default
599653
600654
601655Debugging
0 commit comments