diff --git a/.env.example b/.env.example index 29c63439c..ba6e7d1a5 100644 --- a/.env.example +++ b/.env.example @@ -22,3 +22,16 @@ # FLOGO_LIB_VERSION=latest ## only if FLOGO_LIB_VERSION is not set: # FLOGO_WEB_LIB_VERSION=latest + +### Feature toggles +### Enable/Disable features +### Syntax: FLOGO_FEATURES_ { + context.body = config.features; + }); + return router; } diff --git a/apps/server/src/config/app-config.js b/apps/server/src/config/app-config.js index 609177877..2b9a54b98 100644 --- a/apps/server/src/config/app-config.js +++ b/apps/server/src/config/app-config.js @@ -1,6 +1,8 @@ import * as path from 'path'; import './load-env'; +import { parseFeatureToggles } from './parse-features-toggles'; +const features = parseFeatureToggles(process.env); const rootPath = path.resolve(__dirname, '..'); const PUBLIC_DIR = @@ -41,6 +43,7 @@ const config = { logsPath: path.join(LOCAL_DIR, 'logs'), logLevel, localPath: LOCAL_DIR, + features, defaultAppJsonPath: path.join(rootPath, 'config', 'sample-app.json'), defaultFlogoDescriptorPath: process.env.FLOGO_WEB_DEFAULT_DESCRIPTOR || diff --git a/apps/server/src/config/index.ts b/apps/server/src/config/index.ts new file mode 100644 index 000000000..3c27ac4ec --- /dev/null +++ b/apps/server/src/config/index.ts @@ -0,0 +1,3 @@ +export { config } from './app-config'; + +export { isFeatureEnabled } from './is-feature-enabled'; diff --git a/apps/server/src/config/is-feature-enabled.ts b/apps/server/src/config/is-feature-enabled.ts new file mode 100644 index 000000000..4a1597b3d --- /dev/null +++ b/apps/server/src/config/is-feature-enabled.ts @@ -0,0 +1,5 @@ +import { config } from './app-config'; + +export function isFeatureEnabled(featureName: string) { + return config.features && config.features[featureName]; +} diff --git a/apps/server/src/config/parse-features-toggles.ts b/apps/server/src/config/parse-features-toggles.ts new file mode 100644 index 000000000..c888faece --- /dev/null +++ b/apps/server/src/config/parse-features-toggles.ts @@ -0,0 +1,13 @@ +const FEATURES_PREFIX = 'FLOGO_FEATURES_'; +const FEATURES_PREFIX_LENGTH = FEATURES_PREFIX.length; + +function featuresReducer(features, [envKey, envValue]: [string, any]) { + if (envKey.startsWith(FEATURES_PREFIX)) { + features[envKey.slice(FEATURES_PREFIX_LENGTH)] = envValue; + } + return features; +} + +export function parseFeatureToggles(fromEnv: NodeJS.ProcessEnv) { + return Object.entries(fromEnv).reduce(featuresReducer, {}); +} diff --git a/libs/lib-client/core/src/services/feature-toggles.ts b/libs/lib-client/core/src/services/feature-toggles.ts new file mode 100644 index 000000000..32139d3f3 --- /dev/null +++ b/libs/lib-client/core/src/services/feature-toggles.ts @@ -0,0 +1,36 @@ +import { Injectable, APP_INITIALIZER, FactoryProvider } from '@angular/core'; +import { tap, catchError } from 'rxjs/operators'; +import { EMPTY } from 'rxjs'; +import { RestApiService } from './restapi'; + +@Injectable({ providedIn: 'root' }) +export class FeatureToggleService { + features: { [featureName: string]: any }; + + isFeatureEnabled(featureName: string) { + return this.features && !!this.features[featureName]; + } +} + +// needed to be exported because of AoT +export function featureToggleServiceFactory( + apiService: RestApiService, + featureService: FeatureToggleService +) { + return () => { + return apiService + .get('_/features') + .pipe( + tap(features => (featureService.features = features)), + catchError(() => EMPTY) + ) + .toPromise(); + }; +} + +export const FEATURE_TOGGLES_APP_INITIALIZER: FactoryProvider = { + provide: APP_INITIALIZER, + useFactory: featureToggleServiceFactory, + deps: [RestApiService, FeatureToggleService], + multi: true, +}; diff --git a/libs/lib-client/core/src/services/index.ts b/libs/lib-client/core/src/services/index.ts index 37277bdee..6c0019357 100644 --- a/libs/lib-client/core/src/services/index.ts +++ b/libs/lib-client/core/src/services/index.ts @@ -12,3 +12,5 @@ export * from './service-url-config.model'; export * from './app-resource.service'; export * from './log.service'; export * from './activated-resource-route'; + +export * from './feature-toggles';