@@ -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,44 +402,71 @@ 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!'  
418+ 
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 
412433
413-  $ composer require lcobucci/jwt 
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
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; 
424452 use Symfony\Component\HttpFoundation\Request; 
425453 use Symfony\Component\HttpFoundation\Response; 
426-  use Symfony\Component\WebLink\Link; 
454+  use Symfony\Component\Mercure\Authorization; 
455+  use Symfony\Component\Mercure\Discovery; 
427456
428457 class DiscoverController extends AbstractController 
429458 { 
430-  public function __invoke(Request $request): Response 
459+  public function __invoke(Request $request, Discovery $discovery, Authorization $authorization ): Response 
431460 { 
432-  $hubUrl = $this->getParameter('mercure.default_hub'); 
433-  $this->addLink($request, new Link('mercure', $hubUrl)); 
434- 
435-  $key = Key\InMemory::plainText('mercure_secret_key'); // don't forget to set this parameter! Test value: !ChangeMe! 
436-  $configuration = Configuration::forSymmetricSigner(new Sha256(), $key); 
461+  $discovery->addLink($request); 
437462
438-  $token  = $configuration->builder()  
439-  ->withClaim('mercure', ['subscribe'  => ["http://example.com/ books/1"]]) // can also be a URI template, or *  
440-  ->getToken($configuration->signer(), $configuration->signingKey())  
441-   ->toString( ); 
463+  $response  = new JsonResponse([  
464+  '@id'  => '/demo/ books/1',  
465+  'availability' => 'https://schema.org/InStock'  
466+  ] ); 
442467
443-  $response = $this->json(['@id' => '/demo/books/1', 'availability' => 'https://schema.org/InStock']); 
444-  $response->headers->set( 
445-  'set-cookie', 
446-  sprintf('mercureAuthorization=%s; path=/.well-known/mercure; secure; httponly; SameSite=strict', $token) 
468+  $response->headers->setCookie( 
469+  $authorization->createCookie($request, ["http://example.com/books/1"]) 
447470 ); 
448471
449472 return $response; 
@@ -459,15 +482,17 @@ Programmatically Generating The JWT Used to Publish
459482--------------------------------------------------- 
460483
461484Instead of directly storing a JWT in the configuration,
462- you can create a service  that will return the token used by
463- the ``Publisher `` object::
485+ you can create a token provider  that will return the token used by
486+ the ``HubInterface `` object::
464487
465-  // src/Mercure/MyJwtProvider .php 
488+  // src/Mercure/MyTokenProvider .php 
466489 namespace App\Mercure; 
467490
468-  final class MyJwtProvider 
491+  use Symfony\Component\Mercure\JWT\TokenProviderInterface; 
492+ 
493+  final class MyTokenProvider implements TokenProviderInterface 
469494 { 
470-  public function __invoke (): string 
495+  public function getToken (): string 
471496 { 
472497 return 'the-JWT'; 
473498 } 
@@ -484,7 +509,8 @@ Then, reference this service in the bundle configuration:
484509 hubs : 
485510 default : 
486511 url : https://mercure-hub.example.com/.well-known/mercure  
487-  jwt_provider : App\Mercure\MyJwtProvider  
512+  jwt : 
513+  provider : App\Mercure\MyTokenProvider  
488514
489515code-block :: xml 
490516
@@ -494,8 +520,9 @@ Then, reference this service in the bundle configuration:
494520 <hub  
495521 name =" default"  
496522 url =" https://mercure-hub.example.com/.well-known/mercure"  
497-  jwt-provider =" App\Mercure\MyJwtProvider"  
498-  /> 
523+  > 
524+  <jwt  provider =" App\Mercure\MyTokenProvider"  
525+  </hub > 
499526 </config > 
500527
501528code-block :: php 
@@ -507,7 +534,9 @@ Then, reference this service in the bundle configuration:
507534 'hubs' => [ 
508535 'default' => [ 
509536 'url' => 'https://mercure-hub.example.com/.well-known/mercure', 
510-  'jwt_provider' => MyJwtProvider::class, 
537+  'jwt' => [ 
538+  'provider' => MyJwtProvider::class, 
539+  ] 
511540 ], 
512541 ], 
513542 ]); 
@@ -568,29 +597,59 @@ its Mercure support.
568597Testing
569598-------- 
570599
571- During functional testing there is no need to send updates to Mercure. They will
572- 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 `::
603+ 
604+  // tests/Functional/.php 
605+  namespace App\Tests\Unit\Controller; 
573606
574-  // tests/Functional/Fixtures/PublisherStub.php 
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 
575632 namespace App\Tests\Functional\Fixtures; 
576633
577-  use Symfony\Component\Mercure\PublisherInterface ; 
634+  use Symfony\Component\Mercure\HubInterface ; 
578635 use Symfony\Component\Mercure\Update; 
579636
580-  class PublisherStub  implements PublisherInterface  
637+  class HubStub  implements HubInterface  
581638 { 
582-  public function __invoke (Update $update): string 
639+  public function publish (Update $update): string 
583640 { 
584-  return ''; 
641+  return 'id '; 
585642 } 
643+ 
644+  // implement rest of HubInterface methods here 
586645 } 
587646
588- PublisherStub  decorates the default publisher  service so no updates are actually
589- 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::
590649
591650 # config/services_test.yaml 
592-  App\Tests\Functional\Fixtures\PublisherStub : 
593-  decorates: mercure.hub.default.publisher  
651+  App\Tests\Functional\Fixtures\HubStub : 
652+  decorates: mercure.hub.default 
594653
595654
596655Debugging
0 commit comments