diff --git a/packages/core/src/event-bus/events/account-verified-event.ts b/packages/core/src/event-bus/events/account-verified-event.ts new file mode 100644 index 0000000000..345c6756b0 --- /dev/null +++ b/packages/core/src/event-bus/events/account-verified-event.ts @@ -0,0 +1,17 @@ +import { RequestContext } from '../../api/common/request-context'; +import { Customer } from '../../entity/customer/customer.entity'; +import { VendureEvent } from '../vendure-event'; + +/** + * @description + * This event is fired when a users email address successfully gets verified after + * the `verifyCustomerAccount` mutation was executed. + * + * @docsCategory events + * @docsPage Event Types + */ +export class AccountVerifiedEvent extends VendureEvent { + constructor(public ctx: RequestContext, public customer: Customer) { + super(); + } +} diff --git a/packages/core/src/event-bus/events/administrator-event.ts b/packages/core/src/event-bus/events/administrator-event.ts new file mode 100644 index 0000000000..9ea1d4d561 --- /dev/null +++ b/packages/core/src/event-bus/events/administrator-event.ts @@ -0,0 +1,27 @@ +import { CreateAdministratorInput, UpdateAdministratorInput } from '@vendure/common/lib/generated-types'; +import { ID } from '@vendure/common/lib/shared-types'; + +import { RequestContext } from '../../api'; +import { Administrator } from '../../entity'; +import { VendureEntityEvent } from '../vendure-entity-event'; + +type AdministratorInputTypes = CreateAdministratorInput | UpdateAdministratorInput | ID; + +/** + * @description + * This event is fired whenever a {@link Administrator} is added, updated or deleted. + * + * @docsCategory events + * @docsPage Event Types + * @since 1.4 + */ +export class AdministratorEvent extends VendureEntityEvent { + constructor( + ctx: RequestContext, + entity: Administrator, + type: 'created' | 'updated' | 'deleted', + input: AdministratorInputTypes, + ) { + super(entity, type, ctx, input); + } +} diff --git a/packages/core/src/event-bus/events/asset-event.ts b/packages/core/src/event-bus/events/asset-event.ts index fce4be2c19..c22174957b 100644 --- a/packages/core/src/event-bus/events/asset-event.ts +++ b/packages/core/src/event-bus/events/asset-event.ts @@ -1,21 +1,37 @@ -import { RequestContext } from '../../api/common/request-context'; +import { CreateAssetInput, DeleteAssetInput, UpdateAssetInput } from '@vendure/common/lib/generated-types'; +import { ID } from '@vendure/common/lib/shared-types'; + +import { RequestContext } from '../../api'; import { Asset } from '../../entity'; -import { VendureEvent } from '../vendure-event'; +import { VendureEntityEvent } from '../vendure-entity-event'; + +type AssetInputTypes = CreateAssetInput | UpdateAssetInput | DeleteAssetInput | ID; /** * @description - * This event is fired whenever aa {@link Asset} is added, updated - * or deleted. + * This event is fired whenever a {@link Asset} is added, updated or deleted. * * @docsCategory events * @docsPage Event Types + * @since 1.4 */ -export class AssetEvent extends VendureEvent { +export class AssetEvent extends VendureEntityEvent { constructor( - public ctx: RequestContext, - public asset: Asset, - public type: 'created' | 'updated' | 'deleted', + ctx: RequestContext, + entity: Asset, + type: 'created' | 'updated' | 'deleted', + input: AssetInputTypes, ) { - super(); + super(entity, type, ctx, input); + } + + /** + * Return an asset field to become compatible with the + * deprecated old version of AssetEvent + * @deprecated Use `entity` instead + * @since 1.4 + */ + get asset(): Asset { + return this.entity; } } diff --git a/packages/core/src/event-bus/events/change-channel-event.ts b/packages/core/src/event-bus/events/change-channel-event.ts new file mode 100644 index 0000000000..9c0676d2fb --- /dev/null +++ b/packages/core/src/event-bus/events/change-channel-event.ts @@ -0,0 +1,27 @@ +import { ID, Type } from '@vendure/common/lib/shared-types'; + +import { RequestContext } from '../../api'; +import { ChannelAware } from '../../common'; +import { VendureEntity } from '../../entity'; +import { VendureEvent } from '../vendure-event'; + +/** + * @description + * This event is fired whenever an {@link ChannelAware} entity is assigned or removed + * from a channel. The entity property contains the value before updating the channels. + * + * @docsCategory events + * @docsPage Event Types + * @since 1.4 + */ +export class ChangeChannelEvent extends VendureEvent { + constructor( + public ctx: RequestContext, + public entity: T, + public channelIds: ID[], + public type: 'assigned' | 'removed', + public entityType?: Type, + ) { + super(); + } +} diff --git a/packages/core/src/event-bus/events/channel-event.ts b/packages/core/src/event-bus/events/channel-event.ts new file mode 100644 index 0000000000..098f7cd5a0 --- /dev/null +++ b/packages/core/src/event-bus/events/channel-event.ts @@ -0,0 +1,27 @@ +import { CreateChannelInput, UpdateChannelInput } from '@vendure/common/lib/generated-types'; +import { ID } from '@vendure/common/lib/shared-types'; + +import { RequestContext } from '../../api'; +import { Channel } from '../../entity'; +import { VendureEntityEvent } from '../vendure-entity-event'; + +type ChannelInputTypes = CreateChannelInput | UpdateChannelInput | ID; + +/** + * @description + * This event is fired whenever a {@link Channel} is added, updated or deleted. + * + * @docsCategory events + * @docsPage Event Types + * @since 1.4 + */ +export class ChannelEvent extends VendureEntityEvent { + constructor( + ctx: RequestContext, + entity: Channel, + type: 'created' | 'updated' | 'deleted', + input: ChannelInputTypes, + ) { + super(entity, type, ctx, input); + } +} diff --git a/packages/core/src/event-bus/events/collection-event.ts b/packages/core/src/event-bus/events/collection-event.ts new file mode 100644 index 0000000000..c1666e9fa9 --- /dev/null +++ b/packages/core/src/event-bus/events/collection-event.ts @@ -0,0 +1,27 @@ +import { CreateCollectionInput, UpdateCollectionInput } from '@vendure/common/lib/generated-types'; +import { ID } from '@vendure/common/lib/shared-types'; + +import { RequestContext } from '../../api'; +import { Collection } from '../../entity'; +import { VendureEntityEvent } from '../vendure-entity-event'; + +type CollectionInputTypes = CreateCollectionInput | UpdateCollectionInput | ID; + +/** + * @description + * This event is fired whenever a {@link Collection} is added, updated or deleted. + * + * @docsCategory events + * @docsPage Event Types + * @since 1.4 + */ +export class CollectionEvent extends VendureEntityEvent { + constructor( + ctx: RequestContext, + entity: Collection, + type: 'created' | 'updated' | 'deleted', + input: CollectionInputTypes, + ) { + super(entity, type, ctx, input); + } +} diff --git a/packages/core/src/event-bus/events/country-event.ts b/packages/core/src/event-bus/events/country-event.ts new file mode 100644 index 0000000000..4f72d8fd07 --- /dev/null +++ b/packages/core/src/event-bus/events/country-event.ts @@ -0,0 +1,27 @@ +import { CreateCountryInput, UpdateCountryInput } from '@vendure/common/lib/generated-types'; +import { ID } from '@vendure/common/lib/shared-types'; + +import { RequestContext } from '../../api'; +import { Country } from '../../entity'; +import { VendureEntityEvent } from '../vendure-entity-event'; + +type CountryInputTypes = CreateCountryInput | UpdateCountryInput | ID; + +/** + * @description + * This event is fired whenever a {@link Country} is added, updated or deleted. + * + * @docsCategory events + * @docsPage Event Types + * @since 1.4 + */ +export class CountryEvent extends VendureEntityEvent { + constructor( + ctx: RequestContext, + entity: Country, + type: 'created' | 'updated' | 'deleted', + input: CountryInputTypes, + ) { + super(entity, type, ctx, input); + } +} diff --git a/packages/core/src/event-bus/events/coupon-code-event.ts b/packages/core/src/event-bus/events/coupon-code-event.ts new file mode 100644 index 0000000000..318b57ca2a --- /dev/null +++ b/packages/core/src/event-bus/events/coupon-code-event.ts @@ -0,0 +1,24 @@ +import { ID } from '@vendure/common/lib/shared-types'; + +import { RequestContext } from '../../api/common/request-context'; +import { VendureEvent } from '../vendure-event'; + +/** + * @description + * This event is fired whenever an coupon code of an active {@link Promotion} + * is assigned or removed to an {@link Order}. + * + * @docsCategory events + * @docsPage Event Types + * @since 1.4 + */ +export class CouponCodeEvent extends VendureEvent { + constructor( + public ctx: RequestContext, + public couponCode: string, + public orderId: ID, + public type: 'assigned' | 'removed', + ) { + super(); + } +} diff --git a/packages/core/src/event-bus/events/customer-address-event.ts b/packages/core/src/event-bus/events/customer-address-event.ts index 3acf89ef53..6a4f74c2a3 100644 --- a/packages/core/src/event-bus/events/customer-address-event.ts +++ b/packages/core/src/event-bus/events/customer-address-event.ts @@ -1,21 +1,40 @@ -import { RequestContext } from '../../api/common/request-context'; +import { CreateAddressInput, UpdateAddressInput } from '@vendure/common/lib/generated-types'; +import { ID } from '@vendure/common/lib/shared-types'; + +import { RequestContext } from '../../api'; import { Address } from '../../entity/address/address.entity'; -import { VendureEvent } from '../vendure-event'; +import { VendureEntityEvent } from '../vendure-entity-event'; + +/** + * Possible input types for Address mutations + */ +type CustomerAddressInputTypes = CreateAddressInput | UpdateAddressInput | ID; /** * @description - * This event is fired whenever a {@link Customer} is added, updated + * This event is fired whenever a {@link Address} is added, updated * or deleted. * * @docsCategory events * @docsPage Event Types + * @since 1.4 */ -export class CustomerAddressEvent extends VendureEvent { +export class CustomerAddressEvent extends VendureEntityEvent { constructor( public ctx: RequestContext, - public address: Address, + public entity: Address, public type: 'created' | 'updated' | 'deleted', + public input: CustomerAddressInputTypes, ) { - super(); + super(entity, type, ctx, input); + } + + /** + * Return an address field to become compatible with the + * deprecated old version of CustomerAddressEvent + * @deprecated Use `entity` instead + */ + get address(): Address { + return this.entity; } } diff --git a/packages/core/src/event-bus/events/customer-event.ts b/packages/core/src/event-bus/events/customer-event.ts index 7c63de214d..34a7b62042 100644 --- a/packages/core/src/event-bus/events/customer-event.ts +++ b/packages/core/src/event-bus/events/customer-event.ts @@ -1,6 +1,15 @@ +import { CreateCustomerInput, UpdateCustomerInput } from '@vendure/common/lib/generated-types'; +import { ID } from '@vendure/common/lib/shared-types'; + import { RequestContext } from '../../api/common/request-context'; import { Customer } from '../../entity/customer/customer.entity'; -import { VendureEvent } from '../vendure-event'; +import { VendureEntityEvent } from '../vendure-entity-event'; + +type CustomerInputTypes = + | CreateCustomerInput + | UpdateCustomerInput + | (Partial & { emailAddress: string }) + | ID; /** * @description @@ -10,12 +19,23 @@ import { VendureEvent } from '../vendure-event'; * @docsCategory events * @docsPage Event Types */ -export class CustomerEvent extends VendureEvent { +export class CustomerEvent extends VendureEntityEvent { constructor( - public ctx: RequestContext, - public customer: Customer, - public type: 'created' | 'updated' | 'deleted', + ctx: RequestContext, + entity: Customer, + type: 'created' | 'updated' | 'deleted', + input: CustomerInputTypes, ) { - super(); + super(entity, type, ctx, input); + } + + /** + * Return an customer field to become compatible with the + * deprecated old version of CustomerEvent + * @deprecated Use `entity` instead + * @since 1.4 + */ + get customer(): Customer { + return this.entity; } } diff --git a/packages/core/src/event-bus/events/customer-group-entity-event.ts b/packages/core/src/event-bus/events/customer-group-entity-event.ts new file mode 100644 index 0000000000..89d0cd8c89 --- /dev/null +++ b/packages/core/src/event-bus/events/customer-group-entity-event.ts @@ -0,0 +1,29 @@ +import { CreateCustomerGroupInput, UpdateCustomerGroupInput } from '@vendure/common/lib/generated-types'; +import { ID } from '@vendure/common/lib/shared-types'; + +import { RequestContext } from '../../api'; +import { CustomerGroup } from '../../entity'; +import { VendureEntityEvent } from '../vendure-entity-event'; + +type CustomerGroupInputTypes = CreateCustomerGroupInput | UpdateCustomerGroupInput | ID; + +/** + * @description + * This event is fired whenever a {@link CustomerGroup} is added, updated or deleted. + * Use this event instead of {@link CustomerGroupEvent} until the next major version! + * + * @docsCategory events + * @docsPage Event Types + * @since 1.4 + */ +export class CustomerGroupEntityEvent extends VendureEntityEvent { + // TODO: Rename to CustomerGroupEvent in v2 + constructor( + ctx: RequestContext, + entity: CustomerGroup, + type: 'created' | 'updated' | 'deleted', + input: CustomerGroupInputTypes, + ) { + super(entity, type, ctx, input); + } +} diff --git a/packages/core/src/event-bus/events/customer-group-event.ts b/packages/core/src/event-bus/events/customer-group-event.ts index 1e8267ddf7..00cc53f9ac 100644 --- a/packages/core/src/event-bus/events/customer-group-event.ts +++ b/packages/core/src/event-bus/events/customer-group-event.ts @@ -10,6 +10,7 @@ import { VendureEvent } from '../vendure-event'; * * @docsCategory events * @docsPage Event Types + * @deprecated Use {@link CustomerGroupChangeEvent} instead */ export class CustomerGroupEvent extends VendureEvent { constructor( @@ -21,3 +22,23 @@ export class CustomerGroupEvent extends VendureEvent { super(); } } + +/** + * @description + * This event is fired whenever one or more {@link Customer} is assigned to or removed from a + * {@link CustomerGroup}. + * + * @docsCategory events + * @docsPage Event Types + * @since 1.4 + */ +export class CustomerGroupChangeEvent extends VendureEvent { + constructor( + public ctx: RequestContext, + public customers: Customer[], + public customGroup: CustomerGroup, + public type: 'assigned' | 'removed', + ) { + super(); + } +} diff --git a/packages/core/src/event-bus/events/facet-event.ts b/packages/core/src/event-bus/events/facet-event.ts new file mode 100644 index 0000000000..dcc53fd962 --- /dev/null +++ b/packages/core/src/event-bus/events/facet-event.ts @@ -0,0 +1,27 @@ +import { CreateFacetInput, UpdateFacetInput } from '@vendure/common/lib/generated-types'; +import { ID } from '@vendure/common/lib/shared-types'; + +import { RequestContext } from '../../api'; +import { Facet } from '../../entity'; +import { VendureEntityEvent } from '../vendure-entity-event'; + +type FacetInputTypes = CreateFacetInput | UpdateFacetInput | ID; + +/** + * @description + * This event is fired whenever a {@link Facet} is added, updated or deleted. + * + * @docsCategory events + * @docsPage Event Types + * @since 1.4 + */ +export class FacetEvent extends VendureEntityEvent { + constructor( + ctx: RequestContext, + entity: Facet, + type: 'created' | 'updated' | 'deleted', + input: FacetInputTypes, + ) { + super(entity, type, ctx, input); + } +} diff --git a/packages/core/src/event-bus/events/facet-value-event.ts b/packages/core/src/event-bus/events/facet-value-event.ts new file mode 100644 index 0000000000..60128c4a2c --- /dev/null +++ b/packages/core/src/event-bus/events/facet-value-event.ts @@ -0,0 +1,35 @@ +import { + CreateFacetValueInput, + CreateFacetValueWithFacetInput, + UpdateFacetValueInput, +} from '@vendure/common/lib/generated-types'; +import { ID } from '@vendure/common/lib/shared-types'; + +import { RequestContext } from '../../api'; +import { FacetValue } from '../../entity'; +import { VendureEntityEvent } from '../vendure-entity-event'; + +type FacetValueInputTypes = + | CreateFacetValueInput + | CreateFacetValueWithFacetInput + | UpdateFacetValueInput + | ID; + +/** + * @description + * This event is fired whenever a {@link FacetValue} is added, updated or deleted. + * + * @docsCategory events + * @docsPage Event Types + * @since 1.4 + */ +export class FacetValueEvent extends VendureEntityEvent { + constructor( + ctx: RequestContext, + entity: FacetValue, + type: 'created' | 'updated' | 'deleted', + input: FacetValueInputTypes, + ) { + super(entity, type, ctx, input); + } +} diff --git a/packages/core/src/event-bus/events/fulfillment-event.ts b/packages/core/src/event-bus/events/fulfillment-event.ts new file mode 100644 index 0000000000..201bb8daa4 --- /dev/null +++ b/packages/core/src/event-bus/events/fulfillment-event.ts @@ -0,0 +1,31 @@ +import { ConfigurableOperationInput } from '@vendure/common/lib/generated-types'; + +import { RequestContext } from '../../api'; +import { Order, OrderItem } from '../../entity'; +import { Fulfillment } from '../../entity/fulfillment/fulfillment.entity'; +import { VendureEntityEvent } from '../vendure-entity-event'; + +/** + * @description + * The inputs used to create a new fulfillment + * @since 1.4 + */ +type CreateFulfillmentInput = { + orders: Order[]; + items: OrderItem[]; + handler: ConfigurableOperationInput; +}; + +/** + * @description + * This event is fired whenever a {@link Fulfillment} is added. The type is always `created`. + * + * @docsCategory events + * @docsPage Event Types + * @since 1.4 + */ +export class FulfillmentEvent extends VendureEntityEvent { + constructor(ctx: RequestContext, entity: Fulfillment, input: CreateFulfillmentInput) { + super(entity, 'created', ctx, input); + } +} diff --git a/packages/core/src/event-bus/events/global-settings-event.ts b/packages/core/src/event-bus/events/global-settings-event.ts new file mode 100644 index 0000000000..698f9f27b0 --- /dev/null +++ b/packages/core/src/event-bus/events/global-settings-event.ts @@ -0,0 +1,20 @@ +import { UpdateGlobalSettingsInput } from '@vendure/common/lib/generated-types'; + +import { RequestContext } from '../../api'; +import { GlobalSettings } from '../../entity/global-settings/global-settings.entity'; +import { VendureEntityEvent } from '../vendure-entity-event'; + +/** + * @description + * This event is fired whenever a {@link GlobalSettings} is added. The type is always `updated`, because it's + * only created once and never deleted. + * + * @docsCategory events + * @docsPage Event Types + * @since 1.4 + */ +export class GlobalSettingsEvent extends VendureEntityEvent { + constructor(ctx: RequestContext, entity: GlobalSettings, input: UpdateGlobalSettingsInput) { + super(entity, 'updated', ctx, input); + } +} diff --git a/packages/core/src/event-bus/events/history-entry-event.ts b/packages/core/src/event-bus/events/history-entry-event.ts new file mode 100644 index 0000000000..5c5dfc00ac --- /dev/null +++ b/packages/core/src/event-bus/events/history-entry-event.ts @@ -0,0 +1,36 @@ +import { HistoryEntryType } from '@vendure/common/lib/generated-types'; +import { ID } from '@vendure/common/lib/shared-types'; + +import { RequestContext } from '../../api/common/request-context'; +import { HistoryEntry } from '../../entity/history-entry/history-entry.entity'; +import { VendureEntityEvent } from '../vendure-entity-event'; + +type HistoryInput = + | { + type: HistoryEntryType; + data?: any; + } + | ID; + +/** + * @description + * This event is fired whenever one {@link HistoryEntry} is added, updated or deleted. + * + * @docsCategory events + * @docsPage Event Types + * @since 1.4 + */ +export class HistoryEntryEvent extends VendureEntityEvent { + public readonly historyType: 'order' | 'customer' | string; + + constructor( + ctx: RequestContext, + entity: HistoryEntry, + type: 'created' | 'updated' | 'deleted', + historyType: 'order' | 'customer' | string, + input: HistoryInput, + ) { + super(entity, type, ctx, input); + this.historyType = historyType; + } +} diff --git a/packages/core/src/event-bus/events/password-reset-verified-event.ts b/packages/core/src/event-bus/events/password-reset-verified-event.ts new file mode 100644 index 0000000000..49f0594d82 --- /dev/null +++ b/packages/core/src/event-bus/events/password-reset-verified-event.ts @@ -0,0 +1,17 @@ +import { RequestContext } from '../../api/common/request-context'; +import { User } from '../../entity/user/user.entity'; +import { VendureEvent } from '../vendure-event'; + +/** + * @description + * This event is fired when a password reset is executed with a verified token. + * + * @docsCategory events + * @docsPage Event Types + * @since 1.4 + */ +export class PasswordResetVerifiedEvent extends VendureEvent { + constructor(public ctx: RequestContext, public user: User) { + super(); + } +} diff --git a/packages/core/src/event-bus/events/payment-method-event.ts b/packages/core/src/event-bus/events/payment-method-event.ts new file mode 100644 index 0000000000..4c9df33878 --- /dev/null +++ b/packages/core/src/event-bus/events/payment-method-event.ts @@ -0,0 +1,27 @@ +import { CreatePaymentMethodInput, UpdatePaymentMethodInput } from '@vendure/common/lib/generated-types'; +import { ID } from '@vendure/common/lib/shared-types'; + +import { RequestContext } from '../../api/common/request-context'; +import { PaymentMethod } from '../../entity'; +import { VendureEntityEvent } from '../vendure-entity-event'; + +type PaymentMethodInputTypes = CreatePaymentMethodInput | UpdatePaymentMethodInput | ID; + +/** + * @description + * This event is fired whenever a {@link PaymentMethod} is added, updated + * or deleted. + * + * @docsCategory events + * @docsPage Event Types + */ +export class PaymentMethodEvent extends VendureEntityEvent { + constructor( + ctx: RequestContext, + entity: PaymentMethod, + type: 'created' | 'updated' | 'deleted', + input: PaymentMethodInputTypes, + ) { + super(entity, type, ctx, input); + } +} diff --git a/packages/core/src/event-bus/events/product-event.ts b/packages/core/src/event-bus/events/product-event.ts index f72b516471..a7250d3015 100644 --- a/packages/core/src/event-bus/events/product-event.ts +++ b/packages/core/src/event-bus/events/product-event.ts @@ -1,6 +1,11 @@ +import { CreateProductInput, UpdateProductInput } from '@vendure/common/lib/generated-types'; +import { ID } from '@vendure/common/lib/shared-types'; + import { RequestContext } from '../../api/common/request-context'; import { Product } from '../../entity'; -import { VendureEvent } from '../vendure-event'; +import { VendureEntityEvent } from '../vendure-entity-event'; + +type ProductInputTypes = CreateProductInput | UpdateProductInput | ID; /** * @description @@ -10,12 +15,23 @@ import { VendureEvent } from '../vendure-event'; * @docsCategory events * @docsPage Event Types */ -export class ProductEvent extends VendureEvent { +export class ProductEvent extends VendureEntityEvent { constructor( - public ctx: RequestContext, - public product: Product, - public type: 'created' | 'updated' | 'deleted', + ctx: RequestContext, + entity: Product, + type: 'created' | 'updated' | 'deleted', + input: ProductInputTypes, ) { - super(); + super(entity, type, ctx, input); + } + + /** + * Return an product field to become compatible with the + * deprecated old version of ProductEvent + * @deprecated Use `entity` instead + * @since 1.4 + */ + get product(): Product { + return this.entity; } } diff --git a/packages/core/src/event-bus/events/product-option-event.ts b/packages/core/src/event-bus/events/product-option-event.ts new file mode 100644 index 0000000000..c2825f75cb --- /dev/null +++ b/packages/core/src/event-bus/events/product-option-event.ts @@ -0,0 +1,35 @@ +import { + CreateGroupOptionInput, + CreateProductOptionInput, + UpdateProductOptionInput, +} from '@vendure/common/lib/generated-types'; +import { ID } from '@vendure/common/lib/shared-types'; + +import { RequestContext } from '../../api/common/request-context'; +import { ProductOption, ProductOptionGroup } from '../../entity'; +import { VendureEntityEvent } from '../vendure-entity-event'; + +type ProductOptionInputTypes = + | CreateGroupOptionInput + | CreateProductOptionInput + | UpdateProductOptionInput + | ID; + +/** + * @description + * This event is fired whenever a {@link ProductOption} is added or updated. + * + * @docsCategory events + * @docsPage Event Types + * @since 1.4 + */ +export class ProductOptionEvent extends VendureEntityEvent { + constructor( + ctx: RequestContext, + entity: ProductOption, + type: 'created' | 'updated', + input: ProductOptionInputTypes, + ) { + super(entity, type, ctx, input); + } +} diff --git a/packages/core/src/event-bus/events/product-option-group-change-event.ts b/packages/core/src/event-bus/events/product-option-group-change-event.ts new file mode 100644 index 0000000000..c230e3afe6 --- /dev/null +++ b/packages/core/src/event-bus/events/product-option-group-change-event.ts @@ -0,0 +1,24 @@ +import { ID } from '@vendure/common/lib/shared-types'; + +import { RequestContext } from '../../api/common/request-context'; +import { Product } from '../../entity'; +import { VendureEvent } from '../vendure-event'; + +/** + * @description + * This event is fired whenever a {@link ProductOptionGroup} is assigned or removed from a {@link Product}. + * + * @docsCategory events + * @docsPage Event Types + * @since 1.4 + */ +export class ProductOptionGroupChangeEvent extends VendureEvent { + constructor( + public ctx: RequestContext, + public product: Product, + public optionGroupId: ID, + public type: 'assigned' | 'removed', + ) { + super(); + } +} diff --git a/packages/core/src/event-bus/events/product-option-group-event.ts b/packages/core/src/event-bus/events/product-option-group-event.ts new file mode 100644 index 0000000000..68efc771d5 --- /dev/null +++ b/packages/core/src/event-bus/events/product-option-group-event.ts @@ -0,0 +1,33 @@ +import { + CreateProductOptionGroupInput, + UpdateProductOptionGroupInput, +} from '@vendure/common/lib/generated-types'; +import { ID } from '@vendure/common/lib/shared-types'; + +import { RequestContext } from '../../api/common/request-context'; +import { ProductOptionGroup } from '../../entity'; +import { VendureEntityEvent } from '../vendure-entity-event'; + +type ProductOptionGroupInputTypes = CreateProductOptionGroupInput | UpdateProductOptionGroupInput | ID; + +/** + * @description + * This event is fired whenever a {@link ProductOptionGroup} is added or updated. + * + * @docsCategory events + * @docsPage Event Types + * @since 1.4 + */ +export class ProductOptionGroupEvent extends VendureEntityEvent< + ProductOptionGroup, + ProductOptionGroupInputTypes +> { + constructor( + ctx: RequestContext, + entity: ProductOptionGroup, + type: 'created' | 'updated', + input: ProductOptionGroupInputTypes, + ) { + super(entity, type, ctx, input); + } +} diff --git a/packages/core/src/event-bus/events/product-variant-event.ts b/packages/core/src/event-bus/events/product-variant-event.ts index 8fe34f57c5..f90eae40e8 100644 --- a/packages/core/src/event-bus/events/product-variant-event.ts +++ b/packages/core/src/event-bus/events/product-variant-event.ts @@ -1,6 +1,11 @@ +import { CreateProductVariantInput, UpdateProductVariantInput } from '@vendure/common/lib/generated-types'; +import { ID } from '@vendure/common/lib/shared-types'; + import { RequestContext } from '../../api/common/request-context'; import { ProductVariant } from '../../entity'; -import { VendureEvent } from '../vendure-event'; +import { VendureEntityEvent } from '../vendure-entity-event'; + +type ProductVariantInputTypes = CreateProductVariantInput[] | UpdateProductVariantInput[] | ID | ID[]; /** * @description @@ -10,12 +15,23 @@ import { VendureEvent } from '../vendure-event'; * @docsCategory events * @docsPage Event Types */ -export class ProductVariantEvent extends VendureEvent { +export class ProductVariantEvent extends VendureEntityEvent { constructor( - public ctx: RequestContext, - public variants: ProductVariant[], - public type: 'created' | 'updated' | 'deleted', + ctx: RequestContext, + entity: ProductVariant[], + type: 'created' | 'updated' | 'deleted', + input: ProductVariantInputTypes, ) { - super(); + super(entity, type, ctx, input); + } + + /** + * Return an variants field to become compatible with the + * deprecated old version of ProductEvent + * @deprecated Use `entity` instead + * @since 1.4 + */ + get variants(): ProductVariant[] { + return this.entity; } } diff --git a/packages/core/src/event-bus/events/promotion-event.ts b/packages/core/src/event-bus/events/promotion-event.ts new file mode 100644 index 0000000000..e55ede9fe6 --- /dev/null +++ b/packages/core/src/event-bus/events/promotion-event.ts @@ -0,0 +1,27 @@ +import { CreatePromotionInput, UpdatePromotionInput } from '@vendure/common/lib/generated-types'; +import { ID } from '@vendure/common/lib/shared-types'; + +import { RequestContext } from '../../api/common/request-context'; +import { Promotion } from '../../entity'; +import { VendureEntityEvent } from '../vendure-entity-event'; + +type PromotionInputTypes = CreatePromotionInput | UpdatePromotionInput | ID; + +/** + * @description + * This event is fired whenever a {@link Promotion} is added, updated + * or deleted. + * + * @docsCategory events + * @docsPage Event Types + */ +export class PromotionEvent extends VendureEntityEvent { + constructor( + ctx: RequestContext, + entity: Promotion, + type: 'created' | 'updated' | 'deleted', + input: PromotionInputTypes, + ) { + super(entity, type, ctx, input); + } +} diff --git a/packages/core/src/event-bus/events/role-change-event.ts b/packages/core/src/event-bus/events/role-change-event.ts new file mode 100644 index 0000000000..110a3cfac1 --- /dev/null +++ b/packages/core/src/event-bus/events/role-change-event.ts @@ -0,0 +1,25 @@ +import { ID } from '@vendure/common/lib/shared-types'; + +import { RequestContext } from '../../api/common/request-context'; +import { Administrator, Role } from '../../entity'; +import { VendureEvent } from '../vendure-event'; + +/** + * @description + * This event is fired whenever one {@link Role} is assigned or removed from a user. + * The property `roleIds` only contains the removed or assigned role ids. + * + * @docsCategory events + * @docsPage Event Types + * @since 1.4 + */ +export class RoleChangeEvent extends VendureEvent { + constructor( + public ctx: RequestContext, + public admin: Administrator, + public roleIds: ID[], + public type: 'assigned' | 'removed', + ) { + super(); + } +} diff --git a/packages/core/src/event-bus/events/role-event.ts b/packages/core/src/event-bus/events/role-event.ts new file mode 100644 index 0000000000..df33a55b60 --- /dev/null +++ b/packages/core/src/event-bus/events/role-event.ts @@ -0,0 +1,27 @@ +import { CreateRoleInput, UpdateRoleInput } from '@vendure/common/lib/generated-types'; +import { ID } from '@vendure/common/lib/shared-types'; + +import { RequestContext } from '../../api/common/request-context'; +import { Role } from '../../entity'; +import { VendureEntityEvent } from '../vendure-entity-event'; + +type RoleInputTypes = CreateRoleInput | UpdateRoleInput | ID; + +/** + * @description + * This event is fired whenever one {@link Role} is is added, updated or deleted. + * + * @docsCategory events + * @docsPage Event Types + * @since 1.4 + */ +export class RoleEvent extends VendureEntityEvent { + constructor( + ctx: RequestContext, + entity: Role, + type: 'created' | 'updated' | 'deleted', + input: RoleInputTypes, + ) { + super(entity, type, ctx, input); + } +} diff --git a/packages/core/src/event-bus/events/shipping-method-event.ts b/packages/core/src/event-bus/events/shipping-method-event.ts new file mode 100644 index 0000000000..35a51f91bb --- /dev/null +++ b/packages/core/src/event-bus/events/shipping-method-event.ts @@ -0,0 +1,27 @@ +import { CreateShippingMethodInput, UpdateShippingMethodInput } from '@vendure/common/lib/generated-types'; +import { ID } from '@vendure/common/lib/shared-types'; + +import { RequestContext } from '../../api/common/request-context'; +import { ShippingMethod } from '../../entity'; +import { VendureEntityEvent } from '../vendure-entity-event'; + +type ShippingMethodInputTypes = CreateShippingMethodInput | UpdateShippingMethodInput | ID; + +/** + * @description + * This event is fired whenever a {@link ShippingMethod} is added, updated + * or deleted. + * + * @docsCategory events + * @docsPage Event Types + */ +export class ShippingMethodEvent extends VendureEntityEvent { + constructor( + ctx: RequestContext, + entity: ShippingMethod, + type: 'created' | 'updated' | 'deleted', + input: ShippingMethodInputTypes, + ) { + super(entity, type, ctx, input); + } +} diff --git a/packages/core/src/event-bus/events/tax-category-event.ts b/packages/core/src/event-bus/events/tax-category-event.ts new file mode 100644 index 0000000000..b775a105cc --- /dev/null +++ b/packages/core/src/event-bus/events/tax-category-event.ts @@ -0,0 +1,27 @@ +import { CreateTaxCategoryInput, UpdateTaxCategoryInput } from '@vendure/common/lib/generated-types'; +import { ID } from '@vendure/common/lib/shared-types'; + +import { RequestContext } from '../../api/common/request-context'; +import { TaxCategory } from '../../entity'; +import { VendureEntityEvent } from '../vendure-entity-event'; + +type TaxCategoryInputTypes = CreateTaxCategoryInput | UpdateTaxCategoryInput | ID; + +/** + * @description + * This event is fired whenever a {@link TaxCategory} is added, updated + * or deleted. + * + * @docsCategory events + * @docsPage Event Types + */ +export class TaxCategoryEvent extends VendureEntityEvent { + constructor( + ctx: RequestContext, + entity: TaxCategory, + type: 'created' | 'updated' | 'deleted', + input: TaxCategoryInputTypes, + ) { + super(entity, type, ctx, input); + } +} diff --git a/packages/core/src/event-bus/events/tax-rate-event.ts b/packages/core/src/event-bus/events/tax-rate-event.ts new file mode 100644 index 0000000000..ad031f7725 --- /dev/null +++ b/packages/core/src/event-bus/events/tax-rate-event.ts @@ -0,0 +1,27 @@ +import { CreateTaxRateInput, UpdateTaxRateInput } from '@vendure/common/lib/generated-types'; +import { ID } from '@vendure/common/lib/shared-types'; + +import { RequestContext } from '../../api/common/request-context'; +import { TaxRate } from '../../entity'; +import { VendureEntityEvent } from '../vendure-entity-event'; + +type TaxRateInputTypes = CreateTaxRateInput | UpdateTaxRateInput | ID; + +/** + * @description + * This event is fired whenever a {@link TaxRate} is added, updated + * or deleted. + * + * @docsCategory events + * @docsPage Event Types + */ +export class TaxRateEvent extends VendureEntityEvent { + constructor( + ctx: RequestContext, + entity: TaxRate, + type: 'created' | 'updated' | 'deleted', + input: TaxRateInputTypes, + ) { + super(entity, type, ctx, input); + } +} diff --git a/packages/core/src/event-bus/events/tax-rate-modification-event.ts b/packages/core/src/event-bus/events/tax-rate-modification-event.ts index e76c972e4c..7e5ae3eae9 100644 --- a/packages/core/src/event-bus/events/tax-rate-modification-event.ts +++ b/packages/core/src/event-bus/events/tax-rate-modification-event.ts @@ -8,6 +8,7 @@ import { VendureEvent } from '../vendure-event'; * * @docsCategory events * @docsPage Event Types + * @deprecated Use TaxRateEvent instead */ export class TaxRateModificationEvent extends VendureEvent { constructor(public ctx: RequestContext, public taxRate: TaxRate) { diff --git a/packages/core/src/event-bus/events/zone-event.ts b/packages/core/src/event-bus/events/zone-event.ts new file mode 100644 index 0000000000..3dba25e016 --- /dev/null +++ b/packages/core/src/event-bus/events/zone-event.ts @@ -0,0 +1,27 @@ +import { CreateZoneInput, UpdateZoneInput } from '@vendure/common/lib/generated-types'; +import { ID } from '@vendure/common/lib/shared-types'; + +import { RequestContext } from '../../api/common/request-context'; +import { Zone } from '../../entity'; +import { VendureEntityEvent } from '../vendure-entity-event'; + +type ZoneInputTypes = CreateZoneInput | UpdateZoneInput | ID; + +/** + * @description + * This event is fired whenever a {@link Zone} is added, updated + * or deleted. + * + * @docsCategory events + * @docsPage Event Types + */ +export class ZoneEvent extends VendureEntityEvent { + constructor( + ctx: RequestContext, + entity: Zone, + type: 'created' | 'updated' | 'deleted', + input: ZoneInputTypes, + ) { + super(entity, type, ctx, input); + } +} diff --git a/packages/core/src/event-bus/events/zone-members-event.ts b/packages/core/src/event-bus/events/zone-members-event.ts new file mode 100644 index 0000000000..6ba1b62b12 --- /dev/null +++ b/packages/core/src/event-bus/events/zone-members-event.ts @@ -0,0 +1,24 @@ +import { ID } from '@vendure/common/lib/shared-types'; + +import { RequestContext } from '../../api/common/request-context'; +import { Zone } from '../../entity'; +import { VendureEvent } from '../vendure-event'; + +/** + * @description + * This event is fired whenever a {@link Zone} gets {@link Country} members assigned or removed + * The `entity` property contains the zone with the already updated member field. + * + * @docsCategory events + * @docsPage Event Types + */ +export class ZoneMembersEvent extends VendureEvent { + constructor( + public ctx: RequestContext, + public entity: Zone, + public type: 'assigned' | 'removed', + public memberIds: ID[], + ) { + super(); + } +} diff --git a/packages/core/src/event-bus/vendure-entity-event.ts b/packages/core/src/event-bus/vendure-entity-event.ts new file mode 100644 index 0000000000..c1f092052d --- /dev/null +++ b/packages/core/src/event-bus/vendure-entity-event.ts @@ -0,0 +1,31 @@ +import { RequestContext } from '../api'; + +import { VendureEvent } from './vendure-event'; + +/** + * @description + * The base class for all entity events used by the EventBus system. + * * For event type `'updated'` the entity is the one before applying the patch (if not documented otherwise). + * * For event type `'deleted'` the input will most likely be an `id: ID` + * + * @docsCategory events + * */ +export abstract class VendureEntityEvent extends VendureEvent { + public readonly entity: Entity; + public readonly type: 'created' | 'updated' | 'deleted'; + public readonly ctx: RequestContext; + public readonly input: Input; + + protected constructor( + entity: Entity, + type: 'created' | 'updated' | 'deleted', + ctx: RequestContext, + input: Input, + ) { + super(); + this.entity = entity; + this.type = type; + this.ctx = ctx; + this.input = input; + } +} diff --git a/packages/core/src/service/services/administrator.service.ts b/packages/core/src/service/services/administrator.service.ts index a3fed4cae9..6ed50a4f03 100644 --- a/packages/core/src/service/services/administrator.service.ts +++ b/packages/core/src/service/services/administrator.service.ts @@ -14,6 +14,9 @@ import { TransactionalConnection } from '../../connection/transactional-connecti import { Administrator } from '../../entity/administrator/administrator.entity'; import { NativeAuthenticationMethod } from '../../entity/authentication-method/native-authentication-method.entity'; import { User } from '../../entity/user/user.entity'; +import { EventBus } from '../../event-bus'; +import { AdministratorEvent } from '../../event-bus/events/administrator-event'; +import { RoleChangeEvent } from '../../event-bus/events/role-change-event'; import { CustomFieldRelationService } from '../helpers/custom-field-relation/custom-field-relation.service'; import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder'; import { PasswordCipher } from '../helpers/password-cipher/password-cipher'; @@ -38,6 +41,7 @@ export class AdministratorService { private userService: UserService, private roleService: RoleService, private customFieldRelationService: CustomFieldRelationService, + private eventBus: EventBus, ) {} /** @internal */ @@ -111,6 +115,7 @@ export class AdministratorService { input, createdAdministrator, ); + this.eventBus.publish(new AdministratorEvent(ctx, createdAdministrator, 'created', input)); return createdAdministrator; } @@ -139,11 +144,21 @@ export class AdministratorService { } } if (input.roleIds) { + const removeIds = administrator.user.roles + .map(role => role.id) + .filter(roleId => (input.roleIds as ID[]).indexOf(roleId) === -1); + + const addIds = (input.roleIds as ID[]).filter( + roleId => !administrator.user.roles.some(role => role.id === roleId), + ); + administrator.user.roles = []; await this.connection.getRepository(ctx, User).save(administrator.user, { reload: false }); for (const roleId of input.roleIds) { updatedAdministrator = await this.assignRole(ctx, administrator.id, roleId); } + this.eventBus.publish(new RoleChangeEvent(ctx, administrator, addIds, 'assigned')); + this.eventBus.publish(new RoleChangeEvent(ctx, administrator, removeIds, 'removed')); } await this.customFieldRelationService.updateRelations( ctx, @@ -151,6 +166,7 @@ export class AdministratorService { input, updatedAdministrator, ); + this.eventBus.publish(new AdministratorEvent(ctx, administrator, 'updated', input)); return updatedAdministrator; } @@ -183,6 +199,7 @@ export class AdministratorService { await this.connection.getRepository(ctx, Administrator).update({ id }, { deletedAt: new Date() }); // tslint:disable-next-line:no-non-null-assertion await this.userService.softDelete(ctx, administrator.user!.id); + this.eventBus.publish(new AdministratorEvent(ctx, administrator, 'deleted', id)); return { result: DeletionResult.DELETED, }; diff --git a/packages/core/src/service/services/asset.service.ts b/packages/core/src/service/services/asset.service.ts index 79775bc247..af00706f67 100644 --- a/packages/core/src/service/services/asset.service.ts +++ b/packages/core/src/service/services/asset.service.ts @@ -266,7 +266,7 @@ export class AssetService { result.tags = tags; await this.connection.getRepository(ctx, Asset).save(result); } - this.eventBus.publish(new AssetEvent(ctx, result, 'created')); + this.eventBus.publish(new AssetEvent(ctx, result, 'created', input)); resolve(result); }); } @@ -284,7 +284,7 @@ export class AssetService { asset.tags = await this.tagService.valuesToTags(ctx, input.tags); } const updatedAsset = await this.connection.getRepository(ctx, Asset).save(asset); - this.eventBus.publish(new AssetEvent(ctx, updatedAsset, 'updated')); + this.eventBus.publish(new AssetEvent(ctx, updatedAsset, 'updated', input)); return updatedAsset; } @@ -419,7 +419,7 @@ export class AssetService { } catch (e) { Logger.error(`error.could-not-delete-asset-file`, undefined, e.stack); } - this.eventBus.publish(new AssetEvent(ctx, deletedAsset, 'deleted')); + this.eventBus.publish(new AssetEvent(ctx, deletedAsset, 'deleted', deletedAsset.id)); } return { result: DeletionResult.DELETED, diff --git a/packages/core/src/service/services/channel.service.ts b/packages/core/src/service/services/channel.service.ts index c6292bbf6a..e5a4ca88b6 100644 --- a/packages/core/src/service/services/channel.service.ts +++ b/packages/core/src/service/services/channel.service.ts @@ -26,6 +26,9 @@ import { Channel } from '../../entity/channel/channel.entity'; import { ProductVariantPrice } from '../../entity/product-variant/product-variant-price.entity'; import { Session } from '../../entity/session/session.entity'; import { Zone } from '../../entity/zone/zone.entity'; +import { EventBus } from '../../event-bus'; +import { ChangeChannelEvent } from '../../event-bus/events/change-channel-event'; +import { ChannelEvent } from '../../event-bus/events/channel-event'; import { CustomFieldRelationService } from '../helpers/custom-field-relation/custom-field-relation.service'; import { patchEntity } from '../helpers/utils/patch-entity'; @@ -46,6 +49,7 @@ export class ChannelService { private configService: ConfigService, private globalSettingsService: GlobalSettingsService, private customFieldRelationService: CustomFieldRelationService, + private eventBus: EventBus, ) {} /** @@ -68,10 +72,14 @@ export class ChannelService { * Assigns a ChannelAware entity to the default Channel as well as any channel * specified in the RequestContext. */ - async assignToCurrentChannel(entity: T, ctx: RequestContext): Promise { + async assignToCurrentChannel( + entity: T, + ctx: RequestContext, + ): Promise { const defaultChannel = await this.getDefaultChannel(); const channelIds = unique([ctx.channelId, defaultChannel.id]); entity.channels = channelIds.map(id => ({ id })) as any; + this.eventBus.publish(new ChangeChannelEvent(ctx, entity, [ctx.channelId], 'assigned')); return entity; } @@ -93,6 +101,7 @@ export class ChannelService { entity.channels.push(channel); } await this.connection.getRepository(ctx, entityType).save(entity as any, { reload: false }); + this.eventBus.publish(new ChangeChannelEvent(ctx, entity, channelIds, 'assigned', entityType)); return entity; } @@ -116,6 +125,7 @@ export class ChannelService { entity.channels = entity.channels.filter(c => !idsAreEqual(c.id, id)); } await this.connection.getRepository(ctx, entityType).save(entity as any, { reload: false }); + this.eventBus.publish(new ChangeChannelEvent(ctx, entity, channelIds, 'removed', entityType)); return entity; } @@ -189,6 +199,7 @@ export class ChannelService { const newChannel = await this.connection.getRepository(ctx, Channel).save(channel); await this.customFieldRelationService.updateRelations(ctx, Channel, input, newChannel); await this.allChannels.refresh(ctx); + this.eventBus.publish(new ChannelEvent(ctx, newChannel, 'created', input)); return channel; } @@ -222,16 +233,19 @@ export class ChannelService { await this.connection.getRepository(ctx, Channel).save(updatedChannel, { reload: false }); await this.customFieldRelationService.updateRelations(ctx, Channel, input, updatedChannel); await this.allChannels.refresh(ctx); + this.eventBus.publish(new ChannelEvent(ctx, channel, 'updated', input)); return assertFound(this.findOne(ctx, channel.id)); } async delete(ctx: RequestContext, id: ID): Promise { - await this.connection.getEntityOrThrow(ctx, Channel, id); + const channel = await this.connection.getEntityOrThrow(ctx, Channel, id); await this.connection.getRepository(ctx, Session).delete({ activeChannelId: id }); await this.connection.getRepository(ctx, Channel).delete(id); await this.connection.getRepository(ctx, ProductVariantPrice).delete({ channelId: id, }); + this.eventBus.publish(new ChannelEvent(ctx, channel, 'deleted', id)); + return { result: DeletionResult.DELETED, }; diff --git a/packages/core/src/service/services/collection.service.ts b/packages/core/src/service/services/collection.service.ts index e51a09a3eb..7f06a2d2f5 100644 --- a/packages/core/src/service/services/collection.service.ts +++ b/packages/core/src/service/services/collection.service.ts @@ -27,6 +27,7 @@ import { CollectionTranslation } from '../../entity/collection/collection-transl import { Collection } from '../../entity/collection/collection.entity'; import { ProductVariant } from '../../entity/product-variant/product-variant.entity'; import { EventBus } from '../../event-bus/event-bus'; +import { CollectionEvent } from '../../event-bus/events/collection-event'; import { CollectionModificationEvent } from '../../event-bus/events/collection-modification-event'; import { ProductEvent } from '../../event-bus/events/product-event'; import { ProductVariantEvent } from '../../event-bus/events/product-variant-event'; @@ -369,11 +370,17 @@ export class CollectionService implements OnModuleInit { }, }); await this.assetService.updateEntityAssets(ctx, collection, input); - await this.customFieldRelationService.updateRelations(ctx, Collection, input, collection); + const collectionWithRelations = await this.customFieldRelationService.updateRelations( + ctx, + Collection, + input, + collection, + ); await this.applyFiltersQueue.add({ ctx: ctx.serialize(), collectionIds: [collection.id], }); + this.eventBus.publish(new CollectionEvent(ctx, collectionWithRelations, 'created', input)); return assertFound(this.findOne(ctx, collection.id)); } @@ -403,6 +410,7 @@ export class CollectionService implements OnModuleInit { const affectedVariantIds = await this.getCollectionProductVariantIds(collection); this.eventBus.publish(new CollectionModificationEvent(ctx, collection, affectedVariantIds)); } + this.eventBus.publish(new CollectionEvent(ctx, collection, 'updated', input)); return assertFound(this.findOne(ctx, collection.id)); } @@ -416,6 +424,7 @@ export class CollectionService implements OnModuleInit { await this.connection.getRepository(ctx, Collection).remove(coll); this.eventBus.publish(new CollectionModificationEvent(ctx, coll, affectedVariantIds)); } + this.eventBus.publish(new CollectionEvent(ctx, collection, 'deleted', id)); return { result: DeletionResult.DELETED, }; diff --git a/packages/core/src/service/services/country.service.ts b/packages/core/src/service/services/country.service.ts index eaac27f1d5..43275040be 100644 --- a/packages/core/src/service/services/country.service.ts +++ b/packages/core/src/service/services/country.service.ts @@ -16,6 +16,8 @@ import { TransactionalConnection } from '../../connection/transactional-connecti import { Address } from '../../entity'; import { CountryTranslation } from '../../entity/country/country-translation.entity'; import { Country } from '../../entity/country/country.entity'; +import { EventBus } from '../../event-bus'; +import { CountryEvent } from '../../event-bus/events/country-event'; import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder'; import { TranslatableSaver } from '../helpers/translatable-saver/translatable-saver'; import { translateDeep } from '../helpers/utils/translate-entity'; @@ -34,7 +36,7 @@ export class CountryService { private connection: TransactionalConnection, private listQueryBuilder: ListQueryBuilder, private translatableSaver: TranslatableSaver, - private zoneService: ZoneService, + private eventBus: EventBus, ) {} findAll( @@ -94,6 +96,7 @@ export class CountryService { entityType: Country, translationType: CountryTranslation, }); + this.eventBus.publish(new CountryEvent(ctx, country, 'created', input)); return assertFound(this.findOne(ctx, country.id)); } @@ -104,6 +107,7 @@ export class CountryService { entityType: Country, translationType: CountryTranslation, }); + this.eventBus.publish(new CountryEvent(ctx, country, 'updated', input)); return assertFound(this.findOne(ctx, country.id)); } @@ -122,6 +126,7 @@ export class CountryService { }; } else { await this.connection.getRepository(ctx, Country).remove(country); + this.eventBus.publish(new CountryEvent(ctx, country, 'deleted', id)); return { result: DeletionResult.DELETED, message: '', diff --git a/packages/core/src/service/services/customer-group.service.ts b/packages/core/src/service/services/customer-group.service.ts index aaa6ded398..1bf4ce15f4 100644 --- a/packages/core/src/service/services/customer-group.service.ts +++ b/packages/core/src/service/services/customer-group.service.ts @@ -19,7 +19,8 @@ import { TransactionalConnection } from '../../connection/transactional-connecti import { CustomerGroup } from '../../entity/customer-group/customer-group.entity'; import { Customer } from '../../entity/customer/customer.entity'; import { EventBus } from '../../event-bus/event-bus'; -import { CustomerGroupEvent } from '../../event-bus/events/customer-group-event'; +import { CustomerGroupEntityEvent } from '../../event-bus/events/customer-group-entity-event'; +import { CustomerGroupChangeEvent, CustomerGroupEvent } from '../../event-bus/events/customer-group-event'; import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder'; import { patchEntity } from '../helpers/utils/patch-entity'; @@ -89,13 +90,16 @@ export class CustomerGroupService { } await this.connection.getRepository(ctx, Customer).save(customers); } - return assertFound(this.findOne(ctx, newCustomerGroup.id)); + const savedCustomerGroup = await assertFound(this.findOne(ctx, newCustomerGroup.id)); + this.eventBus.publish(new CustomerGroupEntityEvent(ctx, savedCustomerGroup, 'created', input)); + return savedCustomerGroup; } async update(ctx: RequestContext, input: UpdateCustomerGroupInput): Promise { const customerGroup = await this.connection.getEntityOrThrow(ctx, CustomerGroup, input.id); const updatedCustomerGroup = patchEntity(customerGroup, input); await this.connection.getRepository(ctx, CustomerGroup).save(updatedCustomerGroup, { reload: false }); + this.eventBus.publish(new CustomerGroupEntityEvent(ctx, customerGroup, 'updated', input)); return assertFound(this.findOne(ctx, customerGroup.id)); } @@ -103,6 +107,7 @@ export class CustomerGroupService { const group = await this.connection.getEntityOrThrow(ctx, CustomerGroup, id); try { await this.connection.getRepository(ctx, CustomerGroup).remove(group); + this.eventBus.publish(new CustomerGroupEntityEvent(ctx, group, 'deleted', id)); return { result: DeletionResult.DELETED, }; @@ -136,6 +141,8 @@ export class CustomerGroupService { await this.connection.getRepository(ctx, Customer).save(customers, { reload: false }); this.eventBus.publish(new CustomerGroupEvent(ctx, customers, group, 'assigned')); + this.eventBus.publish(new CustomerGroupChangeEvent(ctx, customers, group, 'assigned')); + return assertFound(this.findOne(ctx, group.id)); } @@ -161,6 +168,7 @@ export class CustomerGroupService { } await this.connection.getRepository(ctx, Customer).save(customers, { reload: false }); this.eventBus.publish(new CustomerGroupEvent(ctx, customers, group, 'removed')); + this.eventBus.publish(new CustomerGroupChangeEvent(ctx, customers, group, 'removed')); return assertFound(this.findOne(ctx, group.id)); } diff --git a/packages/core/src/service/services/customer.service.ts b/packages/core/src/service/services/customer.service.ts index 5d6bb0da0c..a7728f0203 100644 --- a/packages/core/src/service/services/customer.service.ts +++ b/packages/core/src/service/services/customer.service.ts @@ -46,11 +46,13 @@ import { HistoryEntry } from '../../entity/history-entry/history-entry.entity'; import { User } from '../../entity/user/user.entity'; import { EventBus } from '../../event-bus/event-bus'; import { AccountRegistrationEvent } from '../../event-bus/events/account-registration-event'; +import { AccountVerifiedEvent } from '../../event-bus/events/account-verified-event'; import { CustomerAddressEvent } from '../../event-bus/events/customer-address-event'; import { CustomerEvent } from '../../event-bus/events/customer-event'; import { IdentifierChangeEvent } from '../../event-bus/events/identifier-change-event'; import { IdentifierChangeRequestEvent } from '../../event-bus/events/identifier-change-request-event'; import { PasswordResetEvent } from '../../event-bus/events/password-reset-event'; +import { PasswordResetVerifiedEvent } from '../../event-bus/events/password-reset-verified-event'; import { CustomFieldRelationService } from '../helpers/custom-field-relation/custom-field-relation.service'; import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder'; import { addressToLine } from '../helpers/utils/address-to-line'; @@ -261,7 +263,7 @@ export class CustomerService { }, }); } - this.eventBus.publish(new CustomerEvent(ctx, createdCustomer, 'created')); + this.eventBus.publish(new CustomerEvent(ctx, createdCustomer, 'created', input)); return createdCustomer; } @@ -326,7 +328,7 @@ export class CustomerService { input, }, }); - this.eventBus.publish(new CustomerEvent(ctx, customer, 'updated')); + this.eventBus.publish(new CustomerEvent(ctx, customer, 'updated', input)); return assertFound(this.findOne(ctx, customer.id)); } @@ -459,7 +461,9 @@ export class CustomerService { strategy: NATIVE_AUTH_STRATEGY_NAME, }, }); - return assertFound(this.findOneByUserId(ctx, result.id)); + const user = assertFound(this.findOneByUserId(ctx, result.id)); + this.eventBus.publish(new AccountVerifiedEvent(ctx, customer)); + return user; } /** @@ -508,6 +512,7 @@ export class CustomerService { type: HistoryEntryType.CUSTOMER_PASSWORD_RESET_VERIFIED, data: {}, }); + this.eventBus.publish(new PasswordResetVerifiedEvent(ctx, result)); return result; } @@ -636,7 +641,7 @@ export class CustomerService { } else { customer = await this.connection.getRepository(ctx, Customer).save(new Customer(input)); await this.channelService.assignToCurrentChannel(customer, ctx); - this.eventBus.publish(new CustomerEvent(ctx, customer, 'created')); + this.eventBus.publish(new CustomerEvent(ctx, customer, 'created', input)); } return this.connection.getRepository(ctx, Customer).save(customer); } @@ -668,7 +673,7 @@ export class CustomerService { type: HistoryEntryType.CUSTOMER_ADDRESS_CREATED, data: { address: addressToLine(createdAddress) }, }); - this.eventBus.publish(new CustomerAddressEvent(ctx, createdAddress, 'created')); + this.eventBus.publish(new CustomerAddressEvent(ctx, createdAddress, 'created', input)); return createdAddress; } @@ -676,7 +681,7 @@ export class CustomerService { const address = await this.connection.getEntityOrThrow(ctx, Address, input.id, { relations: ['customer', 'country'], }); - const customer = await this.connection.findOneInChannel( + const customer = await this.connection.findOneInChannel( ctx, Customer, address.customer.id, @@ -704,7 +709,7 @@ export class CustomerService { input, }, }); - this.eventBus.publish(new CustomerAddressEvent(ctx, updatedAddress, 'updated')); + this.eventBus.publish(new CustomerAddressEvent(ctx, updatedAddress, 'updated', input)); return updatedAddress; } @@ -732,7 +737,7 @@ export class CustomerService { }, }); await this.connection.getRepository(ctx, Address).remove(address); - this.eventBus.publish(new CustomerAddressEvent(ctx, address, 'deleted')); + this.eventBus.publish(new CustomerAddressEvent(ctx, address, 'deleted', id)); return true; } @@ -745,7 +750,7 @@ export class CustomerService { .update({ id: customerId }, { deletedAt: new Date() }); // tslint:disable-next-line:no-non-null-assertion await this.userService.softDelete(ctx, customer.user!.id); - this.eventBus.publish(new CustomerEvent(ctx, customer, 'deleted')); + this.eventBus.publish(new CustomerEvent(ctx, customer, 'deleted', customerId)); return { result: DeletionResult.DELETED, }; diff --git a/packages/core/src/service/services/facet-value.service.ts b/packages/core/src/service/services/facet-value.service.ts index eade2dcfcb..c990ff85df 100644 --- a/packages/core/src/service/services/facet-value.service.ts +++ b/packages/core/src/service/services/facet-value.service.ts @@ -18,6 +18,8 @@ import { Product, ProductVariant } from '../../entity'; import { FacetValueTranslation } from '../../entity/facet-value/facet-value-translation.entity'; import { FacetValue } from '../../entity/facet-value/facet-value.entity'; import { Facet } from '../../entity/facet/facet.entity'; +import { EventBus } from '../../event-bus'; +import { FacetValueEvent } from '../../event-bus/events/facet-value-event'; import { CustomFieldRelationService } from '../helpers/custom-field-relation/custom-field-relation.service'; import { TranslatableSaver } from '../helpers/translatable-saver/translatable-saver'; import { translateDeep } from '../helpers/utils/translate-entity'; @@ -38,6 +40,7 @@ export class FacetValueService { private configService: ConfigService, private customFieldRelationService: CustomFieldRelationService, private channelService: ChannelService, + private eventBus: EventBus, ) {} findAll(lang: LanguageCode): Promise>> { @@ -97,12 +100,13 @@ export class FacetValueService { await this.channelService.assignToCurrentChannel(fv, ctx); }, }); - await this.customFieldRelationService.updateRelations( + const facetValueWithRelations = await this.customFieldRelationService.updateRelations( ctx, FacetValue, input as CreateFacetValueInput, facetValue, ); + this.eventBus.publish(new FacetValueEvent(ctx, facetValueWithRelations, 'created', input)); return assertFound(this.findOne(ctx, facetValue.id)); } @@ -114,6 +118,7 @@ export class FacetValueService { translationType: FacetValueTranslation, }); await this.customFieldRelationService.updateRelations(ctx, FacetValue, input, facetValue); + this.eventBus.publish(new FacetValueEvent(ctx, facetValue, 'updated', input)); return assertFound(this.findOne(ctx, facetValue.id)); } @@ -133,6 +138,7 @@ export class FacetValueService { } else if (force) { const facetValue = await this.connection.getEntityOrThrow(ctx, FacetValue, id); await this.connection.getRepository(ctx, FacetValue).remove(facetValue); + this.eventBus.publish(new FacetValueEvent(ctx, facetValue, 'deleted', id)); message = ctx.translate('message.facet-value-force-deleted', i18nVars); result = DeletionResult.DELETED; } else { diff --git a/packages/core/src/service/services/facet.service.ts b/packages/core/src/service/services/facet.service.ts index 5cf9830040..25bfa3f5af 100644 --- a/packages/core/src/service/services/facet.service.ts +++ b/packages/core/src/service/services/facet.service.ts @@ -6,7 +6,6 @@ import { LanguageCode, UpdateFacetInput, } from '@vendure/common/lib/generated-types'; -import { normalizeString } from '@vendure/common/lib/normalize-string'; import { ID, PaginatedList } from '@vendure/common/lib/shared-types'; import { RequestContext } from '../../api/common/request-context'; @@ -17,6 +16,8 @@ import { ConfigService } from '../../config/config.service'; import { TransactionalConnection } from '../../connection/transactional-connection'; import { FacetTranslation } from '../../entity/facet/facet-translation.entity'; import { Facet } from '../../entity/facet/facet.entity'; +import { EventBus } from '../../event-bus'; +import { FacetEvent } from '../../event-bus/events/facet-event'; import { CustomFieldRelationService } from '../helpers/custom-field-relation/custom-field-relation.service'; import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder'; import { TranslatableSaver } from '../helpers/translatable-saver/translatable-saver'; @@ -41,6 +42,7 @@ export class FacetService { private configService: ConfigService, private channelService: ChannelService, private customFieldRelationService: CustomFieldRelationService, + private eventBus: EventBus, ) {} findAll( @@ -112,7 +114,13 @@ export class FacetService { await this.channelService.assignToCurrentChannel(f, ctx); }, }); - await this.customFieldRelationService.updateRelations(ctx, Facet, input, facet); + const facetWithRelations = await this.customFieldRelationService.updateRelations( + ctx, + Facet, + input, + facet, + ); + this.eventBus.publish(new FacetEvent(ctx, facetWithRelations, 'created', input)); return assertFound(this.findOne(ctx, facet.id)); } @@ -127,6 +135,7 @@ export class FacetService { }, }); await this.customFieldRelationService.updateRelations(ctx, Facet, input, facet); + this.eventBus.publish(new FacetEvent(ctx, facet, 'updated', input)); return assertFound(this.findOne(ctx, facet.id)); } @@ -159,6 +168,7 @@ export class FacetService { await this.connection.getRepository(ctx, Facet).remove(facet); message = ctx.translate('message.facet-force-deleted', i18nVars); result = DeletionResult.DELETED; + this.eventBus.publish(new FacetEvent(ctx, facet, 'deleted', id)); } else { message = ctx.translate('message.facet-used', i18nVars); result = DeletionResult.NOT_DELETED; diff --git a/packages/core/src/service/services/fulfillment.service.ts b/packages/core/src/service/services/fulfillment.service.ts index 2810a046be..58d5e44d69 100644 --- a/packages/core/src/service/services/fulfillment.service.ts +++ b/packages/core/src/service/services/fulfillment.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { ConfigurableOperationInput } from '@vendure/common/lib/generated-types'; -import { DeepPartial, ID } from '@vendure/common/lib/shared-types'; +import { ID } from '@vendure/common/lib/shared-types'; import { isObject } from '@vendure/common/lib/shared-utils'; import { RequestContext } from '../../api/common/request-context'; @@ -9,13 +9,13 @@ import { FulfillmentStateTransitionError, InvalidFulfillmentHandlerError, } from '../../common/error/generated-graphql-admin-errors'; -import { OrderStateTransitionError } from '../../common/error/generated-graphql-shop-errors'; import { ConfigService } from '../../config/config.service'; import { TransactionalConnection } from '../../connection/transactional-connection'; import { Fulfillment } from '../../entity/fulfillment/fulfillment.entity'; import { OrderItem } from '../../entity/order-item/order-item.entity'; import { Order } from '../../entity/order/order.entity'; import { EventBus } from '../../event-bus/event-bus'; +import { FulfillmentEvent } from '../../event-bus/events/fulfillment-event'; import { FulfillmentStateTransitionEvent } from '../../event-bus/events/fulfillment-state-transition-event'; import { CustomFieldRelationService } from '../helpers/custom-field-relation/custom-field-relation.service'; import { FulfillmentState } from '../helpers/fulfillment-state-machine/fulfillment-state'; @@ -80,12 +80,19 @@ export class FulfillmentService { handlerCode: fulfillmentHandler.code, }), ); - await this.customFieldRelationService.updateRelations( + const fulfillmentWithRelations = await this.customFieldRelationService.updateRelations( ctx, Fulfillment, fulfillmentPartial, newFulfillment, ); + this.eventBus.publish( + new FulfillmentEvent(ctx, fulfillmentWithRelations, { + orders, + items, + handler, + }), + ); return newFulfillment; } diff --git a/packages/core/src/service/services/global-settings.service.ts b/packages/core/src/service/services/global-settings.service.ts index 1001d56461..953014deaf 100644 --- a/packages/core/src/service/services/global-settings.service.ts +++ b/packages/core/src/service/services/global-settings.service.ts @@ -6,6 +6,8 @@ import { InternalServerError } from '../../common/error/errors'; import { ConfigService } from '../../config/config.service'; import { TransactionalConnection } from '../../connection/transactional-connection'; import { GlobalSettings } from '../../entity/global-settings/global-settings.entity'; +import { EventBus } from '../../event-bus'; +import { GlobalSettingsEvent } from '../../event-bus/events/global-settings-event'; import { CustomFieldRelationService } from '../helpers/custom-field-relation/custom-field-relation.service'; import { patchEntity } from '../helpers/utils/patch-entity'; @@ -21,6 +23,7 @@ export class GlobalSettingsService { private connection: TransactionalConnection, private configService: ConfigService, private customFieldRelationService: CustomFieldRelationService, + private eventBus: EventBus, ) {} /** @@ -64,6 +67,7 @@ export class GlobalSettingsService { async updateSettings(ctx: RequestContext, input: UpdateGlobalSettingsInput): Promise { const settings = await this.getSettings(ctx); + this.eventBus.publish(new GlobalSettingsEvent(ctx, settings, input)); patchEntity(settings, input); await this.customFieldRelationService.updateRelations(ctx, GlobalSettings, input, settings); return this.connection.getRepository(ctx, GlobalSettings).save(settings); diff --git a/packages/core/src/service/services/history.service.ts b/packages/core/src/service/services/history.service.ts index e6e9a459a2..0916672799 100644 --- a/packages/core/src/service/services/history.service.ts +++ b/packages/core/src/service/services/history.service.ts @@ -14,6 +14,8 @@ import { Administrator } from '../../entity/administrator/administrator.entity'; import { CustomerHistoryEntry } from '../../entity/history-entry/customer-history-entry.entity'; import { HistoryEntry } from '../../entity/history-entry/history-entry.entity'; import { OrderHistoryEntry } from '../../entity/history-entry/order-history-entry.entity'; +import { EventBus } from '../../event-bus'; +import { HistoryEntryEvent } from '../../event-bus/events/history-entry-event'; import { FulfillmentState } from '../helpers/fulfillment-state-machine/fulfillment-state'; import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder'; import { OrderState } from '../helpers/order-state-machine/order-state'; @@ -150,6 +152,7 @@ export class HistoryService { private connection: TransactionalConnection, private administratorService: AdministratorService, private listQueryBuilder: ListQueryBuilder, + private eventBus: EventBus, ) {} async getHistoryForOrder( @@ -187,7 +190,9 @@ export class HistoryService { order: { id: orderId }, administrator, }); - return this.connection.getRepository(ctx, OrderHistoryEntry).save(entry); + const history = await this.connection.getRepository(ctx, OrderHistoryEntry).save(entry); + this.eventBus.publish(new HistoryEntryEvent(ctx, history, 'created', 'order', { type, data })); + return history; } async getHistoryForCustomer( @@ -226,7 +231,9 @@ export class HistoryService { customer: { id: customerId }, administrator, }); - return this.connection.getRepository(ctx, CustomerHistoryEntry).save(entry); + const history = await this.connection.getRepository(ctx, CustomerHistoryEntry).save(entry); + this.eventBus.publish(new HistoryEntryEvent(ctx, history, 'created', 'customer', { type, data })); + return history; } async updateOrderHistoryEntry( @@ -247,12 +254,15 @@ export class HistoryService { if (administrator) { entry.administrator = administrator; } - return this.connection.getRepository(ctx, OrderHistoryEntry).save(entry); + const newEntry = await this.connection.getRepository(ctx, OrderHistoryEntry).save(entry); + this.eventBus.publish(new HistoryEntryEvent(ctx, entry, 'updated', 'order', args)); + return newEntry; } async deleteOrderHistoryEntry(ctx: RequestContext, id: ID): Promise { const entry = await this.connection.getEntityOrThrow(ctx, OrderHistoryEntry, id); await this.connection.getRepository(ctx, OrderHistoryEntry).remove(entry); + this.eventBus.publish(new HistoryEntryEvent(ctx, entry, 'deleted', 'order', id)); } async updateCustomerHistoryEntry( @@ -270,12 +280,15 @@ export class HistoryService { if (administrator) { entry.administrator = administrator; } - return this.connection.getRepository(ctx, CustomerHistoryEntry).save(entry); + const newEntry = await this.connection.getRepository(ctx, CustomerHistoryEntry).save(entry); + this.eventBus.publish(new HistoryEntryEvent(ctx, entry, 'updated', 'customer', args)); + return newEntry; } async deleteCustomerHistoryEntry(ctx: RequestContext, id: ID): Promise { const entry = await this.connection.getEntityOrThrow(ctx, CustomerHistoryEntry, id); await this.connection.getRepository(ctx, CustomerHistoryEntry).remove(entry); + this.eventBus.publish(new HistoryEntryEvent(ctx, entry, 'deleted', 'customer', id)); } private async getAdministratorFromContext(ctx: RequestContext): Promise { diff --git a/packages/core/src/service/services/order.service.ts b/packages/core/src/service/services/order.service.ts index f9bd792c18..16353062c8 100644 --- a/packages/core/src/service/services/order.service.ts +++ b/packages/core/src/service/services/order.service.ts @@ -88,6 +88,7 @@ import { ShippingLine } from '../../entity/shipping-line/shipping-line.entity'; import { Surcharge } from '../../entity/surcharge/surcharge.entity'; import { User } from '../../entity/user/user.entity'; import { EventBus } from '../../event-bus/event-bus'; +import { CouponCodeEvent } from '../../event-bus/events/coupon-code-event'; import { OrderStateTransitionEvent } from '../../event-bus/events/order-state-transition-event'; import { RefundStateTransitionEvent } from '../../event-bus/events/refund-state-transition-event'; import { CustomFieldRelationService } from '../helpers/custom-field-relation/custom-field-relation.service'; @@ -627,6 +628,7 @@ export class OrderService { type: HistoryEntryType.ORDER_COUPON_APPLIED, data: { couponCode, promotionId: validationResult.id }, }); + this.eventBus.publish(new CouponCodeEvent(ctx, couponCode, orderId, 'assigned')); return this.applyPriceAdjustments(ctx, order); } @@ -654,6 +656,7 @@ export class OrderService { type: HistoryEntryType.ORDER_COUPON_REMOVED, data: { couponCode }, }); + this.eventBus.publish(new CouponCodeEvent(ctx, couponCode, orderId, 'removed')); const result = await this.applyPriceAdjustments(ctx, order); await this.connection.getRepository(ctx, OrderItem).save(affectedOrderItems); return result; diff --git a/packages/core/src/service/services/payment-method.service.ts b/packages/core/src/service/services/payment-method.service.ts index 30438ad581..2f4058be6d 100644 --- a/packages/core/src/service/services/payment-method.service.ts +++ b/packages/core/src/service/services/payment-method.service.ts @@ -22,6 +22,7 @@ import { TransactionalConnection } from '../../connection/transactional-connecti import { Order } from '../../entity/order/order.entity'; import { PaymentMethod } from '../../entity/payment-method/payment-method.entity'; import { EventBus } from '../../event-bus/event-bus'; +import { PaymentMethodEvent } from '../../event-bus/events/payment-method-event'; import { ConfigArgService } from '../helpers/config-arg/config-arg.service'; import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder'; import { patchEntity } from '../helpers/utils/patch-entity'; @@ -72,7 +73,11 @@ export class PaymentMethodService { ); } await this.channelService.assignToCurrentChannel(paymentMethod, ctx); - return this.connection.getRepository(ctx, PaymentMethod).save(paymentMethod); + const savedPaymentMethod = await this.connection + .getRepository(ctx, PaymentMethod) + .save(paymentMethod); + this.eventBus.publish(new PaymentMethodEvent(ctx, savedPaymentMethod, 'created', input)); + return savedPaymentMethod; } async update(ctx: RequestContext, input: UpdatePaymentMethodInput): Promise { @@ -90,6 +95,7 @@ export class PaymentMethodService { if (input.handler) { paymentMethod.handler = this.configArgService.parseInput('PaymentMethodHandler', input.handler); } + this.eventBus.publish(new PaymentMethodEvent(ctx, paymentMethod, 'updated', input)); return this.connection.getRepository(ctx, PaymentMethod).save(updatedPaymentMethod); } @@ -115,6 +121,7 @@ export class PaymentMethodService { } try { await this.connection.getRepository(ctx, PaymentMethod).remove(paymentMethod); + this.eventBus.publish(new PaymentMethodEvent(ctx, paymentMethod, 'deleted', paymentMethodId)); return { result: DeletionResult.DELETED, }; @@ -129,6 +136,7 @@ export class PaymentMethodService { // but will remove from the current channel paymentMethod.channels = paymentMethod.channels.filter(c => !idsAreEqual(c.id, ctx.channelId)); await this.connection.getRepository(ctx, PaymentMethod).save(paymentMethod); + this.eventBus.publish(new PaymentMethodEvent(ctx, paymentMethod, 'deleted', paymentMethodId)); return { result: DeletionResult.DELETED, }; diff --git a/packages/core/src/service/services/product-option-group.service.ts b/packages/core/src/service/services/product-option-group.service.ts index 70715e8be2..2df798097d 100644 --- a/packages/core/src/service/services/product-option-group.service.ts +++ b/packages/core/src/service/services/product-option-group.service.ts @@ -12,6 +12,8 @@ import { assertFound } from '../../common/utils'; import { TransactionalConnection } from '../../connection/transactional-connection'; import { ProductOptionGroupTranslation } from '../../entity/product-option-group/product-option-group-translation.entity'; import { ProductOptionGroup } from '../../entity/product-option-group/product-option-group.entity'; +import { EventBus } from '../../event-bus'; +import { ProductOptionGroupEvent } from '../../event-bus/events/product-option-group-event'; import { CustomFieldRelationService } from '../helpers/custom-field-relation/custom-field-relation.service'; import { TranslatableSaver } from '../helpers/translatable-saver/translatable-saver'; import { translateDeep } from '../helpers/utils/translate-entity'; @@ -28,6 +30,7 @@ export class ProductOptionGroupService { private connection: TransactionalConnection, private translatableSaver: TranslatableSaver, private customFieldRelationService: CustomFieldRelationService, + private eventBus: EventBus, ) {} findAll(ctx: RequestContext, filterTerm?: string): Promise>> { @@ -79,7 +82,13 @@ export class ProductOptionGroupService { entityType: ProductOptionGroup, translationType: ProductOptionGroupTranslation, }); - await this.customFieldRelationService.updateRelations(ctx, ProductOptionGroup, input, group); + const groupWithRelations = await this.customFieldRelationService.updateRelations( + ctx, + ProductOptionGroup, + input, + group, + ); + this.eventBus.publish(new ProductOptionGroupEvent(ctx, groupWithRelations, 'created', input)); return assertFound(this.findOne(ctx, group.id)); } @@ -94,6 +103,7 @@ export class ProductOptionGroupService { translationType: ProductOptionGroupTranslation, }); await this.customFieldRelationService.updateRelations(ctx, ProductOptionGroup, input, group); + this.eventBus.publish(new ProductOptionGroupEvent(ctx, group, 'updated', input)); return assertFound(this.findOne(ctx, group.id)); } } diff --git a/packages/core/src/service/services/product-option.service.ts b/packages/core/src/service/services/product-option.service.ts index 0a0e1cdcfd..6519eb2720 100644 --- a/packages/core/src/service/services/product-option.service.ts +++ b/packages/core/src/service/services/product-option.service.ts @@ -13,6 +13,8 @@ import { TransactionalConnection } from '../../connection/transactional-connecti import { ProductOptionGroup } from '../../entity/product-option-group/product-option-group.entity'; import { ProductOptionTranslation } from '../../entity/product-option/product-option-translation.entity'; import { ProductOption } from '../../entity/product-option/product-option.entity'; +import { EventBus } from '../../event-bus'; +import { ProductOptionEvent } from '../../event-bus/events/product-option-event'; import { CustomFieldRelationService } from '../helpers/custom-field-relation/custom-field-relation.service'; import { TranslatableSaver } from '../helpers/translatable-saver/translatable-saver'; import { translateDeep } from '../helpers/utils/translate-entity'; @@ -29,6 +31,7 @@ export class ProductOptionService { private connection: TransactionalConnection, private translatableSaver: TranslatableSaver, private customFieldRelationService: CustomFieldRelationService, + private eventBus: EventBus, ) {} findAll(ctx: RequestContext): Promise>> { @@ -65,12 +68,13 @@ export class ProductOptionService { translationType: ProductOptionTranslation, beforeSave: po => (po.group = productOptionGroup), }); - await this.customFieldRelationService.updateRelations( + const optionWithRelations = await this.customFieldRelationService.updateRelations( ctx, ProductOption, input as CreateProductOptionInput, option, ); + this.eventBus.publish(new ProductOptionEvent(ctx, optionWithRelations, 'created', input)); return assertFound(this.findOne(ctx, option.id)); } @@ -82,6 +86,7 @@ export class ProductOptionService { translationType: ProductOptionTranslation, }); await this.customFieldRelationService.updateRelations(ctx, ProductOption, input, option); + this.eventBus.publish(new ProductOptionEvent(ctx, option, 'updated', input)); return assertFound(this.findOne(ctx, option.id)); } } diff --git a/packages/core/src/service/services/product-variant.service.ts b/packages/core/src/service/services/product-variant.service.ts index 5452dcfe65..3f4a052649 100644 --- a/packages/core/src/service/services/product-variant.service.ts +++ b/packages/core/src/service/services/product-variant.service.ts @@ -355,7 +355,7 @@ export class ProductVariantService { ids.push(id); } const createdVariants = await this.findByIds(ctx, ids); - this.eventBus.publish(new ProductVariantEvent(ctx, createdVariants, 'created')); + this.eventBus.publish(new ProductVariantEvent(ctx, createdVariants, 'created', input)); return createdVariants; } @@ -370,7 +370,7 @@ export class ProductVariantService { ctx, input.map(i => i.id), ); - this.eventBus.publish(new ProductVariantEvent(ctx, updatedVariants, 'updated')); + this.eventBus.publish(new ProductVariantEvent(ctx, updatedVariants, 'updated', input)); return updatedVariants; } @@ -540,7 +540,7 @@ export class ProductVariantService { variant.deletedAt = new Date(); } await this.connection.getRepository(ctx, ProductVariant).save(variants, { reload: false }); - this.eventBus.publish(new ProductVariantEvent(ctx, variants, 'deleted')); + this.eventBus.publish(new ProductVariantEvent(ctx, variants, 'deleted', id)); return { result: DeletionResult.DELETED, }; diff --git a/packages/core/src/service/services/product.service.ts b/packages/core/src/service/services/product.service.ts index 1a90727ddc..c2e04fc369 100644 --- a/packages/core/src/service/services/product.service.ts +++ b/packages/core/src/service/services/product.service.ts @@ -28,6 +28,7 @@ import { Product } from '../../entity/product/product.entity'; import { EventBus } from '../../event-bus/event-bus'; import { ProductChannelEvent } from '../../event-bus/events/product-channel-event'; import { ProductEvent } from '../../event-bus/events/product-event'; +import { ProductOptionGroupChangeEvent } from '../../event-bus/events/product-option-group-change-event'; import { CustomFieldRelationService } from '../helpers/custom-field-relation/custom-field-relation.service'; import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder'; import { SlugValidator } from '../helpers/slug-validator/slug-validator'; @@ -193,7 +194,7 @@ export class ProductService { }); await this.customFieldRelationService.updateRelations(ctx, Product, input, product); await this.assetService.updateEntityAssets(ctx, product, input); - this.eventBus.publish(new ProductEvent(ctx, product, 'created')); + this.eventBus.publish(new ProductEvent(ctx, product, 'created', input)); return assertFound(this.findOne(ctx, product.id)); } @@ -223,7 +224,7 @@ export class ProductService { }, }); await this.customFieldRelationService.updateRelations(ctx, Product, input, updatedProduct); - this.eventBus.publish(new ProductEvent(ctx, updatedProduct, 'updated')); + this.eventBus.publish(new ProductEvent(ctx, updatedProduct, 'updated', input)); return assertFound(this.findOne(ctx, updatedProduct.id)); } @@ -234,7 +235,7 @@ export class ProductService { }); product.deletedAt = new Date(); await this.connection.getRepository(ctx, Product).save(product, { reload: false }); - this.eventBus.publish(new ProductEvent(ctx, product, 'deleted')); + this.eventBus.publish(new ProductEvent(ctx, product, 'deleted', productId)); await this.productVariantService.softDelete( ctx, product.variants.map(v => v.id), @@ -327,6 +328,7 @@ export class ProductService { } await this.connection.getRepository(ctx, Product).save(product, { reload: false }); + this.eventBus.publish(new ProductOptionGroupChangeEvent(ctx, product, optionGroupId, 'assigned')); return assertFound(this.findOne(ctx, productId)); } @@ -346,6 +348,7 @@ export class ProductService { product.optionGroups = product.optionGroups.filter(g => g.id !== optionGroupId); await this.connection.getRepository(ctx, Product).save(product, { reload: false }); + this.eventBus.publish(new ProductOptionGroupChangeEvent(ctx, product, optionGroupId, 'removed')); return assertFound(this.findOne(ctx, productId)); } diff --git a/packages/core/src/service/services/promotion.service.ts b/packages/core/src/service/services/promotion.service.ts index 5c0463c424..77c6089ca4 100644 --- a/packages/core/src/service/services/promotion.service.ts +++ b/packages/core/src/service/services/promotion.service.ts @@ -34,6 +34,8 @@ import { PromotionCondition } from '../../config/promotion/promotion-condition'; import { TransactionalConnection } from '../../connection/transactional-connection'; import { Order } from '../../entity/order/order.entity'; import { Promotion } from '../../entity/promotion/promotion.entity'; +import { EventBus } from '../../event-bus'; +import { PromotionEvent } from '../../event-bus/events/promotion-event'; import { ConfigArgService } from '../helpers/config-arg/config-arg.service'; import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder'; import { patchEntity } from '../helpers/utils/patch-entity'; @@ -57,6 +59,7 @@ export class PromotionService { private channelService: ChannelService, private listQueryBuilder: ListQueryBuilder, private configArgService: ConfigArgService, + private eventBus: EventBus, ) { this.availableConditions = this.configService.promotionOptions.promotionConditions || []; this.availableActions = this.configService.promotionOptions.promotionActions || []; @@ -116,6 +119,7 @@ export class PromotionService { } await this.channelService.assignToCurrentChannel(promotion, ctx); const newPromotion = await this.connection.getRepository(ctx, Promotion).save(promotion); + this.eventBus.publish(new PromotionEvent(ctx, newPromotion, 'created', input)); return assertFound(this.findOne(ctx, newPromotion.id)); } @@ -142,14 +146,17 @@ export class PromotionService { } promotion.priorityScore = this.calculatePriorityScore(input); await this.connection.getRepository(ctx, Promotion).save(updatedPromotion, { reload: false }); + this.eventBus.publish(new PromotionEvent(ctx, promotion, 'updated', input)); return assertFound(this.findOne(ctx, updatedPromotion.id)); } async softDeletePromotion(ctx: RequestContext, promotionId: ID): Promise { - await this.connection.getEntityOrThrow(ctx, Promotion, promotionId); + const promotion = await this.connection.getEntityOrThrow(ctx, Promotion, promotionId); await this.connection .getRepository(ctx, Promotion) .update({ id: promotionId }, { deletedAt: new Date() }); + this.eventBus.publish(new PromotionEvent(ctx, promotion, 'deleted', promotionId)); + return { result: DeletionResult.DELETED, }; diff --git a/packages/core/src/service/services/role.service.ts b/packages/core/src/service/services/role.service.ts index 562cd91f5d..dbfdaa0656 100644 --- a/packages/core/src/service/services/role.service.ts +++ b/packages/core/src/service/services/role.service.ts @@ -30,6 +30,8 @@ import { TransactionalConnection } from '../../connection/transactional-connecti import { Channel } from '../../entity/channel/channel.entity'; import { Role } from '../../entity/role/role.entity'; import { User } from '../../entity/user/user.entity'; +import { EventBus } from '../../event-bus'; +import { RoleEvent } from '../../event-bus/events/role-event'; import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder'; import { getUserChannelsPermissions } from '../helpers/utils/get-user-channels-permissions'; import { patchEntity } from '../helpers/utils/patch-entity'; @@ -49,6 +51,7 @@ export class RoleService { private channelService: ChannelService, private listQueryBuilder: ListQueryBuilder, private configService: ConfigService, + private eventBus: EventBus, ) {} async initRoles() { @@ -143,7 +146,9 @@ export class RoleService { } else { targetChannels = [ctx.channel]; } - return this.createRoleForChannels(ctx, input, targetChannels); + const role = await this.createRoleForChannels(ctx, input, targetChannels); + this.eventBus.publish(new RoleEvent(ctx, role, 'created', input)); + return role; } async update(ctx: RequestContext, input: UpdateRoleInput): Promise { @@ -166,7 +171,8 @@ export class RoleService { updatedRole.channels = await this.getPermittedChannels(ctx, input.channelIds); } await this.connection.getRepository(ctx, Role).save(updatedRole, { reload: false }); - return assertFound(this.findOne(ctx, role.id)); + this.eventBus.publish(new RoleEvent(ctx, role, 'updated', input)); + return await assertFound(this.findOne(ctx, role.id)); } async delete(ctx: RequestContext, id: ID): Promise { @@ -178,6 +184,7 @@ export class RoleService { throw new InternalServerError(`error.cannot-delete-role`, { roleCode: role.code }); } await this.connection.getRepository(ctx, Role).remove(role); + this.eventBus.publish(new RoleEvent(ctx, role, 'deleted', id)); return { result: DeletionResult.DELETED, }; diff --git a/packages/core/src/service/services/shipping-method.service.ts b/packages/core/src/service/services/shipping-method.service.ts index 97588f5603..92516e8743 100644 --- a/packages/core/src/service/services/shipping-method.service.ts +++ b/packages/core/src/service/services/shipping-method.service.ts @@ -19,6 +19,8 @@ import { TransactionalConnection } from '../../connection/transactional-connecti import { Channel } from '../../entity/channel/channel.entity'; import { ShippingMethodTranslation } from '../../entity/shipping-method/shipping-method-translation.entity'; import { ShippingMethod } from '../../entity/shipping-method/shipping-method.entity'; +import { EventBus } from '../../event-bus'; +import { ShippingMethodEvent } from '../../event-bus/events/shipping-method-event'; import { ConfigArgService } from '../helpers/config-arg/config-arg.service'; import { CustomFieldRelationService } from '../helpers/custom-field-relation/custom-field-relation.service'; import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder'; @@ -43,6 +45,7 @@ export class ShippingMethodService { private configArgService: ConfigArgService, private translatableSaver: TranslatableSaver, private customFieldRelationService: CustomFieldRelationService, + private eventBus: EventBus, ) {} /** @internal */ @@ -113,7 +116,13 @@ export class ShippingMethodService { const newShippingMethod = await this.connection .getRepository(ctx, ShippingMethod) .save(shippingMethod); - await this.customFieldRelationService.updateRelations(ctx, ShippingMethod, input, newShippingMethod); + const shippingMethodWithRelations = await this.customFieldRelationService.updateRelations( + ctx, + ShippingMethod, + input, + newShippingMethod, + ); + this.eventBus.publish(new ShippingMethodEvent(ctx, shippingMethodWithRelations, 'created', input)); return assertFound(this.findOne(ctx, newShippingMethod.id)); } @@ -155,6 +164,7 @@ export class ShippingMethodService { input, updatedShippingMethod, ); + this.eventBus.publish(new ShippingMethodEvent(ctx, shippingMethod, 'updated', input)); return assertFound(this.findOne(ctx, shippingMethod.id)); } @@ -165,6 +175,7 @@ export class ShippingMethodService { }); shippingMethod.deletedAt = new Date(); await this.connection.getRepository(ctx, ShippingMethod).save(shippingMethod, { reload: false }); + this.eventBus.publish(new ShippingMethodEvent(ctx, shippingMethod, 'deleted', id)); return { result: DeletionResult.DELETED, }; diff --git a/packages/core/src/service/services/tax-category.service.ts b/packages/core/src/service/services/tax-category.service.ts index 59ceeb12aa..320edb3c04 100644 --- a/packages/core/src/service/services/tax-category.service.ts +++ b/packages/core/src/service/services/tax-category.service.ts @@ -13,6 +13,8 @@ import { assertFound } from '../../common/utils'; import { TransactionalConnection } from '../../connection/transactional-connection'; import { TaxCategory } from '../../entity/tax-category/tax-category.entity'; import { TaxRate } from '../../entity/tax-rate/tax-rate.entity'; +import { EventBus } from '../../event-bus'; +import { TaxCategoryEvent } from '../../event-bus/events/tax-category-event'; import { patchEntity } from '../helpers/utils/patch-entity'; /** @@ -23,7 +25,7 @@ import { patchEntity } from '../helpers/utils/patch-entity'; */ @Injectable() export class TaxCategoryService { - constructor(private connection: TransactionalConnection) {} + constructor(private connection: TransactionalConnection, private eventBus: EventBus) {} findAll(ctx: RequestContext): Promise { return this.connection.getRepository(ctx, TaxCategory).find(); @@ -41,6 +43,7 @@ export class TaxCategoryService { .update({ isDefault: true }, { isDefault: false }); } const newTaxCategory = await this.connection.getRepository(ctx, TaxCategory).save(taxCategory); + this.eventBus.publish(new TaxCategoryEvent(ctx, newTaxCategory, 'created', input)); return assertFound(this.findOne(ctx, newTaxCategory.id)); } @@ -56,6 +59,7 @@ export class TaxCategoryService { .update({ isDefault: true }, { isDefault: false }); } await this.connection.getRepository(ctx, TaxCategory).save(updatedTaxCategory, { reload: false }); + this.eventBus.publish(new TaxCategoryEvent(ctx, taxCategory, 'updated', input)); return assertFound(this.findOne(ctx, taxCategory.id)); } @@ -78,6 +82,7 @@ export class TaxCategoryService { try { await this.connection.getRepository(ctx, TaxCategory).remove(taxCategory); + this.eventBus.publish(new TaxCategoryEvent(ctx, taxCategory, 'deleted', id)); return { result: DeletionResult.DELETED, }; diff --git a/packages/core/src/service/services/tax-rate.service.ts b/packages/core/src/service/services/tax-rate.service.ts index 09faa36dab..4236efed44 100644 --- a/packages/core/src/service/services/tax-rate.service.ts +++ b/packages/core/src/service/services/tax-rate.service.ts @@ -18,6 +18,7 @@ import { TaxCategory } from '../../entity/tax-category/tax-category.entity'; import { TaxRate } from '../../entity/tax-rate/tax-rate.entity'; import { Zone } from '../../entity/zone/zone.entity'; import { EventBus } from '../../event-bus/event-bus'; +import { TaxRateEvent } from '../../event-bus/events/tax-rate-event'; import { TaxRateModificationEvent } from '../../event-bus/events/tax-rate-modification-event'; import { ListQueryBuilder } from '../helpers/list-query-builder/list-query-builder'; import { patchEntity } from '../helpers/utils/patch-entity'; @@ -76,6 +77,7 @@ export class TaxRateService { const newTaxRate = await this.connection.getRepository(ctx, TaxRate).save(taxRate); await this.updateActiveTaxRates(ctx); this.eventBus.publish(new TaxRateModificationEvent(ctx, newTaxRate)); + this.eventBus.publish(new TaxRateEvent(ctx, newTaxRate, 'created', input)); return assertFound(this.findOne(ctx, newTaxRate.id)); } @@ -110,6 +112,8 @@ export class TaxRateService { await this.connection.commitOpenTransaction(ctx); this.eventBus.publish(new TaxRateModificationEvent(ctx, updatedTaxRate)); + this.eventBus.publish(new TaxRateEvent(ctx, updatedTaxRate, 'updated', input)); + return assertFound(this.findOne(ctx, taxRate.id)); } @@ -117,6 +121,7 @@ export class TaxRateService { const taxRate = await this.connection.getEntityOrThrow(ctx, TaxRate, id); try { await this.connection.getRepository(ctx, TaxRate).remove(taxRate); + this.eventBus.publish(new TaxRateEvent(ctx, taxRate, 'deleted', id)); return { result: DeletionResult.DELETED, }; diff --git a/packages/core/src/service/services/zone.service.ts b/packages/core/src/service/services/zone.service.ts index 9d4504313e..1b818f831c 100644 --- a/packages/core/src/service/services/zone.service.ts +++ b/packages/core/src/service/services/zone.service.ts @@ -18,6 +18,9 @@ import { TransactionalConnection } from '../../connection/transactional-connecti import { Channel, TaxRate } from '../../entity'; import { Country } from '../../entity/country/country.entity'; import { Zone } from '../../entity/zone/zone.entity'; +import { EventBus } from '../../event-bus'; +import { ZoneEvent } from '../../event-bus/events/zone-event'; +import { ZoneMembersEvent } from '../../event-bus/events/zone-members-event'; import { patchEntity } from '../helpers/utils/patch-entity'; import { translateDeep } from '../helpers/utils/translate-entity'; @@ -33,7 +36,11 @@ export class ZoneService { * We cache all Zones to avoid hitting the DB many times per request. */ private zones: SelfRefreshingCache; - constructor(private connection: TransactionalConnection, private configService: ConfigService) {} + constructor( + private connection: TransactionalConnection, + private configService: ConfigService, + private eventBus: EventBus, + ) {} /** @internal */ async initZones() { @@ -80,6 +87,7 @@ export class ZoneService { } const newZone = await this.connection.getRepository(ctx, Zone).save(zone); await this.zones.refresh(ctx); + this.eventBus.publish(new ZoneEvent(ctx, newZone, 'created', input)); return assertFound(this.findOne(ctx, newZone.id)); } @@ -88,6 +96,7 @@ export class ZoneService { const updatedZone = patchEntity(zone, input); await this.connection.getRepository(ctx, Zone).save(updatedZone, { reload: false }); await this.zones.refresh(ctx); + this.eventBus.publish(new ZoneEvent(ctx, zone, 'updated', input)); return assertFound(this.findOne(ctx, zone.id)); } @@ -126,6 +135,7 @@ export class ZoneService { } else { await this.connection.getRepository(ctx, Zone).remove(zone); await this.zones.refresh(ctx); + this.eventBus.publish(new ZoneEvent(ctx, zone, 'deleted', id)); return { result: DeletionResult.DELETED, message: '', @@ -133,28 +143,33 @@ export class ZoneService { } } - async addMembersToZone(ctx: RequestContext, input: MutationAddMembersToZoneArgs): Promise { - const countries = await this.getCountriesFromIds(ctx, input.memberIds); - const zone = await this.connection.getEntityOrThrow(ctx, Zone, input.zoneId, { + async addMembersToZone( + ctx: RequestContext, + { memberIds, zoneId }: MutationAddMembersToZoneArgs, + ): Promise { + const countries = await this.getCountriesFromIds(ctx, memberIds); + const zone = await this.connection.getEntityOrThrow(ctx, Zone, zoneId, { relations: ['members'], }); const members = unique(zone.members.concat(countries), 'id'); zone.members = members; await this.connection.getRepository(ctx, Zone).save(zone, { reload: false }); await this.zones.refresh(ctx); + this.eventBus.publish(new ZoneMembersEvent(ctx, zone, 'assigned', memberIds)); return assertFound(this.findOne(ctx, zone.id)); } async removeMembersFromZone( ctx: RequestContext, - input: MutationRemoveMembersFromZoneArgs, + { memberIds, zoneId }: MutationRemoveMembersFromZoneArgs, ): Promise { - const zone = await this.connection.getEntityOrThrow(ctx, Zone, input.zoneId, { + const zone = await this.connection.getEntityOrThrow(ctx, Zone, zoneId, { relations: ['members'], }); - zone.members = zone.members.filter(country => !input.memberIds.includes(country.id)); + zone.members = zone.members.filter(country => !memberIds.includes(country.id)); await this.connection.getRepository(ctx, Zone).save(zone, { reload: false }); await this.zones.refresh(ctx); + this.eventBus.publish(new ZoneMembersEvent(ctx, zone, 'removed', memberIds)); return assertFound(this.findOne(ctx, zone.id)); }