Feat(medusa): implement feature flags (#1768)

* feat: add feature flag loading in projects

* fix: make feature flag consume itself

* fix: rename container registration to featureFlagRouter

* fix: refactor

* behavioral feature flags

* add environment to server

* limit "useTemplateDb" to non feature flagged migrations

* filter migrations and entities according to those which are enabled in the environment

* run only migrations that are enabled when running 'medusa migrations run'

* add logging to the featureflag loader

* initial implementation of featureFlagEntity

* column descriptors

* initial startServerWithEnv (to be refactored)

* update commands

* final touches

* update loaders to fix unit tests

* enable all batch job tests

* update seed method

* add api test capabilities

* revert batch job test

* revert formatting changes

* pr feedback

* pr feedback

* remove unused imports

* rename feature flag decorators

* pr feedback

Co-authored-by: Sebastian Rindom <skrindom@gmail.com>
This commit is contained in:
Philip Korsholm
2022-07-04 15:39:30 +02:00
committed by GitHub
co-authored by Sebastian Rindom
parent f0704a7e17
commit 41681b45b1
19 changed files with 527 additions and 58 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
import { getConfigFile } from "medusa-core-utils"
import path from "path"
import { Column, ColumnOptions, ColumnType } from "typeorm"
import path from "path"
import { getConfigFile } from "medusa-core-utils"
const pgSqliteTypeMapping: { [key: string]: ColumnType } = {
increment: "rowid",
@@ -0,0 +1,66 @@
import { getConfigFile } from "medusa-core-utils"
import { Column, ColumnOptions, Entity, EntityOptions } from "typeorm"
import featureFlagsLoader from "../loaders/feature-flags"
import path from "path"
import { ConfigModule } from "../types/global"
import { FlagRouter } from "./flag-router"
export function FeatureFlagColumn(
featureFlag: string,
columnOptions: ColumnOptions
): PropertyDecorator {
const featureFlagRouter = getFeatureFlagRouter()
if (!featureFlagRouter.isFeatureEnabled(featureFlag)) {
return (): void => {
// noop
}
}
return Column(columnOptions)
}
export function FeatureFlagDecorators(
featureFlag: string,
decorators: PropertyDecorator[]
): PropertyDecorator {
const featureFlagRouter = getFeatureFlagRouter()
if (!featureFlagRouter.isFeatureEnabled(featureFlag)) {
return (): void => {
// noop
}
}
// eslint-disable-next-line @typescript-eslint/ban-types
return (target: Object, propertyKey: string | symbol): void => {
decorators.forEach((decorator) => {
decorator(target, propertyKey)
})
}
}
export function FeatureFlagEntity(
featureFlag: string,
name?: string,
options?: EntityOptions
): ClassDecorator {
// eslint-disable-next-line @typescript-eslint/ban-types
return function (target: Function): void {
target["isFeatureEnabled"] = function (): boolean {
const featureFlagRouter = getFeatureFlagRouter()
// const featureFlagRouter = featureFlagsLoader(configModule)
return featureFlagRouter.isFeatureEnabled(featureFlag)
}
Entity(name, options)(target)
}
}
function getFeatureFlagRouter(): FlagRouter {
const { configModule } = getConfigFile(
path.resolve("."),
`medusa-config`
) as { configModule: ConfigModule }
return featureFlagsLoader(configModule)
}
+17
View File
@@ -0,0 +1,17 @@
import { IFlagRouter } from "../types/feature-flags"
export class FlagRouter implements IFlagRouter {
private flags: Record<string, boolean> = {}
constructor(flags: Record<string, boolean>) {
this.flags = flags
}
public isFeatureEnabled(key: string): boolean {
return !!this.flags[key]
}
public setFlag(key: string, value = true): void {
this.flags[key] = value
}
}