chore(order): dml (#10292)

* ../../core/types/src/dml/index.ts

* ../../core/types/src/dml/index.ts

* fix: relationships mapping

* handle nullable foreign keys types

* handle nullable foreign keys types

* handle nullable foreign keys types

* continue to update product category repository

* fix all product category repositories issues

* fix product category service types

* fix product module service types

* fix product module service types

* fix repository template type

* refactor: use a singleton DMLToMikroORM factory instance

Since the MikroORM MetadataStorage is global, we will also have to turn DML
to MikroORM entities conversion use a global bucket as well

* refactor: update product module to use DML in tests

* wip: tests

* WIP product linkable fixes

* continue type fixing and start test fixing

* test: fix more tests

* fix repository

* fix pivot table computaion + fix mikro orm repository

* fix many to many management and configuration

* fix many to many management and configuration

* fix many to many management and configuration

* update product tag relation configuration

* Introduce experimental dml hooks to fix some issues with categories

* more fixes

* fix product tests

* add missing id prefixes

* fix product category handle management

* test: fix more failing tests

* test: make it all green

* test: fix breaking tests

* fix: build issues

* fix: build issues

* fix: more breaking tests

* refactor: fix issues after merge

* refactor: fix issues after merge

* refactor: surpress types error

* test: fix DML failing tests

* improve many to many inference + tests

* Wip fix columns from product entity

* remove product model before create hook and manage handle validation and transformation at the service level

* test: fix breaking unit tests

* fix: product module service to not update handle on product update

* fix define link and joiner config

* test: fix joiner config test

* test: fix joiner config test

* fix joiner config primary keys

* Fix joiner config builder

* Fix joiner config builder

* test: remove only modifier from test

* refactor: remove hooks usage from product collection

* refactor: remove hooks usage from product-option

* refactor: remove hooks usage for computing category handle

* refactor: remove hooks usage from productCategory model

* refactor: remove hooks from DML

* refactor: remove cruft

* order dml

* cleanup

* re add foerign key indexes

* wip

* chore: remove unused types

* wip

* changes

* rm raw

* autoincrement

* wip

* rel

* refactor: cleanup

* migration and models configuration adjustments

* cleanup

* number searchable

* fix random ordering

* fix

* test: fix product-category tests

* test: update breaking DML tests

* test: array assertion to not care about ordering

* fix: temporarily apply id ordering for products

* types

* wip

* WIP type improvements

* update order models

* partially fix types temporarely

* rel

* fix: recursive type issue

* improve type inference breaks

* improve type inference breaks

* update models

* rm nullable

* default value

* repository

* update default value handling

* fix unit tests

* WIP

* toMikroORM

* fix relations

* cascades

* fix

* experimental dml hooks

* rm migration

* serial

* nullable autoincrement

* fix model

* model changes

* fix one to one DML

* order test

* fix addresses

* fix unit tests

* Re align dml entity name inference

* update model table name config

* update model table name config

* revert

* update return relation

* WIP

* hasOne

* models

* fix

* model

* initial commit

* cart service

* order module

* utils unit test

* index engine

* changeset

* merge

* fix hasOne with fk

* update

* free text filter per entity

* tests

* prod category

* property string many to many

* fix big number

* link modules migration set names

* merge

* shipping option rules

* serializer

* unit test

* fix test mikro orm init

* fix test mikro orm init

* Maintain merge object properties

* fix test mikro orm init

* prevent unit test from connecting to db

* wip

* fix test

* fix test

* link test

* schema

* models

* auto increment

* hook

* model hooks

* order

* wip

* orm version

* request return field

* fix return configuration on order model

* workflows

* core flows

* unit test

* test

* base repo

* test

* base repo

* test fix

* inventory move

* locking inventory

* test

* free text fix

* rm timeout mock

* migrate fulfillment values

* v6.4.3

* cleanup

* link-modules update sql

* revert test

* remove fake timers

---------

Co-authored-by: adrien2p <adrien.deperetti@gmail.com>
Co-authored-by: Harminder Virk <virk.officials@gmail.com>
Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
authored and GitHub committed 2025-01-21 08:04:47 -05:00
1 parent 05c8a67d8e
commit cc73802ab3
178 files changed
+5731 -7120

No files matched your search

@@ -63,7 +63,7 @@ class ProductOptionValue {
fieldName: "option_id",
mapToPk: true,
nullable: true,
onDelete: "cascade",
deleteRule: "cascade",
})
option_id: string | null
@@ -88,7 +88,7 @@ class ProductVariant {
@ManyToOne(() => Product, {
columnType: "text",
nullable: true,
onDelete: "cascade",
deleteRule: "cascade",
fieldName: "product_id",
mapToPk: true,
})
@@ -1,4 +1,5 @@
import { Entity, MikroORM, OnInit, Property } from "@mikro-orm/core"
import { defineConfig } from "@mikro-orm/postgresql"
import { BaseEntity } from "../base-entity"
describe("BaseEntity", () => {
@@ -10,11 +11,15 @@ describe("BaseEntity", () => {
}
}
const orm = await MikroORM.init({
entities: [Entity1],
dbName: "test",
type: "postgresql",
})
const orm = await MikroORM.init(
defineConfig({
entities: [Entity1],
dbName: "test",
user: "postgres",
password: "",
connect: false,
})
)
const manager = orm.em.fork()
const entity1 = manager.create(Entity1, {})
@@ -28,11 +33,15 @@ describe("BaseEntity", () => {
@Entity()
class Entity1 extends BaseEntity {}
const orm = await MikroORM.init({
entities: [Entity1],
dbName: "test",
type: "postgresql",
})
const orm = await MikroORM.init(
defineConfig({
entities: [Entity1],
dbName: "test",
user: "postgres",
password: "",
connect: false,
})
)
const manager = orm.em.fork()
const entity1 = manager.create(Entity1, {})
@@ -52,11 +61,15 @@ describe("BaseEntity", () => {
@Entity()
class ProductOptionValue extends BaseEntity {}
const orm = await MikroORM.init({
entities: [ProductModel, ProductCategoryEntity, ProductOptionValue],
dbName: "test",
type: "postgresql",
})
const orm = await MikroORM.init(
defineConfig({
entities: [ProductModel, ProductCategoryEntity, ProductOptionValue],
dbName: "test",
user: "postgres",
password: "",
connect: false,
})
)
const manager = orm.em.fork()
@@ -105,11 +118,15 @@ describe("BaseEntity", () => {
}
}
const orm = await MikroORM.init({
entities: [ProductModel, ProductCategoryEntity, ProductOptionValue],
dbName: "test",
type: "postgresql",
})
const orm = await MikroORM.init(
defineConfig({
entities: [ProductModel, ProductCategoryEntity, ProductOptionValue],
dbName: "test",
user: "postgres",
password: "",
connect: false,
})
)
const manager = orm.em.fork()
@@ -1,5 +1,6 @@
import { BigNumberRawValue } from "@medusajs/types"
import { Entity, MikroORM, PrimaryKey } from "@mikro-orm/core"
import { defineConfig } from "@mikro-orm/postgresql"
import { BigNumber } from "../../../totals/big-number"
import { MikroOrmBigNumberProperty } from "../big-number-field"
@@ -23,11 +24,15 @@ describe("@MikroOrmBigNumberProperty", () => {
let orm!: MikroORM
beforeEach(async () => {
orm = await MikroORM.init({
entities: [TestAmount],
dbName: "test",
type: "postgresql",
})
orm = await MikroORM.init(
defineConfig({
entities: [TestAmount],
dbName: "test",
user: "postgres",
password: "",
connect: false,
})
)
})
afterEach(async () => {
@@ -51,9 +56,7 @@ describe("@MikroOrmBigNumberProperty", () => {
try {
;(testAmount as any).amount = null
} catch (e) {
expect(e.message).toEqual(
"Invalid BigNumber value: null. Should be one of: string, number, BigNumber (bignumber.js), BigNumberRawValue"
)
expect(e.message).toEqual("Cannot set value null for amount.")
}
testAmount.nullable_amount = null
@@ -1,4 +1,5 @@
import { MikroORM } from "@mikro-orm/core"
import { defineConfig } from "@mikro-orm/postgresql"
import { SearchableEntity1, SearchableEntity2 } from "../__fixtures__/utils"
import { mikroOrmFreeTextSearchFilterOptionsFactory } from "../mikro-orm-free-text-search-filter"
@@ -6,21 +7,23 @@ describe("mikroOrmFreeTextSearchFilterOptionsFactory", () => {
let orm
beforeEach(async () => {
orm = await MikroORM.init({
entities: [SearchableEntity1, SearchableEntity2],
dbName: "test",
type: "postgresql",
})
orm = await MikroORM.init(
defineConfig({
entities: [SearchableEntity1, SearchableEntity2],
user: "postgres",
password: "",
dbName: "test",
connect: false,
})
)
})
it("should return a filter function that filters entities based on the free text search value", async () => {
const entityManager = orm.em.fork()
const freeTextSearchValue = "search"
const models = [SearchableEntity1, SearchableEntity2]
let filterConstraints = mikroOrmFreeTextSearchFilterOptionsFactory(
models
SearchableEntity1.name
).cond(
{
value: freeTextSearchValue,
@@ -51,7 +54,9 @@ describe("mikroOrmFreeTextSearchFilterOptionsFactory", () => {
],
})
filterConstraints = mikroOrmFreeTextSearchFilterOptionsFactory(models).cond(
filterConstraints = mikroOrmFreeTextSearchFilterOptionsFactory(
SearchableEntity2.name
).cond(
{
value: freeTextSearchValue,
fromEntity: SearchableEntity2.name,
@@ -1,4 +1,5 @@
import { MikroORM } from "@mikro-orm/core"
import { defineConfig } from "@mikro-orm/postgresql"
import {
Entity1WithUnDecoratedProp,
Entity2WithUnDecoratedProp,
@@ -11,18 +12,22 @@ import { mikroOrmSerializer } from "../mikro-orm-serializer"
describe("mikroOrmSerializer", () => {
beforeEach(async () => {
await MikroORM.init({
entities: [
Entity1WithUnDecoratedProp,
Entity2WithUnDecoratedProp,
Product,
ProductOption,
ProductOptionValue,
ProductVariant,
],
dbName: "test",
type: "postgresql",
})
await MikroORM.init(
defineConfig({
entities: [
Entity1WithUnDecoratedProp,
Entity2WithUnDecoratedProp,
Product,
ProductOption,
ProductOptionValue,
ProductVariant,
],
user: "postgres",
password: "",
dbName: "test",
connect: false,
})
)
})
it("should serialize an entity", async () => {
@@ -1,7 +1,7 @@
import { CustomTsMigrationGenerator } from "../mikro-orm-create-connection"
function unwrapSql(sql: string) {
return sql.match(/this.addSql\('(.*?)'\)/)?.[1]
return sql.toString().match(/this.addSql\(`(.*?)`\)/)?.[1]
}
describe("CustomTsMigrationGenerator", () => {
@@ -26,20 +26,24 @@ export function MikroOrmBigNumberProperty(
set(value: BigNumberInput) {
if (options?.nullable && !isPresent(value)) {
this.__helper.__data[columnName] = null
this.__helper.__data[rawColumnName]
this.__helper.__data[rawColumnName] = null
this[rawColumnName] = null
} else {
let bigNumber: BigNumber
if (value instanceof BigNumber) {
bigNumber = value
} else if (this[rawColumnName]) {
const precision = this[rawColumnName].precision
bigNumber = new BigNumber(value, {
precision,
})
} else {
bigNumber = new BigNumber(value)
try {
if (value instanceof BigNumber) {
bigNumber = value
} else if (this[rawColumnName]) {
const precision = this[rawColumnName].precision
bigNumber = new BigNumber(value, {
precision,
})
} else {
bigNumber = new BigNumber(value)
}
} catch (e) {
throw new Error(`Cannot set value ${value} for ${columnName}.`)
}
const raw = bigNumber.raw!
@@ -80,6 +84,7 @@ export function MikroOrmBigNumberProperty(
type: "any",
columnType: "numeric",
trackChanges: false,
runtimeType: "any",
...options,
})(target, columnName)
}
@@ -1,5 +1,5 @@
const DB_HOST = process.env.DB_HOST ?? "localhost"
const DB_USERNAME = process.env.DB_USERNAME ?? ""
const DB_USERNAME = process.env.DB_USERNAME ?? "postgres"
const DB_PASSWORD = process.env.DB_PASSWORD
export const pgGodCredentials = {
@@ -1,3 +1,4 @@
import { BigNumberRawValue } from "@medusajs/types"
import {
BeforeCreate,
Collection,
@@ -13,11 +14,11 @@ import {
Unique,
wrap,
} from "@mikro-orm/core"
import { mikroOrmBaseRepositoryFactory } from "../../mikro-orm-repository"
import { defineConfig } from "@mikro-orm/postgresql"
import BigNumber from "bignumber.js"
import { dropDatabase } from "pg-god"
import { MikroOrmBigNumberProperty } from "../../big-number-field"
import BigNumber from "bignumber.js"
import { BigNumberRawValue } from "@medusajs/types"
import { mikroOrmBaseRepositoryFactory } from "../../mikro-orm-repository"
import { getDatabaseURL, pgGodCredentials } from "../__fixtures__/database"
const dbName = "mikroorm-integration-1"
@@ -78,7 +79,7 @@ class Entity2 {
nullable: true,
mapToPk: true,
fieldName: "entity1_id",
onDelete: "set null",
deleteRule: "set null",
})
entity1_id: string
@@ -143,11 +144,12 @@ describe("mikroOrmRepository", () => {
pgGodCredentials
)
orm = await MikroORM.init({
entities: [Entity1, Entity2],
clientUrl: getDatabaseURL(dbName),
type: "postgresql",
})
orm = await MikroORM.init(
defineConfig({
entities: [Entity1, Entity2],
clientUrl: getDatabaseURL(dbName),
})
)
const generator = orm.getSchemaGenerator()
await generator.ensureDatabase()
@@ -1,6 +1,8 @@
import { mikroOrmUpdateDeletedAtRecursively } from "../../utils"
import { MikroORM } from "@mikro-orm/core"
import { SqlEntityManager } from "@mikro-orm/postgresql"
import { defineConfig, SqlEntityManager } from "@mikro-orm/postgresql"
import { dropDatabase } from "pg-god"
import { mikroOrmUpdateDeletedAtRecursively } from "../../utils"
import { getDatabaseURL, pgGodCredentials } from "../__fixtures__/database"
import {
DeepRecursiveEntity1,
DeepRecursiveEntity2,
@@ -12,8 +14,6 @@ import {
RecursiveEntity1,
RecursiveEntity2,
} from "../__fixtures__/utils"
import { dropDatabase } from "pg-god"
import { getDatabaseURL, pgGodCredentials } from "../__fixtures__/database"
const dbName = "mikroorm-utils-integration-1"
@@ -38,21 +38,22 @@ describe("mikroOrmUpdateDeletedAtRecursively", () => {
pgGodCredentials
)
orm = await MikroORM.init({
entities: [
Entity1,
Entity2,
RecursiveEntity1,
RecursiveEntity2,
DeepRecursiveEntity1,
DeepRecursiveEntity2,
DeepRecursiveEntity3,
DeepRecursiveEntity4,
InternalCircularDependencyEntity1,
],
clientUrl: getDatabaseURL(dbName),
type: "postgresql",
})
orm = await MikroORM.init(
defineConfig({
entities: [
Entity1,
Entity2,
RecursiveEntity1,
RecursiveEntity2,
DeepRecursiveEntity1,
DeepRecursiveEntity2,
DeepRecursiveEntity3,
DeepRecursiveEntity4,
InternalCircularDependencyEntity1,
],
clientUrl: getDatabaseURL(dbName),
})
)
const generator = orm.getSchemaGenerator()
await generator.ensureDatabase()
@@ -45,34 +45,38 @@ export async function mikroOrmCreateConnection(
schema = database.connection.context?.client?.config?.searchPath
}
const { MikroORM } = await import("@mikro-orm/postgresql")
return await MikroORM.init({
discovery: { disableDynamicFileAccess: true, warnWhenNoEntities: false },
entities,
debug: database.debug ?? process.env.NODE_ENV?.startsWith("dev") ?? false,
baseDir: process.cwd(),
clientUrl,
schema,
driverOptions,
tsNode: process.env.APP_ENV === "development",
type: "postgresql",
filters: database.filters ?? {},
migrations: {
disableForeignKeys: false,
path: pathToMigrations,
generator: CustomTsMigrationGenerator,
silent: !(
database.debug ??
process.env.NODE_ENV?.startsWith("dev") ??
false
),
},
schemaGenerator: {
disableForeignKeys: false,
},
pool: {
min: 2,
...database.pool,
},
})
const { MikroORM, defineConfig } = await import("@mikro-orm/postgresql")
return await MikroORM.init(
defineConfig({
discovery: { disableDynamicFileAccess: true, warnWhenNoEntities: false },
entities,
debug: database.debug ?? process.env.NODE_ENV?.startsWith("dev") ?? false,
baseDir: process.cwd(),
clientUrl,
schema,
driverOptions,
tsNode: process.env.APP_ENV === "development",
filters: database.filters ?? {},
assign: {
convertCustomTypes: true,
},
migrations: {
disableForeignKeys: false,
path: pathToMigrations,
generator: CustomTsMigrationGenerator,
silent: !(
database.debug ??
process.env.NODE_ENV?.startsWith("dev") ??
false
),
},
schemaGenerator: {
disableForeignKeys: false,
},
pool: {
min: 2,
...database.pool,
},
})
)
}
@@ -4,23 +4,20 @@ import type {
FindOneOptions,
FindOptions,
} from "@mikro-orm/core"
import { EntityMetadata, EntitySchema, ReferenceType } from "@mikro-orm/core"
import { EntityMetadata, ReferenceKind } from "@mikro-orm/core"
import { SqlEntityManager } from "@mikro-orm/postgresql"
export const FreeTextSearchFilterKey = "freeTextSearch"
export const FreeTextSearchFilterKeyPrefix = "freeTextSearch_"
interface FilterArgument {
value: string
fromEntity: string
}
function getEntityProperties(entity: EntityClass<any> | EntitySchema): {
function getEntityProperties(metadata: EntityMetadata<any>): {
[key: string]: EntityProperty<any>
} {
return (
(entity as EntityClass<any>)?.prototype.__meta?.properties ??
(entity as EntitySchema).meta?.properties
)
return metadata.properties
}
function retrieveRelationsConstraints(
@@ -31,7 +28,7 @@ function retrieveRelationsConstraints(
type: string
name: string
},
models: (EntityClass<any> | EntitySchema)[],
metadata: EntityMetadata<any>,
searchValue: string,
visited: Set<string> = new Set(),
shouldStop: boolean = false
@@ -46,13 +43,12 @@ function retrieveRelationsConstraints(
const relationFreeTextSearchWhere: any = []
const relationClass = models.find((m) => m.name === relation.type)!
const relationProperties = getEntityProperties(relationClass)
const relationProperties = getEntityProperties(metadata)
for (const propertyConfiguration of Object.values(relationProperties)) {
if (
!(propertyConfiguration as any).searchable ||
propertyConfiguration.reference !== ReferenceType.SCALAR
propertyConfiguration.kind !== ReferenceKind.SCALAR
) {
continue
}
@@ -69,9 +65,7 @@ function retrieveRelationsConstraints(
})
}
const innerRelations: EntityProperty[] =
(relationClass as EntityClass<any>)?.prototype.__meta?.relations ??
(relationClass as EntitySchema).meta?.relations
const innerRelations: EntityProperty[] = metadata.relations
for (const innerRelation of innerRelations) {
const branchVisited = new Set(Array.from(visited))
@@ -108,7 +102,7 @@ function retrieveRelationsConstraints(
mapToPk: innerRelation.mapToPk,
type: innerRelation.type,
},
models,
innerRelation.targetMeta!,
searchValue,
branchVisited,
isSelfCircularDependency
@@ -128,10 +122,9 @@ function retrieveRelationsConstraints(
return relationFreeTextSearchWhere
}
export const mikroOrmFreeTextSearchFilterOptionsFactory = (
models: (EntityClass<any> | EntitySchema)[]
) => {
export const mikroOrmFreeTextSearchFilterOptionsFactory = (model: string) => {
return {
name: FreeTextSearchFilterKeyPrefix + model,
cond: (
freeTextSearchArgs: FilterArgument,
operation: string,
@@ -144,7 +137,7 @@ export const mikroOrmFreeTextSearchFilterOptionsFactory = (
return {}
}
const { value, fromEntity } = freeTextSearchArgs
const { value } = freeTextSearchArgs
if (options?.visited?.size) {
/**
@@ -158,17 +151,17 @@ export const mikroOrmFreeTextSearchFilterOptionsFactory = (
}
}
const entityMetadata = manager.getDriver().getMetadata().get(fromEntity)
const entityMetadata = manager.getDriver().getMetadata().get(model)
const freeTextSearchWhere = retrieveRelationsConstraints(
{
targetMeta: entityMetadata,
mapToPk: false,
searchable: true,
type: fromEntity,
type: model,
name: entityMetadata.name!,
},
models,
entityMetadata,
value
)
@@ -180,8 +173,5 @@ export const mikroOrmFreeTextSearchFilterOptionsFactory = (
$or: freeTextSearchWhere,
}
},
default: true,
args: false,
entity: models.map((m) => m.name) as string[],
}
}
@@ -20,7 +20,7 @@ import {
LoadStrategy,
FilterQuery as MikroFilterQuery,
FindOptions as MikroOptions,
ReferenceType,
ReferenceKind,
} from "@mikro-orm/core"
import { SqlEntityManager } from "@mikro-orm/postgresql"
import {
@@ -245,24 +245,6 @@ export class MikroOrmBaseRepository<const T extends object = object>
return [entities, softDeletedEntitiesMap]
}
applyFreeTextSearchFilters<T>(
findOptions: DAL.FindOptions<T & { q?: string }>,
retrieveConstraintsToApply: (q: string) => any[]
): void {
if (!("q" in findOptions.where) || !findOptions.where.q) {
delete findOptions.where.q
return
}
const q = findOptions.where.q as string
delete findOptions.where.q
findOptions.where = {
$and: [findOptions.where, { $or: retrieveConstraintsToApply(q) }],
} as unknown as DAL.FindOptions<T & { q?: string }>["where"]
}
}
export class MikroOrmBaseTreeRepository<
@@ -391,7 +373,7 @@ export function mikroOrmBaseRepositoryFactory<const T extends object>(
const relation = relations.find((relation) => relation.name === key)
const shouldInit =
relation &&
relation.reference === ReferenceType.MANY_TO_MANY &&
relation.kind === ReferenceKind.MANY_TO_MANY &&
Array.isArray(update[key]) &&
!update[key].length
@@ -434,7 +416,9 @@ export function mikroOrmBaseRepositoryFactory<const T extends object>(
await this.initManyToManyToDetachAllItemsIfNeeded(data, context)
data.map((_, index) => {
manager.assign(data[index].entity, data[index].update)
manager.assign(data[index].entity, data[index].update, {
mergeObjectProperties: true,
})
manager.persist(data[index].entity)
})
@@ -578,7 +562,7 @@ export function mikroOrmBaseRepositoryFactory<const T extends object>(
const existingEntity = existingEntitiesMap.get(key)
if (existingEntity) {
const updatedType = manager.assign(existingEntity, data_)
updatedEntities.push(updatedType)
updatedEntities.push(updatedType as any)
} else {
const newEntity = manager.create(this.entity, data_)
createdEntities.push(newEntity as InferRepositoryReturnType<T>)
@@ -698,8 +682,8 @@ export function mikroOrmBaseRepositoryFactory<const T extends object>(
// TODO: Handle ONE_TO_ONE
// One to one and Many to one are handled outside of the assignment as they need to happen before the main entity is created
if (
relation.reference === ReferenceType.ONE_TO_ONE ||
relation.reference === ReferenceType.MANY_TO_ONE
relation.kind === ReferenceKind.ONE_TO_ONE ||
relation.kind === ReferenceKind.MANY_TO_ONE
) {
return
}
@@ -765,7 +749,7 @@ export function mikroOrmBaseRepositoryFactory<const T extends object>(
return this.getEntityWithId(manager, relation.type, normalizedItem)
})
if (relation.reference === ReferenceType.MANY_TO_MANY) {
if (relation.kind === ReferenceKind.MANY_TO_MANY) {
const currentPivotColumn = relation.inverseJoinColumns[0]
const parentPivotColumn = relation.joinColumns[0]
@@ -805,7 +789,7 @@ export function mikroOrmBaseRepositoryFactory<const T extends object>(
return { entities: normalizedData, performedActions }
}
if (relation.reference === ReferenceType.ONE_TO_MANY) {
if (relation.kind === ReferenceKind.ONE_TO_MANY) {
const joinColumns =
relation.targetMeta?.properties[relation.mappedBy]?.joinColumns
@@ -815,7 +799,7 @@ export function mikroOrmBaseRepositoryFactory<const T extends object>(
joinColumnsConstraints[joinColumn] = data[referencedColumnName]
})
const toDeleteEntities = await manager.find<any>(
const toDeleteEntities = await manager.find<any, any, "id">(
relation.type,
{
...joinColumnsConstraints,
@@ -869,7 +853,7 @@ export function mikroOrmBaseRepositoryFactory<const T extends object>(
}
// If it is a many-to-one we ensure the ID is set for when we want to set/unset an association
if (relation.reference === ReferenceType.MANY_TO_ONE) {
if (relation.kind === ReferenceKind.MANY_TO_ONE) {
if (originalData === null) {
entryCopy[relation.joinColumns[0]] = null
return null
@@ -927,7 +911,7 @@ export function mikroOrmBaseRepositoryFactory<const T extends object>(
.filter(
([_, propDef]: any) =>
propDef.persist === false &&
propDef.reference === ReferenceType.MANY_TO_ONE
propDef.kind === ReferenceKind.MANY_TO_ONE
)
.forEach(([key]) => {
delete resp[key]
@@ -7,36 +7,45 @@ import {
Loaded,
Platform,
Reference,
ReferenceType,
ReferenceKind,
SerializationContext,
SerializeOptions,
Utils,
} from "@mikro-orm/core"
type CustomSerializeOptions<T, P = any> = SerializeOptions<T, P & string> & {
preventCircularRef?: boolean
populate?: [keyof T][] | boolean
}
function isVisible<T extends object>(
meta: EntityMetadata<T>,
propName: string,
options: SerializeOptions<T, any> & { preventCircularRef?: boolean } = {}
options: CustomSerializeOptions<T> = {}
): boolean {
if (options.populate === true) {
return options.populate
return true
}
if (
Array.isArray(options.populate) &&
options.populate?.find(
options.exclude?.find((item) => item === propName)
) {
return false
}
if (
Array.isArray(options.populate) &&
(options.populate?.find(
(item) => item === propName || item.startsWith(propName + ".")
)
) ||
options.populate.includes("*"))
) {
return true
}
if (options.exclude?.find((item) => item === propName)) {
return false
}
const prop = meta.properties[propName]
const visible = prop && !prop.hidden
const visible = (prop && !prop.hidden) || prop === undefined // allow unknown properties
const prefixed = prop && !prop.primary && propName.startsWith("_") // ignore prefixed properties, if it's not a PK
return visible && !prefixed
@@ -45,13 +54,14 @@ function isVisible<T extends object>(
function isPopulated<T extends object>(
entity: T,
propName: string,
options: SerializeOptions<T, any>
options: CustomSerializeOptions<T>
): boolean {
if (
typeof options.populate !== "boolean" &&
options.populate?.find(
Array.isArray(options.populate) &&
(options.populate?.find(
(item) => item === propName || item.startsWith(propName + ".")
)
) ||
options.populate.includes("*"))
) {
return true
}
@@ -78,9 +88,7 @@ function filterEntityPropToSerialize({
}: {
propName: string
meta: EntityMetadata
options: SerializeOptions<object, any> & {
preventCircularRef?: boolean
}
options: CustomSerializeOptions<any>
parents?: string[]
}): boolean {
parents ??= []
@@ -92,7 +100,7 @@ function filterEntityPropToSerialize({
prop &&
options.preventCircularRef &&
isVisibleRes &&
prop.reference !== ReferenceType.SCALAR
prop.kind !== ReferenceKind.SCALAR
) {
// mapToPk would represent a foreign key and we want to keep them
if (!!prop.mapToPk) {
@@ -108,7 +116,7 @@ function filterEntityPropToSerialize({
export class EntitySerializer {
static serialize<T extends object, P extends string = never>(
entity: T,
options: SerializeOptions<T, P> & { preventCircularRef?: boolean } = {},
options: CustomSerializeOptions<T, P> = {},
parents: string[] = []
): EntityDTO<Loaded<T, P>> {
const parents_ = Array.from(new Set(parents))
@@ -118,12 +126,11 @@ export class EntitySerializer {
let contextCreated = false
if (!wrapped.__serializationContext.root) {
const root = new SerializationContext<T>()
const root = new SerializationContext<T>({} as any)
SerializationContext.propagate(
root,
entity,
(meta, prop) =>
meta.properties[prop]?.reference !== ReferenceType.SCALAR
(meta, prop) => meta.properties[prop]?.kind !== ReferenceKind.SCALAR
)
contextCreated = true
}
@@ -238,25 +245,25 @@ export class EntitySerializer {
private static propertyName<T>(
meta: EntityMetadata<T>,
prop: keyof T & string,
prop: string,
platform?: Platform
): string {
/* istanbul ignore next */
if (meta.properties[prop]?.serializedName) {
return meta.properties[prop].serializedName as keyof T & string
return meta.properties[prop].serializedName as string
}
if (meta.properties[prop]?.primary && platform) {
return platform.getSerializedPrimaryKeyField(prop) as keyof T & string
return platform.getSerializedPrimaryKeyField(prop) as string
}
return prop
}
private static processProperty<T extends object>(
prop: keyof T & string,
prop: string,
entity: T,
options: SerializeOptions<T, any>,
options: CustomSerializeOptions<T>,
parents: string[] = []
): T[keyof T] | undefined {
const parents_ = [...parents, entity.constructor.name]
@@ -284,12 +291,17 @@ export class EntitySerializer {
}
if (Utils.isCollection(entity[prop])) {
return this.processCollection(prop, entity, options, parents_)
return this.processCollection(
prop as keyof T & string,
entity,
options,
parents_
)
}
if (Utils.isEntity(entity[prop], true)) {
return this.processEntity(
prop,
prop as keyof T & string,
entity,
wrapped.__platform,
options,
@@ -298,7 +310,7 @@ export class EntitySerializer {
}
/* istanbul ignore next */
if (property?.reference === ReferenceType.EMBEDDED) {
if (property?.reference === ReferenceKind.EMBEDDED) {
if (Array.isArray(entity[prop])) {
return (entity[prop] as object[]).map((item) =>
helper(item).toJSON()
@@ -322,9 +334,9 @@ export class EntitySerializer {
}
private static extractChildOptions<T extends object, U extends object>(
options: SerializeOptions<T, any>,
options: CustomSerializeOptions<T>,
prop: keyof T & string
): SerializeOptions<U, any> {
): CustomSerializeOptions<U> {
const extractChildElements = (items: string[]) => {
return items
.filter((field) => field.startsWith(`${prop}.`))
@@ -333,20 +345,22 @@ export class EntitySerializer {
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>
populate:
Array.isArray(options.populate) && !options.populate.includes("*")
? extractChildElements(options.populate as unknown as string[])
: options.populate,
exclude:
Array.isArray(options.exclude) && !options.exclude.includes("*")
? extractChildElements(options.exclude)
: options.exclude,
} as CustomSerializeOptions<U>
}
private static processEntity<T extends object>(
prop: keyof T & string,
entity: T,
platform: Platform,
options: SerializeOptions<T, any>,
options: CustomSerializeOptions<T>,
parents: string[] = []
): T[keyof T] | undefined {
const parents_ = [...parents, entity.constructor.name]
@@ -373,7 +387,7 @@ export class EntitySerializer {
private static processCollection<T extends object>(
prop: keyof T & string,
entity: T,
options: SerializeOptions<T, any>,
options: CustomSerializeOptions<T>,
parents: string[] = []
): T[keyof T] | undefined {
const parents_ = [...parents, entity.constructor.name]
@@ -401,6 +415,7 @@ export const mikroOrmSerializer = <TOutput extends object>(
data: any,
options?: Parameters<typeof EntitySerializer.serialize>[1] & {
preventCircularRef?: boolean
populate?: string[] | boolean
}
): Promise<TOutput> => {
return new Promise<TOutput>((resolve) => {
@@ -422,10 +437,11 @@ export const mikroOrmSerializer = <TOutput extends object>(
let result: any = forSerialization.map((entity) =>
EntitySerializer.serialize(entity, {
forceObject: true,
populate: true,
populate: ["*"],
preventCircularRef: true,
...options,
} as SerializeOptions<any, any>)
} as CustomSerializeOptions<any>)
) as TOutput[]
if (notForSerialization.length) {