fix: Remove extra saving on serialization which breaks the chain (#9465)

**What**
The extra serialization check hapen to break the serialization chain by reusing already serialized entities when they have been serialized from a different parents sequence
This commit is contained in:
Adrien de Peretti
2024-10-05 09:53:56 +02:00
committed by GitHub
parent cc77ca1413
commit ffc35f2b6e
3 changed files with 188 additions and 12 deletions

View File

@@ -1,6 +1,8 @@
import {
Cascade,
Collection,
Entity,
ManyToMany,
ManyToOne,
OneToMany,
PrimaryKey,
@@ -9,6 +11,104 @@ import {
} from "@mikro-orm/core"
import { Searchable } from "../decorators/searchable"
@Entity()
class Product {
@PrimaryKey()
id: string
@Property()
name: string
@OneToMany(() => ProductOption, (o) => o.product, {
cascade: ["soft-remove"] as any,
})
options = new Collection<ProductOption>(this)
@OneToMany(() => ProductVariant, (variant) => variant.product, {
cascade: ["soft-remove"] as any,
})
variants = new Collection<ProductVariant>(this)
}
@Entity()
class ProductOption {
@PrimaryKey()
id: string
@Property()
name: string
@ManyToOne(() => Product, {
persist: false,
nullable: true,
})
product: Product | null
@OneToMany(() => ProductOptionValue, (value) => value.option, {
cascade: [Cascade.PERSIST, "soft-remove" as any],
})
values = new Collection<ProductOptionValue>(this)
}
@Entity()
class ProductOptionValue {
@PrimaryKey()
id: string
@Property()
name: string
@ManyToOne(() => ProductOption, {
columnType: "text",
fieldName: "option_id",
mapToPk: true,
nullable: true,
onDelete: "cascade",
})
option_id: string | null
@ManyToOne(() => ProductOption, {
nullable: true,
persist: false,
})
option: ProductOption | null
@ManyToMany(() => ProductVariant, (variant) => variant.options)
variants = new Collection<ProductVariant>(this)
}
@Entity()
class ProductVariant {
@PrimaryKey()
id: string
@Property()
name: string
@ManyToOne(() => Product, {
columnType: "text",
nullable: true,
onDelete: "cascade",
fieldName: "product_id",
mapToPk: true,
})
product_id: string | null
@ManyToOne(() => Product, {
persist: false,
nullable: true,
})
product: Product | null
@ManyToMany(() => ProductOptionValue, "variants", {
owner: true,
pivotTable: "product_variant_option",
joinColumn: "variant_id",
inverseJoinColumn: "option_value_id",
})
options = new Collection<ProductOptionValue>(this)
}
// Circular dependency one level
@Entity()
class RecursiveEntity1 {
@@ -114,6 +214,11 @@ class DeepRecursiveEntity1 {
@Property({ nullable: true })
deleted_at: Date | null
@OneToMany(() => DeepRecursiveEntity3, (entity3) => entity3.entity1, {
cascade: ["soft-remove"] as any,
})
entity3 = new Collection<DeepRecursiveEntity3>(this)
@OneToMany(() => DeepRecursiveEntity2, (entity2) => entity2.entity1, {
cascade: ["soft-remove"] as any,
})
@@ -349,4 +454,8 @@ export {
RecursiveEntity2,
SearchableEntity1,
SearchableEntity2,
Product,
ProductOption,
ProductOptionValue,
ProductVariant,
}

View File

@@ -2,13 +2,24 @@ import { MikroORM } from "@mikro-orm/core"
import {
Entity1WithUnDecoratedProp,
Entity2WithUnDecoratedProp,
Product,
ProductOption,
ProductOptionValue,
ProductVariant,
} from "../__fixtures__/utils"
import { mikroOrmSerializer } from "../mikro-orm-serializer"
describe("mikroOrmSerializer", () => {
beforeEach(async () => {
await MikroORM.init({
entities: [Entity1WithUnDecoratedProp, Entity2WithUnDecoratedProp],
entities: [
Entity1WithUnDecoratedProp,
Entity2WithUnDecoratedProp,
Product,
ProductOption,
ProductOptionValue,
ProductVariant,
],
dbName: "test",
type: "postgresql",
})
@@ -119,4 +130,70 @@ describe("mikroOrmSerializer", () => {
],
})
})
it(`should properly serialize nested relations and sibling to not return parents into children`, async () => {
const productOptionValue = new ProductOptionValue()
productOptionValue.id = "1"
productOptionValue.name = "Product option value 1"
productOptionValue.option_id = "1"
const productOptions = new ProductOption()
productOptions.id = "1"
productOptions.name = "Product option 1"
productOptions.values.add(productOptionValue)
const productVariant = new ProductVariant()
productVariant.id = "1"
productVariant.name = "Product variant 1"
productVariant.options.add(productOptionValue)
const product = new Product()
product.id = "1"
product.name = "Product 1"
product.options.add(productOptions)
product.variants.add(productVariant)
const serialized = await mikroOrmSerializer(product)
expect(serialized).toEqual({
id: "1",
options: [
{
id: "1",
values: [
{
id: "1",
variants: [
{
id: "1",
name: "Product variant 1",
},
],
name: "Product option value 1",
option_id: "1",
},
],
name: "Product option 1",
},
],
variants: [
{
id: "1",
options: [
{
id: "1",
name: "Product option value 1",
option_id: "1",
option: {
id: "1",
name: "Product option 1",
},
},
],
name: "Product variant 1",
},
],
name: "Product 1",
})
})
})

View File

@@ -142,16 +142,6 @@ export class EntitySerializer {
root.visited.add(entity)
}
// Virtually augment the serialization context
root.visitedSerialized ??= new Map()
const primaryKeysValues = Array.from(keys)
.map((key) => entity[key])
.join("-")
if (root.visitedSerialized.has(primaryKeysValues)) {
return root.visitedSerialized.get(primaryKeysValues)
}
;[...keys]
/** Medusa Custom properties filtering **/
.filter((prop) =>
@@ -242,7 +232,7 @@ export class EntitySerializer {
parents_
))
)
root.visitedSerialized.set(primaryKeysValues, ret)
return ret
}