From ffc35f2b6e78c02777ab70b90462281dbe9d7136 Mon Sep 17 00:00:00 2001 From: Adrien de Peretti Date: Sat, 5 Oct 2024 09:53:56 +0200 Subject: [PATCH] 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 --- .../src/dal/mikro-orm/__fixtures__/utils.ts | 109 ++++++++++++++++++ .../__tests__/mikro-orm-serializer.spec.ts | 79 ++++++++++++- .../src/dal/mikro-orm/mikro-orm-serializer.ts | 12 +- 3 files changed, 188 insertions(+), 12 deletions(-) diff --git a/packages/core/utils/src/dal/mikro-orm/__fixtures__/utils.ts b/packages/core/utils/src/dal/mikro-orm/__fixtures__/utils.ts index 97021f172dd61..8e64311ab664c 100644 --- a/packages/core/utils/src/dal/mikro-orm/__fixtures__/utils.ts +++ b/packages/core/utils/src/dal/mikro-orm/__fixtures__/utils.ts @@ -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(this) + + @OneToMany(() => ProductVariant, (variant) => variant.product, { + cascade: ["soft-remove"] as any, + }) + variants = new Collection(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(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(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(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(this) + @OneToMany(() => DeepRecursiveEntity2, (entity2) => entity2.entity1, { cascade: ["soft-remove"] as any, }) @@ -349,4 +454,8 @@ export { RecursiveEntity2, SearchableEntity1, SearchableEntity2, + Product, + ProductOption, + ProductOptionValue, + ProductVariant, } diff --git a/packages/core/utils/src/dal/mikro-orm/__tests__/mikro-orm-serializer.spec.ts b/packages/core/utils/src/dal/mikro-orm/__tests__/mikro-orm-serializer.spec.ts index 9e2f5b5376c44..dbeccea8fd449 100644 --- a/packages/core/utils/src/dal/mikro-orm/__tests__/mikro-orm-serializer.spec.ts +++ b/packages/core/utils/src/dal/mikro-orm/__tests__/mikro-orm-serializer.spec.ts @@ -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", + }) + }) }) diff --git a/packages/core/utils/src/dal/mikro-orm/mikro-orm-serializer.ts b/packages/core/utils/src/dal/mikro-orm/mikro-orm-serializer.ts index 4581543a61647..d0a8dce80c302 100644 --- a/packages/core/utils/src/dal/mikro-orm/mikro-orm-serializer.ts +++ b/packages/core/utils/src/dal/mikro-orm/mikro-orm-serializer.ts @@ -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 }