Fix/load medusa proj (#6639)
**What** - Runs loadMedusaV2 when v2 flag is enabled. - V2 loader loads only project-level framework features. E.g., project APIs, subscribers, jobs, and workflows. - No plugin support yet.
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
export * from "./auth"
|
||||||
export * from "./api-key"
|
export * from "./api-key"
|
||||||
export * from "./customer"
|
export * from "./customer"
|
||||||
export * from "./customer-group"
|
export * from "./customer-group"
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import searchIndexLoader from "./search-index"
|
|||||||
import servicesLoader from "./services"
|
import servicesLoader from "./services"
|
||||||
import strategiesLoader from "./strategies"
|
import strategiesLoader from "./strategies"
|
||||||
import subscribersLoader from "./subscribers"
|
import subscribersLoader from "./subscribers"
|
||||||
|
import medusaProjectApisLoader from "./load-medusa-project-apis"
|
||||||
|
|
||||||
type Options = {
|
type Options = {
|
||||||
directory: string
|
directory: string
|
||||||
@@ -81,7 +82,12 @@ async function loadLegacyModulesEntities(configModules, container) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadMedusaV2({ configModule, featureFlagRouter, expressApp }) {
|
async function loadMedusaV2({
|
||||||
|
rootDirectory,
|
||||||
|
configModule,
|
||||||
|
featureFlagRouter,
|
||||||
|
expressApp,
|
||||||
|
}) {
|
||||||
const container = createMedusaContainer()
|
const container = createMedusaContainer()
|
||||||
|
|
||||||
// Add additional information to context of request
|
// Add additional information to context of request
|
||||||
@@ -124,6 +130,14 @@ async function loadMedusaV2({ configModule, featureFlagRouter, expressApp }) {
|
|||||||
featureFlagRouter,
|
featureFlagRouter,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
medusaProjectApisLoader({
|
||||||
|
rootDirectory,
|
||||||
|
container,
|
||||||
|
app: expressApp,
|
||||||
|
configModule,
|
||||||
|
activityId: "medusa-project-apis",
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
container,
|
container,
|
||||||
app: expressApp,
|
app: expressApp,
|
||||||
@@ -146,7 +160,12 @@ export default async ({
|
|||||||
track("FEATURE_FLAGS_LOADED")
|
track("FEATURE_FLAGS_LOADED")
|
||||||
|
|
||||||
if (featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) {
|
if (featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)) {
|
||||||
return await loadMedusaV2({ configModule, featureFlagRouter, expressApp })
|
return await loadMedusaV2({
|
||||||
|
rootDirectory,
|
||||||
|
configModule,
|
||||||
|
featureFlagRouter,
|
||||||
|
expressApp,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const container = createMedusaContainer()
|
const container = createMedusaContainer()
|
||||||
|
|||||||
200
packages/medusa/src/loaders/load-medusa-project-apis.ts
Normal file
200
packages/medusa/src/loaders/load-medusa-project-apis.ts
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
import { promiseAll } from "@medusajs/utils"
|
||||||
|
import { Express } from "express"
|
||||||
|
import glob from "glob"
|
||||||
|
import _ from "lodash"
|
||||||
|
import { trackInstallation } from "medusa-telemetry"
|
||||||
|
import { EOL } from "os"
|
||||||
|
import path from "path"
|
||||||
|
import { ConfigModule, Logger, MedusaContainer } from "../types/global"
|
||||||
|
import ScheduledJobsLoader from "./helpers/jobs"
|
||||||
|
import { RoutesLoader } from "./helpers/routing"
|
||||||
|
import { SubscriberLoader } from "./helpers/subscribers"
|
||||||
|
import logger from "./logger"
|
||||||
|
|
||||||
|
type Options = {
|
||||||
|
rootDirectory: string
|
||||||
|
container: MedusaContainer
|
||||||
|
configModule: ConfigModule
|
||||||
|
app: Express
|
||||||
|
activityId: string
|
||||||
|
}
|
||||||
|
|
||||||
|
type PluginDetails = {
|
||||||
|
resolve: string
|
||||||
|
name: string
|
||||||
|
id: string
|
||||||
|
options: Record<string, unknown>
|
||||||
|
version: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MEDUSA_PROJECT_NAME = "project-plugin"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers all services in the services directory
|
||||||
|
*/
|
||||||
|
export default async ({
|
||||||
|
rootDirectory,
|
||||||
|
container,
|
||||||
|
app,
|
||||||
|
configModule,
|
||||||
|
activityId,
|
||||||
|
}: Options): Promise<void> => {
|
||||||
|
const resolved = getResolvedPlugins(rootDirectory, configModule) || []
|
||||||
|
|
||||||
|
await promiseAll(
|
||||||
|
resolved.map(async (pluginDetails) => {
|
||||||
|
await registerApi(pluginDetails, app, container, configModule, activityId)
|
||||||
|
await registerSubscribers(pluginDetails, container, activityId)
|
||||||
|
await registerWorkflows(pluginDetails)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
await promiseAll(
|
||||||
|
resolved.map(async (pluginDetails) => runLoaders(pluginDetails, container))
|
||||||
|
)
|
||||||
|
|
||||||
|
if (configModule.projectConfig.redis_url) {
|
||||||
|
await Promise.all(
|
||||||
|
resolved.map(async (pluginDetails) => {
|
||||||
|
await registerScheduledJobs(pluginDetails, container)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
logger.warn(
|
||||||
|
"You don't have Redis configured. Scheduled jobs will not be enabled."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
resolved.forEach((plugin) => trackInstallation(plugin.name, "plugin"))
|
||||||
|
}
|
||||||
|
|
||||||
|
function getResolvedPlugins(
|
||||||
|
rootDirectory: string,
|
||||||
|
configModule: ConfigModule,
|
||||||
|
extensionDirectoryPath = "dist"
|
||||||
|
): undefined | PluginDetails[] {
|
||||||
|
const extensionDirectory = path.join(rootDirectory, extensionDirectoryPath)
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
resolve: extensionDirectory,
|
||||||
|
name: MEDUSA_PROJECT_NAME,
|
||||||
|
id: createPluginId(MEDUSA_PROJECT_NAME),
|
||||||
|
options: configModule,
|
||||||
|
version: createFileContentHash(process.cwd(), `**`),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runLoaders(
|
||||||
|
pluginDetails: PluginDetails,
|
||||||
|
container: MedusaContainer
|
||||||
|
): Promise<void> {
|
||||||
|
const loaderFiles = glob.sync(
|
||||||
|
`${pluginDetails.resolve}/loaders/[!__]*.js`,
|
||||||
|
{}
|
||||||
|
)
|
||||||
|
await promiseAll(
|
||||||
|
loaderFiles.map(async (loader) => {
|
||||||
|
try {
|
||||||
|
const module = require(loader).default
|
||||||
|
if (typeof module === "function") {
|
||||||
|
await module(container, pluginDetails.options)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
const logger = container.resolve<Logger>("logger")
|
||||||
|
logger.warn(`Running loader failed: ${err.message}`)
|
||||||
|
return Promise.resolve()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function registerScheduledJobs(
|
||||||
|
pluginDetails: PluginDetails,
|
||||||
|
container: MedusaContainer
|
||||||
|
): Promise<void> {
|
||||||
|
await new ScheduledJobsLoader(
|
||||||
|
path.join(pluginDetails.resolve, "jobs"),
|
||||||
|
container,
|
||||||
|
pluginDetails.options
|
||||||
|
).load()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers the plugin's api routes.
|
||||||
|
*/
|
||||||
|
async function registerApi(
|
||||||
|
pluginDetails: PluginDetails,
|
||||||
|
app: Express,
|
||||||
|
container: MedusaContainer,
|
||||||
|
configmodule: ConfigModule,
|
||||||
|
activityId: string
|
||||||
|
): Promise<Express> {
|
||||||
|
const logger = container.resolve<Logger>("logger")
|
||||||
|
const projectName =
|
||||||
|
pluginDetails.name === MEDUSA_PROJECT_NAME
|
||||||
|
? "your Medusa project"
|
||||||
|
: `${pluginDetails.name}`
|
||||||
|
|
||||||
|
logger.progress(activityId, `Registering custom endpoints for ${projectName}`)
|
||||||
|
|
||||||
|
try {
|
||||||
|
/**
|
||||||
|
* Register the plugin's API routes using the file based routing.
|
||||||
|
*/
|
||||||
|
await new RoutesLoader({
|
||||||
|
app,
|
||||||
|
rootDir: path.join(pluginDetails.resolve, "api"),
|
||||||
|
activityId: activityId,
|
||||||
|
configModule: configmodule,
|
||||||
|
}).load()
|
||||||
|
} catch (err) {
|
||||||
|
logger.warn(
|
||||||
|
`An error occurred while registering API Routes in ${projectName}${
|
||||||
|
err.stack ? EOL + err.stack : ""
|
||||||
|
}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a plugin's subscribers at the right location in our container.
|
||||||
|
* Subscribers are registered directly in the container.
|
||||||
|
* @param {object} pluginDetails - the plugin details including plugin options,
|
||||||
|
* version, id, resolved path, etc. See resolvePlugin
|
||||||
|
* @param {object} container - the container where the services will be
|
||||||
|
* registered
|
||||||
|
* @return {void}
|
||||||
|
*/
|
||||||
|
async function registerSubscribers(
|
||||||
|
pluginDetails: PluginDetails,
|
||||||
|
container: MedusaContainer,
|
||||||
|
activityId: string
|
||||||
|
): Promise<void> {
|
||||||
|
await new SubscriberLoader(
|
||||||
|
path.join(pluginDetails.resolve, "subscribers"),
|
||||||
|
container,
|
||||||
|
pluginDetails.options,
|
||||||
|
activityId
|
||||||
|
).load()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* import files from the workflows directory to run the registration of the wofklows
|
||||||
|
* @param pluginDetails
|
||||||
|
*/
|
||||||
|
async function registerWorkflows(pluginDetails: PluginDetails): Promise<void> {
|
||||||
|
const files = glob.sync(`${pluginDetails.resolve}/workflows/*.js`, {})
|
||||||
|
await Promise.all(files.map(async (file) => import(file)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Create unique id for each plugin
|
||||||
|
function createPluginId(name: string): string {
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFileContentHash(path, files): string {
|
||||||
|
return path + files
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
ExternalModuleDeclaration,
|
ExternalModuleDeclaration,
|
||||||
InternalModuleDeclaration,
|
InternalModuleDeclaration,
|
||||||
|
MODULE_RESOURCE_TYPE,
|
||||||
MODULE_SCOPE,
|
MODULE_SCOPE,
|
||||||
ModuleDefinition,
|
ModuleDefinition,
|
||||||
ModuleExports,
|
ModuleExports,
|
||||||
@@ -25,7 +26,11 @@ export const registerMedusaModule = (
|
|||||||
const modDefinition = definition ?? ModulesDefinition[moduleKey]
|
const modDefinition = definition ?? ModulesDefinition[moduleKey]
|
||||||
|
|
||||||
if (modDefinition === undefined) {
|
if (modDefinition === undefined) {
|
||||||
throw new Error(`Module: ${moduleKey} is not defined.`)
|
moduleResolutions[moduleKey] = getCustomModuleResolution(
|
||||||
|
moduleKey,
|
||||||
|
moduleDeclaration as InternalModuleDeclaration
|
||||||
|
)
|
||||||
|
return moduleResolutions
|
||||||
}
|
}
|
||||||
|
|
||||||
const modDeclaration =
|
const modDeclaration =
|
||||||
@@ -53,6 +58,38 @@ export const registerMedusaModule = (
|
|||||||
return moduleResolutions
|
return moduleResolutions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getCustomModuleResolution(
|
||||||
|
key: string,
|
||||||
|
moduleConfig: InternalModuleDeclaration | string
|
||||||
|
): ModuleResolution {
|
||||||
|
const isString = typeof moduleConfig === "string"
|
||||||
|
const resolutionPath = resolveCwd(
|
||||||
|
isString ? moduleConfig : (moduleConfig.resolve as string)
|
||||||
|
)
|
||||||
|
|
||||||
|
return {
|
||||||
|
resolutionPath,
|
||||||
|
definition: {
|
||||||
|
key,
|
||||||
|
label: `Custom: ${key}`,
|
||||||
|
isRequired: false,
|
||||||
|
defaultPackage: "",
|
||||||
|
dependencies: [],
|
||||||
|
registrationName: key,
|
||||||
|
defaultModuleDeclaration: {
|
||||||
|
resources: MODULE_RESOURCE_TYPE.SHARED,
|
||||||
|
scope: MODULE_SCOPE.INTERNAL,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
moduleDeclaration: {
|
||||||
|
resources: MODULE_RESOURCE_TYPE.SHARED,
|
||||||
|
scope: MODULE_SCOPE.INTERNAL,
|
||||||
|
},
|
||||||
|
dependencies: [],
|
||||||
|
options: {},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export const registerMedusaLinkModule = (
|
export const registerMedusaLinkModule = (
|
||||||
definition: ModuleDefinition,
|
definition: ModuleDefinition,
|
||||||
moduleDeclaration: Partial<InternalModuleDeclaration>,
|
moduleDeclaration: Partial<InternalModuleDeclaration>,
|
||||||
|
|||||||
Reference in New Issue
Block a user