feat(medusa, modules-sdk, types, utils): Re work modules loading and remove legacy functions (#5496)

This commit is contained in:
Adrien de Peretti
2023-11-02 17:59:13 +01:00
committed by GitHub
parent ca411e54eb
commit 154c9b43bd
39 changed files with 616 additions and 393 deletions
@@ -1,4 +1,3 @@
import { ModulesHelper } from "@medusajs/modules-sdk"
import { FlagRouter } from "@medusajs/utils"
import { defaultRelationsExtended } from "."
import {
@@ -7,6 +6,7 @@ import {
StoreService,
} from "../../../../services"
import { ExtendedStoreDTO } from "../../../../types/store"
import { MedusaModule } from "@medusajs/modules-sdk"
/**
* @oas [get] /admin/store
@@ -62,7 +62,6 @@ export default async (req, res) => {
const storeService: StoreService = req.scope.resolve("storeService")
const featureFlagRouter: FlagRouter = req.scope.resolve("featureFlagRouter")
const modulesHelper: ModulesHelper = req.scope.resolve("modulesHelper")
const paymentProviderService: PaymentProviderService = req.scope.resolve(
"paymentProviderService"
@@ -80,7 +79,16 @@ export default async (req, res) => {
})) as ExtendedStoreDTO
data.feature_flags = featureFlagRouter.listFlags()
data.modules = modulesHelper.modules
data.modules = MedusaModule.getLoadedModules()
.map((loadedModule) => {
return Object.entries(loadedModule).map(([key, service]) => {
return {
module: key,
resolution: service.__definition.defaultPackage,
}
})
})
.flat()
const paymentProviders = await paymentProviderService.list()
const fulfillmentProviders = await fulfillmentProviderService.list()
+15 -14
View File
@@ -12,23 +12,24 @@ import {
} from "../services"
import getMigrations, { getModuleSharedResources } from "./utils/get-migrations"
import { ConfigModule } from "../types/global"
import { CreateProductCategoryInput } from "../types/product-category"
import { CreateProductInput } from "../types/product"
import { IPricingModuleService } from "@medusajs/types"
import express from "express"
import fs from "fs"
import { sync as existsSync } from "fs-exists-cached"
import { getConfigFile } from "medusa-core-utils"
import { track } from "medusa-telemetry"
import path from "path"
import loaders from "../loaders"
import { handleConfigError } from "../loaders/config"
import featureFlagLoader from "../loaders/feature-flags"
import IsolatePricingDomainFeatureFlag from "../loaders/feature-flags/isolate-pricing-domain"
import Logger from "../loaders/logger"
import PublishableApiKeyService from "../services/publishable-api-key"
import { SalesChannel } from "../models"
import { sync as existsSync } from "fs-exists-cached"
import express from "express"
import featureFlagLoader from "../loaders/feature-flags"
import fs from "fs"
import { getConfigFile } from "medusa-core-utils"
import { handleConfigError } from "../loaders/config"
import loaders from "../loaders"
import path from "path"
import { track } from "medusa-telemetry"
import PublishableApiKeyService from "../services/publishable-api-key"
import { ConfigModule } from "../types/global"
import { CreateProductInput } from "../types/product"
import { CreateProductCategoryInput } from "../types/product-category"
import { ModuleRegistrationName } from "@medusajs/modules-sdk"
type SeedOptions = {
directory: string
@@ -122,7 +123,7 @@ const seed = async function ({ directory, migrate, seedFile }: SeedOptions) {
"shippingProfileService"
)
const pricingModuleService: IPricingModuleService = container.resolve(
"pricingModuleService"
ModuleRegistrationName.PRICING
)
/* eslint-enable */
@@ -1,4 +1,4 @@
import { MedusaModule, registerModules } from "@medusajs/modules-sdk"
import { MedusaModule, registerMedusaModule } from "@medusajs/modules-sdk"
import fs from "fs"
import { sync as existsSync } from "fs-exists-cached"
import glob from "glob"
@@ -96,8 +96,13 @@ function resolvePlugin(pluginName) {
export function getInternalModules(configModule) {
const modules = []
const moduleResolutions = {}
const moduleResolutions = registerModules(configModule.modules)
Object.entries(configModule.modules ?? {}).forEach(([moduleKey, module]) => {
moduleResolutions[moduleKey] = registerMedusaModule(moduleKey, module)[
moduleKey
]
})
for (const moduleResolution of Object.values(moduleResolutions)) {
if (
@@ -256,7 +261,12 @@ export const getModuleSharedResources = (configModule, featureFlagsRouter) => {
}
export const runIsolatedModulesMigration = async (configModule) => {
const moduleResolutions = registerModules(configModule.modules)
const moduleResolutions = {}
Object.entries(configModule.modules ?? {}).forEach(([moduleKey, module]) => {
moduleResolutions[moduleKey] = registerMedusaModule(moduleKey, module)[
moduleKey
]
})
for (const moduleResolution of Object.values(moduleResolutions)) {
if (
@@ -276,7 +286,12 @@ export const runIsolatedModulesMigration = async (configModule) => {
}
export const revertIsolatedModulesMigration = async (configModule) => {
const moduleResolutions = registerModules(configModule.modules)
const moduleResolutions = {}
Object.entries(configModule.modules ?? {}).forEach(([moduleKey, module]) => {
moduleResolutions[moduleKey] = registerMedusaModule(moduleKey, module)[
moduleKey
]
})
for (const moduleResolution of Object.values(moduleResolutions)) {
if (
+15 -9
View File
@@ -3,7 +3,6 @@ import {
moduleLoader,
ModulesDefinition,
registerMedusaModule,
registerModules,
} from "@medusajs/modules-sdk"
import { asValue, createContainer } from "awilix"
import express from "express"
@@ -19,7 +18,6 @@ import passportLoader from "../loaders/passport"
import repositories from "../loaders/repositories"
import servicesLoader from "../loaders/services"
import strategiesLoader from "../loaders/strategies"
import modules from "../modules-config"
const adminSessionOpts = {
cookieName: "session",
@@ -33,12 +31,13 @@ const clientSessionOpts = {
secret: "test",
}
const moduleResolutions = registerModules({})
// Load non legacy modules
Object.keys(modules).map((moduleKey) => {
const moduleResolutions = {}
Object.entries(ModulesDefinition).forEach(([moduleKey, module]) => {
moduleResolutions[moduleKey] = registerMedusaModule(
moduleKey,
ModulesDefinition[moduleKey]
module.defaultModuleDeclaration,
undefined,
module
)[moduleKey]
})
@@ -84,7 +83,7 @@ container.register("modulesHelper", asValue(moduleHelper))
container.register("configModule", asValue(config))
container.register({
logger: asValue({
error: () => { },
error: () => {},
}),
manager: asValue(MockManager),
})
@@ -146,7 +145,10 @@ export async function request(method, url, opts = {}) {
headers.Cookie = headers.Cookie || ""
if (opts.adminSession) {
const token = jwt.sign(
{ user_id: opts.adminSession.userId || opts.adminSession.jwt?.userId, domain: "admin" },
{
user_id: opts.adminSession.userId || opts.adminSession.jwt?.userId,
domain: "admin",
},
config.projectConfig.jwt_secret
)
@@ -154,7 +156,11 @@ export async function request(method, url, opts = {}) {
}
if (opts.clientSession) {
const token = jwt.sign(
{ customer_id: opts.clientSession.customer_id || opts.clientSession.jwt?.customer_id, domain: "store" },
{
customer_id:
opts.clientSession.customer_id || opts.clientSession.jwt?.customer_id,
domain: "store",
},
config.projectConfig.jwt_secret
)
+54 -22
View File
@@ -1,4 +1,8 @@
import { moduleLoader, registerModules } from "@medusajs/modules-sdk"
import {
InternalModuleDeclaration,
ModulesDefinition,
} from "@medusajs/modules-sdk"
import { MODULE_RESOURCE_TYPE } from "@medusajs/types"
import { Express, NextFunction, Request, Response } from "express"
import databaseLoader, { dataSource } from "./database"
@@ -17,10 +21,8 @@ import loadConfig from "./config"
import defaultsLoader from "./defaults"
import expressLoader from "./express"
import featureFlagsLoader from "./feature-flags"
import IsolatePricingDomainFeatureFlag from "./feature-flags/isolate-pricing-domain"
import IsolateProductDomainFeatureFlag from "./feature-flags/isolate-product-domain"
import Logger from "./logger"
import loadMedusaApp from "./medusa-app"
import loadMedusaApp, { mergeDefaultModules } from "./medusa-app"
import modelsLoader from "./models"
import passportLoader from "./passport"
import pgConnectionLoader from "./pg-connection"
@@ -37,6 +39,34 @@ type Options = {
isTest: boolean
}
async function loadLegacyModulesEntities(configModules, container) {
for (const [moduleName, moduleConfig] of Object.entries(configModules)) {
const definition = ModulesDefinition[moduleName]
if (!definition.isLegacy) {
continue
}
if (
(moduleConfig as InternalModuleDeclaration).resources ===
MODULE_RESOURCE_TYPE.SHARED ||
(definition.defaultModuleDeclaration as InternalModuleDeclaration)
.resources === MODULE_RESOURCE_TYPE.SHARED
) {
const module = await import(
(moduleConfig as InternalModuleDeclaration).resolve ??
(definition.defaultPackage as string)
)
if (module.default?.models) {
module.default.models.map((model) =>
container.registerAdd("db_entities", asValue(model))
)
}
}
}
}
export default async ({
directory: rootDirectory,
expressApp,
@@ -98,16 +128,8 @@ export default async ({
const pgConnection = await pgConnectionLoader({ container, configModule })
const modulesActivity = Logger.activity(`Initializing modules${EOL}`)
track("MODULES_INIT_STARTED")
await moduleLoader({
container,
moduleResolutions: registerModules(configModule?.modules),
logger: Logger,
})
const modAct = Logger.success(modulesActivity, "Modules initialized") || {}
track("MODULES_INIT_COMPLETED", { duration: modAct.duration })
const configModules = mergeDefaultModules(configModule.modules)
await loadLegacyModulesEntities(configModules, container)
const dbActivity = Logger.activity(`Initializing database${EOL}`)
track("DATABASE_INIT_STARTED")
@@ -129,13 +151,6 @@ export default async ({
})
container.register("remoteQuery", asValue(null)) // ensure remoteQuery is always registered
// Only load non legacy modules, the legacy modules (non migrated yet) are retrieved by the registerModule above
if (
featureFlagRouter.isFeatureEnabled(IsolateProductDomainFeatureFlag.key) ||
featureFlagRouter.isFeatureEnabled(IsolatePricingDomainFeatureFlag.key)
) {
await loadMedusaApp({ configModule, container })
}
const servicesActivity = Logger.activity(`Initializing services${EOL}`)
track("SERVICES_INIT_STARTED")
@@ -143,6 +158,18 @@ export default async ({
const servAct = Logger.success(servicesActivity, "Services initialized") || {}
track("SERVICES_INIT_COMPLETED", { duration: servAct.duration })
const modulesActivity = Logger.activity(`Initializing modules${EOL}`)
track("MODULES_INIT_STARTED")
// Move before services init once all modules are migrated and do not rely on core resources anymore
await loadMedusaApp({
configModule,
container,
})
const modAct = Logger.success(modulesActivity, "Modules initialized") || {}
track("MODULES_INIT_COMPLETED", { duration: modAct.duration })
const expActivity = Logger.activity(`Initializing express${EOL}`)
track("EXPRESS_INIT_STARTED")
await expressLoader({ app: expressApp, configModule })
@@ -196,5 +223,10 @@ export default async ({
Logger.success(searchActivity, "Indexing event emitted") || {}
track("SEARCH_ENGINE_INDEXING_COMPLETED", { duration: searchAct.duration })
return { container, dbConnection, app: expressApp, pgConnection }
return {
container,
dbConnection,
app: expressApp,
pgConnection,
}
}
+72 -14
View File
@@ -1,26 +1,51 @@
import { CommonTypes, MedusaContainer } from "@medusajs/types"
import {
CommonTypes,
InternalModuleDeclaration,
MedusaContainer,
ModuleDefinition,
} from "@medusajs/types"
import {
MedusaApp,
MedusaAppOutput,
ModulesDefinition,
} from "@medusajs/modules-sdk"
import { ContainerRegistrationKeys } from "@medusajs/utils"
import { ContainerRegistrationKeys, isObject } from "@medusajs/utils"
import { asValue } from "awilix"
import { joinerConfig } from "../joiner-config"
import { mergeModulesConfig } from "../utils/merge-modules-config"
import modulesConfig from "../modules-config"
import { remoteQueryFetchData } from ".."
export function mergeDefaultModules(
modulesConfig: CommonTypes.ConfigModule["modules"]
) {
const defaultModules = Object.values(ModulesDefinition).filter(
(definition: ModuleDefinition) => {
return !!definition.defaultPackage
}
)
const configModules = { ...modulesConfig } ?? {}
for (const defaultModule of defaultModules as ModuleDefinition[]) {
configModules[defaultModule.key] ??= defaultModule.defaultModuleDeclaration
}
return configModules
}
export const loadMedusaApp = async (
{
configModule,
container,
}: { configModule: CommonTypes.ConfigModule; container: MedusaContainer },
}: {
configModule: {
modules?: CommonTypes.ConfigModule["modules"]
projectConfig: CommonTypes.ConfigModule["projectConfig"]
}
container: MedusaContainer
},
config = { registerInContainer: true }
): Promise<MedusaAppOutput> => {
mergeModulesConfig(configModule.modules ?? {}, modulesConfig)
const injectedDependencies = {
[ContainerRegistrationKeys.PG_CONNECTION]: container.resolve(
ContainerRegistrationKeys.PG_CONNECTION
@@ -34,8 +59,34 @@ export const loadMedusaApp = async (
},
}
container.register(ContainerRegistrationKeys.REMOTE_QUERY, asValue(undefined))
container.register(ContainerRegistrationKeys.REMOTE_LINK, asValue(undefined))
const configModules = mergeDefaultModules(configModule.modules)
// Apply default options to legacy modules
for (const moduleKey of Object.keys(configModules)) {
if (!ModulesDefinition[moduleKey].isLegacy) {
continue
}
if (isObject(configModules[moduleKey])) {
;(
configModules[moduleKey] as Partial<InternalModuleDeclaration>
).options ??= {
database: {
type: "postgres",
url: configModule.projectConfig.database_url,
extra: configModule.projectConfig.database_extra,
schema: configModule.projectConfig.database_schema,
logging: configModule.projectConfig.database_logging,
},
}
}
}
const medusaApp = await MedusaApp({
modulesConfig,
modulesConfig: configModules,
servicesConfig: joinerConfig,
remoteFetchData: remoteQueryFetchData(container),
sharedContainer: container,
@@ -48,18 +99,25 @@ export const loadMedusaApp = async (
}
container.register("remoteLink", asValue(medusaApp.link))
container.register(
ContainerRegistrationKeys.REMOTE_QUERY,
asValue(medusaApp.query)
)
const { query, modules } = medusaApp
// Medusa app load all non legacy modules, so we need to register them in the container since they are into their own container
// We might decide to do it elsewhere but for now I think it is fine
for (const [serviceKey, moduleService] of Object.entries(modules)) {
for (const [serviceKey, moduleService] of Object.entries(medusaApp.modules)) {
container.register(
ModulesDefinition[serviceKey].registrationName,
asValue(moduleService)
)
}
container.register("remoteQuery", asValue(query))
// Register all unresolved modules as undefined to be present in the container with undefined value by defaul
// but still resolvable
for (const [, moduleDefinition] of Object.entries(ModulesDefinition)) {
if (!container.hasRegistration(moduleDefinition.registrationName)) {
container.register(moduleDefinition.registrationName, asValue(undefined))
}
}
return medusaApp
}
+2 -2
View File
@@ -1,5 +1,5 @@
import { ContainerRegistrationKeys, ModulesSdkUtils } from "@medusajs/utils"
import { AwilixContainer, asValue } from "awilix"
import { asValue, AwilixContainer } from "awilix"
import { ConfigModule } from "../types/global"
type Options = {
@@ -9,7 +9,7 @@ type Options = {
export default async ({ container, configModule }: Options): Promise<any> => {
if (container.hasRegistration(ContainerRegistrationKeys.PG_CONNECTION)) {
return
return container.resolve(ContainerRegistrationKeys.PG_CONNECTION)
}
// Share a knex connection to be consumed by the shared modules
+1
View File
@@ -2,6 +2,7 @@ import { MedusaModuleConfig, Modules } from "@medusajs/modules-sdk"
const modules: MedusaModuleConfig = {
[Modules.PRODUCT]: true,
[Modules.PRICING]: true,
}
export default modules
+5 -3
View File
@@ -27,14 +27,17 @@ export default class EventBusService
protected readonly config_: ConfigModule
protected readonly stagedJobService_: StagedJobService
// eslint-disable-next-line max-len
protected readonly eventBusModuleService_: EventBusTypes.IEventBusModuleService
protected get eventBusModuleService_(): EventBusTypes.IEventBusModuleService {
return this.__container__.eventBusModuleService
}
protected readonly logger_: Logger
protected shouldEnqueuerRun: boolean
protected enqueue_: Promise<void>
constructor(
{ stagedJobService, eventBusModuleService, logger }: InjectedDependencies,
{ stagedJobService, logger }: InjectedDependencies,
config,
isSingleton = true
) {
@@ -43,7 +46,6 @@ export default class EventBusService
this.logger_ = logger
this.config_ = config
this.eventBusModuleService_ = eventBusModuleService
this.stagedJobService_ = stagedJobService
if (process.env.NODE_ENV !== "test" && isSingleton) {
+4 -3
View File
@@ -70,7 +70,10 @@ export default class OrderEditService extends TransactionBaseService {
protected readonly taxProviderService_: TaxProviderService
protected readonly lineItemAdjustmentService_: LineItemAdjustmentService
protected readonly orderEditItemChangeService_: OrderEditItemChangeService
protected readonly inventoryService_: IInventoryService | undefined
protected get inventoryService_(): IInventoryService | undefined {
return this.__container__.inventoryService
}
constructor({
orderEditRepository,
@@ -82,7 +85,6 @@ export default class OrderEditService extends TransactionBaseService {
orderEditItemChangeService,
lineItemAdjustmentService,
taxProviderService,
inventoryService,
}: InjectedDependencies) {
// eslint-disable-next-line prefer-rest-params
super(arguments[0])
@@ -96,7 +98,6 @@ export default class OrderEditService extends TransactionBaseService {
this.orderEditItemChangeService_ = orderEditItemChangeService
this.lineItemAdjustmentService_ = lineItemAdjustmentService
this.taxProviderService_ = taxProviderService
this.inventoryService_ = inventoryService
}
async retrieve(
+7 -7
View File
@@ -2,7 +2,6 @@ import {
CalculatedPriceSetDTO,
IPricingModuleService,
PriceSetMoneyAmountDTO,
RemoteJoinerQuery,
RemoteQueryFunction,
} from "@medusajs/types"
import { FlagRouter, removeNullish } from "@medusajs/utils"
@@ -56,8 +55,13 @@ class PricingService extends TransactionBaseService {
protected readonly priceSelectionStrategy: IPriceSelectionStrategy
protected readonly productVariantService: ProductVariantService
protected readonly featureFlagRouter: FlagRouter
protected readonly pricingModuleService: IPricingModuleService
protected readonly remoteQuery: RemoteQueryFunction
protected get pricingModuleService(): IPricingModuleService {
return this.__container__.pricingModuleService
}
protected get remoteQuery(): RemoteQueryFunction {
return this.__container__.remoteQuery
}
constructor({
productVariantService,
@@ -65,8 +69,6 @@ class PricingService extends TransactionBaseService {
regionService,
priceSelectionStrategy,
featureFlagRouter,
remoteQuery,
pricingModuleService,
}: InjectedDependencies) {
// eslint-disable-next-line prefer-rest-params
super(arguments[0])
@@ -76,8 +78,6 @@ class PricingService extends TransactionBaseService {
this.priceSelectionStrategy = priceSelectionStrategy
this.productVariantService = productVariantService
this.featureFlagRouter = featureFlagRouter
this.pricingModuleService = pricingModuleService
this.remoteQuery = remoteQuery
}
/**
@@ -1,6 +1,5 @@
import { EntityManager, In } from "typeorm"
import {
ICacheService,
IEventBusService,
IInventoryService,
InventoryItemDTO,
@@ -42,17 +41,20 @@ class ProductVariantInventoryService extends TransactionBaseService {
protected readonly salesChannelLocationService_: SalesChannelLocationService
protected readonly salesChannelInventoryService_: SalesChannelInventoryService
protected readonly productVariantService_: ProductVariantService
protected readonly stockLocationService_: IStockLocationService
protected readonly inventoryService_: IInventoryService
protected readonly eventBusService_: IEventBusService
protected readonly cacheService_: ICacheService
protected get inventoryService_(): IInventoryService {
return this.__container__.inventoryService
}
protected get stockLocationService_(): IStockLocationService {
return this.__container__.stockLocationService
}
constructor({
stockLocationService,
salesChannelLocationService,
salesChannelInventoryService,
productVariantService,
inventoryService,
eventBusService,
}: InjectedDependencies) {
// eslint-disable-next-line prefer-rest-params
@@ -60,9 +62,7 @@ class ProductVariantInventoryService extends TransactionBaseService {
this.salesChannelLocationService_ = salesChannelLocationService
this.salesChannelInventoryService_ = salesChannelInventoryService
this.stockLocationService_ = stockLocationService
this.productVariantService_ = productVariantService
this.inventoryService_ = inventoryService
this.eventBusService_ = eventBusService
}
@@ -13,11 +13,13 @@ type InjectedDependencies = {
class SalesChannelInventoryService extends TransactionBaseService {
protected readonly salesChannelLocationService_: SalesChannelLocationService
protected readonly eventBusService_: EventBusTypes.IEventBusService
protected readonly inventoryService_: IInventoryService
protected get inventoryService_(): IInventoryService {
return this.__container__.inventoryService
}
constructor({
salesChannelLocationService,
inventoryService,
eventBusService,
}: InjectedDependencies) {
// eslint-disable-next-line prefer-rest-params
@@ -25,7 +27,6 @@ class SalesChannelInventoryService extends TransactionBaseService {
this.salesChannelLocationService_ = salesChannelLocationService
this.eventBusService_ = eventBusService
this.inventoryService_ = inventoryService
}
/**
@@ -19,19 +19,17 @@ type InjectedDependencies = {
class SalesChannelLocationService extends TransactionBaseService {
protected readonly salesChannelService_: SalesChannelService
protected readonly eventBusService_: IEventBusService
protected readonly stockLocationService_: IStockLocationService
constructor({
salesChannelService,
stockLocationService,
eventBusService,
}: InjectedDependencies) {
protected get stockLocationService_(): IStockLocationService {
return this.__container__.stockLocationService
}
constructor({ salesChannelService, eventBusService }: InjectedDependencies) {
// eslint-disable-next-line prefer-rest-params
super(arguments[0])
this.salesChannelService_ = salesChannelService
this.eventBusService_ = eventBusService
this.stockLocationService_ = stockLocationService
}
/**
@@ -1,11 +1,6 @@
import {
ConfigModule,
ExternalModuleDeclaration,
InternalModuleDeclaration,
} from "@medusajs/types"
import { ConfigModule } from "@medusajs/types"
import { ModulesDefinition } from "@medusajs/modules-sdk"
import { isObject } from "./is-object"
/**
* Merge the modules config from the medusa-config file with the modules config from medusa package
@@ -13,24 +8,30 @@ import { isObject } from "./is-object"
* @param medusaInternalModulesConfig
*/
export function mergeModulesConfig(
modules: ConfigModule["modules"],
medusaInternalModulesConfig
modules: ConfigModule["modules"] = {},
medusaInternalModulesConfig = {}
) {
for (const [moduleName, moduleConfig] of Object.entries(modules as any)) {
const modules_ = ({ ...modules } as ConfigModule["modules"])!
const userModulesConfigKeys = Object.keys(modules)
const internalModulesConfigKeys = Object.keys(medusaInternalModulesConfig)
const allModulesKeys = new Set([
...userModulesConfigKeys,
...internalModulesConfigKeys,
])
for (const moduleName of allModulesKeys) {
const internalModuleConfig = medusaInternalModulesConfig[moduleName]
const moduleDefinition = ModulesDefinition[moduleName]
if (moduleDefinition?.isLegacy) {
continue
}
const isModuleEnabled = moduleConfig === true || isObject(moduleConfig)
if (!isModuleEnabled) {
delete medusaInternalModulesConfig[moduleName]
} else {
medusaInternalModulesConfig[moduleName] = moduleConfig as Partial<
InternalModuleDeclaration | ExternalModuleDeclaration
>
}
modules_[moduleName] ??= internalModuleConfig
}
return modules_
}