From c2cbe966524c648f94bc3e94907bba19beb4c505 Mon Sep 17 00:00:00 2001 From: adrien2p Date: Fri, 11 Oct 2024 11:31:53 +0200 Subject: [PATCH 1/2] fix(utils): Cascade sOCoft deletion management --- .../core/utils/src/dal/mikro-orm/utils.ts | 9 +- packages/core/utils/src/dml/entity.ts | 2 +- .../__tests__/many-to-one.spec.ts | 133 +++++++++++++++++- 3 files changed, 136 insertions(+), 8 deletions(-) diff --git a/packages/core/utils/src/dal/mikro-orm/utils.ts b/packages/core/utils/src/dal/mikro-orm/utils.ts index 64123bbdc3da4..8900cbb02a0a5 100644 --- a/packages/core/utils/src/dal/mikro-orm/utils.ts +++ b/packages/core/utils/src/dal/mikro-orm/utils.ts @@ -1,6 +1,7 @@ import { Collection, EntityMetadata, FindOptions, wrap } from "@mikro-orm/core" import { SqlEntityManager } from "@mikro-orm/postgresql" import { buildQuery } from "../../modules-sdk/build-query" +import { isString } from "../../common/is-string" function detectCircularDependency( manager: SqlEntityManager, @@ -23,7 +24,13 @@ function detectCircularDependency( for (const relation of relationsToCascade) { const branchVisited = new Set(Array.from(visited)) - const isSelfCircularDependency = entityMetadata.class === relation.entity() + const relationEntity = + typeof relation.entity === "function" + ? relation.entity() + : relation.entity + const isSelfCircularDependency = isString(relationEntity) + ? entityMetadata.className === relationEntity + : entityMetadata.class === relationEntity if (!isSelfCircularDependency && branchVisited.has(relation.name)) { const dependencies = Array.from(visited) diff --git a/packages/core/utils/src/dml/entity.ts b/packages/core/utils/src/dml/entity.ts index ab0f4931be9f7..b11888cbbe5d3 100644 --- a/packages/core/utils/src/dml/entity.ts +++ b/packages/core/utils/src/dml/entity.ts @@ -131,7 +131,7 @@ export class DmlEntity< */ cascades( options: EntityCascades< - ExtractEntityRelations + ExtractEntityRelations > ) { const childToParentCascades = options.delete?.filter((relationship) => { diff --git a/packages/core/utils/src/dml/integration-tests/__tests__/many-to-one.spec.ts b/packages/core/utils/src/dml/integration-tests/__tests__/many-to-one.spec.ts index d668a2ad76d10..07a4c7cf1e221 100644 --- a/packages/core/utils/src/dml/integration-tests/__tests__/many-to-one.spec.ts +++ b/packages/core/utils/src/dml/integration-tests/__tests__/many-to-one.spec.ts @@ -2,11 +2,17 @@ import { MetadataStorage, MikroORM } from "@mikro-orm/core" import { model } from "../../entity-builder" import { toMikroOrmEntities } from "../../helpers/create-mikro-orm-entity" import { createDatabase, dropDatabase } from "pg-god" -import { CustomTsMigrationGenerator, mikroOrmSerializer } from "../../../dal" +import { + CustomTsMigrationGenerator, + mikroOrmSerializer, + mikroOrmUpdateDeletedAtRecursively, + SoftDeletableFilterKey, +} from "../../../dal" import { EntityConstructor } from "@medusajs/types" import { pgGodCredentials } from "../utils" import { FileSystem } from "../../../common" import { join } from "path" +import { SqlEntityManager } from "@mikro-orm/postgresql" export const fileSystem = new FileSystem( join(__dirname, "../../integration-tests-migrations-many-to-one") @@ -31,11 +37,15 @@ describe("manyToOne - belongTo", () => { user: model.belongsTo(() => user, { mappedBy: "teams" }), }) - const user = model.define("user", { - id: model.id().primaryKey(), - username: model.text(), - teams: model.hasMany(() => team, { mappedBy: "user" }), - }) + const user = model + .define("user", { + id: model.id().primaryKey(), + username: model.text(), + teams: model.hasMany(() => team, { mappedBy: "user" }), + }) + .cascades({ + delete: ["teams"], + }) ;[User, Team] = toMikroOrmEntities([user, team]) @@ -148,4 +158,115 @@ describe("manyToOne - belongTo", () => { ], }) }) + + it(`should handle soft delete cascade`, async () => { + let manager = orm.em.fork() + + const user1 = manager.create(User, { + username: "User 1", + }) + + await manager.persistAndFlush([user1]) + manager = orm.em.fork() + + const team1 = manager.create(Team, { + name: "Team 1", + user_id: user1.id, + }) + const team2 = manager.create(Team, { + name: "Team 2", + user_id: user1.id, + }) + + await manager.persistAndFlush([team1, team2]) + manager = orm.em.fork() + + let teams = await manager.find( + Team, + {}, + { + populate: ["user"], + } + ) + + const serializedTeams = await mikroOrmSerializer>( + teams + ) + expect(serializedTeams).toHaveLength(2) + expect(serializedTeams).toEqual( + expect.arrayContaining([ + { + id: team1.id, + name: "Team 1", + created_at: expect.any(Date), + updated_at: expect.any(Date), + deleted_at: null, + user_id: user1.id, + user: { + id: user1.id, + username: "User 1", + created_at: expect.any(Date), + updated_at: expect.any(Date), + deleted_at: null, + }, + }, + { + id: team2.id, + name: "Team 2", + created_at: expect.any(Date), + updated_at: expect.any(Date), + deleted_at: null, + user_id: user1.id, + user: { + id: user1.id, + username: "User 1", + created_at: expect.any(Date), + updated_at: expect.any(Date), + deleted_at: null, + }, + }, + ]) + ) + + manager = orm.em.fork() + const userToDelete = await manager.findOne(User, { + id: user1.id, + }) + await mikroOrmUpdateDeletedAtRecursively( + manager as SqlEntityManager, + [userToDelete], + new Date() + ) + + teams = await manager.find( + Team, + {}, + { + populate: ["user"], + filters: { + [SoftDeletableFilterKey]: { + withDeleted: true, + }, + }, + } + ) + + expect(teams).toHaveLength(2) + expect(teams).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + deleted_at: expect.any(Date), + user: expect.objectContaining({ + deleted_at: expect.any(Date), + }), + }), + expect.objectContaining({ + deleted_at: expect.any(Date), + user: expect.objectContaining({ + deleted_at: expect.any(Date), + }), + }), + ]) + ) + }) }) From d1438c1a51b43d47b1ce77dc47527e9cb37c44d0 Mon Sep 17 00:00:00 2001 From: adrien2p Date: Fri, 11 Oct 2024 11:34:16 +0200 Subject: [PATCH 2/2] fix(utils): Cascade sOCoft deletion management --- packages/core/utils/src/dml/entity.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/utils/src/dml/entity.ts b/packages/core/utils/src/dml/entity.ts index b11888cbbe5d3..ab0f4931be9f7 100644 --- a/packages/core/utils/src/dml/entity.ts +++ b/packages/core/utils/src/dml/entity.ts @@ -131,7 +131,7 @@ export class DmlEntity< */ cascades( options: EntityCascades< - ExtractEntityRelations + ExtractEntityRelations > ) { const childToParentCascades = options.delete?.filter((relationship) => {