fix: Handle multiple id prop generation (#8097)

* fix: Handle multiple id prop generation

* throw on duplicate id + cleanup

* fix mistakenly removed files

* fix

* fix

* passzord string

* passzord string
This commit is contained in:
Adrien de Peretti
2024-07-12 16:41:19 +02:00
committed by GitHub
parent 82b620c488
commit 136da3f3ce
10 changed files with 111 additions and 69 deletions

View File

@@ -7,6 +7,8 @@ import {
toMikroOrmEntities,
toMikroORMEntity,
} from "../helpers/create-mikro-orm-entity"
import { User } from "@medusajs/icons"
import { DuplicateIdPropertyError } from "../errors"
describe("Entity builder", () => {
beforeEach(() => {
@@ -548,6 +550,26 @@ describe("Entity builder", () => {
})
})
test("should throw on duplicate id definition", () => {
const user = model.define("user", {
id: model.id().primaryKey(),
uuid: model.id(),
name: model.text(),
})
let err
try {
toMikroORMEntity(user)
} catch (e) {
err = e
}
expect(err).toBeInstanceOf(DuplicateIdPropertyError)
expect(err.message).toBe(
"The model User can only have one usage of the id() property"
)
})
test("should mark a property as searchable", () => {
const user = model.define("user", {
id: model.number(),

View File

@@ -0,0 +1,5 @@
export class DuplicateIdPropertyError extends Error {
constructor(modelName: string) {
super(`The model ${modelName} can only have one usage of the id() property`)
}
}

View File

@@ -13,6 +13,8 @@ import { defineProperty } from "./entity-builder/define-property"
import { defineRelationship } from "./entity-builder/define-relationship"
import { parseEntityName } from "./entity-builder/parse-entity-name"
import { applyEntityIndexes, applyIndexes } from "./mikro-orm/apply-indexes"
import { IdProperty } from "../properties/id"
import { DuplicateIdPropertyError } from "../errors"
/**
* Factory function to create the mikro orm entity builder. The return
@@ -62,6 +64,8 @@ export function createMikrORMEntity() {
MANY_TO_MANY_TRACKED_REALTIONS,
}
let hasIdAlreadyDefined = false
/**
* Processing schema fields
*/
@@ -69,6 +73,13 @@ export function createMikrORMEntity() {
const field = property.parse(name)
if ("fieldName" in field) {
if (IdProperty.isIdProperty(field)) {
if (hasIdAlreadyDefined) {
throw new DuplicateIdPropertyError(modelName)
}
hasIdAlreadyDefined = true
}
defineProperty(MikroORMEntity, name, property as PropertyType<any>)
applyIndexes(MikroORMEntity, tableName, field)
applySearchable(MikroORMEntity, field)

View File

@@ -190,7 +190,8 @@ export function defineProperty(
/**
* Hook to generate entity within the code
*/
MikroORMEntity.prototype.generateId = function () {
const generateIdMethodName = `generateId`
MikroORMEntity.prototype[generateIdMethodName] = function () {
this[field.fieldName] = generateEntityId(
this[field.fieldName],
field.dataType.options?.prefix
@@ -200,8 +201,8 @@ export function defineProperty(
/**
* Execute hook via lifecycle decorators
*/
BeforeCreate()(MikroORMEntity.prototype, "generateId")
OnInit()(MikroORMEntity.prototype, "generateId")
BeforeCreate()(MikroORMEntity.prototype, generateIdMethodName)
OnInit()(MikroORMEntity.prototype, generateIdMethodName)
return
}

View File

@@ -3,41 +3,34 @@ import {
MetadataStorage,
MikroORM,
} from "@mikro-orm/core"
import { model } from "../entity-builder"
import { toMikroOrmEntities } from "../helpers/create-mikro-orm-entity"
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 { CustomTsMigrationGenerator, mikroOrmSerializer } from "../../../dal"
import { EntityConstructor } from "@medusajs/types"
import { pgGodCredentials } from "../utils"
import { FileSystem } from "../../../common"
import { join } from "path"
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"))
export const fileSystem = new FileSystem(
join(__dirname, "../../integration-tests-migrations-enum")
)
describe("EntityBuilder | enum", () => {
const dbName = "EntityBuilder-enum"
let orm!: MikroORM
let Team: EntityConstructor<any>, User: EntityConstructor<any>
let User: EntityConstructor<any>
afterAll(() => {
fileSystem.cleanup()
afterAll(async () => {
await fileSystem.cleanup()
})
beforeEach(async () => {
MetadataStorage.clear()
const user = model.define("user", {
id: model.id(),
id: model.id().primaryKey(),
username: model.text(),
role: model.enum(["admin", "moderator", "editor"]),
})
@@ -50,8 +43,14 @@ describe("EntityBuilder | enum", () => {
entities: [User],
tsNode: true,
dbName,
debug: true,
password: pgGodCredentials.password,
host: pgGodCredentials.host,
user: pgGodCredentials.user,
type: "postgresql",
migrations: {
generator: CustomTsMigrationGenerator,
path: fileSystem.basePath,
},
})
const migrator = orm.getMigrator()

View File

@@ -1,23 +1,16 @@
import { MetadataStorage, MikroORM } from "@mikro-orm/core"
import { model } from "../entity-builder"
import { toMikroOrmEntities } from "../helpers/create-mikro-orm-entity"
import { model } from "../../entity-builder"
import { toMikroOrmEntities } from "../../helpers/create-mikro-orm-entity"
import { createDatabase, dropDatabase } from "pg-god"
import { CustomTsMigrationGenerator, mikroOrmSerializer } from "../../dal"
import { FileSystem } from "../../common"
import { join } from "path"
import { CustomTsMigrationGenerator, mikroOrmSerializer } from "../../../dal"
import { EntityConstructor } from "@medusajs/types"
import { pgGodCredentials } from "../utils"
import { FileSystem } from "../../../common"
import { join } from "path"
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"))
export const fileSystem = new FileSystem(
join(__dirname, "../../integration-tests-migrations-many-to-many")
)
describe("manyToMany - manyToMany", () => {
const dbName = "EntityBuilder-ManyToMany"
@@ -28,15 +21,15 @@ describe("manyToMany - manyToMany", () => {
User: EntityConstructor<any>,
Squad: EntityConstructor<any>
afterAll(() => {
fileSystem.cleanup()
afterAll(async () => {
await fileSystem.cleanup()
})
beforeEach(async () => {
MetadataStorage.clear()
const team = model.define("team", {
id: model.id(),
id: model.id().primaryKey(),
name: model.text(),
users: model.manyToMany(() => user, {
pivotEntity: () => squad,
@@ -45,13 +38,13 @@ describe("manyToMany - manyToMany", () => {
})
const squad = model.define("teamUsers", {
id: model.id(),
id: model.id().primaryKey(),
user: model.belongsTo(() => user, { mappedBy: "squads" }),
squad: model.belongsTo(() => team, { mappedBy: "users" }),
})
const user = model.define("user", {
id: model.id(),
id: model.id().primaryKey(),
username: model.text(),
squads: model.manyToMany(() => team, {
pivotEntity: () => squad,
@@ -67,10 +60,13 @@ describe("manyToMany - manyToMany", () => {
entities: [Team, User, Squad],
tsNode: true,
dbName,
debug: true,
password: pgGodCredentials.password,
host: pgGodCredentials.host,
user: pgGodCredentials.user,
type: "postgresql",
migrations: {
generator: CustomTsMigrationGenerator,
path: fileSystem.basePath,
},
})

View File

@@ -1,23 +1,16 @@
import { MetadataStorage, MikroORM } from "@mikro-orm/core"
import { model } from "../entity-builder"
import { toMikroOrmEntities } from "../helpers/create-mikro-orm-entity"
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 { CustomTsMigrationGenerator, mikroOrmSerializer } from "../../../dal"
import { EntityConstructor } from "@medusajs/types"
import { pgGodCredentials } from "../utils"
import { FileSystem } from "../../../common"
import { join } from "path"
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"))
export const fileSystem = new FileSystem(
join(__dirname, "../../integration-tests-migrations-many-to-one")
)
describe("manyToOne - belongTo", () => {
const dbName = "EntityBuilder-ManyToOne"
@@ -25,21 +18,21 @@ describe("manyToOne - belongTo", () => {
let orm!: MikroORM
let Team: EntityConstructor<any>, User: EntityConstructor<any>
afterAll(() => {
fileSystem.cleanup()
afterAll(async () => {
await fileSystem.cleanup()
})
beforeEach(async () => {
MetadataStorage.clear()
const team = model.define("team", {
id: model.id(),
id: model.id().primaryKey(),
name: model.text(),
user: model.belongsTo(() => user, { mappedBy: "teams" }),
})
const user = model.define("user", {
id: model.id(),
id: model.id().primaryKey(),
username: model.text(),
teams: model.hasMany(() => team, { mappedBy: "user" }),
})
@@ -52,8 +45,14 @@ describe("manyToOne - belongTo", () => {
entities: [Team, User],
tsNode: true,
dbName,
debug: true,
password: pgGodCredentials.password,
host: pgGodCredentials.host,
user: pgGodCredentials.user,
type: "postgresql",
migrations: {
generator: CustomTsMigrationGenerator,
path: fileSystem.basePath,
},
})
const migrator = orm.getMigrator()

View File

@@ -0,0 +1,9 @@
const DB_HOST = process.env.DB_HOST ?? "localhost"
const DB_USERNAME = process.env.DB_USERNAME ?? ""
const DB_PASSWORD = process.env.DB_PASSWORD ?? ""
export const pgGodCredentials = {
user: DB_USERNAME,
password: DB_PASSWORD,
host: DB_HOST,
}

View File

@@ -11,7 +11,7 @@ export class IdProperty extends BaseProperty<string> {
[IsIdProperty] = true
static isIdProperty(value: any): value is IdProperty {
return !!value?.[IsIdProperty]
return !!value?.[IsIdProperty] || value?.dataType?.name === "id"
}
protected dataType: {