From 6d43daa930c3e68ecda3b89b5bcd80ab2a001c9c Mon Sep 17 00:00:00 2001 From: Harminder Virk Date: Wed, 12 Jun 2024 14:39:03 +0530 Subject: [PATCH] Code cleanup and add support for default values and nullable relationships (#7687) --- .../dml/__tests__/base_relationship.spec.ts | 29 +++++++++ .../src/dml/__tests__/base_schema.spec.ts | 44 +++---------- .../src/dml/__tests__/boolean_schema.spec.ts | 1 - .../dml/__tests__/date_time_schema.spec.ts | 1 - .../src/dml/__tests__/entity_builder.spec.ts | 50 ++++++++++++++ .../src/dml/__tests__/enum_schema.spec.ts | 10 +-- .../__tests__/has_many_relationship.spec.ts | 1 + .../__tests__/has_one_relationship.spec.ts | 1 + .../src/dml/__tests__/json_schema.spec.ts | 1 - .../src/dml/__tests__/many_to_many.spec.ts | 1 + .../src/dml/__tests__/number_schema.spec.ts | 1 - .../src/dml/__tests__/text_schema.spec.ts | 3 +- packages/core/utils/src/dml/entity.ts | 4 ++ packages/core/utils/src/dml/entity_builder.ts | 65 ++++++++++++++++++- .../dml/helpers/create_mikro_orm_entity.ts | 44 ++++++++----- .../core/utils/src/dml/modifiers/nullable.ts | 19 +++--- .../core/utils/src/dml/modifiers/optional.ts | 36 ---------- packages/core/utils/src/dml/relations/base.ts | 9 +++ .../core/utils/src/dml/relations/has_many.ts | 10 +++ .../core/utils/src/dml/relations/has_one.ts | 10 +++ .../utils/src/dml/relations/many_to_many.ts | 11 ++++ packages/core/utils/src/dml/schema/base.ts | 11 ++-- packages/core/utils/src/dml/schema/boolean.ts | 4 ++ .../core/utils/src/dml/schema/date_time.ts | 4 ++ packages/core/utils/src/dml/schema/enum.ts | 4 ++ packages/core/utils/src/dml/schema/json.ts | 4 ++ packages/core/utils/src/dml/schema/number.ts | 4 ++ packages/core/utils/src/dml/schema/text.ts | 5 +- packages/core/utils/src/dml/symbols.ts | 3 - packages/core/utils/src/dml/types.ts | 32 +++++---- 30 files changed, 291 insertions(+), 131 deletions(-) delete mode 100644 packages/core/utils/src/dml/modifiers/optional.ts delete mode 100644 packages/core/utils/src/dml/symbols.ts diff --git a/packages/core/utils/src/dml/__tests__/base_relationship.spec.ts b/packages/core/utils/src/dml/__tests__/base_relationship.spec.ts index 6e2773bbce..911160e9b8 100644 --- a/packages/core/utils/src/dml/__tests__/base_relationship.spec.ts +++ b/packages/core/utils/src/dml/__tests__/base_relationship.spec.ts @@ -21,6 +21,35 @@ describe("Base relationship", () => { expect(relationship.parse("user")).toEqual({ name: "user", type: "hasOne", + nullable: false, + entity: entityRef, + options: { + foreignKey: "user_id", + }, + }) + }) + + test("mark relationship as nullable", () => { + class HasOne extends BaseRelationship { + protected relationshipType: "hasOne" | "hasMany" | "manyToMany" = "hasOne" + } + + const user = { + username: new TextSchema(), + } + + const entityRef = () => user + const relationship = new HasOne(entityRef, { + foreignKey: "user_id", + }).nullable() + + expectTypeOf(relationship["$dataType"]).toEqualTypeOf< + (() => typeof user) | null + >() + expect(relationship.parse("user")).toEqual({ + name: "user", + type: "hasOne", + nullable: true, entity: entityRef, options: { foreignKey: "user_id", diff --git a/packages/core/utils/src/dml/__tests__/base_schema.spec.ts b/packages/core/utils/src/dml/__tests__/base_schema.spec.ts index 6bede8522a..8a13e2edf8 100644 --- a/packages/core/utils/src/dml/__tests__/base_schema.spec.ts +++ b/packages/core/utils/src/dml/__tests__/base_schema.spec.ts @@ -6,7 +6,7 @@ describe("Base schema", () => { test("create a schema type from base schema", () => { class StringSchema extends BaseSchema { protected dataType: SchemaMetadata["dataType"] = { - name: "string", + name: "text", } } @@ -16,10 +16,9 @@ describe("Base schema", () => { expect(schema.parse("username")).toEqual({ fieldName: "username", dataType: { - name: "string", + name: "text", }, nullable: false, - optional: false, indexes: [], relationships: [], }) @@ -28,7 +27,7 @@ describe("Base schema", () => { test("apply nullable modifier", () => { class StringSchema extends BaseSchema { protected dataType: SchemaMetadata["dataType"] = { - name: "string", + name: "text", } } @@ -38,54 +37,31 @@ describe("Base schema", () => { expect(schema.parse("username")).toEqual({ fieldName: "username", dataType: { - name: "string", + name: "text", }, nullable: true, - optional: false, indexes: [], relationships: [], }) }) - test("apply optional modifier", () => { + test("define default value", () => { class StringSchema extends BaseSchema { protected dataType: SchemaMetadata["dataType"] = { - name: "string", + name: "text", } } - const schema = new StringSchema().optional() + const schema = new StringSchema().default("foo") - expectTypeOf(schema["$dataType"]).toEqualTypeOf() + expectTypeOf(schema["$dataType"]).toEqualTypeOf() expect(schema.parse("username")).toEqual({ fieldName: "username", dataType: { - name: "string", + name: "text", }, + defaultValue: "foo", nullable: false, - optional: true, - indexes: [], - relationships: [], - }) - }) - - test("apply optional + nullable modifier", () => { - class StringSchema extends BaseSchema { - protected dataType: SchemaMetadata["dataType"] = { - name: "string", - } - } - - const schema = new StringSchema().optional().nullable() - - expectTypeOf(schema["$dataType"]).toEqualTypeOf() - expect(schema.parse("username")).toEqual({ - fieldName: "username", - dataType: { - name: "string", - }, - nullable: true, - optional: true, indexes: [], relationships: [], }) diff --git a/packages/core/utils/src/dml/__tests__/boolean_schema.spec.ts b/packages/core/utils/src/dml/__tests__/boolean_schema.spec.ts index ab1ff798e3..25679e3350 100644 --- a/packages/core/utils/src/dml/__tests__/boolean_schema.spec.ts +++ b/packages/core/utils/src/dml/__tests__/boolean_schema.spec.ts @@ -12,7 +12,6 @@ describe("Boolean schema", () => { name: "boolean", }, nullable: false, - optional: false, indexes: [], relationships: [], }) diff --git a/packages/core/utils/src/dml/__tests__/date_time_schema.spec.ts b/packages/core/utils/src/dml/__tests__/date_time_schema.spec.ts index cebfad687a..f6b5e8ba6a 100644 --- a/packages/core/utils/src/dml/__tests__/date_time_schema.spec.ts +++ b/packages/core/utils/src/dml/__tests__/date_time_schema.spec.ts @@ -12,7 +12,6 @@ describe("DateTime schema", () => { name: "dateTime", }, nullable: false, - optional: false, indexes: [], relationships: [], }) 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 44def05e21..378cdfd7ed 100644 --- a/packages/core/utils/src/dml/__tests__/entity_builder.spec.ts +++ b/packages/core/utils/src/dml/__tests__/entity_builder.spec.ts @@ -122,6 +122,56 @@ describe("Entity builder", () => { ]) }) + test("define an entity with default value", () => { + const model = new EntityBuilder() + const user = model.define("user", { + id: model.number(), + username: model.text().default("foo"), + email: model.text(), + }) + + const User = createMikrORMEntity(user) + expectTypeOf(new User()).toMatchTypeOf<{ + id: number + username: string + email: string + }>() + + const metaData = MetadataStorage.getMetadataFromDecorator(User) + expect(metaData.className).toEqual("User") + expect(metaData.path).toEqual("User") + expect(metaData.properties).toEqual({ + id: { + reference: "scalar", + type: "number", + columnType: "integer", + name: "id", + nullable: false, + getter: false, + setter: false, + }, + username: { + reference: "scalar", + type: "string", + default: "foo", + 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, + }, + }) + }) + test("define hasMany relationship", () => { const model = new EntityBuilder() const email = model.define("email", { diff --git a/packages/core/utils/src/dml/__tests__/enum_schema.spec.ts b/packages/core/utils/src/dml/__tests__/enum_schema.spec.ts index c4649a8496..712171637c 100644 --- a/packages/core/utils/src/dml/__tests__/enum_schema.spec.ts +++ b/packages/core/utils/src/dml/__tests__/enum_schema.spec.ts @@ -17,19 +17,16 @@ describe("Enum schema", () => { }, }, nullable: false, - optional: false, indexes: [], relationships: [], }) }) - test("apply nullable and optional modifiers", () => { - const schema = new EnumSchema(["admin", "moderator", "editor"]) - .nullable() - .optional() + test("apply nullable modifier", () => { + const schema = new EnumSchema(["admin", "moderator", "editor"]).nullable() expectTypeOf(schema["$dataType"]).toEqualTypeOf< - "admin" | "moderator" | "editor" | null | undefined + "admin" | "moderator" | "editor" | null >() expect(schema.parse("role")).toEqual({ @@ -41,7 +38,6 @@ describe("Enum schema", () => { }, }, nullable: true, - optional: true, indexes: [], relationships: [], }) diff --git a/packages/core/utils/src/dml/__tests__/has_many_relationship.spec.ts b/packages/core/utils/src/dml/__tests__/has_many_relationship.spec.ts index 2c3129337c..f57ba247b1 100644 --- a/packages/core/utils/src/dml/__tests__/has_many_relationship.spec.ts +++ b/packages/core/utils/src/dml/__tests__/has_many_relationship.spec.ts @@ -15,6 +15,7 @@ describe("HasMany relationship", () => { expect(relationship.parse("user")).toEqual({ name: "user", type: "hasMany", + nullable: false, entity: entityRef, options: {}, }) diff --git a/packages/core/utils/src/dml/__tests__/has_one_relationship.spec.ts b/packages/core/utils/src/dml/__tests__/has_one_relationship.spec.ts index d592f01aa2..d21e16fcd5 100644 --- a/packages/core/utils/src/dml/__tests__/has_one_relationship.spec.ts +++ b/packages/core/utils/src/dml/__tests__/has_one_relationship.spec.ts @@ -15,6 +15,7 @@ describe("HasOne relationship", () => { expect(relationship.parse("user")).toEqual({ name: "user", type: "hasOne", + nullable: false, entity: entityRef, options: {}, }) diff --git a/packages/core/utils/src/dml/__tests__/json_schema.spec.ts b/packages/core/utils/src/dml/__tests__/json_schema.spec.ts index a56b753c6c..5888d58c1a 100644 --- a/packages/core/utils/src/dml/__tests__/json_schema.spec.ts +++ b/packages/core/utils/src/dml/__tests__/json_schema.spec.ts @@ -12,7 +12,6 @@ describe("JSON schema", () => { name: "json", }, nullable: false, - optional: false, indexes: [], relationships: [], }) diff --git a/packages/core/utils/src/dml/__tests__/many_to_many.spec.ts b/packages/core/utils/src/dml/__tests__/many_to_many.spec.ts index 685650e4e8..6451c3233d 100644 --- a/packages/core/utils/src/dml/__tests__/many_to_many.spec.ts +++ b/packages/core/utils/src/dml/__tests__/many_to_many.spec.ts @@ -15,6 +15,7 @@ describe("ManyToMany relationship", () => { expect(relationship.parse("user")).toEqual({ name: "user", type: "manyToMany", + nullable: false, entity: entityRef, options: {}, }) diff --git a/packages/core/utils/src/dml/__tests__/number_schema.spec.ts b/packages/core/utils/src/dml/__tests__/number_schema.spec.ts index 8f2909aa86..6da2e431b3 100644 --- a/packages/core/utils/src/dml/__tests__/number_schema.spec.ts +++ b/packages/core/utils/src/dml/__tests__/number_schema.spec.ts @@ -12,7 +12,6 @@ describe("Number schema", () => { name: "number", }, nullable: false, - optional: false, indexes: [], relationships: [], }) diff --git a/packages/core/utils/src/dml/__tests__/text_schema.spec.ts b/packages/core/utils/src/dml/__tests__/text_schema.spec.ts index ef1b7b27f8..7399ef5a2a 100644 --- a/packages/core/utils/src/dml/__tests__/text_schema.spec.ts +++ b/packages/core/utils/src/dml/__tests__/text_schema.spec.ts @@ -9,10 +9,9 @@ describe("String schema", () => { expect(schema.parse("username")).toEqual({ fieldName: "username", dataType: { - name: "string", + name: "text", }, nullable: false, - optional: false, indexes: [], relationships: [], }) diff --git a/packages/core/utils/src/dml/entity.ts b/packages/core/utils/src/dml/entity.ts index fb793bbadd..591618891c 100644 --- a/packages/core/utils/src/dml/entity.ts +++ b/packages/core/utils/src/dml/entity.ts @@ -1,5 +1,9 @@ import { RelationshipType, SchemaType } from "./types" +/** + * Dml entity is a representation of a DML model with a unique + * name, its schema and relationships. + */ export class DmlEntity< Schema extends Record | RelationshipType> > { diff --git a/packages/core/utils/src/dml/entity_builder.ts b/packages/core/utils/src/dml/entity_builder.ts index 06364b2a39..753988b30d 100644 --- a/packages/core/utils/src/dml/entity_builder.ts +++ b/packages/core/utils/src/dml/entity_builder.ts @@ -1,5 +1,6 @@ import { DmlEntity } from "./entity" import { TextSchema } from "./schema/text" +import { EnumSchema } from "./schema/enum" import { JSONSchema } from "./schema/json" import { HasOne } from "./relations/has_one" import { HasMany } from "./relations/has_many" @@ -8,52 +9,114 @@ import { BooleanSchema } from "./schema/boolean" import { DateTimeSchema } from "./schema/date_time" import { ManyToMany } from "./relations/many_to_many" import { RelationshipType, SchemaType } from "./types" -import { EnumSchema } from "./schema/enum" import { HasOneThroughMany } from "./relations/has_one_through_many" +/** + * Entity builder exposes the API to create an entity and define its + * schema using the shorthand methods. + */ export class EntityBuilder { + /** + * Define an entity or a model. The name should be unique across + * all the entities. + */ define< Schema extends Record | RelationshipType> >(name: string, schema: Schema) { return new DmlEntity(name, schema) } + /** + * Define a text/string based column + */ text() { return new TextSchema() } + /** + * Define a boolean column + */ boolean() { return new BooleanSchema() } + /** + * Define a numeric/integer column + */ number() { return new NumberSchema() } + /** + * Define a timestampz column + */ dateTime() { return new DateTimeSchema() } + /** + * Define a JSON column to store data as a + * JSON string + */ json() { return new JSONSchema() } + /** + * Define an enum column where only a pre-defined set + * of values are allowed. + */ enum(values: Values[]) { return new EnumSchema(values) } + /** + * Has one relationship defines a relationship between two entities + * where the owner of the relationship has exactly one instance + * of the related entity. + * + * For example: A user "hasOne" profile + * + * You may use the "belongsTo" relationship to define the inverse + * of the "hasOne" relationship + */ hasOne(entityBuilder: T, options?: Record) { return new HasOne(entityBuilder, options || {}) } + /** + * Has many relationship defines a relationship between two entities + * where the owner of the relationship has many instance of the + * related entity. + * + * For example: + * + * - A user "hasMany" books + * - A user "hasMany" addresses + */ hasMany(entityBuilder: T, options?: Record) { return new HasMany(entityBuilder, options || {}) } + /** + * Define a hasOneThroughMany relationship between two entities. + * @todo Remove in favor of "belongTo" + */ hasOneThroughMany(entityBuilder: T, options?: Record) { return new HasOneThroughMany(entityBuilder, options || {}) } + /** + * ManyToMany relationship defines a relationship between two entities + * where the owner of the relationship has many instance of the + * related entity via a pivot table. + * + * For example: + * + * - A user has many teams. But a team has many users as well. So this + * relationship requires a pivot table to establish a many to many + * relationship between two entities + */ manyToMany(entityBuilder: T, options?: Record) { return new ManyToMany(entityBuilder, options || {}) } 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 156d5e825f..34a1e70dbf 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 @@ -23,32 +23,36 @@ import type { /** * DML entity data types to PostgreSQL data types via - * Mikro ORM + * Mikro ORM. + * + * We remove "enum" type from here, because we use a dedicated + * mikro orm decorator for that */ const COLUMN_TYPES: { - [K in KnownDataTypes]: string + [K in Exclude]: string } = { boolean: "boolean", dateTime: "timestamptz", number: "integer", - string: "text", + text: "text", json: "jsonb", - enum: "enum", // ignore for now } /** * DML entity data types to Mikro ORM property - * types + * types. + * + * We remove "enum" type from here, because we use a dedicated + * mikro orm decorator for that */ const PROPERTY_TYPES: { - [K in KnownDataTypes]: string + [K in Exclude]: string } = { boolean: "boolean", dateTime: "date", number: "number", - string: "string", + text: "string", json: "any", - enum: "enum", // ignore for now } /** @@ -65,20 +69,23 @@ function defineProperty( Enum({ items: () => field.dataType.options!.choices, nullable: field.nullable, + default: field.defaultValue, })(MikroORMEntity.prototype, field.fieldName) return } + /** + * Define rest of properties + */ const columnType = COLUMN_TYPES[field.dataType.name] const propertyType = PROPERTY_TYPES[field.dataType.name] - /** - * @todo: Add support for default value - */ - Property({ columnType, type: propertyType, nullable: field.nullable })( - MikroORMEntity.prototype, - field.fieldName - ) + Property({ + columnType, + type: propertyType, + nullable: field.nullable, + default: field.defaultValue, + })(MikroORMEntity.prototype, field.fieldName) } /** @@ -89,7 +96,8 @@ function defineRelationship( relationship: RelationshipMetadata ) { /** - * Defining relationships + * We expect the relationship.entity to be a function that + * lazily returns the related entity */ const relatedEntity = typeof relationship.entity === "function" @@ -115,6 +123,10 @@ function defineRelationship( ) } + /** + * Converting the related entity name (which should be in camelCase) + * to "PascalCase" + */ const relatedModelName = upperCaseFirst(relatedEntity.name) /** diff --git a/packages/core/utils/src/dml/modifiers/nullable.ts b/packages/core/utils/src/dml/modifiers/nullable.ts index 2390c635b3..2a1e223a4f 100644 --- a/packages/core/utils/src/dml/modifiers/nullable.ts +++ b/packages/core/utils/src/dml/modifiers/nullable.ts @@ -1,6 +1,8 @@ -import { SchemaType } from "../types" -import { OptionalModifier } from "./optional" +import { MaybeFieldMetadata } from "../types" +/** + * Nullable modifier marks a schema node as nullable + */ export class NullableModifier { /** * A type-only property to infer the JavScript data-type @@ -12,17 +14,12 @@ export class NullableModifier { * The parent schema on which the nullable modifier is * applied */ - #schema: SchemaType - - constructor(schema: SchemaType) { - this.#schema = schema + #schema: { + parse(fieldName: string): MaybeFieldMetadata } - /** - * Apply optional modifier on the schema - */ - optional() { - return new OptionalModifier(this) + constructor(schema: { parse(fieldName: string): MaybeFieldMetadata }) { + this.#schema = schema } /** diff --git a/packages/core/utils/src/dml/modifiers/optional.ts b/packages/core/utils/src/dml/modifiers/optional.ts deleted file mode 100644 index 1fcb3cec38..0000000000 --- a/packages/core/utils/src/dml/modifiers/optional.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { SchemaType } from "../types" -import { NullableModifier } from "./nullable" - -export class OptionalModifier { - /** - * 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 - - constructor(schema: SchemaType) { - this.#schema = schema - } - - /** - * Apply nullable modifier on the schema - */ - nullable() { - return new NullableModifier(this) - } - - /** - * Returns the serialized metadata - */ - parse(fieldName: string) { - const schema = this.#schema.parse(fieldName) - schema.optional = true - return schema - } -} diff --git a/packages/core/utils/src/dml/relations/base.ts b/packages/core/utils/src/dml/relations/base.ts index b3a724dd56..77fbdb02dc 100644 --- a/packages/core/utils/src/dml/relations/base.ts +++ b/packages/core/utils/src/dml/relations/base.ts @@ -1,3 +1,4 @@ +import { NullableModifier } from "../modifiers/nullable" import { RelationshipMetadata, RelationshipType } from "../types" /** @@ -24,12 +25,20 @@ export abstract class BaseRelationship implements RelationshipType { this.#options = options } + /** + * Apply nullable modifier on the schema + */ + nullable() { + return new NullableModifier(this) + } + /** * Returns the parsed copy of the relationship */ parse(relationshipName: string): RelationshipMetadata { return { name: relationshipName, + nullable: false, entity: this.#referencedEntity, options: this.#options, type: this.relationshipType, diff --git a/packages/core/utils/src/dml/relations/has_many.ts b/packages/core/utils/src/dml/relations/has_many.ts index 9f774c4086..18adba4b2c 100644 --- a/packages/core/utils/src/dml/relations/has_many.ts +++ b/packages/core/utils/src/dml/relations/has_many.ts @@ -1,6 +1,16 @@ import { BaseRelationship } from "./base" import { RelationshipMetadata } from "../types" +/** + * HasMany relationship defines a relationship between two entities + * where the owner of the relationship has many instance of the + * related entity. + * + * For example: + * + * - A user HasMany books + * - A user HasMany addresses + */ export class HasMany extends BaseRelationship { protected relationshipType: RelationshipMetadata["type"] = "hasMany" } diff --git a/packages/core/utils/src/dml/relations/has_one.ts b/packages/core/utils/src/dml/relations/has_one.ts index 2825d8ce21..12ecf2057e 100644 --- a/packages/core/utils/src/dml/relations/has_one.ts +++ b/packages/core/utils/src/dml/relations/has_one.ts @@ -1,6 +1,16 @@ import { BaseRelationship } from "./base" import { RelationshipMetadata } from "../types" +/** + * HasOne relationship defines a relationship between two entities + * where the owner of the relationship has exactly one instance + * of the related entity. + * + * For example: A user HasOne profile + * + * You may use the "BelongsTo" relationship to define the inverse + * of the "HasOne" relationship + */ export class HasOne extends BaseRelationship { protected relationshipType: RelationshipMetadata["type"] = "hasOne" } diff --git a/packages/core/utils/src/dml/relations/many_to_many.ts b/packages/core/utils/src/dml/relations/many_to_many.ts index 85b6c1eb87..dd8da98860 100644 --- a/packages/core/utils/src/dml/relations/many_to_many.ts +++ b/packages/core/utils/src/dml/relations/many_to_many.ts @@ -1,6 +1,17 @@ import { BaseRelationship } from "./base" import { RelationshipMetadata } from "../types" +/** + * ManyToMany relationship defines a relationship between two entities + * where the owner of the relationship has many instance of the + * related entity via a pivot table. + * + * For example: + * + * - A user has many teams. But a team has many users as well. So this + * relationship requires a pivot table to establish a many to many + * relationship between two entities + */ export class ManyToMany extends BaseRelationship { protected relationshipType: RelationshipMetadata["type"] = "manyToMany" } diff --git a/packages/core/utils/src/dml/schema/base.ts b/packages/core/utils/src/dml/schema/base.ts index 387220d700..14b4b1b25f 100644 --- a/packages/core/utils/src/dml/schema/base.ts +++ b/packages/core/utils/src/dml/schema/base.ts @@ -1,6 +1,5 @@ import { SchemaMetadata, SchemaType } from "../types" import { NullableModifier } from "../modifiers/nullable" -import { OptionalModifier } from "../modifiers/optional" /** * The base schema class offers shared affordances to define @@ -9,6 +8,7 @@ import { OptionalModifier } from "../modifiers/optional" export abstract class BaseSchema implements SchemaType { #indexes: SchemaMetadata["indexes"] = [] #relationships: SchemaMetadata["relationships"] = [] + #defaultValue?: T /** * The runtime dataType for the schema. It is not the same as @@ -30,10 +30,11 @@ export abstract class BaseSchema implements SchemaType { } /** - * Apply optional modifier on the schema + * Define default value for the property */ - optional() { - return new OptionalModifier(this) + default(value: T) { + this.#defaultValue = value + return this } /** @@ -44,7 +45,7 @@ export abstract class BaseSchema implements SchemaType { fieldName, dataType: this.dataType, nullable: false, - optional: false, + defaultValue: this.#defaultValue, indexes: this.#indexes, relationships: this.#relationships, } diff --git a/packages/core/utils/src/dml/schema/boolean.ts b/packages/core/utils/src/dml/schema/boolean.ts index 530f42317f..9b87647dd1 100644 --- a/packages/core/utils/src/dml/schema/boolean.ts +++ b/packages/core/utils/src/dml/schema/boolean.ts @@ -1,6 +1,10 @@ import { SchemaMetadata } from "../types" import { BaseSchema } from "./base" +/** + * The BooleanSchema class is used to define a boolean + * property + */ export class BooleanSchema extends BaseSchema { protected dataType: SchemaMetadata["dataType"] = { name: "boolean", diff --git a/packages/core/utils/src/dml/schema/date_time.ts b/packages/core/utils/src/dml/schema/date_time.ts index fb46fc435f..6faee23037 100644 --- a/packages/core/utils/src/dml/schema/date_time.ts +++ b/packages/core/utils/src/dml/schema/date_time.ts @@ -1,6 +1,10 @@ import { SchemaMetadata } from "../types" import { BaseSchema } from "./base" +/** + * The DateTimeSchema class is used to define a timestampz + * property + */ export class DateTimeSchema extends BaseSchema { protected dataType: SchemaMetadata["dataType"] = { name: "dateTime", diff --git a/packages/core/utils/src/dml/schema/enum.ts b/packages/core/utils/src/dml/schema/enum.ts index bbe2ccbd8a..ca1de451ce 100644 --- a/packages/core/utils/src/dml/schema/enum.ts +++ b/packages/core/utils/src/dml/schema/enum.ts @@ -1,6 +1,10 @@ import { SchemaMetadata } from "../types" import { BaseSchema } from "./base" +/** + * The EnumSchema is used to define a property with pre-defined + * list of choices. + */ export class EnumSchema< const Values extends unknown > extends BaseSchema { diff --git a/packages/core/utils/src/dml/schema/json.ts b/packages/core/utils/src/dml/schema/json.ts index 0ed61039c1..2ad657c822 100644 --- a/packages/core/utils/src/dml/schema/json.ts +++ b/packages/core/utils/src/dml/schema/json.ts @@ -1,6 +1,10 @@ import { SchemaMetadata } from "../types" import { BaseSchema } from "./base" +/** + * The JSONSchema is used to define a property that stores + * data as a JSON string + */ export class JSONSchema extends BaseSchema { protected dataType: SchemaMetadata["dataType"] = { name: "json", diff --git a/packages/core/utils/src/dml/schema/number.ts b/packages/core/utils/src/dml/schema/number.ts index bdff8a0032..9b09a8931b 100644 --- a/packages/core/utils/src/dml/schema/number.ts +++ b/packages/core/utils/src/dml/schema/number.ts @@ -1,6 +1,10 @@ import { SchemaMetadata } from "../types" import { BaseSchema } from "./base" +/** + * The NumberSchema is used to define a numeric/integer + * property + */ export class NumberSchema extends BaseSchema { protected dataType: SchemaMetadata["dataType"] = { name: "number", diff --git a/packages/core/utils/src/dml/schema/text.ts b/packages/core/utils/src/dml/schema/text.ts index 3a04b4f5c1..ef6f806fe5 100644 --- a/packages/core/utils/src/dml/schema/text.ts +++ b/packages/core/utils/src/dml/schema/text.ts @@ -1,8 +1,11 @@ import { SchemaMetadata } from "../types" import { BaseSchema } from "./base" +/** + * The NumberSchema is used to define a textual property + */ export class TextSchema extends BaseSchema { protected dataType: SchemaMetadata["dataType"] = { - name: "string", + name: "text", } } diff --git a/packages/core/utils/src/dml/symbols.ts b/packages/core/utils/src/dml/symbols.ts deleted file mode 100644 index 8e540cfd9f..0000000000 --- a/packages/core/utils/src/dml/symbols.ts +++ /dev/null @@ -1,3 +0,0 @@ -export const MIKRO_ORM_ENTITY_GENERATOR = Symbol.for( - "generate_mikro_orm_entity" -) diff --git a/packages/core/utils/src/dml/types.ts b/packages/core/utils/src/dml/types.ts index 33c4f37de7..54f6721791 100644 --- a/packages/core/utils/src/dml/types.ts +++ b/packages/core/utils/src/dml/types.ts @@ -4,7 +4,7 @@ import { DmlEntity } from "./entity" * The supported data types */ export type KnownDataTypes = - | "string" + | "text" | "boolean" | "enum" | "number" @@ -12,23 +12,22 @@ export type KnownDataTypes = | "json" /** - * The meta-data returned by the relationship parse - * method + * Any field that contains "nullable" and "optional" properties + * in their metadata are qualified as maybe fields. + * + * This allows us to wrap them inside "NullableModifier" and + * "OptionalModifier" classes. */ -export type RelationshipMetadata = { - name: string - type: "hasOne" | "hasMany" | "hasOneThroughMany" | "manyToMany" - entity: unknown - options: Record +export type MaybeFieldMetadata = { + nullable: boolean } /** * The meta-data returned by the schema parse method */ -export type SchemaMetadata = { - nullable: boolean - optional: boolean +export type SchemaMetadata = MaybeFieldMetadata & { fieldName: string + defaultValue?: any dataType: { name: KnownDataTypes options?: Record @@ -50,6 +49,17 @@ export type SchemaType = { parse(fieldName: string): SchemaMetadata } +/** + * The meta-data returned by the relationship parse + * method + */ +export type RelationshipMetadata = MaybeFieldMetadata & { + name: string + type: "hasOne" | "hasMany" | "hasOneThroughMany" | "manyToMany" + entity: unknown + options: Record +} + /** * Definition of a relationship type. It should have a parse * method to get the metadata and a type-only property