chore(utils): Soft delete should allow self referencing circular deps (#6504)

**What**
Self referencing circular dep should not be an error and should be accepted
This commit is contained in:
Adrien de Peretti
2024-02-26 10:37:46 +01:00
committed by GitHub
parent d983329481
commit 56cbf88115
5 changed files with 91 additions and 5 deletions

View File

@@ -189,6 +189,42 @@ class DeepRecursiveEntity4 {
entity1: DeepRecursiveEntity1
}
// Internal circular dependency
@Entity()
class InternalCircularDependencyEntity1 {
constructor(props: {
id: string
deleted_at: Date | null
parent?: InternalCircularDependencyEntity1
}) {
this.id = props.id
this.deleted_at = props.deleted_at
if (props.parent) {
this.parent = props.parent
}
}
@PrimaryKey()
id: string
@Property()
deleted_at: Date | null
@OneToMany(
() => InternalCircularDependencyEntity1,
(entity) => entity.parent,
{
cascade: ["soft-remove"] as any,
}
)
children = new Collection<InternalCircularDependencyEntity1>(this)
@ManyToOne(() => InternalCircularDependencyEntity1)
parent: InternalCircularDependencyEntity1
}
export {
RecursiveEntity1,
RecursiveEntity2,
@@ -198,4 +234,5 @@ export {
DeepRecursiveEntity2,
DeepRecursiveEntity3,
DeepRecursiveEntity4,
InternalCircularDependencyEntity1,
}

View File

@@ -8,6 +8,7 @@ import {
DeepRecursiveEntity4,
Entity1,
Entity2,
InternalCircularDependencyEntity1,
RecursiveEntity1,
RecursiveEntity2,
} from "../__fixtures__/utils"
@@ -38,6 +39,7 @@ describe("mikroOrmUpdateDeletedAtRecursively", () => {
DeepRecursiveEntity2,
DeepRecursiveEntity3,
DeepRecursiveEntity4,
InternalCircularDependencyEntity1,
],
dbName: "test",
type: "postgresql",
@@ -65,6 +67,26 @@ describe("mikroOrmUpdateDeletedAtRecursively", () => {
expect(entity2.deleted_at).toEqual(deletedAt)
})
it("should successfully mark the entities deleted_at recursively with internal parent/child relation", async () => {
const manager = orm.em.fork() as SqlEntityManager
const entity1 = new InternalCircularDependencyEntity1({
id: "1",
deleted_at: null,
})
const childEntity1 = new InternalCircularDependencyEntity1({
id: "2",
deleted_at: null,
parent: entity1,
})
const deletedAt = new Date()
await mikroOrmUpdateDeletedAtRecursively(manager, [entity1], deletedAt)
expect(entity1.deleted_at).toEqual(deletedAt)
expect(childEntity1.deleted_at).toEqual(deletedAt)
})
it("should throw an error when a circular dependency is detected", async () => {
const manager = orm.em.fork() as SqlEntityManager
const entity1 = new RecursiveEntity1({ id: "1", deleted_at: null })

View File

@@ -5,8 +5,13 @@ import { SqlEntityManager } from "@mikro-orm/postgresql"
function detectCircularDependency(
manager: SqlEntityManager,
entityMetadata: EntityMetadata,
visited: Set<string> = new Set()
visited: Set<string> = new Set(),
shouldStop: boolean = false
) {
if (shouldStop) {
return
}
visited.add(entityMetadata.className)
const relations = entityMetadata.relations
@@ -17,7 +22,9 @@ function detectCircularDependency(
for (const relation of relationsToCascade) {
const branchVisited = new Set(Array.from(visited))
if (branchVisited.has(relation.name)) {
const isSelfCircularDependency = entityMetadata.class === relation.entity()
if (!isSelfCircularDependency && branchVisited.has(relation.name)) {
const dependencies = Array.from(visited)
dependencies.push(entityMetadata.className)
const circularDependencyStr = dependencies.join(" -> ")
@@ -33,7 +40,12 @@ function detectCircularDependency(
.getMetadata()
.get(relation.type)
detectCircularDependency(manager, relationEntityMetadata, branchVisited)
detectCircularDependency(
manager,
relationEntityMetadata,
branchVisited,
isSelfCircularDependency
)
}
}
@@ -82,6 +94,9 @@ async function performCascadingSoftDeletion<T>(
if (!entityRelation) {
// Fixes the case of many to many through pivot table
entityRelation = await retrieveEntity()
if (!entityRelation) {
continue
}
}
const isCollection = "toArray" in entityRelation
@@ -94,10 +109,17 @@ async function performCascadingSoftDeletion<T>(
}
relationEntities = entityRelation.getItems()
} else {
const initializedEntityRelation = await wrap(entityRelation).init()
const wrappedEntity = wrap(entityRelation)
const initializedEntityRelation = wrappedEntity.isInitialized()
? entityRelation
: await wrap(entityRelation).init()
relationEntities = [initializedEntityRelation]
}
if (!relationEntities.length) {
continue
}
await mikroOrmUpdateDeletedAtRecursively(manager, relationEntities, value)
}