diff --git a/packages/core/utils/package.json b/packages/core/utils/package.json index f1d3b5ef08..d03d36330e 100644 --- a/packages/core/utils/package.json +++ b/packages/core/utils/package.json @@ -48,6 +48,6 @@ "build": "rimraf dist && tsc --build", "watch": "tsc --build --watch", "test": "jest --silent --bail --maxWorkers=50% --forceExit --testPathIgnorePatterns='/integration-tests/' -- src/**/__tests__/**/*.ts", - "test:integration": "jest --silent --bail --maxWorkers=50% --forceExit -- src/**/integration-tests/__tests__/**/*.ts" + "test:integration": "jest --silent --bail --runInBand --forceExit -- src/**/integration-tests/__tests__/**/*.ts" } } diff --git a/packages/core/utils/src/dml/__tests__/entity-builder.spec.ts b/packages/core/utils/src/dml/__tests__/entity-builder.spec.ts index 70d19edffb..b3fc5023dd 100644 --- a/packages/core/utils/src/dml/__tests__/entity-builder.spec.ts +++ b/packages/core/utils/src/dml/__tests__/entity-builder.spec.ts @@ -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(), diff --git a/packages/core/utils/src/dml/errors.ts b/packages/core/utils/src/dml/errors.ts new file mode 100644 index 0000000000..25b633d247 --- /dev/null +++ b/packages/core/utils/src/dml/errors.ts @@ -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`) + } +} diff --git a/packages/core/utils/src/dml/helpers/create-mikro-orm-entity.ts b/packages/core/utils/src/dml/helpers/create-mikro-orm-entity.ts index cefe53edac..4b2c3db732 100644 --- a/packages/core/utils/src/dml/helpers/create-mikro-orm-entity.ts +++ b/packages/core/utils/src/dml/helpers/create-mikro-orm-entity.ts @@ -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) applyIndexes(MikroORMEntity, tableName, field) applySearchable(MikroORMEntity, field) diff --git a/packages/core/utils/src/dml/helpers/entity-builder/define-property.ts b/packages/core/utils/src/dml/helpers/entity-builder/define-property.ts index 25ac7e2b21..23874af6d6 100644 --- a/packages/core/utils/src/dml/helpers/entity-builder/define-property.ts +++ b/packages/core/utils/src/dml/helpers/entity-builder/define-property.ts @@ -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 } diff --git a/packages/core/utils/src/dml/integration-tests/enum.spec.ts b/packages/core/utils/src/dml/integration-tests/__tests__/enum.spec.ts similarity index 72% rename from packages/core/utils/src/dml/integration-tests/enum.spec.ts rename to packages/core/utils/src/dml/integration-tests/__tests__/enum.spec.ts index 40ebcd0358..5ce2b0dab1 100644 --- a/packages/core/utils/src/dml/integration-tests/enum.spec.ts +++ b/packages/core/utils/src/dml/integration-tests/__tests__/enum.spec.ts @@ -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, User: EntityConstructor + let User: EntityConstructor - 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() diff --git a/packages/core/utils/src/dml/integration-tests/many-to-many.spec.ts b/packages/core/utils/src/dml/integration-tests/__tests__/many-to-many.spec.ts similarity index 86% rename from packages/core/utils/src/dml/integration-tests/many-to-many.spec.ts rename to packages/core/utils/src/dml/integration-tests/__tests__/many-to-many.spec.ts index b2ce26236f..54c5afa017 100644 --- a/packages/core/utils/src/dml/integration-tests/many-to-many.spec.ts +++ b/packages/core/utils/src/dml/integration-tests/__tests__/many-to-many.spec.ts @@ -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, Squad: EntityConstructor - 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, }, }) diff --git a/packages/core/utils/src/dml/integration-tests/many-to-one.spec.ts b/packages/core/utils/src/dml/integration-tests/__tests__/many-to-one.spec.ts similarity index 79% rename from packages/core/utils/src/dml/integration-tests/many-to-one.spec.ts rename to packages/core/utils/src/dml/integration-tests/__tests__/many-to-one.spec.ts index 2f54b291e4..ec0ab4b329 100644 --- a/packages/core/utils/src/dml/integration-tests/many-to-one.spec.ts +++ b/packages/core/utils/src/dml/integration-tests/__tests__/many-to-one.spec.ts @@ -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, User: EntityConstructor - 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() diff --git a/packages/core/utils/src/dml/integration-tests/utils.ts b/packages/core/utils/src/dml/integration-tests/utils.ts new file mode 100644 index 0000000000..687c8942fd --- /dev/null +++ b/packages/core/utils/src/dml/integration-tests/utils.ts @@ -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, +} diff --git a/packages/core/utils/src/dml/properties/id.ts b/packages/core/utils/src/dml/properties/id.ts index ab2be6efc8..c836972239 100644 --- a/packages/core/utils/src/dml/properties/id.ts +++ b/packages/core/utils/src/dml/properties/id.ts @@ -11,7 +11,7 @@ export class IdProperty extends BaseProperty { [IsIdProperty] = true static isIdProperty(value: any): value is IdProperty { - return !!value?.[IsIdProperty] + return !!value?.[IsIdProperty] || value?.dataType?.name === "id" } protected dataType: {