Add support for id property type (#7775)

This commit is contained in:
Harminder Virk
2024-06-19 17:59:03 +05:30
committed by GitHub
parent ef5719a3d8
commit 2895ccfba8
6 changed files with 420 additions and 2 deletions
@@ -744,6 +744,317 @@ 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)
expectTypeOf(new User()).toMatchTypeOf<{
id: string
username: string
email: string
created_at: Date
updated_at: Date
deleted_at: Date | null
}>()
const metaData = MetadataStorage.getMetadataFromDecorator(User)
expect(metaData.className).toEqual("User")
expect(metaData.path).toEqual("User")
expect(metaData.hooks).toEqual({
beforeCreate: ["generateId"],
onInit: ["generateId"],
})
expect(metaData.filters).toEqual({
softDeletable: {
name: "softDeletable",
cond: expect.any(Function),
default: true,
args: false,
},
})
expect(metaData.properties).toEqual({
id: {
reference: "scalar",
type: "string",
columnType: "text",
name: "id",
nullable: false,
primary: true,
},
username: {
reference: "scalar",
type: "string",
columnType: "text",
name: "username",
nullable: false,
getter: false,
setter: false,
},
email: {
reference: "scalar",
type: "string",
columnType: "text",
name: "email",
nullable: false,
getter: false,
setter: false,
},
created_at: {
reference: "scalar",
type: "date",
columnType: "timestamptz",
name: "created_at",
defaultRaw: "now()",
onCreate: expect.any(Function),
nullable: false,
getter: false,
setter: false,
},
updated_at: {
reference: "scalar",
type: "date",
columnType: "timestamptz",
name: "updated_at",
defaultRaw: "now()",
onCreate: expect.any(Function),
onUpdate: expect.any(Function),
nullable: false,
getter: false,
setter: false,
},
deleted_at: {
reference: "scalar",
type: "date",
columnType: "timestamptz",
name: "deleted_at",
nullable: true,
getter: false,
setter: false,
},
})
})
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)
expectTypeOf(new User()).toMatchTypeOf<{
id: string
username: string
email: string
created_at: Date
updated_at: Date
deleted_at: Date | null
}>()
const metaData = MetadataStorage.getMetadataFromDecorator(User)
const userInstance = new User()
userInstance["generateId"]()
expect(metaData.className).toEqual("User")
expect(metaData.path).toEqual("User")
expect(metaData.hooks).toEqual({
beforeCreate: ["generateId"],
onInit: ["generateId"],
})
expect(metaData.filters).toEqual({
softDeletable: {
name: "softDeletable",
cond: expect.any(Function),
default: true,
args: false,
},
})
expect(metaData.properties).toEqual({
id: {
reference: "scalar",
type: "string",
columnType: "text",
name: "id",
nullable: false,
getter: false,
setter: false,
},
username: {
reference: "scalar",
type: "string",
columnType: "text",
name: "username",
nullable: false,
getter: false,
setter: false,
},
email: {
reference: "scalar",
type: "string",
columnType: "text",
name: "email",
nullable: false,
getter: false,
setter: false,
},
created_at: {
reference: "scalar",
type: "date",
columnType: "timestamptz",
name: "created_at",
defaultRaw: "now()",
onCreate: expect.any(Function),
nullable: false,
getter: false,
setter: false,
},
updated_at: {
reference: "scalar",
type: "date",
columnType: "timestamptz",
name: "updated_at",
defaultRaw: "now()",
onCreate: expect.any(Function),
onUpdate: expect.any(Function),
nullable: false,
getter: false,
setter: false,
},
deleted_at: {
reference: "scalar",
type: "date",
columnType: "timestamptz",
name: "deleted_at",
nullable: true,
getter: false,
setter: false,
},
})
expect(userInstance.id).toBeDefined()
})
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)
expectTypeOf(new User()).toMatchTypeOf<{
id: string
username: string
email: string
created_at: Date
updated_at: Date
deleted_at: Date | null
}>()
const metaData = MetadataStorage.getMetadataFromDecorator(User)
const userInstance = new User()
userInstance["generateId"]()
expect(metaData.className).toEqual("User")
expect(metaData.path).toEqual("User")
expect(metaData.hooks).toEqual({
beforeCreate: ["generateId"],
onInit: ["generateId"],
})
expect(metaData.filters).toEqual({
softDeletable: {
name: "softDeletable",
cond: expect.any(Function),
default: true,
args: false,
},
})
expect(metaData.properties).toEqual({
id: {
reference: "scalar",
type: "string",
columnType: "text",
name: "id",
nullable: false,
getter: false,
setter: false,
},
username: {
reference: "scalar",
type: "string",
columnType: "text",
name: "username",
nullable: false,
getter: false,
setter: false,
},
email: {
reference: "scalar",
type: "string",
columnType: "text",
name: "email",
nullable: false,
getter: false,
setter: false,
},
created_at: {
reference: "scalar",
type: "date",
columnType: "timestamptz",
name: "created_at",
defaultRaw: "now()",
onCreate: expect.any(Function),
nullable: false,
getter: false,
setter: false,
},
updated_at: {
reference: "scalar",
type: "date",
columnType: "timestamptz",
name: "updated_at",
defaultRaw: "now()",
onCreate: expect.any(Function),
onUpdate: expect.any(Function),
nullable: false,
getter: false,
setter: false,
},
deleted_at: {
reference: "scalar",
type: "date",
columnType: "timestamptz",
name: "deleted_at",
nullable: true,
getter: false,
setter: false,
},
})
expect(userInstance.id.startsWith("us_")).toBeTruthy()
})
})
describe("Entity builder | indexes", () => {
test("define index on a field", () => {
const model = new EntityBuilder()
@@ -0,0 +1,40 @@
import { expectTypeOf } from "expect-type"
import { IdProperty } from "../properties/id"
describe("Id property", () => {
test("create id property type", () => {
const property = new IdProperty()
expectTypeOf(property["$dataType"]).toEqualTypeOf<string>()
expect(property.parse("id")).toEqual({
fieldName: "id",
dataType: {
name: "id",
options: {
primaryKey: true,
},
},
nullable: false,
indexes: [],
relationships: [],
})
})
test("create id property type with marking it as a primary key", () => {
const property = new IdProperty({ primaryKey: false })
expectTypeOf(property["$dataType"]).toEqualTypeOf<string>()
expect(property.parse("id")).toEqual({
fieldName: "id",
dataType: {
name: "id",
options: {
primaryKey: false,
},
},
nullable: false,
indexes: [],
relationships: [],
})
})
})
@@ -15,6 +15,7 @@ import type {
PropertyType,
} from "./types"
import { NullableModifier } from "./properties/nullable"
import { IdProperty } from "./properties/id"
/**
* The implicit properties added by EntityBuilder in every schema
@@ -63,6 +64,14 @@ export class EntityBuilder {
})
}
/**
* Define an id property. Id properties are marked
* primary by default
*/
id(options: ConstructorParameters<typeof IdProperty>[0]) {
return new IdProperty(options)
}
/**
* Define a text/string based column
*/
@@ -7,6 +7,9 @@ import {
ManyToMany,
ManyToOne,
Filter,
PrimaryKey,
BeforeCreate,
OnInit,
} from "@mikro-orm/core"
import { DmlEntity } from "../entity"
import {
@@ -14,6 +17,7 @@ import {
camelToSnakeCase,
createPsqlIndexStatementHelper,
toCamelCase,
generateEntityId,
} from "../../common"
import { upperCaseFirst } from "../../common/upper-case-first"
import type {
@@ -39,7 +43,7 @@ import { ManyToMany as DmlManyToMany } from "../relations/many-to-many"
* mikro orm decorator for that
*/
const COLUMN_TYPES: {
[K in Exclude<KnownDataTypes, "enum">]: string
[K in Exclude<KnownDataTypes, "enum" | "id">]: string
} = {
boolean: "boolean",
dateTime: "timestamptz",
@@ -56,7 +60,7 @@ const COLUMN_TYPES: {
* mikro orm decorator for that
*/
const PROPERTY_TYPES: {
[K in Exclude<KnownDataTypes, "enum">]: string
[K in Exclude<KnownDataTypes, "enum" | "id">]: string
} = {
boolean: "boolean",
dateTime: "date",
@@ -170,6 +174,39 @@ export function createMikrORMEntity() {
return
}
/**
* Defining an id property
*/
if (field.dataType.name === "id") {
const IdDecorator = field.dataType.options?.primaryKey
? PrimaryKey({
columnType: "text",
type: "string",
nullable: field.nullable,
})
: Property({
columnType: "text",
type: "string",
nullable: field.nullable,
})
IdDecorator(MikroORMEntity.prototype, field.fieldName)
/**
* Hook to generate entity within the code
*/
MikroORMEntity.prototype.generateId = function () {
this.id = generateEntityId(this.id, field.dataType.options?.prefix)
}
/**
* Execute hook via lifecycle decorators
*/
BeforeCreate()(MikroORMEntity.prototype, "generateId")
OnInit()(MikroORMEntity.prototype, "generateId")
return
}
/**
* Define rest of properties
*/
@@ -0,0 +1,20 @@
import { BaseProperty } from "./base"
/**
* The Id property defines a unique identifier for the schema.
* Most of the times it will be the primary as well.
*/
export class IdProperty extends BaseProperty<string> {
protected dataType: {
name: "id"
options: {
primaryKey: boolean
prefix?: string
}
}
constructor(options?: { primaryKey?: boolean; prefix?: string }) {
super()
this.dataType = { name: "id", options: { primaryKey: true, ...options } }
}
}
+1
View File
@@ -10,6 +10,7 @@ export type KnownDataTypes =
| "number"
| "dateTime"
| "json"
| "id"
/**
* List of available relationships at DML level