chore: make module loaders DML aware and auto generate joiner config (#7781)

* chore: make module loaders DML aware and auto generate joiner config

* fixes and cleanup

* improve dml entity check

* add unit tests on load resources

* cleanup deps

* cleanup deps

* cleanup Modules

* finalise

* fix modules-sdk jest

* fix modules-sdk jest

* fix import

* fix import
This commit is contained in:
Adrien de Peretti authored and GitHub committed 2024-06-20 18:18:07 +02:00
1 parent 03924a4ff6
commit 937a632eb6
191 files changed
+1105 -613

No files matched your search

+278 -305
View File
@@ -1,5 +1,5 @@
import { ModuleDefinition } from "@medusajs/types"
import { upperCaseFirst } from "@medusajs/utils"
import { Modules, upperCaseFirst } from "@medusajs/utils"
import { MODULE_RESOURCE_TYPE, MODULE_SCOPE } from "./types"
export enum LinkModuleUtils {
@@ -7,34 +7,6 @@ export enum LinkModuleUtils {
REMOTE_LINK = "remoteLink",
}
// TODO: Remove this enum and use the one from @medusajs/utils
export enum Modules {
AUTH = "auth",
CACHE = "cacheService",
CART = "cart",
CUSTOMER = "customer",
EVENT_BUS = "eventBus",
INVENTORY = "inventoryService",
LINK = "linkModules",
PAYMENT = "payment",
PRICING = "pricingService",
PRODUCT = "productService",
PROMOTION = "promotion",
SALES_CHANNEL = "salesChannel",
TAX = "tax",
FULFILLMENT = "fulfillment",
STOCK_LOCATION = "stockLocationService",
USER = "user",
WORKFLOW_ENGINE = "workflows",
REGION = "region",
ORDER = "order",
API_KEY = "apiKey",
STORE = "store",
CURRENCY = "currency",
FILE = "file",
NOTIFICATION = "notification",
}
export enum ModuleRegistrationName {
AUTH = "authModuleService",
CACHE = "cacheService",
@@ -88,305 +60,306 @@ export const MODULE_PACKAGE_NAMES = {
[Modules.NOTIFICATION]: "@medusajs/notification",
}
export const ModulesDefinition: { [key: string | Modules]: ModuleDefinition } =
{
[Modules.EVENT_BUS]: {
key: Modules.EVENT_BUS,
registrationName: ModuleRegistrationName.EVENT_BUS,
defaultPackage: MODULE_PACKAGE_NAMES[Modules.EVENT_BUS],
label: upperCaseFirst(ModuleRegistrationName.EVENT_BUS),
isRequired: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
export const ModulesDefinition: {
[key: string]: ModuleDefinition
} = {
[Modules.EVENT_BUS]: {
key: Modules.EVENT_BUS,
registrationName: ModuleRegistrationName.EVENT_BUS,
defaultPackage: MODULE_PACKAGE_NAMES[Modules.EVENT_BUS],
label: upperCaseFirst(ModuleRegistrationName.EVENT_BUS),
isRequired: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
[Modules.STOCK_LOCATION]: {
key: Modules.STOCK_LOCATION,
registrationName: ModuleRegistrationName.STOCK_LOCATION,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.STOCK_LOCATION),
isRequired: false,
isQueryable: true,
dependencies: [ModuleRegistrationName.EVENT_BUS],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
},
[Modules.STOCK_LOCATION]: {
key: Modules.STOCK_LOCATION,
registrationName: ModuleRegistrationName.STOCK_LOCATION,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.STOCK_LOCATION),
isRequired: false,
isQueryable: true,
dependencies: [ModuleRegistrationName.EVENT_BUS],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
[Modules.INVENTORY]: {
key: Modules.INVENTORY,
registrationName: ModuleRegistrationName.INVENTORY,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.INVENTORY),
isRequired: false,
isQueryable: true,
dependencies: [ModuleRegistrationName.EVENT_BUS],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
},
[Modules.INVENTORY]: {
key: Modules.INVENTORY,
registrationName: ModuleRegistrationName.INVENTORY,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.INVENTORY),
isRequired: false,
isQueryable: true,
dependencies: [ModuleRegistrationName.EVENT_BUS],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
[Modules.CACHE]: {
key: Modules.CACHE,
registrationName: ModuleRegistrationName.CACHE,
defaultPackage: MODULE_PACKAGE_NAMES[Modules.CACHE],
label: upperCaseFirst(ModuleRegistrationName.CACHE),
isRequired: true,
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
},
[Modules.CACHE]: {
key: Modules.CACHE,
registrationName: ModuleRegistrationName.CACHE,
defaultPackage: MODULE_PACKAGE_NAMES[Modules.CACHE],
label: upperCaseFirst(ModuleRegistrationName.CACHE),
isRequired: true,
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
[Modules.PRODUCT]: {
key: Modules.PRODUCT,
registrationName: ModuleRegistrationName.PRODUCT,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.PRODUCT),
isRequired: false,
isQueryable: true,
dependencies: [ModuleRegistrationName.EVENT_BUS, "logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
},
[Modules.PRODUCT]: {
key: Modules.PRODUCT,
registrationName: ModuleRegistrationName.PRODUCT,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.PRODUCT),
isRequired: false,
isQueryable: true,
dependencies: [ModuleRegistrationName.EVENT_BUS, "logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
[Modules.PRICING]: {
key: Modules.PRICING,
registrationName: ModuleRegistrationName.PRICING,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.PRICING),
isRequired: false,
isQueryable: true,
dependencies: [ModuleRegistrationName.EVENT_BUS, "logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
},
[Modules.PRICING]: {
key: Modules.PRICING,
registrationName: ModuleRegistrationName.PRICING,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.PRICING),
isRequired: false,
isQueryable: true,
dependencies: [ModuleRegistrationName.EVENT_BUS, "logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
[Modules.PROMOTION]: {
key: Modules.PROMOTION,
registrationName: ModuleRegistrationName.PROMOTION,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.PROMOTION),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
},
[Modules.PROMOTION]: {
key: Modules.PROMOTION,
registrationName: ModuleRegistrationName.PROMOTION,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.PROMOTION),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
[Modules.AUTH]: {
key: Modules.AUTH,
registrationName: ModuleRegistrationName.AUTH,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.AUTH),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
},
[Modules.AUTH]: {
key: Modules.AUTH,
registrationName: ModuleRegistrationName.AUTH,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.AUTH),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
[Modules.WORKFLOW_ENGINE]: {
key: Modules.WORKFLOW_ENGINE,
registrationName: ModuleRegistrationName.WORKFLOW_ENGINE,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.WORKFLOW_ENGINE),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
},
[Modules.WORKFLOW_ENGINE]: {
key: Modules.WORKFLOW_ENGINE,
registrationName: ModuleRegistrationName.WORKFLOW_ENGINE,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.WORKFLOW_ENGINE),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
[Modules.SALES_CHANNEL]: {
key: Modules.SALES_CHANNEL,
registrationName: ModuleRegistrationName.SALES_CHANNEL,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.SALES_CHANNEL),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
},
[Modules.SALES_CHANNEL]: {
key: Modules.SALES_CHANNEL,
registrationName: ModuleRegistrationName.SALES_CHANNEL,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.SALES_CHANNEL),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
[Modules.FULFILLMENT]: {
key: Modules.FULFILLMENT,
registrationName: ModuleRegistrationName.FULFILLMENT,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.FULFILLMENT),
isRequired: false,
isQueryable: true,
dependencies: ["logger", ModuleRegistrationName.EVENT_BUS],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
},
[Modules.FULFILLMENT]: {
key: Modules.FULFILLMENT,
registrationName: ModuleRegistrationName.FULFILLMENT,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.FULFILLMENT),
isRequired: false,
isQueryable: true,
dependencies: ["logger", ModuleRegistrationName.EVENT_BUS],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
[Modules.CART]: {
key: Modules.CART,
registrationName: ModuleRegistrationName.CART,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.CART),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
},
[Modules.CART]: {
key: Modules.CART,
registrationName: ModuleRegistrationName.CART,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.CART),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
[Modules.CUSTOMER]: {
key: Modules.CUSTOMER,
registrationName: ModuleRegistrationName.CUSTOMER,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.CUSTOMER),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
},
[Modules.CUSTOMER]: {
key: Modules.CUSTOMER,
registrationName: ModuleRegistrationName.CUSTOMER,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.CUSTOMER),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
[Modules.PAYMENT]: {
key: Modules.PAYMENT,
registrationName: ModuleRegistrationName.PAYMENT,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.PAYMENT),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
},
[Modules.PAYMENT]: {
key: Modules.PAYMENT,
registrationName: ModuleRegistrationName.PAYMENT,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.PAYMENT),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
[Modules.USER]: {
key: Modules.USER,
registrationName: ModuleRegistrationName.USER,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.USER),
isRequired: false,
isQueryable: true,
dependencies: [ModuleRegistrationName.EVENT_BUS, "logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
},
[Modules.USER]: {
key: Modules.USER,
registrationName: ModuleRegistrationName.USER,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.USER),
isRequired: false,
isQueryable: true,
dependencies: [ModuleRegistrationName.EVENT_BUS, "logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
[Modules.REGION]: {
key: Modules.REGION,
registrationName: ModuleRegistrationName.REGION,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.REGION),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
},
[Modules.REGION]: {
key: Modules.REGION,
registrationName: ModuleRegistrationName.REGION,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.REGION),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
[Modules.ORDER]: {
key: Modules.ORDER,
registrationName: ModuleRegistrationName.ORDER,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.ORDER),
isRequired: false,
isQueryable: true,
dependencies: ["logger", ModuleRegistrationName.EVENT_BUS],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
},
[Modules.ORDER]: {
key: Modules.ORDER,
registrationName: ModuleRegistrationName.ORDER,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.ORDER),
isRequired: false,
isQueryable: true,
dependencies: ["logger", ModuleRegistrationName.EVENT_BUS],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
[Modules.TAX]: {
key: Modules.TAX,
registrationName: ModuleRegistrationName.TAX,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.TAX),
isRequired: false,
isQueryable: true,
dependencies: ["logger", ModuleRegistrationName.EVENT_BUS],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
},
[Modules.TAX]: {
key: Modules.TAX,
registrationName: ModuleRegistrationName.TAX,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.TAX),
isRequired: false,
isQueryable: true,
dependencies: ["logger", ModuleRegistrationName.EVENT_BUS],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
[Modules.API_KEY]: {
key: Modules.API_KEY,
registrationName: ModuleRegistrationName.API_KEY,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.API_KEY),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
},
[Modules.API_KEY]: {
key: Modules.API_KEY,
registrationName: ModuleRegistrationName.API_KEY,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.API_KEY),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
[Modules.STORE]: {
key: Modules.STORE,
registrationName: ModuleRegistrationName.STORE,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.STORE),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
},
[Modules.STORE]: {
key: Modules.STORE,
registrationName: ModuleRegistrationName.STORE,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.STORE),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
[Modules.CURRENCY]: {
key: Modules.CURRENCY,
registrationName: ModuleRegistrationName.CURRENCY,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.CURRENCY),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
},
[Modules.CURRENCY]: {
key: Modules.CURRENCY,
registrationName: ModuleRegistrationName.CURRENCY,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.CURRENCY),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
[Modules.FILE]: {
key: Modules.FILE,
registrationName: ModuleRegistrationName.FILE,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.FILE),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
},
[Modules.FILE]: {
key: Modules.FILE,
registrationName: ModuleRegistrationName.FILE,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.FILE),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
[Modules.NOTIFICATION]: {
key: Modules.NOTIFICATION,
registrationName: ModuleRegistrationName.NOTIFICATION,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.NOTIFICATION),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
},
[Modules.NOTIFICATION]: {
key: Modules.NOTIFICATION,
registrationName: ModuleRegistrationName.NOTIFICATION,
defaultPackage: false,
label: upperCaseFirst(ModuleRegistrationName.NOTIFICATION),
isRequired: false,
isQueryable: true,
dependencies: ["logger"],
defaultModuleDeclaration: {
scope: MODULE_SCOPE.INTERNAL,
resources: MODULE_RESOURCE_TYPE.SHARED,
},
}
},
}
export const MODULE_DEFINITIONS: ModuleDefinition[] =
Object.values(ModulesDefinition)
@@ -0,0 +1,4 @@
export * as ModuleWithDmlMixedWithoutJoinerConfigFixtures from "./module-with-dml-mixed-without-joiner-config"
export * as ModuleWithDmlWithoutJoinerConfigFixtures from "./module-with-dml-without-joiner-config"
export * as ModuleWithoutJoinerConfigFixtures from "./module-without-joiner-config"
export * as ModuleWithJoinerConfigFixtures from "./module-with-joiner-config"
@@ -0,0 +1,11 @@
import { ModuleExports } from "@medusajs/types"
import { ModuleService } from "./services/module-service"
const moduleExports: ModuleExports = {
service: ModuleService,
}
export * from "./models"
export * from "./services/module-service"
export default moduleExports
@@ -0,0 +1,5 @@
import { model } from "@medusajs/utils"
export const dmlEntity = model.define("dmlEntity", {
name: model.text(),
})
@@ -0,0 +1,7 @@
import { Entity, Property } from "@mikro-orm/core"
@Entity()
export class EntityModel {
@Property({ columnType: "int" })
id!: number
}
@@ -0,0 +1,2 @@
export * from "./entity"
export * from "./dml-entity"
@@ -0,0 +1,3 @@
import { IModuleService } from "@medusajs/types"
export class ModuleService implements IModuleService {}
@@ -0,0 +1,11 @@
import { ModuleExports } from "@medusajs/types"
import { ModuleService } from "./services/module-service"
const moduleExports: ModuleExports = {
service: ModuleService,
}
export * from "./models"
export * from "./services/module-service"
export default moduleExports
@@ -0,0 +1,5 @@
import { model } from "@medusajs/utils"
export const entityModel = model.define("entityModel", {
name: model.text(),
})
@@ -0,0 +1,5 @@
import { model } from "@medusajs/utils"
export const dmlEntity = model.define("dmlEntity", {
name: model.text(),
})
@@ -0,0 +1,2 @@
export * from "./dml-entity"
export * from "./dml-entity-model"
@@ -0,0 +1,3 @@
import { IModuleService } from "@medusajs/types"
export class ModuleService implements IModuleService {}
@@ -0,0 +1,11 @@
import { ModuleExports } from "@medusajs/types"
import { ModuleService } from "./services/module-service"
const moduleExports: ModuleExports = {
service: ModuleService,
}
export * from "./models"
export * from "./services/module-service"
export default moduleExports
@@ -0,0 +1,7 @@
import { Entity, Property } from "@mikro-orm/core"
@Entity()
export class Entity2 {
@Property({ columnType: "int" })
id!: number
}
@@ -0,0 +1,7 @@
import { Entity, Property } from "@mikro-orm/core"
@Entity()
export class EntityModel {
@Property({ columnType: "int" })
id!: number
}
@@ -0,0 +1,2 @@
export * from "./entity"
export * from "./entity-2"
@@ -0,0 +1,17 @@
import { IModuleService, ModuleJoinerConfig } from "@medusajs/types"
import { defineJoinerConfig } from "@medusajs/utils"
export class ModuleService implements IModuleService {
__joinerConfig(): ModuleJoinerConfig {
return defineJoinerConfig("module-service", {
alias: [
{
name: ["custom_name"],
args: {
entity: "Custom",
},
},
],
})
}
}
@@ -0,0 +1,11 @@
import { ModuleExports } from "@medusajs/types"
import { ModuleService } from "./services/module-service"
const moduleExports: ModuleExports = {
service: ModuleService,
}
export * from "./models"
export * from "./services/module-service"
export default moduleExports
@@ -0,0 +1,7 @@
import { Entity, Property } from "@mikro-orm/core"
@Entity()
export class Entity2 {
@Property({ columnType: "int" })
id!: number
}
@@ -0,0 +1,7 @@
import { Entity, Property } from "@mikro-orm/core"
@Entity()
export class EntityModel {
@Property({ columnType: "int" })
id!: number
}
@@ -0,0 +1,2 @@
export * from "./entity"
export * from "./entity-2"
@@ -0,0 +1,3 @@
import { IModuleService } from "@medusajs/types"
export class ModuleService implements IModuleService {}
@@ -0,0 +1,316 @@
import { loadResources } from "../load-internal"
import { IModuleService, ModuleResolution } from "@medusajs/types"
import { join } from "path"
import {
ModuleWithDmlMixedWithoutJoinerConfigFixtures,
ModuleWithDmlWithoutJoinerConfigFixtures,
ModuleWithJoinerConfigFixtures,
ModuleWithoutJoinerConfigFixtures,
} from "../__fixtures__"
import { upperCaseFirst } from "@medusajs/utils"
describe("load internal - load resources", () => {
describe("when loading the module resources from a path", () => {
test("should return the correct resources and generate the correct joiner config from a mix of DML entities and mikro orm entities", async () => {
const { ModuleService, EntityModel, dmlEntity } =
ModuleWithDmlMixedWithoutJoinerConfigFixtures
const moduleResolution: ModuleResolution = {
resolutionPath: join(
__dirname,
"../__fixtures__/module-with-dml-mixed-without-joiner-config"
),
definition: {
key: "module-with-dml-mixed-without-joiner-config",
registrationName: "service",
label: "Module with DML mixed without joiner config",
defaultPackage: false,
defaultModuleDeclaration: {
scope: "internal",
resources: "shared",
},
},
}
expect(
(ModuleService.prototype as IModuleService).__joinerConfig
).toBeUndefined()
const resources = await loadResources(moduleResolution)
expect(resources).toBeDefined()
expect(resources.services).toHaveLength(1)
expect(resources.services[0]).toEqual(ModuleService)
expect(resources.models).toHaveLength(2)
expect(resources.models).toEqual(
expect.arrayContaining([
expect.objectContaining({ name: upperCaseFirst(dmlEntity.name) }),
expect.objectContaining({ name: upperCaseFirst(EntityModel.name) }),
])
)
expect(resources.repositories).toHaveLength(0)
expect(resources.loaders).toHaveLength(2)
expect(resources.loaders).toEqual([
expect.objectContaining({ name: "connectionLoader" }),
expect.objectContaining({ name: "containerLoader" }),
])
expect(resources.moduleService).toEqual(ModuleService)
expect(
(resources.moduleService.prototype as IModuleService).__joinerConfig
).toBeDefined()
const generatedJoinerConfig = (
resources.moduleService.prototype as IModuleService
).__joinerConfig()
expect(generatedJoinerConfig).toEqual({
serviceName: "module-with-dml-mixed-without-joiner-config",
primaryKeys: ["id"],
linkableKeys: {
dml_entity_id: "DmlEntity",
entity_model_id: "EntityModel",
},
alias: [
{
name: ["dml_entity", "dml_entities"],
args: {
entity: "DmlEntity",
methodSuffix: "DmlEntities",
},
},
{
name: ["entity_model", "entity_models"],
args: {
entity: "EntityModel",
methodSuffix: "EntityModels",
},
},
],
})
})
test("should return the correct resources and generate the correct joiner config from DML entities", async () => {
const { ModuleService, entityModel, dmlEntity } =
ModuleWithDmlWithoutJoinerConfigFixtures
const moduleResolution: ModuleResolution = {
resolutionPath: join(
__dirname,
"../__fixtures__/module-with-dml-without-joiner-config"
),
definition: {
key: "module-with-dml-without-joiner-config",
registrationName: "service",
label: "Module with DML without joiner config",
defaultPackage: false,
defaultModuleDeclaration: {
scope: "internal",
resources: "shared",
},
},
}
expect(
(ModuleService.prototype as IModuleService).__joinerConfig
).toBeUndefined()
const resources = await loadResources(moduleResolution)
expect(resources).toBeDefined()
expect(resources.services).toHaveLength(1)
expect(resources.services[0]).toEqual(ModuleService)
expect(resources.models).toHaveLength(2)
expect(resources.models).toEqual(
expect.arrayContaining([
expect.objectContaining({ name: upperCaseFirst(dmlEntity.name) }),
expect.objectContaining({ name: upperCaseFirst(entityModel.name) }),
])
)
expect(resources.repositories).toHaveLength(0)
expect(resources.loaders).toHaveLength(2)
expect(resources.loaders).toEqual([
expect.objectContaining({ name: "connectionLoader" }),
expect.objectContaining({ name: "containerLoader" }),
])
expect(resources.moduleService).toEqual(ModuleService)
expect(
(resources.moduleService.prototype as IModuleService).__joinerConfig
).toBeDefined()
const generatedJoinerConfig = (
resources.moduleService.prototype as IModuleService
).__joinerConfig()
expect(generatedJoinerConfig).toEqual({
serviceName: "module-with-dml-without-joiner-config",
primaryKeys: ["id"],
linkableKeys: {
entity_model_id: "EntityModel",
dml_entity_id: "DmlEntity",
},
alias: [
{
name: ["entity_model", "entity_models"],
args: {
entity: "EntityModel",
methodSuffix: "EntityModels",
},
},
{
name: ["dml_entity", "dml_entities"],
args: {
entity: "DmlEntity",
methodSuffix: "DmlEntities",
},
},
],
})
})
test("should return the correct resources and generate the correct joiner config from mikro orm entities", async () => {
const { ModuleService, EntityModel, Entity2 } =
ModuleWithoutJoinerConfigFixtures
const moduleResolution: ModuleResolution = {
resolutionPath: join(
__dirname,
"../__fixtures__/module-without-joiner-config"
),
definition: {
key: "module-without-joiner-config",
registrationName: "service",
label: "Module without joiner config",
defaultPackage: false,
defaultModuleDeclaration: {
scope: "internal",
resources: "shared",
},
},
}
expect(
(ModuleService.prototype as IModuleService).__joinerConfig
).toBeUndefined()
const resources = await loadResources(moduleResolution)
expect(resources).toBeDefined()
expect(resources.services).toHaveLength(1)
expect(resources.services[0]).toEqual(ModuleService)
expect(resources.models).toHaveLength(2)
expect(resources.models).toEqual(
expect.arrayContaining([
expect.objectContaining({ name: upperCaseFirst(EntityModel.name) }),
expect.objectContaining({ name: upperCaseFirst(Entity2.name) }),
])
)
expect(resources.repositories).toHaveLength(0)
expect(resources.loaders).toHaveLength(2)
expect(resources.loaders).toEqual([
expect.objectContaining({ name: "connectionLoader" }),
expect.objectContaining({ name: "containerLoader" }),
])
expect(resources.moduleService).toEqual(ModuleService)
expect(
(resources.moduleService.prototype as IModuleService).__joinerConfig
).toBeDefined()
const generatedJoinerConfig = (
resources.moduleService.prototype as IModuleService
).__joinerConfig()
expect(generatedJoinerConfig).toEqual({
serviceName: "module-without-joiner-config",
primaryKeys: ["id"],
linkableKeys: {
entity2_id: "Entity2",
entity_model_id: "EntityModel",
},
alias: [
{
name: ["entity2", "entity2s"],
args: {
entity: "Entity2",
methodSuffix: "Entity2s",
},
},
{
name: ["entity_model", "entity_models"],
args: {
entity: "EntityModel",
methodSuffix: "EntityModels",
},
},
],
})
})
test("should return the correct resources and use the given joiner config", async () => {
const { ModuleService, EntityModel, Entity2 } =
ModuleWithJoinerConfigFixtures
const moduleResolution: ModuleResolution = {
resolutionPath: join(
__dirname,
"../__fixtures__/module-with-joiner-config"
),
definition: {
key: "module-without-joiner-config",
registrationName: "service",
label: "Module without joiner config",
defaultPackage: false,
defaultModuleDeclaration: {
scope: "internal",
resources: "shared",
},
},
}
expect(
(ModuleService.prototype as IModuleService).__joinerConfig
).toBeDefined()
const resources = await loadResources(moduleResolution)
expect(resources).toBeDefined()
expect(resources.services).toHaveLength(1)
expect(resources.services[0]).toEqual(ModuleService)
expect(resources.models).toHaveLength(2)
expect(resources.models).toEqual(
expect.arrayContaining([
expect.objectContaining({ name: upperCaseFirst(EntityModel.name) }),
expect.objectContaining({ name: upperCaseFirst(Entity2.name) }),
])
)
expect(resources.repositories).toHaveLength(0)
expect(resources.loaders).toHaveLength(2)
expect(resources.loaders).toEqual([
expect.objectContaining({ name: "connectionLoader" }),
expect.objectContaining({ name: "containerLoader" }),
])
expect(resources.moduleService).toEqual(ModuleService)
const generatedJoinerConfig = (
resources.moduleService.prototype as IModuleService
).__joinerConfig()
expect(generatedJoinerConfig).toEqual({
serviceName: "module-service",
primaryKeys: ["id"],
linkableKeys: {},
alias: [
{
name: ["custom_name"],
args: {
entity: "Custom",
methodSuffix: "Customs",
},
},
],
})
})
})
})
@@ -10,9 +10,12 @@ import {
} from "@medusajs/types"
import {
ContainerRegistrationKeys,
createMedusaContainer,
createMikrORMEntity,
defineJoinerConfig,
DmlEntity,
MedusaModuleType,
ModulesSdkUtils,
createMedusaContainer,
} from "@medusajs/utils"
import { asFunction, asValue } from "awilix"
import { statSync } from "fs"
@@ -78,9 +81,9 @@ export async function loadInternalModule(
if (resolution.resolutionPath) {
moduleResources = await loadResources(
loadedModule?.loaders ?? [],
resolution,
logger
logger,
loadedModule?.loaders ?? []
)
}
@@ -180,9 +183,9 @@ export async function loadModuleMigrations(
// Generate migration scripts if they are not present
if (!runMigrations || !revertMigration) {
const moduleResources = await loadResources(
loadedModule?.loaders ?? [],
resolution,
console as unknown as Logger
console as unknown as Logger,
loadedModule?.loaders ?? []
)
const migrationScriptOptions = {
@@ -240,10 +243,10 @@ async function importAllFromDir(path: string) {
})
}
async function loadResources(
loadedModuleLoaders: ModuleLoaderFunction[],
export async function loadResources(
moduleResolution: ModuleResolution,
logger: Logger
logger: Logger = console as unknown as Logger,
loadedModuleLoaders?: ModuleLoaderFunction[]
): Promise<ModuleResource> {
let modulePath = moduleResolution.resolutionPath as string
let normalizedPath = modulePath
@@ -267,12 +270,21 @@ async function loadResources(
),
])
const entityBuilder = createMikrORMEntity()
const cleanupResources = (resources) => {
return Object.values(resources).filter(
(resource): resource is Function => {
return typeof resource === "function"
}
)
return Object.values(resources)
.map((resource) => {
if (DmlEntity.isDmlEntity(resource)) {
return entityBuilder(resource as DmlEntity<any>)
}
if (typeof resource === "function") {
return resource
}
return null
})
.filter((v): v is Function => !!v)
}
const potentialServices = [...new Set(cleanupResources(services))]
@@ -288,6 +300,12 @@ async function loadResources(
migrationPath: normalizedPath + "/migrations",
})
generateJoinerConfigIfNecessary({
moduleResolution,
service: moduleService,
models: potentialModels,
})
return {
services: potentialServices,
models: potentialModels,
@@ -342,7 +360,7 @@ async function runLoaders(
}
function prepareLoaders({
loadedModuleLoaders,
loadedModuleLoaders = [] as ModuleLoaderFunction[],
models,
repositories,
services,
@@ -407,3 +425,23 @@ function prepareLoaders({
return finalLoaders
}
function generateJoinerConfigIfNecessary({
moduleResolution,
service,
models,
}: {
moduleResolution: ModuleResolution
service: Constructor<IModuleService>
models: Function[]
}) {
if (service.prototype.__joinerConfig) {
return
}
service.prototype.__joinerConfig = function () {
return defineJoinerConfig(moduleResolution.definition.key, {
entityQueryingConfig: models,
})
}
}
+3 -6
View File
@@ -18,19 +18,16 @@ import type {
} from "@medusajs/types"
import {
ContainerRegistrationKeys,
ModulesSdkUtils,
createMedusaContainer,
isObject,
isString,
Modules,
ModulesSdkUtils,
promiseAll,
} from "@medusajs/utils"
import { asValue } from "awilix"
import type { Knex } from "knex"
import {
MODULE_PACKAGE_NAMES,
ModuleRegistrationName,
Modules,
} from "./definitions"
import { MODULE_PACKAGE_NAMES, ModuleRegistrationName } from "./definitions"
import { MedusaModule, RegisterModuleJoinerConfig } from "./medusa-module"
import { RemoteLink } from "./remote-link"
import { RemoteQuery } from "./remote-query"
+1 -2
View File
@@ -4,8 +4,7 @@ import {
ModuleJoinerRelationship,
} from "@medusajs/types"
import { isObject, promiseAll, toPascalCase } from "@medusajs/utils"
import { Modules } from "./definitions"
import { isObject, Modules, promiseAll, toPascalCase } from "@medusajs/utils"
import { MedusaModule } from "./medusa-module"
import { convertRecordsToLinkDefinition } from "./utils/convert-data-to-link-definition"
import { linkingErrorMessage } from "./utils/linking-error"