|
|
@@ -1,5 +1,5 @@
|
|
|
---
|
|
|
-title: "Events"
|
|
|
+title: 'Events'
|
|
|
---
|
|
|
|
|
|
Vendure emits events which can be subscribed to by plugins. These events are published by the [EventBus](/reference/typescript-api/events/event-bus/) and
|
|
|
@@ -7,10 +7,10 @@ likewise the `EventBus` is used to subscribe to events.
|
|
|
|
|
|
An event exists for virtually all significant actions which occur in the system, such as:
|
|
|
|
|
|
-- When entities (e.g. `Product`, `Order`, `Customer`) are created, updated or deleted
|
|
|
-- When a user registers an account
|
|
|
-- When a user logs in or out
|
|
|
-- When the state of an `Order`, `Payment`, `Fulfillment` or `Refund` changes
|
|
|
+- When entities (e.g. `Product`, `Order`, `Customer`) are created, updated or deleted
|
|
|
+- When a user registers an account
|
|
|
+- When a user logs in or out
|
|
|
+- When the state of an `Order`, `Payment`, `Fulfillment` or `Refund` changes
|
|
|
|
|
|
A full list of the available events follows.
|
|
|
|
|
|
@@ -19,66 +19,66 @@ A full list of the available events follows.
|
|
|
<div class="row">
|
|
|
<div class="col col--6">
|
|
|
|
|
|
- - [`AccountRegistrationEvent`](/reference/typescript-api/events/event-types#accountregistrationevent)
|
|
|
- - [`AccountVerifiedEvent`](/reference/typescript-api/events/event-types#accountverifiedevent)
|
|
|
- - [`AdministratorEvent`](/reference/typescript-api/events/event-types#administratorevent)
|
|
|
- - [`AssetChannelEvent`](/reference/typescript-api/events/event-types#assetchannelevent)
|
|
|
- - [`AssetEvent`](/reference/typescript-api/events/event-types#assetevent)
|
|
|
- - [`AttemptedLoginEvent`](/reference/typescript-api/events/event-types#attemptedloginevent)
|
|
|
- - [`ChangeChannelEvent`](/reference/typescript-api/events/event-types#changechannelevent)
|
|
|
- - [`ChannelEvent`](/reference/typescript-api/events/event-types#channelevent)
|
|
|
- - [`CollectionEvent`](/reference/typescript-api/events/event-types#collectionevent)
|
|
|
- - [`CollectionModificationEvent`](/reference/typescript-api/events/event-types#collectionmodificationevent)
|
|
|
- - [`CountryEvent`](/reference/typescript-api/events/event-types#countryevent)
|
|
|
- - [`CouponCodeEvent`](/reference/typescript-api/events/event-types#couponcodeevent)
|
|
|
- - [`CustomerAddressEvent`](/reference/typescript-api/events/event-types#customeraddressevent)
|
|
|
- - [`CustomerEvent`](/reference/typescript-api/events/event-types#customerevent)
|
|
|
- - [`CustomerGroupChangeEvent`](/reference/typescript-api/events/event-types#customergroupchangeevent)
|
|
|
- - [`CustomerGroupEvent`](/reference/typescript-api/events/event-types#customergroupevent)
|
|
|
- - [`FacetEvent`](/reference/typescript-api/events/event-types#facetevent)
|
|
|
- - [`FacetValueEvent`](/reference/typescript-api/events/event-types#facetvalueevent)
|
|
|
- - [`FulfillmentEvent`](/reference/typescript-api/events/event-types#fulfillmentevent)
|
|
|
- - [`FulfillmentStateTransitionEvent`](/reference/typescript-api/events/event-types#fulfillmentstatetransitionevent)
|
|
|
- - [`GlobalSettingsEvent`](/reference/typescript-api/events/event-types#globalsettingsevent)
|
|
|
- - [`HistoryEntryEvent`](/reference/typescript-api/events/event-types#historyentryevent)
|
|
|
- - [`IdentifierChangeEvent`](/reference/typescript-api/events/event-types#identifierchangeevent)
|
|
|
- - [`IdentifierChangeRequestEvent`](/reference/typescript-api/events/event-types#identifierchangerequestevent)
|
|
|
- - [`InitializerEvent`](/reference/typescript-api/events/event-types#initializerevent)
|
|
|
- - [`LoginEvent`](/reference/typescript-api/events/event-types#loginevent)
|
|
|
- - [`LogoutEvent`](/reference/typescript-api/events/event-types#logoutevent)
|
|
|
- - [`OrderEvent`](/reference/typescript-api/events/event-types#orderevent)
|
|
|
+- [`AccountRegistrationEvent`](/reference/typescript-api/events/event-types#accountregistrationevent)
|
|
|
+- [`AccountVerifiedEvent`](/reference/typescript-api/events/event-types#accountverifiedevent)
|
|
|
+- [`AdministratorEvent`](/reference/typescript-api/events/event-types#administratorevent)
|
|
|
+- [`AssetChannelEvent`](/reference/typescript-api/events/event-types#assetchannelevent)
|
|
|
+- [`AssetEvent`](/reference/typescript-api/events/event-types#assetevent)
|
|
|
+- [`AttemptedLoginEvent`](/reference/typescript-api/events/event-types#attemptedloginevent)
|
|
|
+- [`ChangeChannelEvent`](/reference/typescript-api/events/event-types#changechannelevent)
|
|
|
+- [`ChannelEvent`](/reference/typescript-api/events/event-types#channelevent)
|
|
|
+- [`CollectionEvent`](/reference/typescript-api/events/event-types#collectionevent)
|
|
|
+- [`CollectionModificationEvent`](/reference/typescript-api/events/event-types#collectionmodificationevent)
|
|
|
+- [`CountryEvent`](/reference/typescript-api/events/event-types#countryevent)
|
|
|
+- [`CouponCodeEvent`](/reference/typescript-api/events/event-types#couponcodeevent)
|
|
|
+- [`CustomerAddressEvent`](/reference/typescript-api/events/event-types#customeraddressevent)
|
|
|
+- [`CustomerEvent`](/reference/typescript-api/events/event-types#customerevent)
|
|
|
+- [`CustomerGroupChangeEvent`](/reference/typescript-api/events/event-types#customergroupchangeevent)
|
|
|
+- [`CustomerGroupEvent`](/reference/typescript-api/events/event-types#customergroupevent)
|
|
|
+- [`FacetEvent`](/reference/typescript-api/events/event-types#facetevent)
|
|
|
+- [`FacetValueEvent`](/reference/typescript-api/events/event-types#facetvalueevent)
|
|
|
+- [`FulfillmentEvent`](/reference/typescript-api/events/event-types#fulfillmentevent)
|
|
|
+- [`FulfillmentStateTransitionEvent`](/reference/typescript-api/events/event-types#fulfillmentstatetransitionevent)
|
|
|
+- [`GlobalSettingsEvent`](/reference/typescript-api/events/event-types#globalsettingsevent)
|
|
|
+- [`HistoryEntryEvent`](/reference/typescript-api/events/event-types#historyentryevent)
|
|
|
+- [`IdentifierChangeEvent`](/reference/typescript-api/events/event-types#identifierchangeevent)
|
|
|
+- [`IdentifierChangeRequestEvent`](/reference/typescript-api/events/event-types#identifierchangerequestevent)
|
|
|
+- [`InitializerEvent`](/reference/typescript-api/events/event-types#initializerevent)
|
|
|
+- [`LoginEvent`](/reference/typescript-api/events/event-types#loginevent)
|
|
|
+- [`LogoutEvent`](/reference/typescript-api/events/event-types#logoutevent)
|
|
|
+- [`OrderEvent`](/reference/typescript-api/events/event-types#orderevent)
|
|
|
|
|
|
</div>
|
|
|
<div class="col col--6">
|
|
|
|
|
|
- - [`OrderLineEvent`](/reference/typescript-api/events/event-types#orderlineevent)
|
|
|
- - [`OrderPlacedEvent`](/reference/typescript-api/events/event-types#orderplacedevent)
|
|
|
- - [`OrderStateTransitionEvent`](/reference/typescript-api/events/event-types#orderstatetransitionevent)
|
|
|
- - [`PasswordResetEvent`](/reference/typescript-api/events/event-types#passwordresetevent)
|
|
|
- - [`PasswordResetVerifiedEvent`](/reference/typescript-api/events/event-types#passwordresetverifiedevent)
|
|
|
- - [`PaymentMethodEvent`](/reference/typescript-api/events/event-types#paymentmethodevent)
|
|
|
- - [`PaymentStateTransitionEvent`](/reference/typescript-api/events/event-types#paymentstatetransitionevent)
|
|
|
- - [`ProductChannelEvent`](/reference/typescript-api/events/event-types#productchannelevent)
|
|
|
- - [`ProductEvent`](/reference/typescript-api/events/event-types#productevent)
|
|
|
- - [`ProductOptionEvent`](/reference/typescript-api/events/event-types#productoptionevent)
|
|
|
- - [`ProductOptionGroupChangeEvent`](/reference/typescript-api/events/event-types#productoptiongroupchangeevent)
|
|
|
- - [`ProductOptionGroupEvent`](/reference/typescript-api/events/event-types#productoptiongroupevent)
|
|
|
- - [`ProductVariantChannelEvent`](/reference/typescript-api/events/event-types#productvariantchannelevent)
|
|
|
- - [`ProductVariantEvent`](/reference/typescript-api/events/event-types#productvariantevent)
|
|
|
- - [`PromotionEvent`](/reference/typescript-api/events/event-types#promotionevent)
|
|
|
- - [`ProvinceEvent`](/reference/typescript-api/events/event-types#provinceevent)
|
|
|
- - [`RefundStateTransitionEvent`](/reference/typescript-api/events/event-types#refundstatetransitionevent)
|
|
|
- - [`RoleChangeEvent`](/reference/typescript-api/events/event-types#rolechangeevent)
|
|
|
- - [`RoleEvent`](/reference/typescript-api/events/event-types#roleevent)
|
|
|
- - [`SearchEvent`](/reference/typescript-api/events/event-types#searchevent)
|
|
|
- - [`SellerEvent`](/reference/typescript-api/events/event-types#sellerevent)
|
|
|
- - [`ShippingMethodEvent`](/reference/typescript-api/events/event-types#shippingmethodevent)
|
|
|
- - [`StockMovementEvent`](/reference/typescript-api/events/event-types#stockmovementevent)
|
|
|
- - [`TaxCategoryEvent`](/reference/typescript-api/events/event-types#taxcategoryevent)
|
|
|
- - [`TaxRateEvent`](/reference/typescript-api/events/event-types#taxrateevent)
|
|
|
- - [`TaxRateModificationEvent`](/reference/typescript-api/events/event-types#taxratemodificationevent)
|
|
|
- - [`ZoneEvent`](/reference/typescript-api/events/event-types#zoneevent)
|
|
|
- - [`ZoneMembersEvent`](/reference/typescript-api/events/event-types#zonemembersevent)
|
|
|
+- [`OrderLineEvent`](/reference/typescript-api/events/event-types#orderlineevent)
|
|
|
+- [`OrderPlacedEvent`](/reference/typescript-api/events/event-types#orderplacedevent)
|
|
|
+- [`OrderStateTransitionEvent`](/reference/typescript-api/events/event-types#orderstatetransitionevent)
|
|
|
+- [`PasswordResetEvent`](/reference/typescript-api/events/event-types#passwordresetevent)
|
|
|
+- [`PasswordResetVerifiedEvent`](/reference/typescript-api/events/event-types#passwordresetverifiedevent)
|
|
|
+- [`PaymentMethodEvent`](/reference/typescript-api/events/event-types#paymentmethodevent)
|
|
|
+- [`PaymentStateTransitionEvent`](/reference/typescript-api/events/event-types#paymentstatetransitionevent)
|
|
|
+- [`ProductChannelEvent`](/reference/typescript-api/events/event-types#productchannelevent)
|
|
|
+- [`ProductEvent`](/reference/typescript-api/events/event-types#productevent)
|
|
|
+- [`ProductOptionEvent`](/reference/typescript-api/events/event-types#productoptionevent)
|
|
|
+- [`ProductOptionGroupChangeEvent`](/reference/typescript-api/events/event-types#productoptiongroupchangeevent)
|
|
|
+- [`ProductOptionGroupEvent`](/reference/typescript-api/events/event-types#productoptiongroupevent)
|
|
|
+- [`ProductVariantChannelEvent`](/reference/typescript-api/events/event-types#productvariantchannelevent)
|
|
|
+- [`ProductVariantEvent`](/reference/typescript-api/events/event-types#productvariantevent)
|
|
|
+- [`PromotionEvent`](/reference/typescript-api/events/event-types#promotionevent)
|
|
|
+- [`ProvinceEvent`](/reference/typescript-api/events/event-types#provinceevent)
|
|
|
+- [`RefundStateTransitionEvent`](/reference/typescript-api/events/event-types#refundstatetransitionevent)
|
|
|
+- [`RoleChangeEvent`](/reference/typescript-api/events/event-types#rolechangeevent)
|
|
|
+- [`RoleEvent`](/reference/typescript-api/events/event-types#roleevent)
|
|
|
+- [`SearchEvent`](/reference/typescript-api/events/event-types#searchevent)
|
|
|
+- [`SellerEvent`](/reference/typescript-api/events/event-types#sellerevent)
|
|
|
+- [`ShippingMethodEvent`](/reference/typescript-api/events/event-types#shippingmethodevent)
|
|
|
+- [`StockMovementEvent`](/reference/typescript-api/events/event-types#stockmovementevent)
|
|
|
+- [`TaxCategoryEvent`](/reference/typescript-api/events/event-types#taxcategoryevent)
|
|
|
+- [`TaxRateEvent`](/reference/typescript-api/events/event-types#taxrateevent)
|
|
|
+- [`TaxRateModificationEvent`](/reference/typescript-api/events/event-types#taxratemodificationevent)
|
|
|
+- [`ZoneEvent`](/reference/typescript-api/events/event-types#zoneevent)
|
|
|
+- [`ZoneMembersEvent`](/reference/typescript-api/events/event-types#zonemembersevent)
|
|
|
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -86,7 +86,7 @@ A full list of the available events follows.
|
|
|
## Subscribing to events
|
|
|
|
|
|
To subscribe to an event, use the `EventBus`'s `.ofType()` method. It is typical to set up subscriptions in the `onModuleInit()` or `onApplicationBootstrap()`
|
|
|
-lifecycle hooks of a plugin or service (see [NestJS Lifecycle events](https://docs.nestjs.com/fundamentals/lifecycle-events).
|
|
|
+lifecycle hooks of a plugin or service (see [NestJS Lifecycle events](https://docs.nestjs.com/fundamentals/lifecycle-events)).
|
|
|
|
|
|
Here's an example where we subscribe to the `ProductEvent` and use it to trigger a rebuild of a static storefront:
|
|
|
|
|
|
@@ -103,15 +103,14 @@ export class StorefrontBuildPlugin implements OnModuleInit {
|
|
|
constructor(
|
|
|
// highlight-next-line
|
|
|
private eventBus: EventBus,
|
|
|
- private storefrontBuildService: StorefrontBuildService) {}
|
|
|
+ private storefrontBuildService: StorefrontBuildService,
|
|
|
+ ) {}
|
|
|
|
|
|
onModuleInit() {
|
|
|
// highlight-start
|
|
|
- this.eventBus
|
|
|
- .ofType(ProductEvent)
|
|
|
- .subscribe(event => {
|
|
|
- this.storefrontBuildService.triggerBuild();
|
|
|
- });
|
|
|
+ this.eventBus.ofType(ProductEvent).subscribe(event => {
|
|
|
+ this.storefrontBuildService.triggerBuild();
|
|
|
+ });
|
|
|
// highlight-end
|
|
|
}
|
|
|
}
|
|
|
@@ -131,12 +130,13 @@ import { debounceTime } from 'rxjs/operators';
|
|
|
|
|
|
this.eventBus
|
|
|
.ofType(ProductEvent)
|
|
|
- // highlight-next-line
|
|
|
+ // highlight-next-line
|
|
|
.pipe(debounceTime(1000))
|
|
|
.subscribe(event => {
|
|
|
this.storefrontBuildService.triggerBuild();
|
|
|
});
|
|
|
```
|
|
|
+
|
|
|
:::
|
|
|
|
|
|
### Subscribing to multiple event types
|
|
|
@@ -145,7 +145,13 @@ Using the `.ofType()` method allows us to subscribe to a single event type. If w
|
|
|
|
|
|
```ts title="src/plugins/my-plugin/my-plugin.plugin.ts"
|
|
|
import { Injectable, OnModuleInit } from '@nestjs/common';
|
|
|
-import { EventBus, PluginCommonModule, VendurePlugin, ProductEvent, ProductVariantEvent } from '@vendure/core';
|
|
|
+import {
|
|
|
+ EventBus,
|
|
|
+ PluginCommonModule,
|
|
|
+ VendurePlugin,
|
|
|
+ ProductEvent,
|
|
|
+ ProductVariantEvent,
|
|
|
+} from '@vendure/core';
|
|
|
|
|
|
@VendurePlugin({
|
|
|
imports: [PluginCommonModule],
|
|
|
@@ -156,8 +162,7 @@ export class MyPluginPlugin implements OnModuleInit {
|
|
|
onModuleInit() {
|
|
|
this.eventBus
|
|
|
// highlight-start
|
|
|
- .filter(event =>
|
|
|
- event instanceof ProductEvent || event instanceof ProductVariantEvent)
|
|
|
+ .filter(event => event instanceof ProductEvent || event instanceof ProductVariantEvent)
|
|
|
// highlight-end
|
|
|
.subscribe(event => {
|
|
|
// the event will be a ProductEvent or ProductVariantEvent
|
|
|
@@ -183,7 +188,7 @@ export class MyPluginService {
|
|
|
async doSomethingWithProduct(ctx: RequestContext, product: Product) {
|
|
|
// ... do something
|
|
|
// highlight-next-line
|
|
|
- this.eventBus.publish(new ProductEvent(ctx, product, 'updated'));
|
|
|
+ await this.eventBus.publish(new ProductEvent(ctx, product, 'updated'));
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
@@ -221,7 +226,10 @@ import { ProductReviewInput } from '../types';
|
|
|
|
|
|
@Injectable()
|
|
|
export class ProductReviewService {
|
|
|
- constructor(private eventBus: EventBus, private productReviewService: ProductReviewService) {}
|
|
|
+ constructor(
|
|
|
+ private eventBus: EventBus,
|
|
|
+ private productReviewService: ProductReviewService,
|
|
|
+ ) {}
|
|
|
|
|
|
async submitReview(ctx: RequestContext, input: ProductReviewInput) {
|
|
|
// highlight-next-line
|
|
|
@@ -278,14 +286,121 @@ export class BlogPlugin implements OnModuleInit {
|
|
|
onModuleInit() {
|
|
|
this.eventBus
|
|
|
// highlight-start
|
|
|
- .ofType(BlogPostEvent).pipe(
|
|
|
- filter(event => event.type === 'created'),
|
|
|
- )
|
|
|
+ .ofType(BlogPostEvent)
|
|
|
+ .pipe(filter(event => event.type === 'created'))
|
|
|
.subscribe(event => {
|
|
|
const blogPost = event.entity;
|
|
|
// do something with the newly created BlogPost
|
|
|
});
|
|
|
- // highlight-end
|
|
|
+ // highlight-end
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## Blocking event handlers
|
|
|
+
|
|
|
+:::note
|
|
|
+The following section is an advanced topic.
|
|
|
+
|
|
|
+The API described in this section was added in Vendure v2.2.0.
|
|
|
+:::
|
|
|
+
|
|
|
+When using the `.ofType().subscribe()` pattern, the event handler is non-blocking. This means that the code that publishes
|
|
|
+the event (the "publishing code") will have no knowledge of any subscribers, and in fact any subscribers will be executed after the code that
|
|
|
+published the event has completed (technically, any ongoing database transactions are completed before the event gets
|
|
|
+emitted to the subscribers). This follows the typical [Observer pattern](https://en.wikipedia.org/wiki/Observer_pattern) and is a good fit for most use-cases.
|
|
|
+
|
|
|
+However, there may be certain situations in which you want the event handler to cause the publishing code to block
|
|
|
+until the event handler has completed. This is done by using a "blocking event handler", which does _not_ follow the
|
|
|
+Observer pattern, but rather it behaves more like a synchronous function call occurring within the publishing code.
|
|
|
+
|
|
|
+You may want to use a blocking event handler in the following situations:
|
|
|
+
|
|
|
+- The event handler is so critical that you need to ensure that it has completed before the publishing code continues. For
|
|
|
+ example, if the event handler must manipulate some financial records.
|
|
|
+- Errors in the event handler code should cause the publishing code to fail (and any database transaction to be rolled back).
|
|
|
+- You want to guard against the edge case that a server instance gets shut down (due to e.g. a fatal error or an auto-scaling event)
|
|
|
+ before event subscribers have been invoked.
|
|
|
+
|
|
|
+In these cases, you can use the `EventBus.registerBlockingEventHandler()` method:
|
|
|
+
|
|
|
+```ts title="src/plugins/my-plugin/my-plugin.plugin.ts"
|
|
|
+import { Injectable, OnModuleInit } from '@nestjs/common';
|
|
|
+import { EventBus, PluginCommonModule, VendurePlugin, CustomerEvent } from '@vendure/core';
|
|
|
+import { CustomerSyncService } from './services/customer-sync.service';
|
|
|
+
|
|
|
+@VendurePlugin({
|
|
|
+ imports: [PluginCommonModule],
|
|
|
+})
|
|
|
+export class MyPluginPlugin implements OnModuleInit {
|
|
|
+ constructor(
|
|
|
+ private eventBus: EventBus,
|
|
|
+ private customerSyncService: CustomerSyncService,
|
|
|
+ ) {}
|
|
|
+
|
|
|
+ onModuleInit() {
|
|
|
+ // highlight-start
|
|
|
+ this.eventBus.registerBlockingEventHandler({
|
|
|
+ type: CustomerEvent,
|
|
|
+ id: 'sync-customer-details-handler',
|
|
|
+ handler: async event => {
|
|
|
+ // This hypothetical service method would do nothing
|
|
|
+ // more than adding a new job to the job queue. This gives us
|
|
|
+ // the guarantee that the job is added before the publishing
|
|
|
+ // code is able to continue, while minimizing the time spent
|
|
|
+ // in the event handler.
|
|
|
+ await this.customerSyncService.triggerCustomerSyncJob(event);
|
|
|
+ },
|
|
|
+ });
|
|
|
+ // highlight-end
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
+
|
|
|
+Key differences between event subscribers and blocking event handlers:
|
|
|
+
|
|
|
+
|
|
|
+Aspect | Event subscribers | Blocking event handlers |
|
|
|
+|---|---|---|
|
|
|
+| **Execution** | Executed _after_ publishing code completes | Execute _during_ the publishing code |
|
|
|
+| **Error handling** | Errors do not affect publishing code | Errors propagated to publishing code |
|
|
|
+| **Transactions** | Guaranteed to execute only after the publishing code transaction has completed | Executed within the transaction of the publishing code |
|
|
|
+| **Performance** | Non-blocking: subscriber function performance has no effect on publishing code | Blocking: handler function will block execution of publishing code. Handler must be fast. |
|
|
|
+
|
|
|
+### Performance considerations
|
|
|
+
|
|
|
+Since blocking event handlers execute within the same transaction as the publishing code, it is important to ensure that they are fast.
|
|
|
+If a single handler takes longer than 100ms to execute, a warning will be logged. Ideally they should be much faster than that - you can
|
|
|
+set your Logger's `logLevel` to `LogLevel.DEBUG` to see the execution time of each handler.
|
|
|
+
|
|
|
+If multiple handlers are registered for a single event, they will be executed sequentially, so the publishing code will be blocked until
|
|
|
+all handlers have completed.
|
|
|
+
|
|
|
+### Order of execution
|
|
|
+
|
|
|
+If you register multiple handlers for the same event, they will be executed in the order in which they were registered.
|
|
|
+If you need more control over this order, i.e. to _guarantee_ that a particular handler will execute before another, you can use
|
|
|
+the `before` or `after` options:
|
|
|
+
|
|
|
+```ts
|
|
|
+// In one part of your code base
|
|
|
+this.eventBus.registerBlockingEventHandler({
|
|
|
+ type: CustomerEvent,
|
|
|
+ id: 'sync-customer-details-handler',
|
|
|
+ handler: async event => {
|
|
|
+ // ...
|
|
|
+ },
|
|
|
+});
|
|
|
+
|
|
|
+
|
|
|
+// In another part of your code base
|
|
|
+this.eventBus.registerBlockingEventHandler({
|
|
|
+ type: CustomerEvent,
|
|
|
+ id: 'check-customer-details-handler',
|
|
|
+ handler: async event => {
|
|
|
+ // ...
|
|
|
+ },
|
|
|
+ // highlight-next-line
|
|
|
+ before: 'sync-customer-details-handler',
|
|
|
+});
|
|
|
+```
|