chore(framework): Move feature flags related resources and cleanup (#8297)

**What**
cleanup and move the feature flag related resources to manage them.

It also include some refactoring around loading and registering the flag as well as not relying on the `glob` package anymore plus some reorganization of the code itself

FIXES FRMW-2625
This commit is contained in:
Adrien de Peretti
2024-07-30 12:20:03 +00:00
committed by GitHub
parent 8a5751c115
commit 9b6de8c02d
21 changed files with 364 additions and 174 deletions
@@ -97,12 +97,15 @@ module.exports = {
Object.entries(env).forEach(([k, v]) => (process.env[k] = v)) Object.entries(env).forEach(([k, v]) => (process.env[k] = v))
} }
const { configModule } = getConfigFile(cwd, `medusa-config`) const {
featureFlagsLoader,
configLoader,
container,
pgConnectionLoader,
} = require("@medusajs/framework")
const featureFlagsLoader = const configModule = configLoader(cwd, `medusa-config`)
require("@medusajs/medusa/dist/loaders/feature-flags").default const featureFlagRouter = await featureFlagsLoader()
const featureFlagRouter = featureFlagsLoader(configModule)
const modelsLoader = require("@medusajs/medusa/dist/loaders/models").default const modelsLoader = require("@medusajs/medusa/dist/loaders/models").default
const entities = modelsLoader({}, { register: false }) const entities = modelsLoader({}, { register: false })
@@ -157,23 +160,11 @@ module.exports = {
force_modules_migration || force_modules_migration ||
featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key) featureFlagRouter.isFeatureEnabled(MedusaV2Flag.key)
) { ) {
const { container, pgConnectionLoader } = await import(
"@medusajs/framework"
)
const featureFlagLoader =
require("@medusajs/medusa/dist/loaders/feature-flags").default
const featureFlagRouter = await featureFlagLoader(configModule)
const pgConnection = pgConnectionLoader() const pgConnection = pgConnectionLoader()
container.register({ container.register({
[ContainerRegistrationKeys.CONFIG_MODULE]: asValue(configModule),
[ContainerRegistrationKeys.LOGGER]: asValue(console), [ContainerRegistrationKeys.LOGGER]: asValue(console),
[ContainerRegistrationKeys.MANAGER]: asValue(dbDataSource.manager), [ContainerRegistrationKeys.MANAGER]: asValue(dbDataSource.manager),
[ContainerRegistrationKeys.PG_CONNECTION]: asValue(pgConnection),
featureFlagRouter: asValue(featureFlagRouter),
}) })
instance.setPgConnection(pgConnection) instance.setPgConnection(pgConnection)
@@ -4,7 +4,7 @@ export async function configLoaderOverride(
entryDirectory: string, entryDirectory: string,
override: { clientUrl: string; debug?: boolean } override: { clientUrl: string; debug?: boolean }
) { ) {
const { configManager } = await import("@medusajs/framework/config") const { configManager } = await import("@medusajs/framework")
const { configModule, error } = getConfigFile< const { configModule, error } = getConfigFile<
ReturnType<typeof configManager.loadConfig> ReturnType<typeof configManager.loadConfig>
>(entryDirectory, "medusa-config.js") >(entryDirectory, "medusa-config.js")
@@ -6,21 +6,21 @@ export async function initDb({ env = {} }: { env?: Record<any, any> }) {
Object.entries(env).forEach(([k, v]) => (process.env[k] = v)) Object.entries(env).forEach(([k, v]) => (process.env[k] = v))
} }
const { configManager, pgConnectionLoader, container } = await import( const {
"@medusajs/framework" configManager,
) pgConnectionLoader,
logger,
container,
featureFlagsLoader,
} = await import("@medusajs/framework")
const configModule = configManager.config const configModule = configManager.config
const pgConnection = pgConnectionLoader() const pgConnection = pgConnectionLoader()
await featureFlagsLoader()
const featureFlagRouter =
require("@medusajs/medusa/dist/loaders/feature-flags").default(configModule)
container.register({ container.register({
[ContainerRegistrationKeys.CONFIG_MODULE]: asValue(configModule), [ContainerRegistrationKeys.LOGGER]: asValue(logger),
[ContainerRegistrationKeys.LOGGER]: asValue(console),
[ContainerRegistrationKeys.PG_CONNECTION]: asValue(pgConnection),
[ContainerRegistrationKeys.FEATURE_FLAG_ROUTER]: asValue(featureFlagRouter),
}) })
try { try {
@@ -379,13 +379,13 @@ export type ProjectConfigOptions = {
/** /**
* Configure the application's worker mode. * Configure the application's worker mode.
* *
* Workers are processes running separately from the main application. They're useful for executing long-running or resource-heavy tasks in the background, such as importing products. * Workers are processes running separately from the main application. They're useful for executing long-running or resource-heavy tasks in the background, such as importing products.
* *
* With a worker, these tasks are offloaded to a separate process. So, they won't affect the performance of the main application. * With a worker, these tasks are offloaded to a separate process. So, they won't affect the performance of the main application.
* *
* ![Diagram showcasing how the server and worker work together](https://res.cloudinary.com/dza7lstvk/image/upload/fl_lossy/f_auto/r_16/ar_16:9,c_pad/v1/Medusa%20Book/medusa-worker_klkbch.jpg?_a=BATFJtAA0) * ![Diagram showcasing how the server and worker work together](https://res.cloudinary.com/dza7lstvk/image/upload/fl_lossy/f_auto/r_16/ar_16:9,c_pad/v1/Medusa%20Book/medusa-worker_klkbch.jpg?_a=BATFJtAA0)
* *
* Medusa has three runtime modes: * Medusa has three runtime modes:
* *
* - Use `shared` to run the application in a single process. * - Use `shared` to run the application in a single process.
@@ -393,7 +393,7 @@ export type ProjectConfigOptions = {
* - Use `server` to run the application server only. * - Use `server` to run the application server only.
* *
* In production, it's recommended to deploy two instances: * In production, it's recommended to deploy two instances:
* *
* 1. One having the `workerMode` configuration set to `server`. * 1. One having the `workerMode` configuration set to `server`.
* 2. Another having the `workerMode` configuration set to `worker`. * 2. Another having the `workerMode` configuration set to `worker`.
* *
@@ -874,7 +874,7 @@ export type ConfigModule = {
* *
* ::: * :::
*/ */
featureFlags: Record<string, boolean | string> featureFlags: Record<string, boolean | string | Record<string, boolean>>
} }
export type PluginDetails = { export type PluginDetails = {
+1
View File
@@ -25,6 +25,7 @@ export * from "./get-set-difference"
export * from "./graceful-shutdown-server" export * from "./graceful-shutdown-server"
export * from "./group-by" export * from "./group-by"
export * from "./handle-postgres-database-error" export * from "./handle-postgres-database-error"
export * from "./is-truthy"
export * from "./is-big-number" export * from "./is-big-number"
export * from "./is-boolean" export * from "./is-boolean"
export * from "./is-date" export * from "./is-date"
@@ -0,0 +1,10 @@
/**
* Return true if the value is truthy and otherwise false
* @param val
*/
export function isTruthy(val: string | boolean | undefined): boolean {
if (typeof val === "string") {
return val.toLowerCase() === "true"
}
return !!val
}
@@ -32,6 +32,12 @@
"import": "./dist/database/index.js", "import": "./dist/database/index.js",
"require": "./dist/database/index.js", "require": "./dist/database/index.js",
"node": "./dist/database/index.js" "node": "./dist/database/index.js"
},
"./feature-flag": {
"types": "./dist/feature-flags/index.d.ts",
"import": "./dist/feature-flags/index.js",
"require": "./dist/feature-flags/index.js",
"node": "./dist/feature-flags/index.js"
} }
}, },
"engines": { "engines": {
@@ -73,6 +79,7 @@
"express-session": "^1.17.3", "express-session": "^1.17.3",
"ioredis": "^5.2.5", "ioredis": "^5.2.5",
"ioredis-mock": "8.4.0", "ioredis-mock": "8.4.0",
"medusa-telemetry": "^0.0.18",
"morgan": "^1.9.1" "morgan": "^1.9.1"
} }
} }
@@ -856,7 +856,7 @@ export type ConfigModule = {
* *
* ::: * :::
*/ */
featureFlags: Record<string, boolean | string> featureFlags: Record<string, boolean | string | Record<string, boolean>>
} }
export type PluginDetails = { export type PluginDetails = {
@@ -1,7 +1,8 @@
import { FileSystem } from "@medusajs/utils" import { FileSystem } from "@medusajs/utils"
import { join } from "path" import { join } from "path"
import { featureFlagsLoader } from "../feature-flag-loader"
import { configManager } from "../../config"
import loadFeatureFlags from "../feature-flags"
const filesystem = new FileSystem(join(__dirname, "__ff-test__")) const filesystem = new FileSystem(join(__dirname, "__ff-test__"))
const buildFeatureFlag = ( const buildFeatureFlag = (
@@ -29,6 +30,11 @@ describe("feature flags", () => {
process.env = { ...OLD_ENV } process.env = { ...OLD_ENV }
await filesystem.cleanup() await filesystem.cleanup()
configManager.loadConfig({
projectConfig: {} as any,
baseDir: filesystem.basePath,
})
}) })
afterAll(async () => { afterAll(async () => {
@@ -37,29 +43,33 @@ describe("feature flags", () => {
}) })
it("should load the flag from project", async () => { it("should load the flag from project", async () => {
configManager.loadConfig({
projectConfig: { featureFlags: { flag_1: false } },
baseDir: filesystem.basePath,
})
await filesystem.create("flags/flag-1.js", buildFeatureFlag("flag-1", true)) await filesystem.create("flags/flag-1.js", buildFeatureFlag("flag-1", true))
const flags = loadFeatureFlags( const flags = await featureFlagsLoader(join(filesystem.basePath, "flags"))
{ featureFlags: { flag_1: false } },
undefined,
join(filesystem.basePath, "flags")
)
expect(flags.isFeatureEnabled("flag_1")).toEqual(false) expect(flags.isFeatureEnabled("flag_1")).toEqual(false)
}) })
it("should load a nested + simple flag from project", async () => { it("should load a nested + simple flag from project", async () => {
configManager.loadConfig({
projectConfig: {
featureFlags: { test: { nested: true }, simpletest: true },
},
baseDir: filesystem.basePath,
})
await filesystem.create("flags/test.js", buildFeatureFlag("test", false)) await filesystem.create("flags/test.js", buildFeatureFlag("test", false))
await filesystem.create( await filesystem.create(
"flags/simpletest.js", "flags/simpletest.js",
buildFeatureFlag("simpletest", false) buildFeatureFlag("simpletest", false)
) )
const flags = loadFeatureFlags( const flags = await featureFlagsLoader(join(filesystem.basePath, "flags"))
{ featureFlags: { test: { nested: true }, simpletest: true } },
undefined,
join(filesystem.basePath, "flags")
)
expect(flags.isFeatureEnabled({ test: "nested" })).toEqual(true) expect(flags.isFeatureEnabled({ test: "nested" })).toEqual(true)
expect(flags.isFeatureEnabled("simpletest")).toEqual(true) expect(flags.isFeatureEnabled("simpletest")).toEqual(true)
@@ -71,11 +81,7 @@ describe("feature flags", () => {
buildFeatureFlag("flag-1", false) buildFeatureFlag("flag-1", false)
) )
const flags = loadFeatureFlags( const flags = await featureFlagsLoader(join(filesystem.basePath, "flags"))
{},
undefined,
join(filesystem.basePath, "flags")
)
expect(flags.isFeatureEnabled("flag_1")).toEqual(false) expect(flags.isFeatureEnabled("flag_1")).toEqual(false)
}) })
@@ -87,16 +93,17 @@ describe("feature flags", () => {
"flags/flag-1.js", "flags/flag-1.js",
buildFeatureFlag("flag-1", false) buildFeatureFlag("flag-1", false)
) )
const flags = loadFeatureFlags( const flags = await featureFlagsLoader(join(filesystem.basePath, "flags"))
{},
undefined,
join(filesystem.basePath, "flags")
)
expect(flags.isFeatureEnabled("flag_1")).toEqual(false) expect(flags.isFeatureEnabled("flag_1")).toEqual(false)
}) })
it("should load mix of flags", async () => { it("should load mix of flags", async () => {
configManager.loadConfig({
projectConfig: { featureFlags: { flag_2: false } },
baseDir: filesystem.basePath,
})
process.env.MEDUSA_FF_FLAG_3 = "true" process.env.MEDUSA_FF_FLAG_3 = "true"
await filesystem.create( await filesystem.create(
"flags/flag-1.js", "flags/flag-1.js",
@@ -111,11 +118,7 @@ describe("feature flags", () => {
buildFeatureFlag("flag-3", false) buildFeatureFlag("flag-3", false)
) )
const flags = loadFeatureFlags( const flags = await featureFlagsLoader(join(filesystem.basePath, "flags"))
{ featureFlags: { flag_2: false } },
undefined,
join(filesystem.basePath, "flags")
)
expect(flags.isFeatureEnabled("flag_1")).toEqual(false) expect(flags.isFeatureEnabled("flag_1")).toEqual(false)
expect(flags.isFeatureEnabled("flag_2")).toEqual(false) expect(flags.isFeatureEnabled("flag_2")).toEqual(false)
@@ -0,0 +1,129 @@
import {
ContainerRegistrationKeys,
FlagRouter,
isDefined,
isObject,
isString,
isTruthy,
objectFromStringPath,
} from "@medusajs/utils"
import { trackFeatureFlag } from "medusa-telemetry"
import { join, normalize } from "path"
import { logger } from "../logger"
import { FlagSettings } from "./types"
import { container } from "../container"
import { asFunction } from "awilix"
import { configManager } from "../config"
import { readdir } from "fs/promises"
export const featureFlagRouter = new FlagRouter({})
container.register(
ContainerRegistrationKeys.FEATURE_FLAG_ROUTER,
asFunction(() => featureFlagRouter)
)
const excludedFiles = ["index.js", "index.ts"]
const excludedExtensions = [".d.ts", ".d.ts.map", ".js.map"]
const flagConfig: Record<string, boolean | Record<string, boolean>> = {}
function registerFlag(
flag: FlagSettings,
projectConfigFlags: Record<string, string | boolean | Record<string, boolean>>
) {
flagConfig[flag.key] = isTruthy(flag.default_val)
let from
if (isDefined(process.env[flag.env_key])) {
from = "environment"
const envVal = process.env[flag.env_key]
// MEDUSA_FF_ANALYTICS="true"
flagConfig[flag.key] = isTruthy(process.env[flag.env_key])
const parsedFromEnv = isString(envVal) ? envVal.split(",") : []
// MEDUSA_FF_WORKFLOWS=createProducts,deleteProducts
if (parsedFromEnv.length > 1) {
flagConfig[flag.key] = objectFromStringPath(parsedFromEnv)
}
} else if (isDefined(projectConfigFlags[flag.key])) {
from = "project config"
// featureFlags: { analytics: "true" | true }
flagConfig[flag.key] = isTruthy(
projectConfigFlags[flag.key] as string | boolean
)
// featureFlags: { workflows: { createProducts: true } }
if (isObject(projectConfigFlags[flag.key])) {
flagConfig[flag.key] = projectConfigFlags[flag.key] as Record<
string,
boolean
>
}
}
if (logger && from) {
logger.info(
`Using flag ${flag.env_key} from ${from} with value ${
flagConfig[flag.key]
}`
)
}
if (flagConfig[flag.key]) {
trackFeatureFlag(flag.key)
}
featureFlagRouter.setFlag(flag.key, flagConfig[flag.key])
}
/**
* Load feature flags from a directory and from the already loaded config under the hood
* @param sourcePath
*/
export async function featureFlagsLoader(
sourcePath?: string
): Promise<FlagRouter> {
const { featureFlags: projectConfigFlags = {} } = configManager.config
if (!sourcePath) {
return featureFlagRouter
}
const flagDir = normalize(sourcePath)
await readdir(flagDir, { recursive: true, withFileTypes: true }).then(
async (files) => {
if (!files?.length) {
return
}
files.map(async (file) => {
if (file.isDirectory()) {
return await featureFlagsLoader(join(flagDir, file.name))
}
if (
excludedExtensions.some((ext) => file.name.endsWith(ext)) ||
excludedFiles.includes(file.name)
) {
return
}
const fileExports = await import(join(flagDir, file.name))
const featureFlag = fileExports.default
if (!featureFlag) {
return
}
registerFlag(featureFlag, projectConfigFlags)
return
})
}
)
return featureFlagRouter
}
@@ -0,0 +1,77 @@
import { isObject, isString } from "@medusajs/utils"
import { FeatureFlagsResponse, IFlagRouter } from "./types"
export class FlagRouter implements IFlagRouter {
private readonly flags: Record<string, boolean | Record<string, boolean>> = {}
constructor(flags: Record<string, boolean | Record<string, boolean>>) {
this.flags = flags
}
/**
* Check if a feature flag is enabled.
* There are two ways of using this method:
* 1. `isFeatureEnabled("myFeatureFlag")`
* 2. `isFeatureEnabled({ myNestedFeatureFlag: "someNestedFlag" })`
* We use 1. for top-level feature flags and 2. for nested feature flags. Almost all flags are top-level.
* An example of a nested flag is workflows. To use it, you would do:
* `isFeatureEnabled({ workflows: Workflows.CreateCart })`
* @param flag - The flag to check
* @return {boolean} - Whether the flag is enabled or not
*/
public isFeatureEnabled(
flag: string | string[] | Record<string, string>
): boolean {
if (isObject(flag)) {
const [nestedFlag, value] = Object.entries(flag)[0]
if (typeof this.flags[nestedFlag] === "boolean") {
return this.flags[nestedFlag] as boolean
}
return !!this.flags[nestedFlag]?.[value]
}
const flags = (Array.isArray(flag) ? flag : [flag]) as string[]
return flags.every((flag_) => {
if (!isString(flag_)) {
throw Error("Flag must be a string an array of string or an object")
}
return !!this.flags[flag_]
})
}
/**
* Sets a feature flag.
* Flags take two shapes:
* `setFlag("myFeatureFlag", true)`
* `setFlag("myFeatureFlag", { nestedFlag: true })`
* These shapes are used for top-level and nested flags respectively, as explained in isFeatureEnabled.
* @param key - The key of the flag to set.
* @param value - The value of the flag to set.
* @return {void} - void
*/
public setFlag(
key: string,
value: boolean | { [key: string]: boolean }
): void {
if (isObject(value)) {
const existing = this.flags[key]
if (!existing) {
this.flags[key] = value
return
}
this.flags[key] = { ...(this.flags[key] as object), ...value }
return
}
this.flags[key] = value
}
public listFlags(): FeatureFlagsResponse {
return Object.entries(this.flags || {}).map(([key, value]) => ({
key,
value,
}))
}
}
@@ -0,0 +1,3 @@
export * from "./types"
export * from "./feature-flag-loader"
export * from "./flag-router"
@@ -0,0 +1,32 @@
export interface IFlagRouter {
isFeatureEnabled: (key: string) => boolean
listFlags: () => FeatureFlagsResponse
}
/**
* @schema FeatureFlagsResponse
* type: array
* items:
* type: object
* required:
* - key
* - value
* properties:
* key:
* description: The key of the feature flag.
* type: string
* value:
* description: The value of the feature flag.
* type: boolean
*/
export type FeatureFlagsResponse = {
key: string
value: boolean | Record<string, boolean>
}[]
export type FlagSettings = {
key: string
description: string
env_key: string
default_val: boolean
}
@@ -3,3 +3,4 @@ export * from "./logger"
export * from "./http" export * from "./http"
export * from "./database" export * from "./database"
export * from "./container" export * from "./container"
export * from "./feature-flags"
+1 -1
View File
@@ -96,7 +96,7 @@ const main = async function ({ directory }) {
} }
try { try {
const container = initializeContainer(directory) const container = await initializeContainer(directory)
const configModule = container.resolve( const configModule = container.resolve(
ContainerRegistrationKeys.CONFIG_MODULE ContainerRegistrationKeys.CONFIG_MODULE
+1 -1
View File
@@ -45,7 +45,7 @@ const main = async function ({ directory }) {
validateInputArgs({ action, modules }) validateInputArgs({ action, modules })
const container = initializeContainer(directory) const container = await initializeContainer(directory)
const configModule = container.resolve( const configModule = container.resolve(
ContainerRegistrationKeys.CONFIG_MODULE ContainerRegistrationKeys.CONFIG_MODULE
@@ -1,94 +0,0 @@
import {
FlagRouter,
isObject,
isString,
objectFromStringPath,
} from "@medusajs/utils"
import glob from "glob"
import { isDefined } from "@medusajs/utils"
import { trackFeatureFlag } from "medusa-telemetry"
import path from "path"
import { FlagSettings } from "../../types/feature-flags"
import { Logger } from "../../types/global"
const isTruthy = (val: string | boolean | undefined): boolean => {
if (typeof val === "string") {
return val.toLowerCase() === "true"
}
return !!val
}
export const featureFlagRouter = new FlagRouter({})
export default (
configModule: {
featureFlags?: Record<string, string | boolean | Record<string, boolean>>
} = {},
logger?: Logger,
flagDirectory?: string
): FlagRouter => {
const { featureFlags: projectConfigFlags = {} } = configModule
const flagDir = path.join(flagDirectory || __dirname, "*.{j,t}s")
const supportedFlags = glob.sync(flagDir, {
ignore: ["**/index.js", "**/index.ts", "**/*.d.ts"],
})
const flagConfig: Record<string, boolean | Record<string, boolean>> = {}
for (const flag of supportedFlags) {
const flagSettings: FlagSettings = require(flag).default
if (!flagSettings) {
continue
}
flagConfig[flagSettings.key] = isTruthy(flagSettings.default_val)
let from
if (isDefined(process.env[flagSettings.env_key])) {
from = "environment"
const envVal = process.env[flagSettings.env_key]
// MEDUSA_FF_ANALYTICS="true"
flagConfig[flagSettings.key] = isTruthy(process.env[flagSettings.env_key])
const parsedFromEnv = isString(envVal) ? envVal.split(",") : []
// MEDUSA_FF_WORKFLOWS=createProducts,deleteProducts
if (parsedFromEnv.length > 1) {
flagConfig[flagSettings.key] = objectFromStringPath(parsedFromEnv)
}
} else if (isDefined(projectConfigFlags[flagSettings.key])) {
from = "project config"
// featureFlags: { analytics: "true" | true }
flagConfig[flagSettings.key] = isTruthy(
projectConfigFlags[flagSettings.key] as string | boolean
)
// featureFlags: { workflows: { createProducts: true } }
if (isObject(projectConfigFlags[flagSettings.key])) {
flagConfig[flagSettings.key] = projectConfigFlags[
flagSettings.key
] as Record<string, boolean>
}
}
if (logger && from) {
logger.info(
`Using flag ${flagSettings.env_key} from ${from} with value ${
flagConfig[flagSettings.key]
}`
)
}
if (flagConfig[flagSettings.key]) {
trackFeatureFlag(flagSettings.key)
}
}
for (const flag of Object.keys(flagConfig)) {
featureFlagRouter.setFlag(flag, flagConfig[flag])
}
return featureFlagRouter
}
@@ -4,20 +4,23 @@ import {
registerMedusaModule, registerMedusaModule,
} from "@medusajs/modules-sdk" } from "@medusajs/modules-sdk"
import { import {
ContainerRegistrationKeys, configManager,
createMedusaContainer, ConfigModule,
generateJwtToken, container,
} from "@medusajs/utils" featureFlagsLoader,
logger,
} from "@medusajs/framework"
import { ContainerRegistrationKeys, generateJwtToken } from "@medusajs/utils"
import { asValue } from "awilix" import { asValue } from "awilix"
import express from "express" import express from "express"
import querystring from "querystring" import querystring from "querystring"
import supertest from "supertest" import supertest from "supertest"
import apiLoader from "../../../../api" import apiLoader from "../../../../api"
import { getResolvedPlugins } from "../../../../helpers/resolve-plugins" import { getResolvedPlugins } from "../../../../helpers/resolve-plugins"
import featureFlagLoader, { featureFlagRouter } from "../../../../feature-flags"
import RoutesLoader from "../.." import RoutesLoader from "../.."
import { config } from "../mocks" import { config } from "../mocks"
import { MedusaContainer } from "@medusajs/types"
function asArray(resolvers) { function asArray(resolvers) {
return { return {
@@ -44,15 +47,18 @@ export const createServer = async (rootDir) => {
)[moduleKey] )[moduleKey]
}) })
const container = createMedusaContainer() configManager.loadConfig({
projectConfig: config as ConfigModule,
baseDir: rootDir,
})
container.registerAdd = function (name, registration) { container.registerAdd = function (this: MedusaContainer, name, registration) {
const storeKey = name + "_STORE" const storeKey = name + "_STORE"
if (this.registrations[storeKey] === undefined) { if (this.registrations[storeKey] === undefined) {
this.register(storeKey, asValue([])) this.register(storeKey, asValue([]))
} }
const store = this.resolve(storeKey) const store = this.resolve(storeKey) as Array<any>
if (this.registrations[name] === undefined) { if (this.registrations[name] === undefined) {
this.register(name, asArray(store)) this.register(name, asArray(store))
@@ -63,7 +69,6 @@ export const createServer = async (rootDir) => {
}.bind(container) }.bind(container)
container.register(ContainerRegistrationKeys.PG_CONNECTION, asValue({})) container.register(ContainerRegistrationKeys.PG_CONNECTION, asValue({}))
container.register("featureFlagRouter", asValue(featureFlagRouter))
container.register("configModule", asValue(config)) container.register("configModule", asValue(config))
container.register({ container.register({
logger: asValue({ logger: asValue({
@@ -87,11 +92,11 @@ export const createServer = async (rootDir) => {
const plugins = getResolvedPlugins(rootDir, config) || [] const plugins = getResolvedPlugins(rootDir, config) || []
featureFlagLoader(config) await featureFlagsLoader()
await moduleLoader({ container, moduleResolutions }) await moduleLoader({ container, moduleResolutions, logger })
app.use((req, res, next) => { app.use((req, res, next) => {
req.scope = container.createScope() req.scope = container.createScope() as MedusaContainer
next() next()
}) })
+9 -5
View File
@@ -3,7 +3,7 @@ import { ConfigModule, MedusaContainer, PluginDetails } from "@medusajs/types"
import { ContainerRegistrationKeys, promiseAll } from "@medusajs/utils" import { ContainerRegistrationKeys, promiseAll } from "@medusajs/utils"
import { asValue } from "awilix" import { asValue } from "awilix"
import { Express, NextFunction, Request, Response } from "express" import { Express, NextFunction, Request, Response } from "express"
import path from "path" import path, { join } from "path"
import requestIp from "request-ip" import requestIp from "request-ip"
import { v4 } from "uuid" import { v4 } from "uuid"
import adminLoader from "./admin" import adminLoader from "./admin"
@@ -12,10 +12,10 @@ import {
configLoader, configLoader,
container, container,
expressLoader, expressLoader,
featureFlagsLoader,
logger, logger,
pgConnectionLoader, pgConnectionLoader,
} from "@medusajs/framework" } from "@medusajs/framework"
import featureFlagsLoader from "./feature-flags"
import { registerJobs } from "./helpers/register-jobs" import { registerJobs } from "./helpers/register-jobs"
import { registerWorkflows } from "./helpers/register-workflows" import { registerWorkflows } from "./helpers/register-workflows"
import { getResolvedPlugins } from "./helpers/resolve-plugins" import { getResolvedPlugins } from "./helpers/resolve-plugins"
@@ -120,9 +120,13 @@ async function loadEntrypoints(
return shutdown return shutdown
} }
export function initializeContainer(rootDirectory: string): MedusaContainer { export async function initializeContainer(
rootDirectory: string
): Promise<MedusaContainer> {
const configModule = configLoader(rootDirectory, "medusa-config.js") const configModule = configLoader(rootDirectory, "medusa-config.js")
const featureFlagRouter = featureFlagsLoader(configModule, logger) const featureFlagRouter = await featureFlagsLoader(
join(__dirname, "feature-flags")
)
container.register({ container.register({
[ContainerRegistrationKeys.LOGGER]: asValue(logger), [ContainerRegistrationKeys.LOGGER]: asValue(logger),
@@ -143,7 +147,7 @@ export default async ({
app: Express app: Express
shutdown: () => Promise<void> shutdown: () => Promise<void>
}> => { }> => {
const container = initializeContainer(rootDirectory) const container = await initializeContainer(rootDirectory)
const configModule = container.resolve( const configModule = container.resolve(
ContainerRegistrationKeys.CONFIG_MODULE ContainerRegistrationKeys.CONFIG_MODULE
) )
@@ -32,6 +32,7 @@
"@medusajs/types": "^1.11.16", "@medusajs/types": "^1.11.16",
"cross-env": "^5.2.1", "cross-env": "^5.2.1",
"jest": "^29.7.0", "jest": "^29.7.0",
"medusa-test-utils": "^1.1.44",
"pg-god": "^1.0.12", "pg-god": "^1.0.12",
"rimraf": "^5.0.1", "rimraf": "^5.0.1",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
+20
View File
@@ -4619,6 +4619,7 @@ __metadata:
express-session: ^1.17.3 express-session: ^1.17.3
ioredis: ^5.2.5 ioredis: ^5.2.5
ioredis-mock: 8.4.0 ioredis-mock: 8.4.0
medusa-telemetry: ^0.0.18
morgan: ^1.9.1 morgan: ^1.9.1
rimraf: ^3.0.2 rimraf: ^3.0.2
tsc-alias: ^1.8.6 tsc-alias: ^1.8.6
@@ -4758,6 +4759,7 @@ __metadata:
awilix: ^8.0.0 awilix: ^8.0.0
cross-env: ^5.2.1 cross-env: ^5.2.1
jest: ^29.7.0 jest: ^29.7.0
medusa-test-utils: ^1.1.44
pg-god: ^1.0.12 pg-god: ^1.0.12
rimraf: ^5.0.1 rimraf: ^5.0.1
ts-node: ^10.9.1 ts-node: ^10.9.1
@@ -22704,6 +22706,24 @@ __metadata:
languageName: unknown languageName: unknown
linkType: soft linkType: soft
"medusa-telemetry@npm:^0.0.18":
version: 0.0.18
resolution: "medusa-telemetry@npm:0.0.18"
dependencies:
"@babel/runtime": ^7.22.10
axios: ^0.21.4
axios-retry: ^3.1.9
boxen: ^5.0.1
ci-info: ^3.2.0
configstore: 5.0.1
global: ^4.4.0
is-docker: ^2.2.1
remove-trailing-slash: ^0.1.1
uuid: ^8.3.2
checksum: 3571c3f578582667b3a48f2f3d9d27299a954fcc1e9165c2fdc441219d6faa0cd0f6be453f0edec783acea83500586c199a21d8b052e6ed3c815da85850fef7f
languageName: node
linkType: hard
"medusa-test-utils@^1.1.42, medusa-test-utils@^1.1.43, medusa-test-utils@^1.1.44, medusa-test-utils@workspace:*, medusa-test-utils@workspace:^, medusa-test-utils@workspace:packages/core/medusa-test-utils": "medusa-test-utils@^1.1.42, medusa-test-utils@^1.1.43, medusa-test-utils@^1.1.44, medusa-test-utils@workspace:*, medusa-test-utils@workspace:^, medusa-test-utils@workspace:packages/core/medusa-test-utils":
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "medusa-test-utils@workspace:packages/core/medusa-test-utils" resolution: "medusa-test-utils@workspace:packages/core/medusa-test-utils"