fix(utils): Mikro orm prop filtering should check existence (#6842)
**What** - Should return non decorated but visible properties. - Should only prevent circular on non scalar fields
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/utils": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix(utils): Mikro orm prop filtering should check existence
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
import { IInventoryServiceNext, IStockLocationService } from "@medusajs/types"
|
import { IInventoryServiceNext, IStockLocationService } from "@medusajs/types"
|
||||||
|
|
||||||
import { ContainerRegistrationKeys } from "@medusajs/utils"
|
import {
|
||||||
|
ContainerRegistrationKeys,
|
||||||
|
remoteQueryObjectFromString,
|
||||||
|
} from "@medusajs/utils"
|
||||||
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
|
||||||
import { createAdminUser } from "../../../helpers/create-admin-user"
|
import { createAdminUser } from "../../../helpers/create-admin-user"
|
||||||
import { remoteQueryObjectFromString } from "@medusajs/utils"
|
|
||||||
|
|
||||||
const { medusaIntegrationTestRunner } = require("medusa-test-utils")
|
const { medusaIntegrationTestRunner } = require("medusa-test-utils")
|
||||||
|
|
||||||
|
|||||||
@@ -78,7 +78,6 @@ medusaIntegrationTestRunner({
|
|||||||
ends_at: expect.any(String),
|
ends_at: expect.any(String),
|
||||||
budget: {
|
budget: {
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
campaign: expect.any(Object),
|
|
||||||
type: "spend",
|
type: "spend",
|
||||||
limit: 1000,
|
limit: 1000,
|
||||||
used: 0,
|
used: 0,
|
||||||
@@ -108,7 +107,6 @@ medusaIntegrationTestRunner({
|
|||||||
ends_at: expect.any(String),
|
ends_at: expect.any(String),
|
||||||
budget: {
|
budget: {
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
campaign: expect.any(Object),
|
|
||||||
type: "usage",
|
type: "usage",
|
||||||
limit: 1000,
|
limit: 1000,
|
||||||
used: 0,
|
used: 0,
|
||||||
@@ -148,7 +146,6 @@ medusaIntegrationTestRunner({
|
|||||||
created_at: expect.any(String),
|
created_at: expect.any(String),
|
||||||
budget: {
|
budget: {
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
campaign: expect.any(Object),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -157,7 +154,6 @@ medusaIntegrationTestRunner({
|
|||||||
created_at: expect.any(String),
|
created_at: expect.any(String),
|
||||||
budget: {
|
budget: {
|
||||||
id: expect.any(String),
|
id: expect.any(String),
|
||||||
campaign: expect.any(Object),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
|
|||||||
@@ -229,6 +229,57 @@ class InternalCircularDependencyEntity1 {
|
|||||||
parent: InternalCircularDependencyEntity1
|
parent: InternalCircularDependencyEntity1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// With un decorated prop
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
class Entity1WithUnDecoratedProp {
|
||||||
|
constructor(props: { id: string; deleted_at: Date | null }) {
|
||||||
|
this.id = props.id
|
||||||
|
this.deleted_at = props.deleted_at
|
||||||
|
}
|
||||||
|
|
||||||
|
unknownProp: string
|
||||||
|
|
||||||
|
@PrimaryKey()
|
||||||
|
id: string
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
deleted_at: Date | null
|
||||||
|
|
||||||
|
@OneToMany(() => Entity2WithUnDecoratedProp, (entity2) => entity2.entity1, {
|
||||||
|
cascade: ["soft-remove"] as any,
|
||||||
|
})
|
||||||
|
entity2 = new Collection<Entity2WithUnDecoratedProp>(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
class Entity2WithUnDecoratedProp {
|
||||||
|
constructor(props: {
|
||||||
|
id: string
|
||||||
|
deleted_at: Date | null
|
||||||
|
entity1: Entity1WithUnDecoratedProp
|
||||||
|
}) {
|
||||||
|
this.id = props.id
|
||||||
|
this.deleted_at = props.deleted_at
|
||||||
|
this.entity1 = props.entity1
|
||||||
|
this.entity1_id = props.entity1.id
|
||||||
|
}
|
||||||
|
|
||||||
|
unknownProp: string
|
||||||
|
|
||||||
|
@PrimaryKey()
|
||||||
|
id: string
|
||||||
|
|
||||||
|
@Property()
|
||||||
|
deleted_at: Date | null
|
||||||
|
|
||||||
|
@ManyToOne(() => Entity1WithUnDecoratedProp, { mapToPk: true })
|
||||||
|
entity1_id: string
|
||||||
|
|
||||||
|
@ManyToOne(() => Entity1WithUnDecoratedProp, { persist: false })
|
||||||
|
entity1: Entity1WithUnDecoratedProp
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
RecursiveEntity1,
|
RecursiveEntity1,
|
||||||
RecursiveEntity2,
|
RecursiveEntity2,
|
||||||
@@ -239,4 +290,6 @@ export {
|
|||||||
DeepRecursiveEntity3,
|
DeepRecursiveEntity3,
|
||||||
DeepRecursiveEntity4,
|
DeepRecursiveEntity4,
|
||||||
InternalCircularDependencyEntity1,
|
InternalCircularDependencyEntity1,
|
||||||
|
Entity1WithUnDecoratedProp,
|
||||||
|
Entity2WithUnDecoratedProp,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,27 @@
|
|||||||
import { MikroORM } from "@mikro-orm/core"
|
import { MikroORM } from "@mikro-orm/core"
|
||||||
import { Entity1, Entity2 } from "../__fixtures__/utils"
|
import {
|
||||||
|
Entity1WithUnDecoratedProp,
|
||||||
|
Entity2WithUnDecoratedProp,
|
||||||
|
} 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: [Entity1, Entity2],
|
entities: [Entity1WithUnDecoratedProp, Entity2WithUnDecoratedProp],
|
||||||
dbName: "test",
|
dbName: "test",
|
||||||
type: "postgresql",
|
type: "postgresql",
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it("should serialize an entity", async () => {
|
it("should serialize an entity", async () => {
|
||||||
const entity1 = new Entity1({ id: "1", deleted_at: null })
|
const entity1 = new Entity1WithUnDecoratedProp({
|
||||||
const entity2 = new Entity2({
|
id: "1",
|
||||||
|
deleted_at: null,
|
||||||
|
})
|
||||||
|
entity1.unknownProp = "calculated"
|
||||||
|
|
||||||
|
const entity2 = new Entity2WithUnDecoratedProp({
|
||||||
id: "2",
|
id: "2",
|
||||||
deleted_at: null,
|
deleted_at: null,
|
||||||
entity1: entity1,
|
entity1: entity1,
|
||||||
@@ -27,6 +35,7 @@ describe("mikroOrmSerializer", () => {
|
|||||||
expect(serialized).toEqual({
|
expect(serialized).toEqual({
|
||||||
id: "1",
|
id: "1",
|
||||||
deleted_at: null,
|
deleted_at: null,
|
||||||
|
unknownProp: "calculated",
|
||||||
entity2: [
|
entity2: [
|
||||||
{
|
{
|
||||||
id: "2",
|
id: "2",
|
||||||
@@ -34,6 +43,7 @@ describe("mikroOrmSerializer", () => {
|
|||||||
entity1: {
|
entity1: {
|
||||||
id: "1",
|
id: "1",
|
||||||
deleted_at: null,
|
deleted_at: null,
|
||||||
|
unknownProp: "calculated",
|
||||||
},
|
},
|
||||||
entity1_id: "1",
|
entity1_id: "1",
|
||||||
},
|
},
|
||||||
@@ -42,8 +52,13 @@ describe("mikroOrmSerializer", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("should serialize an array of entities", async () => {
|
it("should serialize an array of entities", async () => {
|
||||||
const entity1 = new Entity1({ id: "1", deleted_at: null })
|
const entity1 = new Entity1WithUnDecoratedProp({
|
||||||
const entity2 = new Entity2({
|
id: "1",
|
||||||
|
deleted_at: null,
|
||||||
|
})
|
||||||
|
entity1.unknownProp = "calculated"
|
||||||
|
|
||||||
|
const entity2 = new Entity2WithUnDecoratedProp({
|
||||||
id: "2",
|
id: "2",
|
||||||
deleted_at: null,
|
deleted_at: null,
|
||||||
entity1: entity1,
|
entity1: entity1,
|
||||||
@@ -57,6 +72,7 @@ describe("mikroOrmSerializer", () => {
|
|||||||
const expectation = {
|
const expectation = {
|
||||||
id: "1",
|
id: "1",
|
||||||
deleted_at: null,
|
deleted_at: null,
|
||||||
|
unknownProp: "calculated",
|
||||||
entity2: [
|
entity2: [
|
||||||
{
|
{
|
||||||
id: "2",
|
id: "2",
|
||||||
@@ -64,6 +80,7 @@ describe("mikroOrmSerializer", () => {
|
|||||||
entity1: {
|
entity1: {
|
||||||
id: "1",
|
id: "1",
|
||||||
deleted_at: null,
|
deleted_at: null,
|
||||||
|
unknownProp: "calculated",
|
||||||
},
|
},
|
||||||
entity1_id: "1",
|
entity1_id: "1",
|
||||||
},
|
},
|
||||||
@@ -74,8 +91,13 @@ describe("mikroOrmSerializer", () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it("should serialize an entity preventing circular relation reference", async () => {
|
it("should serialize an entity preventing circular relation reference", async () => {
|
||||||
const entity1 = new Entity1({ id: "1", deleted_at: null })
|
const entity1 = new Entity1WithUnDecoratedProp({
|
||||||
const entity2 = new Entity2({
|
id: "1",
|
||||||
|
deleted_at: null,
|
||||||
|
})
|
||||||
|
entity1.unknownProp = "calculated"
|
||||||
|
|
||||||
|
const entity2 = new Entity2WithUnDecoratedProp({
|
||||||
id: "2",
|
id: "2",
|
||||||
deleted_at: null,
|
deleted_at: null,
|
||||||
entity1: entity1,
|
entity1: entity1,
|
||||||
@@ -87,6 +109,7 @@ describe("mikroOrmSerializer", () => {
|
|||||||
expect(serialized).toEqual({
|
expect(serialized).toEqual({
|
||||||
id: "1",
|
id: "1",
|
||||||
deleted_at: null,
|
deleted_at: null,
|
||||||
|
unknownProp: "calculated",
|
||||||
entity2: [
|
entity2: [
|
||||||
{
|
{
|
||||||
id: "2",
|
id: "2",
|
||||||
|
|||||||
@@ -65,26 +65,32 @@ function isPopulated<T extends object>(
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Customer property filtering for the serialization which takes into account the parent entity to filter out circular references if configured for.
|
* Customer property filtering for the serialization which takes into account the parent entity to filter out circular references if configured for.
|
||||||
* @param prop
|
* @param propName
|
||||||
* @param meta
|
* @param meta
|
||||||
* @param options
|
* @param options
|
||||||
* @param parent
|
* @param parent
|
||||||
*/
|
*/
|
||||||
function filterEntityPropToSerialize(
|
function filterEntityPropToSerialize(
|
||||||
prop: string,
|
propName: string,
|
||||||
meta: EntityMetadata,
|
meta: EntityMetadata,
|
||||||
options: SerializeOptions<object, any> & {
|
options: SerializeOptions<object, any> & {
|
||||||
preventCircularRef?: boolean
|
preventCircularRef?: boolean
|
||||||
} = {},
|
} = {},
|
||||||
parent?: object
|
parent?: object
|
||||||
): boolean {
|
): boolean {
|
||||||
const isVisibleRes = isVisible(meta, prop, options)
|
const isVisibleRes = isVisible(meta, propName, options)
|
||||||
if (options.preventCircularRef && isVisibleRes && parent) {
|
const prop = meta.properties[propName]
|
||||||
return (
|
|
||||||
// mapToPk would represent a foreign key and we want to keep them
|
// Only prevent circular references if prop is a relation
|
||||||
meta.properties[prop].mapToPk ||
|
if (
|
||||||
parent.constructor.name !== meta.properties[prop].type
|
prop &&
|
||||||
)
|
options.preventCircularRef &&
|
||||||
|
isVisibleRes &&
|
||||||
|
parent &&
|
||||||
|
prop.reference !== ReferenceType.SCALAR
|
||||||
|
) {
|
||||||
|
// mapToPk would represent a foreign key and we want to keep them
|
||||||
|
return !!prop.mapToPk || parent.constructor.name !== prop.type
|
||||||
}
|
}
|
||||||
return isVisibleRes
|
return isVisibleRes
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user