From b4c0f131b70ba950339c1ca4d81b5ce062a588a3 Mon Sep 17 00:00:00 2001 From: "Carlos R. L. Rodrigues" <37986729+carlos-r-l-rodrigues@users.noreply.github.com> Date: Mon, 1 Sep 2025 11:04:43 -0300 Subject: [PATCH] chore(framework,medusa): load custom flags before medusa config (#13312) * chore(framework,medusa): load custom flags before medusa config * test * test runner * changeset * check manager featureFlags * discover and register flags * rm comments * update changeset * changeset * use local cli * execute from local medusa command --------- Co-authored-by: Adrien de Peretti --- .changeset/good-meals-watch.md | 9 ++++ .github/workflows/test-cli-with-database.yml | 8 ++-- .../feature-flag/medusa-config.ts | 21 ++++++--- .../feature-flag/src/feature-flags/env-ff.ts | 8 ++++ .../src/feature-flags/feature-flag-loader.ts | 28 ++++++------ .../src/loaders/utils/load-internal.ts | 19 ++++---- .../discover-and-register-feature-flags.ts | 43 +++++++++++++++++++ .../core/utils/src/feature-flags/index.ts | 1 + .../src/medusa-test-runner-utils/config.ts | 14 +++++- packages/medusa/src/loaders/index.ts | 3 ++ 10 files changed, 118 insertions(+), 36 deletions(-) create mode 100644 .changeset/good-meals-watch.md create mode 100644 integration-tests/http/__fixtures__/feature-flag/src/feature-flags/env-ff.ts create mode 100644 packages/core/utils/src/feature-flags/discover-and-register-feature-flags.ts diff --git a/.changeset/good-meals-watch.md b/.changeset/good-meals-watch.md new file mode 100644 index 0000000000..31bec25324 --- /dev/null +++ b/.changeset/good-meals-watch.md @@ -0,0 +1,9 @@ +--- +"@medusajs/framework": patch +"@medusajs/medusa": patch +"@medusajs/test-utils": patch +"@medusajs/modules-sdk": patch +"@medusajs/utils": patch +--- + +chore(framework): load custom flags before medusa config diff --git a/.github/workflows/test-cli-with-database.yml b/.github/workflows/test-cli-with-database.yml index 36eccdbd37..3ef79e476d 100644 --- a/.github/workflows/test-cli-with-database.yml +++ b/.github/workflows/test-cli-with-database.yml @@ -71,22 +71,22 @@ jobs: working-directory: ../cli-test - name: Run migrations - run: npx medusa db:migrate + run: yarn medusa db:migrate working-directory: ../cli-test - name: Create admin user - run: npx medusa user -e test@test.com -p password -i admin_123 + run: yarn medusa user -e test@test.com -p password -i admin_123 working-directory: ../cli-test - name: Run development server - run: npx medusa develop & + run: yarn medusa develop & working-directory: ../cli-test - name: Testing development server uses: ./.github/actions/test-server - name: Starting medusa - run: npx medusa start & + run: yarn medusa start & working-directory: ../cli-test - name: Testing server diff --git a/integration-tests/http/__fixtures__/feature-flag/medusa-config.ts b/integration-tests/http/__fixtures__/feature-flag/medusa-config.ts index 800e7a7828..be00942ec2 100644 --- a/integration-tests/http/__fixtures__/feature-flag/medusa-config.ts +++ b/integration-tests/http/__fixtures__/feature-flag/medusa-config.ts @@ -1,3 +1,7 @@ +import { InputConfigModules } from "@medusajs/types" +import { FeatureFlag } from "@medusajs/utils" +import { EnvFeatureFlag } from "./src/feature-flags/env-ff" + const { defineConfig } = require("@medusajs/framework/utils") const DB_HOST = process.env.DB_HOST @@ -8,6 +12,16 @@ const DB_URL = `postgres://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOST}/${DB_NAME}` process.env.DATABASE_URL = DB_URL +const modules = [] as InputConfigModules + +// The custom feature is available here and has default value set to true +if (FeatureFlag.isFeatureEnabled(EnvFeatureFlag.key)) { + modules.push({ + key: "custom", + resolve: "src/modules/custom", + }) +} + module.exports = defineConfig({ admin: { disable: true, @@ -17,10 +31,5 @@ module.exports = defineConfig({ jwtSecret: "secret", }, }, - modules: [ - { - key: "custom", - resolve: "src/modules/custom", - }, - ], + modules, }) diff --git a/integration-tests/http/__fixtures__/feature-flag/src/feature-flags/env-ff.ts b/integration-tests/http/__fixtures__/feature-flag/src/feature-flags/env-ff.ts new file mode 100644 index 0000000000..6557a6bf1c --- /dev/null +++ b/integration-tests/http/__fixtures__/feature-flag/src/feature-flags/env-ff.ts @@ -0,0 +1,8 @@ +import { FlagSettings } from "@medusajs/framework/feature-flags" + +export const EnvFeatureFlag: FlagSettings = { + key: "env_ff", + default_val: true, + env_key: "ENV_FF", + description: "Environment feature flag", +} diff --git a/packages/core/framework/src/feature-flags/feature-flag-loader.ts b/packages/core/framework/src/feature-flags/feature-flag-loader.ts index 4c153ac69a..5d8e62b0ce 100644 --- a/packages/core/framework/src/feature-flags/feature-flag-loader.ts +++ b/packages/core/framework/src/feature-flags/feature-flag-loader.ts @@ -1,16 +1,15 @@ import { trackFeatureFlag } from "@medusajs/telemetry" import { ContainerRegistrationKeys, - discoverFeatureFlagsFromDir, + discoverAndRegisterFeatureFlags, FeatureFlag, FlagRouter, - registerFeatureFlag, } from "@medusajs/utils" import { asFunction } from "awilix" import { normalize } from "path" import { configManager } from "../config" import { container } from "../container" -import { FlagSettings } from "./types" +import { logger as defaultLogger } from "../logger" container.register( ContainerRegistrationKeys.FEATURE_FLAG_ROUTER, @@ -24,7 +23,11 @@ container.register( export async function featureFlagsLoader( sourcePath?: string ): Promise { - const { featureFlags: projectConfigFlags = {}, logger } = configManager.config + const confManager = !!configManager.baseDir + ? configManager.config + : { featureFlags: {}, logger: defaultLogger } + + const { featureFlags: projectConfigFlags = {}, logger } = confManager if (!sourcePath) { return FeatureFlag @@ -32,16 +35,13 @@ export async function featureFlagsLoader( const flagDir = normalize(sourcePath) - const discovered = await discoverFeatureFlagsFromDir(flagDir) - for (const def of discovered) { - registerFeatureFlag({ - flag: def as FlagSettings, - projectConfigFlags, - router: FeatureFlag, - logger, - track: (key) => trackFeatureFlag(key), - }) - } + await discoverAndRegisterFeatureFlags({ + flagDir, + projectConfigFlags, + router: FeatureFlag, + logger, + track: (key) => trackFeatureFlag(key), + }) return FeatureFlag } diff --git a/packages/core/modules-sdk/src/loaders/utils/load-internal.ts b/packages/core/modules-sdk/src/loaders/utils/load-internal.ts index 04c4d62a12..cc16b75f44 100644 --- a/packages/core/modules-sdk/src/loaders/utils/load-internal.ts +++ b/packages/core/modules-sdk/src/loaders/utils/load-internal.ts @@ -17,7 +17,7 @@ import { ContainerRegistrationKeys, createMedusaContainer, defineJoinerConfig, - discoverFeatureFlagsFromDir, + discoverAndRegisterFeatureFlags, DmlEntity, dynamicImport, FeatureFlag, @@ -28,7 +28,6 @@ import { MedusaModuleType, Modules, ModulesSdkUtils, - registerFeatureFlag, stringifyCircular, toMikroOrmEntities, } from "@medusajs/utils" @@ -579,7 +578,6 @@ export async function loadResources({ } const flagDir = resolve(normalizedPath) - const discovered = await discoverFeatureFlagsFromDir(flagDir, 1) const configModule = container.resolve( ContainerRegistrationKeys.CONFIG_MODULE, @@ -588,14 +586,13 @@ export async function loadResources({ } ) as ConfigModule - for (const def of discovered) { - registerFeatureFlag({ - flag: def, - projectConfigFlags: configModule?.featureFlags ?? {}, - router: FeatureFlag, - logger, - }) - } + await discoverAndRegisterFeatureFlags({ + flagDir, + projectConfigFlags: configModule?.featureFlags ?? {}, + router: FeatureFlag, + logger, + maxDepth: 1, + }) const [moduleService, services, models, repositories] = await Promise.all([ dynamicImport(modulePath).then((moduleExports) => { diff --git a/packages/core/utils/src/feature-flags/discover-and-register-feature-flags.ts b/packages/core/utils/src/feature-flags/discover-and-register-feature-flags.ts new file mode 100644 index 0000000000..aef21c6130 --- /dev/null +++ b/packages/core/utils/src/feature-flags/discover-and-register-feature-flags.ts @@ -0,0 +1,43 @@ +import { FlagSettings, Logger } from "@medusajs/types" +import { discoverFeatureFlagsFromDir } from "./discover-feature-flags" +import { FlagRouter } from "./flag-router" +import { registerFeatureFlag } from "./register-flag" + +export interface DiscoverAndRegisterOptions { + flagDir: string + projectConfigFlags?: Record + router: FlagRouter + logger?: Logger + track?: (key: string) => void + maxDepth?: number +} + +/** + * Utility function to discover and register feature flags from a directory + */ +export async function discoverAndRegisterFeatureFlags( + options: DiscoverAndRegisterOptions +): Promise { + const { + flagDir, + projectConfigFlags = {}, + router, + logger, + track, + maxDepth, + } = options + + const discovered = await discoverFeatureFlagsFromDir(flagDir, maxDepth) + + for (const def of discovered) { + const registerOptions: Parameters[0] = { + flag: def as FlagSettings, + projectConfigFlags, + router, + logger, + track, + } + + registerFeatureFlag(registerOptions) + } +} diff --git a/packages/core/utils/src/feature-flags/index.ts b/packages/core/utils/src/feature-flags/index.ts index 41e69eddfc..21fe40540b 100644 --- a/packages/core/utils/src/feature-flags/index.ts +++ b/packages/core/utils/src/feature-flags/index.ts @@ -1,3 +1,4 @@ export * from "./discover-feature-flags" +export * from "./discover-and-register-feature-flags" export * from "./flag-router" export * from "./register-flag" diff --git a/packages/medusa-test-utils/src/medusa-test-runner-utils/config.ts b/packages/medusa-test-utils/src/medusa-test-runner-utils/config.ts index ece9f505da..ebf4c2419a 100644 --- a/packages/medusa-test-utils/src/medusa-test-runner-utils/config.ts +++ b/packages/medusa-test-utils/src/medusa-test-runner-utils/config.ts @@ -1,4 +1,8 @@ -import { getConfigFile } from "@medusajs/framework/utils" +import { + FeatureFlag, + getConfigFile, + discoverAndRegisterFeatureFlags, +} from "@medusajs/framework/utils" export async function configLoaderOverride( entryDirectory: string, @@ -6,6 +10,14 @@ export async function configLoaderOverride( ) { const { configManager } = await import("@medusajs/framework/config") const { logger } = await import("@medusajs/framework") + + await discoverAndRegisterFeatureFlags({ + flagDir: entryDirectory, + projectConfigFlags: {}, + router: FeatureFlag, + logger, + }) + const { configModule, error } = await getConfigFile< ReturnType >(entryDirectory, "medusa-config") diff --git a/packages/medusa/src/loaders/index.ts b/packages/medusa/src/loaders/index.ts index 164095c912..04860082f2 100644 --- a/packages/medusa/src/loaders/index.ts +++ b/packages/medusa/src/loaders/index.ts @@ -139,7 +139,10 @@ export async function initializeContainer( skipDbConnection?: boolean } ): Promise { + // custom flags from medusa project + await featureFlagsLoader(rootDirectory) const configDir = await configLoader(rootDirectory, "medusa-config") + // core flags await featureFlagsLoader(join(__dirname, "..")) const customLogger = configDir.logger ?? defaultLogger