chore: return meta information on created/updated/deleted object from upsertWithReplace (#7616)
**What** - Return the updated/deleted/created entities from the upsert with replace according to the configuration This will help to emit the events from the product module as it rely on this abstraction in many places
This commit is contained in:
committed by
GitHub
parent
8387c72e96
commit
da3837f2bc
@@ -8,6 +8,15 @@ import {
|
||||
UpsertWithReplaceConfig,
|
||||
} from "./index"
|
||||
|
||||
type EntityClassName = string
|
||||
type EntityValues = { id: string }[]
|
||||
|
||||
export type PerformedActions = {
|
||||
created: Record<EntityClassName, EntityValues>
|
||||
updated: Record<EntityClassName, EntityValues>
|
||||
deleted: Record<EntityClassName, EntityValues>
|
||||
}
|
||||
|
||||
/**
|
||||
* Data access layer (DAL) interface to implements for any repository service.
|
||||
* This layer helps to separate the business logic (service layer) from accessing the
|
||||
@@ -78,7 +87,7 @@ export interface RepositoryService<T = any> extends BaseRepositoryService<T> {
|
||||
data: any[],
|
||||
config?: UpsertWithReplaceConfig<T>,
|
||||
context?: Context
|
||||
): Promise<T[]>
|
||||
): Promise<{ entities: T[]; performedActions: PerformedActions }>
|
||||
}
|
||||
|
||||
export interface TreeRepositoryService<T = any>
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
BaseFilterable,
|
||||
FilterQuery as InternalFilterQuery,
|
||||
FilterQuery,
|
||||
PerformedActions,
|
||||
UpsertWithReplaceConfig,
|
||||
} from "../dal"
|
||||
|
||||
@@ -88,5 +89,5 @@ export interface InternalModuleService<
|
||||
data: any[],
|
||||
config?: UpsertWithReplaceConfig<TEntity>,
|
||||
sharedContext?: Context
|
||||
): Promise<TEntity[]>
|
||||
): Promise<{ entities: TEntity[]; performedActions: PerformedActions }>
|
||||
}
|
||||
|
||||
@@ -176,7 +176,17 @@ describe("mikroOrmRepository", () => {
|
||||
it("should successfully create a flat entity", async () => {
|
||||
const entity1 = { id: "1", title: "en1", amount: 100 }
|
||||
|
||||
const resp = await manager1().upsertWithReplace([entity1])
|
||||
const { entities: resp, performedActions } =
|
||||
await manager1().upsertWithReplace([entity1])
|
||||
|
||||
expect(performedActions).toEqual({
|
||||
created: {
|
||||
[Entity1.name]: [expect.objectContaining({ id: entity1.id })],
|
||||
},
|
||||
updated: {},
|
||||
deleted: {},
|
||||
})
|
||||
|
||||
const listedEntities = await manager1().find()
|
||||
|
||||
expect(listedEntities).toHaveLength(1)
|
||||
@@ -193,9 +203,29 @@ describe("mikroOrmRepository", () => {
|
||||
it("should successfully update a flat entity", async () => {
|
||||
const entity1 = { id: "1", title: "en1" }
|
||||
|
||||
await manager1().upsertWithReplace([entity1])
|
||||
const { performedActions: performedActions1 } =
|
||||
await manager1().upsertWithReplace([entity1])
|
||||
|
||||
expect(performedActions1).toEqual({
|
||||
created: {
|
||||
[Entity1.name]: [expect.objectContaining({ id: entity1.id })],
|
||||
},
|
||||
updated: {},
|
||||
deleted: {},
|
||||
})
|
||||
|
||||
entity1.title = "newen1"
|
||||
await manager1().upsertWithReplace([entity1])
|
||||
const { performedActions: performedActions2 } =
|
||||
await manager1().upsertWithReplace([entity1])
|
||||
|
||||
expect(performedActions2).toEqual({
|
||||
created: {},
|
||||
updated: {
|
||||
[Entity1.name]: [expect.objectContaining({ id: entity1.id })],
|
||||
},
|
||||
deleted: {},
|
||||
})
|
||||
|
||||
const listedEntities = await manager1().find()
|
||||
|
||||
expect(listedEntities).toHaveLength(1)
|
||||
@@ -210,9 +240,30 @@ describe("mikroOrmRepository", () => {
|
||||
it("should successfully do a partial update a flat entity", async () => {
|
||||
const entity1 = { id: "1", title: "en1" }
|
||||
|
||||
await manager1().upsertWithReplace([entity1])
|
||||
const { performedActions: performedActions1 } =
|
||||
await manager1().upsertWithReplace([entity1])
|
||||
|
||||
expect(performedActions1).toEqual({
|
||||
created: {
|
||||
[Entity1.name]: [expect.objectContaining({ id: entity1.id })],
|
||||
},
|
||||
updated: {},
|
||||
deleted: {},
|
||||
})
|
||||
|
||||
entity1.title = undefined as any
|
||||
await manager1().upsertWithReplace([entity1])
|
||||
|
||||
const { performedActions: performedActions2 } =
|
||||
await manager1().upsertWithReplace([entity1])
|
||||
|
||||
expect(performedActions2).toEqual({
|
||||
created: {},
|
||||
updated: {
|
||||
[Entity1.name]: [expect.objectContaining({ id: entity1.id })],
|
||||
},
|
||||
deleted: {},
|
||||
})
|
||||
|
||||
const listedEntities = await manager1().find()
|
||||
|
||||
expect(listedEntities).toHaveLength(1)
|
||||
@@ -240,6 +291,7 @@ describe("mikroOrmRepository", () => {
|
||||
)
|
||||
})
|
||||
|
||||
// TODO: I believe this should not be allowed
|
||||
it("should successfully create the parent entity of a many-to-one", async () => {
|
||||
const entity2 = {
|
||||
id: "2",
|
||||
@@ -343,9 +395,19 @@ describe("mikroOrmRepository", () => {
|
||||
entity2: [{ title: "en2-1" }, { title: "en2-2" }],
|
||||
}
|
||||
|
||||
await manager1().upsertWithReplace([entity1], {
|
||||
relations: [],
|
||||
const { performedActions: performedActions1 } =
|
||||
await manager1().upsertWithReplace([entity1], {
|
||||
relations: [],
|
||||
})
|
||||
|
||||
expect(performedActions1).toEqual({
|
||||
created: {
|
||||
[Entity1.name]: [expect.objectContaining({ id: entity1.id })],
|
||||
},
|
||||
updated: {},
|
||||
deleted: {},
|
||||
})
|
||||
|
||||
const listedEntities = await manager1().find({
|
||||
where: { id: "1" },
|
||||
options: { populate: ["entity2"] },
|
||||
@@ -368,14 +430,29 @@ describe("mikroOrmRepository", () => {
|
||||
entity2: [{ title: "en2-1" }, { title: "en2-2" }],
|
||||
}
|
||||
|
||||
await manager1().upsertWithReplace([entity1], {
|
||||
relations: ["entity2"],
|
||||
})
|
||||
const { performedActions: performedActions1 } =
|
||||
await manager1().upsertWithReplace([entity1], {
|
||||
relations: ["entity2"],
|
||||
})
|
||||
|
||||
const listedEntities = await manager1().find({
|
||||
where: { id: "1" },
|
||||
options: { populate: ["entity2"] },
|
||||
})
|
||||
|
||||
const entities2 = listedEntities.flatMap((entity1) =>
|
||||
entity1.entity2.getItems()
|
||||
)
|
||||
|
||||
expect(performedActions1).toEqual({
|
||||
created: {
|
||||
[Entity1.name]: [expect.objectContaining({ id: entity1.id })],
|
||||
[Entity2.name]: entities2.map((entity2) => ({ id: entity2.id })),
|
||||
},
|
||||
updated: {},
|
||||
deleted: {},
|
||||
})
|
||||
|
||||
expect(listedEntities).toHaveLength(1)
|
||||
expect(listedEntities[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
@@ -403,14 +480,26 @@ describe("mikroOrmRepository", () => {
|
||||
entity2: [{ title: "en2-1", entity1: null }],
|
||||
}
|
||||
|
||||
await manager1().upsertWithReplace([entity1], {
|
||||
relations: ["entity2"],
|
||||
})
|
||||
const { performedActions: performedActions1 } =
|
||||
await manager1().upsertWithReplace([entity1], {
|
||||
relations: ["entity2"],
|
||||
})
|
||||
const listedEntities = await manager1().find({
|
||||
where: { id: "1" },
|
||||
options: { populate: ["entity2"] },
|
||||
})
|
||||
|
||||
expect(performedActions1).toEqual({
|
||||
created: {
|
||||
[Entity1.name]: [expect.objectContaining({ id: entity1.id })],
|
||||
[Entity2.name]: [
|
||||
expect.objectContaining({ id: listedEntities[0].entity2[0].id }),
|
||||
],
|
||||
},
|
||||
updated: {},
|
||||
deleted: {},
|
||||
})
|
||||
|
||||
expect(listedEntities).toHaveLength(1)
|
||||
expect(listedEntities[0]).toEqual(
|
||||
expect.objectContaining({
|
||||
@@ -435,12 +524,35 @@ describe("mikroOrmRepository", () => {
|
||||
entity2: [{ title: "en2-1" }, { title: "en2-2" }],
|
||||
}
|
||||
|
||||
await manager1().upsertWithReplace([entity1], {
|
||||
relations: ["entity2"],
|
||||
const { entities: entities1, performedActions: performedActions1 } =
|
||||
await manager1().upsertWithReplace([entity1], {
|
||||
relations: ["entity2"],
|
||||
})
|
||||
|
||||
expect(performedActions1).toEqual({
|
||||
created: {
|
||||
[Entity1.name]: [expect.objectContaining({ id: entity1.id })],
|
||||
[Entity2.name]: entities1[0].entity2.map((entity2) =>
|
||||
expect.objectContaining({ id: entity2.id })
|
||||
),
|
||||
},
|
||||
updated: {},
|
||||
deleted: {},
|
||||
})
|
||||
|
||||
entity1.entity2.push({ title: "en2-3" })
|
||||
await manager1().upsertWithReplace([entity1], {
|
||||
relations: [],
|
||||
|
||||
const { performedActions: performedActions2 } =
|
||||
await manager1().upsertWithReplace([entity1], {
|
||||
relations: [],
|
||||
})
|
||||
|
||||
expect(performedActions2).toEqual({
|
||||
created: {},
|
||||
updated: {
|
||||
[Entity1.name]: [expect.objectContaining({ id: entity1.id })],
|
||||
},
|
||||
deleted: {},
|
||||
})
|
||||
|
||||
const listedEntities = await manager1().find({
|
||||
@@ -478,14 +590,42 @@ describe("mikroOrmRepository", () => {
|
||||
] as any[],
|
||||
}
|
||||
|
||||
await manager1().upsertWithReplace([entity1], {
|
||||
relations: ["entity2"],
|
||||
const { entities: entities1, performedActions: performedActions1 } =
|
||||
await manager1().upsertWithReplace([entity1], {
|
||||
relations: ["entity2"],
|
||||
})
|
||||
|
||||
expect(performedActions1).toEqual({
|
||||
created: {
|
||||
[Entity1.name]: [expect.objectContaining({ id: entity1.id })],
|
||||
[Entity2.name]: entities1[0].entity2.map((entity2) =>
|
||||
expect.objectContaining({ id: entity2.id })
|
||||
),
|
||||
},
|
||||
updated: {},
|
||||
deleted: {},
|
||||
})
|
||||
|
||||
entity1.entity2 = [{ id: "2", title: "newen2-1" }, { title: "en2-3" }]
|
||||
|
||||
await manager1().upsertWithReplace([entity1], {
|
||||
relations: ["entity2"],
|
||||
const { entities: entities2, performedActions: performedActions2 } =
|
||||
await manager1().upsertWithReplace([entity1], {
|
||||
relations: ["entity2"],
|
||||
})
|
||||
|
||||
const entity2En23 = entities2[0].entity2.find((e) => e.title === "en2-3")!
|
||||
|
||||
expect(performedActions2).toEqual({
|
||||
created: {
|
||||
[Entity2.name]: [expect.objectContaining({ id: entity2En23.id })],
|
||||
},
|
||||
updated: {
|
||||
[Entity1.name]: [expect.objectContaining({ id: entity1.id })],
|
||||
[Entity2.name]: [expect.objectContaining({ id: "2" })],
|
||||
},
|
||||
deleted: {
|
||||
[Entity2.name]: [expect.objectContaining({ id: "3" })],
|
||||
},
|
||||
})
|
||||
|
||||
const listedEntities = await manager1().find({
|
||||
@@ -640,7 +780,7 @@ describe("mikroOrmRepository", () => {
|
||||
] as any,
|
||||
}
|
||||
|
||||
let resp = await manager1().upsertWithReplace([entity1], {
|
||||
await manager1().upsertWithReplace([entity1], {
|
||||
relations: ["entity3"],
|
||||
})
|
||||
|
||||
@@ -648,7 +788,7 @@ describe("mikroOrmRepository", () => {
|
||||
entity1.entity3 = [{ id: "4", title: "newen3-1" }, { title: "en3-4" }]
|
||||
|
||||
// We don't do many-to-many updates, so id: 4 entity should remain unchanged
|
||||
resp = await manager1().upsertWithReplace([entity1], {
|
||||
await manager1().upsertWithReplace([entity1], {
|
||||
relations: ["entity3"],
|
||||
})
|
||||
|
||||
@@ -718,9 +858,12 @@ describe("mikroOrmRepository", () => {
|
||||
] as any,
|
||||
}
|
||||
|
||||
const mainEntity = await manager1().upsertWithReplace([entity1], {
|
||||
relations: ["entity3"],
|
||||
})
|
||||
const { entities: mainEntity } = await manager1().upsertWithReplace(
|
||||
[entity1],
|
||||
{
|
||||
relations: ["entity3"],
|
||||
}
|
||||
)
|
||||
|
||||
entity1.title = "newen1"
|
||||
entity1.entity3 = [{ id: "4", title: "newen3-1" }, { title: "en3-4" }]
|
||||
@@ -771,11 +914,31 @@ describe("mikroOrmRepository", () => {
|
||||
entity3: [{ title: "en3-1" }, { title: "en3-2" }] as any,
|
||||
}
|
||||
|
||||
const [createResp] = await manager1().upsertWithReplace([entity1], {
|
||||
const {
|
||||
entities: [createResp],
|
||||
performedActions: performedActions1,
|
||||
} = await manager1().upsertWithReplace([entity1], {
|
||||
relations: ["entity2", "entity3"],
|
||||
})
|
||||
|
||||
expect(performedActions1).toEqual({
|
||||
created: {
|
||||
[Entity1.name]: [expect.objectContaining({ id: createResp.id })],
|
||||
[Entity2.name]: [
|
||||
expect.objectContaining({ id: createResp.entity2[0].id }),
|
||||
],
|
||||
[Entity3.name]: createResp.entity3.map((entity3) =>
|
||||
expect.objectContaining({ id: entity3.id })
|
||||
),
|
||||
},
|
||||
updated: {},
|
||||
deleted: {},
|
||||
})
|
||||
|
||||
createResp.title = "newen1"
|
||||
const [updateResp] = await manager1().upsertWithReplace([createResp], {
|
||||
const {
|
||||
entities: [updateResp],
|
||||
} = await manager1().upsertWithReplace([createResp], {
|
||||
relations: ["entity2", "entity3"],
|
||||
})
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import {
|
||||
DAL,
|
||||
FilterQuery,
|
||||
FilterQuery as InternalFilterQuery,
|
||||
PerformedActions,
|
||||
RepositoryService,
|
||||
RepositoryTransformOptions,
|
||||
UpsertWithReplaceConfig,
|
||||
@@ -147,7 +148,7 @@ export class MikroOrmBaseRepository<T extends object = object>
|
||||
relations: [],
|
||||
},
|
||||
context: Context = {}
|
||||
): Promise<T[]> {
|
||||
): Promise<{ entities: T[]; performedActions: PerformedActions }> {
|
||||
throw new Error("Method not implemented.")
|
||||
}
|
||||
|
||||
@@ -457,10 +458,17 @@ export function mikroOrmBaseRepositoryFactory<T extends object = object>(
|
||||
relations: [],
|
||||
},
|
||||
context: Context = {}
|
||||
): Promise<T[]> {
|
||||
if (!data.length) {
|
||||
return []
|
||||
): Promise<{ entities: T[]; performedActions: PerformedActions }> {
|
||||
const performedActions: PerformedActions = {
|
||||
created: {},
|
||||
updated: {},
|
||||
deleted: {},
|
||||
}
|
||||
|
||||
if (!data.length) {
|
||||
return { entities: [], performedActions }
|
||||
}
|
||||
|
||||
// We want to convert a potential ORM model to a POJO
|
||||
const normalizedData: any[] = await this.serialize(data)
|
||||
|
||||
@@ -507,11 +515,12 @@ export function mikroOrmBaseRepositoryFactory<T extends object = object>(
|
||||
return mainEntity
|
||||
})
|
||||
|
||||
const upsertedTopLevelEntities = await this.upsertMany_(
|
||||
manager,
|
||||
entity.name,
|
||||
toUpsert
|
||||
)
|
||||
let {
|
||||
orderedEntities: upsertedTopLevelEntities,
|
||||
performedActions: performedActions_,
|
||||
} = await this.upsertMany_(manager, entity.name, toUpsert)
|
||||
|
||||
this.mergePerformedActions(performedActions, performedActions_)
|
||||
|
||||
await promiseAll(
|
||||
upsertedTopLevelEntities
|
||||
@@ -534,12 +543,15 @@ export function mikroOrmBaseRepositoryFactory<T extends object = object>(
|
||||
return
|
||||
}
|
||||
|
||||
reconstructedEntry[relationName] =
|
||||
const { entities, performedActions: performedActions_ } =
|
||||
await this.assignCollectionRelation_(
|
||||
manager,
|
||||
{ ...originalEntry, id: (entityEntry as any).id },
|
||||
relation
|
||||
)
|
||||
|
||||
this.mergePerformedActions(performedActions, performedActions_)
|
||||
reconstructedEntry[relationName] = entities
|
||||
return
|
||||
})
|
||||
})
|
||||
@@ -551,7 +563,21 @@ export function mikroOrmBaseRepositoryFactory<T extends object = object>(
|
||||
// manager.create(entity, r, { persist: false })
|
||||
// )
|
||||
|
||||
return reconstructedResponse
|
||||
return { entities: reconstructedResponse, performedActions }
|
||||
}
|
||||
|
||||
private mergePerformedActions(
|
||||
performedActions: PerformedActions,
|
||||
newPerformedActions: PerformedActions
|
||||
) {
|
||||
Object.entries(newPerformedActions).forEach(([action, entities]) => {
|
||||
Object.entries(entities as Record<string, any[]>).forEach(
|
||||
([entityName, entityData]) => {
|
||||
performedActions[action][entityName] ??= []
|
||||
performedActions[action][entityName].push(...entityData)
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
// FUTURE: We can make this performant by only aggregating the operations, but only executing them at the end.
|
||||
@@ -559,11 +585,18 @@ export function mikroOrmBaseRepositoryFactory<T extends object = object>(
|
||||
manager: SqlEntityManager,
|
||||
data: T,
|
||||
relation: EntityProperty
|
||||
) {
|
||||
): Promise<{ entities: any[]; performedActions: PerformedActions }> {
|
||||
const dataForRelation = data[relation.name]
|
||||
|
||||
const performedActions: PerformedActions = {
|
||||
created: {},
|
||||
updated: {},
|
||||
deleted: {},
|
||||
}
|
||||
|
||||
// If the field is not set, we ignore it. Null and empty arrays are a valid input and are handled below
|
||||
if (dataForRelation === undefined) {
|
||||
return undefined
|
||||
return { entities: [], performedActions }
|
||||
}
|
||||
|
||||
// Make sure the data is correctly initialized with IDs before using it
|
||||
@@ -580,10 +613,16 @@ export function mikroOrmBaseRepositoryFactory<T extends object = object>(
|
||||
[parentPivotColumn]: (data as any).id,
|
||||
})
|
||||
|
||||
return normalizedData
|
||||
return { entities: normalizedData, performedActions }
|
||||
}
|
||||
|
||||
await this.upsertMany_(manager, relation.type, normalizedData, true)
|
||||
const { performedActions: performedActions_ } = await this.upsertMany_(
|
||||
manager,
|
||||
relation.type,
|
||||
normalizedData,
|
||||
true
|
||||
)
|
||||
this.mergePerformedActions(performedActions, performedActions_)
|
||||
|
||||
const pivotData = normalizedData.map((currModel) => {
|
||||
return {
|
||||
@@ -602,7 +641,7 @@ export function mikroOrmBaseRepositoryFactory<T extends object = object>(
|
||||
},
|
||||
})
|
||||
|
||||
return normalizedData
|
||||
return { entities: normalizedData, performedActions }
|
||||
}
|
||||
|
||||
if (relation.reference === ReferenceType.ONE_TO_MANY) {
|
||||
@@ -622,18 +661,38 @@ export function mikroOrmBaseRepositoryFactory<T extends object = object>(
|
||||
})
|
||||
})
|
||||
|
||||
await this.upsertMany_(manager, relation.type, normalizedData)
|
||||
const { performedActions: performedActions_ } =
|
||||
await this.upsertMany_(manager, relation.type, normalizedData)
|
||||
this.mergePerformedActions(performedActions, performedActions_)
|
||||
}
|
||||
|
||||
const toDeleteEntities = await manager.find<any>(
|
||||
relation.type,
|
||||
{
|
||||
id: { $nin: normalizedData.map((d: any) => d.id) },
|
||||
},
|
||||
{
|
||||
fields: ["id"],
|
||||
}
|
||||
)
|
||||
const toDeleteIds = toDeleteEntities.map((d: any) => d.id)
|
||||
|
||||
await manager.nativeDelete(relation.type, {
|
||||
...joinColumnsConstraints,
|
||||
id: { $nin: normalizedData.map((d: any) => d.id) },
|
||||
id: { $in: toDeleteIds },
|
||||
})
|
||||
|
||||
return normalizedData
|
||||
if (toDeleteEntities.length) {
|
||||
performedActions.deleted[relation.type] ??= []
|
||||
performedActions.deleted[relation.type].push(
|
||||
...toDeleteEntities.map((d) => ({ id: d.id }))
|
||||
)
|
||||
}
|
||||
|
||||
return { entities: normalizedData, performedActions }
|
||||
}
|
||||
|
||||
return normalizedData
|
||||
return { entities: normalizedData, performedActions }
|
||||
}
|
||||
|
||||
protected handleRelationAssignment_(
|
||||
@@ -714,7 +773,7 @@ export function mikroOrmBaseRepositoryFactory<T extends object = object>(
|
||||
entityName: string,
|
||||
entries: any[],
|
||||
skipUpdate: boolean = false
|
||||
) {
|
||||
): Promise<{ orderedEntities: any[]; performedActions: PerformedActions }> {
|
||||
const selectQb = manager.qb(entityName)
|
||||
const existingEntities: any[] = await selectQb.select("*").where({
|
||||
id: { $in: entries.map((d) => d.id) },
|
||||
@@ -726,6 +785,12 @@ export function mikroOrmBaseRepositoryFactory<T extends object = object>(
|
||||
|
||||
const orderedEntities: T[] = []
|
||||
|
||||
const performedActions = {
|
||||
created: {},
|
||||
updated: {},
|
||||
deleted: {},
|
||||
}
|
||||
|
||||
await promiseAll(
|
||||
entries.map(async (data) => {
|
||||
const existingEntity = existingEntitiesMap.get(data.id)
|
||||
@@ -735,19 +800,31 @@ export function mikroOrmBaseRepositoryFactory<T extends object = object>(
|
||||
return
|
||||
}
|
||||
await manager.nativeUpdate(entityName, { id: data.id }, data)
|
||||
performedActions.updated[entityName] ??= []
|
||||
performedActions.updated[entityName].push({ id: data.id })
|
||||
} else {
|
||||
const qb = manager.qb(entityName)
|
||||
if (skipUpdate) {
|
||||
await qb.insert(data).onConflict().ignore().execute()
|
||||
const res = await qb
|
||||
.insert(data)
|
||||
.onConflict()
|
||||
.ignore()
|
||||
.execute("all", true)
|
||||
if (res) {
|
||||
performedActions.created[entityName] ??= []
|
||||
performedActions.created[entityName].push({ id: data.id })
|
||||
}
|
||||
} else {
|
||||
await manager.insert(entityName, data)
|
||||
performedActions.created[entityName] ??= []
|
||||
performedActions.created[entityName].push({ id: data.id })
|
||||
// await manager.insert(entityName, data)
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
|
||||
return orderedEntities
|
||||
return { orderedEntities, performedActions }
|
||||
}
|
||||
|
||||
async restore(
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
FilterQuery as InternalFilterQuery,
|
||||
FindConfig,
|
||||
ModulesSdkTypes,
|
||||
PerformedActions,
|
||||
UpsertWithReplaceConfig,
|
||||
} from "@medusajs/types"
|
||||
import { EntitySchema } from "@mikro-orm/core"
|
||||
@@ -493,12 +494,12 @@ export function internalModuleServiceFactory<
|
||||
data: any[],
|
||||
config?: UpsertWithReplaceConfig<TEntity>,
|
||||
sharedContext?: Context
|
||||
): Promise<TEntity[]>
|
||||
): Promise<{ entities: TEntity[]; performedActions: PerformedActions }>
|
||||
upsertWithReplace(
|
||||
data: any,
|
||||
config?: UpsertWithReplaceConfig<TEntity>,
|
||||
sharedContext?: Context
|
||||
): Promise<TEntity>
|
||||
): Promise<{ entities: TEntity; performedActions: PerformedActions }>
|
||||
|
||||
@InjectTransactionManager(propertyRepositoryName)
|
||||
async upsertWithReplace(
|
||||
@@ -507,14 +508,18 @@ export function internalModuleServiceFactory<
|
||||
relations: [],
|
||||
},
|
||||
@MedusaContext() sharedContext: Context = {}
|
||||
): Promise<TEntity | TEntity[]> {
|
||||
): Promise<{
|
||||
entities: TEntity | TEntity[]
|
||||
performedActions: PerformedActions
|
||||
}> {
|
||||
const data_ = Array.isArray(data) ? data : [data]
|
||||
const entities = await this[propertyRepositoryName].upsertWithReplace(
|
||||
data_,
|
||||
config,
|
||||
sharedContext
|
||||
)
|
||||
return Array.isArray(data) ? entities : entities[0]
|
||||
const { entities, performedActions } = await this[
|
||||
propertyRepositoryName
|
||||
].upsertWithReplace(data_, config, sharedContext)
|
||||
return {
|
||||
entities: Array.isArray(data) ? entities : entities[0],
|
||||
performedActions,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -498,13 +498,14 @@ export default class PricingModuleService<
|
||||
const normalizedData = await this.normalizeUpdateData(data, sharedContext)
|
||||
|
||||
const prices = normalizedData.flatMap((priceSet) => priceSet.prices || [])
|
||||
const upsertedPrices = await this.priceService_.upsertWithReplace(
|
||||
prices,
|
||||
{
|
||||
relations: ["price_rules"],
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
const { entities: upsertedPrices } =
|
||||
await this.priceService_.upsertWithReplace(
|
||||
prices,
|
||||
{
|
||||
relations: ["price_rules"],
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
|
||||
const priceSetsToUpsert = normalizedData.map((priceSet) => {
|
||||
const { prices, ...rest } = priceSet
|
||||
@@ -520,11 +521,14 @@ export default class PricingModuleService<
|
||||
}
|
||||
})
|
||||
|
||||
return await this.priceSetService_.upsertWithReplace(
|
||||
priceSetsToUpsert,
|
||||
{ relations: ["prices"] },
|
||||
sharedContext
|
||||
)
|
||||
const { entities: priceSets } =
|
||||
await this.priceSetService_.upsertWithReplace(
|
||||
priceSetsToUpsert,
|
||||
{ relations: ["prices"] },
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return priceSets
|
||||
}
|
||||
|
||||
async addRules(
|
||||
|
||||
@@ -24,7 +24,9 @@ import {
|
||||
arrayDifference,
|
||||
InjectManager,
|
||||
InjectTransactionManager,
|
||||
isPresent,
|
||||
isString,
|
||||
isValidHandle,
|
||||
kebabCase,
|
||||
MedusaContext,
|
||||
MedusaError,
|
||||
@@ -32,9 +34,7 @@ import {
|
||||
ProductStatus,
|
||||
promiseAll,
|
||||
removeUndefined,
|
||||
isValidHandle,
|
||||
toHandle,
|
||||
isPresent,
|
||||
} from "@medusajs/utils"
|
||||
import {
|
||||
ProductCategoryEventData,
|
||||
@@ -373,16 +373,19 @@ export default class ProductModuleService<
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return this.productVariantService_.upsertWithReplace(
|
||||
ProductModuleService.assignOptionsToVariants(
|
||||
variantsWithProductId,
|
||||
productOptions
|
||||
),
|
||||
{
|
||||
relations: ["options"],
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
const { entities: productVariants } =
|
||||
await this.productVariantService_.upsertWithReplace(
|
||||
ProductModuleService.assignOptionsToVariants(
|
||||
variantsWithProductId,
|
||||
productOptions
|
||||
),
|
||||
{
|
||||
relations: ["options"],
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return productVariants
|
||||
}
|
||||
|
||||
createTags(
|
||||
@@ -811,11 +814,14 @@ export default class ProductModuleService<
|
||||
} as UpdateProductOptionInput
|
||||
})
|
||||
|
||||
return await this.productOptionService_.upsertWithReplace(
|
||||
normalizedInput,
|
||||
{ relations: ["values"] },
|
||||
sharedContext
|
||||
)
|
||||
const { entities: productOptions } =
|
||||
await this.productOptionService_.upsertWithReplace(
|
||||
normalizedInput,
|
||||
{ relations: ["values"] },
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return productOptions
|
||||
}
|
||||
|
||||
createCollections(
|
||||
@@ -865,11 +871,14 @@ export default class ProductModuleService<
|
||||
|
||||
// It's safe to use upsertWithReplace here since we only have product IDs and the only operation to do is update the product
|
||||
// with the collection ID
|
||||
return await this.productCollectionService_.upsertWithReplace(
|
||||
normalizedInput,
|
||||
{ relations: ["products"] },
|
||||
sharedContext
|
||||
)
|
||||
const { entities: productCollections } =
|
||||
await this.productCollectionService_.upsertWithReplace(
|
||||
normalizedInput,
|
||||
{ relations: ["products"] },
|
||||
sharedContext
|
||||
)
|
||||
|
||||
return productCollections
|
||||
}
|
||||
|
||||
async upsertCollections(
|
||||
@@ -1279,13 +1288,14 @@ export default class ProductModuleService<
|
||||
})
|
||||
)
|
||||
|
||||
const productData = await this.productService_.upsertWithReplace(
|
||||
normalizedInput,
|
||||
{
|
||||
relations: ["images", "tags", "categories"],
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
const { entities: productData } =
|
||||
await this.productService_.upsertWithReplace(
|
||||
normalizedInput,
|
||||
{
|
||||
relations: ["images", "tags", "categories"],
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
|
||||
await promiseAll(
|
||||
// Note: It's safe to rely on the order here as `upsertWithReplace` preserves the order of the input
|
||||
@@ -1295,7 +1305,7 @@ export default class ProductModuleService<
|
||||
upsertedProduct.variants = []
|
||||
|
||||
if (product.options?.length) {
|
||||
upsertedProduct.options =
|
||||
const { entities: productOptions } =
|
||||
await this.productOptionService_.upsertWithReplace(
|
||||
product.options?.map((option) => ({
|
||||
...option,
|
||||
@@ -1304,10 +1314,11 @@ export default class ProductModuleService<
|
||||
{ relations: ["values"] },
|
||||
sharedContext
|
||||
)
|
||||
upsertedProduct.options = productOptions
|
||||
}
|
||||
|
||||
if (product.variants?.length) {
|
||||
upsertedProduct.variants =
|
||||
const { entities: productVariants } =
|
||||
await this.productVariantService_.upsertWithReplace(
|
||||
ProductModuleService.assignOptionsToVariants(
|
||||
product.variants?.map((v) => ({
|
||||
@@ -1319,6 +1330,7 @@ export default class ProductModuleService<
|
||||
{ relations: ["options"] },
|
||||
sharedContext
|
||||
)
|
||||
upsertedProduct.variants = productVariants
|
||||
}
|
||||
})
|
||||
)
|
||||
@@ -1342,13 +1354,14 @@ export default class ProductModuleService<
|
||||
})
|
||||
)
|
||||
|
||||
const productData = await this.productService_.upsertWithReplace(
|
||||
normalizedInput,
|
||||
{
|
||||
relations: ["images", "tags", "categories"],
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
const { entities: productData } =
|
||||
await this.productService_.upsertWithReplace(
|
||||
normalizedInput,
|
||||
{
|
||||
relations: ["images", "tags", "categories"],
|
||||
},
|
||||
sharedContext
|
||||
)
|
||||
|
||||
// There is more than 1-level depth of relations here, so we need to handle the options and variants manually
|
||||
await promiseAll(
|
||||
@@ -1358,7 +1371,7 @@ export default class ProductModuleService<
|
||||
let allOptions: any[] = []
|
||||
|
||||
if (product.options?.length) {
|
||||
upsertedProduct.options =
|
||||
const { entities: productOptions } =
|
||||
await this.productOptionService_.upsertWithReplace(
|
||||
product.options?.map((option) => ({
|
||||
...option,
|
||||
@@ -1367,6 +1380,7 @@ export default class ProductModuleService<
|
||||
{ relations: ["values"] },
|
||||
sharedContext
|
||||
)
|
||||
upsertedProduct.options = productOptions
|
||||
|
||||
// Since we handle the options and variants outside of the product upsert, we need to clean up manually
|
||||
await this.productOptionService_.delete(
|
||||
@@ -1391,7 +1405,7 @@ export default class ProductModuleService<
|
||||
}
|
||||
|
||||
if (product.variants?.length) {
|
||||
upsertedProduct.variants =
|
||||
const { entities: productVariants } =
|
||||
await this.productVariantService_.upsertWithReplace(
|
||||
ProductModuleService.assignOptionsToVariants(
|
||||
product.variants?.map((v) => ({
|
||||
@@ -1403,6 +1417,7 @@ export default class ProductModuleService<
|
||||
{ relations: ["options"] },
|
||||
sharedContext
|
||||
)
|
||||
upsertedProduct.variants = productVariants
|
||||
|
||||
await this.productVariantService_.delete(
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user