Skip to content
This repository has been archived by the owner on Jun 10, 2021. It is now read-only.

Commit

Permalink
Merge branch 'feature/ch310/kill-switch-for-flags_2' into 'master'
Browse files Browse the repository at this point in the history
Kill Switch for Flags

See merge request bullet-train/bullet-train-frontend!96
  • Loading branch information
kyle-ssg committed Dec 5, 2020
2 parents 5bb12b5 + 4923905 commit bc2fb88
Show file tree
Hide file tree
Showing 13 changed files with 89 additions and 45 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ If you have any questions about our projects you can email <a href="mailto:proje

## Running locally against your own Flagsmith API instance

We use Flagsmith to manage features we rollout, if you are using your own Flagsmith environment (i.e. by editing project_x.js-> bulletTrain) then you will need to have a replica of our flags.
We use Flagsmith to manage features we rollout, if you are using your own Flagsmith environment (i.e. by editing project_x.js-> flagsmith) then you will need to have a replica of our flags.

A list of the flags and remote config we're currently using in production can be found here https://gist.github.com/kyle-ssg/55f3b869c28bdd13c02c6688bc76c67f.

Expand Down
1 change: 1 addition & 0 deletions common/dispatcher/action-constants.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const Actions = Object.assign({}, require('./base/_action-constants'), {
'ACCEPT_INVITE': 'ACCEPT_INVITE',
'CHANGE_USER_FLAG': 'CHANGE_USER_FLAG',
'CREATE_ENV': 'CREATE_ENV',
'CREATE_FLAG': 'CREATE_FLAG',
'CREATE_ORGANISATION': 'CREATE_ORGANISATION',
Expand Down
5 changes: 1 addition & 4 deletions common/dispatcher/app-actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -238,13 +238,10 @@ const AppActions = Object.assign({}, require('./base/_app-actions'), {
...params,
});
},
changeUserFlag(identity, identityFlag, environmentId, payload) {
changeUserFlag(identity) {
Dispatcher.handleViewAction({
actionType: Actions.CHANGE_USER_FLAG,
identity,
identityFlag,
environmentId,
payload,
});
},
selectOrganisation(id) {
Expand Down
11 changes: 6 additions & 5 deletions common/project.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
module.exports = global.Project = {
api: 'https://api-staging.flagsmith.com/api/v1/',
api: 'https://api-dev.flagsmith.com/api/v1/',
flagsmithClientAPI: 'https://api.flagsmith.com/api/v1/',
flagsmith: 'ENktaJnfLVbLifybz34JmX', // This is our Bullet Train API key - Bullet Train runs on Bullet Train!
env: 'staging', // This is used for Sentry tracking
ga: 'UA-120237963-7', // This is our Google Analytics key
maintenance: false, // trigger maintenance mode
flagsmith: '8KzETdDeMY7xkqkSkY3Gsg', // This is our Bullet Train API key - Bullet Train runs on Bullet Train!
debug: false,
delighted: true, // determines whether to shw delighted feedback widget
env: 'dev', // This is used for Sentry tracking
ga: 'UA-120237963-3', // This is our Google Analytics key
maintenance: false, // trigger maintenance mode
demoAccount: {
email: '[email protected]',
password: 'demo_account',
Expand Down
4 changes: 2 additions & 2 deletions common/providers/ConfigProvider.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { Component } from 'react';
import bulletTrain from 'bullet-train-client';
import flagsmith from 'flagsmith';
import ConfigStore from '../stores/config-store';

module.exports = (WrappedComponent) => {
Expand All @@ -24,7 +24,7 @@ module.exports = (WrappedComponent) => {

render() {
const { isLoading,error } = this.state;
const { getValue, hasFeature } = bulletTrain;
const { getValue, hasFeature } = flagsmith;

return (
<WrappedComponent
Expand Down
5 changes: 3 additions & 2 deletions common/stores/config-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const controller = {
} else {
store.changed();
}
store.model = bulletTrain.getAllFlags();
store.model = flagsmith.getAllFlags();
},
};

Expand All @@ -36,10 +36,11 @@ store.dispatcherIndex = Dispatcher.register(store, (payload) => {
}
});

bulletTrain.init({
flagsmith.init({
environmentID: Project.flagsmith,
onChange: controller.loaded,
api: Project.flagsmithClientAPI,
enableAnalytics: true,
}).catch(() => {
controller.onError();
});
Expand Down
7 changes: 4 additions & 3 deletions common/stores/identity-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,13 @@ const controller = {
store.saved();
});
},
changeUserFlag({identity, identityFlag, environmentId, payload}) {
changeUserFlag({ identity, identityFlag, environmentId, payload, onSuccess }) {
store.saving();
API.trackEvent(Constants.events.TOGGLE_USER_FEATURE);

const prom = data.put(`${Project.api}environments/${environmentId}/identities/${identity}/featurestates/${identityFlag}/`, Object.assign({}, payload))
const prom = data.put(`${Project.api}environments/${environmentId}/identities/${identity}/featurestates/${identityFlag}/`, Object.assign({}, payload));
prom.then((res) => {
if (onSuccess) onSuccess();
store.saved();
});
},
Expand Down Expand Up @@ -141,7 +142,7 @@ store.dispatcherIndex = Dispatcher.register(store, (payload) => {
controller.deleteIdentityTrait(action.envId, action.identity, action.id);
break;
case Actions.CHANGE_USER_FLAG:
controller.changeUserFlag(action.identity, action.identityFlag, action.environmentId, action.payload);
controller.changeUserFlag(action.identity);
break;
default:
}
Expand Down
10 changes: 5 additions & 5 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"babel-plugin-transform-react-remove-prop-types": "0.4.18",
"babel-runtime": "6.26.0",
"bootstrap": "4.0.0-alpha.3",
"bullet-train-client": "0.0.83",
"flagsmith": "1.1.0",
"bullet-train-rules-engine": "0.0.7",
"chai": "4.2.0",
"classnames": "2.2.6",
Expand Down
1 change: 0 additions & 1 deletion tests/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ const clearDown = function (browser, done) {
}
}
if (token) {
console.log(`${Project.api}e2etests/teardown/`, token.trim());
fetch(`${Project.api}e2etests/teardown/`, {
method: 'POST',
headers: {
Expand Down
72 changes: 58 additions & 14 deletions web/components/modals/CreateFlag.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ const CreateFlag = class extends Component {
initial_value: Utils.getTypedValue(feature_state_value),
description,
allowEditDescription,
enabledIndentity: false,
enabledSegment: false,
};
}

Expand Down Expand Up @@ -114,32 +116,68 @@ const CreateFlag = class extends Component {
}
}

disableSegment = (items) => {
changeSegment = (items) => {
const { enabledSegment } = this.state;
items.forEach((item) => {
item.enabled = false;
item.enabled = enabledSegment;
});
this.props.updateSegments(items);
this.setState({ enabledSegment: !enabledSegment });
}

disableIdentity = (items) => {
changeIdentity = (items) => {
const { environmentId } = this.props;
items.forEach((item) => {
const { enabledIndentity } = this.state;

Promise.all(items.map(item => new Promise((resolve) => {
AppActions.changeUserFlag({
identityFlag: item.id,
identity: item.identity.id,
environmentId,
onSuccess: resolve,
payload: {
id: item.identity.id,
enabled: false,
enabled: enabledIndentity,
value: item.identity.identifier,
},
});
}))).then(() => {
this.userOverridesPage(1);
});

this.setState({ enabledIndentity: !enabledIndentity });
}

toggleUserFlag = ({ id, feature_state_value, enabled, identity }) => {
const { environmentId } = this.props;

AppActions.changeUserFlag({
identityFlag: id,
identity: identity.id,
environmentId,
onSuccess: () => {
this.userOverridesPage(1);
},
payload: {
id: identity.id,
enabled: !enabled,
value: identity.identifier,
},
});
this.userOverridesPage(1);
}

render() {
const { name, initial_value, hide_from_client, default_enabled, featureType, type, description } = this.state;
const {
name,
initial_value,
hide_from_client,
default_enabled,
featureType,
type,
description,
enabledSegment,
enabledIndentity,
} = this.state;
const { isEdit, hasFeature, projectFlag, environmentFlag, identity, identityName } = this.props;
const Provider = identity ? IdentityProvider : FeatureListProvider;
const valueString = isEdit ? 'Value' : 'Initial value';
Expand Down Expand Up @@ -304,8 +342,8 @@ const CreateFlag = class extends Component {
)}
action={
this.props.hasFeature('killswitch') && (
<Button onClick={() => this.disableSegment(this.props.segmentOverrides)} type="button" className="btn--outline-red">
Disable
<Button onClick={() => this.changeSegment(this.props.segmentOverrides)} type="button" className={`btn--outline${enabledSegment ? '' : '-red'}`}>
{enabledSegment ? 'Enable All' : 'Disable All'}
</Button>
)
}
Expand Down Expand Up @@ -347,10 +385,11 @@ const CreateFlag = class extends Component {
)}
action={
this.props.hasFeature('killswitch') && (
<Button onClick={() => this.disableIdentity(this.state.userOverrides)} type="button" className="btn--outline-red">
Disable
<Button onClick={() => this.changeIdentity(this.state.userOverrides)} type="button" className={`btn--outline${enabledIndentity ? '' : '-red'}`}>
{enabledIndentity ? 'Enable All' : 'Disable All'}
</Button>
)}
)
}
className="no-pad"
icon="ion-md-person"
items={this.state.userOverrides}
Expand All @@ -361,8 +400,13 @@ const CreateFlag = class extends Component {
renderRow={({ id, feature_state_value, enabled, identity }) => (
<Row
onClick={() => {
this.close();
this.props.router.history.push(`/project/${this.props.projectId}/environment/${this.props.environmentId}/users/${identity.identifier}/${identity.id}`);
if (type === 'FLAG') {
this.toggleUserFlag({ id, feature_state_value, enabled, identity });
} else {
// todo: allow for editing from this screen
this.close();
this.props.router.history.push(`/project/${this.props.projectId}/environment/${this.props.environmentId}/users/${identity.identifier}/${identity.id}`);
}
}} space className="list-item cursor-pointer"
key={id}
>
Expand Down
10 changes: 5 additions & 5 deletions web/project/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,8 @@ global.API = {
.set('email', id);
amplitude.getInstance().identify(identify);
}
bulletTrain.identify(id);
bulletTrain.setTrait('email', id);
flagsmith.identify(id);
flagsmith.setTrait('email', id);
},
identify(id, user = {}) {
if (id === Project.excludeAnalytics) return;
Expand Down Expand Up @@ -168,8 +168,8 @@ global.API = {

amplitude.getInstance().identify(identify);
}
bulletTrain.identify(id);
bulletTrain.setTrait('email', id);
flagsmith.identify(id);
flagsmith.setTrait('email', id);
if (window.$crisp) {
$crisp.push(['set', 'user:email', id]);
$crisp.push(['set', 'user:nickname', `${user.first_name} ${user.last_name}`]);
Expand All @@ -195,7 +195,7 @@ global.API = {
if (Project.mixpanel) {
mixpanel.reset();
}
bulletTrain.logout();
flagsmith.logout();
},
log() {
console.log.apply(this, arguments);
Expand Down
4 changes: 2 additions & 2 deletions web/project/libs.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import bulletTrain from 'bullet-train-client'; // Add this line if you're using bulletTrain via npm
import flagsmith from 'flagsmith'; // Add this line if you're using flagsmith via npm

import 'ionicons/dist/css/ionicons.min.css';

Expand Down Expand Up @@ -26,7 +26,7 @@ import Project from '../../common/project';

window.isMobile = isMobile || $(window).width() <= 576;

window.bulletTrain = bulletTrain;
window.flagsmith = flagsmith;
window.moment = require('moment/min/moment.min');

window._ = { each,intersection, sortBy, orderBy, filter, find, partial, findIndex, range, map, cloneDeep, keyBy, throttle, every, get };
Expand Down

0 comments on commit bc2fb88

Please sign in to comment.