feat(utils): custom serialization that allows for non self ref (#6836)
* feat(utils): custom serialization that allows for non self ref * Create fast-teachers-greet.md * cleanup + tests * cleanup + tests * fix import * fix map to pk relations
This commit is contained in:
1 parent
4cf71af07d
commit
e0b02a1012
7 files changed
+506
-35
No files matched your search
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/utils": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat(utils): custom serialization that allows for non self ref
|
||||||
@@ -2,6 +2,7 @@ export * from "./mikro-orm/big-number-field"
|
|||||||
export * from "./mikro-orm/mikro-orm-create-connection"
|
export * from "./mikro-orm/mikro-orm-create-connection"
|
||||||
export * from "./mikro-orm/mikro-orm-repository"
|
export * from "./mikro-orm/mikro-orm-repository"
|
||||||
export * from "./mikro-orm/mikro-orm-soft-deletable-filter"
|
export * from "./mikro-orm/mikro-orm-soft-deletable-filter"
|
||||||
|
export * from "./mikro-orm/mikro-orm-serializer"
|
||||||
export * from "./mikro-orm/utils"
|
export * from "./mikro-orm/utils"
|
||||||
export * from "./repositories"
|
export * from "./repositories"
|
||||||
export * from "./utils"
|
export * from "./utils"
|
||||||
@@ -82,6 +82,7 @@ class Entity2 {
|
|||||||
this.id = props.id
|
this.id = props.id
|
||||||
this.deleted_at = props.deleted_at
|
this.deleted_at = props.deleted_at
|
||||||
this.entity1 = props.entity1
|
this.entity1 = props.entity1
|
||||||
|
this.entity1_id = props.entity1.id
|
||||||
}
|
}
|
||||||
|
|
||||||
@PrimaryKey()
|
@PrimaryKey()
|
||||||
@@ -90,7 +91,10 @@ class Entity2 {
|
|||||||
@Property()
|
@Property()
|
||||||
deleted_at: Date | null
|
deleted_at: Date | null
|
||||||
|
|
||||||
@ManyToOne(() => Entity1)
|
@ManyToOne(() => Entity1, { mapToPk: true })
|
||||||
|
entity1_id: string
|
||||||
|
|
||||||
|
@ManyToOne(() => Entity1, { persist: false })
|
||||||
entity1: Entity1
|
entity1: Entity1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import { MikroORM } from "@mikro-orm/core"
|
||||||
|
import { Entity1, Entity2 } from "../__fixtures__/utils"
|
||||||
|
import { mikroOrmSerializer } from "../mikro-orm-serializer"
|
||||||
|
|
||||||
|
describe("mikroOrmSerializer", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
await MikroORM.init({
|
||||||
|
entities: [Entity1, Entity2],
|
||||||
|
dbName: "test",
|
||||||
|
type: "postgresql",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should serialize an entity", async () => {
|
||||||
|
const entity1 = new Entity1({ id: "1", deleted_at: null })
|
||||||
|
const entity2 = new Entity2({
|
||||||
|
id: "2",
|
||||||
|
deleted_at: null,
|
||||||
|
entity1: entity1,
|
||||||
|
})
|
||||||
|
entity1.entity2.add(entity2)
|
||||||
|
|
||||||
|
const serialized = await mikroOrmSerializer(entity1, {
|
||||||
|
preventCircularRef: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(serialized).toEqual({
|
||||||
|
id: "1",
|
||||||
|
deleted_at: null,
|
||||||
|
entity2: [
|
||||||
|
{
|
||||||
|
id: "2",
|
||||||
|
deleted_at: null,
|
||||||
|
entity1: {
|
||||||
|
id: "1",
|
||||||
|
deleted_at: null,
|
||||||
|
},
|
||||||
|
entity1_id: "1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should serialize an array of entities", async () => {
|
||||||
|
const entity1 = new Entity1({ id: "1", deleted_at: null })
|
||||||
|
const entity2 = new Entity2({
|
||||||
|
id: "2",
|
||||||
|
deleted_at: null,
|
||||||
|
entity1: entity1,
|
||||||
|
})
|
||||||
|
entity1.entity2.add(entity2)
|
||||||
|
|
||||||
|
const serialized = await mikroOrmSerializer([entity1, entity1], {
|
||||||
|
preventCircularRef: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
const expectation = {
|
||||||
|
id: "1",
|
||||||
|
deleted_at: null,
|
||||||
|
entity2: [
|
||||||
|
{
|
||||||
|
id: "2",
|
||||||
|
deleted_at: null,
|
||||||
|
entity1: {
|
||||||
|
id: "1",
|
||||||
|
deleted_at: null,
|
||||||
|
},
|
||||||
|
entity1_id: "1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(serialized).toEqual([expectation, expectation])
|
||||||
|
})
|
||||||
|
|
||||||
|
it("should serialize an entity preventing circular relation reference", async () => {
|
||||||
|
const entity1 = new Entity1({ id: "1", deleted_at: null })
|
||||||
|
const entity2 = new Entity2({
|
||||||
|
id: "2",
|
||||||
|
deleted_at: null,
|
||||||
|
entity1: entity1,
|
||||||
|
})
|
||||||
|
entity1.entity2.add(entity2)
|
||||||
|
|
||||||
|
const serialized = await mikroOrmSerializer(entity1)
|
||||||
|
|
||||||
|
expect(serialized).toEqual({
|
||||||
|
id: "1",
|
||||||
|
deleted_at: null,
|
||||||
|
entity2: [
|
||||||
|
{
|
||||||
|
id: "2",
|
||||||
|
deleted_at: null,
|
||||||
|
entity1_id: "1",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -26,7 +26,8 @@ import {
|
|||||||
getSoftDeletedCascadedEntitiesIdsMappedBy,
|
getSoftDeletedCascadedEntitiesIdsMappedBy,
|
||||||
transactionWrapper,
|
transactionWrapper,
|
||||||
} from "../utils"
|
} from "../utils"
|
||||||
import { mikroOrmSerializer, mikroOrmUpdateDeletedAtRecursively } from "./utils"
|
import { mikroOrmUpdateDeletedAtRecursively } from "./utils"
|
||||||
|
import { mikroOrmSerializer } from "./mikro-orm-serializer"
|
||||||
|
|
||||||
export class MikroOrmBase<T = any> {
|
export class MikroOrmBase<T = any> {
|
||||||
readonly manager_: any
|
readonly manager_: any
|
||||||
|
|||||||
@@ -0,0 +1,394 @@
|
|||||||
|
import {
|
||||||
|
Collection,
|
||||||
|
EntityDTO,
|
||||||
|
EntityMetadata,
|
||||||
|
helper,
|
||||||
|
IPrimaryKey,
|
||||||
|
Loaded,
|
||||||
|
Platform,
|
||||||
|
Reference,
|
||||||
|
ReferenceType,
|
||||||
|
SerializationContext,
|
||||||
|
Utils,
|
||||||
|
} from "@mikro-orm/core"
|
||||||
|
import { SerializeOptions } from "@mikro-orm/core/serialization/EntitySerializer"
|
||||||
|
|
||||||
|
function isVisible<T extends object>(
|
||||||
|
meta: EntityMetadata<T>,
|
||||||
|
propName: string,
|
||||||
|
options: SerializeOptions<T, any> & { preventCircularRef?: boolean } = {}
|
||||||
|
): boolean {
|
||||||
|
if (options.populate === true) {
|
||||||
|
return options.populate
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
Array.isArray(options.populate) &&
|
||||||
|
options.populate?.find(
|
||||||
|
(item) => item === propName || item.startsWith(propName + ".")
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.exclude?.find((item) => item === propName)) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const prop = meta.properties[propName]
|
||||||
|
const visible = prop && !prop.hidden
|
||||||
|
const prefixed = prop && !prop.primary && propName.startsWith("_") // ignore prefixed properties, if it's not a PK
|
||||||
|
|
||||||
|
return visible && !prefixed
|
||||||
|
}
|
||||||
|
|
||||||
|
function isPopulated<T extends object>(
|
||||||
|
entity: T,
|
||||||
|
propName: string,
|
||||||
|
options: SerializeOptions<T, any>
|
||||||
|
): boolean {
|
||||||
|
if (
|
||||||
|
typeof options.populate !== "boolean" &&
|
||||||
|
options.populate?.find(
|
||||||
|
(item) => item === propName || item.startsWith(propName + ".")
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof options.populate === "boolean") {
|
||||||
|
return options.populate
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Customer property filtering for the serialization which takes into account the parent entity to filter out circular references if configured for.
|
||||||
|
* @param prop
|
||||||
|
* @param meta
|
||||||
|
* @param options
|
||||||
|
* @param parent
|
||||||
|
*/
|
||||||
|
function filterEntityPropToSerialize(
|
||||||
|
prop: string,
|
||||||
|
meta: EntityMetadata,
|
||||||
|
options: SerializeOptions<object, any> & {
|
||||||
|
preventCircularRef?: boolean
|
||||||
|
} = {},
|
||||||
|
parent?: object
|
||||||
|
): boolean {
|
||||||
|
const isVisibleRes = isVisible(meta, prop, options)
|
||||||
|
if (options.preventCircularRef && isVisibleRes && parent) {
|
||||||
|
return (
|
||||||
|
// mapToPk would represent a foreign key and we want to keep them
|
||||||
|
meta.properties[prop].mapToPk ||
|
||||||
|
parent.constructor.name !== meta.properties[prop].type
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return isVisibleRes
|
||||||
|
}
|
||||||
|
|
||||||
|
export class EntitySerializer {
|
||||||
|
static serialize<T extends object, P extends string = never>(
|
||||||
|
entity: T,
|
||||||
|
options: SerializeOptions<T, P> & { preventCircularRef?: boolean } = {},
|
||||||
|
parent?: object
|
||||||
|
): EntityDTO<Loaded<T, P>> {
|
||||||
|
const wrapped = helper(entity)
|
||||||
|
const meta = wrapped.__meta
|
||||||
|
let contextCreated = false
|
||||||
|
|
||||||
|
if (!wrapped.__serializationContext.root) {
|
||||||
|
const root = new SerializationContext<T>()
|
||||||
|
SerializationContext.propagate(
|
||||||
|
root,
|
||||||
|
entity,
|
||||||
|
(meta, prop) =>
|
||||||
|
meta.properties[prop]?.reference !== ReferenceType.SCALAR
|
||||||
|
)
|
||||||
|
contextCreated = true
|
||||||
|
}
|
||||||
|
|
||||||
|
const root = wrapped.__serializationContext.root!
|
||||||
|
const ret = {} as EntityDTO<Loaded<T, P>>
|
||||||
|
const keys = new Set<string>(meta.primaryKeys)
|
||||||
|
Object.keys(entity).forEach((prop) => keys.add(prop))
|
||||||
|
const visited = root.visited.has(entity)
|
||||||
|
|
||||||
|
if (!visited) {
|
||||||
|
root.visited.add(entity)
|
||||||
|
}
|
||||||
|
|
||||||
|
;[...keys]
|
||||||
|
/** Medusa Custom properties filtering **/
|
||||||
|
.filter((prop) =>
|
||||||
|
filterEntityPropToSerialize(prop, meta, options, parent)
|
||||||
|
)
|
||||||
|
.map((prop) => {
|
||||||
|
const cycle = root.visit(meta.className, prop)
|
||||||
|
|
||||||
|
if (cycle && visited) {
|
||||||
|
return [prop, undefined]
|
||||||
|
}
|
||||||
|
|
||||||
|
const val = this.processProperty<T>(
|
||||||
|
prop as keyof T & string,
|
||||||
|
entity,
|
||||||
|
options
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!cycle) {
|
||||||
|
root.leave(meta.className, prop)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.skipNull && Utils.isPlainObject(val)) {
|
||||||
|
Utils.dropUndefinedProperties(val, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
return [prop, val]
|
||||||
|
})
|
||||||
|
.filter(
|
||||||
|
([, value]) =>
|
||||||
|
typeof value !== "undefined" && !(value === null && options.skipNull)
|
||||||
|
)
|
||||||
|
.forEach(
|
||||||
|
([prop, value]) =>
|
||||||
|
(ret[
|
||||||
|
this.propertyName(
|
||||||
|
meta,
|
||||||
|
prop as keyof T & string,
|
||||||
|
wrapped.__platform
|
||||||
|
)
|
||||||
|
] = value as T[keyof T & string])
|
||||||
|
)
|
||||||
|
|
||||||
|
if (contextCreated) {
|
||||||
|
root.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!wrapped.isInitialized()) {
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
// decorated getters
|
||||||
|
meta.props
|
||||||
|
.filter(
|
||||||
|
(prop) =>
|
||||||
|
prop.getter &&
|
||||||
|
prop.getterName === undefined &&
|
||||||
|
typeof entity[prop.name] !== "undefined" &&
|
||||||
|
isVisible(meta, prop.name, options)
|
||||||
|
)
|
||||||
|
.forEach(
|
||||||
|
(prop) =>
|
||||||
|
(ret[this.propertyName(meta, prop.name, wrapped.__platform)] =
|
||||||
|
this.processProperty(prop.name, entity, options))
|
||||||
|
)
|
||||||
|
|
||||||
|
// decorated get methods
|
||||||
|
meta.props
|
||||||
|
.filter(
|
||||||
|
(prop) =>
|
||||||
|
prop.getterName &&
|
||||||
|
(entity[prop.getterName] as unknown) instanceof Function &&
|
||||||
|
isVisible(meta, prop.name, options)
|
||||||
|
)
|
||||||
|
.forEach(
|
||||||
|
(prop) =>
|
||||||
|
(ret[this.propertyName(meta, prop.name, wrapped.__platform)] =
|
||||||
|
this.processProperty(
|
||||||
|
prop.getterName as keyof T & string,
|
||||||
|
entity,
|
||||||
|
options
|
||||||
|
))
|
||||||
|
)
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
private static propertyName<T>(
|
||||||
|
meta: EntityMetadata<T>,
|
||||||
|
prop: keyof T & string,
|
||||||
|
platform?: Platform
|
||||||
|
): string {
|
||||||
|
/* istanbul ignore next */
|
||||||
|
if (meta.properties[prop]?.serializedName) {
|
||||||
|
return meta.properties[prop].serializedName as keyof T & string
|
||||||
|
}
|
||||||
|
|
||||||
|
if (meta.properties[prop]?.primary && platform) {
|
||||||
|
return platform.getSerializedPrimaryKeyField(prop) as keyof T & string
|
||||||
|
}
|
||||||
|
|
||||||
|
return prop
|
||||||
|
}
|
||||||
|
|
||||||
|
private static processProperty<T extends object>(
|
||||||
|
prop: keyof T & string,
|
||||||
|
entity: T,
|
||||||
|
options: SerializeOptions<T, any>
|
||||||
|
): T[keyof T] | undefined {
|
||||||
|
const parts = prop.split(".")
|
||||||
|
prop = parts[0] as string & keyof T
|
||||||
|
const wrapped = helper(entity)
|
||||||
|
const property = wrapped.__meta.properties[prop]
|
||||||
|
const serializer = property?.serializer
|
||||||
|
|
||||||
|
// getter method
|
||||||
|
if ((entity[prop] as unknown) instanceof Function) {
|
||||||
|
const returnValue = (
|
||||||
|
entity[prop] as unknown as () => T[keyof T & string]
|
||||||
|
)()
|
||||||
|
if (!options.ignoreSerializers && serializer) {
|
||||||
|
return serializer(returnValue)
|
||||||
|
}
|
||||||
|
return returnValue
|
||||||
|
}
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
|
if (!options.ignoreSerializers && serializer) {
|
||||||
|
return serializer(entity[prop])
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Utils.isCollection(entity[prop])) {
|
||||||
|
return this.processCollection(prop, entity, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Utils.isEntity(entity[prop], true)) {
|
||||||
|
return this.processEntity(prop, entity, wrapped.__platform, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
|
if (property?.reference === ReferenceType.EMBEDDED) {
|
||||||
|
if (Array.isArray(entity[prop])) {
|
||||||
|
return (entity[prop] as object[]).map((item) =>
|
||||||
|
helper(item).toJSON()
|
||||||
|
) as T[keyof T]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Utils.isObject(entity[prop])) {
|
||||||
|
return helper(entity[prop]).toJSON() as T[keyof T]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const customType = property?.customType
|
||||||
|
|
||||||
|
if (customType) {
|
||||||
|
return customType.toJSON(entity[prop], wrapped.__platform)
|
||||||
|
}
|
||||||
|
|
||||||
|
return wrapped.__platform.normalizePrimaryKey(
|
||||||
|
entity[prop] as unknown as IPrimaryKey
|
||||||
|
) as unknown as T[keyof T]
|
||||||
|
}
|
||||||
|
|
||||||
|
private static extractChildOptions<T extends object, U extends object>(
|
||||||
|
options: SerializeOptions<T, any>,
|
||||||
|
prop: keyof T & string
|
||||||
|
): SerializeOptions<U, any> {
|
||||||
|
const extractChildElements = (items: string[]) => {
|
||||||
|
return items
|
||||||
|
.filter((field) => field.startsWith(`${prop}.`))
|
||||||
|
.map((field) => field.substring(prop.length + 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...options,
|
||||||
|
populate: Array.isArray(options.populate)
|
||||||
|
? extractChildElements(options.populate)
|
||||||
|
: options.populate,
|
||||||
|
exclude: Array.isArray(options.exclude)
|
||||||
|
? extractChildElements(options.exclude)
|
||||||
|
: options.exclude,
|
||||||
|
} as SerializeOptions<U, any>
|
||||||
|
}
|
||||||
|
|
||||||
|
private static processEntity<T extends object>(
|
||||||
|
prop: keyof T & string,
|
||||||
|
entity: T,
|
||||||
|
platform: Platform,
|
||||||
|
options: SerializeOptions<T, any>
|
||||||
|
): T[keyof T] | undefined {
|
||||||
|
const child = Reference.unwrapReference(entity[prop] as T)
|
||||||
|
const wrapped = helper(child)
|
||||||
|
const populated =
|
||||||
|
isPopulated(child, prop, options) && wrapped.isInitialized()
|
||||||
|
const expand = populated || options.forceObject || !wrapped.__managed
|
||||||
|
|
||||||
|
if (expand) {
|
||||||
|
return this.serialize(
|
||||||
|
child,
|
||||||
|
this.extractChildOptions(options, prop),
|
||||||
|
/** passing the entity as the parent for circular filtering **/
|
||||||
|
entity
|
||||||
|
) as T[keyof T]
|
||||||
|
}
|
||||||
|
|
||||||
|
return platform.normalizePrimaryKey(
|
||||||
|
wrapped.getPrimaryKey() as IPrimaryKey
|
||||||
|
) as T[keyof T]
|
||||||
|
}
|
||||||
|
|
||||||
|
private static processCollection<T extends object>(
|
||||||
|
prop: keyof T & string,
|
||||||
|
entity: T,
|
||||||
|
options: SerializeOptions<T, any>
|
||||||
|
): T[keyof T] | undefined {
|
||||||
|
const col = entity[prop] as unknown as Collection<T>
|
||||||
|
|
||||||
|
if (!col.isInitialized()) {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
return col.getItems(false).map((item) => {
|
||||||
|
if (isPopulated(item, prop, options)) {
|
||||||
|
return this.serialize(
|
||||||
|
item,
|
||||||
|
this.extractChildOptions(options, prop),
|
||||||
|
/** passing the entity as the parent for circular filtering **/
|
||||||
|
entity
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return helper(item).getPrimaryKey()
|
||||||
|
}) as unknown as T[keyof T]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const mikroOrmSerializer = <TOutput extends object>(
|
||||||
|
data: any,
|
||||||
|
options?: Parameters<typeof EntitySerializer.serialize>[1] & {
|
||||||
|
preventCircularRef?: boolean
|
||||||
|
}
|
||||||
|
): TOutput => {
|
||||||
|
options ??= {}
|
||||||
|
|
||||||
|
const data_ = (Array.isArray(data) ? data : [data]).filter(Boolean)
|
||||||
|
|
||||||
|
const forSerialization: unknown[] = []
|
||||||
|
const notForSerialization: unknown[] = []
|
||||||
|
|
||||||
|
data_.forEach((object) => {
|
||||||
|
if (object.__meta) {
|
||||||
|
return forSerialization.push(object)
|
||||||
|
}
|
||||||
|
|
||||||
|
return notForSerialization.push(object)
|
||||||
|
})
|
||||||
|
|
||||||
|
let result: any = forSerialization.map((entity) =>
|
||||||
|
EntitySerializer.serialize(entity, {
|
||||||
|
forceObject: true,
|
||||||
|
populate: true,
|
||||||
|
preventCircularRef: true,
|
||||||
|
...options,
|
||||||
|
} as SerializeOptions<any, any>)
|
||||||
|
) as TOutput[]
|
||||||
|
|
||||||
|
if (notForSerialization.length) {
|
||||||
|
result = result.concat(notForSerialization)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.isArray(data) ? result : result[0]
|
||||||
|
}
|
||||||
@@ -142,36 +142,3 @@ export const mikroOrmUpdateDeletedAtRecursively = async <
|
|||||||
await performCascadingSoftDeletion(manager, entity, value)
|
await performCascadingSoftDeletion(manager, entity, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const mikroOrmSerializer = async <TOutput extends object>(
|
|
||||||
data: any,
|
|
||||||
options?: any
|
|
||||||
): Promise<TOutput> => {
|
|
||||||
options ??= {}
|
|
||||||
|
|
||||||
const data_ = (Array.isArray(data) ? data : [data]).filter(Boolean)
|
|
||||||
|
|
||||||
const forSerialization: unknown[] = []
|
|
||||||
const notForSerialization: unknown[] = []
|
|
||||||
|
|
||||||
data_.forEach((object) => {
|
|
||||||
if (object.__meta) {
|
|
||||||
return forSerialization.push(object)
|
|
||||||
}
|
|
||||||
|
|
||||||
return notForSerialization.push(object)
|
|
||||||
})
|
|
||||||
|
|
||||||
const { serialize } = await import("@mikro-orm/core")
|
|
||||||
let result: any = serialize(forSerialization, {
|
|
||||||
forceObject: true,
|
|
||||||
populate: true,
|
|
||||||
...options,
|
|
||||||
}) as TOutput[]
|
|
||||||
|
|
||||||
if (notForSerialization.length) {
|
|
||||||
result = result.concat(notForSerialization)
|
|
||||||
}
|
|
||||||
|
|
||||||
return Array.isArray(data) ? result : result[0]
|
|
||||||
}
|
|
||||||
Reference in new issue
Block a user