Initial implementation of data types and base schema (#7644)
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
import { expectTypeOf } from "expect-type"
|
||||
import { TextSchema } from "../schema/text"
|
||||
import { BaseRelationship } from "../relations/base"
|
||||
|
||||
describe("Base relationship", () => {
|
||||
test("define a custom relationship", () => {
|
||||
class HasOne<T> extends BaseRelationship<T> {
|
||||
protected relationshipType: "hasOne" | "hasMany" | "manyToMany" = "hasOne"
|
||||
}
|
||||
|
||||
const user = {
|
||||
username: new TextSchema(),
|
||||
}
|
||||
|
||||
const entityRef = () => user
|
||||
const relationship = new HasOne(entityRef, {
|
||||
foreignKey: "user_id",
|
||||
})
|
||||
|
||||
expectTypeOf(relationship["$dataType"]).toEqualTypeOf<() => typeof user>()
|
||||
expect(relationship.parse("user")).toEqual({
|
||||
name: "user",
|
||||
type: "hasOne",
|
||||
entity: entityRef,
|
||||
options: {
|
||||
foreignKey: "user_id",
|
||||
},
|
||||
})
|
||||
})
|
||||
})
|
||||
93
packages/core/utils/src/dml/__tests__/base_schema.spec.ts
Normal file
93
packages/core/utils/src/dml/__tests__/base_schema.spec.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
import { expectTypeOf } from "expect-type"
|
||||
import { BaseSchema } from "../schema/base"
|
||||
import { SchemaMetadata } from "../types"
|
||||
|
||||
describe("Base schema", () => {
|
||||
test("create a schema type from base schema", () => {
|
||||
class StringSchema extends BaseSchema<string> {
|
||||
protected dataType: SchemaMetadata["dataType"] = {
|
||||
name: "string",
|
||||
}
|
||||
}
|
||||
|
||||
const schema = new StringSchema()
|
||||
|
||||
expectTypeOf(schema["$dataType"]).toEqualTypeOf<string>()
|
||||
expect(schema.parse("username")).toEqual({
|
||||
fieldName: "username",
|
||||
dataType: {
|
||||
name: "string",
|
||||
},
|
||||
nullable: false,
|
||||
optional: false,
|
||||
indexes: [],
|
||||
relationships: [],
|
||||
})
|
||||
})
|
||||
|
||||
test("apply nullable modifier", () => {
|
||||
class StringSchema extends BaseSchema<string> {
|
||||
protected dataType: SchemaMetadata["dataType"] = {
|
||||
name: "string",
|
||||
}
|
||||
}
|
||||
|
||||
const schema = new StringSchema().nullable()
|
||||
|
||||
expectTypeOf(schema["$dataType"]).toEqualTypeOf<string | null>()
|
||||
expect(schema.parse("username")).toEqual({
|
||||
fieldName: "username",
|
||||
dataType: {
|
||||
name: "string",
|
||||
},
|
||||
nullable: true,
|
||||
optional: false,
|
||||
indexes: [],
|
||||
relationships: [],
|
||||
})
|
||||
})
|
||||
|
||||
test("apply optional modifier", () => {
|
||||
class StringSchema extends BaseSchema<string> {
|
||||
protected dataType: SchemaMetadata["dataType"] = {
|
||||
name: "string",
|
||||
}
|
||||
}
|
||||
|
||||
const schema = new StringSchema().optional()
|
||||
|
||||
expectTypeOf(schema["$dataType"]).toEqualTypeOf<string | undefined>()
|
||||
expect(schema.parse("username")).toEqual({
|
||||
fieldName: "username",
|
||||
dataType: {
|
||||
name: "string",
|
||||
},
|
||||
nullable: false,
|
||||
optional: true,
|
||||
indexes: [],
|
||||
relationships: [],
|
||||
})
|
||||
})
|
||||
|
||||
test("apply optional + nullable modifier", () => {
|
||||
class StringSchema extends BaseSchema<string> {
|
||||
protected dataType: SchemaMetadata["dataType"] = {
|
||||
name: "string",
|
||||
}
|
||||
}
|
||||
|
||||
const schema = new StringSchema().optional().nullable()
|
||||
|
||||
expectTypeOf(schema["$dataType"]).toEqualTypeOf<string | undefined | null>()
|
||||
expect(schema.parse("username")).toEqual({
|
||||
fieldName: "username",
|
||||
dataType: {
|
||||
name: "string",
|
||||
},
|
||||
nullable: true,
|
||||
optional: true,
|
||||
indexes: [],
|
||||
relationships: [],
|
||||
})
|
||||
})
|
||||
})
|
||||
20
packages/core/utils/src/dml/__tests__/boolean_schema.spec.ts
Normal file
20
packages/core/utils/src/dml/__tests__/boolean_schema.spec.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { expectTypeOf } from "expect-type"
|
||||
import { BooleanSchema } from "../schema/boolean"
|
||||
|
||||
describe("Boolean schema", () => {
|
||||
test("create boolean schema type", () => {
|
||||
const schema = new BooleanSchema()
|
||||
|
||||
expectTypeOf(schema["$dataType"]).toEqualTypeOf<boolean>()
|
||||
expect(schema.parse("isAdmin")).toEqual({
|
||||
fieldName: "isAdmin",
|
||||
dataType: {
|
||||
name: "boolean",
|
||||
},
|
||||
nullable: false,
|
||||
optional: false,
|
||||
indexes: [],
|
||||
relationships: [],
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,20 @@
|
||||
import { expectTypeOf } from "expect-type"
|
||||
import { DateTimeSchema } from "../schema/date_time"
|
||||
|
||||
describe("DateTime schema", () => {
|
||||
test("create datetime schema type", () => {
|
||||
const schema = new DateTimeSchema()
|
||||
|
||||
expectTypeOf(schema["$dataType"]).toEqualTypeOf<Date>()
|
||||
expect(schema.parse("created_at")).toEqual({
|
||||
fieldName: "created_at",
|
||||
dataType: {
|
||||
name: "dateTime",
|
||||
},
|
||||
nullable: false,
|
||||
optional: false,
|
||||
indexes: [],
|
||||
relationships: [],
|
||||
})
|
||||
})
|
||||
})
|
||||
66
packages/core/utils/src/dml/__tests__/entity_builder.spec.ts
Normal file
66
packages/core/utils/src/dml/__tests__/entity_builder.spec.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { expectTypeOf } from "expect-type"
|
||||
import { Infer, MikroORMEntity } from "../types"
|
||||
import { EntityBuilder } from "../entity_builder"
|
||||
|
||||
describe("Entity builder", () => {
|
||||
test("define an entity", () => {
|
||||
const model = new EntityBuilder()
|
||||
const user = model.define("user", {
|
||||
id: model.number(),
|
||||
username: model.text(),
|
||||
email: model.text(),
|
||||
})
|
||||
|
||||
expectTypeOf<InstanceType<Infer<typeof user>>>().toMatchTypeOf<{
|
||||
id: number
|
||||
username: string
|
||||
email: string
|
||||
}>()
|
||||
})
|
||||
|
||||
test("define an entity with relationships", () => {
|
||||
const model = new EntityBuilder()
|
||||
const email = model.define("email", {
|
||||
email: model.text(),
|
||||
isVerified: model.boolean(),
|
||||
})
|
||||
|
||||
const user = model.define("user", {
|
||||
id: model.number(),
|
||||
username: model.text(),
|
||||
emails: model.hasMany(() => email),
|
||||
})
|
||||
|
||||
expectTypeOf<InstanceType<Infer<typeof user>>>().toEqualTypeOf<{
|
||||
id: number
|
||||
username: string
|
||||
emails: MikroORMEntity<{ email: string; isVerified: boolean }>
|
||||
}>()
|
||||
})
|
||||
|
||||
test("define an entity with recursive relationships", () => {
|
||||
const model = new EntityBuilder()
|
||||
const order = model.define("order", {
|
||||
amount: model.number(),
|
||||
user: model.hasOne(() => user),
|
||||
})
|
||||
|
||||
const user = model.define("user", {
|
||||
id: model.number(),
|
||||
username: model.text(),
|
||||
orders: model.hasMany(() => order),
|
||||
})
|
||||
|
||||
expectTypeOf<InstanceType<Infer<typeof user>>>().toMatchTypeOf<{
|
||||
id: number
|
||||
username: string
|
||||
orders: MikroORMEntity<{
|
||||
amount: number
|
||||
user: MikroORMEntity<{
|
||||
id: number
|
||||
username: string
|
||||
}>
|
||||
}>
|
||||
}>()
|
||||
})
|
||||
})
|
||||
49
packages/core/utils/src/dml/__tests__/enum_schema.spec.ts
Normal file
49
packages/core/utils/src/dml/__tests__/enum_schema.spec.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { expectTypeOf } from "expect-type"
|
||||
import { EnumSchema } from "../schema/enum"
|
||||
|
||||
describe("Enum schema", () => {
|
||||
test("create enum schema type", () => {
|
||||
const schema = new EnumSchema(["admin", "moderator", "editor"])
|
||||
expectTypeOf(schema["$dataType"]).toEqualTypeOf<
|
||||
"admin" | "moderator" | "editor"
|
||||
>()
|
||||
|
||||
expect(schema.parse("role")).toEqual({
|
||||
fieldName: "role",
|
||||
dataType: {
|
||||
name: "enum",
|
||||
options: {
|
||||
choices: ["admin", "moderator", "editor"],
|
||||
},
|
||||
},
|
||||
nullable: false,
|
||||
optional: false,
|
||||
indexes: [],
|
||||
relationships: [],
|
||||
})
|
||||
})
|
||||
|
||||
test("apply nullable and optional modifiers", () => {
|
||||
const schema = new EnumSchema(["admin", "moderator", "editor"])
|
||||
.nullable()
|
||||
.optional()
|
||||
|
||||
expectTypeOf(schema["$dataType"]).toEqualTypeOf<
|
||||
"admin" | "moderator" | "editor" | null | undefined
|
||||
>()
|
||||
|
||||
expect(schema.parse("role")).toEqual({
|
||||
fieldName: "role",
|
||||
dataType: {
|
||||
name: "enum",
|
||||
options: {
|
||||
choices: ["admin", "moderator", "editor"],
|
||||
},
|
||||
},
|
||||
nullable: true,
|
||||
optional: true,
|
||||
indexes: [],
|
||||
relationships: [],
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,22 @@
|
||||
import { expectTypeOf } from "expect-type"
|
||||
import { HasMany } from "../relations/has_many"
|
||||
import { TextSchema } from "../schema/text"
|
||||
|
||||
describe("HasMany relationship", () => {
|
||||
test("define hasMany relationship", () => {
|
||||
const user = {
|
||||
username: new TextSchema(),
|
||||
}
|
||||
|
||||
const entityRef = () => user
|
||||
const relationship = new HasMany(entityRef, {})
|
||||
|
||||
expectTypeOf(relationship["$dataType"]).toEqualTypeOf<() => typeof user>()
|
||||
expect(relationship.parse("user")).toEqual({
|
||||
name: "user",
|
||||
type: "hasMany",
|
||||
entity: entityRef,
|
||||
options: {},
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,22 @@
|
||||
import { expectTypeOf } from "expect-type"
|
||||
import { HasOne } from "../relations/has_one"
|
||||
import { TextSchema } from "../schema/text"
|
||||
|
||||
describe("HasOne relationship", () => {
|
||||
test("define hasOne relationship", () => {
|
||||
const user = {
|
||||
username: new TextSchema(),
|
||||
}
|
||||
|
||||
const entityRef = () => user
|
||||
const relationship = new HasOne(entityRef, {})
|
||||
|
||||
expectTypeOf(relationship["$dataType"]).toEqualTypeOf<() => typeof user>()
|
||||
expect(relationship.parse("user")).toEqual({
|
||||
name: "user",
|
||||
type: "hasOne",
|
||||
entity: entityRef,
|
||||
options: {},
|
||||
})
|
||||
})
|
||||
})
|
||||
20
packages/core/utils/src/dml/__tests__/json_schema.spec.ts
Normal file
20
packages/core/utils/src/dml/__tests__/json_schema.spec.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { expectTypeOf } from "expect-type"
|
||||
import { JSONSchema } from "../schema/json"
|
||||
|
||||
describe("JSON schema", () => {
|
||||
test("create json schema type", () => {
|
||||
const schema = new JSONSchema()
|
||||
|
||||
expectTypeOf(schema["$dataType"]).toEqualTypeOf<string>()
|
||||
expect(schema.parse("coordinates")).toEqual({
|
||||
fieldName: "coordinates",
|
||||
dataType: {
|
||||
name: "json",
|
||||
},
|
||||
nullable: false,
|
||||
optional: false,
|
||||
indexes: [],
|
||||
relationships: [],
|
||||
})
|
||||
})
|
||||
})
|
||||
22
packages/core/utils/src/dml/__tests__/many_to_many.spec.ts
Normal file
22
packages/core/utils/src/dml/__tests__/many_to_many.spec.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { expectTypeOf } from "expect-type"
|
||||
import { TextSchema } from "../schema/text"
|
||||
import { ManyToMany } from "../relations/many_to_many"
|
||||
|
||||
describe("ManyToMany relationship", () => {
|
||||
test("define manyToMany relationship", () => {
|
||||
const user = {
|
||||
username: new TextSchema(),
|
||||
}
|
||||
|
||||
const entityRef = () => user
|
||||
const relationship = new ManyToMany(entityRef, {})
|
||||
|
||||
expectTypeOf(relationship["$dataType"]).toEqualTypeOf<() => typeof user>()
|
||||
expect(relationship.parse("user")).toEqual({
|
||||
name: "user",
|
||||
type: "manyToMany",
|
||||
entity: entityRef,
|
||||
options: {},
|
||||
})
|
||||
})
|
||||
})
|
||||
20
packages/core/utils/src/dml/__tests__/number_schema.spec.ts
Normal file
20
packages/core/utils/src/dml/__tests__/number_schema.spec.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { expectTypeOf } from "expect-type"
|
||||
import { NumberSchema } from "../schema/number"
|
||||
|
||||
describe("Number schema", () => {
|
||||
test("create number schema type", () => {
|
||||
const schema = new NumberSchema()
|
||||
|
||||
expectTypeOf(schema["$dataType"]).toEqualTypeOf<number>()
|
||||
expect(schema.parse("age")).toEqual({
|
||||
fieldName: "age",
|
||||
dataType: {
|
||||
name: "number",
|
||||
},
|
||||
nullable: false,
|
||||
optional: false,
|
||||
indexes: [],
|
||||
relationships: [],
|
||||
})
|
||||
})
|
||||
})
|
||||
20
packages/core/utils/src/dml/__tests__/text_schema.spec.ts
Normal file
20
packages/core/utils/src/dml/__tests__/text_schema.spec.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { expectTypeOf } from "expect-type"
|
||||
import { TextSchema } from "../schema/text"
|
||||
|
||||
describe("String schema", () => {
|
||||
test("create string schema type", () => {
|
||||
const schema = new TextSchema()
|
||||
|
||||
expectTypeOf(schema["$dataType"]).toEqualTypeOf<string>()
|
||||
expect(schema.parse("username")).toEqual({
|
||||
fieldName: "username",
|
||||
dataType: {
|
||||
name: "string",
|
||||
},
|
||||
nullable: false,
|
||||
optional: false,
|
||||
indexes: [],
|
||||
relationships: [],
|
||||
})
|
||||
})
|
||||
})
|
||||
13
packages/core/utils/src/dml/entity.ts
Normal file
13
packages/core/utils/src/dml/entity.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { RelationshipType, SchemaType } from "./types"
|
||||
|
||||
export class DmlEntity<
|
||||
Schema extends Record<string, SchemaType<any> | RelationshipType<any>>
|
||||
> {
|
||||
#name: string
|
||||
#schema: Schema
|
||||
|
||||
constructor(name: string, schema: Schema) {
|
||||
this.#name = name
|
||||
this.#schema = schema
|
||||
}
|
||||
}
|
||||
50
packages/core/utils/src/dml/entity_builder.ts
Normal file
50
packages/core/utils/src/dml/entity_builder.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { DmlEntity } from "./entity"
|
||||
import { TextSchema } from "./schema/text"
|
||||
import { JSONSchema } from "./schema/json"
|
||||
import { HasOne } from "./relations/has_one"
|
||||
import { HasMany } from "./relations/has_many"
|
||||
import { NumberSchema } from "./schema/number"
|
||||
import { BooleanSchema } from "./schema/boolean"
|
||||
import { DateTimeSchema } from "./schema/date_time"
|
||||
import { ManyToMany } from "./relations/many_to_many"
|
||||
import { RelationshipType, SchemaType } from "./types"
|
||||
|
||||
export class EntityBuilder {
|
||||
define<
|
||||
Schema extends Record<string, SchemaType<any> | RelationshipType<any>>
|
||||
>(name: string, schema: Schema) {
|
||||
return new DmlEntity(name, schema)
|
||||
}
|
||||
|
||||
text() {
|
||||
return new TextSchema()
|
||||
}
|
||||
|
||||
boolean() {
|
||||
return new BooleanSchema()
|
||||
}
|
||||
|
||||
number() {
|
||||
return new NumberSchema()
|
||||
}
|
||||
|
||||
dateTime() {
|
||||
return new DateTimeSchema()
|
||||
}
|
||||
|
||||
json() {
|
||||
return new JSONSchema()
|
||||
}
|
||||
|
||||
hasOne<T>(entityBuilder: T, options?: Record<string, any>) {
|
||||
return new HasOne<T>(entityBuilder, options || {})
|
||||
}
|
||||
|
||||
hasMany<T>(entityBuilder: T, options?: Record<string, any>) {
|
||||
return new HasMany<T>(entityBuilder, options || {})
|
||||
}
|
||||
|
||||
manyToMany<T>(entityBuilder: T, options?: Record<string, any>) {
|
||||
return new ManyToMany<T>(entityBuilder, options || {})
|
||||
}
|
||||
}
|
||||
36
packages/core/utils/src/dml/modifiers/nullable.ts
Normal file
36
packages/core/utils/src/dml/modifiers/nullable.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { SchemaType } from "../types"
|
||||
import { OptionalModifier } from "./optional"
|
||||
|
||||
export class NullableModifier<T> {
|
||||
/**
|
||||
* A type-only property to infer the JavScript data-type
|
||||
* of the schema property
|
||||
*/
|
||||
declare $dataType: T | null
|
||||
|
||||
/**
|
||||
* The parent schema on which the nullable modifier is
|
||||
* applied
|
||||
*/
|
||||
#schema: SchemaType<T>
|
||||
|
||||
constructor(schema: SchemaType<T>) {
|
||||
this.#schema = schema
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply optional modifier on the schema
|
||||
*/
|
||||
optional() {
|
||||
return new OptionalModifier<T | null>(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the serialized metadata
|
||||
*/
|
||||
parse(fieldName: string) {
|
||||
const schema = this.#schema.parse(fieldName)
|
||||
schema.nullable = true
|
||||
return schema
|
||||
}
|
||||
}
|
||||
36
packages/core/utils/src/dml/modifiers/optional.ts
Normal file
36
packages/core/utils/src/dml/modifiers/optional.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { SchemaType } from "../types"
|
||||
import { NullableModifier } from "./nullable"
|
||||
|
||||
export class OptionalModifier<T> {
|
||||
/**
|
||||
* A type-only property to infer the JavScript data-type
|
||||
* of the schema property
|
||||
*/
|
||||
declare $dataType: T | undefined
|
||||
|
||||
/**
|
||||
* The parent schema on which the nullable modifier is
|
||||
* applied
|
||||
*/
|
||||
#schema: SchemaType<T>
|
||||
|
||||
constructor(schema: SchemaType<T>) {
|
||||
this.#schema = schema
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply nullable modifier on the schema
|
||||
*/
|
||||
nullable() {
|
||||
return new NullableModifier<T | undefined>(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the serialized metadata
|
||||
*/
|
||||
parse(fieldName: string) {
|
||||
const schema = this.#schema.parse(fieldName)
|
||||
schema.optional = true
|
||||
return schema
|
||||
}
|
||||
}
|
||||
38
packages/core/utils/src/dml/relations/base.ts
Normal file
38
packages/core/utils/src/dml/relations/base.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { RelationshipMetadata, RelationshipType } from "../types"
|
||||
|
||||
/**
|
||||
* The BaseRelationship encapsulates the repetitive parts of defining
|
||||
* a relationship
|
||||
*/
|
||||
export abstract class BaseRelationship<T> implements RelationshipType<T> {
|
||||
#referencedEntity: T
|
||||
#options: Record<string, any>
|
||||
|
||||
/**
|
||||
* The relationship type.
|
||||
*/
|
||||
protected abstract relationshipType: RelationshipMetadata["type"]
|
||||
|
||||
/**
|
||||
* A type-only property to infer the JavScript data-type
|
||||
* of the relationship property
|
||||
*/
|
||||
declare $dataType: T
|
||||
|
||||
constructor(referencedEntity: T, options: Record<string, any>) {
|
||||
this.#referencedEntity = referencedEntity
|
||||
this.#options = options
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parsed copy of the relationship
|
||||
*/
|
||||
parse(relationshipName: string): RelationshipMetadata {
|
||||
return {
|
||||
name: relationshipName,
|
||||
entity: this.#referencedEntity,
|
||||
options: this.#options,
|
||||
type: this.relationshipType,
|
||||
}
|
||||
}
|
||||
}
|
||||
6
packages/core/utils/src/dml/relations/has_many.ts
Normal file
6
packages/core/utils/src/dml/relations/has_many.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { BaseRelationship } from "./base"
|
||||
import { RelationshipMetadata } from "../types"
|
||||
|
||||
export class HasMany<T> extends BaseRelationship<T> {
|
||||
protected relationshipType: RelationshipMetadata["type"] = "hasMany"
|
||||
}
|
||||
6
packages/core/utils/src/dml/relations/has_one.ts
Normal file
6
packages/core/utils/src/dml/relations/has_one.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { BaseRelationship } from "./base"
|
||||
import { RelationshipMetadata } from "../types"
|
||||
|
||||
export class HasOne<T> extends BaseRelationship<T> {
|
||||
protected relationshipType: RelationshipMetadata["type"] = "hasOne"
|
||||
}
|
||||
6
packages/core/utils/src/dml/relations/many_to_many.ts
Normal file
6
packages/core/utils/src/dml/relations/many_to_many.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { BaseRelationship } from "./base"
|
||||
import { RelationshipMetadata } from "../types"
|
||||
|
||||
export class ManyToMany<T> extends BaseRelationship<T> {
|
||||
protected relationshipType: RelationshipMetadata["type"] = "manyToMany"
|
||||
}
|
||||
52
packages/core/utils/src/dml/schema/base.ts
Normal file
52
packages/core/utils/src/dml/schema/base.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { SchemaMetadata, SchemaType } from "../types"
|
||||
import { NullableModifier } from "../modifiers/nullable"
|
||||
import { OptionalModifier } from "../modifiers/optional"
|
||||
|
||||
/**
|
||||
* The base schema class offers shared affordances to define
|
||||
* schema classes
|
||||
*/
|
||||
export abstract class BaseSchema<T> implements SchemaType<T> {
|
||||
#indexes: SchemaMetadata["indexes"] = []
|
||||
#relationships: SchemaMetadata["relationships"] = []
|
||||
|
||||
/**
|
||||
* The runtime dataType for the schema. It is not the same as
|
||||
* the "$dataType".
|
||||
*/
|
||||
protected abstract dataType: SchemaMetadata["dataType"]
|
||||
|
||||
/**
|
||||
* A type-only property to infer the JavScript data-type
|
||||
* of the schema property
|
||||
*/
|
||||
declare $dataType: T
|
||||
|
||||
/**
|
||||
* Apply nullable modifier on the schema
|
||||
*/
|
||||
nullable() {
|
||||
return new NullableModifier<T>(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply optional modifier on the schema
|
||||
*/
|
||||
optional() {
|
||||
return new OptionalModifier<T>(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the serialized metadata
|
||||
*/
|
||||
parse(fieldName: string): SchemaMetadata {
|
||||
return {
|
||||
fieldName,
|
||||
dataType: this.dataType,
|
||||
nullable: false,
|
||||
optional: false,
|
||||
indexes: this.#indexes,
|
||||
relationships: this.#relationships,
|
||||
}
|
||||
}
|
||||
}
|
||||
8
packages/core/utils/src/dml/schema/boolean.ts
Normal file
8
packages/core/utils/src/dml/schema/boolean.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { SchemaMetadata } from "../types"
|
||||
import { BaseSchema } from "./base"
|
||||
|
||||
export class BooleanSchema extends BaseSchema<boolean> {
|
||||
protected dataType: SchemaMetadata["dataType"] = {
|
||||
name: "boolean",
|
||||
}
|
||||
}
|
||||
8
packages/core/utils/src/dml/schema/date_time.ts
Normal file
8
packages/core/utils/src/dml/schema/date_time.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { SchemaMetadata } from "../types"
|
||||
import { BaseSchema } from "./base"
|
||||
|
||||
export class DateTimeSchema extends BaseSchema<Date> {
|
||||
protected dataType: SchemaMetadata["dataType"] = {
|
||||
name: "dateTime",
|
||||
}
|
||||
}
|
||||
15
packages/core/utils/src/dml/schema/enum.ts
Normal file
15
packages/core/utils/src/dml/schema/enum.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { SchemaMetadata } from "../types"
|
||||
import { BaseSchema } from "./base"
|
||||
|
||||
export class EnumSchema<
|
||||
const Values extends unknown
|
||||
> extends BaseSchema<Values> {
|
||||
protected dataType: SchemaMetadata["dataType"] = {
|
||||
name: "enum",
|
||||
}
|
||||
|
||||
constructor(values: Values[]) {
|
||||
super()
|
||||
this.dataType.options = { choices: values }
|
||||
}
|
||||
}
|
||||
8
packages/core/utils/src/dml/schema/json.ts
Normal file
8
packages/core/utils/src/dml/schema/json.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { SchemaMetadata } from "../types"
|
||||
import { BaseSchema } from "./base"
|
||||
|
||||
export class JSONSchema extends BaseSchema<string> {
|
||||
protected dataType: SchemaMetadata["dataType"] = {
|
||||
name: "json",
|
||||
}
|
||||
}
|
||||
8
packages/core/utils/src/dml/schema/number.ts
Normal file
8
packages/core/utils/src/dml/schema/number.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { SchemaMetadata } from "../types"
|
||||
import { BaseSchema } from "./base"
|
||||
|
||||
export class NumberSchema extends BaseSchema<number> {
|
||||
protected dataType: SchemaMetadata["dataType"] = {
|
||||
name: "number",
|
||||
}
|
||||
}
|
||||
8
packages/core/utils/src/dml/schema/text.ts
Normal file
8
packages/core/utils/src/dml/schema/text.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import { SchemaMetadata } from "../types"
|
||||
import { BaseSchema } from "./base"
|
||||
|
||||
export class TextSchema extends BaseSchema<string> {
|
||||
protected dataType: SchemaMetadata["dataType"] = {
|
||||
name: "string",
|
||||
}
|
||||
}
|
||||
3
packages/core/utils/src/dml/symbols.ts
Normal file
3
packages/core/utils/src/dml/symbols.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export const MIKRO_ORM_ENTITY_GENERATOR = Symbol.for(
|
||||
"generate_mikro_orm_entity"
|
||||
)
|
||||
82
packages/core/utils/src/dml/types.ts
Normal file
82
packages/core/utils/src/dml/types.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { DmlEntity } from "./entity"
|
||||
|
||||
/**
|
||||
* The supported data types
|
||||
*/
|
||||
export type KnownDataTypes =
|
||||
| "string"
|
||||
| "boolean"
|
||||
| "enum"
|
||||
| "number"
|
||||
| "dateTime"
|
||||
| "json"
|
||||
| "any"
|
||||
|
||||
/**
|
||||
* The meta-data returned by the relationship parse
|
||||
* method
|
||||
*/
|
||||
export type RelationshipMetadata = {
|
||||
name: string
|
||||
type: "hasOne" | "hasMany" | "manyToMany"
|
||||
entity: unknown
|
||||
options: Record<string, any>
|
||||
}
|
||||
|
||||
/**
|
||||
* The meta-data returned by the schema parse method
|
||||
*/
|
||||
export type SchemaMetadata = {
|
||||
nullable: boolean
|
||||
optional: boolean
|
||||
fieldName: string
|
||||
dataType: {
|
||||
name: KnownDataTypes
|
||||
options?: Record<string, any>
|
||||
}
|
||||
indexes: {
|
||||
name: string
|
||||
type: string
|
||||
}[]
|
||||
relationships: RelationshipMetadata[]
|
||||
}
|
||||
|
||||
/**
|
||||
* Definition of a schema type. It should have a parse
|
||||
* method to get the metadata and a type-only property
|
||||
* to get its static type
|
||||
*/
|
||||
export type SchemaType<T> = {
|
||||
$dataType: T
|
||||
parse(fieldName: string): SchemaMetadata
|
||||
}
|
||||
|
||||
/**
|
||||
* Definition of a relationship type. It should have a parse
|
||||
* method to get the metadata and a type-only property
|
||||
* to get its static type
|
||||
*/
|
||||
export type RelationshipType<T> = {
|
||||
$dataType: T
|
||||
parse(relationshipName: string): RelationshipMetadata
|
||||
}
|
||||
|
||||
/**
|
||||
* A type-only representation of a MikroORM entity. Since we generate
|
||||
* entities on the fly, we need a way to represent a type-safe
|
||||
* constructor and its instance properties.
|
||||
*/
|
||||
export interface MikroORMEntity<Props> extends Function {
|
||||
new (): Props
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to infer the schema type of a DmlEntity
|
||||
*/
|
||||
export type Infer<T> = T extends DmlEntity<infer Schema>
|
||||
? MikroORMEntity<{
|
||||
[K in keyof Schema]: Schema[K]["$dataType"] extends () => infer R
|
||||
? Infer<R>
|
||||
: Schema[K]["$dataType"]
|
||||
}>
|
||||
: never
|
||||
Reference in New Issue
Block a user