Rename schema to property (#7761)

This commit is contained in:
Harminder Virk
2024-06-18 16:14:09 +05:30
committed by GitHub
parent 4c962b97b1
commit 8410592239
36 changed files with 324 additions and 322 deletions
@@ -0,0 +1,69 @@
import { expectTypeOf } from "expect-type"
import { BaseProperty } from "../properties/base"
import { PropertyMetadata } from "../types"
describe("Base property", () => {
test("create a property type from base property", () => {
class StringProperty extends BaseProperty<string> {
protected dataType: PropertyMetadata["dataType"] = {
name: "text",
}
}
const property = new StringProperty()
expectTypeOf(property["$dataType"]).toEqualTypeOf<string>()
expect(property.parse("username")).toEqual({
fieldName: "username",
dataType: {
name: "text",
},
nullable: false,
indexes: [],
relationships: [],
})
})
test("apply nullable modifier", () => {
class StringProperty extends BaseProperty<string> {
protected dataType: PropertyMetadata["dataType"] = {
name: "text",
}
}
const property = new StringProperty().nullable()
expectTypeOf(property["$dataType"]).toEqualTypeOf<string | null>()
expect(property.parse("username")).toEqual({
fieldName: "username",
dataType: {
name: "text",
},
nullable: true,
indexes: [],
relationships: [],
})
})
test("define default value", () => {
class StringProperty extends BaseProperty<string> {
protected dataType: PropertyMetadata["dataType"] = {
name: "text",
}
}
const property = new StringProperty().default("foo")
expectTypeOf(property["$dataType"]).toEqualTypeOf<string>()
expect(property.parse("username")).toEqual({
fieldName: "username",
dataType: {
name: "text",
},
defaultValue: "foo",
nullable: false,
indexes: [],
relationships: [],
})
})
})
@@ -1,5 +1,5 @@
import { expectTypeOf } from "expect-type"
import { TextSchema } from "../schema/text"
import { TextProperty } from "../properties/text"
import { BaseRelationship } from "../relations/base"
describe("Base relationship", () => {
@@ -9,7 +9,7 @@ describe("Base relationship", () => {
}
const user = {
username: new TextSchema(),
username: new TextProperty(),
}
const entityRef = () => user
@@ -1,69 +0,0 @@
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: "text",
}
}
const schema = new StringSchema()
expectTypeOf(schema["$dataType"]).toEqualTypeOf<string>()
expect(schema.parse("username")).toEqual({
fieldName: "username",
dataType: {
name: "text",
},
nullable: false,
indexes: [],
relationships: [],
})
})
test("apply nullable modifier", () => {
class StringSchema extends BaseSchema<string> {
protected dataType: SchemaMetadata["dataType"] = {
name: "text",
}
}
const schema = new StringSchema().nullable()
expectTypeOf(schema["$dataType"]).toEqualTypeOf<string | null>()
expect(schema.parse("username")).toEqual({
fieldName: "username",
dataType: {
name: "text",
},
nullable: true,
indexes: [],
relationships: [],
})
})
test("define default value", () => {
class StringSchema extends BaseSchema<string> {
protected dataType: SchemaMetadata["dataType"] = {
name: "text",
}
}
const schema = new StringSchema().default("foo")
expectTypeOf(schema["$dataType"]).toEqualTypeOf<string>()
expect(schema.parse("username")).toEqual({
fieldName: "username",
dataType: {
name: "text",
},
defaultValue: "foo",
nullable: false,
indexes: [],
relationships: [],
})
})
})
@@ -0,0 +1,19 @@
import { expectTypeOf } from "expect-type"
import { BooleanProperty } from "../properties/boolean"
describe("Boolean property", () => {
test("create boolean property type", () => {
const property = new BooleanProperty()
expectTypeOf(property["$dataType"]).toEqualTypeOf<boolean>()
expect(property.parse("isAdmin")).toEqual({
fieldName: "isAdmin",
dataType: {
name: "boolean",
},
nullable: false,
indexes: [],
relationships: [],
})
})
})
@@ -1,19 +0,0 @@
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,
indexes: [],
relationships: [],
})
})
})
@@ -0,0 +1,19 @@
import { expectTypeOf } from "expect-type"
import { DateTimeProperty } from "../properties/date-time"
describe("DateTime property", () => {
test("create datetime property type", () => {
const property = new DateTimeProperty()
expectTypeOf(property["$dataType"]).toEqualTypeOf<Date>()
expect(property.parse("created_at")).toEqual({
fieldName: "created_at",
dataType: {
name: "dateTime",
},
nullable: false,
indexes: [],
relationships: [],
})
})
})
@@ -1,19 +0,0 @@
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,
indexes: [],
relationships: [],
})
})
})
@@ -1,14 +1,14 @@
import { expectTypeOf } from "expect-type"
import { EnumSchema } from "../schema/enum"
import { EnumProperty } from "../properties/enum"
describe("Enum schema", () => {
test("create enum schema type", () => {
const schema = new EnumSchema(["admin", "moderator", "editor"])
expectTypeOf(schema["$dataType"]).toEqualTypeOf<
describe("Enum property", () => {
test("create enum property type", () => {
const property = new EnumProperty(["admin", "moderator", "editor"])
expectTypeOf(property["$dataType"]).toEqualTypeOf<
"admin" | "moderator" | "editor"
>()
expect(schema.parse("role")).toEqual({
expect(property.parse("role")).toEqual({
fieldName: "role",
dataType: {
name: "enum",
@@ -23,13 +23,17 @@ describe("Enum schema", () => {
})
test("apply nullable modifier", () => {
const schema = new EnumSchema(["admin", "moderator", "editor"]).nullable()
const property = new EnumProperty([
"admin",
"moderator",
"editor",
]).nullable()
expectTypeOf(schema["$dataType"]).toEqualTypeOf<
expectTypeOf(property["$dataType"]).toEqualTypeOf<
"admin" | "moderator" | "editor" | null
>()
expect(schema.parse("role")).toEqual({
expect(property.parse("role")).toEqual({
fieldName: "role",
dataType: {
name: "enum",
@@ -1,11 +1,11 @@
import { expectTypeOf } from "expect-type"
import { TextSchema } from "../schema/text"
import { TextProperty } from "../properties/text"
import { HasMany } from "../relations/has-many"
describe("HasMany relationship", () => {
test("define hasMany relationship", () => {
const user = {
username: new TextSchema(),
username: new TextProperty(),
}
const entityRef = () => user
@@ -1,11 +1,11 @@
import { expectTypeOf } from "expect-type"
import { TextSchema } from "../schema/text"
import { TextProperty } from "../properties/text"
import { HasOne } from "../relations/has-one"
describe("HasOne relationship", () => {
test("define hasOne relationship", () => {
const user = {
username: new TextSchema(),
username: new TextProperty(),
}
const entityRef = () => user
@@ -22,7 +22,7 @@ describe("HasOne relationship", () => {
test("mark relationship as nullable", () => {
const user = {
username: new TextSchema(),
username: new TextProperty(),
}
const entityRef = () => user
@@ -0,0 +1,19 @@
import { expectTypeOf } from "expect-type"
import { JSONProperty } from "../properties/json"
describe("JSON property", () => {
test("create json property type", () => {
const property = new JSONProperty()
expectTypeOf(property["$dataType"]).toEqualTypeOf<string>()
expect(property.parse("coordinates")).toEqual({
fieldName: "coordinates",
dataType: {
name: "json",
},
nullable: false,
indexes: [],
relationships: [],
})
})
})
@@ -1,19 +0,0 @@
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,
indexes: [],
relationships: [],
})
})
})
@@ -1,11 +1,11 @@
import { expectTypeOf } from "expect-type"
import { TextSchema } from "../schema/text"
import { TextProperty } from "../properties/text"
import { ManyToMany } from "../relations/many-to-many"
describe("ManyToMany relationship", () => {
test("define manyToMany relationship", () => {
const user = {
username: new TextSchema(),
username: new TextProperty(),
}
const entityRef = () => user
@@ -0,0 +1,19 @@
import { expectTypeOf } from "expect-type"
import { NumberProperty } from "../properties/number"
describe("Number property", () => {
test("create number property type", () => {
const property = new NumberProperty()
expectTypeOf(property["$dataType"]).toEqualTypeOf<number>()
expect(property.parse("age")).toEqual({
fieldName: "age",
dataType: {
name: "number",
},
nullable: false,
indexes: [],
relationships: [],
})
})
})
@@ -1,19 +0,0 @@
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,
indexes: [],
relationships: [],
})
})
})
@@ -0,0 +1,19 @@
import { expectTypeOf } from "expect-type"
import { TextProperty } from "../properties/text"
describe("Text property", () => {
test("create text property type", () => {
const property = new TextProperty()
expectTypeOf(property["$dataType"]).toEqualTypeOf<string>()
expect(property.parse("username")).toEqual({
fieldName: "username",
dataType: {
name: "text",
},
nullable: false,
indexes: [],
relationships: [],
})
})
})
@@ -1,19 +0,0 @@
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: "text",
},
nullable: false,
indexes: [],
relationships: [],
})
})
})
+21 -17
View File
@@ -1,16 +1,20 @@
import { DmlEntity } from "./entity"
import { TextSchema } from "./schema/text"
import { EnumSchema } from "./schema/enum"
import { JSONSchema } from "./schema/json"
import { TextProperty } from "./properties/text"
import { EnumProperty } from "./properties/enum"
import { JSONProperty } from "./properties/json"
import { HasOne } from "./relations/has-one"
import { HasMany } from "./relations/has-many"
import { NumberSchema } from "./schema/number"
import { BooleanSchema } from "./schema/boolean"
import { NumberProperty } from "./properties/number"
import { BooleanProperty } from "./properties/boolean"
import { BelongsTo } from "./relations/belongs-to"
import { DateTimeSchema } from "./schema/date-time"
import { DateTimeProperty } from "./properties/date-time"
import { ManyToMany } from "./relations/many-to-many"
import type { RelationshipOptions, RelationshipType, SchemaType } from "./types"
import { NullableModifier } from "./schema/nullable"
import type {
RelationshipOptions,
RelationshipType,
PropertyType,
} from "./types"
import { NullableModifier } from "./properties/nullable"
/**
* Entity builder exposes the API to create an entity and define its
@@ -22,15 +26,15 @@ export class EntityBuilder {
* all the entities.
*/
define<
Schema extends Record<string, SchemaType<any> | RelationshipType<any>>
Schema extends Record<string, PropertyType<any> | RelationshipType<any>>
>(name: string, schema: Schema) {
return new DmlEntity<
Schema & {
deleted_at: NullableModifier<Date, DateTimeSchema>
deleted_at: NullableModifier<Date, DateTimeProperty>
}
>(name, {
...schema,
deleted_at: new DateTimeSchema().nullable(),
deleted_at: new DateTimeProperty().nullable(),
})
}
@@ -38,28 +42,28 @@ export class EntityBuilder {
* Define a text/string based column
*/
text() {
return new TextSchema()
return new TextProperty()
}
/**
* Define a boolean column
*/
boolean() {
return new BooleanSchema()
return new BooleanProperty()
}
/**
* Define a numeric/integer column
*/
number() {
return new NumberSchema()
return new NumberProperty()
}
/**
* Define a timestampz column
*/
dateTime() {
return new DateTimeSchema()
return new DateTimeProperty()
}
/**
@@ -67,7 +71,7 @@ export class EntityBuilder {
* JSON string
*/
json() {
return new JSONSchema()
return new JSONProperty()
}
/**
@@ -75,7 +79,7 @@ export class EntityBuilder {
* of values are allowed.
*/
enum<const Values extends unknown>(values: Values[]) {
return new EnumSchema<Values>(values)
return new EnumProperty<Values>(values)
}
/**
+6 -4
View File
@@ -1,6 +1,6 @@
import { BelongsTo } from "./relations/belongs-to"
import {
SchemaType,
PropertyType,
EntityCascades,
RelationshipType,
ExtractEntityRelations,
@@ -11,7 +11,7 @@ import {
* name, its schema and relationships.
*/
export class DmlEntity<
Schema extends Record<string, SchemaType<any> | RelationshipType<any>>
Schema extends Record<string, PropertyType<any> | RelationshipType<any>>
> {
#cascades: EntityCascades<string[]> = {}
constructor(public name: string, public schema: Schema) {}
@@ -21,12 +21,14 @@ export class DmlEntity<
*/
parse(): {
name: string
schema: SchemaType<any> | RelationshipType<any>
schema: PropertyType<any> | RelationshipType<any>
cascades: EntityCascades<string[]>
} {
return {
name: this.name,
schema: this.schema as unknown as SchemaType<any> | RelationshipType<any>,
schema: this.schema as unknown as
| PropertyType<any>
| RelationshipType<any>,
cascades: this.#cascades,
}
}
@@ -17,10 +17,10 @@ import {
import { upperCaseFirst } from "../../common/upper-case-first"
import type {
Infer,
SchemaType,
PropertyType,
EntityCascades,
KnownDataTypes,
SchemaMetadata,
PropertyMetadata,
RelationshipType,
EntityConstructor,
RelationshipMetadata,
@@ -90,7 +90,7 @@ export function createMikrORMEntity() {
*/
function defineProperty(
MikroORMEntity: EntityConstructor<any>,
field: SchemaMetadata
field: PropertyMetadata
) {
/**
* Defining an enum property
@@ -124,7 +124,7 @@ export function createMikrORMEntity() {
function applyIndexes(
MikroORMEntity: EntityConstructor<any>,
tableName: string,
field: SchemaMetadata
field: PropertyMetadata
) {
field.indexes.forEach((index) => {
const name =
@@ -149,7 +149,7 @@ export function createMikrORMEntity() {
MikroORMEntity: EntityConstructor<any>,
relationship: RelationshipMetadata,
relatedEntity: DmlEntity<
Record<string, SchemaType<any> | RelationshipType<any>>
Record<string, PropertyType<any> | RelationshipType<any>>
>,
cascades: EntityCascades<string[]>
) {
@@ -173,7 +173,7 @@ export function createMikrORMEntity() {
MikroORMEntity: EntityConstructor<any>,
relationship: RelationshipMetadata,
relatedEntity: DmlEntity<
Record<string, SchemaType<any> | RelationshipType<any>>
Record<string, PropertyType<any> | RelationshipType<any>>
>,
cascades: EntityCascades<string[]>
) {
@@ -203,7 +203,7 @@ export function createMikrORMEntity() {
MikroORMEntity: EntityConstructor<any>,
relationship: RelationshipMetadata,
relatedEntity: DmlEntity<
Record<string, SchemaType<any> | RelationshipType<any>>
Record<string, PropertyType<any> | RelationshipType<any>>
>
) {
const mappedBy =
@@ -280,7 +280,7 @@ export function createMikrORMEntity() {
MikroORMEntity: EntityConstructor<any>,
relationship: RelationshipMetadata,
relatedEntity: DmlEntity<
Record<string, SchemaType<any> | RelationshipType<any>>
Record<string, PropertyType<any> | RelationshipType<any>>
>,
cascades: EntityCascades<string[]>
) {
@@ -1,20 +1,27 @@
import { SchemaMetadata, SchemaType } from "../types"
import { PropertyMetadata, PropertyType } from "../types"
import { NullableModifier } from "./nullable"
/**
* The base schema class offers shared affordances to define
* schema classes
* The BaseProperty class offers shared affordances to define
* property classes
*/
export abstract class BaseSchema<T> implements SchemaType<T> {
#indexes: SchemaMetadata["indexes"] = []
#relationships: SchemaMetadata["relationships"] = []
export abstract class BaseProperty<T> implements PropertyType<T> {
/**
* Defined indexes and relationships
*/
#indexes: PropertyMetadata["indexes"] = []
#relationships: PropertyMetadata["relationships"] = []
/**
* Default value for the property
*/
#defaultValue?: T
/**
* The runtime dataType for the schema. It is not the same as
* the "$dataType".
*/
protected abstract dataType: SchemaMetadata["dataType"]
protected abstract dataType: PropertyMetadata["dataType"]
/**
* A type-only property to infer the JavScript data-type
@@ -56,7 +63,7 @@ export abstract class BaseSchema<T> implements SchemaType<T> {
/**
* Returns the serialized metadata
*/
parse(fieldName: string): SchemaMetadata {
parse(fieldName: string): PropertyMetadata {
return {
fieldName,
dataType: this.dataType,
@@ -0,0 +1,11 @@
import { BaseProperty } from "./base"
/**
* The BooleanProperty class is used to define a boolean
* property
*/
export class BooleanProperty extends BaseProperty<boolean> {
protected dataType = {
name: "boolean",
} as const
}
@@ -0,0 +1,11 @@
import { BaseProperty } from "./base"
/**
* The DateTimeProperty class is used to define a timestampz
* property
*/
export class DateTimeProperty extends BaseProperty<Date> {
protected dataType = {
name: "dateTime",
} as const
}
@@ -0,0 +1,19 @@
import { BaseProperty } from "./base"
/**
* The EnumProperty is used to define a property with pre-defined
* list of choices.
*/
export class EnumProperty<
const Values extends unknown
> extends BaseProperty<Values> {
protected dataType: {
name: "enum"
options: { choices: Values[] }
}
constructor(values: Values[]) {
super()
this.dataType = { name: "enum", options: { choices: values } }
}
}
@@ -0,0 +1,11 @@
import { BaseProperty } from "./base"
/**
* The JSONProperty is used to define a property that stores
* data as a JSON string
*/
export class JSONProperty extends BaseProperty<string> {
protected dataType = {
name: "json",
} as const
}
@@ -1,12 +1,11 @@
import { RelationshipType, SchemaType } from "../types"
import { PropertyType } from "../types"
/**
* Nullable modifier marks a schema node as nullable
*/
export class NullableModifier<
T,
Schema extends SchemaType<T> | RelationshipType<T>
> {
export class NullableModifier<T, Schema extends PropertyType<T>>
implements PropertyType<T | null>
{
/**
* A type-only property to infer the JavScript data-type
* of the schema property
@@ -26,9 +25,9 @@ export class NullableModifier<
/**
* Returns the serialized metadata
*/
parse(fieldName: string): ReturnType<Schema["parse"]> {
parse(fieldName: string) {
const schema = this.#schema.parse(fieldName)
schema.nullable = true
return schema as ReturnType<Schema["parse"]>
return schema
}
}
@@ -0,0 +1,11 @@
import { BaseProperty } from "./base"
/**
* The NumberProperty is used to define a numeric/integer
* property
*/
export class NumberProperty extends BaseProperty<number> {
protected dataType = {
name: "number",
} as const
}
@@ -0,0 +1,10 @@
import { BaseProperty } from "./base"
/**
* The TextProperty is used to define a textual property
*/
export class TextProperty extends BaseProperty<string> {
protected dataType = {
name: "text",
} as const
}
@@ -1,4 +1,4 @@
import { RelationshipType, SchemaType } from "../types"
import { RelationshipType, PropertyType } from "../types"
/**
* Nullable modifier marks a schema node as nullable
@@ -1,12 +0,0 @@
import { SchemaMetadata } from "../types"
import { BaseSchema } from "./base"
/**
* The BooleanSchema class is used to define a boolean
* property
*/
export class BooleanSchema extends BaseSchema<boolean> {
protected dataType: SchemaMetadata["dataType"] = {
name: "boolean",
}
}
@@ -1,12 +0,0 @@
import { SchemaMetadata } from "../types"
import { BaseSchema } from "./base"
/**
* The DateTimeSchema class is used to define a timestampz
* property
*/
export class DateTimeSchema extends BaseSchema<Date> {
protected dataType: SchemaMetadata["dataType"] = {
name: "dateTime",
}
}
@@ -1,19 +0,0 @@
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<Values> {
protected dataType: SchemaMetadata["dataType"] = {
name: "enum",
}
constructor(values: Values[]) {
super()
this.dataType.options = { choices: values }
}
}
@@ -1,12 +0,0 @@
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<string> {
protected dataType: SchemaMetadata["dataType"] = {
name: "json",
}
}
@@ -1,12 +0,0 @@
import { SchemaMetadata } from "../types"
import { BaseSchema } from "./base"
/**
* The NumberSchema is used to define a numeric/integer
* property
*/
export class NumberSchema extends BaseSchema<number> {
protected dataType: SchemaMetadata["dataType"] = {
name: "number",
}
}
@@ -1,11 +0,0 @@
import { SchemaMetadata } from "../types"
import { BaseSchema } from "./base"
/**
* The NumberSchema is used to define a textual property
*/
export class TextSchema extends BaseSchema<string> {
protected dataType: SchemaMetadata["dataType"] = {
name: "text",
}
}
+8 -17
View File
@@ -21,22 +21,12 @@ export type RelationshipTypes =
| "manyToMany"
/**
* 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.
* The meta-data returned by the property parse method
*/
export type MaybeFieldMetadata = {
nullable: boolean
}
/**
* The meta-data returned by the schema parse method
*/
export type SchemaMetadata = MaybeFieldMetadata & {
export type PropertyMetadata = {
fieldName: string
defaultValue?: any
nullable: boolean
dataType: {
name: KnownDataTypes
options?: Record<string, any>
@@ -49,13 +39,13 @@ export type SchemaMetadata = MaybeFieldMetadata & {
}
/**
* Definition of a schema type. It should have a parse
* Definition of a property 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> = {
export type PropertyType<T> = {
$dataType: T
parse(fieldName: string): SchemaMetadata
parse(fieldName: string): PropertyMetadata
}
/**
@@ -69,10 +59,11 @@ export type RelationshipOptions = {
* The meta-data returned by the relationship parse
* method
*/
export type RelationshipMetadata = MaybeFieldMetadata & {
export type RelationshipMetadata = {
name: string
type: RelationshipTypes
entity: unknown
nullable: boolean
mappedBy?: string
}