feat: add optional fields (#10150)
This commit is contained in:
1 parent
18a60e2d2e
commit
661ea7865c
17 files changed
+368
-7
No files matched your search
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"@medusajs/types": patch
|
||||||
|
"@medusajs/utils": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat: add optional fields
|
||||||
@@ -62,6 +62,7 @@ export type PropertyMetadata = {
|
|||||||
fieldName: string
|
fieldName: string
|
||||||
defaultValue?: any
|
defaultValue?: any
|
||||||
nullable: boolean
|
nullable: boolean
|
||||||
|
optional: boolean
|
||||||
dataType: {
|
dataType: {
|
||||||
name: KnownDataTypes
|
name: KnownDataTypes
|
||||||
options?: Record<string, any>
|
options?: Record<string, any>
|
||||||
@@ -177,12 +178,25 @@ export type InferHasManyFields<Relation> = Relation extends () => IDmlEntity<
|
|||||||
*/
|
*/
|
||||||
export type InferManyToManyFields<Relation> = InferHasManyFields<Relation>
|
export type InferManyToManyFields<Relation> = InferHasManyFields<Relation>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Only processed property that can be undefined and mark them as optional
|
||||||
|
*/
|
||||||
|
export type InferOptionalFields<Schema extends DMLSchema> = Prettify<{
|
||||||
|
[K in keyof Schema as undefined extends Schema[K]["$dataType"]
|
||||||
|
? K
|
||||||
|
: never]?: Schema[K]["$dataType"]
|
||||||
|
}>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inferring the types of the schema fields from the DML
|
* Inferring the types of the schema fields from the DML
|
||||||
* entity
|
* entity
|
||||||
*/
|
*/
|
||||||
export type InferSchemaFields<Schema extends DMLSchema> = Prettify<{
|
export type InferSchemaFields<Schema extends DMLSchema> = Prettify<
|
||||||
[K in keyof Schema]: Schema[K] extends RelationshipType<any>
|
{
|
||||||
|
// Omit optional properties to manage them separately and mark them as optional
|
||||||
|
[K in keyof Schema as undefined extends Schema[K]["$dataType"]
|
||||||
|
? never
|
||||||
|
: K]: Schema[K] extends RelationshipType<any>
|
||||||
? Schema[K]["type"] extends "belongsTo"
|
? Schema[K]["type"] extends "belongsTo"
|
||||||
? InferBelongsToFields<Schema[K]["$dataType"]>
|
? InferBelongsToFields<Schema[K]["$dataType"]>
|
||||||
: Schema[K]["type"] extends "hasOne"
|
: Schema[K]["type"] extends "hasOne"
|
||||||
@@ -193,7 +207,8 @@ export type InferSchemaFields<Schema extends DMLSchema> = Prettify<{
|
|||||||
? InferManyToManyFields<Schema[K]["$dataType"]>
|
? InferManyToManyFields<Schema[K]["$dataType"]>
|
||||||
: never
|
: never
|
||||||
: Schema[K]["$dataType"]
|
: Schema[K]["$dataType"]
|
||||||
}>
|
} & InferOptionalFields<Schema>
|
||||||
|
>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper to infer the schema type of a DmlEntity
|
* Helper to infer the schema type of a DmlEntity
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ describe("Array property", () => {
|
|||||||
name: "array",
|
name: "array",
|
||||||
},
|
},
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
optional: false,
|
||||||
indexes: [],
|
indexes: [],
|
||||||
relationships: [],
|
relationships: [],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
|
import { PropertyMetadata } from "@medusajs/types"
|
||||||
import { expectTypeOf } from "expect-type"
|
import { expectTypeOf } from "expect-type"
|
||||||
import { BaseProperty } from "../properties/base"
|
import { BaseProperty } from "../properties/base"
|
||||||
import { PropertyMetadata } from "@medusajs/types"
|
|
||||||
import { TextProperty } from "../properties/text"
|
import { TextProperty } from "../properties/text"
|
||||||
|
|
||||||
describe("Base property", () => {
|
describe("Base property", () => {
|
||||||
@@ -20,6 +20,7 @@ describe("Base property", () => {
|
|||||||
name: "text",
|
name: "text",
|
||||||
},
|
},
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
optional: false,
|
||||||
indexes: [],
|
indexes: [],
|
||||||
relationships: [],
|
relationships: [],
|
||||||
})
|
})
|
||||||
@@ -38,6 +39,7 @@ describe("Base property", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
optional: false,
|
||||||
indexes: [],
|
indexes: [],
|
||||||
relationships: [],
|
relationships: [],
|
||||||
})
|
})
|
||||||
@@ -59,6 +61,7 @@ describe("Base property", () => {
|
|||||||
name: "text",
|
name: "text",
|
||||||
},
|
},
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
optional: false,
|
||||||
indexes: [],
|
indexes: [],
|
||||||
relationships: [],
|
relationships: [],
|
||||||
})
|
})
|
||||||
@@ -81,6 +84,98 @@ describe("Base property", () => {
|
|||||||
},
|
},
|
||||||
defaultValue: "foo",
|
defaultValue: "foo",
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
optional: false,
|
||||||
|
indexes: [],
|
||||||
|
relationships: [],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test("apply optional modifier", () => {
|
||||||
|
class StringProperty extends BaseProperty<string> {
|
||||||
|
protected dataType: PropertyMetadata["dataType"] = {
|
||||||
|
name: "text",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const property = new StringProperty().optional()
|
||||||
|
|
||||||
|
expectTypeOf(property["$dataType"]).toEqualTypeOf<string | undefined>()
|
||||||
|
expect(property.parse("username")).toEqual({
|
||||||
|
fieldName: "username",
|
||||||
|
dataType: {
|
||||||
|
name: "text",
|
||||||
|
},
|
||||||
|
nullable: false,
|
||||||
|
optional: true,
|
||||||
|
indexes: [],
|
||||||
|
relationships: [],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test("apply optional and nullable modifier", () => {
|
||||||
|
class StringProperty extends BaseProperty<string> {
|
||||||
|
protected dataType: PropertyMetadata["dataType"] = {
|
||||||
|
name: "text",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const property = new StringProperty().optional().nullable()
|
||||||
|
expectTypeOf(property["$dataType"]).toEqualTypeOf<
|
||||||
|
string | undefined | null
|
||||||
|
>()
|
||||||
|
expect(property.parse("username")).toEqual({
|
||||||
|
fieldName: "username",
|
||||||
|
dataType: {
|
||||||
|
name: "text",
|
||||||
|
},
|
||||||
|
nullable: true,
|
||||||
|
optional: true,
|
||||||
|
indexes: [],
|
||||||
|
relationships: [],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test("apply nullable and optional modifier", () => {
|
||||||
|
class StringProperty extends BaseProperty<string> {
|
||||||
|
protected dataType: PropertyMetadata["dataType"] = {
|
||||||
|
name: "text",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const property = new StringProperty().nullable().optional()
|
||||||
|
expectTypeOf(property["$dataType"]).toEqualTypeOf<
|
||||||
|
string | null | undefined
|
||||||
|
>()
|
||||||
|
expect(property.parse("username")).toEqual({
|
||||||
|
fieldName: "username",
|
||||||
|
dataType: {
|
||||||
|
name: "text",
|
||||||
|
},
|
||||||
|
nullable: true,
|
||||||
|
optional: true,
|
||||||
|
indexes: [],
|
||||||
|
relationships: [],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
test("define default value as a callback", () => {
|
||||||
|
class StringProperty extends BaseProperty<string> {
|
||||||
|
protected dataType: PropertyMetadata["dataType"] = {
|
||||||
|
name: "text",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const property = new StringProperty().default(() => "22")
|
||||||
|
|
||||||
|
expectTypeOf(property["$dataType"]).toEqualTypeOf<string>()
|
||||||
|
expect(property.parse("username")).toEqual({
|
||||||
|
fieldName: "username",
|
||||||
|
dataType: {
|
||||||
|
name: "text",
|
||||||
|
},
|
||||||
|
defaultValue: expect.any(Function),
|
||||||
|
nullable: false,
|
||||||
|
optional: false,
|
||||||
indexes: [],
|
indexes: [],
|
||||||
relationships: [],
|
relationships: [],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ describe("Big Number property", () => {
|
|||||||
name: "bigNumber",
|
name: "bigNumber",
|
||||||
},
|
},
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
optional: false,
|
||||||
indexes: [],
|
indexes: [],
|
||||||
relationships: [],
|
relationships: [],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ describe("Boolean property", () => {
|
|||||||
name: "boolean",
|
name: "boolean",
|
||||||
},
|
},
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
optional: false,
|
||||||
indexes: [],
|
indexes: [],
|
||||||
relationships: [],
|
relationships: [],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ describe("DateTime property", () => {
|
|||||||
name: "dateTime",
|
name: "dateTime",
|
||||||
},
|
},
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
optional: false,
|
||||||
indexes: [],
|
indexes: [],
|
||||||
relationships: [],
|
relationships: [],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
toMikroOrmEntities,
|
toMikroOrmEntities,
|
||||||
toMikroORMEntity,
|
toMikroORMEntity,
|
||||||
} from "../helpers/create-mikro-orm-entity"
|
} from "../helpers/create-mikro-orm-entity"
|
||||||
|
import { InferTypeOf } from "@medusajs/types"
|
||||||
|
|
||||||
describe("Entity builder", () => {
|
describe("Entity builder", () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -1447,6 +1448,130 @@ describe("Entity builder", () => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("define a property with default runtime value", () => {
|
||||||
|
const user = model.define("user", {
|
||||||
|
id: model.number(),
|
||||||
|
username: model.text().default((schema) => {
|
||||||
|
const { email } = schema as InferTypeOf<typeof user>
|
||||||
|
return email.replace(/\@.*/, "")
|
||||||
|
}),
|
||||||
|
email: model.text(),
|
||||||
|
spend_limit: model.bigNumber().default(500.4),
|
||||||
|
})
|
||||||
|
|
||||||
|
const User = toMikroORMEntity(user)
|
||||||
|
expectTypeOf(new User()).toMatchTypeOf<{
|
||||||
|
id: number
|
||||||
|
username: string
|
||||||
|
email: string
|
||||||
|
deleted_at: Date | null
|
||||||
|
}>()
|
||||||
|
|
||||||
|
const metaData = MetadataStorage.getMetadataFromDecorator(User)
|
||||||
|
expect(metaData.className).toEqual("User")
|
||||||
|
expect(metaData.path).toEqual("User")
|
||||||
|
|
||||||
|
expect(metaData.filters).toEqual({
|
||||||
|
softDeletable: {
|
||||||
|
name: "softDeletable",
|
||||||
|
cond: expect.any(Function),
|
||||||
|
default: true,
|
||||||
|
args: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
expect(metaData.properties).toEqual({
|
||||||
|
id: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "number",
|
||||||
|
columnType: "integer",
|
||||||
|
name: "id",
|
||||||
|
fieldName: "id",
|
||||||
|
nullable: false,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
username: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "string",
|
||||||
|
default: expect.any(Function),
|
||||||
|
columnType: "text",
|
||||||
|
name: "username",
|
||||||
|
fieldName: "username",
|
||||||
|
nullable: false,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
email: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "string",
|
||||||
|
columnType: "text",
|
||||||
|
name: "email",
|
||||||
|
fieldName: "email",
|
||||||
|
nullable: false,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
spend_limit: {
|
||||||
|
columnType: "numeric",
|
||||||
|
default: 500.4,
|
||||||
|
getter: true,
|
||||||
|
name: "spend_limit",
|
||||||
|
fieldName: "spend_limit",
|
||||||
|
nullable: false,
|
||||||
|
reference: "scalar",
|
||||||
|
setter: true,
|
||||||
|
trackChanges: false,
|
||||||
|
type: "any",
|
||||||
|
},
|
||||||
|
raw_spend_limit: {
|
||||||
|
columnType: "jsonb",
|
||||||
|
getter: false,
|
||||||
|
name: "raw_spend_limit",
|
||||||
|
fieldName: "raw_spend_limit",
|
||||||
|
nullable: false,
|
||||||
|
reference: "scalar",
|
||||||
|
setter: false,
|
||||||
|
type: "any",
|
||||||
|
},
|
||||||
|
created_at: {
|
||||||
|
reference: "scalar",
|
||||||
|
type: "date",
|
||||||
|
columnType: "timestamptz",
|
||||||
|
name: "created_at",
|
||||||
|
fieldName: "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",
|
||||||
|
fieldName: "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",
|
||||||
|
fieldName: "deleted_at",
|
||||||
|
nullable: true,
|
||||||
|
getter: false,
|
||||||
|
setter: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("Entity builder | id", () => {
|
describe("Entity builder | id", () => {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ describe("Enum property", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
optional: false,
|
||||||
indexes: [],
|
indexes: [],
|
||||||
relationships: [],
|
relationships: [],
|
||||||
})
|
})
|
||||||
@@ -42,6 +43,7 @@ describe("Enum property", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
nullable: true,
|
nullable: true,
|
||||||
|
optional: false,
|
||||||
indexes: [],
|
indexes: [],
|
||||||
relationships: [],
|
relationships: [],
|
||||||
})
|
})
|
||||||
@@ -66,6 +68,7 @@ describe("Enum property", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
optional: false,
|
||||||
indexes: [],
|
indexes: [],
|
||||||
relationships: [],
|
relationships: [],
|
||||||
})
|
})
|
||||||
@@ -90,6 +93,7 @@ describe("Enum property", () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
optional: false,
|
||||||
indexes: [],
|
indexes: [],
|
||||||
relationships: [],
|
relationships: [],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ describe("Id property", () => {
|
|||||||
options: {},
|
options: {},
|
||||||
},
|
},
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
optional: false,
|
||||||
indexes: [],
|
indexes: [],
|
||||||
relationships: [],
|
relationships: [],
|
||||||
})
|
})
|
||||||
@@ -29,6 +30,7 @@ describe("Id property", () => {
|
|||||||
options: {},
|
options: {},
|
||||||
},
|
},
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
optional: false,
|
||||||
indexes: [],
|
indexes: [],
|
||||||
relationships: [],
|
relationships: [],
|
||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ describe("JSON property", () => {
|
|||||||
name: "json",
|
name: "json",
|
||||||
},
|
},
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
optional: false,
|
||||||
indexes: [],
|
indexes: [],
|
||||||
relationships: [],
|
relationships: [],
|
||||||
})
|
})
|
||||||
@@ -30,6 +31,7 @@ describe("JSON property", () => {
|
|||||||
a: 1,
|
a: 1,
|
||||||
},
|
},
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
optional: false,
|
||||||
indexes: [],
|
indexes: [],
|
||||||
relationships: [],
|
relationships: [],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ describe("Number property", () => {
|
|||||||
options: {},
|
options: {},
|
||||||
},
|
},
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
optional: false,
|
||||||
indexes: [],
|
indexes: [],
|
||||||
relationships: [],
|
relationships: [],
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ describe("Text property", () => {
|
|||||||
options: { searchable: false },
|
options: { searchable: false },
|
||||||
},
|
},
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
optional: false,
|
||||||
indexes: [],
|
indexes: [],
|
||||||
relationships: [],
|
relationships: [],
|
||||||
})
|
})
|
||||||
@@ -29,6 +30,7 @@ describe("Text property", () => {
|
|||||||
options: { searchable: false },
|
options: { searchable: false },
|
||||||
},
|
},
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
optional: false,
|
||||||
indexes: [],
|
indexes: [],
|
||||||
relationships: [],
|
relationships: [],
|
||||||
primaryKey: true,
|
primaryKey: true,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { PropertyMetadata, PropertyType } from "@medusajs/types"
|
import { PropertyMetadata, PropertyType } from "@medusajs/types"
|
||||||
import { NullableModifier } from "./nullable"
|
import { NullableModifier } from "./nullable"
|
||||||
|
import { OptionalModifier } from "./optional"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The BaseProperty class offers shared affordances to define
|
* The BaseProperty class offers shared affordances to define
|
||||||
@@ -15,7 +16,7 @@ export abstract class BaseProperty<T> implements PropertyType<T> {
|
|||||||
/**
|
/**
|
||||||
* Default value for the property
|
* Default value for the property
|
||||||
*/
|
*/
|
||||||
#defaultValue?: T
|
#defaultValue?: T | ((schema: any) => T)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The runtime dataType for the schema. It is not the same as
|
* The runtime dataType for the schema. It is not the same as
|
||||||
@@ -48,6 +49,25 @@ export abstract class BaseProperty<T> implements PropertyType<T> {
|
|||||||
return new NullableModifier<T, this>(this)
|
return new NullableModifier<T, this>(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method indicates that a property's value can be `optional`.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* import { model } from "@medusajs/framework/utils"
|
||||||
|
*
|
||||||
|
* const MyCustom = model.define("my_custom", {
|
||||||
|
* price: model.bigNumber().optional(),
|
||||||
|
* // ...
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* export default MyCustom
|
||||||
|
*
|
||||||
|
* @customNamespace Property Configuration Methods
|
||||||
|
*/
|
||||||
|
optional() {
|
||||||
|
return new OptionalModifier<T, this>(this)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method defines an index on a property.
|
* This method defines an index on a property.
|
||||||
*
|
*
|
||||||
@@ -119,7 +139,7 @@ export abstract class BaseProperty<T> implements PropertyType<T> {
|
|||||||
*
|
*
|
||||||
* @customNamespace Property Configuration Methods
|
* @customNamespace Property Configuration Methods
|
||||||
*/
|
*/
|
||||||
default(value: T) {
|
default(value: T | ((schema: any) => T)) {
|
||||||
this.#defaultValue = value
|
this.#defaultValue = value
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
@@ -132,6 +152,7 @@ export abstract class BaseProperty<T> implements PropertyType<T> {
|
|||||||
fieldName,
|
fieldName,
|
||||||
dataType: this.dataType,
|
dataType: this.dataType,
|
||||||
nullable: false,
|
nullable: false,
|
||||||
|
optional: false,
|
||||||
defaultValue: this.#defaultValue,
|
defaultValue: this.#defaultValue,
|
||||||
indexes: this.#indexes,
|
indexes: this.#indexes,
|
||||||
relationships: this.#relationships,
|
relationships: this.#relationships,
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ export * from "./enum"
|
|||||||
export * from "./id"
|
export * from "./id"
|
||||||
export * from "./json"
|
export * from "./json"
|
||||||
export * from "./nullable"
|
export * from "./nullable"
|
||||||
|
export * from "./optional"
|
||||||
export * from "./number"
|
export * from "./number"
|
||||||
export * from "./primary-key"
|
export * from "./primary-key"
|
||||||
export * from "./text"
|
export * from "./text"
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
import { PropertyType } from "@medusajs/types"
|
import { PropertyType } from "@medusajs/types"
|
||||||
|
import { OptionalModifier } from "./optional"
|
||||||
|
|
||||||
const IsNullableModifier = Symbol.for("isNullableModifier")
|
const IsNullableModifier = Symbol.for("isNullableModifier")
|
||||||
/**
|
/**
|
||||||
* Nullable modifier marks a schema node as nullable
|
* Nullable modifier marks a schema node as nullable
|
||||||
*/
|
*/
|
||||||
export class NullableModifier<T, Schema extends PropertyType<T>>
|
export class NullableModifier<T, Schema extends PropertyType<NoInfer<T>>>
|
||||||
implements PropertyType<T | null>
|
implements PropertyType<T | null>
|
||||||
{
|
{
|
||||||
[IsNullableModifier]: true = true
|
[IsNullableModifier]: true = true
|
||||||
@@ -12,6 +13,7 @@ export class NullableModifier<T, Schema extends PropertyType<T>>
|
|||||||
static isNullableModifier(obj: any): obj is NullableModifier<any, any> {
|
static isNullableModifier(obj: any): obj is NullableModifier<any, any> {
|
||||||
return !!obj?.[IsNullableModifier]
|
return !!obj?.[IsNullableModifier]
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A type-only property to infer the JavScript data-type
|
* A type-only property to infer the JavScript data-type
|
||||||
* of the schema property
|
* of the schema property
|
||||||
@@ -28,6 +30,25 @@ export class NullableModifier<T, Schema extends PropertyType<T>>
|
|||||||
this.#schema = schema
|
this.#schema = schema
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method indicates that a property's value can be `optional`.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* import { model } from "@medusajs/framework/utils"
|
||||||
|
*
|
||||||
|
* const MyCustom = model.define("my_custom", {
|
||||||
|
* price: model.bigNumber().optional(),
|
||||||
|
* // ...
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* export default MyCustom
|
||||||
|
*
|
||||||
|
* @customNamespace Property Configuration Methods
|
||||||
|
*/
|
||||||
|
optional() {
|
||||||
|
return new OptionalModifier<T | null, this>(this)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the serialized metadata
|
* Returns the serialized metadata
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import { PropertyType } from "@medusajs/types"
|
||||||
|
import { NullableModifier } from "./nullable"
|
||||||
|
|
||||||
|
const IsOptionalModifier = Symbol.for("IsOptionalModifier")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Nullable modifier marks a schema node as optional and
|
||||||
|
* allows for default values
|
||||||
|
*/
|
||||||
|
export class OptionalModifier<T, Schema extends PropertyType<T>>
|
||||||
|
implements PropertyType<T | undefined>
|
||||||
|
{
|
||||||
|
[IsOptionalModifier]: true = true
|
||||||
|
|
||||||
|
static isOptionalModifier(obj: any): obj is OptionalModifier<any, any> {
|
||||||
|
return !!obj?.[IsOptionalModifier]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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: Schema
|
||||||
|
|
||||||
|
constructor(schema: Schema) {
|
||||||
|
this.#schema = schema
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method indicates that a property's value can be `null`.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* import { model } from "@medusajs/framework/utils"
|
||||||
|
*
|
||||||
|
* const MyCustom = model.define("my_custom", {
|
||||||
|
* price: model.bigNumber().optional().nullable(),
|
||||||
|
* // ...
|
||||||
|
* })
|
||||||
|
*
|
||||||
|
* export default MyCustom
|
||||||
|
*
|
||||||
|
* @customNamespace Property Configuration Methods
|
||||||
|
*/
|
||||||
|
nullable() {
|
||||||
|
return new NullableModifier<T | undefined, this>(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the serialized metadata
|
||||||
|
*/
|
||||||
|
parse(fieldName: string) {
|
||||||
|
const schema = this.#schema.parse(fieldName)
|
||||||
|
schema.optional = true
|
||||||
|
return schema
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user