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:
1 parent
cc77ca1413
commit
ffc35f2b6e
3 files changed
+188
-12
No files matched your search
@@ -1,6 +1,8 @@
|
|||||||
import {
|
import {
|
||||||
|
Cascade,
|
||||||
Collection,
|
Collection,
|
||||||
Entity,
|
Entity,
|
||||||
|
ManyToMany,
|
||||||
ManyToOne,
|
ManyToOne,
|
||||||
OneToMany,
|
OneToMany,
|
||||||
PrimaryKey,
|
PrimaryKey,
|
||||||
@@ -9,6 +11,104 @@ import {
|
|||||||
} from "@mikro-orm/core"
|
} from "@mikro-orm/core"
|
||||||
import { Searchable } from "../decorators/searchable"
|
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
|
// Circular dependency one level
|
||||||
@Entity()
|
@Entity()
|
||||||
class RecursiveEntity1 {
|
class RecursiveEntity1 {
|
||||||
@@ -114,6 +214,11 @@ class DeepRecursiveEntity1 {
|
|||||||
@Property({ nullable: true })
|
@Property({ nullable: true })
|
||||||
deleted_at: Date | null
|
deleted_at: Date | null
|
||||||
|
|
||||||
|
@OneToMany(() => DeepRecursiveEntity3, (entity3) => entity3.entity1, {
|
||||||
|
cascade: ["soft-remove"] as any,
|
||||||
|
})
|
||||||
|
entity3 = new Collection<DeepRecursiveEntity3>(this)
|
||||||
|
|
||||||
@OneToMany(() => DeepRecursiveEntity2, (entity2) => entity2.entity1, {
|
@OneToMany(() => DeepRecursiveEntity2, (entity2) => entity2.entity1, {
|
||||||
cascade: ["soft-remove"] as any,
|
cascade: ["soft-remove"] as any,
|
||||||
})
|
})
|
||||||
@@ -349,4 +454,8 @@ export {
|
|||||||
RecursiveEntity2,
|
RecursiveEntity2,
|
||||||
SearchableEntity1,
|
SearchableEntity1,
|
||||||
SearchableEntity2,
|
SearchableEntity2,
|
||||||
|
Product,
|
||||||
|
ProductOption,
|
||||||
|
ProductOptionValue,
|
||||||
|
ProductVariant,
|
||||||
}
|
}
|
||||||
@@ -2,13 +2,24 @@ import { MikroORM } from "@mikro-orm/core"
|
|||||||
import {
|
import {
|
||||||
Entity1WithUnDecoratedProp,
|
Entity1WithUnDecoratedProp,
|
||||||
Entity2WithUnDecoratedProp,
|
Entity2WithUnDecoratedProp,
|
||||||
|
Product,
|
||||||
|
ProductOption,
|
||||||
|
ProductOptionValue,
|
||||||
|
ProductVariant,
|
||||||
} from "../__fixtures__/utils"
|
} from "../__fixtures__/utils"
|
||||||
import { mikroOrmSerializer } from "../mikro-orm-serializer"
|
import { mikroOrmSerializer } from "../mikro-orm-serializer"
|
||||||
|
|
||||||
describe("mikroOrmSerializer", () => {
|
describe("mikroOrmSerializer", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await MikroORM.init({
|
await MikroORM.init({
|
||||||
entities: [Entity1WithUnDecoratedProp, Entity2WithUnDecoratedProp],
|
entities: [
|
||||||
|
Entity1WithUnDecoratedProp,
|
||||||
|
Entity2WithUnDecoratedProp,
|
||||||
|
Product,
|
||||||
|
ProductOption,
|
||||||
|
ProductOptionValue,
|
||||||
|
ProductVariant,
|
||||||
|
],
|
||||||
dbName: "test",
|
dbName: "test",
|
||||||
type: "postgresql",
|
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",
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
@@ -142,16 +142,6 @@ export class EntitySerializer {
|
|||||||
root.visited.add(entity)
|
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]
|
;[...keys]
|
||||||
/** Medusa Custom properties filtering **/
|
/** Medusa Custom properties filtering **/
|
||||||
.filter((prop) =>
|
.filter((prop) =>
|
||||||
@@ -242,7 +232,7 @@ export class EntitySerializer {
|
|||||||
parents_
|
parents_
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
root.visitedSerialized.set(primaryKeysValues, ret)
|
|
||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user