The main motivation for doing feature toggling is to decouple the process for deploying code to production and releasing new features. This helps reducing risk, and allow us to easily manage which features to enable
Feature toggles decouple deployment of code from release of new features. For more insight, read Martin Fowler's and Pete Hodgson's Feature flag, A/B testing article
The fabric8 feature toggles is based on unleash upstream open source project.
- fabric8-toggles is where the server and its DB run.
- fabric8-toggles-service is the service used by fabric8-ui to turn on/off and roll out features.
fabric8-ui
targetsfabric8-toggles-service
. - fabric8-ui in feature-flag module is where the angular components and services for toggles are.
Admin consoles are available here:
NOTE: atm, we don't have any sync mechanism between the 2 instances. This is 2 separate DB.
To login to toggles admin console, we use GitHub authentication and we provide some authorisation checking that your user is part of some organisation.
- You will be prompted to authenticate though GH.
- If not yet part of the RHDT organization, please open a pull request to the UserDB (under VPN; Check DevGuide for details)
- If you're already part of the RHDT organization but not a member of either prod-preview or prod team, contact some admins in mattermost #fabric8-platform and ask them to add you. You will need to be an internal user to be granted access. Once granted, try again.
Search for Sign out
in the admin console. You will be logged out of fabric8-toggles admin ui but not logged out of GH. If you want to login with a different GH account, logout of GH.
- You can import the lib in your application:
npm install ngx-feature-flag
- Pre-requisites
- node v8.9.1+ (required by anuglar-cli 6+)
- npm 5.5.1
This angular library is built using angular-cli's workspace. The main application is the demo app. The library source is under /project/ngx-feature-flag
- Install the dependencies
npm i
- Build library & demo app
npm run build
- Run the library tests
npm test
NOTE: to run the library test in watch mode:
npm run test:lib:dev
- Fix lint errors (package import for ex)
npm run lint:fix
npm i
npm run build
npm start
Open your browser on http://localhost:4200/
Use the service directly to request the Feature.
Take the new launchpad wizard as an example:
- In admin ui, add your new feature with a
featureName
(hereAppLauncher
), associate aenableByLevel
strategy, enter thelevel
parameter (internal
,experimental
,beta
,released
). - routing file: add
FeatureFlagResolver
(responsible to query thefabric8-toggles-service
, also specify the name of your feature infeatureName
. HereAppLauncher
should exactly match the feature name (ie: this is the external key between fabric8-ui and fabric8-toggles) defined in fabric8-toggles admin UI.
{
path: '_applauncher',
resolve: {
featureFlagConfig: FeatureFlagResolver
},
loadChildren: './app-launcher/app-launcher.module#AppLauncherModule',
data: {
title: 'App Launcher',
featureName: 'AppLauncher'
}
},
Atm we have 2 rollout strategies to use with fabric8-toggles-service
:
- Enable by level
enableByLevel
is the most commonly used. It allows to tag a feature for a particular level. Feature flag level are:internal
,experimental
,beta
,released
.internal
will only show it to accounts with an @redhat.com email. It will have a notification on the screen: "This feature is open to Red Hat users only. You can manage pre-production features on your profile page."experimental
will be "This feature is experimental. You can manage pre-production features on your profile page."beta
notification will be "This feature is in beta. You can manage pre-production features on your profile page."released
makes the feature default for everyone.
NOTE: Anonymous user can only see the
released
state. For other level, you need to be logged-in.
- Enable by emails.
enableByEmails
is used for a work in progress feature. You can create a feature with the strategyemableByEmails
and then list the different users' emails of the other developers you want to collaborate with on this WIP feature.
For example, while working on new dashboard
feature, Adam wanted to share the new dashboard only with a group of UI developers. Easy way is to used the enableByEmails
. Once the feature is ready, Adam could remove the strategy enableByEmails
and replace it with a strategy enableByLevel
with a level of internal
to start with.
ngx-feature-flag
provides 2 components to help you hide and show your features under feature-flag.
TIP: As a best practise, if you know a page of your app will have several feature flag, group them per page. We use a naming convention:
Page.MyFeature
wherePage
is the name you use in the FeatureFlagResolver.
For example, let's hide EnvironmentWidgetComponent
which is shown in a Environment
page. Follow the best practices and name your feature Environment.Test
. If your don't want to "group" features, you can call it simply Test
.
- In unleash admin ui, add your new feature with a
featureName
(hereEnvironment.Test
), associate aenableByLevel
strategy, enter thelevel
parameter (internal
,experimental
,beta
,released
). If this level (for ex: experimental) is below your user-consent level (for ex: beta), you won't see the component. - In your routing file:
add
FeatureFlagResolver
(responsible to query thefabric8-toggles-service
, also specify the name of your feature infeatureName
. HereEnvironment.Test
should exactly match the feature name (ie: this is the external key between fabric8-ui and fabric8-toggles) defined in fabric8-toggles admin UI. Note: this step is optional if you do not group your component per page. - In your page or component template:
<f8-feature-toggle featureName="Environment.Test" [userLevel]="user"></f8-feature-toggle>
<ng-template #user>
YOUR NEW HTML
</ng-template>
- In your Module, import
FeatureFlagModule
:
@NgModule({
imports: [CommonModule, FormsModule, RouterModule, MomentModule, FeatureFlagModule ],
declarations: [EnvironmentWidgetComponent],
exports: [EnvironmentWidgetComponent]
})
export class EnvironmentWidgetModule { }
NOTE: the component is hidden but still initialized.
Similar to precedent section. You put the new code in the HTML element that content user-level
and the old code in default-level
.
For example:
<f8-feature-toggle featureName="Analyze.newSpaceDashboard" [userLevel]="user" [defaultLevel]="default"></f8-feature-toggle>
<ng-template #user>
YOUR NEW HTML
</ng-template>
<ng-template #default>
YOUR OLD HTML
</ng-template>
This use case could be while the feature is under development and you want to make sure loading the component will not break the whole UI.
For example, let's dynamically load EnvironmentWidgetComponent
to carry-on with the same example. but now we don't want to just hide the component, we want to make sure the component code is not loaded at all (this could be useful if for some reasons the component code may cause runtime failure).
- In unleash admin ui, add your new feature with a
featureName
(hereEnvironment.Test
), associate aenableByLevel
strategy, enter thelevel
parameter (internal
,experimental
,beta
,released
). If this level (for ex: experimental) is below your user-consent level (for ex: beta), you won't see the component. - In
analyze-overview.component.html
template, replace<fabric8-environment-widget />
by<f8-feature-toggle-loader featureName="Environment.Test"></f8-feature-toggle-loader>
whereEnvironment.Test
is the name of the feature. Choose an meaningful name like:env.widget
. For test purpose here we reuseEnvironment.Test
. - In the module associated to your dynamically loaded component add an
entryComponents
. For ex, inanalyze-overview.module.ts
:
@NgModule({
imports: [
CommonModule,
AnalyzeOverviewRoutingModule,
FeatureFlagModule,
FormsModule,
EditSpaceDescriptionWidgetModule,
AnalyticalReportWidgetModule,
CreateWorkItemWidgetModule,
AddCodebaseWidgetModule,
PipelinesWidgetModule,
EnvironmentWidgetModule,
ForgeWizardModule,
ModalModule.forRoot()
],
declarations: [AnalyzeOverviewComponent],
entryComponents: [EnvironmentWidgetComponent]
})
export class AnalyzeOverviewModule {
constructor(http: Http) { }
}
Because we use Feature-flag, we've also added FeatureFlagModule
in the imports of the module.
Note: For more information on
entryComponents
read angular docs on entryComponents and dynamic component load.
- Tell fabric8-ui which feature match which component. In
src/app/feature-flag.mapping.ts
, add acase
inconvertFeatureNameToComponent
method:
convertFeatureNameToComponent(name: string): Type<any> {
switch (name) {
case 'Environment.Test': {
return EnvironmentWidgetComponent;
}
default: {
return null;
}
}
}
Better than just usage you want to see it in action? run the demo:
- with direct access to prod-preview uncomment
- with mock data (default option)
npm run build:demo
npm run start:demo
Go to http://localhost:8001
-
pre-requisites Login to npmjs central repo with your credential (you should be owner of the library).
-
build
ngx-feature-flag
as a npm library
npm run build
- publish
npm publish dist
Note: semantic release are done via fabric8cd using
semantic-release