Mark keys as primary with explicit method call (#7900)
This commit is contained in:
@@ -1291,7 +1291,8 @@ describe("Entity builder", () => {
|
||||
columnType: "text",
|
||||
name: "id",
|
||||
nullable: false,
|
||||
primary: true,
|
||||
getter: false,
|
||||
setter: false,
|
||||
},
|
||||
username: {
|
||||
reference: "scalar",
|
||||
@@ -1346,9 +1347,9 @@ describe("Entity builder", () => {
|
||||
})
|
||||
})
|
||||
|
||||
test("mark id as non-primary", () => {
|
||||
test("mark id as primary", () => {
|
||||
const user = model.define("user", {
|
||||
id: model.id({ primaryKey: false }),
|
||||
id: model.id().primaryKey(),
|
||||
username: model.text(),
|
||||
email: model.text(),
|
||||
})
|
||||
@@ -1391,8 +1392,7 @@ describe("Entity builder", () => {
|
||||
columnType: "text",
|
||||
name: "id",
|
||||
nullable: false,
|
||||
getter: false,
|
||||
setter: false,
|
||||
primary: true,
|
||||
},
|
||||
username: {
|
||||
reference: "scalar",
|
||||
@@ -1451,7 +1451,7 @@ describe("Entity builder", () => {
|
||||
|
||||
test("define prefix for the id", () => {
|
||||
const user = model.define("user", {
|
||||
id: model.id({ primaryKey: false, prefix: "us" }),
|
||||
id: model.id({ prefix: "us" }).primaryKey(),
|
||||
username: model.text(),
|
||||
email: model.text(),
|
||||
})
|
||||
@@ -1494,8 +1494,7 @@ describe("Entity builder", () => {
|
||||
columnType: "text",
|
||||
name: "id",
|
||||
nullable: false,
|
||||
getter: false,
|
||||
setter: false,
|
||||
primary: true,
|
||||
},
|
||||
username: {
|
||||
reference: "scalar",
|
||||
@@ -1554,124 +1553,25 @@ describe("Entity builder", () => {
|
||||
})
|
||||
|
||||
describe("Entity builder | primaryKey", () => {
|
||||
test("should create both id fields and primaryKey fields", () => {
|
||||
test("should infer primaryKeys from a model", () => {
|
||||
const user = model.define("user", {
|
||||
id: model.id(),
|
||||
id: model.id().primaryKey(),
|
||||
email: model.text().primaryKey(),
|
||||
account_id: model.number().primaryKey(),
|
||||
account_id: model.number(),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
const User = entityBuilder(user)
|
||||
|
||||
expectTypeOf(new User()).toMatchTypeOf<{
|
||||
id: string
|
||||
email: string
|
||||
account_id: number
|
||||
}>()
|
||||
|
||||
const metaData = MetadataStorage.getMetadataFromDecorator(User)
|
||||
|
||||
expect(metaData.properties).toEqual({
|
||||
id: {
|
||||
reference: "scalar",
|
||||
type: "string",
|
||||
columnType: "text",
|
||||
name: "id",
|
||||
nullable: false,
|
||||
getter: false,
|
||||
setter: false,
|
||||
},
|
||||
email: {
|
||||
columnType: "text",
|
||||
name: "email",
|
||||
nullable: false,
|
||||
primary: true,
|
||||
reference: "scalar",
|
||||
type: "string",
|
||||
},
|
||||
account_id: {
|
||||
columnType: "integer",
|
||||
name: "account_id",
|
||||
nullable: false,
|
||||
primary: true,
|
||||
reference: "scalar",
|
||||
type: "number",
|
||||
},
|
||||
created_at: {
|
||||
reference: "scalar",
|
||||
type: "date",
|
||||
columnType: "timestamptz",
|
||||
name: "created_at",
|
||||
defaultRaw: "now()",
|
||||
onCreate: expect.any(Function),
|
||||
nullable: false,
|
||||
getter: false,
|
||||
setter: false,
|
||||
},
|
||||
updated_at: {
|
||||
reference: "scalar",
|
||||
type: "date",
|
||||
columnType: "timestamptz",
|
||||
name: "updated_at",
|
||||
defaultRaw: "now()",
|
||||
onCreate: expect.any(Function),
|
||||
onUpdate: expect.any(Function),
|
||||
nullable: false,
|
||||
getter: false,
|
||||
setter: false,
|
||||
},
|
||||
deleted_at: {
|
||||
reference: "scalar",
|
||||
type: "date",
|
||||
columnType: "timestamptz",
|
||||
name: "deleted_at",
|
||||
nullable: true,
|
||||
getter: false,
|
||||
setter: false,
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
test("should infer primaryKeys from a model", () => {
|
||||
let user = model.define("user", {
|
||||
id: model.id(),
|
||||
email: model.text(),
|
||||
account_id: model.number(),
|
||||
})
|
||||
|
||||
const entityBuilder = createMikrORMEntity()
|
||||
let User = entityBuilder(user)
|
||||
let metaData = MetadataStorage.getMetadataFromDecorator(User)
|
||||
|
||||
expect(metaData.properties.id).toEqual({
|
||||
columnType: "text",
|
||||
name: "id",
|
||||
nullable: false,
|
||||
reference: "scalar",
|
||||
type: "string",
|
||||
primary: true,
|
||||
reference: "scalar",
|
||||
type: "string",
|
||||
})
|
||||
|
||||
user = model.define("user", {
|
||||
id: model.id(),
|
||||
email: model.text().primaryKey(),
|
||||
account_id: model.number(),
|
||||
})
|
||||
|
||||
User = entityBuilder(user)
|
||||
metaData = MetadataStorage.getMetadataFromDecorator(User)
|
||||
|
||||
expect(metaData.properties.id).toEqual({
|
||||
columnType: "text",
|
||||
name: "id",
|
||||
nullable: false,
|
||||
reference: "scalar",
|
||||
type: "string",
|
||||
getter: false,
|
||||
setter: false,
|
||||
})
|
||||
|
||||
expect(metaData.properties.email).toEqual({
|
||||
columnType: "text",
|
||||
name: "email",
|
||||
@@ -1680,53 +1580,6 @@ describe("Entity builder", () => {
|
||||
type: "string",
|
||||
primary: true,
|
||||
})
|
||||
|
||||
expect(metaData.properties.account_id).toEqual({
|
||||
columnType: "integer",
|
||||
name: "account_id",
|
||||
nullable: false,
|
||||
reference: "scalar",
|
||||
type: "number",
|
||||
getter: false,
|
||||
setter: false,
|
||||
})
|
||||
|
||||
user = model.define("user", {
|
||||
id: model.id(),
|
||||
email: model.text().primaryKey(),
|
||||
account_id: model.number().primaryKey(),
|
||||
})
|
||||
|
||||
User = entityBuilder(user)
|
||||
metaData = MetadataStorage.getMetadataFromDecorator(User)
|
||||
|
||||
expect(metaData.properties.id).toEqual({
|
||||
columnType: "text",
|
||||
name: "id",
|
||||
nullable: false,
|
||||
reference: "scalar",
|
||||
type: "string",
|
||||
getter: false,
|
||||
setter: false,
|
||||
})
|
||||
|
||||
expect(metaData.properties.email).toEqual({
|
||||
columnType: "text",
|
||||
name: "email",
|
||||
nullable: false,
|
||||
reference: "scalar",
|
||||
type: "string",
|
||||
primary: true,
|
||||
})
|
||||
|
||||
expect(metaData.properties.account_id).toEqual({
|
||||
columnType: "integer",
|
||||
name: "account_id",
|
||||
nullable: false,
|
||||
reference: "scalar",
|
||||
type: "number",
|
||||
primary: true,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -5,24 +5,6 @@ describe("Id property", () => {
|
||||
test("create id property type", () => {
|
||||
const property = new IdProperty()
|
||||
|
||||
expectTypeOf(property["$dataType"]).toEqualTypeOf<string>()
|
||||
expect(property.parse("id")).toEqual({
|
||||
fieldName: "id",
|
||||
dataType: {
|
||||
name: "id",
|
||||
options: {
|
||||
primaryKey: true,
|
||||
},
|
||||
},
|
||||
nullable: false,
|
||||
indexes: [],
|
||||
relationships: [],
|
||||
})
|
||||
})
|
||||
|
||||
test("create id property type with marking it as a primary key", () => {
|
||||
const property = new IdProperty({ primaryKey: false })
|
||||
|
||||
expectTypeOf(property["$dataType"]).toEqualTypeOf<string>()
|
||||
expect(property.parse("id")).toEqual({
|
||||
fieldName: "id",
|
||||
@@ -37,4 +19,22 @@ describe("Id property", () => {
|
||||
relationships: [],
|
||||
})
|
||||
})
|
||||
|
||||
test("create id property type with marking it as a primary key", () => {
|
||||
const property = new IdProperty().primaryKey()
|
||||
|
||||
expectTypeOf(property["$dataType"]).toEqualTypeOf<string>()
|
||||
expect(property.parse("id")).toEqual({
|
||||
fieldName: "id",
|
||||
dataType: {
|
||||
name: "id",
|
||||
options: {
|
||||
primaryKey: true,
|
||||
},
|
||||
},
|
||||
nullable: false,
|
||||
indexes: [],
|
||||
relationships: [],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -17,4 +17,20 @@ describe("Text property", () => {
|
||||
relationships: [],
|
||||
})
|
||||
})
|
||||
|
||||
test("mark text property as primary key", () => {
|
||||
const property = new TextProperty().primaryKey()
|
||||
|
||||
expectTypeOf(property["$dataType"]).toEqualTypeOf<string>()
|
||||
expect(property.parse("username")).toEqual({
|
||||
fieldName: "username",
|
||||
dataType: {
|
||||
name: "text",
|
||||
options: { primaryKey: true, searchable: false },
|
||||
},
|
||||
nullable: false,
|
||||
indexes: [],
|
||||
relationships: [],
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -2,7 +2,6 @@ import type { DMLSchema, RelationshipOptions } from "@medusajs/types"
|
||||
import { DmlEntity } from "./entity"
|
||||
import { createBigNumberProperties } from "./helpers/entity-builder/create-big-number-properties"
|
||||
import { createDefaultProperties } from "./helpers/entity-builder/create-default-properties"
|
||||
import { inferPrimaryKeyProperties } from "./helpers/entity-builder/infer-primary-key-properties"
|
||||
import { ArrayProperty } from "./properties/array"
|
||||
import { BigNumberProperty } from "./properties/big-number"
|
||||
import { BooleanProperty } from "./properties/boolean"
|
||||
@@ -22,43 +21,45 @@ import { ManyToMany } from "./relations/many-to-many"
|
||||
*/
|
||||
const IMPLICIT_PROPERTIES = ["created_at", "updated_at", "deleted_at"]
|
||||
|
||||
export type DefineOptions = string | {
|
||||
/**
|
||||
* The data model's name.
|
||||
*/
|
||||
name?: string
|
||||
/**
|
||||
* The name of the data model's table in the database.
|
||||
*/
|
||||
tableName: string
|
||||
}
|
||||
export type DefineOptions =
|
||||
| string
|
||||
| {
|
||||
/**
|
||||
* The data model's name.
|
||||
*/
|
||||
name?: string
|
||||
/**
|
||||
* The name of the data model's table in the database.
|
||||
*/
|
||||
tableName: string
|
||||
}
|
||||
|
||||
export type ManyToManyOptions = RelationshipOptions &
|
||||
(
|
||||
| {
|
||||
/**
|
||||
* The name of the pivot table
|
||||
* created in the database for this relationship.
|
||||
*/
|
||||
pivotTable?: string
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
pivotEntity?: never
|
||||
}
|
||||
| {
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
pivotTable?: never
|
||||
/**
|
||||
* A function that returns the data model
|
||||
* representing the pivot table created in the
|
||||
* database for this relationship.
|
||||
*/
|
||||
pivotEntity?: () => DmlEntity<any>
|
||||
}
|
||||
)
|
||||
(
|
||||
| {
|
||||
/**
|
||||
* The name of the pivot table
|
||||
* created in the database for this relationship.
|
||||
*/
|
||||
pivotTable?: string
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
pivotEntity?: never
|
||||
}
|
||||
| {
|
||||
/**
|
||||
* @ignore
|
||||
*/
|
||||
pivotTable?: never
|
||||
/**
|
||||
* A function that returns the data model
|
||||
* representing the pivot table created in the
|
||||
* database for this relationship.
|
||||
*/
|
||||
pivotEntity?: () => DmlEntity<any>
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* Entity builder exposes the API to create an entity and define its
|
||||
@@ -81,21 +82,21 @@ export class EntityBuilder {
|
||||
|
||||
/**
|
||||
* This method defines a data model.
|
||||
*
|
||||
*
|
||||
* @typeParam Schema - The type of the accepted schema in the second parameter of the method.
|
||||
*
|
||||
*
|
||||
* @param {DefineOptions} nameOrConfig - Either the data model's name, or configurations to name the data model.
|
||||
* The data model's name must be unique.
|
||||
* @param {Schema} schema - The schema of the data model's properties.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* import { model } from "@medusajs/utils"
|
||||
*
|
||||
*
|
||||
* const MyCustom = model.define("my_custom", {
|
||||
* id: model.id(),
|
||||
* name: model.text(),
|
||||
* })
|
||||
*
|
||||
*
|
||||
* export default MyCustom
|
||||
*/
|
||||
define<Schema extends DMLSchema>(
|
||||
@@ -103,7 +104,6 @@ export class EntityBuilder {
|
||||
schema: Schema
|
||||
) {
|
||||
this.#disallowImplicitProperties(schema)
|
||||
schema = inferPrimaryKeyProperties(schema)
|
||||
|
||||
return new DmlEntity(nameOrConfig, {
|
||||
...schema,
|
||||
@@ -114,40 +114,39 @@ export class EntityBuilder {
|
||||
|
||||
/**
|
||||
* This method defines an automatically generated string ID property.
|
||||
*
|
||||
* By default, this property is considered to be the data model’s primary key.
|
||||
*
|
||||
* @param {ConstructorParameters<typeof IdProperty>[0]} options - The ID's options.
|
||||
*
|
||||
*
|
||||
* You must use the "primaryKey" modifier to mark the property as the
|
||||
* primary key.
|
||||
*
|
||||
* @example
|
||||
* import { model } from "@medusajs/utils"
|
||||
*
|
||||
* const MyCustom = model.define("my_custom", {
|
||||
* id: model.id(),
|
||||
*
|
||||
* const User = model.define("User", {
|
||||
* id: model.id().primaryKey(),
|
||||
* // ...
|
||||
* })
|
||||
*
|
||||
* export default MyCustom
|
||||
*
|
||||
*
|
||||
* export default User
|
||||
*
|
||||
* @customNamespace Property Types
|
||||
*/
|
||||
id(options?: ConstructorParameters<typeof IdProperty>[0]) {
|
||||
id(options?: { prefix?: string }) {
|
||||
return new IdProperty(options)
|
||||
}
|
||||
|
||||
/**
|
||||
* This method defines a string property.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* import { model } from "@medusajs/utils"
|
||||
*
|
||||
*
|
||||
* const MyCustom = model.define("my_custom", {
|
||||
* name: model.text(),
|
||||
* // ...
|
||||
* })
|
||||
*
|
||||
*
|
||||
* export default MyCustom
|
||||
*
|
||||
*
|
||||
* @customNamespace Property Types
|
||||
*/
|
||||
text() {
|
||||
@@ -156,17 +155,17 @@ export class EntityBuilder {
|
||||
|
||||
/**
|
||||
* This method defines a boolean property.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* import { model } from "@medusajs/utils"
|
||||
*
|
||||
*
|
||||
* const MyCustom = model.define("my_custom", {
|
||||
* hasAccount: model.boolean(),
|
||||
* // ...
|
||||
* })
|
||||
*
|
||||
*
|
||||
* export default MyCustom
|
||||
*
|
||||
*
|
||||
* @customNamespace Property Types
|
||||
*/
|
||||
boolean() {
|
||||
@@ -175,17 +174,17 @@ export class EntityBuilder {
|
||||
|
||||
/**
|
||||
* This method defines a number property.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* import { model } from "@medusajs/utils"
|
||||
*
|
||||
*
|
||||
* const MyCustom = model.define("my_custom", {
|
||||
* age: model.number(),
|
||||
* // ...
|
||||
* })
|
||||
*
|
||||
*
|
||||
* export default MyCustom
|
||||
*
|
||||
*
|
||||
* @customNamespace Property Types
|
||||
*/
|
||||
number() {
|
||||
@@ -194,19 +193,19 @@ export class EntityBuilder {
|
||||
|
||||
/**
|
||||
* This method defines a number property that expects large numbers, such as prices.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* import { model } from "@medusajs/utils"
|
||||
*
|
||||
*
|
||||
* const MyCustom = model.define("my_custom", {
|
||||
* price: model.bigNumber(),
|
||||
* // ...
|
||||
* })
|
||||
*
|
||||
*
|
||||
* export default MyCustom
|
||||
*
|
||||
*
|
||||
* @customNamespace Property Types
|
||||
*
|
||||
*
|
||||
* @privateRemarks
|
||||
* This property produces an additional
|
||||
* column - raw_{{ property_name }}, which stores the configuration
|
||||
@@ -218,17 +217,17 @@ export class EntityBuilder {
|
||||
|
||||
/**
|
||||
* This method defines an array of strings property.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* import { model } from "@medusajs/utils"
|
||||
*
|
||||
*
|
||||
* const MyCustom = model.define("my_custom", {
|
||||
* names: model.array(),
|
||||
* // ...
|
||||
* })
|
||||
*
|
||||
*
|
||||
* export default MyCustom
|
||||
*
|
||||
*
|
||||
* @customNamespace Property Types
|
||||
*/
|
||||
array() {
|
||||
@@ -237,17 +236,17 @@ export class EntityBuilder {
|
||||
|
||||
/**
|
||||
* This method defines a timestamp property.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* import { model } from "@medusajs/utils"
|
||||
*
|
||||
*
|
||||
* const MyCustom = model.define("my_custom", {
|
||||
* date_of_birth: model.dateTime(),
|
||||
* // ...
|
||||
* })
|
||||
*
|
||||
*
|
||||
* export default MyCustom
|
||||
*
|
||||
*
|
||||
* @customNamespace Property Types
|
||||
*/
|
||||
dateTime() {
|
||||
@@ -256,17 +255,17 @@ export class EntityBuilder {
|
||||
|
||||
/**
|
||||
* This method defines a property whose value is a stringified JSON object.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* import { model } from "@medusajs/utils"
|
||||
*
|
||||
*
|
||||
* const MyCustom = model.define("my_custom", {
|
||||
* metadata: model.json(),
|
||||
* // ...
|
||||
* })
|
||||
*
|
||||
*
|
||||
* export default MyCustom
|
||||
*
|
||||
*
|
||||
* @customNamespace Property Types
|
||||
*/
|
||||
json() {
|
||||
@@ -275,21 +274,21 @@ export class EntityBuilder {
|
||||
|
||||
/**
|
||||
* This method defines a property whose value can only be one of the specified values.
|
||||
*
|
||||
*
|
||||
* @typeParam Values - The type of possible values. By default, it's `string`.
|
||||
*
|
||||
*
|
||||
* @param {Values[]} values - An array of possible values.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* import { model } from "@medusajs/utils"
|
||||
*
|
||||
*
|
||||
* const MyCustom = model.define("my_custom", {
|
||||
* color: model.enum(["black", "white"]),
|
||||
* // ...
|
||||
* })
|
||||
*
|
||||
*
|
||||
* export default MyCustom
|
||||
*
|
||||
*
|
||||
* @customNamespace Property Types
|
||||
*/
|
||||
enum<const Values extends unknown>(values: Values[]) {
|
||||
@@ -302,24 +301,24 @@ export class EntityBuilder {
|
||||
* data model.
|
||||
*
|
||||
* For example: A user "hasOne" email.
|
||||
*
|
||||
* Use the {@link belongsTo} method to define the inverse of this relationship in
|
||||
*
|
||||
* Use the {@link belongsTo} method to define the inverse of this relationship in
|
||||
* the other data model.
|
||||
*
|
||||
*
|
||||
* @typeParam T - The type of the entity builder passed as a first parameter. By default, it's
|
||||
* a function returning the related model.
|
||||
*
|
||||
*
|
||||
* @param {T} entityBuilder - A function that returns the data model this model is related to.
|
||||
* @param {RelationshipOptions} options - The relationship's options.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* import { model } from "@medusajs/utils"
|
||||
*
|
||||
*
|
||||
* const User = model.define("user", {
|
||||
* id: model.id(),
|
||||
* email: model.hasOne(() => Email),
|
||||
* })
|
||||
*
|
||||
*
|
||||
* @customNamespace Relationship Methods
|
||||
*/
|
||||
hasOne<T>(entityBuilder: T, options?: RelationshipOptions) {
|
||||
@@ -328,15 +327,15 @@ export class EntityBuilder {
|
||||
|
||||
/**
|
||||
* This method defines the inverse of the {@link hasOne} or {@link hasMany} relationship.
|
||||
*
|
||||
*
|
||||
* For example, a product "belongsTo" a store.
|
||||
*
|
||||
*
|
||||
* @typeParam T - The type of the entity builder passed as a first parameter. By default, it's
|
||||
* a function returning the related model.
|
||||
*
|
||||
*
|
||||
* @param {T} entityBuilder - A function that returns the data model this model is related to.
|
||||
* @param {RelationshipOptions} options - The relationship's options.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* const Product = model.define("product", {
|
||||
* id: model.id(),
|
||||
@@ -344,7 +343,7 @@ export class EntityBuilder {
|
||||
* mappedBy: "products",
|
||||
* }),
|
||||
* })
|
||||
*
|
||||
*
|
||||
* @customNamespace Relationship Methods
|
||||
*/
|
||||
belongsTo<T>(entityBuilder: T, options?: RelationshipOptions) {
|
||||
@@ -357,21 +356,21 @@ export class EntityBuilder {
|
||||
* data model, but the related data model only has one owner.
|
||||
*
|
||||
* For example, a store "hasMany" products.
|
||||
*
|
||||
*
|
||||
* @typeParam T - The type of the entity builder passed as a first parameter. By default, it's
|
||||
* a function returning the related model.
|
||||
*
|
||||
*
|
||||
* @param {T} entityBuilder - A function that returns the data model this model is related to.
|
||||
* @param {RelationshipOptions} options - The relationship's options.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* import { model } from "@medusajs/utils"
|
||||
*
|
||||
*
|
||||
* const Store = model.define("store", {
|
||||
* id: model.id(),
|
||||
* products: model.hasMany(() => Product),
|
||||
* })
|
||||
*
|
||||
*
|
||||
* @customNamespace Relationship Methods
|
||||
*/
|
||||
hasMany<T>(entityBuilder: T, options?: RelationshipOptions) {
|
||||
@@ -384,32 +383,29 @@ export class EntityBuilder {
|
||||
*
|
||||
* For example, an order is associated with many products, and a product
|
||||
* is associated with many orders.
|
||||
*
|
||||
*
|
||||
* @typeParam T - The type of the entity builder passed as a first parameter. By default, it's
|
||||
* a function returning the related model.
|
||||
*
|
||||
*
|
||||
* @param {T} entityBuilder - A function that returns the data model this model is related to.
|
||||
* @param {RelationshipOptions} options - The relationship's options.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* import { model } from "@medusajs/utils"
|
||||
*
|
||||
*
|
||||
* const Order = model.define("order", {
|
||||
* id: model.id(),
|
||||
* products: model.manyToMany(() => Product),
|
||||
* })
|
||||
*
|
||||
*
|
||||
* const Product = model.define("product", {
|
||||
* id: model.id(),
|
||||
* order: model.manyToMany(() => Order),
|
||||
* })
|
||||
*
|
||||
*
|
||||
* @customNamespace Relationship Methods
|
||||
*/
|
||||
manyToMany<T>(
|
||||
entityBuilder: T,
|
||||
options?: ManyToManyOptions
|
||||
) {
|
||||
manyToMany<T>(entityBuilder: T, options?: ManyToManyOptions) {
|
||||
return new ManyToMany<T>(entityBuilder, options || {})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,16 +96,16 @@ export class DmlEntity<Schema extends DMLSchema> implements IDmlEntity<Schema> {
|
||||
/**
|
||||
* This method configures which related data models an operation, such as deletion,
|
||||
* should be cascaded to.
|
||||
*
|
||||
*
|
||||
* For example, if a store is deleted, its product should also be deleted.
|
||||
*
|
||||
*
|
||||
* @param options - The cascades configurations. They object's keys are the names of
|
||||
* the actions, such as `deleted`, and the value is an array of relations that the
|
||||
* the actions, such as `deleted`, and the value is an array of relations that the
|
||||
* action should be cascaded to.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* import { model } from "@medusajs/utils"
|
||||
*
|
||||
*
|
||||
* const Store = model.define("store", {
|
||||
* id: model.id(),
|
||||
* products: model.hasMany(() => Product),
|
||||
@@ -113,7 +113,7 @@ export class DmlEntity<Schema extends DMLSchema> implements IDmlEntity<Schema> {
|
||||
* .cascades({
|
||||
* delete: ["products"],
|
||||
* })
|
||||
*
|
||||
*
|
||||
* @customNamespace Model Methods
|
||||
*/
|
||||
cascades(
|
||||
@@ -142,15 +142,15 @@ export class DmlEntity<Schema extends DMLSchema> implements IDmlEntity<Schema> {
|
||||
/**
|
||||
* This method defines indices on the data model. An index can be on multiple columns
|
||||
* and have conditions.
|
||||
*
|
||||
*
|
||||
* @param indexes - The index's configuration.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* An example of a simple index:
|
||||
*
|
||||
*
|
||||
* ```ts
|
||||
* import { model } from "@medusajs/utils"
|
||||
*
|
||||
*
|
||||
* const MyCustom = model.define("my_custom", {
|
||||
* id: model.id(),
|
||||
* name: model.text(),
|
||||
@@ -160,15 +160,15 @@ export class DmlEntity<Schema extends DMLSchema> implements IDmlEntity<Schema> {
|
||||
* on: ["name", "age"],
|
||||
* },
|
||||
* ])
|
||||
*
|
||||
*
|
||||
* export default MyCustom
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* To add a condition on the index, use the `where` option:
|
||||
*
|
||||
*
|
||||
* ```ts
|
||||
* import { model } from "@medusajs/utils"
|
||||
*
|
||||
*
|
||||
* const MyCustom = model.define("my_custom", {
|
||||
* id: model.id(),
|
||||
* name: model.text(),
|
||||
@@ -181,15 +181,15 @@ export class DmlEntity<Schema extends DMLSchema> implements IDmlEntity<Schema> {
|
||||
* }
|
||||
* },
|
||||
* ])
|
||||
*
|
||||
*
|
||||
* export default MyCustom
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* The condition can also be a negation. For example:
|
||||
*
|
||||
*
|
||||
* ```ts
|
||||
* import { model } from "@medusajs/utils"
|
||||
*
|
||||
*
|
||||
* const MyCustom = model.define("my_custom", {
|
||||
* id: model.id(),
|
||||
* name: model.text(),
|
||||
@@ -204,12 +204,12 @@ export class DmlEntity<Schema extends DMLSchema> implements IDmlEntity<Schema> {
|
||||
* }
|
||||
* },
|
||||
* ])
|
||||
*
|
||||
*
|
||||
* export default MyCustom
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* In this example, the index is created when the value of `age` doesn't equal `30`.
|
||||
*
|
||||
*
|
||||
* @customNamespace Model Methods
|
||||
*/
|
||||
indexes(indexes: EntityIndex<Schema, string | QueryCondition<Schema>>[]) {
|
||||
|
||||
@@ -87,14 +87,14 @@ export function createMikrORMEntity() {
|
||||
*/
|
||||
export const toMikroORMEntity = <T>(
|
||||
entity: T
|
||||
): T extends DmlEntity<infer Schema> ? Infer<T> : T => {
|
||||
): T extends DmlEntity<any> ? Infer<T> : T => {
|
||||
let mikroOrmEntity: T | EntityConstructor<any> = entity
|
||||
|
||||
if (DmlEntity.isDmlEntity(entity)) {
|
||||
mikroOrmEntity = createMikrORMEntity()(entity)
|
||||
}
|
||||
|
||||
return mikroOrmEntity as T extends DmlEntity<infer Schema> ? Infer<T> : T
|
||||
return mikroOrmEntity as T extends DmlEntity<any> ? Infer<T> : T
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
import { DMLSchema } from "@medusajs/types"
|
||||
import { IdProperty } from "../../properties/id"
|
||||
|
||||
/*
|
||||
The id() property is an core opinionated property that will act as a primaryKey
|
||||
by default and come with built-in logic when converted to a mikroorm entity. If no other
|
||||
primaryKey() properties are found within the schema, we continue treating the id() property
|
||||
as a primaryKey. When other fields are set as explicit primaryKey fields, we convert the
|
||||
id() property to no longer be a primaryKey.
|
||||
|
||||
Example:
|
||||
Model 1:
|
||||
id: model.id() -> primary key
|
||||
code: model.text()
|
||||
|
||||
Model 2:
|
||||
id: model.id()
|
||||
code: model.text().primaryKey() -> primary key
|
||||
|
||||
Model 3:
|
||||
id: model.id()
|
||||
code: model.text().primaryKey() -> composite primary key
|
||||
name: model.text().primaryKey() -> composite primary key
|
||||
*/
|
||||
export function inferPrimaryKeyProperties<TSchema extends DMLSchema>(
|
||||
schema: TSchema
|
||||
) {
|
||||
// If explicit primaryKey fields are not found, no inferrence is required. Return early.
|
||||
if (!getExplicitPrimaryKeyFields(schema).length) {
|
||||
return schema
|
||||
}
|
||||
|
||||
// If explicit primaryKey fields are found, set any id() properties to no longer be
|
||||
// set to primaryKey.
|
||||
for (const [field, property] of Object.entries(schema)) {
|
||||
const parsed = property.parse(field)
|
||||
const isRelationshipType = "type" in parsed
|
||||
|
||||
if (isRelationshipType) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (parsed.dataType.name === "id") {
|
||||
;(property as IdProperty).primaryKey(false)
|
||||
}
|
||||
}
|
||||
|
||||
return schema
|
||||
}
|
||||
|
||||
/*
|
||||
Gets all explicit primary key fields from a schema, except id properties.
|
||||
|
||||
eg: model.define('test', {
|
||||
id: model.id(), -> implicit primaryKey field,
|
||||
text: model.text(),
|
||||
textPrimary: model.text().primaryKey(), -> explicit primaryKey field
|
||||
numberPrimary: model.number().primaryKey(), -> explicit primaryKey field
|
||||
belongsTo: model.belongsTo(() => belongsToAnother),
|
||||
})
|
||||
*/
|
||||
function getExplicitPrimaryKeyFields(schema: DMLSchema) {
|
||||
return Object.entries(schema).filter(([field, property]) => {
|
||||
const parsed = property.parse(field)
|
||||
const isRelationshipType = "type" in parsed
|
||||
|
||||
// Return early if its a relationship property or an id property
|
||||
if (isRelationshipType || parsed.dataType.name === "id") {
|
||||
return false
|
||||
}
|
||||
|
||||
return !!parsed.dataType.options?.primaryKey
|
||||
})
|
||||
}
|
||||
@@ -11,33 +11,33 @@ export class IdProperty extends BaseProperty<string> {
|
||||
primaryKey: boolean
|
||||
prefix?: string
|
||||
}
|
||||
} = {
|
||||
name: "id",
|
||||
options: { primaryKey: false },
|
||||
}
|
||||
|
||||
constructor(options?: {
|
||||
/**
|
||||
* Whether the ID is the data model's primary key.
|
||||
*
|
||||
* @defaultValue true
|
||||
*/
|
||||
primaryKey?: boolean
|
||||
/**
|
||||
* By default, Medusa shortens the data model's name and uses it as the
|
||||
* prefix of all IDs. For example, `cm_123`.
|
||||
*
|
||||
* Use this option to specify the prefix to use instead.
|
||||
*/
|
||||
prefix?: string
|
||||
}) {
|
||||
constructor(options?: { prefix?: string }) {
|
||||
super()
|
||||
this.dataType = {
|
||||
name: "id",
|
||||
options: { primaryKey: true, ...options },
|
||||
}
|
||||
this.dataType.options.prefix = options?.prefix
|
||||
}
|
||||
|
||||
primaryKey(decision: boolean) {
|
||||
this.dataType.options.primaryKey = decision
|
||||
|
||||
/**
|
||||
* This method indicates that the property is the data model's primary key.
|
||||
*
|
||||
* @example
|
||||
* import { model } from "@medusajs/utils"
|
||||
*
|
||||
* const Product = model.define("Product", {
|
||||
* id: model.id().primaryKey(),
|
||||
* // ...
|
||||
* })
|
||||
*
|
||||
* export default Product
|
||||
*
|
||||
* @customNamespace Property Configuration Methods
|
||||
*/
|
||||
primaryKey() {
|
||||
this.dataType.options.primaryKey = true
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,23 +8,30 @@ export class TextProperty extends BaseProperty<string> {
|
||||
name: "text"
|
||||
options: {
|
||||
primaryKey: boolean
|
||||
prefix?: string
|
||||
searchable: boolean
|
||||
}
|
||||
} = {
|
||||
name: "text",
|
||||
options: {
|
||||
primaryKey: false,
|
||||
searchable: false,
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* This method indicates that the property is the data model's primary key.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* import { model } from "@medusajs/utils"
|
||||
*
|
||||
* const MyCustom = model.define("my_custom", {
|
||||
*
|
||||
* const Product = model.define("Product", {
|
||||
* code: model.text().primaryKey(),
|
||||
* // ...
|
||||
* })
|
||||
*
|
||||
* export default MyCustom
|
||||
*
|
||||
*
|
||||
* export default Product
|
||||
*
|
||||
* @customNamespace Property Configuration Methods
|
||||
*/
|
||||
primaryKey() {
|
||||
@@ -35,17 +42,17 @@ export class TextProperty extends BaseProperty<string> {
|
||||
|
||||
/**
|
||||
* This method indicates that a text property is searchable.
|
||||
*
|
||||
*
|
||||
* @example
|
||||
* import { model } from "@medusajs/utils"
|
||||
*
|
||||
*
|
||||
* const MyCustom = model.define("my_custom", {
|
||||
* name: model.text().searchable(),
|
||||
* // ...
|
||||
* })
|
||||
*
|
||||
*
|
||||
* export default MyCustom
|
||||
*
|
||||
*
|
||||
* @customNamespace Property Configuration Methods
|
||||
*/
|
||||
searchable() {
|
||||
@@ -53,13 +60,4 @@ export class TextProperty extends BaseProperty<string> {
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
constructor(options?: { primaryKey?: boolean; searchable?: boolean }) {
|
||||
super()
|
||||
|
||||
this.dataType = {
|
||||
name: "text",
|
||||
options: { primaryKey: false, searchable: false, ...options },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user