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:
1 parent
8387c72e96
commit
da3837f2bc
7 files changed
+327
-53
No files matched your search
@@ -8,6 +8,15 @@ import {
|
|||||||
UpsertWithReplaceConfig,
|
UpsertWithReplaceConfig,
|
||||||
} from "./index"
|
} 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.
|
* Data access layer (DAL) interface to implements for any repository service.
|
||||||
* This layer helps to separate the business logic (service layer) from accessing the
|
* 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[],
|
data: any[],
|
||||||
config?: UpsertWithReplaceConfig<T>,
|
config?: UpsertWithReplaceConfig<T>,
|
||||||
context?: Context
|
context?: Context
|
||||||
): Promise<T[]>
|
): Promise<{ entities: T[]; performedActions: PerformedActions }>
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TreeRepositoryService<T = any>
|
export interface TreeRepositoryService<T = any>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
BaseFilterable,
|
BaseFilterable,
|
||||||
FilterQuery as InternalFilterQuery,
|
FilterQuery as InternalFilterQuery,
|
||||||
FilterQuery,
|
FilterQuery,
|
||||||
|
PerformedActions,
|
||||||
UpsertWithReplaceConfig,
|
UpsertWithReplaceConfig,
|
||||||
} from "../dal"
|
} from "../dal"
|
||||||
|
|
||||||
@@ -88,5 +89,5 @@ export interface InternalModuleService<
|
|||||||
data: any[],
|
data: any[],
|
||||||
config?: UpsertWithReplaceConfig<TEntity>,
|
config?: UpsertWithReplaceConfig<TEntity>,
|
||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<TEntity[]>
|
): Promise<{ entities: TEntity[]; performedActions: PerformedActions }>
|
||||||
}
|
}
|
||||||
+170
-7
@@ -176,7 +176,17 @@ describe("mikroOrmRepository", () => {
|
|||||||
it("should successfully create a flat entity", async () => {
|
it("should successfully create a flat entity", async () => {
|
||||||
const entity1 = { id: "1", title: "en1", amount: 100 }
|
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()
|
const listedEntities = await manager1().find()
|
||||||
|
|
||||||
expect(listedEntities).toHaveLength(1)
|
expect(listedEntities).toHaveLength(1)
|
||||||
@@ -193,9 +203,29 @@ describe("mikroOrmRepository", () => {
|
|||||||
it("should successfully update a flat entity", async () => {
|
it("should successfully update a flat entity", async () => {
|
||||||
const entity1 = { id: "1", title: "en1" }
|
const entity1 = { id: "1", title: "en1" }
|
||||||
|
|
||||||
|
const { performedActions: performedActions1 } =
|
||||||
await manager1().upsertWithReplace([entity1])
|
await manager1().upsertWithReplace([entity1])
|
||||||
|
|
||||||
|
expect(performedActions1).toEqual({
|
||||||
|
created: {
|
||||||
|
[Entity1.name]: [expect.objectContaining({ id: entity1.id })],
|
||||||
|
},
|
||||||
|
updated: {},
|
||||||
|
deleted: {},
|
||||||
|
})
|
||||||
|
|
||||||
entity1.title = "newen1"
|
entity1.title = "newen1"
|
||||||
|
const { performedActions: performedActions2 } =
|
||||||
await manager1().upsertWithReplace([entity1])
|
await manager1().upsertWithReplace([entity1])
|
||||||
|
|
||||||
|
expect(performedActions2).toEqual({
|
||||||
|
created: {},
|
||||||
|
updated: {
|
||||||
|
[Entity1.name]: [expect.objectContaining({ id: entity1.id })],
|
||||||
|
},
|
||||||
|
deleted: {},
|
||||||
|
})
|
||||||
|
|
||||||
const listedEntities = await manager1().find()
|
const listedEntities = await manager1().find()
|
||||||
|
|
||||||
expect(listedEntities).toHaveLength(1)
|
expect(listedEntities).toHaveLength(1)
|
||||||
@@ -210,9 +240,30 @@ describe("mikroOrmRepository", () => {
|
|||||||
it("should successfully do a partial update a flat entity", async () => {
|
it("should successfully do a partial update a flat entity", async () => {
|
||||||
const entity1 = { id: "1", title: "en1" }
|
const entity1 = { id: "1", title: "en1" }
|
||||||
|
|
||||||
|
const { performedActions: performedActions1 } =
|
||||||
await manager1().upsertWithReplace([entity1])
|
await manager1().upsertWithReplace([entity1])
|
||||||
|
|
||||||
|
expect(performedActions1).toEqual({
|
||||||
|
created: {
|
||||||
|
[Entity1.name]: [expect.objectContaining({ id: entity1.id })],
|
||||||
|
},
|
||||||
|
updated: {},
|
||||||
|
deleted: {},
|
||||||
|
})
|
||||||
|
|
||||||
entity1.title = undefined as any
|
entity1.title = undefined as any
|
||||||
|
|
||||||
|
const { performedActions: performedActions2 } =
|
||||||
await manager1().upsertWithReplace([entity1])
|
await manager1().upsertWithReplace([entity1])
|
||||||
|
|
||||||
|
expect(performedActions2).toEqual({
|
||||||
|
created: {},
|
||||||
|
updated: {
|
||||||
|
[Entity1.name]: [expect.objectContaining({ id: entity1.id })],
|
||||||
|
},
|
||||||
|
deleted: {},
|
||||||
|
})
|
||||||
|
|
||||||
const listedEntities = await manager1().find()
|
const listedEntities = await manager1().find()
|
||||||
|
|
||||||
expect(listedEntities).toHaveLength(1)
|
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 () => {
|
it("should successfully create the parent entity of a many-to-one", async () => {
|
||||||
const entity2 = {
|
const entity2 = {
|
||||||
id: "2",
|
id: "2",
|
||||||
@@ -343,9 +395,19 @@ describe("mikroOrmRepository", () => {
|
|||||||
entity2: [{ title: "en2-1" }, { title: "en2-2" }],
|
entity2: [{ title: "en2-1" }, { title: "en2-2" }],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { performedActions: performedActions1 } =
|
||||||
await manager1().upsertWithReplace([entity1], {
|
await manager1().upsertWithReplace([entity1], {
|
||||||
relations: [],
|
relations: [],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
expect(performedActions1).toEqual({
|
||||||
|
created: {
|
||||||
|
[Entity1.name]: [expect.objectContaining({ id: entity1.id })],
|
||||||
|
},
|
||||||
|
updated: {},
|
||||||
|
deleted: {},
|
||||||
|
})
|
||||||
|
|
||||||
const listedEntities = await manager1().find({
|
const listedEntities = await manager1().find({
|
||||||
where: { id: "1" },
|
where: { id: "1" },
|
||||||
options: { populate: ["entity2"] },
|
options: { populate: ["entity2"] },
|
||||||
@@ -368,14 +430,29 @@ describe("mikroOrmRepository", () => {
|
|||||||
entity2: [{ title: "en2-1" }, { title: "en2-2" }],
|
entity2: [{ title: "en2-1" }, { title: "en2-2" }],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { performedActions: performedActions1 } =
|
||||||
await manager1().upsertWithReplace([entity1], {
|
await manager1().upsertWithReplace([entity1], {
|
||||||
relations: ["entity2"],
|
relations: ["entity2"],
|
||||||
})
|
})
|
||||||
|
|
||||||
const listedEntities = await manager1().find({
|
const listedEntities = await manager1().find({
|
||||||
where: { id: "1" },
|
where: { id: "1" },
|
||||||
options: { populate: ["entity2"] },
|
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).toHaveLength(1)
|
||||||
expect(listedEntities[0]).toEqual(
|
expect(listedEntities[0]).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@@ -403,6 +480,7 @@ describe("mikroOrmRepository", () => {
|
|||||||
entity2: [{ title: "en2-1", entity1: null }],
|
entity2: [{ title: "en2-1", entity1: null }],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { performedActions: performedActions1 } =
|
||||||
await manager1().upsertWithReplace([entity1], {
|
await manager1().upsertWithReplace([entity1], {
|
||||||
relations: ["entity2"],
|
relations: ["entity2"],
|
||||||
})
|
})
|
||||||
@@ -411,6 +489,17 @@ describe("mikroOrmRepository", () => {
|
|||||||
options: { populate: ["entity2"] },
|
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).toHaveLength(1)
|
||||||
expect(listedEntities[0]).toEqual(
|
expect(listedEntities[0]).toEqual(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
@@ -435,14 +524,37 @@ describe("mikroOrmRepository", () => {
|
|||||||
entity2: [{ title: "en2-1" }, { title: "en2-2" }],
|
entity2: [{ title: "en2-1" }, { title: "en2-2" }],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { entities: entities1, performedActions: performedActions1 } =
|
||||||
await manager1().upsertWithReplace([entity1], {
|
await manager1().upsertWithReplace([entity1], {
|
||||||
relations: ["entity2"],
|
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" })
|
entity1.entity2.push({ title: "en2-3" })
|
||||||
|
|
||||||
|
const { performedActions: performedActions2 } =
|
||||||
await manager1().upsertWithReplace([entity1], {
|
await manager1().upsertWithReplace([entity1], {
|
||||||
relations: [],
|
relations: [],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
expect(performedActions2).toEqual({
|
||||||
|
created: {},
|
||||||
|
updated: {
|
||||||
|
[Entity1.name]: [expect.objectContaining({ id: entity1.id })],
|
||||||
|
},
|
||||||
|
deleted: {},
|
||||||
|
})
|
||||||
|
|
||||||
const listedEntities = await manager1().find({
|
const listedEntities = await manager1().find({
|
||||||
where: { id: "1" },
|
where: { id: "1" },
|
||||||
options: { populate: ["entity2"] },
|
options: { populate: ["entity2"] },
|
||||||
@@ -478,16 +590,44 @@ describe("mikroOrmRepository", () => {
|
|||||||
] as any[],
|
] as any[],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { entities: entities1, performedActions: performedActions1 } =
|
||||||
await manager1().upsertWithReplace([entity1], {
|
await manager1().upsertWithReplace([entity1], {
|
||||||
relations: ["entity2"],
|
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" }]
|
entity1.entity2 = [{ id: "2", title: "newen2-1" }, { title: "en2-3" }]
|
||||||
|
|
||||||
|
const { entities: entities2, performedActions: performedActions2 } =
|
||||||
await manager1().upsertWithReplace([entity1], {
|
await manager1().upsertWithReplace([entity1], {
|
||||||
relations: ["entity2"],
|
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({
|
const listedEntities = await manager1().find({
|
||||||
where: { id: "1" },
|
where: { id: "1" },
|
||||||
options: { populate: ["entity2"] },
|
options: { populate: ["entity2"] },
|
||||||
@@ -640,7 +780,7 @@ describe("mikroOrmRepository", () => {
|
|||||||
] as any,
|
] as any,
|
||||||
}
|
}
|
||||||
|
|
||||||
let resp = await manager1().upsertWithReplace([entity1], {
|
await manager1().upsertWithReplace([entity1], {
|
||||||
relations: ["entity3"],
|
relations: ["entity3"],
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -648,7 +788,7 @@ describe("mikroOrmRepository", () => {
|
|||||||
entity1.entity3 = [{ id: "4", title: "newen3-1" }, { title: "en3-4" }]
|
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
|
// 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"],
|
relations: ["entity3"],
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -718,9 +858,12 @@ describe("mikroOrmRepository", () => {
|
|||||||
] as any,
|
] as any,
|
||||||
}
|
}
|
||||||
|
|
||||||
const mainEntity = await manager1().upsertWithReplace([entity1], {
|
const { entities: mainEntity } = await manager1().upsertWithReplace(
|
||||||
|
[entity1],
|
||||||
|
{
|
||||||
relations: ["entity3"],
|
relations: ["entity3"],
|
||||||
})
|
}
|
||||||
|
)
|
||||||
|
|
||||||
entity1.title = "newen1"
|
entity1.title = "newen1"
|
||||||
entity1.entity3 = [{ id: "4", title: "newen3-1" }, { title: "en3-4" }]
|
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,
|
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"],
|
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"
|
createResp.title = "newen1"
|
||||||
const [updateResp] = await manager1().upsertWithReplace([createResp], {
|
const {
|
||||||
|
entities: [updateResp],
|
||||||
|
} = await manager1().upsertWithReplace([createResp], {
|
||||||
relations: ["entity2", "entity3"],
|
relations: ["entity2", "entity3"],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
DAL,
|
DAL,
|
||||||
FilterQuery,
|
FilterQuery,
|
||||||
FilterQuery as InternalFilterQuery,
|
FilterQuery as InternalFilterQuery,
|
||||||
|
PerformedActions,
|
||||||
RepositoryService,
|
RepositoryService,
|
||||||
RepositoryTransformOptions,
|
RepositoryTransformOptions,
|
||||||
UpsertWithReplaceConfig,
|
UpsertWithReplaceConfig,
|
||||||
@@ -147,7 +148,7 @@ export class MikroOrmBaseRepository<T extends object = object>
|
|||||||
relations: [],
|
relations: [],
|
||||||
},
|
},
|
||||||
context: Context = {}
|
context: Context = {}
|
||||||
): Promise<T[]> {
|
): Promise<{ entities: T[]; performedActions: PerformedActions }> {
|
||||||
throw new Error("Method not implemented.")
|
throw new Error("Method not implemented.")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -457,10 +458,17 @@ export function mikroOrmBaseRepositoryFactory<T extends object = object>(
|
|||||||
relations: [],
|
relations: [],
|
||||||
},
|
},
|
||||||
context: Context = {}
|
context: Context = {}
|
||||||
): Promise<T[]> {
|
): Promise<{ entities: T[]; performedActions: PerformedActions }> {
|
||||||
if (!data.length) {
|
const performedActions: PerformedActions = {
|
||||||
return []
|
created: {},
|
||||||
|
updated: {},
|
||||||
|
deleted: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!data.length) {
|
||||||
|
return { entities: [], performedActions }
|
||||||
|
}
|
||||||
|
|
||||||
// We want to convert a potential ORM model to a POJO
|
// We want to convert a potential ORM model to a POJO
|
||||||
const normalizedData: any[] = await this.serialize(data)
|
const normalizedData: any[] = await this.serialize(data)
|
||||||
|
|
||||||
@@ -507,11 +515,12 @@ export function mikroOrmBaseRepositoryFactory<T extends object = object>(
|
|||||||
return mainEntity
|
return mainEntity
|
||||||
})
|
})
|
||||||
|
|
||||||
const upsertedTopLevelEntities = await this.upsertMany_(
|
let {
|
||||||
manager,
|
orderedEntities: upsertedTopLevelEntities,
|
||||||
entity.name,
|
performedActions: performedActions_,
|
||||||
toUpsert
|
} = await this.upsertMany_(manager, entity.name, toUpsert)
|
||||||
)
|
|
||||||
|
this.mergePerformedActions(performedActions, performedActions_)
|
||||||
|
|
||||||
await promiseAll(
|
await promiseAll(
|
||||||
upsertedTopLevelEntities
|
upsertedTopLevelEntities
|
||||||
@@ -534,12 +543,15 @@ export function mikroOrmBaseRepositoryFactory<T extends object = object>(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
reconstructedEntry[relationName] =
|
const { entities, performedActions: performedActions_ } =
|
||||||
await this.assignCollectionRelation_(
|
await this.assignCollectionRelation_(
|
||||||
manager,
|
manager,
|
||||||
{ ...originalEntry, id: (entityEntry as any).id },
|
{ ...originalEntry, id: (entityEntry as any).id },
|
||||||
relation
|
relation
|
||||||
)
|
)
|
||||||
|
|
||||||
|
this.mergePerformedActions(performedActions, performedActions_)
|
||||||
|
reconstructedEntry[relationName] = entities
|
||||||
return
|
return
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -551,7 +563,21 @@ export function mikroOrmBaseRepositoryFactory<T extends object = object>(
|
|||||||
// manager.create(entity, r, { persist: false })
|
// 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.
|
// 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,
|
manager: SqlEntityManager,
|
||||||
data: T,
|
data: T,
|
||||||
relation: EntityProperty
|
relation: EntityProperty
|
||||||
) {
|
): Promise<{ entities: any[]; performedActions: PerformedActions }> {
|
||||||
const dataForRelation = data[relation.name]
|
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 the field is not set, we ignore it. Null and empty arrays are a valid input and are handled below
|
||||||
if (dataForRelation === undefined) {
|
if (dataForRelation === undefined) {
|
||||||
return undefined
|
return { entities: [], performedActions }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the data is correctly initialized with IDs before using it
|
// 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,
|
[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) => {
|
const pivotData = normalizedData.map((currModel) => {
|
||||||
return {
|
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) {
|
if (relation.reference === ReferenceType.ONE_TO_MANY) {
|
||||||
@@ -622,18 +661,38 @@ export function mikroOrmBaseRepositoryFactory<T extends object = object>(
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const { performedActions: performedActions_ } =
|
||||||
await this.upsertMany_(manager, relation.type, normalizedData)
|
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, {
|
await manager.nativeDelete(relation.type, {
|
||||||
...joinColumnsConstraints,
|
...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 normalizedData
|
return { entities: normalizedData, performedActions }
|
||||||
|
}
|
||||||
|
|
||||||
|
return { entities: normalizedData, performedActions }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected handleRelationAssignment_(
|
protected handleRelationAssignment_(
|
||||||
@@ -714,7 +773,7 @@ export function mikroOrmBaseRepositoryFactory<T extends object = object>(
|
|||||||
entityName: string,
|
entityName: string,
|
||||||
entries: any[],
|
entries: any[],
|
||||||
skipUpdate: boolean = false
|
skipUpdate: boolean = false
|
||||||
) {
|
): Promise<{ orderedEntities: any[]; performedActions: PerformedActions }> {
|
||||||
const selectQb = manager.qb(entityName)
|
const selectQb = manager.qb(entityName)
|
||||||
const existingEntities: any[] = await selectQb.select("*").where({
|
const existingEntities: any[] = await selectQb.select("*").where({
|
||||||
id: { $in: entries.map((d) => d.id) },
|
id: { $in: entries.map((d) => d.id) },
|
||||||
@@ -726,6 +785,12 @@ export function mikroOrmBaseRepositoryFactory<T extends object = object>(
|
|||||||
|
|
||||||
const orderedEntities: T[] = []
|
const orderedEntities: T[] = []
|
||||||
|
|
||||||
|
const performedActions = {
|
||||||
|
created: {},
|
||||||
|
updated: {},
|
||||||
|
deleted: {},
|
||||||
|
}
|
||||||
|
|
||||||
await promiseAll(
|
await promiseAll(
|
||||||
entries.map(async (data) => {
|
entries.map(async (data) => {
|
||||||
const existingEntity = existingEntitiesMap.get(data.id)
|
const existingEntity = existingEntitiesMap.get(data.id)
|
||||||
@@ -735,19 +800,31 @@ export function mikroOrmBaseRepositoryFactory<T extends object = object>(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
await manager.nativeUpdate(entityName, { id: data.id }, data)
|
await manager.nativeUpdate(entityName, { id: data.id }, data)
|
||||||
|
performedActions.updated[entityName] ??= []
|
||||||
|
performedActions.updated[entityName].push({ id: data.id })
|
||||||
} else {
|
} else {
|
||||||
const qb = manager.qb(entityName)
|
const qb = manager.qb(entityName)
|
||||||
if (skipUpdate) {
|
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 {
|
} else {
|
||||||
await manager.insert(entityName, data)
|
await manager.insert(entityName, data)
|
||||||
|
performedActions.created[entityName] ??= []
|
||||||
|
performedActions.created[entityName].push({ id: data.id })
|
||||||
// await manager.insert(entityName, data)
|
// await manager.insert(entityName, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
return orderedEntities
|
return { orderedEntities, performedActions }
|
||||||
}
|
}
|
||||||
|
|
||||||
async restore(
|
async restore(
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import {
|
|||||||
FilterQuery as InternalFilterQuery,
|
FilterQuery as InternalFilterQuery,
|
||||||
FindConfig,
|
FindConfig,
|
||||||
ModulesSdkTypes,
|
ModulesSdkTypes,
|
||||||
|
PerformedActions,
|
||||||
UpsertWithReplaceConfig,
|
UpsertWithReplaceConfig,
|
||||||
} from "@medusajs/types"
|
} from "@medusajs/types"
|
||||||
import { EntitySchema } from "@mikro-orm/core"
|
import { EntitySchema } from "@mikro-orm/core"
|
||||||
@@ -493,12 +494,12 @@ export function internalModuleServiceFactory<
|
|||||||
data: any[],
|
data: any[],
|
||||||
config?: UpsertWithReplaceConfig<TEntity>,
|
config?: UpsertWithReplaceConfig<TEntity>,
|
||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<TEntity[]>
|
): Promise<{ entities: TEntity[]; performedActions: PerformedActions }>
|
||||||
upsertWithReplace(
|
upsertWithReplace(
|
||||||
data: any,
|
data: any,
|
||||||
config?: UpsertWithReplaceConfig<TEntity>,
|
config?: UpsertWithReplaceConfig<TEntity>,
|
||||||
sharedContext?: Context
|
sharedContext?: Context
|
||||||
): Promise<TEntity>
|
): Promise<{ entities: TEntity; performedActions: PerformedActions }>
|
||||||
|
|
||||||
@InjectTransactionManager(propertyRepositoryName)
|
@InjectTransactionManager(propertyRepositoryName)
|
||||||
async upsertWithReplace(
|
async upsertWithReplace(
|
||||||
@@ -507,14 +508,18 @@ export function internalModuleServiceFactory<
|
|||||||
relations: [],
|
relations: [],
|
||||||
},
|
},
|
||||||
@MedusaContext() sharedContext: Context = {}
|
@MedusaContext() sharedContext: Context = {}
|
||||||
): Promise<TEntity | TEntity[]> {
|
): Promise<{
|
||||||
|
entities: TEntity | TEntity[]
|
||||||
|
performedActions: PerformedActions
|
||||||
|
}> {
|
||||||
const data_ = Array.isArray(data) ? data : [data]
|
const data_ = Array.isArray(data) ? data : [data]
|
||||||
const entities = await this[propertyRepositoryName].upsertWithReplace(
|
const { entities, performedActions } = await this[
|
||||||
data_,
|
propertyRepositoryName
|
||||||
config,
|
].upsertWithReplace(data_, config, sharedContext)
|
||||||
sharedContext
|
return {
|
||||||
)
|
entities: Array.isArray(data) ? entities : entities[0],
|
||||||
return Array.isArray(data) ? entities : entities[0]
|
performedActions,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -498,7 +498,8 @@ export default class PricingModuleService<
|
|||||||
const normalizedData = await this.normalizeUpdateData(data, sharedContext)
|
const normalizedData = await this.normalizeUpdateData(data, sharedContext)
|
||||||
|
|
||||||
const prices = normalizedData.flatMap((priceSet) => priceSet.prices || [])
|
const prices = normalizedData.flatMap((priceSet) => priceSet.prices || [])
|
||||||
const upsertedPrices = await this.priceService_.upsertWithReplace(
|
const { entities: upsertedPrices } =
|
||||||
|
await this.priceService_.upsertWithReplace(
|
||||||
prices,
|
prices,
|
||||||
{
|
{
|
||||||
relations: ["price_rules"],
|
relations: ["price_rules"],
|
||||||
@@ -520,11 +521,14 @@ export default class PricingModuleService<
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return await this.priceSetService_.upsertWithReplace(
|
const { entities: priceSets } =
|
||||||
|
await this.priceSetService_.upsertWithReplace(
|
||||||
priceSetsToUpsert,
|
priceSetsToUpsert,
|
||||||
{ relations: ["prices"] },
|
{ relations: ["prices"] },
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return priceSets
|
||||||
}
|
}
|
||||||
|
|
||||||
async addRules(
|
async addRules(
|
||||||
|
|||||||
@@ -24,7 +24,9 @@ import {
|
|||||||
arrayDifference,
|
arrayDifference,
|
||||||
InjectManager,
|
InjectManager,
|
||||||
InjectTransactionManager,
|
InjectTransactionManager,
|
||||||
|
isPresent,
|
||||||
isString,
|
isString,
|
||||||
|
isValidHandle,
|
||||||
kebabCase,
|
kebabCase,
|
||||||
MedusaContext,
|
MedusaContext,
|
||||||
MedusaError,
|
MedusaError,
|
||||||
@@ -32,9 +34,7 @@ import {
|
|||||||
ProductStatus,
|
ProductStatus,
|
||||||
promiseAll,
|
promiseAll,
|
||||||
removeUndefined,
|
removeUndefined,
|
||||||
isValidHandle,
|
|
||||||
toHandle,
|
toHandle,
|
||||||
isPresent,
|
|
||||||
} from "@medusajs/utils"
|
} from "@medusajs/utils"
|
||||||
import {
|
import {
|
||||||
ProductCategoryEventData,
|
ProductCategoryEventData,
|
||||||
@@ -373,7 +373,8 @@ export default class ProductModuleService<
|
|||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
return this.productVariantService_.upsertWithReplace(
|
const { entities: productVariants } =
|
||||||
|
await this.productVariantService_.upsertWithReplace(
|
||||||
ProductModuleService.assignOptionsToVariants(
|
ProductModuleService.assignOptionsToVariants(
|
||||||
variantsWithProductId,
|
variantsWithProductId,
|
||||||
productOptions
|
productOptions
|
||||||
@@ -383,6 +384,8 @@ export default class ProductModuleService<
|
|||||||
},
|
},
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return productVariants
|
||||||
}
|
}
|
||||||
|
|
||||||
createTags(
|
createTags(
|
||||||
@@ -811,11 +814,14 @@ export default class ProductModuleService<
|
|||||||
} as UpdateProductOptionInput
|
} as UpdateProductOptionInput
|
||||||
})
|
})
|
||||||
|
|
||||||
return await this.productOptionService_.upsertWithReplace(
|
const { entities: productOptions } =
|
||||||
|
await this.productOptionService_.upsertWithReplace(
|
||||||
normalizedInput,
|
normalizedInput,
|
||||||
{ relations: ["values"] },
|
{ relations: ["values"] },
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return productOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
createCollections(
|
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
|
// 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
|
// with the collection ID
|
||||||
return await this.productCollectionService_.upsertWithReplace(
|
const { entities: productCollections } =
|
||||||
|
await this.productCollectionService_.upsertWithReplace(
|
||||||
normalizedInput,
|
normalizedInput,
|
||||||
{ relations: ["products"] },
|
{ relations: ["products"] },
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
|
||||||
|
return productCollections
|
||||||
}
|
}
|
||||||
|
|
||||||
async upsertCollections(
|
async upsertCollections(
|
||||||
@@ -1279,7 +1288,8 @@ export default class ProductModuleService<
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
const productData = await this.productService_.upsertWithReplace(
|
const { entities: productData } =
|
||||||
|
await this.productService_.upsertWithReplace(
|
||||||
normalizedInput,
|
normalizedInput,
|
||||||
{
|
{
|
||||||
relations: ["images", "tags", "categories"],
|
relations: ["images", "tags", "categories"],
|
||||||
@@ -1295,7 +1305,7 @@ export default class ProductModuleService<
|
|||||||
upsertedProduct.variants = []
|
upsertedProduct.variants = []
|
||||||
|
|
||||||
if (product.options?.length) {
|
if (product.options?.length) {
|
||||||
upsertedProduct.options =
|
const { entities: productOptions } =
|
||||||
await this.productOptionService_.upsertWithReplace(
|
await this.productOptionService_.upsertWithReplace(
|
||||||
product.options?.map((option) => ({
|
product.options?.map((option) => ({
|
||||||
...option,
|
...option,
|
||||||
@@ -1304,10 +1314,11 @@ export default class ProductModuleService<
|
|||||||
{ relations: ["values"] },
|
{ relations: ["values"] },
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
upsertedProduct.options = productOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
if (product.variants?.length) {
|
if (product.variants?.length) {
|
||||||
upsertedProduct.variants =
|
const { entities: productVariants } =
|
||||||
await this.productVariantService_.upsertWithReplace(
|
await this.productVariantService_.upsertWithReplace(
|
||||||
ProductModuleService.assignOptionsToVariants(
|
ProductModuleService.assignOptionsToVariants(
|
||||||
product.variants?.map((v) => ({
|
product.variants?.map((v) => ({
|
||||||
@@ -1319,6 +1330,7 @@ export default class ProductModuleService<
|
|||||||
{ relations: ["options"] },
|
{ relations: ["options"] },
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
upsertedProduct.variants = productVariants
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
@@ -1342,7 +1354,8 @@ export default class ProductModuleService<
|
|||||||
})
|
})
|
||||||
)
|
)
|
||||||
|
|
||||||
const productData = await this.productService_.upsertWithReplace(
|
const { entities: productData } =
|
||||||
|
await this.productService_.upsertWithReplace(
|
||||||
normalizedInput,
|
normalizedInput,
|
||||||
{
|
{
|
||||||
relations: ["images", "tags", "categories"],
|
relations: ["images", "tags", "categories"],
|
||||||
@@ -1358,7 +1371,7 @@ export default class ProductModuleService<
|
|||||||
let allOptions: any[] = []
|
let allOptions: any[] = []
|
||||||
|
|
||||||
if (product.options?.length) {
|
if (product.options?.length) {
|
||||||
upsertedProduct.options =
|
const { entities: productOptions } =
|
||||||
await this.productOptionService_.upsertWithReplace(
|
await this.productOptionService_.upsertWithReplace(
|
||||||
product.options?.map((option) => ({
|
product.options?.map((option) => ({
|
||||||
...option,
|
...option,
|
||||||
@@ -1367,6 +1380,7 @@ export default class ProductModuleService<
|
|||||||
{ relations: ["values"] },
|
{ relations: ["values"] },
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
upsertedProduct.options = productOptions
|
||||||
|
|
||||||
// Since we handle the options and variants outside of the product upsert, we need to clean up manually
|
// Since we handle the options and variants outside of the product upsert, we need to clean up manually
|
||||||
await this.productOptionService_.delete(
|
await this.productOptionService_.delete(
|
||||||
@@ -1391,7 +1405,7 @@ export default class ProductModuleService<
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (product.variants?.length) {
|
if (product.variants?.length) {
|
||||||
upsertedProduct.variants =
|
const { entities: productVariants } =
|
||||||
await this.productVariantService_.upsertWithReplace(
|
await this.productVariantService_.upsertWithReplace(
|
||||||
ProductModuleService.assignOptionsToVariants(
|
ProductModuleService.assignOptionsToVariants(
|
||||||
product.variants?.map((v) => ({
|
product.variants?.map((v) => ({
|
||||||
@@ -1403,6 +1417,7 @@ export default class ProductModuleService<
|
|||||||
{ relations: ["options"] },
|
{ relations: ["options"] },
|
||||||
sharedContext
|
sharedContext
|
||||||
)
|
)
|
||||||
|
upsertedProduct.variants = productVariants
|
||||||
|
|
||||||
await this.productVariantService_.delete(
|
await this.productVariantService_.delete(
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in new issue
Block a user