From 617a5972bff2ce99056af362764d4b14a9c9feff Mon Sep 17 00:00:00 2001 From: Adrien de Peretti Date: Wed, 3 Jul 2024 13:12:49 +0200 Subject: [PATCH] feat: refactor module joiner config and links generation (#7859) * feat: refactor module joiner config and links generation * improve typings * WIP * WIP * WIP * rename type file * create link config * finish typings and add utils * improve links * WIP typings * finalize ExportModule utils * finalize ExportModule utils * fix: dml tests * improve and fixes * simplify typings with id changes * add toJSON * multiple fixes and entity builder fixes * fix currency searchable * fix tests * medusa service refactoring * cleanup * cleanup and fixes * make module name optional * renaming --------- Co-authored-by: Harminder Virk --- .../models/dml-entity.ts | 1 + .../models/dml-entity-model.ts | 1 + .../models/dml-entity.ts | 1 + .../src/loaders/utils/load-internal.ts | 25 +- packages/core/types/src/common/camel-case.ts | 89 +++ packages/core/types/src/common/index.ts | 1 + packages/core/types/src/dml/index.ts | 49 +- packages/core/types/src/modules-sdk/index.ts | 4 +- .../core/utils/src/common/to-camel-case.ts | 2 +- .../src/dml/__tests__/base-property.spec.ts | 1 - .../src/dml/__tests__/entity-builder.spec.ts | 17 +- .../src/dml/__tests__/id-property.spec.ts | 9 +- .../src/dml/__tests__/number-property.spec.ts | 4 +- .../src/dml/__tests__/text-property.spec.ts | 5 +- packages/core/utils/src/dml/entity-builder.ts | 34 +- packages/core/utils/src/dml/entity.ts | 37 +- .../dml/helpers/create-mikro-orm-entity.ts | 24 +- .../create-big-number-properties.ts | 2 +- .../helpers/entity-builder/define-property.ts | 10 +- .../entity-builder/define-relationship.ts | 8 +- .../entity-builder/parse-entity-name.ts | 2 +- packages/core/utils/src/dml/properties/id.ts | 15 +- .../core/utils/src/dml/properties/number.ts | 11 +- .../utils/src/dml/properties/primary-key.ts | 39 + .../core/utils/src/dml/properties/text.ts | 7 +- packages/core/utils/src/dml/relations/base.ts | 10 + .../core/utils/src/dml/relations/nullable.ts | 4 +- .../__fixtures__/joiner-config/entities.ts | 53 ++ .../__tests__/joiner-config-builder.spec.ts | 750 +++++++++++------- .../src/modules-sdk/event-builder-factory.ts | 6 +- packages/core/utils/src/modules-sdk/index.ts | 2 +- .../src/modules-sdk/joiner-config-builder.ts | 229 +++++- .../src/modules-sdk/loaders/load-models.ts | 10 +- .../modules-sdk/medusa-internal-service.ts | 18 +- .../utils/src/modules-sdk/medusa-service.ts | 296 ++----- .../mikro-orm-cli-config-builder.ts | 2 +- packages/core/utils/src/modules-sdk/module.ts | 45 ++ .../src/modules-sdk/types/links-config.ts | 131 +++ .../src/modules-sdk/types/medusa-service.ts | 261 ++++++ packages/modules/api-key/src/joiner-config.ts | 10 +- .../src/services/api-key-module-service.ts | 4 +- packages/modules/auth/src/joiner-config.ts | 12 +- .../modules/auth/src/services/auth-module.ts | 4 +- packages/modules/cart/src/joiner-config.ts | 10 +- .../modules/cart/src/services/cart-module.ts | 4 +- packages/modules/currency/src/index.ts | 9 +- .../modules/currency/src/joiner-config.ts | 10 +- .../modules/currency/src/models/currency.ts | 2 +- .../src/services/currency-module-service.ts | 6 +- .../modules/customer/src/joiner-config.ts | 10 +- .../customer/src/services/customer-module.ts | 7 +- packages/modules/file/src/joiner-config.ts | 12 +- .../modules/fulfillment/src/joiner-config.ts | 10 +- .../services/fulfillment-module-service.ts | 4 +- .../inventory-next/src/joiner-config.ts | 10 +- .../src/services/inventory-level.ts | 5 +- .../src/services/inventory-module.ts | 21 +- .../modules/notification/src/joiner-config.ts | 12 +- .../services/notification-module-service.ts | 6 +- .../src/services/notification-provider.ts | 9 +- packages/modules/order/src/joiner-config.ts | 10 +- .../src/services/order-change-service.ts | 5 +- .../src/services/order-module-service.ts | 4 +- .../order/src/services/order-service.ts | 5 +- packages/modules/payment/src/joiner-config.ts | 17 +- .../payment/src/services/payment-module.ts | 4 +- packages/modules/pricing/src/joiner-config.ts | 12 +- .../pricing/src/services/pricing-module.ts | 4 +- packages/modules/product/src/joiner-config.ts | 11 +- .../src/services/product-module-service.ts | 23 +- .../modules/product/src/services/product.ts | 5 +- .../modules/promotion/src/joiner-config.ts | 12 +- .../src/services/promotion-module.ts | 22 +- packages/modules/region/src/joiner-config.ts | 10 +- .../region/src/services/region-module.ts | 14 +- .../sales-channel/src/joiner-config.ts | 10 +- .../src/services/sales-channel-module.ts | 12 +- .../stock-location-next/src/joiner-config.ts | 10 +- .../src/services/stock-location-module.ts | 4 +- packages/modules/store/src/joiner-config.ts | 10 +- .../src/services/store-module-service.ts | 4 +- packages/modules/tax/src/joiner-config.ts | 10 +- .../tax/src/services/tax-module-service.ts | 4 +- packages/modules/user/src/joiner-config.ts | 10 +- .../modules/user/src/services/user-module.ts | 4 +- .../src/joiner-config.ts | 10 +- .../src/services/workflows-module.ts | 4 +- .../src/joiner-config.ts | 10 +- .../src/services/workflows-module.ts | 4 +- 89 files changed, 1706 insertions(+), 950 deletions(-) create mode 100644 packages/core/types/src/common/camel-case.ts create mode 100644 packages/core/utils/src/dml/properties/primary-key.ts create mode 100644 packages/core/utils/src/modules-sdk/__fixtures__/joiner-config/entities.ts create mode 100644 packages/core/utils/src/modules-sdk/module.ts create mode 100644 packages/core/utils/src/modules-sdk/types/links-config.ts create mode 100644 packages/core/utils/src/modules-sdk/types/medusa-service.ts diff --git a/packages/core/modules-sdk/src/loaders/utils/__fixtures__/module-with-dml-mixed-without-joiner-config/models/dml-entity.ts b/packages/core/modules-sdk/src/loaders/utils/__fixtures__/module-with-dml-mixed-without-joiner-config/models/dml-entity.ts index fe765f6e3d..29662827f7 100644 --- a/packages/core/modules-sdk/src/loaders/utils/__fixtures__/module-with-dml-mixed-without-joiner-config/models/dml-entity.ts +++ b/packages/core/modules-sdk/src/loaders/utils/__fixtures__/module-with-dml-mixed-without-joiner-config/models/dml-entity.ts @@ -1,5 +1,6 @@ import { model } from "@medusajs/utils" export const dmlEntity = model.define("dmlEntity", { + id: model.id().primaryKey(), name: model.text(), }) diff --git a/packages/core/modules-sdk/src/loaders/utils/__fixtures__/module-with-dml-without-joiner-config/models/dml-entity-model.ts b/packages/core/modules-sdk/src/loaders/utils/__fixtures__/module-with-dml-without-joiner-config/models/dml-entity-model.ts index 4bc3b2bd87..327ec0cfb3 100644 --- a/packages/core/modules-sdk/src/loaders/utils/__fixtures__/module-with-dml-without-joiner-config/models/dml-entity-model.ts +++ b/packages/core/modules-sdk/src/loaders/utils/__fixtures__/module-with-dml-without-joiner-config/models/dml-entity-model.ts @@ -1,5 +1,6 @@ import { model } from "@medusajs/utils" export const entityModel = model.define("entityModel", { + id: model.id().primaryKey(), name: model.text(), }) diff --git a/packages/core/modules-sdk/src/loaders/utils/__fixtures__/module-with-dml-without-joiner-config/models/dml-entity.ts b/packages/core/modules-sdk/src/loaders/utils/__fixtures__/module-with-dml-without-joiner-config/models/dml-entity.ts index fe765f6e3d..29662827f7 100644 --- a/packages/core/modules-sdk/src/loaders/utils/__fixtures__/module-with-dml-without-joiner-config/models/dml-entity.ts +++ b/packages/core/modules-sdk/src/loaders/utils/__fixtures__/module-with-dml-without-joiner-config/models/dml-entity.ts @@ -1,5 +1,6 @@ import { model } from "@medusajs/utils" export const dmlEntity = model.define("dmlEntity", { + id: model.id().primaryKey(), name: model.text(), }) diff --git a/packages/core/modules-sdk/src/loaders/utils/load-internal.ts b/packages/core/modules-sdk/src/loaders/utils/load-internal.ts index 5bf3fe9268..cc4f9f7618 100644 --- a/packages/core/modules-sdk/src/loaders/utils/load-internal.ts +++ b/packages/core/modules-sdk/src/loaders/utils/load-internal.ts @@ -11,11 +11,11 @@ import { import { ContainerRegistrationKeys, createMedusaContainer, - createMikrORMEntity, defineJoinerConfig, DmlEntity, MedusaModuleType, ModulesSdkUtils, + toMikroOrmEntities, } from "@medusajs/utils" import { asFunction, asValue } from "awilix" import { statSync } from "fs" @@ -270,12 +270,11 @@ export async function loadResources( ), ]) - const entityBuilder = createMikrORMEntity() const cleanupResources = (resources) => { return Object.values(resources) .map((resource) => { if (DmlEntity.isDmlEntity(resource)) { - return entityBuilder(resource as DmlEntity) + return resource } if (typeof resource === "function") { @@ -289,11 +288,12 @@ export async function loadResources( const potentialServices = [...new Set(cleanupResources(services))] const potentialModels = [...new Set(cleanupResources(models))] + const mikroOrmModels = toMikroOrmEntities(potentialModels) const potentialRepositories = [...new Set(cleanupResources(repositories))] const finalLoaders = prepareLoaders({ loadedModuleLoaders, - models: potentialModels, + models: mikroOrmModels, repositories: potentialRepositories, services: potentialServices, moduleResolution, @@ -308,7 +308,7 @@ export async function loadResources( return { services: potentialServices, - models: potentialModels, + models: mikroOrmModels, repositories: potentialRepositories, loaders: finalLoaders, moduleService, @@ -433,15 +433,20 @@ function generateJoinerConfigIfNecessary({ }: { moduleResolution: ModuleResolution service: Constructor - models: Function[] + models: (Function | DmlEntity)[] }) { - if (service.prototype.__joinerConfig) { - return - } + const originalJoinerConfigFn = service.prototype.__joinerConfig service.prototype.__joinerConfig = function () { + if (originalJoinerConfigFn) { + return { + serviceName: moduleResolution.definition.key, + ...originalJoinerConfigFn(), + } + } + return defineJoinerConfig(moduleResolution.definition.key, { - entityQueryingConfig: models, + models, }) } } diff --git a/packages/core/types/src/common/camel-case.ts b/packages/core/types/src/common/camel-case.ts new file mode 100644 index 0000000000..d59001c3aa --- /dev/null +++ b/packages/core/types/src/common/camel-case.ts @@ -0,0 +1,89 @@ +type IsStringLiteral = Type extends string + ? string extends Type + ? false + : true + : false + +type WordInCamelCase< + Type, + Word extends string = "" +> = Type extends `${Word}${infer NextCharacter}${infer _}` + ? NextCharacter extends Capitalize + ? Word + : WordInCamelCase + : Word + +type Separator = "_" | "-" + +type IncludesSeparator = Type extends `${string}${Separator}${string}` + ? true + : false + +type IsOneWord = Type extends Lowercase + ? true + : Type extends Uppercase + ? true + : false + +type IsCamelCase = Type extends Uncapitalize ? true : false + +type IsPascalCase = Type extends Capitalize ? true : false + +/** snake_case, CONSTANT_CASE, kebab-case or COBOL-CASE */ +type SeparatorCaseParser< + Type, + Tuple extends readonly any[] = [] +> = Type extends `${infer Word}${Separator}${infer Tail}` + ? SeparatorCaseParser]> + : Type extends `${infer Word}` + ? [...Tuple, Lowercase] + : Tuple + +type CamelCaseParser = Type extends "" + ? Tuple + : Type extends `${WordInCamelCase}${infer Tail}` + ? Type extends `${infer Word}${Tail}` + ? CamelCaseParser, [...Tuple, Lowercase]> + : never + : never + +// Convert first character of string literal type to lowercase and reuse CamelCaseParser +type PascalCaseParser = Type extends string + ? CamelCaseParser> + : never + +type SplitAnyCase = IncludesSeparator extends true + ? SeparatorCaseParser + : IsOneWord extends true + ? [Lowercase] + : IsCamelCase extends true + ? CamelCaseParser + : IsPascalCase extends true + ? PascalCaseParser + : [] + +type PascalCapitalizer = Type extends [ + infer Head, + ...infer Tail +] + ? Head extends string + ? PascalCapitalizer]> + : PascalCapitalizer + : Tuple + +type CamelCapitalizer = Type extends [infer First, ...infer Tail] + ? PascalCapitalizer + : [] + +type Join = Type extends [ + infer Head, + ...infer Tail +] + ? Head extends string + ? Join + : Join + : JoinedString + +export type CamelCase = IsStringLiteral extends true + ? Join>> + : Type diff --git a/packages/core/types/src/common/index.ts b/packages/core/types/src/common/index.ts index 01b88856d0..d72d11179f 100644 --- a/packages/core/types/src/common/index.ts +++ b/packages/core/types/src/common/index.ts @@ -5,3 +5,4 @@ export * from "./config-module" export * from "./medusa-cli" export * from "./medusa-container" export * from "./with-calculated" +export * from "./camel-case" diff --git a/packages/core/types/src/dml/index.ts b/packages/core/types/src/dml/index.ts index 5a9eb5f109..321de7b1fc 100644 --- a/packages/core/types/src/dml/index.ts +++ b/packages/core/types/src/dml/index.ts @@ -1,3 +1,5 @@ +import { CamelCase } from "../common" + /** * Symbol to identify a DML entity from an object */ @@ -13,11 +15,26 @@ export type DMLSchema = Record< PropertyType | RelationshipType > +export type IDmlEntityConfig = string | { name?: string; tableName: string } + +export type InferDmlEntityNameFromConfig = + TConfig extends string + ? Capitalize> + : TConfig extends { name: string } + ? Capitalize> + : TConfig extends { tableName: string } + ? Capitalize> + : never + /** * Representation of a DML entity */ -export interface IDmlEntity { +export interface IDmlEntity< + Schema extends DMLSchema, + Config extends IDmlEntityConfig +> { [IsDmlEntity]: true + name: InferDmlEntityNameFromConfig schema: Schema } @@ -60,6 +77,7 @@ export type PropertyMetadata = { type: "index" | "unique" }[] relationships: RelationshipMetadata[] + primaryKey?: boolean } /** @@ -125,22 +143,23 @@ export type InferForeignKeys = { [K in keyof Schema as Schema[K] extends { type: infer Type } ? Type extends RelationshipTypes ? `${K & string}_id` - : K - : K]: Schema[K] extends { type: infer Type } + : never + : never]: Schema[K] extends { type: infer Type } ? Type extends RelationshipTypes ? string - : Schema[K] - : Schema[K] + : never + : never } /** * Infer fields for a belongsTo relationship */ export type InferBelongsToFields = Relation extends () => IDmlEntity< - infer R + infer R, + any > ? InferSchemaFields - : Relation extends () => IDmlEntity | null + : Relation extends () => IDmlEntity | null ? InferSchemaFields | null : never @@ -153,7 +172,8 @@ export type InferHasOneFields = InferBelongsToFields * Infer fields for hasMany relationship */ export type InferHasManyFields = Relation extends () => IDmlEntity< - infer R + infer R, + any > ? InferSchemaFields[] : never @@ -184,7 +204,7 @@ export type InferSchemaFields = { /** * Helper to infer the schema type of a DmlEntity */ -export type Infer = T extends IDmlEntity +export type Infer = T extends IDmlEntity ? EntityConstructor> : never @@ -217,12 +237,12 @@ export type EntityCascades = { /** * Helper to infer the instance type of a IDmlEntity once converted as an Entity */ -export type InferTypeOf> = InstanceType> +export type InferTypeOf> = InstanceType> /** * Used in the module sdk internal service to infer propert entity typings from DML */ -export type InferEntityType = T extends IDmlEntity +export type InferEntityType = T extends IDmlEntity ? InferTypeOf : T @@ -230,7 +250,8 @@ export type InferEntityType = T extends IDmlEntity * Infer all indexable properties from a DML entity including inferred foreign keys and excluding relationship */ export type InferIndexableProperties = keyof (T extends IDmlEntity< - infer Schema + infer Schema, + any > ? { [K in keyof Schema as Schema[K] extends { type: infer Type } @@ -258,7 +279,7 @@ export type EntityIndex< /** * The list of properties to create the index on. */ - on: InferIndexableProperties>[] + on: InferIndexableProperties>[] /** * Conditions to restrict which records are indexed. */ @@ -270,7 +291,7 @@ export type NeQueryValue = { $ne: SimpleQueryValue } export type QueryValue = SimpleQueryValue | NeQueryValue export type QueryCondition = { - [K in keyof IDmlEntity["schema"]]?: T[K] extends object + [K in keyof IDmlEntity["schema"]]?: T[K] extends object ? QueryValue : QueryCondition } diff --git a/packages/core/types/src/modules-sdk/index.ts b/packages/core/types/src/modules-sdk/index.ts index 00a5756ac4..d6caf986d3 100644 --- a/packages/core/types/src/modules-sdk/index.ts +++ b/packages/core/types/src/modules-sdk/index.ts @@ -224,8 +224,8 @@ export declare type ModuleJoinerRelationship = JoinerRelationship & { deleteCascade?: boolean } -export type ModuleExports = { - service: Constructor +export type ModuleExports> = { + service: T loaders?: ModuleLoaderFunction[] runMigrations?( options: LoaderOptions, diff --git a/packages/core/utils/src/common/to-camel-case.ts b/packages/core/utils/src/common/to-camel-case.ts index 827d2f07bc..9ab31b7cc7 100644 --- a/packages/core/utils/src/common/to-camel-case.ts +++ b/packages/core/utils/src/common/to-camel-case.ts @@ -1,5 +1,5 @@ export function toCamelCase(str: string): string { - return /^([a-z]+)(([A-Z]([a-z]+))+)$/.test(str) + return /^([a-zA-Z]+)(([A-Z]([a-z]+))+)$/.test(str) ? str : str .toLowerCase() diff --git a/packages/core/utils/src/dml/__tests__/base-property.spec.ts b/packages/core/utils/src/dml/__tests__/base-property.spec.ts index afc0f69db2..dedb719f16 100644 --- a/packages/core/utils/src/dml/__tests__/base-property.spec.ts +++ b/packages/core/utils/src/dml/__tests__/base-property.spec.ts @@ -34,7 +34,6 @@ describe("Base property", () => { dataType: { name: "text", options: { - primaryKey: false, searchable: true, }, }, diff --git a/packages/core/utils/src/dml/__tests__/entity-builder.spec.ts b/packages/core/utils/src/dml/__tests__/entity-builder.spec.ts index 1b28265f4e..fd7310f851 100644 --- a/packages/core/utils/src/dml/__tests__/entity-builder.spec.ts +++ b/packages/core/utils/src/dml/__tests__/entity-builder.spec.ts @@ -1,4 +1,4 @@ -import { ArrayType, MetadataStorage } from "@mikro-orm/core" +import { ArrayType, EntityMetadata, MetadataStorage } from "@mikro-orm/core" import { expectTypeOf } from "expect-type" import { DmlEntity } from "../entity" import { model } from "../entity-builder" @@ -72,7 +72,7 @@ describe("Entity builder", () => { phones: model.array(), }) - expect(user.name).toEqual("user") + expect(user.name).toEqual("User") expect(user.parse().tableName).toEqual("user") const User = toMikroORMEntity(user) @@ -202,7 +202,7 @@ describe("Entity builder", () => { } ) - expect(user.name).toEqual("user") + expect(user.name).toEqual("User") expect(user.parse().tableName).toEqual("user_table") const User = toMikroORMEntity(user) @@ -324,7 +324,7 @@ describe("Entity builder", () => { } ) - expect(user.name).toEqual("userRole") + expect(user.name).toEqual("UserRole") expect(user.parse().tableName).toEqual("user_role") const User = toMikroORMEntity(user) @@ -1558,9 +1558,10 @@ describe("Entity builder", () => { account_id: model.number(), }) - const entityBuilder = createMikrORMEntity() - const User = entityBuilder(user) - const metaData = MetadataStorage.getMetadataFromDecorator(User) + const User = toMikroORMEntity(user) + const metaData = MetadataStorage.getMetadataFromDecorator( + User + ) as unknown as EntityMetadata> expect(metaData.properties.id).toEqual({ columnType: "text", @@ -3848,7 +3849,7 @@ describe("Entity builder", () => { }) expect(defineEmail).toThrow( - 'Cannot cascade delete "user" relationship(s) from "email" entity. Child to parent cascades are not allowed' + 'Cannot cascade delete "user" relationship(s) from "Email" entity. Child to parent cascades are not allowed' ) }) diff --git a/packages/core/utils/src/dml/__tests__/id-property.spec.ts b/packages/core/utils/src/dml/__tests__/id-property.spec.ts index c6d190d154..ce63f59da3 100644 --- a/packages/core/utils/src/dml/__tests__/id-property.spec.ts +++ b/packages/core/utils/src/dml/__tests__/id-property.spec.ts @@ -10,9 +10,7 @@ describe("Id property", () => { fieldName: "id", dataType: { name: "id", - options: { - primaryKey: false, - }, + options: {}, }, nullable: false, indexes: [], @@ -28,13 +26,12 @@ describe("Id property", () => { fieldName: "id", dataType: { name: "id", - options: { - primaryKey: true, - }, + options: {}, }, nullable: false, indexes: [], relationships: [], + primaryKey: true, }) }) }) diff --git a/packages/core/utils/src/dml/__tests__/number-property.spec.ts b/packages/core/utils/src/dml/__tests__/number-property.spec.ts index df2d7f987c..6d4231f312 100644 --- a/packages/core/utils/src/dml/__tests__/number-property.spec.ts +++ b/packages/core/utils/src/dml/__tests__/number-property.spec.ts @@ -10,9 +10,7 @@ describe("Number property", () => { fieldName: "age", dataType: { name: "number", - options: { - primaryKey: false, - }, + options: {}, }, nullable: false, indexes: [], diff --git a/packages/core/utils/src/dml/__tests__/text-property.spec.ts b/packages/core/utils/src/dml/__tests__/text-property.spec.ts index c34a28b999..b14c2eca7e 100644 --- a/packages/core/utils/src/dml/__tests__/text-property.spec.ts +++ b/packages/core/utils/src/dml/__tests__/text-property.spec.ts @@ -10,7 +10,7 @@ describe("Text property", () => { fieldName: "username", dataType: { name: "text", - options: { primaryKey: false, searchable: false }, + options: { searchable: false }, }, nullable: false, indexes: [], @@ -26,11 +26,12 @@ describe("Text property", () => { fieldName: "username", dataType: { name: "text", - options: { primaryKey: true, searchable: false }, + options: { searchable: false }, }, nullable: false, indexes: [], relationships: [], + primaryKey: true, }) }) }) diff --git a/packages/core/utils/src/dml/entity-builder.ts b/packages/core/utils/src/dml/entity-builder.ts index 1acd00a43d..853c2c2425 100644 --- a/packages/core/utils/src/dml/entity-builder.ts +++ b/packages/core/utils/src/dml/entity-builder.ts @@ -1,7 +1,17 @@ -import type { DMLSchema, RelationshipOptions } from "@medusajs/types" +import { + DMLSchema, + IDmlEntityConfig, + 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 { + createBigNumberProperties, + DMLSchemaWithBigNumber, +} from "./helpers/entity-builder/create-big-number-properties" +import { + createDefaultProperties, + DMLSchemaDefaults, +} from "./helpers/entity-builder/create-default-properties" import { ArrayProperty } from "./properties/array" import { BigNumberProperty } from "./properties/big-number" import { BooleanProperty } from "./properties/boolean" @@ -57,7 +67,7 @@ export type ManyToManyOptions = RelationshipOptions & * representing the pivot table created in the * database for this relationship. */ - pivotEntity?: () => DmlEntity + pivotEntity?: () => DmlEntity } ) @@ -99,17 +109,23 @@ export class EntityBuilder { * * export default MyCustom */ - define( - nameOrConfig: DefineOptions, + define( + nameOrConfig: TConfig, schema: Schema - ) { + ): DmlEntity< + Schema & DMLSchemaWithBigNumber & DMLSchemaDefaults, + TConfig + > { this.#disallowImplicitProperties(schema) - return new DmlEntity(nameOrConfig, { + return new DmlEntity(nameOrConfig, { ...schema, ...createBigNumberProperties(schema), ...createDefaultProperties(), - }) + }) as unknown as DmlEntity< + Schema & DMLSchemaWithBigNumber & DMLSchemaDefaults, + TConfig + > } /** diff --git a/packages/core/utils/src/dml/entity.ts b/packages/core/utils/src/dml/entity.ts index b56ca1a8b9..4ebb2db726 100644 --- a/packages/core/utils/src/dml/entity.ts +++ b/packages/core/utils/src/dml/entity.ts @@ -4,25 +4,32 @@ import { EntityIndex, ExtractEntityRelations, IDmlEntity, + IDmlEntityConfig, + InferDmlEntityNameFromConfig, IsDmlEntity, QueryCondition, } from "@medusajs/types" -import { isObject, isString, toCamelCase } from "../common" +import { isObject, isString, toCamelCase, upperCaseFirst } from "../common" import { transformIndexWhere } from "./helpers/entity-builder/build-indexes" import { BelongsTo } from "./relations/belongs-to" -type Config = string | { name?: string; tableName: string } - -function extractNameAndTableName(nameOrConfig: Config) { +function extractNameAndTableName( + nameOrConfig: Config +) { const result = { name: "", tableName: "", + } as { + name: InferDmlEntityNameFromConfig + tableName: string } if (isString(nameOrConfig)) { const [schema, ...rest] = nameOrConfig.split(".") const name = rest.length ? rest.join(".") : schema - result.name = toCamelCase(name) + result.name = upperCaseFirst( + toCamelCase(name) + ) as InferDmlEntityNameFromConfig result.tableName = nameOrConfig } @@ -37,7 +44,9 @@ function extractNameAndTableName(nameOrConfig: Config) { const [schema, ...rest] = potentialName.split(".") const name = rest.length ? rest.join(".") : schema - result.name = toCamelCase(name) + result.name = upperCaseFirst( + toCamelCase(name) + ) as InferDmlEntityNameFromConfig result.tableName = nameOrConfig.tableName } @@ -48,17 +57,23 @@ function extractNameAndTableName(nameOrConfig: Config) { * Dml entity is a representation of a DML model with a unique * name, its schema and relationships. */ -export class DmlEntity implements IDmlEntity { +export class DmlEntity< + Schema extends DMLSchema, + TConfig extends IDmlEntityConfig +> implements IDmlEntity +{ [IsDmlEntity]: true = true - name: string + name: InferDmlEntityNameFromConfig + schema: Schema readonly #tableName: string #cascades: EntityCascades = {} #indexes: EntityIndex[] = [] - constructor(nameOrConfig: Config, public schema: Schema) { + constructor(nameOrConfig: TConfig, schema: Schema) { const { name, tableName } = extractNameAndTableName(nameOrConfig) + this.schema = schema this.name = name this.#tableName = tableName } @@ -70,7 +85,7 @@ export class DmlEntity implements IDmlEntity { * * @param entity */ - static isDmlEntity(entity: unknown): entity is DmlEntity { + static isDmlEntity(entity: unknown): entity is DmlEntity { return !!entity?.[IsDmlEntity] } @@ -78,7 +93,7 @@ export class DmlEntity implements IDmlEntity { * Parse entity to get its underlying information */ parse(): { - name: string + name: InferDmlEntityNameFromConfig tableName: string schema: DMLSchema cascades: EntityCascades diff --git a/packages/core/utils/src/dml/helpers/create-mikro-orm-entity.ts b/packages/core/utils/src/dml/helpers/create-mikro-orm-entity.ts index 46dc5659b0..cefe53edac 100644 --- a/packages/core/utils/src/dml/helpers/create-mikro-orm-entity.ts +++ b/packages/core/utils/src/dml/helpers/create-mikro-orm-entity.ts @@ -1,4 +1,10 @@ -import type { EntityConstructor, Infer } from "@medusajs/types" +import type { + DMLSchema, + EntityConstructor, + IDmlEntity, + Infer, + PropertyType, +} from "@medusajs/types" import { Entity, Filter } from "@mikro-orm/core" import { mikroOrmSoftDeletableFilterOptions } from "../../dal" import { DmlEntity } from "../entity" @@ -34,7 +40,9 @@ export function createMikrORMEntity() { * A helper function to define a Mikro ORM entity from a * DML entity. */ - return function createEntity>(entity: T): Infer { + return function createEntity>( + entity: T + ): Infer { class MikroORMEntity {} const { schema, cascades, indexes: entityIndexes = [] } = entity.parse() @@ -57,11 +65,11 @@ export function createMikrORMEntity() { /** * Processing schema fields */ - Object.entries(schema).forEach(([name, property]) => { + Object.entries(schema as DMLSchema).forEach(([name, property]) => { const field = property.parse(name) if ("fieldName" in field) { - defineProperty(MikroORMEntity, field) + defineProperty(MikroORMEntity, name, property as PropertyType) applyIndexes(MikroORMEntity, tableName, field) applySearchable(MikroORMEntity, field) } else { @@ -85,14 +93,16 @@ export function createMikrORMEntity() { * return the input idempotently * @param entity */ -export const toMikroORMEntity = (entity: T): Infer => { +export const toMikroORMEntity = ( + entity: T +): T extends IDmlEntity ? Infer : T => { let mikroOrmEntity: T | EntityConstructor = entity if (DmlEntity.isDmlEntity(entity)) { mikroOrmEntity = createMikrORMEntity()(entity) } - return mikroOrmEntity as Infer + return mikroOrmEntity as T extends IDmlEntity ? Infer : T } /** @@ -110,6 +120,6 @@ export const toMikroOrmEntities = function (entities: T) { return entity }) as { - [K in keyof T]: T[K] extends DmlEntity ? Infer : T[K] + [K in keyof T]: T[K] extends IDmlEntity ? Infer : T[K] } } diff --git a/packages/core/utils/src/dml/helpers/entity-builder/create-big-number-properties.ts b/packages/core/utils/src/dml/helpers/entity-builder/create-big-number-properties.ts index 4a572cfdc0..9716a98bad 100644 --- a/packages/core/utils/src/dml/helpers/entity-builder/create-big-number-properties.ts +++ b/packages/core/utils/src/dml/helpers/entity-builder/create-big-number-properties.ts @@ -13,7 +13,7 @@ import { NullableModifier } from "../../properties/nullable" * test.amount // valid | type = number * test.raw_amount // valid | type = Record */ -type DMLSchemaWithBigNumber = { +export type DMLSchemaWithBigNumber = { [K in keyof T]: T[K] } & { [K in keyof T as T[K] extends diff --git a/packages/core/utils/src/dml/helpers/entity-builder/define-property.ts b/packages/core/utils/src/dml/helpers/entity-builder/define-property.ts index 92c37d3d44..25ac7e2b21 100644 --- a/packages/core/utils/src/dml/helpers/entity-builder/define-property.ts +++ b/packages/core/utils/src/dml/helpers/entity-builder/define-property.ts @@ -2,6 +2,7 @@ import { EntityConstructor, KnownDataTypes, PropertyMetadata, + PropertyType, } from "@medusajs/types" import { MikroOrmBigNumberProperty } from "../../../dal" import { generateEntityId, isDefined } from "../../../common" @@ -14,6 +15,7 @@ import { Property, Utils, } from "@mikro-orm/core" +import { PrimaryKeyModifier } from "../../properties/primary-key" /** * DML entity data types to PostgreSQL data types via @@ -91,8 +93,10 @@ const SPECIAL_PROPERTIES: { */ export function defineProperty( MikroORMEntity: EntityConstructor, - field: PropertyMetadata + propertyName: string, + property: PropertyType ) { + const field = property.parse(propertyName) /** * Here we initialize nullable properties with a null value */ @@ -169,7 +173,7 @@ export function defineProperty( * Defining an id property */ if (field.dataType.name === "id") { - const IdDecorator = field.dataType.options?.primaryKey + const IdDecorator = PrimaryKeyModifier.isPrimaryKeyModifier(property) ? PrimaryKey({ columnType: "text", type: "string", @@ -211,7 +215,7 @@ export function defineProperty( /** * Defining a primary key property */ - if (field.dataType.options?.primaryKey) { + if (PrimaryKeyModifier.isPrimaryKeyModifier(property)) { PrimaryKey({ columnType, type: propertyType, diff --git a/packages/core/utils/src/dml/helpers/entity-builder/define-relationship.ts b/packages/core/utils/src/dml/helpers/entity-builder/define-relationship.ts index 8dda65a0e5..4337f1a96d 100644 --- a/packages/core/utils/src/dml/helpers/entity-builder/define-relationship.ts +++ b/packages/core/utils/src/dml/helpers/entity-builder/define-relationship.ts @@ -9,9 +9,9 @@ import { BeforeCreate, ManyToMany, ManyToOne, - OnInit, OneToMany, OneToOne, + OnInit, Property, } from "@mikro-orm/core" import { camelToSnakeCase, pluralize } from "../../../common" @@ -81,7 +81,8 @@ export function defineBelongsToRelationship( MikroORMEntity: EntityConstructor, relationship: RelationshipMetadata, relatedEntity: DmlEntity< - Record | RelationshipType> + Record | RelationshipType>, + any >, { relatedModelName }: { relatedModelName: string } ) { @@ -213,7 +214,8 @@ export function defineManyToManyRelationship( MikroORMEntity: EntityConstructor, relationship: RelationshipMetadata, relatedEntity: DmlEntity< - Record | RelationshipType> + Record | RelationshipType>, + any >, { relatedModelName, diff --git a/packages/core/utils/src/dml/helpers/entity-builder/parse-entity-name.ts b/packages/core/utils/src/dml/helpers/entity-builder/parse-entity-name.ts index b34d3af52b..1ec8f4e49f 100644 --- a/packages/core/utils/src/dml/helpers/entity-builder/parse-entity-name.ts +++ b/packages/core/utils/src/dml/helpers/entity-builder/parse-entity-name.ts @@ -5,7 +5,7 @@ import { camelToSnakeCase, toCamelCase, upperCaseFirst } from "../../../common" * Parses entity name and returns model and table name from * it */ -export function parseEntityName(entity: DmlEntity) { +export function parseEntityName(entity: DmlEntity) { const parsedEntity = entity.parse() /** diff --git a/packages/core/utils/src/dml/properties/id.ts b/packages/core/utils/src/dml/properties/id.ts index 42d5325b9f..ab2be6efc8 100644 --- a/packages/core/utils/src/dml/properties/id.ts +++ b/packages/core/utils/src/dml/properties/id.ts @@ -1,19 +1,27 @@ import { BaseProperty } from "./base" +import { PrimaryKeyModifier } from "./primary-key" + +const IsIdProperty = Symbol("IsIdProperty") /** * The Id property defines a unique identifier for the schema. * Most of the times it will be the primary as well. */ export class IdProperty extends BaseProperty { + [IsIdProperty] = true + + static isIdProperty(value: any): value is IdProperty { + return !!value?.[IsIdProperty] + } + protected dataType: { name: "id" options: { - primaryKey: boolean prefix?: string } } = { name: "id", - options: { primaryKey: false }, + options: {}, } constructor(options?: { prefix?: string }) { @@ -37,7 +45,6 @@ export class IdProperty extends BaseProperty { * @customNamespace Property Configuration Methods */ primaryKey() { - this.dataType.options.primaryKey = true - return this + return new PrimaryKeyModifier(this) } } diff --git a/packages/core/utils/src/dml/properties/number.ts b/packages/core/utils/src/dml/properties/number.ts index e72d0cccdc..f8c8fc1490 100644 --- a/packages/core/utils/src/dml/properties/number.ts +++ b/packages/core/utils/src/dml/properties/number.ts @@ -1,4 +1,5 @@ import { BaseProperty } from "./base" +import { PrimaryKeyModifier } from "./primary-key" /** * The NumberProperty is used to define a numeric/integer @@ -7,15 +8,11 @@ import { BaseProperty } from "./base" export class NumberProperty extends BaseProperty { protected dataType: { name: "number" - options: { - primaryKey: boolean - } + options: {} } primaryKey() { - this.dataType.options.primaryKey = true - - return this + return new PrimaryKeyModifier(this) } constructor(options?: { primaryKey?: boolean }) { @@ -23,7 +20,7 @@ export class NumberProperty extends BaseProperty { this.dataType = { name: "number", - options: { primaryKey: false, ...options }, + options: { ...options }, } } } diff --git a/packages/core/utils/src/dml/properties/primary-key.ts b/packages/core/utils/src/dml/properties/primary-key.ts new file mode 100644 index 0000000000..77ed360fee --- /dev/null +++ b/packages/core/utils/src/dml/properties/primary-key.ts @@ -0,0 +1,39 @@ +import { PropertyType } from "@medusajs/types" + +const IsPrimaryKeyModifier = Symbol.for("isPrimaryKeyModifier") +/** + * PrimaryKey modifier marks a schema node as primaryKey + */ +export class PrimaryKeyModifier> + implements PropertyType +{ + [IsPrimaryKeyModifier]: true = true + + static isPrimaryKeyModifier(obj: any): obj is PrimaryKeyModifier { + return !!obj?.[IsPrimaryKeyModifier] + } + /** + * A type-only property to infer the JavScript data-type + * of the schema property + */ + declare $dataType: T + + /** + * The parent schema on which the primaryKey modifier is + * applied + */ + #schema: Schema + + constructor(schema: Schema) { + this.#schema = schema + } + + /** + * Returns the serialized metadata + */ + parse(fieldName: string) { + const schema = this.#schema.parse(fieldName) + schema.primaryKey = true + return schema + } +} diff --git a/packages/core/utils/src/dml/properties/text.ts b/packages/core/utils/src/dml/properties/text.ts index 052c32b4c2..f329d348e1 100644 --- a/packages/core/utils/src/dml/properties/text.ts +++ b/packages/core/utils/src/dml/properties/text.ts @@ -1,4 +1,5 @@ import { BaseProperty } from "./base" +import { PrimaryKeyModifier } from "./primary-key" /** * The TextProperty is used to define a textual property @@ -7,14 +8,12 @@ export class TextProperty extends BaseProperty { protected dataType: { name: "text" options: { - primaryKey: boolean prefix?: string searchable: boolean } } = { name: "text", options: { - primaryKey: false, searchable: false, }, } @@ -35,9 +34,7 @@ export class TextProperty extends BaseProperty { * @customNamespace Property Configuration Methods */ primaryKey() { - this.dataType.options.primaryKey = true - - return this + return new PrimaryKeyModifier(this) } /** diff --git a/packages/core/utils/src/dml/relations/base.ts b/packages/core/utils/src/dml/relations/base.ts index 906554909d..55af61c929 100644 --- a/packages/core/utils/src/dml/relations/base.ts +++ b/packages/core/utils/src/dml/relations/base.ts @@ -5,11 +5,15 @@ import { RelationshipTypes, } from "@medusajs/types" +export const IsRelationship = Symbol.for("isRelationship") + /** * The BaseRelationship encapsulates the repetitive parts of defining * a relationship */ export abstract class BaseRelationship implements RelationshipType { + [IsRelationship]: true = true + #referencedEntity: T /** @@ -28,6 +32,12 @@ export abstract class BaseRelationship implements RelationshipType { */ declare $dataType: T + static isRelationship( + relationship: any + ): relationship is BaseRelationship { + return !!relationship?.[IsRelationship] + } + constructor(referencedEntity: T, options: RelationshipOptions) { this.#referencedEntity = referencedEntity this.options = options diff --git a/packages/core/utils/src/dml/relations/nullable.ts b/packages/core/utils/src/dml/relations/nullable.ts index e5f7749d7e..c1a45a76b7 100644 --- a/packages/core/utils/src/dml/relations/nullable.ts +++ b/packages/core/utils/src/dml/relations/nullable.ts @@ -1,4 +1,5 @@ import { RelationshipType } from "@medusajs/types" +import { IsRelationship } from "./base" const IsNullableModifier = Symbol.for("isNullableModifier") @@ -8,7 +9,8 @@ const IsNullableModifier = Symbol.for("isNullableModifier") export class NullableModifier> implements RelationshipType { - [IsNullableModifier]: true = true + [IsNullableModifier]: true = true; + [IsRelationship]: true = true static isNullableModifier( modifier: any diff --git a/packages/core/utils/src/modules-sdk/__fixtures__/joiner-config/entities.ts b/packages/core/utils/src/modules-sdk/__fixtures__/joiner-config/entities.ts new file mode 100644 index 0000000000..5a70c88333 --- /dev/null +++ b/packages/core/utils/src/modules-sdk/__fixtures__/joiner-config/entities.ts @@ -0,0 +1,53 @@ +import { model } from "../../../dml" + +export const FulfillmentSet = { + name: "FulfillmentSet", +} +export const ShippingOption = { + name: "ShippingOption", +} +export const ShippingProfile = { + name: "ShippingProfile", +} +export const Fulfillment = { + name: "Fulfillment", +} +export const FulfillmentProvider = { + name: "FulfillmentProvider", +} +export const ServiceZone = { + name: "ServiceZone", +} +export const GeoZone = { + name: "GeoZone", +} +export const ShippingOptionRule = { + name: "ShippingOptionRule", +} + +export const dmlFulfillmentSet = model.define(FulfillmentSet.name, { + id: model.id().primaryKey(), +}) +export const dmlShippingOption = model.define(ShippingOption.name, { + id: model.id().primaryKey(), +}) +export const dmlShippingProfile = model.define(ShippingProfile.name, { + id: model.id().primaryKey(), +}) +export const dmlFulfillment = model.define(Fulfillment.name, { + id: model.id().primaryKey(), +}) +export const dmlFulfillmentProvider = model.define(FulfillmentProvider.name, { + id: model.id().primaryKey(), +}) +export const dmlServiceZone = model.define(ServiceZone.name, { + id: model.id().primaryKey(), +}) + +export const dmlGeoZone = model.define(GeoZone.name, { + id: model.id().primaryKey(), +}) + +export const dmlShippingOptionRule = model.define(ShippingOptionRule.name, { + id: model.id().primaryKey(), +}) diff --git a/packages/core/utils/src/modules-sdk/__tests__/joiner-config-builder.spec.ts b/packages/core/utils/src/modules-sdk/__tests__/joiner-config-builder.spec.ts index 24d3958158..6608610555 100644 --- a/packages/core/utils/src/modules-sdk/__tests__/joiner-config-builder.spec.ts +++ b/packages/core/utils/src/modules-sdk/__tests__/joiner-config-builder.spec.ts @@ -1,315 +1,505 @@ -import { defineJoinerConfig } from "../joiner-config-builder" +import { + buildLinkableKeysFromDmlObjects, + buildLinkableKeysFromMikroOrmObjects, + buildLinkConfigFromDmlObjects, + defineJoinerConfig, +} from "../joiner-config-builder" import { Modules } from "../definition" +import { model } from "../../dml" +import { expectTypeOf } from "expect-type" +import { + dmlFulfillment, + dmlFulfillmentProvider, + dmlFulfillmentSet, + dmlGeoZone, + dmlServiceZone, + dmlShippingOption, + dmlShippingOptionRule, + dmlShippingProfile, + Fulfillment, + FulfillmentProvider, + FulfillmentSet, + GeoZone, + ServiceZone, + ShippingOption, + ShippingOptionRule, + ShippingProfile, +} from "../__fixtures__/joiner-config/entities" -const FulfillmentSet = { - name: "FulfillmentSet", -} -const ShippingOption = { - name: "ShippingOption", -} -const ShippingProfile = { - name: "ShippingProfile", -} -const Fulfillment = { - name: "Fulfillment", -} -const FulfillmentProvider = { - name: "FulfillmentProvider", -} -const ServiceZone = { - name: "ServiceZone", -} -const GeoZone = { - name: "GeoZone", -} -const ShippingOptionRule = { - name: "ShippingOptionRule", -} +describe("joiner-config-builder", () => { + describe("defineJoiner | Mikro orm objects", () => { + it("should return a full joiner configuration", () => { + const joinerConfig = defineJoinerConfig(Modules.FULFILLMENT, { + models: [ + FulfillmentSet, + ShippingOption, + ShippingProfile, + Fulfillment, + FulfillmentProvider, + ServiceZone, + GeoZone, + ShippingOptionRule, + ], + }) -describe("defineJoiner", () => { - it("should return a full joiner configuration", () => { - const joinerConfig = defineJoinerConfig(Modules.FULFILLMENT, { - entityQueryingConfig: [ - FulfillmentSet, - ShippingOption, - ShippingProfile, - Fulfillment, - FulfillmentProvider, - ServiceZone, - GeoZone, - ShippingOptionRule, - ], + expect(joinerConfig).toEqual({ + serviceName: Modules.FULFILLMENT, + primaryKeys: ["id"], + schema: undefined, + linkableKeys: { + fulfillment_set_id: FulfillmentSet.name, + shipping_option_id: ShippingOption.name, + shipping_profile_id: ShippingProfile.name, + fulfillment_id: Fulfillment.name, + fulfillment_provider_id: FulfillmentProvider.name, + service_zone_id: ServiceZone.name, + geo_zone_id: GeoZone.name, + shipping_option_rule_id: ShippingOptionRule.name, + }, + alias: [ + { + name: ["fulfillment_set", "fulfillment_sets"], + args: { + entity: FulfillmentSet.name, + methodSuffix: "FulfillmentSets", + }, + }, + { + name: ["shipping_option", "shipping_options"], + args: { + entity: ShippingOption.name, + methodSuffix: "ShippingOptions", + }, + }, + { + name: ["shipping_profile", "shipping_profiles"], + args: { + entity: ShippingProfile.name, + methodSuffix: "ShippingProfiles", + }, + }, + { + name: ["fulfillment", "fulfillments"], + args: { + entity: Fulfillment.name, + methodSuffix: "Fulfillments", + }, + }, + { + name: ["fulfillment_provider", "fulfillment_providers"], + args: { + entity: FulfillmentProvider.name, + methodSuffix: "FulfillmentProviders", + }, + }, + { + name: ["service_zone", "service_zones"], + args: { + entity: ServiceZone.name, + methodSuffix: "ServiceZones", + }, + }, + { + name: ["geo_zone", "geo_zones"], + args: { + entity: GeoZone.name, + methodSuffix: "GeoZones", + }, + }, + { + name: ["shipping_option_rule", "shipping_option_rules"], + args: { + entity: ShippingOptionRule.name, + methodSuffix: "ShippingOptionRules", + }, + }, + ], + }) }) - expect(joinerConfig).toEqual({ - serviceName: Modules.FULFILLMENT, - primaryKeys: ["id"], - schema: undefined, - linkableKeys: { - fulfillment_set_id: FulfillmentSet.name, - shipping_option_id: ShippingOption.name, - shipping_profile_id: ShippingProfile.name, - fulfillment_id: Fulfillment.name, - fulfillment_provider_id: FulfillmentProvider.name, - service_zone_id: ServiceZone.name, - geo_zone_id: GeoZone.name, - shipping_option_rule_id: ShippingOptionRule.name, - }, - alias: [ - { - name: ["fulfillment_set", "fulfillment_sets"], - args: { - entity: FulfillmentSet.name, - methodSuffix: "FulfillmentSets", + it("should return a full joiner configuration with custom aliases", () => { + const joinerConfig = defineJoinerConfig(Modules.FULFILLMENT, { + alias: [ + { + name: ["custom", "customs"], + args: { + entity: "Custom", + methodSuffix: "Customs", + }, }, - }, - { - name: ["shipping_option", "shipping_options"], - args: { - entity: ShippingOption.name, - methodSuffix: "ShippingOptions", + ], + }) + + expect(joinerConfig).toEqual({ + serviceName: Modules.FULFILLMENT, + primaryKeys: ["id"], + schema: undefined, + linkableKeys: {}, + alias: [ + { + name: ["custom", "customs"], + args: { + entity: "Custom", + methodSuffix: "Customs", + }, }, - }, - { - name: ["shipping_profile", "shipping_profiles"], - args: { - entity: ShippingProfile.name, - methodSuffix: "ShippingProfiles", + ], + }) + }) + + it("should return a full joiner configuration with custom aliases and models", () => { + const joinerConfig = defineJoinerConfig(Modules.FULFILLMENT, { + models: [ + FulfillmentSet, + ShippingOption, + ShippingProfile, + Fulfillment, + FulfillmentProvider, + ServiceZone, + GeoZone, + ShippingOptionRule, + ], + alias: [ + { + name: ["custom", "customs"], + args: { + entity: "Custom", + methodSuffix: "Customs", + }, }, + ], + }) + + expect(joinerConfig).toEqual({ + serviceName: Modules.FULFILLMENT, + primaryKeys: ["id"], + schema: undefined, + linkableKeys: { + fulfillment_set_id: FulfillmentSet.name, + shipping_option_id: ShippingOption.name, + shipping_profile_id: ShippingProfile.name, + fulfillment_id: Fulfillment.name, + fulfillment_provider_id: FulfillmentProvider.name, + service_zone_id: ServiceZone.name, + geo_zone_id: GeoZone.name, + shipping_option_rule_id: ShippingOptionRule.name, }, - { - name: ["fulfillment", "fulfillments"], - args: { - entity: Fulfillment.name, - methodSuffix: "Fulfillments", + alias: [ + { + name: ["custom", "customs"], + args: { + entity: "Custom", + methodSuffix: "Customs", + }, }, - }, - { - name: ["fulfillment_provider", "fulfillment_providers"], - args: { - entity: FulfillmentProvider.name, - methodSuffix: "FulfillmentProviders", + { + name: ["fulfillment_set", "fulfillment_sets"], + args: { + entity: FulfillmentSet.name, + methodSuffix: "FulfillmentSets", + }, }, - }, - { - name: ["service_zone", "service_zones"], - args: { - entity: ServiceZone.name, - methodSuffix: "ServiceZones", + { + name: ["shipping_option", "shipping_options"], + args: { + entity: ShippingOption.name, + methodSuffix: "ShippingOptions", + }, }, - }, - { - name: ["geo_zone", "geo_zones"], - args: { - entity: GeoZone.name, - methodSuffix: "GeoZones", + { + name: ["shipping_profile", "shipping_profiles"], + args: { + entity: ShippingProfile.name, + methodSuffix: "ShippingProfiles", + }, }, - }, - { - name: ["shipping_option_rule", "shipping_option_rules"], - args: { - entity: ShippingOptionRule.name, - methodSuffix: "ShippingOptionRules", + { + name: ["fulfillment", "fulfillments"], + args: { + entity: Fulfillment.name, + methodSuffix: "Fulfillments", + }, }, + { + name: ["fulfillment_provider", "fulfillment_providers"], + args: { + entity: FulfillmentProvider.name, + methodSuffix: "FulfillmentProviders", + }, + }, + { + name: ["service_zone", "service_zones"], + args: { + entity: ServiceZone.name, + methodSuffix: "ServiceZones", + }, + }, + { + name: ["geo_zone", "geo_zones"], + args: { + entity: GeoZone.name, + methodSuffix: "GeoZones", + }, + }, + { + name: ["shipping_option_rule", "shipping_option_rules"], + args: { + entity: ShippingOptionRule.name, + methodSuffix: "ShippingOptionRules", + }, + }, + ], + }) + }) + + it("should return a full joiner configuration with custom aliases without method suffix", () => { + const joinerConfig = defineJoinerConfig(Modules.FULFILLMENT, { + alias: [ + { + name: ["custom", "customs"], + args: { + entity: "Custom", + }, + }, + ], + }) + + expect(joinerConfig).toEqual({ + serviceName: Modules.FULFILLMENT, + primaryKeys: ["id"], + schema: undefined, + linkableKeys: {}, + alias: [ + { + name: ["custom", "customs"], + args: { + entity: "Custom", + methodSuffix: "Customs", + }, + }, + ], + }) + }) + + it("should return a full joiner configuration with custom aliases overriding defaults", () => { + const joinerConfig = defineJoinerConfig(Modules.FULFILLMENT, { + models: [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", + }, + }, + ], + }) }) }) - it("should return a full joiner configuration with custom aliases", () => { - const joinerConfig = defineJoinerConfig(Modules.FULFILLMENT, { - alias: [ - { - name: ["custom", "customs"], - args: { - entity: "Custom", - methodSuffix: "Customs", - }, - }, - ], - }) + describe("defineJoiner | DML objects", () => { + it("should return a full joiner configuration", () => { + const joinerConfig = defineJoinerConfig(Modules.FULFILLMENT, { + models: [ + dmlFulfillmentSet, + dmlShippingOption, + dmlShippingProfile, + dmlFulfillment, + dmlFulfillmentProvider, + dmlServiceZone, + dmlGeoZone, + dmlShippingOptionRule, + ], + }) - expect(joinerConfig).toEqual({ - serviceName: Modules.FULFILLMENT, - primaryKeys: ["id"], - schema: undefined, - linkableKeys: {}, - alias: [ - { - name: ["custom", "customs"], - args: { - entity: "Custom", - methodSuffix: "Customs", - }, + expect(joinerConfig).toEqual({ + serviceName: Modules.FULFILLMENT, + primaryKeys: ["id"], + schema: undefined, + linkableKeys: { + fulfillment_set_id: FulfillmentSet.name, + shipping_option_id: ShippingOption.name, + shipping_profile_id: ShippingProfile.name, + fulfillment_id: Fulfillment.name, + fulfillment_provider_id: FulfillmentProvider.name, + service_zone_id: ServiceZone.name, + geo_zone_id: GeoZone.name, + shipping_option_rule_id: ShippingOptionRule.name, }, - ], + alias: [ + { + name: ["fulfillment_set", "fulfillment_sets"], + args: { + entity: FulfillmentSet.name, + methodSuffix: "FulfillmentSets", + }, + }, + { + name: ["shipping_option", "shipping_options"], + args: { + entity: ShippingOption.name, + methodSuffix: "ShippingOptions", + }, + }, + { + name: ["shipping_profile", "shipping_profiles"], + args: { + entity: ShippingProfile.name, + methodSuffix: "ShippingProfiles", + }, + }, + { + name: ["fulfillment", "fulfillments"], + args: { + entity: Fulfillment.name, + methodSuffix: "Fulfillments", + }, + }, + { + name: ["fulfillment_provider", "fulfillment_providers"], + args: { + entity: FulfillmentProvider.name, + methodSuffix: "FulfillmentProviders", + }, + }, + { + name: ["service_zone", "service_zones"], + args: { + entity: ServiceZone.name, + methodSuffix: "ServiceZones", + }, + }, + { + name: ["geo_zone", "geo_zones"], + args: { + entity: GeoZone.name, + methodSuffix: "GeoZones", + }, + }, + { + name: ["shipping_option_rule", "shipping_option_rules"], + args: { + entity: ShippingOptionRule.name, + methodSuffix: "ShippingOptionRules", + }, + }, + ], + }) }) }) - it("should return a full joiner configuration with custom aliases and models", () => { - const joinerConfig = defineJoinerConfig(Modules.FULFILLMENT, { - entityQueryingConfig: [ - FulfillmentSet, - ShippingOption, - ShippingProfile, - Fulfillment, - FulfillmentProvider, - ServiceZone, - GeoZone, - ShippingOptionRule, - ], - alias: [ - { - name: ["custom", "customs"], - args: { - entity: "Custom", - methodSuffix: "Customs", - }, - }, - ], - }) + describe("buildLinkableKeysFromDmlObjects", () => { + it("should return a linkableKeys object based on the DML's primary keys", () => { + const user = model.define("user", { + id: model.id().primaryKey(), + name: model.text(), + }) - expect(joinerConfig).toEqual({ - serviceName: Modules.FULFILLMENT, - primaryKeys: ["id"], - schema: undefined, - linkableKeys: { - fulfillment_set_id: FulfillmentSet.name, - shipping_option_id: ShippingOption.name, - shipping_profile_id: ShippingProfile.name, - fulfillment_id: Fulfillment.name, - fulfillment_provider_id: FulfillmentProvider.name, - service_zone_id: ServiceZone.name, - geo_zone_id: GeoZone.name, - shipping_option_rule_id: ShippingOptionRule.name, - }, - alias: [ - { - name: ["custom", "customs"], - args: { - entity: "Custom", - methodSuffix: "Customs", - }, - }, - { - name: ["fulfillment_set", "fulfillment_sets"], - args: { - entity: FulfillmentSet.name, - methodSuffix: "FulfillmentSets", - }, - }, - { - name: ["shipping_option", "shipping_options"], - args: { - entity: ShippingOption.name, - methodSuffix: "ShippingOptions", - }, - }, - { - name: ["shipping_profile", "shipping_profiles"], - args: { - entity: ShippingProfile.name, - methodSuffix: "ShippingProfiles", - }, - }, - { - name: ["fulfillment", "fulfillments"], - args: { - entity: Fulfillment.name, - methodSuffix: "Fulfillments", - }, - }, - { - name: ["fulfillment_provider", "fulfillment_providers"], - args: { - entity: FulfillmentProvider.name, - methodSuffix: "FulfillmentProviders", - }, - }, - { - name: ["service_zone", "service_zones"], - args: { - entity: ServiceZone.name, - methodSuffix: "ServiceZones", - }, - }, - { - name: ["geo_zone", "geo_zones"], - args: { - entity: GeoZone.name, - methodSuffix: "GeoZones", - }, - }, - { - name: ["shipping_option_rule", "shipping_option_rules"], - args: { - entity: ShippingOptionRule.name, - methodSuffix: "ShippingOptionRules", - }, - }, - ], + const car = model.define("car", { + id: model.id(), + number_plate: model.text().primaryKey(), + test: model.text(), + }) + + const linkableKeys = buildLinkableKeysFromDmlObjects([user, car]) + expectTypeOf(linkableKeys).toMatchTypeOf<{ + user_id: "User" + car_number_plate: "Car" + }>() + + expect(linkableKeys).toEqual({ + user_id: user.name, + car_number_plate: car.name, + }) }) }) - it("should return a full joiner configuration with custom aliases without method suffix", () => { - const joinerConfig = defineJoinerConfig(Modules.FULFILLMENT, { - alias: [ - { - name: ["custom", "customs"], - args: { - entity: "Custom", - }, - }, - ], - }) + describe("buildLinkableKeysFromMikroOrmObjects", () => { + it("should return a linkableKeys object based on the mikro orm models name", () => { + class User {} + class Car {} - expect(joinerConfig).toEqual({ - serviceName: Modules.FULFILLMENT, - primaryKeys: ["id"], - schema: undefined, - linkableKeys: {}, - alias: [ - { - name: ["custom", "customs"], - args: { - entity: "Custom", - methodSuffix: "Customs", - }, - }, - ], + const linkableKeys = buildLinkableKeysFromMikroOrmObjects([Car, User]) + + expect(linkableKeys).toEqual({ + user_id: User.name, + car_id: Car.name, + }) }) }) - 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", - }, - }, - ], - }) + describe("buildLinkConfigFromDmlObjects", () => { + it("should return a link config object based on the DML's primary keys", () => { + const user = model.define("user", { + id: model.id().primaryKey(), + name: model.text(), + }) - 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", - }, - }, - ], + const car = model.define("car", { + id: model.id(), + number_plate: model.text().primaryKey(), + }) + + const linkConfig = buildLinkConfigFromDmlObjects([user, car]) + + expectTypeOf(linkConfig).toMatchTypeOf<{ + user: { + id: { + linkable: "user_id" + primaryKey: "id" + } + toJSON: () => { + linkable: string + primaryKey: string + } + } + car: { + number_plate: { + linkable: "car_number_plate" + primaryKey: "number_plate" + } + toJSON: () => { + linkable: string + primaryKey: string + } + } + }>() + + expect(linkConfig.user.id).toEqual({ + linkable: "user_id", + primaryKey: "id", + }) + expect(linkConfig.car.number_plate).toEqual({ + linkable: "car_number_plate", + primaryKey: "number_plate", + }) + + expect(linkConfig.car.toJSON()).toEqual({ + linkable: "car_number_plate", + primaryKey: "number_plate", + }) + expect(linkConfig.user.toJSON()).toEqual({ + linkable: "user_id", + primaryKey: "id", + }) }) }) }) diff --git a/packages/core/utils/src/modules-sdk/event-builder-factory.ts b/packages/core/utils/src/modules-sdk/event-builder-factory.ts index 204ac7e942..bed6a6edd9 100644 --- a/packages/core/utils/src/modules-sdk/event-builder-factory.ts +++ b/packages/core/utils/src/modules-sdk/event-builder-factory.ts @@ -1,6 +1,9 @@ import { Context, EventBusTypes } from "@medusajs/types" +// TODO should that move closer to the event bus? and maybe be rename to moduleEventBuilderFactory + /** + * * Factory function to create event builders for different entities * * @example @@ -48,7 +51,8 @@ export function eventBuilderFactory({ // The event enums contains event formatted like so [object]_[action] e.g. PRODUCT_CREATED // We expect the keys of events to be fully uppercased - const eventName = eventsEnum[`${object.toUpperCase()}_${action.toUpperCase()}`] + const eventName = + eventsEnum[`${object.toUpperCase()}_${action.toUpperCase()}`] data.forEach((dataItem) => { messages.push({ diff --git a/packages/core/utils/src/modules-sdk/index.ts b/packages/core/utils/src/modules-sdk/index.ts index aeb2ceb573..7cfe583322 100644 --- a/packages/core/utils/src/modules-sdk/index.ts +++ b/packages/core/utils/src/modules-sdk/index.ts @@ -13,4 +13,4 @@ export * from "./medusa-internal-service" export * from "./medusa-service" export * from "./migration-scripts" export * from "./mikro-orm-cli-config-builder" - +export * from "./module" diff --git a/packages/core/utils/src/modules-sdk/joiner-config-builder.ts b/packages/core/utils/src/modules-sdk/joiner-config-builder.ts index 00c4244c26..273efefdca 100644 --- a/packages/core/utils/src/modules-sdk/joiner-config-builder.ts +++ b/packages/core/utils/src/modules-sdk/joiner-config-builder.ts @@ -1,14 +1,24 @@ -import { JoinerServiceConfigAlias, ModuleJoinerConfig } from "@medusajs/types" +import { + JoinerServiceConfigAlias, + ModuleJoinerConfig, + PropertyType, +} from "@medusajs/types" import { dirname, join } from "path" import { - MapToConfig, camelToSnakeCase, deduplicate, getCallerFilePath, + isObject, + lowerCaseFirst, + MapToConfig, pluralize, upperCaseFirst, } from "../common" import { loadModels } from "./loaders/load-models" +import { DmlEntity } from "../dml" +import { BaseRelationship } from "../dml/relations/base" +import { PrimaryKeyModifier } from "../dml/properties/primary-key" +import { InferLinkableKeys, InfersLinksConfig } from "./types/links-config" /** * Define joiner config for a module based on the models (object representation or entities) present in the models directory. This action will be sync until @@ -20,7 +30,7 @@ import { loadModels } from "./loaders/load-models" * @param moduleName * @param alias * @param schema - * @param entityQueryingConfig + * @param models * @param linkableKeys * @param primaryKeys */ @@ -29,13 +39,13 @@ export function defineJoinerConfig( { alias, schema, - entityQueryingConfig, + models, linkableKeys, primaryKeys, }: { alias?: JoinerServiceConfigAlias[] schema?: string - entityQueryingConfig?: { name: string }[] + models?: DmlEntity[] | { name: string }[] linkableKeys?: Record primaryKeys?: string[] } = {} @@ -62,20 +72,64 @@ export function defineJoinerConfig( basePath = join(basePath, "models") - const models = deduplicate( - [...(entityQueryingConfig ?? loadModels(basePath))].flatMap((v) => v!.name) - ).map((name) => ({ name })) + let loadedModels = models ?? loadModels(basePath) + const modelDefinitions = new Map( + loadedModels + .filter((model) => !!DmlEntity.isDmlEntity(model)) + .map((model) => [model.name, model]) + ) + const mikroOrmObjects = new Map( + loadedModels + .filter((model) => !DmlEntity.isDmlEntity(model)) + .map((model) => [model.name, model]) + ) + + // We prioritize DML if there is any equivalent Mikro orm entities found + loadedModels = [...modelDefinitions.values()] + mikroOrmObjects.forEach((model) => { + if (modelDefinitions.has(model.name)) { + return + } + + loadedModels.push(model) + }) + + if (!linkableKeys) { + const linkableKeysFromDml = buildLinkableKeysFromDmlObjects([ + ...modelDefinitions.values(), + ]) + const linkableKeysFromMikroOrm = buildLinkableKeysFromMikroOrmObjects([ + ...mikroOrmObjects.values(), + ]) + linkableKeys = { + ...linkableKeysFromDml, + ...linkableKeysFromMikroOrm, + } + } + + if (!primaryKeys && modelDefinitions.size) { + const linkConfig = buildLinkConfigFromDmlObjects([ + ...modelDefinitions.values(), + ]) + + primaryKeys = deduplicate( + Object.values(linkConfig).flatMap((entityLinkConfig) => { + return (Object.values(entityLinkConfig) as any[]) + .filter((linkableConfig) => isObject(linkableConfig)) + .map((linkableConfig) => { + return linkableConfig.primaryKey + }) + }) + ) + } + + // TODO: In the context of DML add a validation on primary keys and linkable keys if the consumer provide them manually. follow up pr return { serviceName: moduleName, primaryKeys: primaryKeys ?? ["id"], schema, - linkableKeys: - linkableKeys ?? - models.reduce((acc, entity) => { - acc[`${camelToSnakeCase(entity.name).toLowerCase()}_id`] = entity.name - return acc - }, {} as Record), + linkableKeys: linkableKeys, alias: [ ...[...(alias ?? ([] as any))].map((alias) => ({ name: alias.name, @@ -86,7 +140,7 @@ export function defineJoinerConfig( pluralize(upperCaseFirst(alias.args.entity)), }, })), - ...models + ...loadedModels .filter((model) => { return ( !alias || !alias.some((alias) => alias.args?.entity === model.name) @@ -106,8 +160,153 @@ export function defineJoinerConfig( } } +/** + * From a set of DML objects, build the linkable keys + * + * @example + * const user = model.define("user", { + * id: model.id(), + * name: model.text(), + * }) + * + * const car = model.define("car", { + * id: model.id(), + * number_plate: model.text().primaryKey(), + * test: model.text(), + * }) + * + * // output: + * // { + * // user_id: 'User', + * // car_number_plate: 'Car', + * // } + * + * @param models + */ +export function buildLinkableKeysFromDmlObjects< + const T extends DmlEntity[], + LinkableKeys = InferLinkableKeys +>(models: T): LinkableKeys { + const linkableKeys = {} as LinkableKeys + + for (const model of models) { + if (!DmlEntity.isDmlEntity(model)) { + continue + } + + const schema = model.schema + const primaryKeys: string[] = [] + + for (const [property, value] of Object.entries(schema)) { + if (BaseRelationship.isRelationship(value)) { + continue + } + + const parsedProperty = (value as PropertyType).parse(property) + if (PrimaryKeyModifier.isPrimaryKeyModifier(value)) { + const linkableKeyName = + parsedProperty.dataType.options?.linkable ?? + `${camelToSnakeCase(model.name).toLowerCase()}_${property}` + primaryKeys.push(linkableKeyName) + } + } + + if (primaryKeys.length) { + primaryKeys.forEach((primaryKey) => { + linkableKeys[primaryKey] = model.name + }) + } + } + + return linkableKeys +} + +/** + * Build linkable keys from MikroORM objects + * @deprecated + * @param models + */ +export function buildLinkableKeysFromMikroOrmObjects( + models: Function[] +): Record { + return models.reduce((acc, entity) => { + acc[`${camelToSnakeCase(entity.name).toLowerCase()}_id`] = entity.name + return acc + }, {}) as Record +} + /** * Build entities name to linkable keys map + * + * @example + * const user = model.define("user", { + * id: model.id(), + * name: model.text(), + * }) + * + * const car = model.define("car", { + * id: model.id(), + * number_plate: model.text().primaryKey(), + * test: model.text(), + * }) + * + * // output: + * // { + * // toJSON: function () { }, + * // user: { + * // id: "user_id", + * // }, + * // car: { + * // number_plate: "car_number_plate", + * // }, + * // } + * + * @param models + */ +export function buildLinkConfigFromDmlObjects< + const T extends DmlEntity[] +>(models: T = [] as unknown as T): InfersLinksConfig { + const linkConfig = {} as InfersLinksConfig + + for (const model of models) { + if (!DmlEntity.isDmlEntity(model)) { + continue + } + + const schema = model.schema + const modelLinkConfig = (linkConfig[lowerCaseFirst(model.name)] ??= { + toJSON: function () { + const linkables = Object.entries(this) + .filter(([name]) => name !== "toJSON") + .map(([, object]) => object) + const lastIndex = linkables.length - 1 + return linkables[lastIndex] + }, + }) + + for (const [property, value] of Object.entries(schema)) { + if (BaseRelationship.isRelationship(value)) { + continue + } + + const parsedProperty = (value as PropertyType).parse(property) + if (PrimaryKeyModifier.isPrimaryKeyModifier(value)) { + const linkableKeyName = + parsedProperty.dataType.options?.linkable ?? + `${camelToSnakeCase(model.name).toLowerCase()}_${property}` + modelLinkConfig[property] = { + linkable: linkableKeyName, + primaryKey: property, + } + } + } + } + + return linkConfig as InfersLinksConfig & Record +} + +/** + * Reversed map from linkableKeys to entity name to linkable keys * @param linkableKeys */ export function buildEntitiesNameToLinkableKeysMap( diff --git a/packages/core/utils/src/modules-sdk/loaders/load-models.ts b/packages/core/utils/src/modules-sdk/loaders/load-models.ts index 8026e8bc10..e83d551f8f 100644 --- a/packages/core/utils/src/modules-sdk/loaders/load-models.ts +++ b/packages/core/utils/src/modules-sdk/loaders/load-models.ts @@ -27,15 +27,15 @@ export function loadModels(basePath: string) { if (stats.isFile()) { try { - const required = require(filePath) as { - [key: string]: { name?: string } - } + const required = require(filePath) - return Object.values(required).filter((resource) => !!resource.name) + return Object.values(required).filter( + (resource: any) => !!resource.name + ) } catch (e) {} } return }) - .filter(Boolean) as { name: string }[] + .filter(Boolean) as any[] } diff --git a/packages/core/utils/src/modules-sdk/medusa-internal-service.ts b/packages/core/utils/src/modules-sdk/medusa-internal-service.ts index 5cade9bee0..a5429d81f6 100644 --- a/packages/core/utils/src/modules-sdk/medusa-internal-service.ts +++ b/packages/core/utils/src/modules-sdk/medusa-internal-service.ts @@ -35,12 +35,16 @@ type SelectorAndData = { data: any } -export function MedusaInternalService( +export function MedusaInternalService< + TContainer extends object = object, + TEntity extends object = any +>( rawModel: any ): { - new ( - container: TContainer - ): ModulesSdkTypes.IMedusaInternalService + new (container: TContainer): ModulesSdkTypes.IMedusaInternalService< + TEntity, + TContainer + > } { const model = DmlEntity.isDmlEntity(rawModel) ? toMikroORMEntity(rawModel) @@ -49,7 +53,7 @@ export function MedusaInternalService( const injectedRepositoryName = `${lowerCaseFirst(model.name)}Repository` const propertyRepositoryName = `__${injectedRepositoryName}__` - class AbstractService_ + class AbstractService_ implements ModulesSdkTypes.IMedusaInternalService { readonly __container__: TContainer; @@ -556,7 +560,5 @@ export function MedusaInternalService( } } - return AbstractService_ as unknown as new ( - container: TContainer - ) => ModulesSdkTypes.IMedusaInternalService + return AbstractService_ as any } diff --git a/packages/core/utils/src/modules-sdk/medusa-service.ts b/packages/core/utils/src/modules-sdk/medusa-service.ts index ac9a2e3c11..77bf317dba 100644 --- a/packages/core/utils/src/modules-sdk/medusa-service.ts +++ b/packages/core/utils/src/modules-sdk/medusa-service.ts @@ -2,11 +2,10 @@ * Utility factory and interfaces for module service public facing API */ import { - Constructor, Context, FindConfig, IEventBusModuleService, - Pluralize, + ModuleJoinerConfig, RepositoryService, RestoreReturn, SoftDeleteReturn, @@ -22,17 +21,15 @@ import { } from "../common" import { InjectManager, MedusaContext } from "./decorators" import { ModuleRegistrationName } from "./definition" -import { DmlEntity } from "../dml" - -type BaseMethods = - | "retrieve" - | "list" - | "listAndCount" - | "delete" - | "softDelete" - | "restore" - | "create" - | "update" +import { + BaseMethods, + EntitiesConfigTemplate, + ExtractKeysFromConfig, + MedusaServiceReturnType, + ModelConfigurationsToConfigTemplate, + TEntityEntries, +} from "./types/medusa-service" +import { buildEntitiesNameToLinkableKeysMap } from "./joiner-config-builder" const readMethods = ["retrieve", "list", "listAndCount"] as BaseMethods[] const writeMethods = [ @@ -45,200 +42,6 @@ const writeMethods = [ const methods: BaseMethods[] = [...readMethods, ...writeMethods] -type ModelDTOConfig = { - dto: object - create?: any - update?: any - /** - * @internal - * @deprecated - */ - singular?: string - /** - * @internal - * @deprecated - */ - plural?: string -} - -type EntitiesConfigTemplate = { [key: string]: ModelDTOConfig } - -type ModelConfigurationsToConfigTemplate = { - [Key in keyof T as `${Capitalize}`]: { - dto: T[Key] extends Constructor ? InstanceType : any - create: any - update: any - singular: T[Key] extends { singular: string } ? T[Key]["singular"] : Key - plural: T[Key] extends { plural: string } - ? T[Key]["plural"] - : Pluralize - } -} - -/** - * @deprecated should all notion of singular and plural be removed once all modules are aligned with the convention - */ -type ExtractSingularName, K = keyof T> = Capitalize< - T[K] extends { singular?: string } ? T[K]["singular"] & string : K & string -> - -/** - * @deprecated should all notion of singular and plural be removed once all modules are aligned with the convention - * The pluralize will move to where it should be used instead - */ -type ExtractPluralName, K = keyof T> = Capitalize< - T[K] extends { - plural?: string - } - ? T[K]["plural"] & string - : Pluralize -> - -// TODO: The future expected entry will be a DML object but in the meantime we have to maintain backward compatibility for ouw own modules and therefore we need to support Constructor as well as this temporary object -type TEntityEntries = Record< - Keys & string, - | Constructor - | DmlEntity - | { name?: string; singular?: string; plural?: string } -> - -type ExtractKeysFromConfig = EntitiesConfig extends { - __empty: any -} - ? string - : keyof EntitiesConfig - -export type AbstractModuleService< - TEntitiesDtoConfig extends Record -> = { - [TEntityName in keyof TEntitiesDtoConfig as `retrieve${ExtractSingularName< - TEntitiesDtoConfig, - TEntityName - >}`]: ( - id: string, - config?: FindConfig, - sharedContext?: Context - ) => Promise -} & { - [TEntityName in keyof TEntitiesDtoConfig as `list${ExtractPluralName< - TEntitiesDtoConfig, - TEntityName - >}`]: ( - filters?: any, - config?: FindConfig, - sharedContext?: Context - ) => Promise -} & { - [TEntityName in keyof TEntitiesDtoConfig as `listAndCount${ExtractPluralName< - TEntitiesDtoConfig, - TEntityName - >}`]: { - (filters?: any, config?: FindConfig, sharedContext?: Context): Promise< - [TEntitiesDtoConfig[TEntityName]["dto"][], number] - > - } -} & { - [TEntityName in keyof TEntitiesDtoConfig as `delete${ExtractPluralName< - TEntitiesDtoConfig, - TEntityName - >}`]: { - ( - primaryKeyValues: string | object | string[] | object[], - sharedContext?: Context - ): Promise - } -} & { - [TEntityName in keyof TEntitiesDtoConfig as `softDelete${ExtractPluralName< - TEntitiesDtoConfig, - TEntityName - >}`]: { - ( - primaryKeyValues: string | object | string[] | object[], - config?: SoftDeleteReturn, - sharedContext?: Context - ): Promise | void> - } -} & { - [TEntityName in keyof TEntitiesDtoConfig as `restore${ExtractPluralName< - TEntitiesDtoConfig, - TEntityName - >}`]: { - ( - primaryKeyValues: string | object | string[] | object[], - config?: RestoreReturn, - sharedContext?: Context - ): Promise | void> - } -} & { - [TEntityName in keyof TEntitiesDtoConfig as `create${ExtractPluralName< - TEntitiesDtoConfig, - TEntityName - >}`]: { - (...args: any[]): Promise - } -} & { - [TEntityName in keyof TEntitiesDtoConfig as `update${ExtractPluralName< - TEntitiesDtoConfig, - TEntityName - >}`]: { - (...args: any[]): Promise - } -} - -// TODO: Because of a bug, those methods were not made visible which now cause issues with the fix as our interface are not consistent with the expectations - -// are not consistent accross modules -/* & { - [TEntityName in keyof TEntitiesDtoConfig as `create${ExtractPluralName< - TEntitiesDtoConfig, - TEntityName - >}`]: { - (data: any[], sharedContext?: Context): Promise< - TEntitiesDtoConfig[TEntityName]["dto"][] - > - } -} & { - [TEntityName in keyof TEntitiesDtoConfig as `create${ExtractPluralName< - TEntitiesDtoConfig, - TEntityName - >}`]: { - (data: any, sharedContext?: Context): Promise< - TEntitiesDtoConfig[TEntityName]["dto"][] - > - } -} & { - [TEntityName in keyof TEntitiesDtoConfig as `update${ExtractPluralName< - TEntitiesDtoConfig, - TEntityName - >}`]: { - ( - data: TEntitiesDtoConfig[TEntityName]["update"][], - sharedContext?: Context - ): Promise - } -} & { - [TEntityName in keyof TEntitiesDtoConfig as `update${ExtractPluralName< - TEntitiesDtoConfig, - TEntityName - >}`]: { - ( - data: TEntitiesDtoConfig[TEntityName]["update"], - sharedContext?: Context - ): Promise - } -} & { - [TEntityName in keyof TEntitiesDtoConfig as `update${ExtractPluralName< - TEntitiesDtoConfig, - TEntityName - >}`]: { - ( - idOrdSelector: any, - data: TEntitiesDtoConfig[TEntityName]["update"], - sharedContext?: Context - ): Promise - } -}*/ - /** * @internal */ @@ -263,6 +66,36 @@ function buildMethodNamesFromModel( }, {}) } +/** + * Accessible from the MedusaService, holds the model objects when provided + */ +export const MedusaServiceModelObjectsSymbol = Symbol.for( + "MedusaServiceModelObjectsSymbol" +) + +/** + * Symbol to mark a class as a Medusa service + */ +export const MedusaServiceSymbol = Symbol.for("MedusaServiceSymbol") + +/** + * Accessible from the MedusaService, holds the entity name to linkable keys map + * to be used for softDelete and restore methods + */ +export const MedusaServiceEntityNameToLinkableKeysMapSymbol = Symbol.for( + "MedusaServiceEntityNameToLinkableKeysMapSymbol" +) + +/** + * Check if a value is a Medusa service + * @param value + */ +export function isMedusaService( + value: any +): value is MedusaServiceReturnType { + return value && value?.prototype[MedusaServiceSymbol] +} + /** * Factory function for creating an abstract module service * @@ -281,26 +114,22 @@ function buildMethodNamesFromModel( * RuleType, * } * - * class MyService extends ModulesSdkUtils.MedusaService(entities, entityNameToLinkableKeysMap) {} + * class MyService extends ModulesSdkUtils.MedusaService(entities) {} * * @param entities - * @param entityNameToLinkableKeysMap */ export function MedusaService< - EntitiesConfig extends EntitiesConfigTemplate = { __empty: any }, - TEntities extends TEntityEntries< + const EntitiesConfig extends EntitiesConfigTemplate = { __empty: any }, + const TEntities extends TEntityEntries< ExtractKeysFromConfig > = TEntityEntries> >( - entities: TEntities, - entityNameToLinkableKeysMap: MapToConfig = {} -): { - new (...args: any[]): AbstractModuleService< - EntitiesConfig extends { __empty: any } - ? ModelConfigurationsToConfigTemplate - : EntitiesConfig - > -} { + entities: TEntities +): MedusaServiceReturnType< + EntitiesConfig extends { __empty: any } + ? ModelConfigurationsToConfigTemplate + : EntitiesConfig +> { const buildAndAssignMethodImpl = function ( klassPrototype: any, method: string, @@ -474,7 +303,7 @@ export function MedusaService< // eg: product.id = product_id, variant.id = variant_id const mappedCascadedEntitiesMap = mapObjectTo( cascadedEntitiesMap, - entityNameToLinkableKeysMap, + this[MedusaServiceEntityNameToLinkableKeysMapSymbol], { pick: config.returnLinkableKeys, } @@ -506,7 +335,7 @@ export function MedusaService< // eg: product.id = product_id, variant.id = variant_id mappedCascadedEntitiesMap = mapObjectTo( cascadedEntitiesMap, - entityNameToLinkableKeysMap, + this[MedusaServiceEntityNameToLinkableKeysMapSymbol], { pick: config.returnLinkableKeys, } @@ -522,11 +351,23 @@ export function MedusaService< } class AbstractModuleService_ { + [MedusaServiceSymbol] = true + + static [MedusaServiceModelObjectsSymbol] = Object.values( + entities + ) as unknown as MedusaServiceReturnType< + EntitiesConfig extends { __empty: any } + ? ModelConfigurationsToConfigTemplate + : EntitiesConfig + >["$modelObjects"]; + + [MedusaServiceEntityNameToLinkableKeysMapSymbol]: MapToConfig + readonly __container__: Record readonly baseRepository_: RepositoryService - readonly eventBusModuleService_: IEventBusModuleService; + readonly eventBusModuleService_: IEventBusModuleService - [key: string]: any + __joinerConfig?(): ModuleJoinerConfig constructor(container: Record) { this.__container__ = container @@ -539,6 +380,11 @@ export function MedusaService< this.eventBusModuleService_ = hasEventBusModuleService ? this.__container__.eventBusModuleService : undefined + + this[MedusaServiceEntityNameToLinkableKeysMapSymbol] = + buildEntitiesNameToLinkableKeysMap( + this.__joinerConfig?.()?.linkableKeys ?? {} + ) } protected async emitEvents_(groupedEvents) { @@ -563,7 +409,7 @@ export function MedusaService< string, TEntities[keyof TEntities], Record - ][] = Object.entries(entities).map(([name, config]) => [ + ][] = Object.entries(entities as {}).map(([name, config]) => [ name, config as TEntities[keyof TEntities], buildMethodNamesFromModel(name, config as TEntities[keyof TEntities]), diff --git a/packages/core/utils/src/modules-sdk/mikro-orm-cli-config-builder.ts b/packages/core/utils/src/modules-sdk/mikro-orm-cli-config-builder.ts index c5433e4f7c..3bfdb77716 100644 --- a/packages/core/utils/src/modules-sdk/mikro-orm-cli-config-builder.ts +++ b/packages/core/utils/src/modules-sdk/mikro-orm-cli-config-builder.ts @@ -14,7 +14,7 @@ type Options = Partial & { | EntityClass | EntityClassGroup | EntitySchema - | DmlEntity + | DmlEntity )[] databaseName: string } diff --git a/packages/core/utils/src/modules-sdk/module.ts b/packages/core/utils/src/modules-sdk/module.ts new file mode 100644 index 0000000000..c4ae408724 --- /dev/null +++ b/packages/core/utils/src/modules-sdk/module.ts @@ -0,0 +1,45 @@ +import { Constructor, ModuleExports } from "@medusajs/types" +import { MedusaServiceModelObjectsSymbol } from "./medusa-service" +import { + buildLinkConfigFromDmlObjects, + defineJoinerConfig, +} from "./joiner-config-builder" +import { InfersLinksConfig } from "./types/links-config" +import { DmlEntity } from "../dml" + +/** + * Wrapper to build the module export and auto generate the joiner config if needed as well as + * return a links object based on the DML objects + * @param moduleName + * @param service + * @param loaders + * @constructor + */ +export function Module< + const Service extends Constructor, + const ModelObjects extends DmlEntity[] = Service extends { + $modelObjects: infer $DmlObjects + } + ? $DmlObjects + : [], + Links = keyof ModelObjects extends never + ? Record + : InfersLinksConfig +>({ + name = "", + service, + loaders, +}: ModuleExports & { name?: string }): ModuleExports & { + links: Links +} { + service.prototype.__joinerConfig ??= defineJoinerConfig(name) + + const dmlObjects = service[MedusaServiceModelObjectsSymbol] + return { + service, + loaders, + links: (dmlObjects?.length + ? buildLinkConfigFromDmlObjects(dmlObjects) + : {}) as Links, + } +} diff --git a/packages/core/utils/src/modules-sdk/types/links-config.ts b/packages/core/utils/src/modules-sdk/types/links-config.ts new file mode 100644 index 0000000000..becbaeea0d --- /dev/null +++ b/packages/core/utils/src/modules-sdk/types/links-config.ts @@ -0,0 +1,131 @@ +import { + DMLSchema, + IDmlEntityConfig, + InferDmlEntityNameFromConfig, + SnakeCase, +} from "@medusajs/types" +import { DmlEntity } from "../../dml" +import { PrimaryKeyModifier } from "../../dml/properties/primary-key" + +type FlattenUnion = T extends { [K in keyof T]: infer U } + ? { [K in keyof T]: U } + : never + +type UnionToIntersection = (U extends any ? (k: U) => void : never) extends ( + k: infer I +) => void + ? I + : never + +type InferLinkableKeyName< + Key, + Property, + DmlConfig extends IDmlEntityConfig +> = Property extends PrimaryKeyModifier + ? `${Lowercase>>}_${Key & + string}` + : never + +type InferSchemaLinkableKeys = T extends DmlEntity< + infer Schema, + infer Config +> + ? { + [K in keyof Schema as Schema[K] extends PrimaryKeyModifier + ? InferLinkableKeyName + : never]: InferDmlEntityNameFromConfig + } + : {} + +type InferSchemasLinkableKeys[]> = { + [K in keyof T]: InferSchemaLinkableKeys +} + +type AggregateSchemasLinkableKeys[]> = { + [K in keyof InferSchemasLinkableKeys]: InferSchemasLinkableKeys[K] +} + +/** + * From an array of DmlEntity, returns a formatted object with the linkable keys + * + * @example: + * + * const user = model.define("user", { + * id: model.id(), + * name: model.text(), + * }) + * + * const car = model.define("car", { + * id: model.id(), + * number_plate: model.text().primaryKey(), + * test: model.text(), + * }) + * + * const linkableKeys = buildLinkableKeysFromDmlObjects([user, car]) // { user_id: 'user', car_number_plate: 'car' } + * + */ +export type InferLinkableKeys[]> = + UnionToIntersection>[0]> + +type InferPrimaryKeyNameOrNever< + Schema extends DMLSchema, + Key extends keyof Schema +> = Schema[Key] extends PrimaryKeyModifier ? Key : never + +type InferSchemaLinksConfig = T extends DmlEntity + ? { + [K in keyof Schema as Schema[K] extends PrimaryKeyModifier + ? InferPrimaryKeyNameOrNever + : never]: { + linkable: InferLinkableKeyName + primaryKey: K + } + } + : {} + +/** + * From an array of DmlEntity, returns a formatted object with the linkable keys + * + * @example: + * + * const user = model.define("user", { + * id: model.id(), + * name: model.text(), + * }) + * + * const car = model.define("car", { + * id: model.id(), + * number_plate: model.text().primaryKey(), + * test: model.text(), + * }) + * + * const linkConfig = buildLinkConfigFromDmlObjects([user, car]) + * // { + * // user: { + * // id: { + * // linkable: 'user_id', + * // primaryKey: 'id' + * // }, + * // toJSON() { ... } + * // }, + * // car: { + * // number_plate: { + * // linkable: 'car_number_plate', + * // primaryKey: 'number_plate' + * // }, + * // toJSON() { ... } + * // } + * // } + * + */ +export type InfersLinksConfig[]> = + UnionToIntersection<{ + [K in keyof T as T[K] extends DmlEntity + ? Uncapitalize> + : never]: InferSchemaLinksConfig & { + toJSON: () => { + linkable: string + primaryKey: string + } + } + }> diff --git a/packages/core/utils/src/modules-sdk/types/medusa-service.ts b/packages/core/utils/src/modules-sdk/types/medusa-service.ts new file mode 100644 index 0000000000..b65db0968c --- /dev/null +++ b/packages/core/utils/src/modules-sdk/types/medusa-service.ts @@ -0,0 +1,261 @@ +import { + Constructor, + Context, + FindConfig, + Pluralize, + RestoreReturn, + SoftDeleteReturn, +} from "@medusajs/types" +import { DmlEntity } from "../../dml" + +export type BaseMethods = + | "retrieve" + | "list" + | "listAndCount" + | "delete" + | "softDelete" + | "restore" + | "create" + | "update" + +export type ModelDTOConfig = { + dto: object + model?: DmlEntity + create?: any + update?: any + /** + * @internal + * @deprecated + */ + singular?: string + /** + * @internal + * @deprecated + */ + plural?: string +} + +export type EntitiesConfigTemplate = { [key: string]: ModelDTOConfig } + +export type ModelConfigurationsToConfigTemplate = { + [Key in keyof T]: { + dto: T[Key] extends Constructor ? InstanceType : any + model: T[Key] extends { model: infer MODEL } + ? MODEL + : T[Key] extends DmlEntity + ? T[Key] + : never + /** + * @deprecated + */ + create: any + update: any + /** + * @deprecated + */ + singular: T[Key] extends { singular: string } ? T[Key]["singular"] : Key + /** + * @deprecated + */ + plural: T[Key] extends { plural: string } + ? T[Key]["plural"] + : Pluralize + } +} + +/** + * @deprecated should all notion of singular and plural be removed once all modules are aligned with the convention + */ +export type ExtractSingularName< + T extends Record, + K = keyof T +> = Capitalize< + T[K] extends { singular?: string } ? T[K]["singular"] & string : K & string +> + +/** + * @deprecated should all notion of singular and plural be removed once all modules are aligned with the convention + * The pluralize will move to where it should be used instead + */ +export type ExtractPluralName< + T extends Record, + K = keyof T +> = Capitalize< + T[K] extends { + plural?: string + } + ? T[K]["plural"] & string + : Pluralize +> + +// TODO: The future expected entry will be a MODEL object but in the meantime we have to maintain backward compatibility for ouw own modules and therefore we need to support Constructor as well as this temporary object +export type TEntityEntries = Record< + Keys & string, + | DmlEntity + /** + * @deprecated + */ + | Constructor + /** + * @deprecated + */ + | { name?: string; singular?: string; plural?: string } +> + +export type ExtractKeysFromConfig = EntitiesConfig extends { + __empty: any +} + ? string + : keyof EntitiesConfig + +export type AbstractModuleService< + TEntitiesDtoConfig extends Record +> = { + [TEntityName in keyof TEntitiesDtoConfig as `retrieve${ExtractSingularName< + TEntitiesDtoConfig, + TEntityName + >}`]: ( + id: string, + config?: FindConfig, + sharedContext?: Context + ) => Promise +} & { + [TEntityName in keyof TEntitiesDtoConfig as `list${ExtractPluralName< + TEntitiesDtoConfig, + TEntityName + >}`]: ( + filters?: any, + config?: FindConfig, + sharedContext?: Context + ) => Promise +} & { + [TEntityName in keyof TEntitiesDtoConfig as `listAndCount${ExtractPluralName< + TEntitiesDtoConfig, + TEntityName + >}`]: { + (filters?: any, config?: FindConfig, sharedContext?: Context): Promise< + [TEntitiesDtoConfig[TEntityName]["dto"][], number] + > + } +} & { + [TEntityName in keyof TEntitiesDtoConfig as `delete${ExtractPluralName< + TEntitiesDtoConfig, + TEntityName + >}`]: { + ( + primaryKeyValues: string | object | string[] | object[], + sharedContext?: Context + ): Promise + } +} & { + [TEntityName in keyof TEntitiesDtoConfig as `softDelete${ExtractPluralName< + TEntitiesDtoConfig, + TEntityName + >}`]: { + ( + primaryKeyValues: string | object | string[] | object[], + config?: SoftDeleteReturn, + sharedContext?: Context + ): Promise | void> + } +} & { + [TEntityName in keyof TEntitiesDtoConfig as `restore${ExtractPluralName< + TEntitiesDtoConfig, + TEntityName + >}`]: { + ( + primaryKeyValues: string | object | string[] | object[], + config?: RestoreReturn, + sharedContext?: Context + ): Promise | void> + } +} & { + [TEntityName in keyof TEntitiesDtoConfig as `create${ExtractPluralName< + TEntitiesDtoConfig, + TEntityName + >}`]: { + (...args: any[]): Promise + } +} & { + [TEntityName in keyof TEntitiesDtoConfig as `update${ExtractPluralName< + TEntitiesDtoConfig, + TEntityName + >}`]: { + (...args: any[]): Promise + } +} + +// TODO: Because of a bug, those methods were not made visible which now cause issues with the fix as our interface are not consistent with the expectations + +// are not consistent accross modules +/* & { + [TEntityName in keyof TEntitiesDtoConfig as `create${ExtractPluralName< + TEntitiesDtoConfig, + TEntityName + >}`]: { + (data: any[], sharedContext?: Context): Promise< + TEntitiesDtoConfig[TEntityName]["dto"][] + > + } +} & { + [TEntityName in keyof TEntitiesDtoConfig as `create${ExtractPluralName< + TEntitiesDtoConfig, + TEntityName + >}`]: { + (data: any, sharedContext?: Context): Promise< + TEntitiesDtoConfig[TEntityName]["dto"][] + > + } +} & { + [TEntityName in keyof TEntitiesDtoConfig as `update${ExtractPluralName< + TEntitiesDtoConfig, + TEntityName + >}`]: { + ( + data: TEntitiesDtoConfig[TEntityName]["update"][], + sharedContext?: Context + ): Promise + } +} & { + [TEntityName in keyof TEntitiesDtoConfig as `update${ExtractPluralName< + TEntitiesDtoConfig, + TEntityName + >}`]: { + ( + data: TEntitiesDtoConfig[TEntityName]["update"], + sharedContext?: Context + ): Promise + } +} & { + [TEntityName in keyof TEntitiesDtoConfig as `update${ExtractPluralName< + TEntitiesDtoConfig, + TEntityName + >}`]: { + ( + idOrdSelector: any, + data: TEntitiesDtoConfig[TEntityName]["update"], + sharedContext?: Context + ): Promise + } +}*/ + +type InferModelFromConfig = { + [K in keyof T as T[K] extends { model: any } + ? K + : K extends DmlEntity + ? K + : never]: T[K] extends { + model: infer MODEL + } + ? MODEL extends DmlEntity + ? MODEL + : never + : T[K] extends DmlEntity + ? T[K] + : never +} + +export type MedusaServiceReturnType> = { + new (...args: any[]): AbstractModuleService + $modelObjects: InferModelFromConfig[keyof InferModelFromConfig][] +} diff --git a/packages/modules/api-key/src/joiner-config.ts b/packages/modules/api-key/src/joiner-config.ts index 7f7cb55a6c..6b728a63b2 100644 --- a/packages/modules/api-key/src/joiner-config.ts +++ b/packages/modules/api-key/src/joiner-config.ts @@ -1,11 +1,3 @@ -import { - buildEntitiesNameToLinkableKeysMap, - defineJoinerConfig, - MapToConfig, - Modules, -} from "@medusajs/utils" +import { defineJoinerConfig, Modules } from "@medusajs/utils" export const joinerConfig = defineJoinerConfig(Modules.API_KEY) - -export const entityNameToLinkableKeysMap: MapToConfig = - buildEntitiesNameToLinkableKeysMap(joinerConfig.linkableKeys) diff --git a/packages/modules/api-key/src/services/api-key-module-service.ts b/packages/modules/api-key/src/services/api-key-module-service.ts index 15fb077286..b4a0ada6b8 100644 --- a/packages/modules/api-key/src/services/api-key-module-service.ts +++ b/packages/modules/api-key/src/services/api-key-module-service.ts @@ -11,7 +11,7 @@ import { ModuleJoinerConfig, ModulesSdkTypes, } from "@medusajs/types" -import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" +import { joinerConfig } from "../joiner-config" import { ApiKey } from "@models" import { CreateApiKeyDTO, @@ -41,7 +41,7 @@ type InjectedDependencies = { export default class ApiKeyModuleService extends MedusaService<{ ApiKey: { dto: ApiKeyTypes.ApiKeyDTO } - }>({ ApiKey }, entityNameToLinkableKeysMap) + }>({ ApiKey }) implements IApiKeyModuleService { protected baseRepository_: DAL.RepositoryService diff --git a/packages/modules/auth/src/joiner-config.ts b/packages/modules/auth/src/joiner-config.ts index b0947b225f..33a3481f29 100644 --- a/packages/modules/auth/src/joiner-config.ts +++ b/packages/modules/auth/src/joiner-config.ts @@ -1,14 +1,6 @@ import { AuthIdentity } from "@models" -import { - buildEntitiesNameToLinkableKeysMap, - defineJoinerConfig, - MapToConfig, - Modules, -} from "@medusajs/utils" +import { defineJoinerConfig, Modules } from "@medusajs/utils" export const joinerConfig = defineJoinerConfig(Modules.AUTH, { - entityQueryingConfig: [AuthIdentity], + models: [AuthIdentity], }) - -export const entityNameToLinkableKeysMap: MapToConfig = - buildEntitiesNameToLinkableKeysMap(joinerConfig.linkableKeys) diff --git a/packages/modules/auth/src/services/auth-module.ts b/packages/modules/auth/src/services/auth-module.ts index d8c3342159..2a754c64e5 100644 --- a/packages/modules/auth/src/services/auth-module.ts +++ b/packages/modules/auth/src/services/auth-module.ts @@ -12,7 +12,7 @@ import { import { AuthIdentity, ProviderIdentity } from "@models" -import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" +import { joinerConfig } from "../joiner-config" import { InjectManager, @@ -32,7 +32,7 @@ export default class AuthModuleService extends MedusaService<{ AuthIdentity: { dto: AuthTypes.AuthIdentityDTO } ProviderIdentity: { dto: AuthTypes.ProviderIdentityDTO } - }>({ AuthIdentity, ProviderIdentity }, entityNameToLinkableKeysMap) + }>({ AuthIdentity, ProviderIdentity }) implements AuthTypes.IAuthModuleService { protected baseRepository_: DAL.RepositoryService diff --git a/packages/modules/cart/src/joiner-config.ts b/packages/modules/cart/src/joiner-config.ts index b5c3118c6b..4296202f2c 100644 --- a/packages/modules/cart/src/joiner-config.ts +++ b/packages/modules/cart/src/joiner-config.ts @@ -1,11 +1,3 @@ -import { - buildEntitiesNameToLinkableKeysMap, - defineJoinerConfig, - MapToConfig, - Modules, -} from "@medusajs/utils" +import { defineJoinerConfig, Modules } from "@medusajs/utils" export const joinerConfig = defineJoinerConfig(Modules.CART) - -export const entityNameToLinkableKeysMap: MapToConfig = - buildEntitiesNameToLinkableKeysMap(joinerConfig.linkableKeys) diff --git a/packages/modules/cart/src/services/cart-module.ts b/packages/modules/cart/src/services/cart-module.ts index 3afc413245..0bfb360b35 100644 --- a/packages/modules/cart/src/services/cart-module.ts +++ b/packages/modules/cart/src/services/cart-module.ts @@ -39,7 +39,7 @@ import { UpdateLineItemDTO, UpdateShippingMethodTaxLineDTO, } from "@types" -import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" +import { joinerConfig } from "../joiner-config" type InjectedDependencies = { baseRepository: DAL.RepositoryService @@ -74,7 +74,7 @@ export default class CartModuleService ShippingMethod: { dto: CartTypes.CartShippingMethodDTO } ShippingMethodAdjustment: { dto: CartTypes.ShippingMethodAdjustmentDTO } ShippingMethodTaxLine: { dto: CartTypes.ShippingMethodTaxLineDTO } - }>(generateMethodForModels, entityNameToLinkableKeysMap) + }>(generateMethodForModels) implements ICartModuleService { protected baseRepository_: DAL.RepositoryService diff --git a/packages/modules/currency/src/index.ts b/packages/modules/currency/src/index.ts index 0e087b98b6..b3e6fe1043 100644 --- a/packages/modules/currency/src/index.ts +++ b/packages/modules/currency/src/index.ts @@ -1,13 +1,12 @@ import { CurrencyModuleService } from "@services" import initialDataLoader from "./loaders/initial-data" -import { ModuleExports } from "@medusajs/types" +import { Module, Modules } from "@medusajs/utils" const service = CurrencyModuleService const loaders = [initialDataLoader] -export const moduleDefinition: ModuleExports = { +export default Module({ + name: Modules.CURRENCY, service, loaders, -} - -export default moduleDefinition +}) diff --git a/packages/modules/currency/src/joiner-config.ts b/packages/modules/currency/src/joiner-config.ts index 21da320a91..fcdb31aa42 100644 --- a/packages/modules/currency/src/joiner-config.ts +++ b/packages/modules/currency/src/joiner-config.ts @@ -1,9 +1,4 @@ -import { - buildEntitiesNameToLinkableKeysMap, - defineJoinerConfig, - MapToConfig, - Modules, -} from "@medusajs/utils" +import { defineJoinerConfig, Modules } from "@medusajs/utils" export const joinerConfig = defineJoinerConfig(Modules.CURRENCY, { primaryKeys: ["code"], @@ -11,6 +6,3 @@ export const joinerConfig = defineJoinerConfig(Modules.CURRENCY, { currency_code: "currency", }, }) - -export const entityNameToLinkableKeysMap: MapToConfig = - buildEntitiesNameToLinkableKeysMap(joinerConfig.linkableKeys) diff --git a/packages/modules/currency/src/models/currency.ts b/packages/modules/currency/src/models/currency.ts index 6843504df9..41a5b02053 100644 --- a/packages/modules/currency/src/models/currency.ts +++ b/packages/modules/currency/src/models/currency.ts @@ -1,7 +1,7 @@ import { model } from "@medusajs/utils" export default model.define("currency", { - code: model.text().primaryKey().searchable(), + code: model.text().searchable().primaryKey(), symbol: model.text(), symbol_native: model.text(), name: model.text().searchable(), diff --git a/packages/modules/currency/src/services/currency-module-service.ts b/packages/modules/currency/src/services/currency-module-service.ts index 8e07dbaa73..45cd46b31c 100644 --- a/packages/modules/currency/src/services/currency-module-service.ts +++ b/packages/modules/currency/src/services/currency-module-service.ts @@ -13,7 +13,7 @@ import { import { MedusaService } from "@medusajs/utils" import { Currency } from "@models" -import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" +import { joinerConfig } from "../joiner-config" type InjectedDependencies = { baseRepository: DAL.RepositoryService @@ -22,8 +22,8 @@ type InjectedDependencies = { export default class CurrencyModuleService extends MedusaService<{ - Currency: { dto: CurrencyTypes.CurrencyDTO } - }>({ Currency }, entityNameToLinkableKeysMap) + Currency: { dto: CurrencyTypes.CurrencyDTO; model: typeof Currency } + }>({ Currency }) implements ICurrencyModuleService { protected baseRepository_: DAL.RepositoryService diff --git a/packages/modules/customer/src/joiner-config.ts b/packages/modules/customer/src/joiner-config.ts index 4c4797584c..449a6da89b 100644 --- a/packages/modules/customer/src/joiner-config.ts +++ b/packages/modules/customer/src/joiner-config.ts @@ -1,9 +1,4 @@ -import { - buildEntitiesNameToLinkableKeysMap, - defineJoinerConfig, - MapToConfig, - Modules, -} from "@medusajs/utils" +import { defineJoinerConfig, Modules } from "@medusajs/utils" export const joinerConfig = defineJoinerConfig(Modules.CUSTOMER, { alias: [ @@ -16,6 +11,3 @@ export const joinerConfig = defineJoinerConfig(Modules.CUSTOMER, { }, ], }) - -export const entityNameToLinkableKeysMap: MapToConfig = - buildEntitiesNameToLinkableKeysMap(joinerConfig.linkableKeys) diff --git a/packages/modules/customer/src/services/customer-module.ts b/packages/modules/customer/src/services/customer-module.ts index 70ef8e221f..c815edce82 100644 --- a/packages/modules/customer/src/services/customer-module.ts +++ b/packages/modules/customer/src/services/customer-module.ts @@ -26,7 +26,7 @@ import { CustomerGroup, CustomerGroupCustomer, } from "@models" -import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" +import { joinerConfig } from "../joiner-config" type InjectedDependencies = { baseRepository: DAL.RepositoryService @@ -42,10 +42,7 @@ export default class CustomerModuleService Customer: { dto: CustomerDTO } CustomerGroup: { dto: CustomerGroupDTO } CustomerGroupCustomer: { dto: CustomerGroupCustomerDTO } - }>( - { Address, Customer, CustomerGroup, CustomerGroupCustomer }, - entityNameToLinkableKeysMap - ) + }>({ Address, Customer, CustomerGroup, CustomerGroupCustomer }) implements ICustomerModuleService { protected baseRepository_: DAL.RepositoryService diff --git a/packages/modules/file/src/joiner-config.ts b/packages/modules/file/src/joiner-config.ts index 56558e8e82..67db8fbcee 100644 --- a/packages/modules/file/src/joiner-config.ts +++ b/packages/modules/file/src/joiner-config.ts @@ -1,13 +1,5 @@ -import { - buildEntitiesNameToLinkableKeysMap, - defineJoinerConfig, - MapToConfig, - Modules, -} from "@medusajs/utils" +import { defineJoinerConfig, Modules } from "@medusajs/utils" export const joinerConfig = defineJoinerConfig(Modules.FILE, { - entityQueryingConfig: [{ name: "File" }], + models: [{ name: "File" }], }) - -export const entityNameToLinkableKeysMap: MapToConfig = - buildEntitiesNameToLinkableKeysMap({}) diff --git a/packages/modules/fulfillment/src/joiner-config.ts b/packages/modules/fulfillment/src/joiner-config.ts index aeae17c335..a92b6a2263 100644 --- a/packages/modules/fulfillment/src/joiner-config.ts +++ b/packages/modules/fulfillment/src/joiner-config.ts @@ -1,9 +1,4 @@ -import { - buildEntitiesNameToLinkableKeysMap, - defineJoinerConfig, - MapToConfig, - Modules, -} from "@medusajs/utils" +import { defineJoinerConfig, Modules } from "@medusajs/utils" import { Fulfillment, FulfillmentSet, @@ -19,6 +14,3 @@ export const joinerConfig = defineJoinerConfig(Modules.FULFILLMENT, { shipping_option_rule_id: ShippingOptionRule.name, }, }) - -export const entityNameToLinkableKeysMap: MapToConfig = - buildEntitiesNameToLinkableKeysMap(joinerConfig.linkableKeys) diff --git a/packages/modules/fulfillment/src/services/fulfillment-module-service.ts b/packages/modules/fulfillment/src/services/fulfillment-module-service.ts index dda983e782..799a6cd671 100644 --- a/packages/modules/fulfillment/src/services/fulfillment-module-service.ts +++ b/packages/modules/fulfillment/src/services/fulfillment-module-service.ts @@ -47,7 +47,7 @@ import { isContextValid, validateAndNormalizeRules, } from "@utils" -import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" +import { joinerConfig } from "../joiner-config" import { UpdateShippingOptionsInput } from "../types/service" import { buildCreatedShippingOptionEvents } from "../utils/events" import FulfillmentProviderService from "./fulfillment-provider" @@ -87,7 +87,7 @@ export default class FulfillmentModuleService ShippingOptionRule: { dto: FulfillmentTypes.ShippingOptionRuleDTO } ShippingOptionType: { dto: FulfillmentTypes.ShippingOptionTypeDTO } FulfillmentProvider: { dto: FulfillmentTypes.FulfillmentProviderDTO } - }>(generateMethodForModels, entityNameToLinkableKeysMap) + }>(generateMethodForModels) implements IFulfillmentModuleService { protected baseRepository_: DAL.RepositoryService diff --git a/packages/modules/inventory-next/src/joiner-config.ts b/packages/modules/inventory-next/src/joiner-config.ts index 82ffa206db..43636ded89 100644 --- a/packages/modules/inventory-next/src/joiner-config.ts +++ b/packages/modules/inventory-next/src/joiner-config.ts @@ -1,9 +1,4 @@ -import { - buildEntitiesNameToLinkableKeysMap, - defineJoinerConfig, - MapToConfig, - Modules, -} from "@medusajs/utils" +import { defineJoinerConfig, Modules } from "@medusajs/utils" export const joinerConfig = defineJoinerConfig(Modules.INVENTORY, { alias: [ @@ -35,6 +30,3 @@ export const joinerConfig = defineJoinerConfig(Modules.INVENTORY, { }, ], }) - -export const entityNameToLinkableKeysMap: MapToConfig = - buildEntitiesNameToLinkableKeysMap(joinerConfig.linkableKeys) diff --git a/packages/modules/inventory-next/src/services/inventory-level.ts b/packages/modules/inventory-next/src/services/inventory-level.ts index 99fbf7b1f1..c39ced5fc2 100644 --- a/packages/modules/inventory-next/src/services/inventory-level.ts +++ b/packages/modules/inventory-next/src/services/inventory-level.ts @@ -8,9 +8,10 @@ type InjectedDependencies = { inventoryLevelRepository: InventoryLevelRepository } -export default class InventoryLevelService extends ModulesSdkUtils.MedusaInternalService( +export default class InventoryLevelService extends ModulesSdkUtils.MedusaInternalService< + InjectedDependencies, InventoryLevel -) { +>(InventoryLevel) { protected readonly inventoryLevelRepository: InventoryLevelRepository constructor(container: InjectedDependencies) { diff --git a/packages/modules/inventory-next/src/services/inventory-module.ts b/packages/modules/inventory-next/src/services/inventory-module.ts index 8ac858a443..65452e760c 100644 --- a/packages/modules/inventory-next/src/services/inventory-module.ts +++ b/packages/modules/inventory-next/src/services/inventory-module.ts @@ -11,22 +11,22 @@ import { } from "@medusajs/types" import { IInventoryService } from "@medusajs/types/dist/inventory" import { + arrayDifference, CommonEvents, EmitEvents, InjectManager, InjectTransactionManager, InventoryEvents, + isDefined, + isString, MedusaContext, MedusaError, MedusaService, - arrayDifference, - isDefined, - isString, partitionArray, promiseAll, } from "@medusajs/utils" import { InventoryItem, InventoryLevel, ReservationItem } from "@models" -import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" +import { joinerConfig } from "../joiner-config" import InventoryLevelService from "./inventory-level" type InjectedDependencies = { @@ -47,14 +47,11 @@ export default class InventoryModuleService ReservationItem: { dto: InventoryTypes.ReservationItemDTO } - }>( - { - InventoryItem, - InventoryLevel, - ReservationItem, - }, - entityNameToLinkableKeysMap - ) + }>({ + InventoryItem, + InventoryLevel, + ReservationItem, + }) implements IInventoryService { protected baseRepository_: DAL.RepositoryService diff --git a/packages/modules/notification/src/joiner-config.ts b/packages/modules/notification/src/joiner-config.ts index 709cd9fc05..ab264acb92 100644 --- a/packages/modules/notification/src/joiner-config.ts +++ b/packages/modules/notification/src/joiner-config.ts @@ -1,13 +1,5 @@ -import { - buildEntitiesNameToLinkableKeysMap, - defineJoinerConfig, - MapToConfig, - Modules, -} from "@medusajs/utils" +import { defineJoinerConfig, Modules } from "@medusajs/utils" export const joinerConfig = defineJoinerConfig(Modules.NOTIFICATION, { - entityQueryingConfig: [{ name: "Notification" }], + models: [{ name: "Notification" }], }) - -export const entityNameToLinkableKeysMap: MapToConfig = - buildEntitiesNameToLinkableKeysMap(joinerConfig.linkableKeys) diff --git a/packages/modules/notification/src/services/notification-module-service.ts b/packages/modules/notification/src/services/notification-module-service.ts index 21b71a1dc6..ecf6215e4e 100644 --- a/packages/modules/notification/src/services/notification-module-service.ts +++ b/packages/modules/notification/src/services/notification-module-service.ts @@ -1,12 +1,12 @@ import { Context, DAL, + InferEntityType, INotificationModuleService, InternalModuleDeclaration, ModuleJoinerConfig, ModulesSdkTypes, NotificationTypes, - InferEntityType, } from "@medusajs/types" import { InjectManager, @@ -17,7 +17,7 @@ import { promiseAll, } from "@medusajs/utils" import { Notification } from "@models" -import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" +import { joinerConfig } from "../joiner-config" import NotificationProviderService from "./notification-provider" type InjectedDependencies = { @@ -31,7 +31,7 @@ type InjectedDependencies = { export default class NotificationModuleService extends MedusaService<{ Notification: { dto: NotificationTypes.NotificationDTO } - }>({ Notification }, entityNameToLinkableKeysMap) + }>({ Notification }) implements INotificationModuleService { protected baseRepository_: DAL.RepositoryService diff --git a/packages/modules/notification/src/services/notification-provider.ts b/packages/modules/notification/src/services/notification-provider.ts index 883a8093f0..9ce377e5b1 100644 --- a/packages/modules/notification/src/services/notification-provider.ts +++ b/packages/modules/notification/src/services/notification-provider.ts @@ -12,9 +12,10 @@ type InjectedDependencies = { ]: NotificationTypes.INotificationProvider } -export default class NotificationProviderService extends ModulesSdkUtils.MedusaInternalService( - NotificationProvider -) { +export default class NotificationProviderService extends ModulesSdkUtils.MedusaInternalService< + InjectedDependencies, + typeof NotificationProvider +>(NotificationProvider) { protected readonly notificationProviderRepository_: DAL.RepositoryService< InferEntityType > @@ -50,6 +51,8 @@ export default class NotificationProviderService extends ModulesSdkUtils.MedusaI ): Promise | undefined> { if (!this.providersCache) { const providers = await this.notificationProviderRepository_.find() + + type name = (typeof NotificationProvider)["name"] this.providersCache = new Map( providers.flatMap((provider) => provider.channels.map((c) => [c, provider]) diff --git a/packages/modules/order/src/joiner-config.ts b/packages/modules/order/src/joiner-config.ts index 5717f1d452..dda935f996 100644 --- a/packages/modules/order/src/joiner-config.ts +++ b/packages/modules/order/src/joiner-config.ts @@ -1,12 +1,4 @@ -import { - buildEntitiesNameToLinkableKeysMap, - defineJoinerConfig, - MapToConfig, - Modules, -} from "@medusajs/utils" +import { defineJoinerConfig, Modules } from "@medusajs/utils" // TODO: review configuration export const joinerConfig = defineJoinerConfig(Modules.ORDER) - -export const entityNameToLinkableKeysMap: MapToConfig = - buildEntitiesNameToLinkableKeysMap(joinerConfig.linkableKeys) diff --git a/packages/modules/order/src/services/order-change-service.ts b/packages/modules/order/src/services/order-change-service.ts index f8e49244d3..aea0a85005 100644 --- a/packages/modules/order/src/services/order-change-service.ts +++ b/packages/modules/order/src/services/order-change-service.ts @@ -20,9 +20,10 @@ type InjectedDependencies = { orderChangeRepository: DAL.RepositoryService } -export default class OrderChangeService extends ModulesSdkUtils.MedusaInternalService( +export default class OrderChangeService extends ModulesSdkUtils.MedusaInternalService< + InjectedDependencies, OrderChange -) { +>(OrderChange) { protected readonly orderChangeRepository_: RepositoryService constructor(container: InjectedDependencies) { diff --git a/packages/modules/order/src/services/order-module-service.ts b/packages/modules/order/src/services/order-module-service.ts index 28efc1c929..a7f0839b78 100644 --- a/packages/modules/order/src/services/order-module-service.ts +++ b/packages/modules/order/src/services/order-module-service.ts @@ -65,7 +65,7 @@ import { UpdateOrderLineItemTaxLineDTO, UpdateOrderShippingMethodTaxLineDTO, } from "@types" -import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" +import { joinerConfig } from "../joiner-config" import { applyChangesToOrder, ApplyOrderChangeDTO, @@ -165,7 +165,7 @@ export default class OrderModuleService< ReturnItem: { dto: any } // TODO: Add return item dto OrderClaim: { dto: any } // TODO: Add claim dto OrderExchange: { dto: any } // TODO: Add exchange dto - }>(generateMethodForModels, entityNameToLinkableKeysMap) + }>(generateMethodForModels) implements IOrderModuleService { protected baseRepository_: DAL.RepositoryService diff --git a/packages/modules/order/src/services/order-service.ts b/packages/modules/order/src/services/order-service.ts index c5c36c9460..35010daf37 100644 --- a/packages/modules/order/src/services/order-service.ts +++ b/packages/modules/order/src/services/order-service.ts @@ -17,9 +17,10 @@ type InjectedDependencies = { orderRepository: DAL.RepositoryService } -export default class OrderService extends ModulesSdkUtils.MedusaInternalService( +export default class OrderService extends ModulesSdkUtils.MedusaInternalService< + InjectedDependencies, Order -) { +>(Order) { protected readonly orderRepository_: RepositoryService constructor(container: InjectedDependencies) { diff --git a/packages/modules/payment/src/joiner-config.ts b/packages/modules/payment/src/joiner-config.ts index 0be93aec88..b8660e30cf 100644 --- a/packages/modules/payment/src/joiner-config.ts +++ b/packages/modules/payment/src/joiner-config.ts @@ -1,9 +1,4 @@ -import { - buildEntitiesNameToLinkableKeysMap, - defineJoinerConfig, - MapToConfig, - Modules, -} from "@medusajs/utils" +import { defineJoinerConfig, Modules } from "@medusajs/utils" import { Payment, PaymentCollection, @@ -12,18 +7,10 @@ import { } from "@models" export const joinerConfig = defineJoinerConfig(Modules.PAYMENT, { - entityQueryingConfig: [ - Payment, - PaymentCollection, - PaymentProvider, - PaymentSession, - ], + models: [Payment, PaymentCollection, PaymentProvider, PaymentSession], linkableKeys: { payment_id: Payment.name, payment_collection_id: PaymentCollection.name, payment_provider_id: PaymentProvider.name, }, }) - -export const entityNameToLinkableKeysMap: MapToConfig = - buildEntitiesNameToLinkableKeysMap(joinerConfig.linkableKeys) diff --git a/packages/modules/payment/src/services/payment-module.ts b/packages/modules/payment/src/services/payment-module.ts index b0861a212c..afa3411aa8 100644 --- a/packages/modules/payment/src/services/payment-module.ts +++ b/packages/modules/payment/src/services/payment-module.ts @@ -48,7 +48,7 @@ import { PaymentSession, Refund, } from "@models" -import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" +import { joinerConfig } from "../joiner-config" import PaymentProviderService from "./payment-provider" type InjectedDependencies = { @@ -76,7 +76,7 @@ export default class PaymentModuleService Payment: { dto: PaymentDTO } Capture: { dto: CaptureDTO } Refund: { dto: RefundDTO } - }>(generateMethodForModels, entityNameToLinkableKeysMap) + }>(generateMethodForModels) implements IPaymentModuleService { protected baseRepository_: DAL.RepositoryService diff --git a/packages/modules/pricing/src/joiner-config.ts b/packages/modules/pricing/src/joiner-config.ts index 296d01de18..99cc1d948c 100644 --- a/packages/modules/pricing/src/joiner-config.ts +++ b/packages/modules/pricing/src/joiner-config.ts @@ -1,19 +1,11 @@ -import { - buildEntitiesNameToLinkableKeysMap, - defineJoinerConfig, - MapToConfig, - Modules, -} from "@medusajs/utils" +import { defineJoinerConfig, Modules } from "@medusajs/utils" import { Price, PriceList, PriceSet } from "@models" export const joinerConfig = defineJoinerConfig(Modules.PRICING, { - entityQueryingConfig: [PriceSet, PriceList, Price], + models: [PriceSet, PriceList, Price], linkableKeys: { price_set_id: PriceSet.name, price_list_id: PriceList.name, price_id: Price.name, }, }) - -export const entityNameToLinkableKeysMap: MapToConfig = - buildEntitiesNameToLinkableKeysMap(joinerConfig.linkableKeys) diff --git a/packages/modules/pricing/src/services/pricing-module.ts b/packages/modules/pricing/src/services/pricing-module.ts index ec5dbf1b07..aec9b06501 100644 --- a/packages/modules/pricing/src/services/pricing-module.ts +++ b/packages/modules/pricing/src/services/pricing-module.ts @@ -38,7 +38,7 @@ import { Price, PriceList, PriceListRule, PriceRule, PriceSet } from "@models" import { ServiceTypes } from "@types" import { eventBuilders, validatePriceListDates } from "@utils" -import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" +import { joinerConfig } from "../joiner-config" import { CreatePriceListDTO, UpsertPriceDTO } from "src/types/services" type InjectedDependencies = { @@ -70,7 +70,7 @@ export default class PricingModuleService } PriceList: { dto: PricingTypes.PriceListDTO } PriceListRule: { dto: PricingTypes.PriceListRuleDTO } - }>(generateMethodForModels, entityNameToLinkableKeysMap) + }>(generateMethodForModels) implements PricingTypes.IPricingModuleService { protected baseRepository_: DAL.RepositoryService diff --git a/packages/modules/product/src/joiner-config.ts b/packages/modules/product/src/joiner-config.ts index 551439b74a..a483ae0bb1 100644 --- a/packages/modules/product/src/joiner-config.ts +++ b/packages/modules/product/src/joiner-config.ts @@ -1,11 +1,7 @@ -import { - buildEntitiesNameToLinkableKeysMap, - defineJoinerConfig, - MapToConfig, - Modules, -} from "@medusajs/utils" +import { defineJoinerConfig, Modules } from "@medusajs/utils" export const joinerConfig = defineJoinerConfig(Modules.PRODUCT, { + primaryKeys: ["id", "handle"], // This module provides more alias than the default config builder alias: [ { @@ -58,6 +54,3 @@ export const joinerConfig = defineJoinerConfig(Modules.PRODUCT, { }, ], }) - -export const entityNameToLinkableKeysMap: MapToConfig = - buildEntitiesNameToLinkableKeysMap(joinerConfig.linkableKeys) diff --git a/packages/modules/product/src/services/product-module-service.ts b/packages/modules/product/src/services/product-module-service.ts index f7acad89cf..6e37829849 100644 --- a/packages/modules/product/src/services/product-module-service.ts +++ b/packages/modules/product/src/services/product-module-service.ts @@ -51,7 +51,7 @@ import { UpdateTypeInput, } from "../types" import { eventBuilders } from "../utils" -import { entityNameToLinkableKeysMap, joinerConfig } from "./../joiner-config" +import { joinerConfig } from "./../joiner-config" type InjectedDependencies = { baseRepository: DAL.RepositoryService @@ -90,18 +90,15 @@ export default class ProductModuleService ProductVariant: { dto: ProductTypes.ProductVariantDTO } - }>( - { - Product, - ProductCategory, - ProductCollection, - ProductOption, - ProductTag, - ProductType, - ProductVariant, - }, - entityNameToLinkableKeysMap - ) + }>({ + Product, + ProductCategory, + ProductCollection, + ProductOption, + ProductTag, + ProductType, + ProductVariant, + }) implements ProductTypes.IProductModuleService { protected baseRepository_: DAL.RepositoryService diff --git a/packages/modules/product/src/services/product.ts b/packages/modules/product/src/services/product.ts index 1470f21828..e6cedb94d6 100644 --- a/packages/modules/product/src/services/product.ts +++ b/packages/modules/product/src/services/product.ts @@ -18,9 +18,10 @@ type NormalizedFilterableProductProps = ProductTypes.FilterableProductProps & { } } -export default class ProductService extends ModulesSdkUtils.MedusaInternalService( +export default class ProductService extends ModulesSdkUtils.MedusaInternalService< + InjectedDependencies, Product -) { +>(Product) { protected readonly productRepository_: DAL.RepositoryService constructor({ productRepository }: InjectedDependencies) { diff --git a/packages/modules/promotion/src/joiner-config.ts b/packages/modules/promotion/src/joiner-config.ts index c3373592d4..e643dea2a6 100644 --- a/packages/modules/promotion/src/joiner-config.ts +++ b/packages/modules/promotion/src/joiner-config.ts @@ -1,19 +1,11 @@ -import { - buildEntitiesNameToLinkableKeysMap, - defineJoinerConfig, - MapToConfig, - Modules, -} from "@medusajs/utils" +import { defineJoinerConfig, Modules } from "@medusajs/utils" import { Campaign, Promotion, PromotionRule } from "@models" export const joinerConfig = defineJoinerConfig(Modules.PROMOTION, { - entityQueryingConfig: [Promotion, Campaign, PromotionRule], + models: [Promotion, Campaign, PromotionRule], linkableKeys: { promotion_id: Promotion.name, campaign_id: Campaign.name, promotion_rule_id: PromotionRule.name, }, }) - -export const entityNameToLinkableKeysMap: MapToConfig = - buildEntitiesNameToLinkableKeysMap(joinerConfig.linkableKeys) diff --git a/packages/modules/promotion/src/services/promotion-module.ts b/packages/modules/promotion/src/services/promotion-module.ts index f7fa40efa7..bf4cbeee6e 100644 --- a/packages/modules/promotion/src/services/promotion-module.ts +++ b/packages/modules/promotion/src/services/promotion-module.ts @@ -23,7 +23,6 @@ import { MedusaContext, MedusaError, MedusaService, - ModulesSdkUtils, PromotionType, transformPropertiesToBigNumber, } from "@medusajs/utils" @@ -54,7 +53,7 @@ import { validateApplicationMethodAttributes, validatePromotionRuleAttributes, } from "@utils" -import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" +import { joinerConfig } from "../joiner-config" import { CreatePromotionRuleValueDTO } from "../types/promotion-rule-value" type InjectedDependencies = { @@ -75,17 +74,14 @@ export default class PromotionModuleService CampaignBudget: { dto: PromotionTypes.CampaignBudgetDTO } PromotionRule: { dto: PromotionTypes.PromotionRuleDTO } PromotionRuleValue: { dto: PromotionTypes.PromotionRuleValueDTO } - }>( - { - Promotion, - ApplicationMethod, - Campaign, - CampaignBudget, - PromotionRule, - PromotionRuleValue, - }, - entityNameToLinkableKeysMap - ) + }>({ + Promotion, + ApplicationMethod, + Campaign, + CampaignBudget, + PromotionRule, + PromotionRuleValue, + }) implements PromotionTypes.IPromotionModuleService { protected baseRepository_: DAL.RepositoryService diff --git a/packages/modules/region/src/joiner-config.ts b/packages/modules/region/src/joiner-config.ts index 4291e972c0..c1864385f5 100644 --- a/packages/modules/region/src/joiner-config.ts +++ b/packages/modules/region/src/joiner-config.ts @@ -1,11 +1,3 @@ -import { - buildEntitiesNameToLinkableKeysMap, - defineJoinerConfig, - MapToConfig, - Modules, -} from "@medusajs/utils" +import { defineJoinerConfig, Modules } from "@medusajs/utils" export const joinerConfig = defineJoinerConfig(Modules.REGION) - -export const entityNameToLinkableKeysMap: MapToConfig = - buildEntitiesNameToLinkableKeysMap(joinerConfig.linkableKeys) diff --git a/packages/modules/region/src/services/region-module.ts b/packages/modules/region/src/services/region-module.ts index 34812b9d39..4f0dc1d791 100644 --- a/packages/modules/region/src/services/region-module.ts +++ b/packages/modules/region/src/services/region-module.ts @@ -3,9 +3,9 @@ import { CreateRegionDTO, DAL, FilterableRegionProps, - IRegionModuleService, InferEntityType, InternalModuleDeclaration, + IRegionModuleService, ModuleJoinerConfig, ModulesSdkTypes, RegionCountryDTO, @@ -15,20 +15,20 @@ import { UpsertRegionDTO, } from "@medusajs/types" import { + arrayDifference, + getDuplicates, InjectManager, InjectTransactionManager, + isString, MedusaContext, MedusaError, MedusaService, - arrayDifference, - getDuplicates, - isString, promiseAll, removeUndefined, } from "@medusajs/utils" -import { RegionCountry as Country, Region } from "@models" +import { Region, RegionCountry as Country } from "@models" import { UpdateRegionInput } from "@types" -import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" +import { joinerConfig } from "../joiner-config" type InjectedDependencies = { baseRepository: DAL.RepositoryService @@ -44,7 +44,7 @@ export default class RegionModuleService Country: { dto: RegionCountryDTO } - }>({ Region, Country }, entityNameToLinkableKeysMap) + }>({ Region, Country }) implements IRegionModuleService { protected baseRepository_: DAL.RepositoryService diff --git a/packages/modules/sales-channel/src/joiner-config.ts b/packages/modules/sales-channel/src/joiner-config.ts index 26250f9174..eed6579ddc 100644 --- a/packages/modules/sales-channel/src/joiner-config.ts +++ b/packages/modules/sales-channel/src/joiner-config.ts @@ -1,11 +1,3 @@ -import { - buildEntitiesNameToLinkableKeysMap, - defineJoinerConfig, - MapToConfig, - Modules, -} from "@medusajs/utils" +import { defineJoinerConfig, Modules } from "@medusajs/utils" export const joinerConfig = defineJoinerConfig(Modules.SALES_CHANNEL) - -export const entityNameToLinkableKeysMap: MapToConfig = - buildEntitiesNameToLinkableKeysMap(joinerConfig.linkableKeys) diff --git a/packages/modules/sales-channel/src/services/sales-channel-module.ts b/packages/modules/sales-channel/src/services/sales-channel-module.ts index 45debc2507..932e1c1ffb 100644 --- a/packages/modules/sales-channel/src/services/sales-channel-module.ts +++ b/packages/modules/sales-channel/src/services/sales-channel-module.ts @@ -9,6 +9,7 @@ import { ModulesSdkTypes, SalesChannelDTO, UpdateSalesChannelDTO, + UpsertSalesChannelDTO, } from "@medusajs/types" import { InjectManager, @@ -20,10 +21,8 @@ import { } from "@medusajs/utils" import { SalesChannel } from "@models" - -import { UpsertSalesChannelDTO } from "@medusajs/types" import { UpdateSalesChanneInput } from "@types" -import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" +import { joinerConfig } from "../joiner-config" type InjectedDependencies = { baseRepository: DAL.RepositoryService @@ -31,10 +30,9 @@ type InjectedDependencies = { } export default class SalesChannelModuleService - extends MedusaService<{ SalesChannel: { dto: SalesChannelDTO } }>( - { SalesChannel }, - entityNameToLinkableKeysMap - ) + extends MedusaService<{ SalesChannel: { dto: SalesChannelDTO } }>({ + SalesChannel, + }) implements ISalesChannelModuleService { protected baseRepository_: DAL.RepositoryService diff --git a/packages/modules/stock-location-next/src/joiner-config.ts b/packages/modules/stock-location-next/src/joiner-config.ts index b073aecfe2..88b7c1403b 100644 --- a/packages/modules/stock-location-next/src/joiner-config.ts +++ b/packages/modules/stock-location-next/src/joiner-config.ts @@ -1,9 +1,4 @@ -import { - buildEntitiesNameToLinkableKeysMap, - defineJoinerConfig, - MapToConfig, - Modules, -} from "@medusajs/utils" +import { defineJoinerConfig, Modules } from "@medusajs/utils" import { StockLocation } from "./models" export const joinerConfig = defineJoinerConfig(Modules.STOCK_LOCATION, { @@ -12,6 +7,3 @@ export const joinerConfig = defineJoinerConfig(Modules.STOCK_LOCATION, { location_id: StockLocation.name, }, }) - -export const entityNameToLinkableKeysMap: MapToConfig = - buildEntitiesNameToLinkableKeysMap(joinerConfig.linkableKeys) diff --git a/packages/modules/stock-location-next/src/services/stock-location-module.ts b/packages/modules/stock-location-next/src/services/stock-location-module.ts index 45e3706145..e17175b2f7 100644 --- a/packages/modules/stock-location-next/src/services/stock-location-module.ts +++ b/packages/modules/stock-location-next/src/services/stock-location-module.ts @@ -21,7 +21,7 @@ import { MedusaService, promiseAll, } from "@medusajs/utils" -import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" +import { joinerConfig } from "../joiner-config" import { StockLocation, StockLocationAddress } from "../models" type InjectedDependencies = { @@ -38,7 +38,7 @@ export default class StockLocationModuleService extends MedusaService<{ StockLocation: { dto: StockLocationTypes.StockLocationDTO } StockLocationAddress: { dto: StockLocationTypes.StockLocationAddressDTO } - }>({ StockLocation, StockLocationAddress }, entityNameToLinkableKeysMap) + }>({ StockLocation, StockLocationAddress }) implements IStockLocationService { protected readonly eventBusModuleService_: IEventBusService diff --git a/packages/modules/store/src/joiner-config.ts b/packages/modules/store/src/joiner-config.ts index 4f99f9f39f..9ebf3b6852 100644 --- a/packages/modules/store/src/joiner-config.ts +++ b/packages/modules/store/src/joiner-config.ts @@ -1,11 +1,3 @@ -import { - buildEntitiesNameToLinkableKeysMap, - defineJoinerConfig, - MapToConfig, - Modules, -} from "@medusajs/utils" +import { defineJoinerConfig, Modules } from "@medusajs/utils" export const joinerConfig = defineJoinerConfig(Modules.STORE) - -export const entityNameToLinkableKeysMap: MapToConfig = - buildEntitiesNameToLinkableKeysMap(joinerConfig.linkableKeys) diff --git a/packages/modules/store/src/services/store-module-service.ts b/packages/modules/store/src/services/store-module-service.ts index 61a3eb5174..950b097895 100644 --- a/packages/modules/store/src/services/store-module-service.ts +++ b/packages/modules/store/src/services/store-module-service.ts @@ -20,7 +20,7 @@ import { } from "@medusajs/utils" import { Store, StoreCurrency } from "@models" -import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" +import { joinerConfig } from "../joiner-config" import { UpdateStoreInput } from "@types" type InjectedDependencies = { @@ -32,7 +32,7 @@ export default class StoreModuleService extends MedusaService<{ Store: { dto: StoreTypes.StoreDTO } StoreCurrency: { dto: StoreTypes.StoreCurrencyDTO } - }>({ Store, StoreCurrency }, entityNameToLinkableKeysMap) + }>({ Store, StoreCurrency }) implements IStoreModuleService { protected baseRepository_: DAL.RepositoryService diff --git a/packages/modules/tax/src/joiner-config.ts b/packages/modules/tax/src/joiner-config.ts index 8b885e986b..3cff9fa261 100644 --- a/packages/modules/tax/src/joiner-config.ts +++ b/packages/modules/tax/src/joiner-config.ts @@ -1,11 +1,3 @@ -import { - buildEntitiesNameToLinkableKeysMap, - defineJoinerConfig, - MapToConfig, - Modules, -} from "@medusajs/utils" +import { defineJoinerConfig, Modules } from "@medusajs/utils" export const joinerConfig = defineJoinerConfig(Modules.TAX) - -export const entityNameToLinkableKeysMap: MapToConfig = - buildEntitiesNameToLinkableKeysMap(joinerConfig.linkableKeys) diff --git a/packages/modules/tax/src/services/tax-module-service.ts b/packages/modules/tax/src/services/tax-module-service.ts index 91057882d6..7c7a621404 100644 --- a/packages/modules/tax/src/services/tax-module-service.ts +++ b/packages/modules/tax/src/services/tax-module-service.ts @@ -20,7 +20,7 @@ import { promiseAll, } from "@medusajs/utils" import { TaxProvider, TaxRate, TaxRateRule, TaxRegion } from "@models" -import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" +import { joinerConfig } from "../joiner-config" type InjectedDependencies = { baseRepository: DAL.RepositoryService @@ -44,7 +44,7 @@ export default class TaxModuleService TaxRegion: { dto: TaxTypes.TaxRegionDTO } TaxRateRule: { dto: TaxTypes.TaxRateRuleDTO } TaxProvider: { dto: TaxTypes.TaxProviderDTO } - }>(generateForModels, entityNameToLinkableKeysMap) + }>(generateForModels) implements ITaxModuleService { protected readonly container_: InjectedDependencies diff --git a/packages/modules/user/src/joiner-config.ts b/packages/modules/user/src/joiner-config.ts index 6d4f655656..5d30938137 100644 --- a/packages/modules/user/src/joiner-config.ts +++ b/packages/modules/user/src/joiner-config.ts @@ -1,11 +1,3 @@ -import { - buildEntitiesNameToLinkableKeysMap, - defineJoinerConfig, - MapToConfig, - Modules, -} from "@medusajs/utils" +import { defineJoinerConfig, Modules } from "@medusajs/utils" export const joinerConfig = defineJoinerConfig(Modules.USER) - -export const entityNameToLinkableKeysMap: MapToConfig = - buildEntitiesNameToLinkableKeysMap(joinerConfig.linkableKeys) diff --git a/packages/modules/user/src/services/user-module.ts b/packages/modules/user/src/services/user-module.ts index b1a98cb811..5e16a30618 100644 --- a/packages/modules/user/src/services/user-module.ts +++ b/packages/modules/user/src/services/user-module.ts @@ -19,7 +19,7 @@ import { UserEvents, } from "@medusajs/utils" import jwt, { JwtPayload } from "jsonwebtoken" -import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" +import { joinerConfig } from "../joiner-config" import crypto from "node:crypto" import { Invite, User } from "@models" @@ -41,7 +41,7 @@ export default class UserModuleService Invite: { dto: UserTypes.InviteDTO } - }>({ User, Invite }, entityNameToLinkableKeysMap) + }>({ User, Invite }) implements UserTypes.IUserModuleService { __joinerConfig(): ModuleJoinerConfig { diff --git a/packages/modules/workflow-engine-inmemory/src/joiner-config.ts b/packages/modules/workflow-engine-inmemory/src/joiner-config.ts index 585b9fa4f0..9e9a51f661 100644 --- a/packages/modules/workflow-engine-inmemory/src/joiner-config.ts +++ b/packages/modules/workflow-engine-inmemory/src/joiner-config.ts @@ -1,11 +1,3 @@ -import { - buildEntitiesNameToLinkableKeysMap, - defineJoinerConfig, - MapToConfig, - Modules, -} from "@medusajs/utils" +import { defineJoinerConfig, Modules } from "@medusajs/utils" export const joinerConfig = defineJoinerConfig(Modules.WORKFLOW_ENGINE) - -export const entityNameToLinkableKeysMap: MapToConfig = - buildEntitiesNameToLinkableKeysMap(joinerConfig.linkableKeys) diff --git a/packages/modules/workflow-engine-inmemory/src/services/workflows-module.ts b/packages/modules/workflow-engine-inmemory/src/services/workflows-module.ts index e0b300dd5e..7623185a8d 100644 --- a/packages/modules/workflow-engine-inmemory/src/services/workflows-module.ts +++ b/packages/modules/workflow-engine-inmemory/src/services/workflows-module.ts @@ -17,7 +17,7 @@ import type { } from "@medusajs/workflows-sdk" import { WorkflowExecution } from "@models" import { WorkflowOrchestratorService } from "@services" -import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" +import { joinerConfig } from "../joiner-config" type InjectedDependencies = { baseRepository: DAL.RepositoryService @@ -29,7 +29,7 @@ export class WorkflowsModuleService< TWorkflowExecution extends WorkflowExecution = WorkflowExecution > extends ModulesSdkUtils.MedusaService<{ WorkflowExecution: { dto: WorkflowExecution } -}>({ WorkflowExecution }, entityNameToLinkableKeysMap) { +}>({ WorkflowExecution }) { protected baseRepository_: DAL.RepositoryService protected workflowExecutionService_: ModulesSdkTypes.IMedusaInternalService protected workflowOrchestratorService_: WorkflowOrchestratorService diff --git a/packages/modules/workflow-engine-redis/src/joiner-config.ts b/packages/modules/workflow-engine-redis/src/joiner-config.ts index 585b9fa4f0..9e9a51f661 100644 --- a/packages/modules/workflow-engine-redis/src/joiner-config.ts +++ b/packages/modules/workflow-engine-redis/src/joiner-config.ts @@ -1,11 +1,3 @@ -import { - buildEntitiesNameToLinkableKeysMap, - defineJoinerConfig, - MapToConfig, - Modules, -} from "@medusajs/utils" +import { defineJoinerConfig, Modules } from "@medusajs/utils" export const joinerConfig = defineJoinerConfig(Modules.WORKFLOW_ENGINE) - -export const entityNameToLinkableKeysMap: MapToConfig = - buildEntitiesNameToLinkableKeysMap(joinerConfig.linkableKeys) diff --git a/packages/modules/workflow-engine-redis/src/services/workflows-module.ts b/packages/modules/workflow-engine-redis/src/services/workflows-module.ts index 708a13fb3f..8fcb566fd0 100644 --- a/packages/modules/workflow-engine-redis/src/services/workflows-module.ts +++ b/packages/modules/workflow-engine-redis/src/services/workflows-module.ts @@ -17,7 +17,7 @@ import type { } from "@medusajs/workflows-sdk" import { WorkflowExecution } from "@models" import { WorkflowOrchestratorService } from "@services" -import { entityNameToLinkableKeysMap, joinerConfig } from "../joiner-config" +import { joinerConfig } from "../joiner-config" type InjectedDependencies = { baseRepository: DAL.RepositoryService @@ -30,7 +30,7 @@ export class WorkflowsModuleService< TWorkflowExecution extends WorkflowExecution = WorkflowExecution > extends ModulesSdkUtils.MedusaService<{ WorkflowExecution: { dto: WorkflowExecution } -}>({ WorkflowExecution }, entityNameToLinkableKeysMap) { +}>({ WorkflowExecution }) { protected baseRepository_: DAL.RepositoryService protected workflowExecutionService_: ModulesSdkTypes.IMedusaInternalService protected workflowOrchestratorService_: WorkflowOrchestratorService