Skip to content

Commit 0357840

Browse files
committed
update mercure documentation
1 parent 49cd53b commit 0357840

File tree

1 file changed

+120
-60
lines changed

1 file changed

+120
-60
lines changed

mercure.rst

Lines changed: 120 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -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
}
@@ -412,44 +408,72 @@ To generate the JWT, we'll use the ``lcobucci/jwt`` library. Install it:
412408
413409
$ composer require lcobucci/jwt
414410
411+
And add your JWT secret to the configuration as follow ::
412+
413+
.. configuration-block::
414+
415+
.. code-block:: yaml
416+
417+
# config/packages/mercure.yaml
418+
mercure:
419+
hubs:
420+
default:
421+
url: https://mercure-hub.example.com/.well-known/mercure
422+
jwt:
423+
secret: '!ChangeMe!'
424+
425+
.. code-block:: xml
426+
427+
<!-- config/packages/mercure.xml -->
428+
<?xml version="1.0" encoding="UTF-8" ?>
429+
<config>
430+
<hub
431+
name="default"
432+
url="https://mercure-hub.example.com/.well-known/mercure"
433+
>
434+
<jwt secret="!ChangeMe!"/>
435+
</hub>
436+
</config>
437+
438+
.. code-block:: php
439+
440+
// config/packages/mercure.php
441+
$container->loadFromExtension('mercure', [
442+
'hubs' => [
443+
'default' => [
444+
'url' => 'https://mercure-hub.example.com/.well-known/mercure',
445+
'jwt' => [
446+
'secret' => '!ChangeMe!',
447+
]
448+
],
449+
],
450+
]);
451+
415452
And here is the controller::
416453

417454
// src/Controller/DiscoverController.php
418455
namespace App\Controller;
419456

420-
use Lcobucci\JWT\Configuration;
421-
use Lcobucci\JWT\Signer\Hmac\Sha256;
422-
use Lcobucci\JWT\Signer\Key;
423457
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
424-
use Symfony\Component\HttpFoundation\Cookie;
425458
use Symfony\Component\HttpFoundation\Request;
426459
use Symfony\Component\HttpFoundation\Response;
427-
use Symfony\Component\WebLink\Link;
460+
use Symfony\Component\Mercure\Authorization;
461+
use Symfony\Component\Mercure\Discovery;
428462

429463
class DiscoverController extends AbstractController
430464
{
431-
public function __invoke(Request $request): Response
465+
public function __invoke(Request $request, Discovery $discovery, Authorization $authorization): Response
432466
{
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);
467+
$discovery->addLink($request);
468+
469+
$response = new JsonResponse([
470+
'@id' => '/demo/books/1',
471+
'availability' => 'https://schema.org/InStock'
472+
]);
473+
474+
$response->headers->setCookie(
475+
$authorization->createCookie($request, ["http://example.com/books/1"])
476+
);
453477

454478
return $response;
455479
}
@@ -464,15 +488,17 @@ Programmatically Generating The JWT Used to Publish
464488
---------------------------------------------------
465489

466490
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::
491+
you can create a token provider that will return the token used by
492+
the ``HubInterface`` object::
469493

470-
// src/Mercure/MyJwtProvider.php
494+
// src/Mercure/MyTokenProvider.php
471495
namespace App\Mercure;
472496

473-
final class MyJwtProvider
497+
use Symfony\Component\Mercure\JWT\TokenProviderInterface;
498+
499+
final class MyTokenProvider implements TokenProviderInterface
474500
{
475-
public function __invoke(): string
501+
public function getToken(): string
476502
{
477503
return 'the-JWT';
478504
}
@@ -489,7 +515,8 @@ Then, reference this service in the bundle configuration:
489515
hubs:
490516
default:
491517
url: https://mercure-hub.example.com/.well-known/mercure
492-
jwt_provider: App\Mercure\MyJwtProvider
518+
jwt:
519+
provider: App\Mercure\MyTokenProvider
493520
494521
.. code-block:: xml
495522
@@ -499,8 +526,9 @@ Then, reference this service in the bundle configuration:
499526
<hub
500527
name="default"
501528
url="https://mercure-hub.example.com/.well-known/mercure"
502-
jwt-provider="App\Mercure\MyJwtProvider"
503-
/>
529+
>
530+
<jwt provider="App\Mercure\MyTokenProvider"/>
531+
</hub>
504532
</config>
505533
506534
.. code-block:: php
@@ -512,7 +540,9 @@ Then, reference this service in the bundle configuration:
512540
'hubs' => [
513541
'default' => [
514542
'url' => 'https://mercure-hub.example.com/.well-known/mercure',
515-
'jwt_provider' => MyJwtProvider::class,
543+
'jwt' => [
544+
'provider' => MyJwtProvider::class,
545+
]
516546
],
517547
],
518548
]);
@@ -573,29 +603,59 @@ its Mercure support.
573603
Testing
574604
--------
575605

576-
During functional testing there is no need to send updates to Mercure. They will
577-
be handled by a stub publisher::
606+
During unit testing there is not need to send updates to Mercure.
607+
608+
You can instead make use of the `MockHub`::
578609

579-
// tests/Functional/Fixtures/PublisherStub.php
610+
// tests/Functional/.php
611+
namespace App\Tests\Unit\Controller;
612+
613+
use App\Controller\MessageController;
614+
use Symfony\Component\Mercure\HubInterface;
615+
use Symfony\Component\Mercure\JWT\StaticTokenProvider;
616+
use Symfony\Component\Mercure\MockHub;
617+
use Symfony\Component\Mercure\Update;
618+
619+
class MessageControllerTest extends TestCase
620+
{
621+
public function testPublishing()
622+
{
623+
$hub = new MockHub('default', 'https://internal/.well-known/mercure', new StaticTokenProvider('foo'), function(Update $update): string {
624+
// $this->assertTrue($update->isPrivate());
625+
626+
return 'id';
627+
});
628+
629+
$controller = new MessageController($hub);
630+
631+
...
632+
}
633+
}
634+
635+
During functional testing you can instead decorate the Hub::
636+
637+
// tests/Functional/Fixtures/HubStub.php
580638
namespace App\Tests\Functional\Fixtures;
581639

582-
use Symfony\Component\Mercure\PublisherInterface;
640+
use Symfony\Component\Mercure\HubInterface;
583641
use Symfony\Component\Mercure\Update;
584642

585-
class PublisherStub implements PublisherInterface
643+
class HubStub implements HubInterface
586644
{
587-
public function __invoke(Update $update): string
645+
public function publish(Update $update): string
588646
{
589-
return '';
647+
return 'id';
590648
}
649+
650+
// implement rest of HubInterface methods here
591651
}
592652

593-
PublisherStub decorates the default publisher service so no updates are actually
594-
sent. Here is the PublisherStub implementation::
653+
HubStub decorates the default hub service so no updates are actually
654+
sent. Here is the HubStub implementation::
595655

596656
# config/services_test.yaml
597-
App\Tests\Functional\Fixtures\PublisherStub:
598-
decorates: mercure.hub.default.publisher
657+
App\Tests\Functional\Fixtures\HubStub:
658+
decorates: mercure.hub.default
599659

600660

601661
Debugging

0 commit comments

Comments
 (0)