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:
Adrien de Peretti
2024-03-27 14:31:26 +00:00
committed by GitHub
parent e0b02a1012
commit 5d9aea053c
6 changed files with 108 additions and 23 deletions
+5
View File
@@ -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
} }