Skip to content

Commit 2ccaec5

Browse files
committed
update mercure documentation
1 parent 49cd53b commit 2ccaec5

File tree

1 file changed

+121
-60
lines changed

1 file changed

+121
-60
lines changed

mercure.rst

Lines changed: 121 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,73 @@ 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+
442+
$container->loadFromExtension('mercure', [
443+
'hubs' => [
444+
'default' => [
445+
'url' => 'https://mercure-hub.example.com/.well-known/mercure',
446+
'jwt' => [
447+
'secret' => '!ChangeMe!',
448+
]
449+
],
450+
],
451+
]);
452+
415453
And here is the controller::
416454

417455
// src/Controller/DiscoverController.php
418456
namespace App\Controller;
419457

420-
use Lcobucci\JWT\Configuration;
421-
use Lcobucci\JWT\Signer\Hmac\Sha256;
422-
use Lcobucci\JWT\Signer\Key;
423458
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
424-
use Symfony\Component\HttpFoundation\Cookie;
425459
use Symfony\Component\HttpFoundation\Request;
426460
use Symfony\Component\HttpFoundation\Response;
427-
use Symfony\Component\WebLink\Link;
461+
use Symfony\Component\Mercure\Authorization;
462+
use Symfony\Component\Mercure\Discovery;
428463

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

454479
return $response;
455480
}
@@ -464,15 +489,17 @@ Programmatically Generating The JWT Used to Publish
464489
---------------------------------------------------
465490

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

470-
// src/Mercure/MyJwtProvider.php
495+
// src/Mercure/MyTokenProvider.php
471496
namespace App\Mercure;
472497

473-
final class MyJwtProvider
498+
use Symfony\Component\Mercure\JWT\TokenProviderInterface;
499+
500+
final class MyTokenProvider implements TokenProviderInterface
474501
{
475-
public function __invoke(): string
502+
public function getToken(): string
476503
{
477504
return 'the-JWT';
478505
}
@@ -489,7 +516,8 @@ Then, reference this service in the bundle configuration:
489516
hubs:
490517
default:
491518
url: https://mercure-hub.example.com/.well-known/mercure
492-
jwt_provider: App\Mercure\MyJwtProvider
519+
jwt:
520+
provider: App\Mercure\MyTokenProvider
493521
494522
.. code-block:: xml
495523
@@ -499,8 +527,9 @@ Then, reference this service in the bundle configuration:
499527
<hub
500528
name="default"
501529
url="https://mercure-hub.example.com/.well-known/mercure"
502-
jwt-provider="App\Mercure\MyJwtProvider"
503-
/>
530+
>
531+
<jwt provider="App\Mercure\MyTokenProvider"/>
532+
</hub>
504533
</config>
505534
506535
.. code-block:: php
@@ -512,7 +541,9 @@ Then, reference this service in the bundle configuration:
512541
'hubs' => [
513542
'default' => [
514543
'url' => 'https://mercure-hub.example.com/.well-known/mercure',
515-
'jwt_provider' => MyJwtProvider::class,
544+
'jwt' => [
545+
'provider' => MyJwtProvider::class,
546+
]
516547
],
517548
],
518549
]);
@@ -573,29 +604,59 @@ its Mercure support.
573604
Testing
574605
--------
575606

576-
During functional testing there is no need to send updates to Mercure. They will
577-
be handled by a stub publisher::
607+
During unit testing there is not need to send updates to Mercure.
578608

579-
// tests/Functional/Fixtures/PublisherStub.php
580-
namespace App\Tests\Functional\Fixtures;
609+
You can instead make use of the `MockHub`::
610+
611+
// tests/Functional/.php
612+
namespace App\Tests\Unit\Controller;
581613

582-
use Symfony\Component\Mercure\PublisherInterface;
614+
use App\Controller\MessageController;
615+
use Symfony\Component\Mercure\JWT\StaticTokenProvider;
616+
use Symfony\Component\Mercure\MockHub;
617+
use Symfony\Component\Mercure\HubInterface;
583618
use Symfony\Component\Mercure\Update;
584619

585-
class PublisherStub implements PublisherInterface
620+
class MessageControllerTest extends TestCase
586621
{
587622
public function __invoke(Update $update): string
588623
{
589-
return '';
624+
$hub = new MockHub('default', 'https://internal/.well-known/mercure', new StaticTokenProvider('foo'), function(Update $update): string {
625+
// $this->assertTrue($update->isPrivate());
626+
627+
return 'id';
628+
});
629+
630+
$controller = new MessageController($hub);
631+
632+
...
633+
}
634+
}
635+
636+
During functional testing you can instead decorate the Hub::
637+
638+
// tests/Functional/Fixtures/HubStub.php
639+
namespace App\Tests\Functional\Fixtures;
640+
641+
use Symfony\Component\Mercure\HubInterface;
642+
use Symfony\Component\Mercure\Update;
643+
644+
class HubStub implements HubInterface
645+
{
646+
public function publish(Update $update): string
647+
{
648+
return 'id';
590649
}
650+
651+
// implement rest of HubInterface methods here
591652
}
592653

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

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

600661

601662
Debugging

0 commit comments

Comments
 (0)