Add support for id property type (#7775)
This commit is contained in:
@@ -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 } }
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ export type KnownDataTypes =
|
||||
| "number"
|
||||
| "dateTime"
|
||||
| "json"
|
||||
| "id"
|
||||
|
||||
/**
|
||||
* List of available relationships at DML level
|
||||
|
||||
Reference in New Issue
Block a user