fix: DML relation management for many to one relation ship foreign keys (#7790)
FIXES CORE-2369 cc @thetutlage Co-authored-by: Oli Juhl <59018053+olivermrbl@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
25210369d9
commit
34c44078e7
@@ -97,17 +97,36 @@ export interface EntityConstructor<Props> extends Function {
|
||||
new (): Props
|
||||
}
|
||||
|
||||
/**
|
||||
* From a IDmlEntity, infer the foreign keys name and type for belongsTo relation meaning hasOne and ManyToOne
|
||||
*/
|
||||
export type InferForeignKeys<T> = T extends IDmlEntity<infer Schema>
|
||||
? {
|
||||
[K in keyof Schema as Schema[K] extends RelationshipType<any>
|
||||
? Schema[K]["type"] extends "belongsTo"
|
||||
? `${K & string}_id`
|
||||
: K
|
||||
: K]: Schema[K] extends RelationshipType<infer R>
|
||||
? Schema[K]["type"] extends "belongsTo"
|
||||
? string
|
||||
: Schema[K]
|
||||
: Schema[K]
|
||||
}
|
||||
: never
|
||||
|
||||
/**
|
||||
* Helper to infer the schema type of a DmlEntity
|
||||
*/
|
||||
export type Infer<T> = T extends IDmlEntity<infer Schema>
|
||||
? EntityConstructor<{
|
||||
[K in keyof Schema]: Schema[K]["$dataType"] extends () => infer R
|
||||
? Infer<R>
|
||||
: Schema[K]["$dataType"] extends (() => infer R) | null
|
||||
? Infer<R> | null
|
||||
: Schema[K]["$dataType"]
|
||||
}>
|
||||
? EntityConstructor<
|
||||
{
|
||||
[K in keyof Schema]: Schema[K]["$dataType"] extends () => infer R
|
||||
? Infer<R>
|
||||
: Schema[K]["$dataType"] extends (() => infer R) | null
|
||||
? Infer<R> | null
|
||||
: Schema[K]["$dataType"]
|
||||
} & InferForeignKeys<T>
|
||||
>
|
||||
: never
|
||||
|
||||
/**
|
||||
@@ -133,7 +152,7 @@ export type EntityCascades<Relationships> = {
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to infer the instance type of a DmlEntity once converted as an Entity
|
||||
* Helper to infer the instance type of a IDmlEntity once converted as an Entity
|
||||
*/
|
||||
export type InferTypeOf<T extends IDmlEntity<any>> = InstanceType<Infer<T>>
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { dirname, join } from "path"
|
||||
import {
|
||||
promises,
|
||||
constants,
|
||||
type Dirent,
|
||||
type MakeDirectoryOptions,
|
||||
promises,
|
||||
type RmOptions,
|
||||
type StatOptions,
|
||||
type WriteFileOptions,
|
||||
type MakeDirectoryOptions,
|
||||
} from "fs"
|
||||
|
||||
const { rm, stat, mkdir, access, readdir, readFile, writeFile } = promises
|
||||
@@ -31,7 +31,7 @@ export class FileSystem {
|
||||
* Cleanup directory
|
||||
*/
|
||||
async cleanup(options?: RmOptions) {
|
||||
return rm(this.basePath, {
|
||||
return await rm(this.basePath, {
|
||||
recursive: true,
|
||||
maxRetries: 10,
|
||||
force: true,
|
||||
@@ -61,7 +61,7 @@ export class FileSystem {
|
||||
* Remove a file
|
||||
*/
|
||||
async remove(filePath: string, options?: RmOptions) {
|
||||
return rm(this.makePath(filePath), {
|
||||
return await rm(this.makePath(filePath), {
|
||||
recursive: true,
|
||||
force: true,
|
||||
maxRetries: 2,
|
||||
@@ -103,7 +103,7 @@ export class FileSystem {
|
||||
* Returns file contents
|
||||
*/
|
||||
async contents(filePath: string) {
|
||||
return readFile(this.makePath(filePath), "utf-8")
|
||||
return await readFile(this.makePath(filePath), "utf-8")
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -139,14 +139,14 @@ export class FileSystem {
|
||||
async createJson(filePath: string, contents: any, options?: JSONFileOptions) {
|
||||
if (options && typeof options === "object") {
|
||||
const { replacer, spaces, ...rest } = options
|
||||
return this.create(
|
||||
return await this.create(
|
||||
filePath,
|
||||
JSON.stringify(contents, replacer, spaces),
|
||||
rest
|
||||
)
|
||||
}
|
||||
|
||||
return this.create(filePath, JSON.stringify(contents), options)
|
||||
return await this.create(filePath, JSON.stringify(contents), options)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { EntityConstructor } from "@medusajs/types"
|
||||
import { MetadataStorage } from "@mikro-orm/core"
|
||||
import { expectTypeOf } from "expect-type"
|
||||
import { DmlEntity } from "../entity"
|
||||
import { EntityBuilder } from "../entity-builder"
|
||||
import { model } from "../entity-builder"
|
||||
import {
|
||||
createMikrORMEntity,
|
||||
toMikroOrmEntities,
|
||||
toMikroORMEntity,
|
||||
} from "../helpers/create-mikro-orm-entity"
|
||||
import { EntityConstructor } from "../types"
|
||||
|
||||
describe("Entity builder", () => {
|
||||
beforeEach(() => {
|
||||
@@ -15,7 +16,6 @@ describe("Entity builder", () => {
|
||||
|
||||
describe("Entity builder | properties", () => {
|
||||
test("should identify a DML entity correctly", () => {
|
||||
const model = new EntityBuilder()
|
||||
const user = model.define("user", {
|
||||
id: model.number(),
|
||||
username: model.text(),
|
||||
@@ -30,7 +30,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("define an entity", () => {
|
||||
const model = new EntityBuilder()
|
||||
const user = model.define("user", {
|
||||
id: model.number(),
|
||||
username: model.text(),
|
||||
@@ -38,8 +37,7 @@ describe("Entity builder", () => {
|
||||
spend_limit: model.bigNumber(),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const User = toMikroORMEntity(user)
|
||||
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
@@ -148,7 +146,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("define a property with default value", () => {
|
||||
const model = new EntityBuilder()
|
||||
const user = model.define("user", {
|
||||
id: model.number(),
|
||||
username: model.text().default("foo"),
|
||||
@@ -156,8 +153,7 @@ describe("Entity builder", () => {
|
||||
spend_limit: model.bigNumber().default(500.4),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const User = toMikroORMEntity(user)
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
username: string
|
||||
@@ -263,7 +259,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("mark property nullable", () => {
|
||||
const model = new EntityBuilder()
|
||||
const user = model.define("user", {
|
||||
id: model.number(),
|
||||
username: model.text().nullable(),
|
||||
@@ -398,7 +393,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("define an entity with enum property", () => {
|
||||
const model = new EntityBuilder()
|
||||
const user = model.define("user", {
|
||||
id: model.number(),
|
||||
username: model.text(),
|
||||
@@ -406,8 +400,7 @@ describe("Entity builder", () => {
|
||||
role: model.enum(["moderator", "admin", "guest"]),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const User = toMikroORMEntity(user)
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
username: string
|
||||
@@ -507,7 +500,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("define enum property with default value", () => {
|
||||
const model = new EntityBuilder()
|
||||
const user = model.define("user", {
|
||||
id: model.number(),
|
||||
username: model.text(),
|
||||
@@ -515,8 +507,7 @@ describe("Entity builder", () => {
|
||||
role: model.enum(["moderator", "admin", "guest"]).default("guest"),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const User = toMikroORMEntity(user)
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
username: string
|
||||
@@ -616,7 +607,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("mark enum property nullable", () => {
|
||||
const model = new EntityBuilder()
|
||||
const user = model.define("user", {
|
||||
id: model.number(),
|
||||
username: model.text(),
|
||||
@@ -624,8 +614,7 @@ describe("Entity builder", () => {
|
||||
role: model.enum(["moderator", "admin", "guest"]).nullable(),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const User = toMikroORMEntity(user)
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
username: string
|
||||
@@ -730,7 +719,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("disallow defining created_at and updated_at timestamps", () => {
|
||||
const model = new EntityBuilder()
|
||||
expect(() =>
|
||||
model.define("user", {
|
||||
id: model.number(),
|
||||
@@ -745,7 +733,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("disallow defining deleted_at timestamp", () => {
|
||||
const model = new EntityBuilder()
|
||||
expect(() =>
|
||||
model.define("user", {
|
||||
id: model.number(),
|
||||
@@ -759,15 +746,13 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("define pg schema name in the entity name", () => {
|
||||
const model = new EntityBuilder()
|
||||
const user = model.define("public.user", {
|
||||
id: model.number(),
|
||||
username: model.text(),
|
||||
email: model.text(),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const User = toMikroORMEntity(user)
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
username: string
|
||||
@@ -857,15 +842,13 @@ describe("Entity builder", () => {
|
||||
|
||||
describe("Entity builder | id", () => {
|
||||
test("define an entity with id property", () => {
|
||||
const model = new EntityBuilder()
|
||||
const user = model.define("user", {
|
||||
id: model.id(),
|
||||
username: model.text(),
|
||||
email: model.text(),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const User = toMikroORMEntity(user)
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: string
|
||||
username: string
|
||||
@@ -956,15 +939,13 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("mark id as non-primary", () => {
|
||||
const model = new EntityBuilder()
|
||||
const user = model.define("user", {
|
||||
id: model.id({ primaryKey: false }),
|
||||
username: model.text(),
|
||||
email: model.text(),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const User = toMikroORMEntity(user)
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: string
|
||||
username: string
|
||||
@@ -1061,15 +1042,13 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("define prefix for the id", () => {
|
||||
const model = new EntityBuilder()
|
||||
const user = model.define("user", {
|
||||
id: model.id({ primaryKey: false, prefix: "us" }),
|
||||
username: model.text(),
|
||||
email: model.text(),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const User = toMikroORMEntity(user)
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: string
|
||||
username: string
|
||||
@@ -1168,7 +1147,6 @@ describe("Entity builder", () => {
|
||||
|
||||
describe("Entity builder | primaryKey", () => {
|
||||
test("should create both id fields and primaryKey fields", () => {
|
||||
const model = new EntityBuilder()
|
||||
const user = model.define("user", {
|
||||
id: model.id({ primaryKey: false }),
|
||||
email: model.text().primaryKey(),
|
||||
@@ -1271,15 +1249,13 @@ describe("Entity builder", () => {
|
||||
|
||||
describe("Entity builder | indexes", () => {
|
||||
test("define index on a field", () => {
|
||||
const model = new EntityBuilder()
|
||||
const user = model.define("user", {
|
||||
id: model.number().index(),
|
||||
username: model.text(),
|
||||
email: model.text().unique(),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const User = toMikroORMEntity(user)
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
username: string
|
||||
@@ -1377,15 +1353,13 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("define index when entity is using an explicit pg Schema", () => {
|
||||
const model = new EntityBuilder()
|
||||
const user = model.define("platform.user", {
|
||||
id: model.number().index(),
|
||||
username: model.text(),
|
||||
email: model.text().unique(),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const User = toMikroORMEntity(user)
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
username: string
|
||||
@@ -1486,7 +1460,6 @@ describe("Entity builder", () => {
|
||||
|
||||
describe("Entity builder | hasOne", () => {
|
||||
test("define hasOne relationship", () => {
|
||||
const model = new EntityBuilder()
|
||||
const email = model.define("email", {
|
||||
email: model.text(),
|
||||
isVerified: model.boolean(),
|
||||
@@ -1498,8 +1471,7 @@ describe("Entity builder", () => {
|
||||
email: model.hasOne(() => email),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const User = toMikroORMEntity(user)
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
username: string
|
||||
@@ -1576,7 +1548,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("mark hasOne relationship as nullable", () => {
|
||||
const model = new EntityBuilder()
|
||||
const email = model.define("email", {
|
||||
email: model.text(),
|
||||
isVerified: model.boolean(),
|
||||
@@ -1588,8 +1559,7 @@ describe("Entity builder", () => {
|
||||
emails: model.hasOne(() => email).nullable(),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const User = toMikroORMEntity(user)
|
||||
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
@@ -1667,7 +1637,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("define custom mappedBy key for relationship", () => {
|
||||
const model = new EntityBuilder()
|
||||
const email = model.define("email", {
|
||||
email: model.text(),
|
||||
isVerified: model.boolean(),
|
||||
@@ -1679,8 +1648,7 @@ describe("Entity builder", () => {
|
||||
email: model.hasOne(() => email, { mappedBy: "owner" }),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const User = toMikroORMEntity(user)
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
username: string
|
||||
@@ -1752,7 +1720,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("define delete cascades for the entity", () => {
|
||||
const model = new EntityBuilder()
|
||||
const email = model.define("email", {
|
||||
email: model.text(),
|
||||
isVerified: model.boolean(),
|
||||
@@ -1768,8 +1735,7 @@ describe("Entity builder", () => {
|
||||
delete: ["email"],
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const User = toMikroORMEntity(user)
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
username: string
|
||||
@@ -1840,7 +1806,7 @@ describe("Entity builder", () => {
|
||||
},
|
||||
})
|
||||
|
||||
const Email = entityBuilder(email)
|
||||
const Email = toMikroORMEntity(email)
|
||||
const emailMetaData = MetadataStorage.getMetadataFromDecorator(Email)
|
||||
expect(emailMetaData.className).toEqual("Email")
|
||||
expect(emailMetaData.path).toEqual("Email")
|
||||
@@ -1899,7 +1865,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("define delete cascades with belongsTo on the other end", () => {
|
||||
const model = new EntityBuilder()
|
||||
const email = model.define("email", {
|
||||
email: model.text(),
|
||||
isVerified: model.boolean(),
|
||||
@@ -1916,8 +1881,7 @@ describe("Entity builder", () => {
|
||||
delete: ["email"],
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const User = toMikroORMEntity(user)
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
username: string
|
||||
@@ -1995,7 +1959,7 @@ describe("Entity builder", () => {
|
||||
},
|
||||
})
|
||||
|
||||
const Email = entityBuilder(email)
|
||||
const Email = toMikroORMEntity(email)
|
||||
const emailMetaData = MetadataStorage.getMetadataFromDecorator(Email)
|
||||
expect(emailMetaData.className).toEqual("Email")
|
||||
expect(emailMetaData.path).toEqual("Email")
|
||||
@@ -2027,6 +1991,15 @@ describe("Entity builder", () => {
|
||||
owner: true,
|
||||
reference: "1:1",
|
||||
},
|
||||
user_id: {
|
||||
columnType: "text",
|
||||
getter: false,
|
||||
name: "user_id",
|
||||
nullable: false,
|
||||
reference: "scalar",
|
||||
setter: false,
|
||||
type: "string",
|
||||
},
|
||||
created_at: {
|
||||
reference: "scalar",
|
||||
type: "date",
|
||||
@@ -2065,7 +2038,6 @@ describe("Entity builder", () => {
|
||||
|
||||
describe("Entity builder | hasMany", () => {
|
||||
test("define hasMany relationship", () => {
|
||||
const model = new EntityBuilder()
|
||||
const email = model.define("email", {
|
||||
email: model.text(),
|
||||
isVerified: model.boolean(),
|
||||
@@ -2077,8 +2049,7 @@ describe("Entity builder", () => {
|
||||
emails: model.hasMany(() => email),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const User = toMikroORMEntity(user)
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
username: string
|
||||
@@ -2150,7 +2121,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("define custom mappedBy property name for hasMany relationship", () => {
|
||||
const model = new EntityBuilder()
|
||||
const email = model.define("email", {
|
||||
email: model.text(),
|
||||
isVerified: model.boolean(),
|
||||
@@ -2164,8 +2134,7 @@ describe("Entity builder", () => {
|
||||
}),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const User = toMikroORMEntity(user)
|
||||
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
@@ -2238,7 +2207,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("define delete cascades for the entity", () => {
|
||||
const model = new EntityBuilder()
|
||||
const email = model.define("email", {
|
||||
email: model.text(),
|
||||
isVerified: model.boolean(),
|
||||
@@ -2254,8 +2222,7 @@ describe("Entity builder", () => {
|
||||
delete: ["emails"],
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const User = toMikroORMEntity(user)
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
username: string
|
||||
@@ -2328,7 +2295,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("define delete cascades with belongsTo on the other end", () => {
|
||||
const model = new EntityBuilder()
|
||||
const email = model.define("email", {
|
||||
email: model.text(),
|
||||
isVerified: model.boolean(),
|
||||
@@ -2345,9 +2311,8 @@ describe("Entity builder", () => {
|
||||
delete: ["emails"],
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const Email = entityBuilder(email)
|
||||
const User = toMikroORMEntity(user)
|
||||
const Email = toMikroORMEntity(email)
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
username: string
|
||||
@@ -2441,10 +2406,11 @@ describe("Entity builder", () => {
|
||||
setter: false,
|
||||
},
|
||||
user: {
|
||||
reference: "m:1",
|
||||
name: "user",
|
||||
entity: "User",
|
||||
name: "user",
|
||||
nullable: false,
|
||||
persist: false,
|
||||
reference: "m:1",
|
||||
},
|
||||
user_id: {
|
||||
columnType: "text",
|
||||
@@ -2453,8 +2419,8 @@ describe("Entity builder", () => {
|
||||
mapToPk: true,
|
||||
name: "user_id",
|
||||
nullable: false,
|
||||
reference: "m:1",
|
||||
onDelete: "cascade",
|
||||
reference: "m:1",
|
||||
},
|
||||
created_at: {
|
||||
reference: "scalar",
|
||||
@@ -2494,8 +2460,6 @@ describe("Entity builder", () => {
|
||||
|
||||
describe("Entity builder | belongsTo", () => {
|
||||
test("define belongsTo relationship with hasOne", () => {
|
||||
const model = new EntityBuilder()
|
||||
|
||||
const email = model.define("email", {
|
||||
email: model.text(),
|
||||
isVerified: model.boolean(),
|
||||
@@ -2508,9 +2472,7 @@ describe("Entity builder", () => {
|
||||
email: model.hasOne(() => email),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const Email = entityBuilder(email)
|
||||
const [User, Email] = [user, email].map(toMikroORMEntity)
|
||||
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
@@ -2630,12 +2592,21 @@ describe("Entity builder", () => {
|
||||
setter: false,
|
||||
},
|
||||
user: {
|
||||
reference: "1:1",
|
||||
name: "user",
|
||||
reference: "1:1",
|
||||
entity: "User",
|
||||
nullable: false,
|
||||
owner: true,
|
||||
mappedBy: "email",
|
||||
owner: true,
|
||||
},
|
||||
user_id: {
|
||||
reference: "scalar",
|
||||
type: "string",
|
||||
columnType: "text",
|
||||
nullable: false,
|
||||
name: "user_id",
|
||||
getter: false,
|
||||
setter: false,
|
||||
},
|
||||
created_at: {
|
||||
reference: "scalar",
|
||||
@@ -2673,8 +2644,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("mark belongsTo with hasOne as nullable", () => {
|
||||
const model = new EntityBuilder()
|
||||
|
||||
const email = model.define("email", {
|
||||
email: model.text(),
|
||||
isVerified: model.boolean(),
|
||||
@@ -2687,9 +2656,8 @@ describe("Entity builder", () => {
|
||||
email: model.hasOne(() => email),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const Email = entityBuilder(email)
|
||||
const User = toMikroORMEntity(user)
|
||||
const Email = toMikroORMEntity(email)
|
||||
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
@@ -2803,12 +2771,21 @@ describe("Entity builder", () => {
|
||||
setter: false,
|
||||
},
|
||||
user: {
|
||||
reference: "1:1",
|
||||
name: "user",
|
||||
reference: "1:1",
|
||||
entity: "User",
|
||||
nullable: true,
|
||||
owner: true,
|
||||
mappedBy: "email",
|
||||
owner: true,
|
||||
},
|
||||
user_id: {
|
||||
reference: "scalar",
|
||||
type: "string",
|
||||
columnType: "text",
|
||||
nullable: true,
|
||||
name: "user_id",
|
||||
getter: false,
|
||||
setter: false,
|
||||
},
|
||||
created_at: {
|
||||
reference: "scalar",
|
||||
@@ -2846,8 +2823,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("define belongsTo relationship with hasMany", () => {
|
||||
const model = new EntityBuilder()
|
||||
|
||||
const email = model.define("email", {
|
||||
email: model.text(),
|
||||
isVerified: model.boolean(),
|
||||
@@ -2860,9 +2835,8 @@ describe("Entity builder", () => {
|
||||
emails: model.hasMany(() => email),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const Email = entityBuilder(email)
|
||||
const User = toMikroORMEntity(user)
|
||||
const Email = toMikroORMEntity(email)
|
||||
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
@@ -2976,19 +2950,20 @@ describe("Entity builder", () => {
|
||||
setter: false,
|
||||
},
|
||||
user: {
|
||||
reference: "m:1",
|
||||
name: "user",
|
||||
reference: "m:1",
|
||||
entity: "User",
|
||||
persist: false,
|
||||
nullable: false,
|
||||
},
|
||||
user_id: {
|
||||
columnType: "text",
|
||||
entity: "User",
|
||||
fieldName: "user_id",
|
||||
mapToPk: true,
|
||||
name: "user_id",
|
||||
nullable: false,
|
||||
reference: "m:1",
|
||||
entity: "User",
|
||||
columnType: "text",
|
||||
mapToPk: true,
|
||||
fieldName: "user_id",
|
||||
nullable: false,
|
||||
},
|
||||
created_at: {
|
||||
reference: "scalar",
|
||||
@@ -3026,8 +3001,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("define belongsTo with hasMany as nullable", () => {
|
||||
const model = new EntityBuilder()
|
||||
|
||||
const email = model.define("email", {
|
||||
email: model.text(),
|
||||
isVerified: model.boolean(),
|
||||
@@ -3040,9 +3013,8 @@ describe("Entity builder", () => {
|
||||
emails: model.hasMany(() => email),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const Email = entityBuilder(email)
|
||||
const User = toMikroORMEntity(user)
|
||||
const Email = toMikroORMEntity(email)
|
||||
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
@@ -3156,19 +3128,20 @@ describe("Entity builder", () => {
|
||||
setter: false,
|
||||
},
|
||||
user: {
|
||||
reference: "m:1",
|
||||
name: "user",
|
||||
reference: "m:1",
|
||||
entity: "User",
|
||||
persist: false,
|
||||
nullable: true,
|
||||
},
|
||||
user_id: {
|
||||
columnType: "text",
|
||||
entity: "User",
|
||||
fieldName: "user_id",
|
||||
mapToPk: true,
|
||||
name: "user_id",
|
||||
nullable: true,
|
||||
reference: "m:1",
|
||||
entity: "User",
|
||||
columnType: "text",
|
||||
mapToPk: true,
|
||||
fieldName: "user_id",
|
||||
nullable: true,
|
||||
},
|
||||
created_at: {
|
||||
reference: "scalar",
|
||||
@@ -3206,8 +3179,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("throw error when other side relationship is missing", () => {
|
||||
const model = new EntityBuilder()
|
||||
|
||||
const email = model.define("email", {
|
||||
email: model.text(),
|
||||
isVerified: model.boolean(),
|
||||
@@ -3219,15 +3190,12 @@ describe("Entity builder", () => {
|
||||
username: model.text(),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
expect(() => entityBuilder(email)).toThrow(
|
||||
expect(() => toMikroORMEntity(email)).toThrow(
|
||||
'Missing property "email" on "User" entity. Make sure to define it as a relationship'
|
||||
)
|
||||
})
|
||||
|
||||
test("throw error when other side relationship is invalid", () => {
|
||||
const model = new EntityBuilder()
|
||||
|
||||
const email = model.define("email", {
|
||||
email: model.text(),
|
||||
isVerified: model.boolean(),
|
||||
@@ -3240,15 +3208,12 @@ describe("Entity builder", () => {
|
||||
email: model.belongsTo(() => email),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
expect(() => entityBuilder(email)).toThrow(
|
||||
expect(() => toMikroORMEntity(email)).toThrow(
|
||||
'Invalid relationship reference for "email" on "User" entity. Make sure to define a hasOne or hasMany relationship'
|
||||
)
|
||||
})
|
||||
|
||||
test("throw error when cascading a parent from a child", () => {
|
||||
const model = new EntityBuilder()
|
||||
|
||||
const user = model.define("user", {
|
||||
id: model.number(),
|
||||
username: model.text(),
|
||||
@@ -3272,8 +3237,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("define relationships when entity names has pg schema name", () => {
|
||||
const model = new EntityBuilder()
|
||||
|
||||
const email = model.define("platform.email", {
|
||||
email: model.text(),
|
||||
isVerified: model.boolean(),
|
||||
@@ -3286,9 +3249,8 @@ describe("Entity builder", () => {
|
||||
email: model.hasOne(() => email),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const Email = entityBuilder(email)
|
||||
const User = toMikroORMEntity(user)
|
||||
const Email = toMikroORMEntity(email)
|
||||
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
@@ -3410,12 +3372,21 @@ describe("Entity builder", () => {
|
||||
setter: false,
|
||||
},
|
||||
user: {
|
||||
reference: "1:1",
|
||||
name: "user",
|
||||
reference: "1:1",
|
||||
entity: "User",
|
||||
nullable: false,
|
||||
owner: true,
|
||||
mappedBy: "email",
|
||||
owner: true,
|
||||
},
|
||||
user_id: {
|
||||
reference: "scalar",
|
||||
type: "string",
|
||||
columnType: "text",
|
||||
nullable: false,
|
||||
name: "user_id",
|
||||
getter: false,
|
||||
setter: false,
|
||||
},
|
||||
created_at: {
|
||||
reference: "scalar",
|
||||
@@ -3453,8 +3424,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("define relationships between cross pg schemas entities", () => {
|
||||
const model = new EntityBuilder()
|
||||
|
||||
const email = model.define("platform.email", {
|
||||
email: model.text(),
|
||||
isVerified: model.boolean(),
|
||||
@@ -3467,9 +3436,8 @@ describe("Entity builder", () => {
|
||||
email: model.hasOne(() => email),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const Email = entityBuilder(email)
|
||||
const User = toMikroORMEntity(user)
|
||||
const Email = toMikroORMEntity(email)
|
||||
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
@@ -3591,12 +3559,21 @@ describe("Entity builder", () => {
|
||||
setter: false,
|
||||
},
|
||||
user: {
|
||||
reference: "1:1",
|
||||
name: "user",
|
||||
reference: "1:1",
|
||||
entity: "User",
|
||||
nullable: false,
|
||||
owner: true,
|
||||
mappedBy: "email",
|
||||
owner: true,
|
||||
},
|
||||
user_id: {
|
||||
reference: "scalar",
|
||||
type: "string",
|
||||
columnType: "text",
|
||||
nullable: false,
|
||||
name: "user_id",
|
||||
getter: false,
|
||||
setter: false,
|
||||
},
|
||||
created_at: {
|
||||
reference: "scalar",
|
||||
@@ -3636,7 +3613,6 @@ describe("Entity builder", () => {
|
||||
|
||||
describe("Entity builder | manyToMany", () => {
|
||||
test("define manyToMany relationship", () => {
|
||||
const model = new EntityBuilder()
|
||||
const team = model.define("team", {
|
||||
id: model.number(),
|
||||
name: model.text(),
|
||||
@@ -3649,9 +3625,8 @@ describe("Entity builder", () => {
|
||||
teams: model.manyToMany(() => team),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const Team = entityBuilder(team)
|
||||
const User = toMikroORMEntity(user)
|
||||
const Team = toMikroORMEntity(team)
|
||||
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
@@ -3805,7 +3780,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("define mappedBy on one side", () => {
|
||||
const model = new EntityBuilder()
|
||||
const team = model.define("team", {
|
||||
id: model.number(),
|
||||
name: model.text(),
|
||||
@@ -3818,9 +3792,8 @@ describe("Entity builder", () => {
|
||||
teams: model.manyToMany(() => team, { mappedBy: "users" }),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const Team = entityBuilder(team)
|
||||
const User = toMikroORMEntity(user)
|
||||
const Team = toMikroORMEntity(team)
|
||||
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
@@ -3975,7 +3948,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("throw error when unable to locate relationship via mappedBy", () => {
|
||||
const model = new EntityBuilder()
|
||||
const team = model.define("team", {
|
||||
id: model.number(),
|
||||
name: model.text(),
|
||||
@@ -3987,14 +3959,12 @@ describe("Entity builder", () => {
|
||||
teams: model.manyToMany(() => team, { mappedBy: "users" }),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
expect(() => entityBuilder(user)).toThrow(
|
||||
expect(() => toMikroORMEntity(user)).toThrow(
|
||||
'Missing property "users" on "Team" entity. Make sure to define it as a relationship'
|
||||
)
|
||||
})
|
||||
|
||||
test("throw error when mappedBy relationship is not a manyToMany", () => {
|
||||
const model = new EntityBuilder()
|
||||
const team = model.define("team", {
|
||||
id: model.number(),
|
||||
name: model.text(),
|
||||
@@ -4007,14 +3977,12 @@ describe("Entity builder", () => {
|
||||
teams: model.manyToMany(() => team, { mappedBy: "users" }),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
expect(() => entityBuilder(user)).toThrow(
|
||||
expect(() => toMikroORMEntity(user)).toThrow(
|
||||
'Invalid relationship reference for "users" on "Team" entity. Make sure to define a manyToMany relationship'
|
||||
)
|
||||
})
|
||||
|
||||
test("define mappedBy on both sides", () => {
|
||||
const model = new EntityBuilder()
|
||||
const team = model.define("team", {
|
||||
id: model.number(),
|
||||
name: model.text(),
|
||||
@@ -4027,9 +3995,7 @@ describe("Entity builder", () => {
|
||||
teams: model.manyToMany(() => team, { mappedBy: "users" }),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const Team = entityBuilder(team)
|
||||
const [User, Team] = toMikroOrmEntities([user, team, {}])
|
||||
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
@@ -4189,7 +4155,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("define mappedBy on both sides and reverse order of registering entities", () => {
|
||||
const model = new EntityBuilder()
|
||||
const team = model.define("team", {
|
||||
id: model.number(),
|
||||
name: model.text(),
|
||||
@@ -4364,7 +4329,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("define multiple many to many relationships to the same entity", () => {
|
||||
const model = new EntityBuilder()
|
||||
const team = model.define("team", {
|
||||
id: model.number(),
|
||||
name: model.text(),
|
||||
@@ -4571,7 +4535,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("define manyToMany relationship when entity names has pg schema name", () => {
|
||||
const model = new EntityBuilder()
|
||||
const team = model.define("platform.team", {
|
||||
id: model.number(),
|
||||
name: model.text(),
|
||||
@@ -4584,9 +4547,8 @@ describe("Entity builder", () => {
|
||||
teams: model.manyToMany(() => team),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const Team = entityBuilder(team)
|
||||
const User = toMikroORMEntity(user)
|
||||
const Team = toMikroORMEntity(team)
|
||||
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
@@ -4742,7 +4704,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("define custom pivot table name", () => {
|
||||
const model = new EntityBuilder()
|
||||
const team = model.define("team", {
|
||||
id: model.number(),
|
||||
name: model.text(),
|
||||
@@ -4757,9 +4718,8 @@ describe("Entity builder", () => {
|
||||
teams: model.manyToMany(() => team, { pivotTable: "users_teams" }),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const Team = entityBuilder(team)
|
||||
const User = toMikroORMEntity(user)
|
||||
const Team = toMikroORMEntity(team)
|
||||
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
@@ -4913,7 +4873,6 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
test("define custom pivot entity", () => {
|
||||
const model = new EntityBuilder()
|
||||
const team = model.define("team", {
|
||||
id: model.number(),
|
||||
name: model.text(),
|
||||
@@ -4934,10 +4893,7 @@ describe("Entity builder", () => {
|
||||
teams: model.manyToMany(() => team, { pivotEntity: () => squad }),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
const Team = entityBuilder(team)
|
||||
const Squad = entityBuilder(squad)
|
||||
const [User, Team, Squad] = toMikroOrmEntities([user, team, squad])
|
||||
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: number
|
||||
@@ -4973,74 +4929,78 @@ describe("Entity builder", () => {
|
||||
expect(squadMetaData.properties).toEqual({
|
||||
id: {
|
||||
reference: "scalar",
|
||||
type: "number",
|
||||
columnType: "integer",
|
||||
name: "id",
|
||||
type: "number",
|
||||
nullable: false,
|
||||
name: "id",
|
||||
getter: false,
|
||||
setter: false,
|
||||
},
|
||||
team: {
|
||||
entity: "Team",
|
||||
name: "team",
|
||||
persist: false,
|
||||
user_id: {
|
||||
name: "user_id",
|
||||
reference: "m:1",
|
||||
},
|
||||
team_id: {
|
||||
entity: "User",
|
||||
columnType: "text",
|
||||
entity: "Team",
|
||||
fieldName: "team_id",
|
||||
mapToPk: true,
|
||||
name: "team_id",
|
||||
fieldName: "user_id",
|
||||
nullable: false,
|
||||
onDelete: undefined,
|
||||
reference: "m:1",
|
||||
},
|
||||
user: {
|
||||
entity: "User",
|
||||
name: "user",
|
||||
reference: "scalar",
|
||||
type: "User",
|
||||
persist: false,
|
||||
reference: "m:1",
|
||||
},
|
||||
user_id: {
|
||||
columnType: "text",
|
||||
entity: "User",
|
||||
fieldName: "user_id",
|
||||
mapToPk: true,
|
||||
name: "user_id",
|
||||
nullable: false,
|
||||
onDelete: undefined,
|
||||
name: "user",
|
||||
getter: false,
|
||||
setter: false,
|
||||
},
|
||||
team_id: {
|
||||
name: "team_id",
|
||||
reference: "m:1",
|
||||
entity: "Team",
|
||||
columnType: "text",
|
||||
mapToPk: true,
|
||||
fieldName: "team_id",
|
||||
nullable: false,
|
||||
},
|
||||
team: {
|
||||
reference: "scalar",
|
||||
type: "Team",
|
||||
persist: false,
|
||||
nullable: false,
|
||||
name: "team",
|
||||
getter: false,
|
||||
setter: false,
|
||||
},
|
||||
created_at: {
|
||||
reference: "scalar",
|
||||
type: "date",
|
||||
columnType: "timestamptz",
|
||||
name: "created_at",
|
||||
defaultRaw: "now()",
|
||||
onCreate: expect.any(Function),
|
||||
type: "date",
|
||||
nullable: false,
|
||||
onCreate: expect.any(Function),
|
||||
defaultRaw: "now()",
|
||||
name: "created_at",
|
||||
getter: false,
|
||||
setter: false,
|
||||
},
|
||||
updated_at: {
|
||||
reference: "scalar",
|
||||
type: "date",
|
||||
columnType: "timestamptz",
|
||||
name: "updated_at",
|
||||
defaultRaw: "now()",
|
||||
type: "date",
|
||||
nullable: false,
|
||||
onCreate: expect.any(Function),
|
||||
onUpdate: expect.any(Function),
|
||||
nullable: false,
|
||||
defaultRaw: "now()",
|
||||
name: "updated_at",
|
||||
getter: false,
|
||||
setter: false,
|
||||
},
|
||||
deleted_at: {
|
||||
reference: "scalar",
|
||||
type: "date",
|
||||
columnType: "timestamptz",
|
||||
name: "deleted_at",
|
||||
type: "date",
|
||||
nullable: true,
|
||||
name: "deleted_at",
|
||||
getter: false,
|
||||
setter: false,
|
||||
},
|
||||
|
||||
@@ -154,6 +154,7 @@ export function createMikrORMEntity() {
|
||||
* - [user.teams]: true // the teams relationship on user is an owner
|
||||
* - [team.users] // cannot be an owner
|
||||
*/
|
||||
// TODO: if we use the util toMikroOrmEntities then a new builder will be used each time, lets think about this. Currently if means that with many to many we need to use the same builder
|
||||
const MANY_TO_MANY_TRACKED_REALTIONS: Record<string, boolean> = {}
|
||||
|
||||
/**
|
||||
@@ -390,6 +391,23 @@ export function createMikrORMEntity() {
|
||||
)
|
||||
}
|
||||
|
||||
function applyForeignKeyAssignationHooks(foreignKeyName: string) {
|
||||
const hookName = `assignRelationFromForeignKeyValue${foreignKeyName}`
|
||||
/**
|
||||
* Hook to handle foreign key assignation
|
||||
*/
|
||||
MikroORMEntity.prototype[hookName] = function () {
|
||||
this[relationship.name] ??= this[foreignKeyName]
|
||||
this[foreignKeyName] ??= this[relationship.name]?.id
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute hook via lifecycle decorators
|
||||
*/
|
||||
BeforeCreate()(MikroORMEntity.prototype, hookName)
|
||||
OnInit()(MikroORMEntity.prototype, hookName)
|
||||
}
|
||||
|
||||
/**
|
||||
* Otherside is a has many. Hence we should defined a ManyToOne
|
||||
*/
|
||||
@@ -397,6 +415,8 @@ export function createMikrORMEntity() {
|
||||
otherSideRelation instanceof HasMany ||
|
||||
otherSideRelation instanceof DmlManyToMany
|
||||
) {
|
||||
const foreignKeyName = camelToSnakeCase(`${relationship.name}Id`)
|
||||
|
||||
ManyToOne({
|
||||
entity: relatedModelName,
|
||||
columnType: "text",
|
||||
@@ -406,10 +426,22 @@ export function createMikrORMEntity() {
|
||||
onDelete: shouldCascade ? "cascade" : undefined,
|
||||
})(MikroORMEntity.prototype, camelToSnakeCase(`${relationship.name}Id`))
|
||||
|
||||
ManyToOne({
|
||||
entity: relatedModelName,
|
||||
persist: false,
|
||||
})(MikroORMEntity.prototype, relationship.name)
|
||||
if (otherSideRelation instanceof DmlManyToMany) {
|
||||
Property({
|
||||
type: relatedModelName,
|
||||
persist: false,
|
||||
nullable: relationship.nullable,
|
||||
})(MikroORMEntity.prototype, relationship.name)
|
||||
} else {
|
||||
// HasMany case
|
||||
ManyToOne({
|
||||
entity: relatedModelName,
|
||||
persist: false,
|
||||
nullable: relationship.nullable,
|
||||
})(MikroORMEntity.prototype, relationship.name)
|
||||
}
|
||||
|
||||
applyForeignKeyAssignationHooks(foreignKeyName)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -417,6 +449,8 @@ export function createMikrORMEntity() {
|
||||
* Otherside is a has one. Hence we should defined a OneToOne
|
||||
*/
|
||||
if (otherSideRelation instanceof HasOne) {
|
||||
const foreignKeyName = camelToSnakeCase(`${relationship.name}Id`)
|
||||
|
||||
OneToOne({
|
||||
entity: relatedModelName,
|
||||
nullable: relationship.nullable,
|
||||
@@ -424,6 +458,23 @@ export function createMikrORMEntity() {
|
||||
owner: true,
|
||||
onDelete: shouldCascade ? "cascade" : undefined,
|
||||
})(MikroORMEntity.prototype, relationship.name)
|
||||
|
||||
if (relationship.nullable) {
|
||||
Object.defineProperty(MikroORMEntity.prototype, foreignKeyName, {
|
||||
value: null,
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
})
|
||||
}
|
||||
|
||||
Property({
|
||||
type: "string",
|
||||
columnType: "text",
|
||||
nullable: relationship.nullable,
|
||||
})(MikroORMEntity.prototype, foreignKeyName)
|
||||
|
||||
applyForeignKeyAssignationHooks(foreignKeyName)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -681,12 +732,18 @@ export function createMikrORMEntity() {
|
||||
* return the input idempotently
|
||||
* @param entity
|
||||
*/
|
||||
export const toMikroORMEntity = (entity: any) => {
|
||||
export const toMikroORMEntity = <T>(
|
||||
entity: T
|
||||
): T extends DmlEntity<infer Schema> ? EntityConstructor<Schema> : T => {
|
||||
let mikroOrmEntity: T | EntityConstructor<any> = entity
|
||||
|
||||
if (DmlEntity.isDmlEntity(entity)) {
|
||||
return createMikrORMEntity()(entity)
|
||||
mikroOrmEntity = createMikrORMEntity()(entity)
|
||||
}
|
||||
|
||||
return entity
|
||||
return mikroOrmEntity as T extends DmlEntity<infer Schema>
|
||||
? EntityConstructor<Schema>
|
||||
: T
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -694,6 +751,16 @@ export const toMikroORMEntity = (entity: any) => {
|
||||
* This action is idempotent if non of the entities are DmlEntity
|
||||
* @param entities
|
||||
*/
|
||||
export const toMikroOrmEntities = function (entities: any[]) {
|
||||
return entities.map(toMikroORMEntity)
|
||||
export const toMikroOrmEntities = function <T extends any[]>(entities: T) {
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
|
||||
return entities.map((entity) => {
|
||||
if (DmlEntity.isDmlEntity(entity)) {
|
||||
return entityBuilder(entity)
|
||||
}
|
||||
|
||||
return entity
|
||||
}) as {
|
||||
[K in keyof T]: T[K] extends DmlEntity<any> ? EntityConstructor<T[K]> : T[K]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,193 @@
|
||||
import { MetadataStorage, MikroORM } from "@mikro-orm/core"
|
||||
import { model } from "../entity-builder"
|
||||
import { toMikroOrmEntities } from "../helpers/create-mikro-orm-entity"
|
||||
import { createDatabase, dropDatabase } from "pg-god"
|
||||
import { mikroOrmSerializer, TSMigrationGenerator } from "../../dal"
|
||||
import { FileSystem } from "../../common"
|
||||
import { join } from "path"
|
||||
import { EntityConstructor } from "@medusajs/types"
|
||||
|
||||
const DB_HOST = process.env.DB_HOST
|
||||
const DB_USERNAME = process.env.DB_USERNAME
|
||||
const DB_PASSWORD = process.env.DB_PASSWORD
|
||||
|
||||
const pgGodCredentials = {
|
||||
user: DB_USERNAME,
|
||||
password: DB_PASSWORD,
|
||||
host: DB_HOST,
|
||||
}
|
||||
|
||||
const fileSystem = new FileSystem(join(__dirname, "../../migrations"))
|
||||
|
||||
describe("manyToMany - manyToMany", () => {
|
||||
const dbName = "EntityBuilder-ManyToMany"
|
||||
|
||||
let orm!: MikroORM
|
||||
|
||||
let Team: EntityConstructor<any>,
|
||||
User: EntityConstructor<any>,
|
||||
Squad: EntityConstructor<any>
|
||||
|
||||
afterAll(() => {
|
||||
fileSystem.cleanup()
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
MetadataStorage.clear()
|
||||
|
||||
const team = model.define("team", {
|
||||
id: model.id(),
|
||||
name: model.text(),
|
||||
users: model.manyToMany(() => user, {
|
||||
pivotEntity: () => squad,
|
||||
mappedBy: "squads",
|
||||
}),
|
||||
})
|
||||
|
||||
const squad = model.define("teamUsers", {
|
||||
id: model.id(),
|
||||
user: model.belongsTo(() => user, { mappedBy: "squads" }),
|
||||
squad: model.belongsTo(() => team, { mappedBy: "users" }),
|
||||
})
|
||||
|
||||
const user = model.define("user", {
|
||||
id: model.id(),
|
||||
username: model.text(),
|
||||
squads: model.manyToMany(() => team, {
|
||||
pivotEntity: () => squad,
|
||||
mappedBy: "users",
|
||||
}),
|
||||
})
|
||||
|
||||
;[User, Squad, Team] = toMikroOrmEntities([user, squad, team])
|
||||
|
||||
await createDatabase({ databaseName: dbName }, pgGodCredentials)
|
||||
|
||||
orm = await MikroORM.init({
|
||||
entities: [Team, User, Squad],
|
||||
tsNode: true,
|
||||
dbName,
|
||||
debug: true,
|
||||
type: "postgresql",
|
||||
migrations: {
|
||||
generator: TSMigrationGenerator,
|
||||
},
|
||||
})
|
||||
|
||||
const migrator = orm.getMigrator()
|
||||
await migrator.createMigration()
|
||||
await migrator.up()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await orm.close()
|
||||
|
||||
await dropDatabase(
|
||||
{ databaseName: dbName, errorIfNonExist: false },
|
||||
pgGodCredentials
|
||||
)
|
||||
})
|
||||
|
||||
it(`should handle the relation properly`, async () => {
|
||||
let manager = orm.em.fork()
|
||||
|
||||
const user1 = manager.create(User, {
|
||||
username: "User 1",
|
||||
})
|
||||
const user2 = manager.create(User, {
|
||||
username: "User 2",
|
||||
})
|
||||
|
||||
await manager.persistAndFlush([user1, user2])
|
||||
manager = orm.em.fork()
|
||||
|
||||
const team1 = manager.create(Team, {
|
||||
name: "Team 1",
|
||||
})
|
||||
const team2 = manager.create(Team, {
|
||||
name: "Team 2",
|
||||
})
|
||||
|
||||
await manager.persistAndFlush([team1, team2])
|
||||
manager = orm.em.fork()
|
||||
|
||||
const squad1 = manager.create(Squad, {
|
||||
user_id: user1.id,
|
||||
squad_id: team1.id,
|
||||
})
|
||||
const squad2 = manager.create(Squad, {
|
||||
user_id: user2.id,
|
||||
squad_id: team1.id,
|
||||
})
|
||||
|
||||
await manager.persistAndFlush([squad1, squad2])
|
||||
manager = orm.em.fork()
|
||||
|
||||
const team = await manager.findOne(
|
||||
Team,
|
||||
{
|
||||
id: team1.id,
|
||||
},
|
||||
{
|
||||
populate: ["users"],
|
||||
}
|
||||
)
|
||||
|
||||
const serializedSquad = mikroOrmSerializer<InstanceType<typeof Team>>(team)
|
||||
|
||||
expect(serializedSquad.users).toHaveLength(2)
|
||||
expect(serializedSquad).toEqual({
|
||||
id: team1.id,
|
||||
name: "Team 1",
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
deleted_at: null,
|
||||
users: expect.arrayContaining([
|
||||
{
|
||||
id: user1.id,
|
||||
username: "User 1",
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
deleted_at: null,
|
||||
},
|
||||
{
|
||||
id: user2.id,
|
||||
username: "User 2",
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
deleted_at: null,
|
||||
},
|
||||
]),
|
||||
})
|
||||
|
||||
const user = await manager.findOne(
|
||||
User,
|
||||
{
|
||||
id: user1.id,
|
||||
},
|
||||
{
|
||||
populate: ["squads"],
|
||||
}
|
||||
)
|
||||
|
||||
const serializedUser = mikroOrmSerializer<InstanceType<typeof User>>(user)
|
||||
|
||||
expect(serializedUser.squads).toHaveLength(1)
|
||||
expect(serializedUser).toEqual({
|
||||
id: user1.id,
|
||||
username: "User 1",
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
deleted_at: null,
|
||||
squads: expect.arrayContaining([
|
||||
{
|
||||
id: team1.id,
|
||||
name: "Team 1",
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
deleted_at: null,
|
||||
},
|
||||
]),
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,152 @@
|
||||
import { MetadataStorage, MikroORM } from "@mikro-orm/core"
|
||||
import { model } from "../entity-builder"
|
||||
import { toMikroOrmEntities } from "../helpers/create-mikro-orm-entity"
|
||||
import { createDatabase, dropDatabase } from "pg-god"
|
||||
import { mikroOrmSerializer } from "../../dal"
|
||||
import { FileSystem } from "../../common"
|
||||
import { join } from "path"
|
||||
import { EntityConstructor } from "@medusajs/types"
|
||||
|
||||
const DB_HOST = process.env.DB_HOST
|
||||
const DB_USERNAME = process.env.DB_USERNAME
|
||||
const DB_PASSWORD = process.env.DB_PASSWORD
|
||||
|
||||
const pgGodCredentials = {
|
||||
user: DB_USERNAME,
|
||||
password: DB_PASSWORD,
|
||||
host: DB_HOST,
|
||||
}
|
||||
|
||||
const fileSystem = new FileSystem(join(__dirname, "../../migrations"))
|
||||
|
||||
describe("manyToOne - belongTo", () => {
|
||||
const dbName = "EntityBuilder-ManyToOne"
|
||||
|
||||
let orm!: MikroORM
|
||||
let Team: EntityConstructor<any>, User: EntityConstructor<any>
|
||||
|
||||
afterAll(() => {
|
||||
fileSystem.cleanup()
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
MetadataStorage.clear()
|
||||
|
||||
const team = model.define("team", {
|
||||
id: model.id(),
|
||||
name: model.text(),
|
||||
user: model.belongsTo(() => user, { mappedBy: "teams" }),
|
||||
})
|
||||
|
||||
const user = model.define("user", {
|
||||
id: model.id(),
|
||||
username: model.text(),
|
||||
teams: model.hasMany(() => team, { mappedBy: "user" }),
|
||||
})
|
||||
|
||||
;[User, Team] = toMikroOrmEntities([user, team])
|
||||
|
||||
await createDatabase({ databaseName: dbName }, pgGodCredentials)
|
||||
|
||||
orm = await MikroORM.init({
|
||||
entities: [Team, User],
|
||||
tsNode: true,
|
||||
dbName,
|
||||
debug: true,
|
||||
type: "postgresql",
|
||||
})
|
||||
|
||||
const migrator = orm.getMigrator()
|
||||
await migrator.createMigration()
|
||||
await migrator.up()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await orm.close()
|
||||
|
||||
await dropDatabase(
|
||||
{ databaseName: dbName, errorIfNonExist: false },
|
||||
pgGodCredentials
|
||||
)
|
||||
})
|
||||
|
||||
it(`should handle the relation properly`, async () => {
|
||||
let manager = orm.em.fork()
|
||||
|
||||
const user1 = manager.create(User, {
|
||||
username: "User 1",
|
||||
})
|
||||
const user2 = manager.create(User, {
|
||||
username: "User 2",
|
||||
})
|
||||
|
||||
await manager.persistAndFlush([user1, user2])
|
||||
manager = orm.em.fork()
|
||||
|
||||
const team1 = manager.create(Team, {
|
||||
name: "Team 1",
|
||||
user_id: user1.id,
|
||||
})
|
||||
const team2 = manager.create(Team, {
|
||||
name: "Team 2",
|
||||
user_id: user2.id,
|
||||
})
|
||||
|
||||
await manager.persistAndFlush([team1, team2])
|
||||
manager = orm.em.fork()
|
||||
|
||||
const team = await manager.findOne(
|
||||
Team,
|
||||
{
|
||||
id: team1.id,
|
||||
},
|
||||
{
|
||||
populate: ["user"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(mikroOrmSerializer<InstanceType<typeof Team>>(team)).toEqual({
|
||||
id: team1.id,
|
||||
name: "Team 1",
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
deleted_at: null,
|
||||
user_id: user1.id,
|
||||
user: {
|
||||
id: user1.id,
|
||||
username: "User 1",
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
deleted_at: null,
|
||||
},
|
||||
})
|
||||
|
||||
const user = await manager.findOne(
|
||||
User,
|
||||
{
|
||||
id: user1.id,
|
||||
},
|
||||
{
|
||||
populate: ["teams"],
|
||||
}
|
||||
)
|
||||
|
||||
expect(mikroOrmSerializer<InstanceType<typeof User>>(user)).toEqual({
|
||||
id: user1.id,
|
||||
username: "User 1",
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
deleted_at: null,
|
||||
teams: [
|
||||
{
|
||||
id: team1.id,
|
||||
name: "Team 1",
|
||||
created_at: expect.any(Date),
|
||||
updated_at: expect.any(Date),
|
||||
deleted_at: null,
|
||||
user_id: user1.id,
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -13,3 +13,4 @@ export * from "./medusa-internal-service"
|
||||
export * from "./medusa-service"
|
||||
export * from "./migration-scripts"
|
||||
export * from "./mikro-orm-cli-config-builder"
|
||||
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import { MikroORMOptions } from "@mikro-orm/core/utils/Configuration"
|
||||
import { DmlEntity, toMikroORMEntity } from "../dml"
|
||||
import { TSMigrationGenerator } from "../dal"
|
||||
import { AnyEntity, EntityClassGroup, EntitySchema } from "@mikro-orm/core"
|
||||
import { EntityClass } from "@mikro-orm/core/typings"
|
||||
import type {
|
||||
AnyEntity,
|
||||
EntityClass,
|
||||
EntityClassGroup,
|
||||
} from "@mikro-orm/core/typings"
|
||||
import type { EntitySchema } from "@mikro-orm/core/metadata/EntitySchema"
|
||||
|
||||
type Options = Partial<MikroORMOptions> & {
|
||||
entities: (
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import * as entities from "./src/models"
|
||||
import { TSMigrationGenerator } from "@medusajs/utils"
|
||||
import { defineMikroOrmCliConfig } from "@medusajs/utils"
|
||||
|
||||
module.exports = {
|
||||
module.exports = defineMikroOrmCliConfig({
|
||||
entities: Object.values(entities),
|
||||
schema: "public",
|
||||
clientUrl: "postgres://postgres@localhost/medusa-fulfillment",
|
||||
type: "postgresql",
|
||||
migrations: {
|
||||
generator: TSMigrationGenerator,
|
||||
},
|
||||
}
|
||||
databaseName: "medusa-fulfillment",
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user