@@ -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
}
@@ -417,39 +413,26 @@ And here is the controller::
417
413
// src/Controller/DiscoverController.php
418
414
namespace App\Controller;
419
415
420
- use Lcobucci\JWT\Configuration;
421
- use Lcobucci\JWT\Signer\Hmac\Sha256;
422
- use Lcobucci\JWT\Signer\Key;
423
416
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
424
- use Symfony\Component\HttpFoundation\Cookie;
425
417
use Symfony\Component\HttpFoundation\Request;
426
418
use Symfony\Component\HttpFoundation\Response;
427
- use Symfony\Component\WebLink\Link;
419
+ use Symfony\Component\Mercure\Authorization;
420
+ use Symfony\Component\Mercure\Discovery;
428
421
429
422
class DiscoverController extends AbstractController
430
423
{
431
- public function __invoke(Request $request): Response
424
+ public function __invoke(Request $request, Discovery $discovery, Authorization $authorization ): Response
432
425
{
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);
426
+ $discovery->addLink($request);
427
+
428
+ $response = new JsonResponse([
429
+ '@id' => '/demo/books/1',
430
+ 'availability' => 'https://schema.org/InStock'
431
+ ]);
432
+
433
+ $response->headers->setCookie(
434
+ $authorization->createCookie($request, ["http://example.com/books/1"])
435
+ );
453
436
454
437
return $response;
455
438
}
@@ -464,15 +447,17 @@ Programmatically Generating The JWT Used to Publish
464
447
---------------------------------------------------
465
448
466
449
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::
450
+ you can create a token provider that will return the token used by
451
+ the ``HubInterface `` object::
469
452
470
- // src/Mercure/MyJwtProvider .php
453
+ // src/Mercure/MyTokenProvider .php
471
454
namespace App\Mercure;
472
455
473
- final class MyJwtProvider
456
+ use Symfony\Component\Mercure\JWT\TokenProviderInterface;
457
+
458
+ final class MyTokenProvider implements TokenProviderInterface
474
459
{
475
- public function __invoke (): string
460
+ public function getToken (): string
476
461
{
477
462
return 'the-JWT';
478
463
}
@@ -489,7 +474,8 @@ Then, reference this service in the bundle configuration:
489
474
hubs :
490
475
default :
491
476
url : https://mercure-hub.example.com/.well-known/mercure
492
- jwt_provider : App\Mercure\MyJwtProvider
477
+ jwt :
478
+ provider : App\Mercure\MyTokenProvider
493
479
494
480
.. code-block :: xml
495
481
@@ -499,8 +485,9 @@ Then, reference this service in the bundle configuration:
499
485
<hub
500
486
name =" default"
501
487
url =" https://mercure-hub.example.com/.well-known/mercure"
502
- jwt-provider =" App\Mercure\MyJwtProvider"
503
- />
488
+ >
489
+ <jwt provider =" App\Mercure\MyTokenProvider" />
490
+ </hub >
504
491
</config >
505
492
506
493
.. code-block :: php
@@ -512,7 +499,9 @@ Then, reference this service in the bundle configuration:
512
499
'hubs' => [
513
500
'default' => [
514
501
'url' => 'https://mercure-hub.example.com/.well-known/mercure',
515
- 'jwt_provider' => MyJwtProvider::class,
502
+ 'jwt' => [
503
+ 'provider' => MyJwtProvider::class,
504
+ ]
516
505
],
517
506
],
518
507
]);
@@ -573,29 +562,59 @@ its Mercure support.
573
562
Testing
574
563
--------
575
564
576
- During functional testing there is no need to send updates to Mercure. They will
577
- be handled by a stub publisher::
565
+ During unit testing there is not need to send updates to Mercure.
578
566
579
- // tests/Functional/Fixtures/PublisherStub.php
580
- namespace App\Tests\Functional\Fixtures;
567
+ You can instead make use of the `MockHub `::
581
568
582
- use Symfony\Component\Mercure\PublisherInterface;
569
+ // tests/Functional/.php
570
+ namespace App\Tests\Unit\Controller;
571
+
572
+ use App\Controller\MessageController;
573
+ use Symfony\Component\Mercure\JWT\StaticTokenProvider;
574
+ use Symfony\Component\Mercure\MockHub;
575
+ use Symfony\Component\Mercure\HubInterface;
583
576
use Symfony\Component\Mercure\Update;
584
577
585
- class PublisherStub implements PublisherInterface
578
+ class MessageControllerTest extends TestCase
586
579
{
587
580
public function __invoke(Update $update): string
588
581
{
589
- return '';
582
+ $hub = new MockHub('default', 'https://internal/.well-known/mercure', new StaticTokenProvider('foo'), function(Update $update): string {
583
+ // $this->assertTrue($update->isPrivate());
584
+
585
+ return 'id';
586
+ });
587
+
588
+ $controller = new MessageController($hub);
589
+
590
+ ...
591
+ }
592
+ }
593
+
594
+ During functional testing you can instead decorate the Hub::
595
+
596
+ // tests/Functional/Fixtures/HubStub.php
597
+ namespace App\Tests\Functional\Fixtures;
598
+
599
+ use Symfony\Component\Mercure\HubInterface;
600
+ use Symfony\Component\Mercure\Update;
601
+
602
+ class HubStub implements HubInterface
603
+ {
604
+ public function publish(Update $update): string
605
+ {
606
+ return 'id';
590
607
}
608
+
609
+ // implement rest of HubInterface methods here
591
610
}
592
611
593
- PublisherStub decorates the default publisher service so no updates are actually
594
- sent. Here is the PublisherStub implementation::
612
+ HubStub decorates the default hub service so no updates are actually
613
+ sent. Here is the HubStub implementation::
595
614
596
615
# config/services_test.yaml
597
- App\Tests\Functional\Fixtures\PublisherStub :
598
- decorates: mercure.hub.default.publisher
616
+ App\Tests\Functional\Fixtures\HubStub :
617
+ decorates: mercure.hub.default
599
618
600
619
601
620
Debugging
0 commit comments