You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
It's important to note that Symfony provides built-in parser services.
99
+
In such cases, configuring the service name and optionally the required secret in the configuration is sufficient; there's no need to create your own parser.
100
+
19
101
Usage in Combination with the Mailer Component
20
-
----------------------------------------------
102
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
21
103
22
104
When using a third-party mailer provider, you can use the Webhook component to
23
105
receive webhook calls from this provider.
@@ -94,8 +176,6 @@ component routing:
94
176
};
95
177
96
178
In this example, we are using ``mailer_mailgun`` as the webhook routing name.
97
-
The routing name must be unique as this is what connects the provider with your
98
-
webhook consumer code.
99
179
100
180
The webhook routing name is part of the URL you need to configure at the
101
181
third-party mailer provider. The URL is the concatenation of your domain name
@@ -106,7 +186,195 @@ For Mailgun, you will get a secret for the webhook. Store this secret as
106
186
MAILER_MAILGUN_SECRET (in the :doc:`secrets management system
107
187
</configuration/secrets>` or in a ``.env`` file).
108
188
109
-
When done, add a :class:`Symfony\\Component\\RemoteEvent\\RemoteEvent` consumer
189
+
Usage in Combination with the Notifier Component
190
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
191
+
192
+
The usage of the Webhook component when using a third-party transport in
193
+
the Notifier is very similar to the usage with the Mailer.
194
+
195
+
Currently, the following third-party SMS transports support webhooks:
Depending on the routing name provided to this command, which corresponds, as discussed earlier,
226
+
to the second and final part of the incoming webhook URL, the command will generate the parser service responsible for parsing your webhook.
227
+
228
+
Additionally, it allows you to specify which RequestMatcher(s) from the HttpFoundation component should be applied to the incoming webhook request.
229
+
This constitutes the initial step of your gateway process, ensuring that the format of the incoming webhook is validated before proceeding to its thorough analysis.
230
+
231
+
Furthermore, the command will create the :class:`RemoteEventConsumer`, which manages the remote event returned by the parser.
232
+
233
+
Moreover, this command will automatically update the previously discussed configuration with the webhook's routing name.
234
+
This ensures that not only are the parser and consumer generated, but also that the configuration is seamlessly updated::
235
+
236
+
// src/Webhook/ExampleRequestParser.php
237
+
final class ExampleRequestParser extends AbstractRequestParser
238
+
{
239
+
protected function getRequestMatcher(): RequestMatcherInterface
240
+
{
241
+
return new ChainRequestMatcher([
242
+
new IsJsonRequestMatcher(),
243
+
new MethodRequestMatcher('POST'),
244
+
new HostRequestMatcher('regex'),
245
+
new ExpressionRequestMatcher(new ExpressionLanguage(), new Expression('expression')),
246
+
new PathRequestMatcher('regex'),
247
+
new IpsRequestMatcher(['127.0.0.1']),
248
+
new PortRequestMatcher(443),
249
+
new SchemeRequestMatcher('https'),
250
+
]);
251
+
}
252
+
253
+
/**
254
+
* @throws JsonException
255
+
*/
256
+
protected function doParse(Request $request, #[\SensitiveParameter] string $secret): ?RemoteEvent
257
+
{
258
+
// Adapt or replace the content of this method to fit your need.
259
+
// e.g Validate the request against $secret and/or Validate the request payload
260
+
// and/or Parse the request payload and return a RemoteEvent object or throw an exception
261
+
262
+
return new RemoteEvent(
263
+
$payload['name'],
264
+
$payload['id'],
265
+
$payload,
266
+
);
267
+
}
268
+
}
269
+
270
+
271
+
Now, imagine that in your case, you receive a notification of a product stock outage, and the received JSON contains details about the affected product and the severity of the outage.
272
+
Depending on the specific product and the severity of the stock outage, your application can trigger different remote events.
273
+
274
+
For instance, you might define ``HighPriorityStockRefillEvent``, ``MediumPriorityStockRefillEvent`` and ``LowPriorityStockRefillEvent``.
275
+
276
+
277
+
By implementing the :class:`PayloadConverterInterface` and its :method:`Symfony\\Component\\RemoteEvent\\PayloadConverterInterface::convert` method, you can encapsulate all the business logic
278
+
involved in creating the appropriate remote event. This converter will be invoked by your parser.
279
+
280
+
For inspiration, you can refer to :class:`MailGunPayloadConverter`::
281
+
282
+
// src/Webhook/ExampleRequestParser.php
283
+
final class ExampleRequestParser extends AbstractRequestParser
284
+
{
285
+
protected function getRequestMatcher(): RequestMatcherInterface
286
+
{
287
+
...
288
+
}
289
+
290
+
/**
291
+
* @throws JsonException
292
+
*/
293
+
protected function doParse(Request $request, #[\SensitiveParameter] string $secret): ?RemoteEvent
294
+
{
295
+
// Adapt or replace the content of this method to fit your need.
296
+
// e.g Validate the request against $secret and/or Validate the request payload
297
+
// and/or Parse the request payload and return a RemoteEvent object or throw an exception
throw new RejectWebhookException(406, $e->getMessage(), $e);
303
+
}
304
+
}
305
+
}
306
+
307
+
// src/RemoteEvent/ExamplePayloadConverter.php
308
+
final class ExamplePayloadConverter implements PayloadConverterInterface
309
+
{
310
+
public function convert(array $payload): AbstractPriorityStockRefillEvent
311
+
{
312
+
...
313
+
314
+
if (....) {
315
+
$event = new HighPriorityStockRefillEvent($name, $payload['id]', $payload])
316
+
} elseif {
317
+
$event = new MediumPriorityStockRefillEvent($name, $payload['id]', $payload])
318
+
} else {
319
+
$event = new LowPriorityStockRefillEvent($name, $payload['id]', $payload])
320
+
}
321
+
322
+
....
323
+
324
+
return $event;
325
+
}
326
+
}
327
+
328
+
From this, we can see that the Remote Event component is highly beneficial for handling webhooks.
329
+
It enables you to convert the incoming webhook data into validated objects that can be efficiently manipulated and utilized according to your requirements.
330
+
331
+
Remote Event Consumer: Handling and Manipulating The Received Data
It is important to note that when the incoming webhook is processed by the :class:`WebhookController`, you have the option to handle the consumption of remote events asynchronously.
335
+
Indeed, this can be configured using a bus, with the default setting pointing to the Messenger component's default bus.
336
+
For more details, refer to the :doc:`Symfony Messenger </components/messenger>` documentation
337
+
338
+
339
+
Whether the remote event is processed synchronously or asynchronously, you'll need a consumer that implements the :class:`ConsumerInterface`.
340
+
If you used the command to set this up, it was created automatically
341
+
342
+
.. code-block:: terminal
343
+
344
+
$ php bin/console make:webhook
345
+
346
+
Otherwise, you'll need to manually add it with the ``AsRemoteEventConsumer`` attribute which will allow you to designate this class as a :class:`ConsumerInterface`,
347
+
making it recognizable to the Remote Event component so it can pass the converted object to it.
348
+
Additionally, the name passed to your attribute is critical; it must match the configuration entry under routing that you specified in the ``webhook.yaml`` file, which in your case is ``my_first_parser```.
349
+
350
+
In the :method:`Symfony\\Component\\RemoteEvent\\Consumer\\ConsumerInterface::consume` method,
351
+
you can access your object containing the event data that triggered the webhook, allowing you to respond appropriately.
352
+
353
+
For example, you can use Mercure to broadcast updates to clients of the hub, among other actions ...::
354
+
355
+
// src/Webhook/ExampleRequestParser.php
356
+
#[AsRemoteEventConsumer('my_first_parser')] # routing name
357
+
final class ExampleWebhookConsumer implements ConsumerInterface
358
+
{
359
+
public function __construct()
360
+
{
361
+
}
362
+
363
+
public function consume(RemoteEvent $event): void
364
+
{
365
+
// Implement your own logic here
366
+
}
367
+
}
368
+
369
+
370
+
If you are using it alongside other components that already include built-in parsers,
371
+
you will need to configure the settings (as mentioned earlier) and also create your own consumer.
372
+
This is necessary because it involves your own business logic and your specific reactions to the remote event(s) that may be received from the built-in parsers.
373
+
374
+
Usage in Combination with the Mailer Component
375
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
376
+
377
+
You can add a :class:`Symfony\\Component\\RemoteEvent\\RemoteEvent` consumer
110
378
to react to incoming webhooks (the webhook routing name is what connects your
111
379
class to the provider).
112
380
@@ -122,7 +390,7 @@ events::
122
390
use Symfony\Component\RemoteEvent\RemoteEvent;
123
391
124
392
#[AsRemoteEventConsumer('mailer_mailgun')]
125
-
class WebhookListener implements ConsumerInterface
393
+
class MailerWebhookConsumer implements ConsumerInterface
126
394
{
127
395
public function consume(RemoteEvent $event): void
128
396
{
@@ -148,19 +416,7 @@ events::
148
416
}
149
417
150
418
Usage in Combination with the Notifier Component
151
-
------------------------------------------------
152
-
153
-
The usage of the Webhook component when using a third-party transport in
154
-
the Notifier is very similar to the usage with the Mailer.
155
-
156
-
Currently, the following third-party SMS transports support webhooks:
For instance, you can utilize the specific :class:`SendWebhookMessage` and :class:`SendWebhookHandler` provided to dispatch the message either synchronously or asynchronously using the Symfony Messenger component.
455
+
456
+
The SendWebhookMessage takes a :class:`Subscriber` as its first argument, which includes the destination URL and the mandatory secret.
457
+
If the secret is missing, an exception will be thrown.
458
+
459
+
As a second argument, it expects a :class:`RemoteEvent` containing the webhook name, the ID, and the payload, which is the substantial information you wish to communicate.
460
+
461
+
The :class:`SendWebhookHandler` configures the headers, the body of the request, and finally sign the headers before making an HTTP request to the specified URL using Symfony's HttpClient component::
462
+
463
+
$subscriber = new Subscriber($urlCallback, $secret);
However, you also have the flexibility to define your own message, handler, or custom mechanism, and process it either synchronously or asynchronously.
0 commit comments