feat(utils,currency): Migrate currency to use DML (#7807)

This commit is contained in:
Riqwan Thamir authored and GitHub committed 2024-06-24 19:07:26 +02:00
1 parent 7414034ac4
commit 96fb7a962e
21 files changed
+312 -116

No files matched your search

@@ -1,7 +1,7 @@
import { medusaIntegrationTestRunner } from "medusa-test-utils" import { medusaIntegrationTestRunner } from "medusa-test-utils"
import { import {
createAdminUser,
adminHeaders, adminHeaders,
createAdminUser,
} from "../../../../helpers/create-admin-user" } from "../../../../helpers/create-admin-user"
jest.setTimeout(30000) jest.setTimeout(30000)
@@ -40,24 +40,26 @@ medusaIntegrationTestRunner({
) )
expect(response.status).toEqual(200) expect(response.status).toEqual(200)
expect(response.data.currencies).toEqual([ expect(response.data.currencies).toEqual(
expect.objectContaining({ expect.arrayContaining([
code: "aud", expect.objectContaining({
name: "Australian Dollar", code: "aud",
}), name: "Australian Dollar",
expect.objectContaining({ }),
code: "byn", expect.objectContaining({
name: "Belarusian Ruble", code: "byn",
}), name: "Belarusian Ruble",
expect.objectContaining({ }),
code: "rub", expect.objectContaining({
name: "Russian Ruble", code: "rub",
}), name: "Russian Ruble",
expect.objectContaining({ }),
code: "usd", expect.objectContaining({
name: "US Dollar", code: "usd",
}), name: "US Dollar",
]) }),
])
)
}) })
}) })
}, },
@@ -60,9 +60,10 @@ medusaIntegrationTestRunner({
adminHeaders adminHeaders
) )
expect(listResp.data.currencies).toHaveLength(1) expect(listResp.data.currencies).toEqual(
expect(listResp.data.currencies[0]).toEqual( expect.arrayContaining([
expect.objectContaining({ code: "zwl", name: "Zimbabwean Dollar" }) expect.objectContaining({ code: "zwl", name: "Zimbabwean Dollar" }),
])
) )
}) })
}) })
@@ -1166,6 +1166,109 @@ describe("Entity builder", () => {
}) })
}) })
describe("Entity builder | primaryKey", () => {
test("should create both id fields and primaryKey fields", () => {
const model = new EntityBuilder()
const user = model.define("user", {
id: model.id({ primaryKey: false }),
email: model.text().primaryKey(),
account_id: model.number().primaryKey(),
})
const entityBuilder = createMikrORMEntity()
const User = entityBuilder(user)
expectTypeOf(new User()).toMatchTypeOf<{
id: string
email: string
account_id: number
}>()
const metaData = MetadataStorage.getMetadataFromDecorator(User)
const userInstance = new User()
userInstance["generateId"]()
expect(metaData.className).toEqual("User")
expect(metaData.path).toEqual("User")
expect(metaData.hooks).toEqual({
beforeCreate: ["generateId"],
onInit: ["generateId"],
})
expect(metaData.filters).toEqual({
softDeletable: {
name: "softDeletable",
cond: expect.any(Function),
default: true,
args: false,
},
})
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,
},
})
expect(userInstance.id).toBeDefined()
})
})
describe("Entity builder | indexes", () => { describe("Entity builder | indexes", () => {
test("define index on a field", () => { test("define index on a field", () => {
const model = new EntityBuilder() const model = new EntityBuilder()
@@ -10,6 +10,9 @@ describe("Number property", () => {
fieldName: "age", fieldName: "age",
dataType: { dataType: {
name: "number", name: "number",
options: {
primaryKey: false,
},
}, },
nullable: false, nullable: false,
indexes: [], indexes: [],
@@ -10,6 +10,7 @@ describe("Text property", () => {
fieldName: "username", fieldName: "username",
dataType: { dataType: {
name: "text", name: "text",
options: { primaryKey: false },
}, },
nullable: false, nullable: false,
indexes: [], indexes: [],
@@ -240,7 +240,10 @@ export function createMikrORMEntity() {
* Hook to generate entity within the code * Hook to generate entity within the code
*/ */
MikroORMEntity.prototype.generateId = function () { MikroORMEntity.prototype.generateId = function () {
this.id = generateEntityId(this.id, field.dataType.options?.prefix) this[field.fieldName] = generateEntityId(
this[field.fieldName],
field.dataType.options?.prefix
)
} }
/** /**
@@ -248,6 +251,7 @@ export function createMikrORMEntity() {
*/ */
BeforeCreate()(MikroORMEntity.prototype, "generateId") BeforeCreate()(MikroORMEntity.prototype, "generateId")
OnInit()(MikroORMEntity.prototype, "generateId") OnInit()(MikroORMEntity.prototype, "generateId")
return return
} }
@@ -257,6 +261,19 @@ export function createMikrORMEntity() {
const columnType = COLUMN_TYPES[field.dataType.name] const columnType = COLUMN_TYPES[field.dataType.name]
const propertyType = PROPERTY_TYPES[field.dataType.name] const propertyType = PROPERTY_TYPES[field.dataType.name]
/**
* Defining a primary key property
*/
if (field.dataType.options?.primaryKey) {
PrimaryKey({
columnType,
type: propertyType,
nullable: false,
})(MikroORMEntity.prototype, field.fieldName)
return
}
Property({ Property({
columnType, columnType,
type: propertyType, type: propertyType,
+4 -1
View File
@@ -15,6 +15,9 @@ export class IdProperty extends BaseProperty<string> {
constructor(options?: { primaryKey?: boolean; prefix?: string }) { constructor(options?: { primaryKey?: boolean; prefix?: string }) {
super() super()
this.dataType = { name: "id", options: { primaryKey: true, ...options } } this.dataType = {
name: "id",
options: { primaryKey: true, ...options },
}
} }
} }
@@ -5,7 +5,25 @@ import { BaseProperty } from "./base"
* property * property
*/ */
export class NumberProperty extends BaseProperty<number> { export class NumberProperty extends BaseProperty<number> {
protected dataType = { protected dataType: {
name: "number", name: "number"
} as const options: {
primaryKey: boolean
}
}
primaryKey() {
this.dataType.options.primaryKey = true
return this
}
constructor(options?: { primaryKey?: boolean }) {
super()
this.dataType = {
name: "number",
options: { primaryKey: false, ...options },
}
}
} }
+21 -3
View File
@@ -4,7 +4,25 @@ import { BaseProperty } from "./base"
* The TextProperty is used to define a textual property * The TextProperty is used to define a textual property
*/ */
export class TextProperty extends BaseProperty<string> { export class TextProperty extends BaseProperty<string> {
protected dataType = { protected dataType: {
name: "text", name: "text"
} as const options: {
primaryKey: boolean
}
}
primaryKey() {
this.dataType.options.primaryKey = true
return this
}
constructor(options?: { primaryKey?: boolean }) {
super()
this.dataType = {
name: "text",
options: { primaryKey: false, ...options },
}
}
} }
@@ -279,4 +279,37 @@ describe("defineJoiner", () => {
], ],
}) })
}) })
it("should return a full joiner configuration with custom aliases overriding defaults", () => {
const joinerConfig = defineJoinerConfig(Modules.FULFILLMENT, {
entityQueryingConfig: [FulfillmentSet],
alias: [
{
name: ["fulfillment_set", "fulfillment_sets"],
args: {
entity: "FulfillmentSet",
methodSuffix: "fulfillmentSetCustom",
},
},
],
})
expect(joinerConfig).toEqual({
serviceName: Modules.FULFILLMENT,
primaryKeys: ["id"],
schema: undefined,
linkableKeys: {
fulfillment_set_id: FulfillmentSet.name,
},
alias: [
{
name: ["fulfillment_set", "fulfillment_sets"],
args: {
entity: "FulfillmentSet",
methodSuffix: "fulfillmentSetCustom",
},
},
],
})
})
}) })
+8 -8
View File
@@ -1,15 +1,15 @@
export * from "./load-module-database-config"
export * from "./decorators"
export * from "./build-query" export * from "./build-query"
export * from "./loaders/mikro-orm-connection-loader"
export * from "./loaders/mikro-orm-connection-loader-factory"
export * from "./loaders/container-loader-factory"
export * from "./create-pg-connection" export * from "./create-pg-connection"
export * from "./migration-scripts" export * from "./decorators"
export * from "./medusa-internal-service"
export * from "./medusa-service"
export * from "./definition" export * from "./definition"
export * from "./event-builder-factory" export * from "./event-builder-factory"
export * from "./joiner-config-builder" export * from "./joiner-config-builder"
export * from "./load-module-database-config"
export * from "./loaders/container-loader-factory"
export * from "./loaders/load-models" export * from "./loaders/load-models"
export * from "./loaders/mikro-orm-connection-loader"
export * from "./loaders/mikro-orm-connection-loader-factory"
export * from "./medusa-internal-service"
export * from "./medusa-service"
export * from "./migration-scripts"
export * from "./mikro-orm-cli-config-builder" export * from "./mikro-orm-cli-config-builder"
@@ -1,4 +1,5 @@
import { ModuleJoinerConfig } from "@medusajs/types" import { JoinerServiceConfigAlias, ModuleJoinerConfig } from "@medusajs/types"
import { join } from "path"
import { import {
camelToSnakeCase, camelToSnakeCase,
deduplicate, deduplicate,
@@ -7,7 +8,6 @@ import {
pluralize, pluralize,
upperCaseFirst, upperCaseFirst,
} from "../common" } from "../common"
import { join } from "path"
import { loadModels } from "./loaders/load-models" import { loadModels } from "./loaders/load-models"
/** /**
@@ -33,7 +33,7 @@ export function defineJoinerConfig(
linkableKeys, linkableKeys,
primaryKeys, primaryKeys,
}: { }: {
alias?: ModuleJoinerConfig["alias"] alias?: JoinerServiceConfigAlias[]
schema?: string schema?: string
entityQueryingConfig?: { name: string }[] entityQueryingConfig?: { name: string }[]
linkableKeys?: Record<string, string> linkableKeys?: Record<string, string>
@@ -79,16 +79,22 @@ export function defineJoinerConfig(
pluralize(upperCaseFirst(alias.args.entity)), pluralize(upperCaseFirst(alias.args.entity)),
}, },
})), })),
...models.map((entity, i) => ({ ...models
name: [ .filter((model) => {
`${camelToSnakeCase(entity.name).toLowerCase()}`, return (
`${pluralize(camelToSnakeCase(entity.name).toLowerCase())}`, !alias || !alias.some((alias) => alias.args?.entity === model.name)
], )
args: { })
entity: entity.name, .map((entity, i) => ({
methodSuffix: pluralize(upperCaseFirst(entity.name)), name: [
}, `${camelToSnakeCase(entity.name).toLowerCase()}`,
})), `${pluralize(camelToSnakeCase(entity.name).toLowerCase())}`,
],
args: {
entity: entity.name,
methodSuffix: pluralize(upperCaseFirst(entity.name)),
},
})),
], ],
} }
} }
@@ -27,10 +27,11 @@ export function loadModels(basePath: string) {
if (stats.isFile()) { if (stats.isFile()) {
try { try {
const required = require(filePath) const required = require(filePath) as {
return Object.values(required).filter( [key: string]: { name?: string }
(resource) => typeof resource === "function" && !!resource.name }
)
return Object.values(required).filter((resource) => !!resource.name)
} catch (e) {} } catch (e) {}
} }
@@ -1,5 +1,4 @@
import { MikroORMOptions } from "@mikro-orm/core/utils/Configuration" import { MikroORMOptions } from "@mikro-orm/core/utils/Configuration"
import { IDmlEntity } from "@medusajs/types"
import { DmlEntity, toMikroORMEntity } from "../dml" import { DmlEntity, toMikroORMEntity } from "../dml"
import { TSMigrationGenerator } from "../dal" import { TSMigrationGenerator } from "../dal"
import { AnyEntity, EntityClassGroup, EntitySchema } from "@mikro-orm/core" import { AnyEntity, EntityClassGroup, EntitySchema } from "@mikro-orm/core"
@@ -11,7 +10,7 @@ type Options = Partial<MikroORMOptions> & {
| EntityClass<AnyEntity> | EntityClass<AnyEntity>
| EntityClassGroup<AnyEntity> | EntityClassGroup<AnyEntity>
| EntitySchema | EntitySchema
| IDmlEntity<any> | DmlEntity<any>
)[] )[]
databaseName: string databaseName: string
} }
@@ -1,8 +1,7 @@
import { defineMikroOrmCliConfig } from "@medusajs/utils"
import * as entities from "./src/models" import * as entities from "./src/models"
module.exports = { export default defineMikroOrmCliConfig({
entities: Object.values(entities), entities: Object.values(entities),
schema: "public", databaseName: "medusa-currency",
clientUrl: "postgres://postgres@localhost/medusa-currency", })
type: "postgresql",
}
@@ -5,7 +5,12 @@ import {
Modules, Modules,
} from "@medusajs/utils" } from "@medusajs/utils"
export const joinerConfig = defineJoinerConfig(Modules.CURRENCY) export const joinerConfig = defineJoinerConfig(Modules.CURRENCY, {
primaryKeys: ["code"],
linkableKeys: {
currency_code: "currency",
},
})
export const entityNameToLinkableKeysMap: MapToConfig = export const entityNameToLinkableKeysMap: MapToConfig =
buildEntitiesNameToLinkableKeysMap(joinerConfig.linkableKeys) buildEntitiesNameToLinkableKeysMap(joinerConfig.linkableKeys)
@@ -14,7 +14,7 @@ export default async ({
const logger = const logger =
container.resolve<Logger>(ContainerRegistrationKeys.LOGGER) ?? console container.resolve<Logger>(ContainerRegistrationKeys.LOGGER) ?? console
const { currencyService_ } = container.resolve<{ const { currencyService_ } = container.resolve<{
currencyService_: ModulesSdkTypes.IMedusaInternalService<Currency> currencyService_: ModulesSdkTypes.IMedusaInternalService<typeof Currency>
}>(ModuleRegistrationName.CURRENCY) }>(ModuleRegistrationName.CURRENCY)
try { try {
@@ -42,7 +42,7 @@
}, },
"decimal_digits": { "decimal_digits": {
"name": "decimal_digits", "name": "decimal_digits",
"type": "int", "type": "integer",
"unsigned": false, "unsigned": false,
"autoincrement": false, "autoincrement": false,
"primary": false, "primary": false,
@@ -90,6 +90,16 @@
"length": 6, "length": 6,
"default": "now()", "default": "now()",
"mappedType": "datetime" "mappedType": "datetime"
},
"deleted_at": {
"name": "deleted_at",
"type": "timestamptz",
"unsigned": false,
"autoincrement": false,
"primary": false,
"nullable": true,
"length": 6,
"mappedType": "datetime"
} }
}, },
"name": "currency", "name": "currency",
@@ -0,0 +1,15 @@
import { Migration } from "@mikro-orm/migrations"
export class Migration20240624082354 extends Migration {
async up(): Promise<void> {
this.addSql(
'alter table if exists "currency" add column if not exists "deleted_at" timestamptz null;'
)
}
async down(): Promise<void> {
this.addSql(
'alter table if exists "currency" drop column if exists "deleted_at";'
)
}
}
@@ -1,50 +1,10 @@
import { BigNumberRawValue } from "@medusajs/types" import { model } from "@medusajs/utils"
import {
BigNumber,
MikroOrmBigNumberProperty,
Searchable,
} from "@medusajs/utils"
import { Entity, PrimaryKey, Property } from "@mikro-orm/core"
@Entity({ tableName: "currency" }) export default model.define("currency", {
class Currency { code: model.text().primaryKey(),
@Searchable() symbol: model.text(),
@PrimaryKey({ columnType: "text" }) symbol_native: model.text(),
code!: string name: model.text(),
decimal_digits: model.number().default(0),
@Property({ columnType: "text" }) rounding: model.bigNumber().default(0),
symbol: string })
@Property({ columnType: "text" })
symbol_native: string
@Searchable()
@Property({ columnType: "text" })
name: string
@Property({ columnType: "int", default: 0 })
decimal_digits: number
@MikroOrmBigNumberProperty({ default: 0 })
rounding: BigNumber | number
@Property({ columnType: "jsonb" })
raw_rounding: BigNumberRawValue
@Property({
onCreate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
created_at: Date
@Property({
onCreate: () => new Date(),
onUpdate: () => new Date(),
columnType: "timestamptz",
defaultRaw: "now()",
})
updated_at: Date
}
export default Currency
@@ -11,9 +11,9 @@ import {
ModulesSdkTypes, ModulesSdkTypes,
} from "@medusajs/types" } from "@medusajs/types"
import { MedusaService } from "@medusajs/utils"
import { Currency } from "@models" import { Currency } from "@models"
import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config"
import { MedusaService } from "@medusajs/utils"
type InjectedDependencies = { type InjectedDependencies = {
baseRepository: DAL.RepositoryService baseRepository: DAL.RepositoryService
@@ -27,7 +27,9 @@ export default class CurrencyModuleService
implements ICurrencyModuleService implements ICurrencyModuleService
{ {
protected baseRepository_: DAL.RepositoryService protected baseRepository_: DAL.RepositoryService
protected readonly currencyService_: ModulesSdkTypes.IMedusaInternalService<Currency> protected readonly currencyService_: ModulesSdkTypes.IMedusaInternalService<
typeof Currency
>
constructor( constructor(
{ baseRepository, currencyService }: InjectedDependencies, { baseRepository, currencyService }: InjectedDependencies,