chore(): Module Internal Events (#13296)

* chore(): Ensure the product module emits all necessary events

* chore(): Ensure the product module emits all necessary events

* Update events tests

* more events and fixes

* more tests and category fixes

* more tests and category fixes

* Add todo

* update updateProduct_ event emitting and adjust test

* Adjust update products implementation to rely on already computed events

* rm unnecessary update variants events

* Fix formatting in changeset for product events

* refactor: Manage event emitting automatically (WIP)

* refactor: Manage event emitting automatically (WIP)

* chore(api-key): Add missing emit events and refactoring

* chore(cart): Add missing emit events and refactoring

* chore(customer): Add missing emit events and refactoring

* chore(fufillment, utils): Add missing emit events and refactoring

* chore(fufillment, utils): Add missing emit events and refactoring

* chore(inventory): Add missing emit events and refactoring

* chore(notification): Add missing emit events and refactoring

* chore(utils): Remove medusa service event handling legacy

* chore(product): Add missing emit events and refactoring

* chore(order): Add missing emit events and refactoring

* chore(payment): Add missing emit events and refactoring

* chore(pricing, util): Add missing emit events and refactoring, fix internal service upsertWithReplace event dispatching

* chore(promotions): Add missing emit events and refactoring

* chore(region): Add missing emit events and refactoring

* chore(sales-channel): Add missing emit events and refactoring

* chore(settings): Add missing emit events and refactoring

* chore(stock-location): Add missing emit events and refactoring

* chore(store): Add missing emit events and refactoring

* chore(taxes): Add missing emit events and refactoring

* chore(user): Add missing emit events and refactoring

* fix unit tests

* rm changeset for regeneration

* Create changeset for Medusa.js patch updates

Add a changeset for patch updates to multiple Medusa.js modules.

* rm unused product event builders

* address feedback

* remove old changeset

* fix event action for token generated

* fix user module events

* fix import

* fix promotion events

* add new module integration tests shard

* fix medusa service

* revert shard

* fix event action

* fix pipeline

* fix pipeline

---------

Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
Adrien de Peretti
2025-09-10 14:37:38 +02:00
committed by GitHub
parent afe21741c4
commit e8822f3e69
55 changed files with 3614 additions and 2353 deletions

View File

@@ -47,18 +47,9 @@ import {
ShippingOptionType,
ShippingProfile,
} from "@models"
import {
buildCreatedFulfillmentEvents,
buildCreatedFulfillmentSetEvents,
buildCreatedServiceZoneEvents,
eventBuilders,
isContextValid,
Rule,
validateAndNormalizeRules,
} from "@utils"
import { isContextValid, Rule, validateAndNormalizeRules } from "@utils"
import { joinerConfig } from "../joiner-config"
import { UpdateShippingOptionsInput } from "../types/service"
import { buildCreatedShippingOptionEvents } from "../utils/events"
import FulfillmentProviderService from "./fulfillment-provider"
const generateMethodForModels = {
@@ -70,7 +61,7 @@ const generateMethodForModels = {
ShippingOptionRule,
ShippingOptionType,
FulfillmentProvider,
// Not adding Fulfillment to not auto generate the methods under the hood and only provide the methods we want to expose8
// Not adding Fulfillment to not auto generate the methods under the hood and only provide the methods we want to expose
}
type InjectedDependencies = {
@@ -339,11 +330,6 @@ export default class FulfillmentModuleService
sharedContext
)
buildCreatedFulfillmentSetEvents({
fulfillmentSets: createdFulfillmentSets,
sharedContext,
})
return createdFulfillmentSets
}
@@ -405,11 +391,6 @@ export default class FulfillmentModuleService
sharedContext
)
buildCreatedServiceZoneEvents({
serviceZones: createdServiceZones,
sharedContext,
})
return createdServiceZones
}
@@ -468,11 +449,6 @@ export default class FulfillmentModuleService
sharedContext
)
buildCreatedShippingOptionEvents({
shippingOptions: createdSO,
sharedContext,
})
return createdSO
}
@@ -487,7 +463,7 @@ export default class FulfillmentModuleService
sharedContext?: Context
): Promise<FulfillmentTypes.ShippingProfileDTO>
@InjectTransactionManager()
@InjectManager()
@EmitEvents()
// @ts-expect-error
async createShippingProfiles(
@@ -503,11 +479,6 @@ export default class FulfillmentModuleService
sharedContext
)
eventBuilders.createdShippingProfile({
data: createdShippingProfiles,
sharedContext,
})
return await this.baseRepository_.serialize<
| FulfillmentTypes.ShippingProfileDTO
| FulfillmentTypes.ShippingProfileDTO[]
@@ -561,11 +532,6 @@ export default class FulfillmentModuleService
sharedContext
)
eventBuilders.createdGeoZone({
data: createdGeoZones,
sharedContext,
})
return await this.baseRepository_.serialize<FulfillmentTypes.GeoZoneDTO[]>(
Array.isArray(data) ? createdGeoZones : createdGeoZones[0]
)
@@ -629,11 +595,6 @@ export default class FulfillmentModuleService
sharedContext
)
eventBuilders.createdShippingOptionRule({
data: createdSORules.map((sor) => ({ id: sor.id })),
sharedContext,
})
return createdSORules
}
@@ -679,11 +640,6 @@ export default class FulfillmentModuleService
throw error
}
buildCreatedFulfillmentEvents({
fulfillments: [fulfillment],
sharedContext,
})
return await this.baseRepository_.serialize<FulfillmentTypes.FulfillmentDTO>(
fulfillment
)
@@ -754,11 +710,6 @@ export default class FulfillmentModuleService
throw error
}
buildCreatedFulfillmentEvents({
fulfillments: [fulfillment],
sharedContext,
})
return await this.baseRepository_.serialize<FulfillmentTypes.FulfillmentDTO>(
fulfillment
)
@@ -943,15 +894,6 @@ export default class FulfillmentModuleService
})
if (serviceZoneIdsToDelete.length) {
eventBuilders.deletedServiceZone({
data: serviceZoneIdsToDelete.map((id) => ({ id })),
sharedContext,
})
eventBuilders.deletedGeoZone({
data: geoZoneIdsToDelete.map((id) => ({ id })),
sharedContext,
})
await promiseAll([
this.geoZoneService_.delete(
{
@@ -973,32 +915,6 @@ export default class FulfillmentModuleService
sharedContext
)
eventBuilders.updatedFulfillmentSet({
data: updatedFulfillmentSets,
sharedContext,
})
const createdServiceZoneIds: string[] = []
const createdGeoZoneIds = updatedFulfillmentSets
.flatMap((f) =>
[...f.service_zones].flatMap((serviceZone) => {
if (!existingServiceZoneIds.includes(serviceZone.id)) {
createdServiceZoneIds.push(serviceZone.id)
}
return serviceZone.geo_zones.map((g) => g.id)
})
)
.filter((id) => !existingGeoZoneIds.includes(id))
eventBuilders.createdServiceZone({
data: createdServiceZoneIds.map((id) => ({ id })),
sharedContext,
})
eventBuilders.createdGeoZone({
data: createdGeoZoneIds.map((id) => ({ id })),
sharedContext,
})
return Array.isArray(data)
? updatedFulfillmentSets
: updatedFulfillmentSets[0]
@@ -1187,11 +1103,6 @@ export default class FulfillmentModuleService
})
if (geoZoneIdsToDelete.length) {
eventBuilders.deletedGeoZone({
data: geoZoneIdsToDelete.map((id) => ({ id })),
sharedContext,
})
await this.geoZoneService_.delete(
{
id: geoZoneIdsToDelete,
@@ -1205,26 +1116,6 @@ export default class FulfillmentModuleService
sharedContext
)
eventBuilders.updatedServiceZone({
data: updatedServiceZones,
sharedContext,
})
const createdGeoZoneIds = updatedServiceZones
.flatMap((serviceZone) => {
return serviceZone.geo_zones.map((g) => g.id)
})
.filter((id) => !existingGeoZoneIds.includes(id))
eventBuilders.createdGeoZone({
data: createdGeoZoneIds.map((id) => ({ id })),
sharedContext,
})
eventBuilders.updatedGeoZone({
data: updatedGeoZoneIds.map((id) => ({ id })),
sharedContext,
})
return Array.isArray(data) ? updatedServiceZones : updatedServiceZones[0]
}
@@ -1485,11 +1376,6 @@ export default class FulfillmentModuleService
})
if (ruleIdsToDelete.length) {
eventBuilders.deletedShippingOptionRule({
data: ruleIdsToDelete.map((id) => ({ id })),
sharedContext,
})
await this.shippingOptionRuleService_.delete(
ruleIdsToDelete,
sharedContext
@@ -1501,71 +1387,11 @@ export default class FulfillmentModuleService
sharedContext
)
this.handleShippingOptionUpdateEvents({
shippingOptionsData: dataArray,
updatedShippingOptions,
optionTypeDeletedIds,
updatedRuleIds,
existingRuleIds,
sharedContext,
})
return Array.isArray(data)
? updatedShippingOptions
: updatedShippingOptions[0]
}
private handleShippingOptionUpdateEvents({
shippingOptionsData,
updatedShippingOptions,
optionTypeDeletedIds,
updatedRuleIds,
existingRuleIds,
sharedContext,
}) {
eventBuilders.updatedShippingOption({
data: updatedShippingOptions,
sharedContext,
})
eventBuilders.deletedShippingOptionType({
data: optionTypeDeletedIds.map((id) => ({ id })),
sharedContext,
})
const createdOptionTypeIds = updatedShippingOptions
.filter((so) => {
const updateData = shippingOptionsData.find((sod) => sod.id === so.id)
return isObject(updateData?.type) && !("id" in updateData.type)
})
.map((so) => so.type.id)
eventBuilders.createdShippingOptionType({
data: createdOptionTypeIds.map((id) => ({ id })),
sharedContext,
})
const createdRuleIds = updatedShippingOptions
.flatMap((so) =>
[...so.rules].map((rule) => {
if (existingRuleIds.includes(rule.id)) {
return
}
return rule.id
})
)
.filter((id): id is string => !!id)
eventBuilders.createdShippingOptionRule({
data: createdRuleIds.map((id) => ({ id })),
sharedContext,
})
eventBuilders.updatedShippingOptionRule({
data: updatedRuleIds.map((id) => ({ id })),
sharedContext,
})
}
async upsertShippingOptions(
data: FulfillmentTypes.UpsertShippingOptionDTO[],
sharedContext?: Context
@@ -1652,7 +1478,8 @@ export default class FulfillmentModuleService
sharedContext?: Context
): Promise<FulfillmentTypes.ShippingOptionTypeDTO>
@InjectTransactionManager()
@InjectManager()
@EmitEvents()
async upsertShippingOptionTypes(
data:
| FulfillmentTypes.UpsertShippingOptionTypeDTO[]
@@ -1662,12 +1489,7 @@ export default class FulfillmentModuleService
| FulfillmentTypes.ShippingOptionTypeDTO[]
| FulfillmentTypes.ShippingOptionTypeDTO
> {
const input = Array.isArray(data) ? data : [data]
const results = await this.shippingOptionTypeService_.upsert(
input,
sharedContext
)
const results = await this.updateShippingOptionTypes_(data, sharedContext)
const allTypes = await this.baseRepository_.serialize<
| FulfillmentTypes.ShippingOptionTypeDTO[]
@@ -1677,6 +1499,23 @@ export default class FulfillmentModuleService
return Array.isArray(data) ? allTypes : allTypes[0]
}
@InjectTransactionManager()
protected async updateShippingOptionTypes_(
data:
| FulfillmentTypes.UpsertShippingOptionTypeDTO[]
| FulfillmentTypes.UpsertShippingOptionTypeDTO,
sharedContext: Context
): Promise<InferEntityType<typeof ShippingOptionType>[]> {
const input = Array.isArray(data) ? data : [data]
const results = await this.shippingOptionTypeService_.upsert(
input,
sharedContext
)
return results
}
// @ts-expect-error
updateShippingOptionTypes(
id: string,
@@ -1691,6 +1530,7 @@ export default class FulfillmentModuleService
): Promise<FulfillmentTypes.ShippingOptionTypeDTO[]>
@InjectManager()
@EmitEvents()
// @ts-expect-error
async updateShippingOptionTypes(
idOrSelector: string | FulfillmentTypes.FilterableShippingOptionTypeProps,
@@ -1747,7 +1587,8 @@ export default class FulfillmentModuleService
sharedContext?: Context
): Promise<FulfillmentTypes.ShippingProfileDTO>
@InjectTransactionManager()
@InjectManager()
@EmitEvents()
// @ts-expect-error
async updateShippingProfiles(
idOrSelector: string | FulfillmentTypes.FilterableShippingProfileProps,
@@ -1756,6 +1597,25 @@ export default class FulfillmentModuleService
): Promise<
FulfillmentTypes.ShippingProfileDTO | FulfillmentTypes.ShippingProfileDTO[]
> {
const profiles = await this.updateShippingProfiles_(
idOrSelector,
data,
sharedContext
)
const updatedProfiles = await this.baseRepository_.serialize<
FulfillmentTypes.ShippingProfileDTO[]
>(profiles)
return isString(idOrSelector) ? updatedProfiles[0] : updatedProfiles
}
@InjectTransactionManager()
protected async updateShippingProfiles_(
idOrSelector: string | FulfillmentTypes.FilterableShippingProfileProps,
data: FulfillmentTypes.UpdateShippingProfileDTO,
@MedusaContext() sharedContext: Context = {}
): Promise<InferEntityType<typeof ShippingProfile>[]> {
let normalizedInput: ({
id: string
} & FulfillmentTypes.UpdateShippingProfileDTO)[] = []
@@ -1784,11 +1644,7 @@ export default class FulfillmentModuleService
sharedContext
)
const updatedProfiles = await this.baseRepository_.serialize<
FulfillmentTypes.ShippingProfileDTO[]
>(profiles)
return isString(idOrSelector) ? updatedProfiles[0] : updatedProfiles
return profiles
}
async upsertShippingProfiles(
@@ -1800,7 +1656,8 @@ export default class FulfillmentModuleService
sharedContext?: Context
): Promise<FulfillmentTypes.ShippingProfileDTO>
@InjectTransactionManager()
@InjectManager()
@EmitEvents()
async upsertShippingProfiles(
data:
| FulfillmentTypes.UpsertShippingProfileDTO[]
@@ -1808,6 +1665,24 @@ export default class FulfillmentModuleService
@MedusaContext() sharedContext: Context = {}
): Promise<
FulfillmentTypes.ShippingProfileDTO[] | FulfillmentTypes.ShippingProfileDTO
> {
const profiles = await this.upsertShippingProfiles_(data, sharedContext)
return await this.baseRepository_.serialize<
| FulfillmentTypes.ShippingProfileDTO[]
| FulfillmentTypes.ShippingProfileDTO
>(Array.isArray(data) ? profiles : profiles[0])
}
@InjectTransactionManager()
protected async upsertShippingProfiles_(
data:
| FulfillmentTypes.UpsertShippingProfileDTO[]
| FulfillmentTypes.UpsertShippingProfileDTO,
@MedusaContext() sharedContext: Context = {}
): Promise<
| InferEntityType<typeof ShippingProfile>[]
| InferEntityType<typeof ShippingProfile>
> {
const input = Array.isArray(data) ? data : [data]
const forUpdate = input.filter((prof) => !!prof.id)
@@ -1831,13 +1706,7 @@ export default class FulfillmentModuleService
)
}
const result = [...created, ...updated]
const allProfiles = await this.baseRepository_.serialize<
| FulfillmentTypes.ShippingProfileDTO[]
| FulfillmentTypes.ShippingProfileDTO
>(result)
return Array.isArray(data) ? allProfiles : allProfiles[0]
return [...created, ...updated]
}
// @ts-expect-error
@@ -1873,11 +1742,6 @@ export default class FulfillmentModuleService
sharedContext
)
eventBuilders.updatedGeoZone({
data: updatedGeoZones,
sharedContext,
})
const serialized = await this.baseRepository_.serialize<
FulfillmentTypes.GeoZoneDTO[]
>(updatedGeoZones)
@@ -1940,11 +1804,6 @@ export default class FulfillmentModuleService
const updatedShippingOptionRules =
await this.shippingOptionRuleService_.update(data_, sharedContext)
eventBuilders.updatedShippingOptionRule({
data: updatedShippingOptionRules.map((rule) => ({ id: rule.id })),
sharedContext,
})
return Array.isArray(data)
? updatedShippingOptionRules
: updatedShippingOptionRules[0]
@@ -1980,9 +1839,6 @@ export default class FulfillmentModuleService
)
const updatedLabelIds: string[] = []
let deletedLabelIds: string[] = []
const existingLabelIds = existingFulfillment.labels.map((label) => label.id)
/**
* @note
@@ -1995,12 +1851,6 @@ export default class FulfillmentModuleService
* and we also need to emit the events later on.
*/
if (isDefined(data.labels) && isPresent(data.labels)) {
const dataLabelIds: string[] = data.labels
.filter((label): label is { id: string } => "id" in label)
.map((label) => label.id)
deletedLabelIds = arrayDifference(existingLabelIds, dataLabelIds)
for (let label of data.labels) {
if (!("id" in label)) {
continue
@@ -2029,49 +1879,9 @@ export default class FulfillmentModuleService
sharedContext
)
this.handleFulfillmentUpdateEvents(
fulfillment,
existingLabelIds,
updatedLabelIds,
deletedLabelIds,
sharedContext
)
return fulfillment
}
private handleFulfillmentUpdateEvents(
fulfillment: InferEntityType<typeof Fulfillment>,
existingLabelIds: string[],
updatedLabelIds: string[],
deletedLabelIds: string[],
sharedContext: Context
) {
eventBuilders.updatedFulfillment({
data: [{ id: fulfillment.id }],
sharedContext,
})
eventBuilders.deletedFulfillmentLabel({
data: deletedLabelIds.map((id) => ({ id })),
sharedContext,
})
eventBuilders.updatedFulfillmentLabel({
data: updatedLabelIds.map((id) => ({ id })),
sharedContext,
})
const createdLabels = fulfillment.labels.filter((label) => {
return !existingLabelIds.includes(label.id)
})
eventBuilders.createdFulfillmentLabel({
data: createdLabels.map((label) => ({ id: label.id })),
sharedContext,
})
}
@InjectManager()
@EmitEvents()
async cancelFulfillment(
@@ -2106,11 +1916,6 @@ export default class FulfillmentModuleService
},
sharedContext
)
eventBuilders.updatedFulfillment({
data: [{ id }],
sharedContext,
})
}
const result = await this.baseRepository_.serialize<FulfillmentDTO>(
@@ -2211,6 +2016,7 @@ export default class FulfillmentModuleService
}
@InjectTransactionManager()
@EmitEvents()
// @ts-expect-error
async deleteShippingProfiles(
ids: string | string[],
@@ -2226,6 +2032,7 @@ export default class FulfillmentModuleService
}
@InjectTransactionManager()
@EmitEvents()
// @ts-expect-error
async softDeleteShippingProfiles<
TReturnableLinkableKeys extends string = string