Gitter migration: Setup redirects (rollout pt. 3)
[gitter.git] / server / web / middlewares / feature-toggles.js
blob7aa98f6d482cda42a3e26710143cd1dc4c33f3c2
1 'use strict';
3 var fflip = require('fflip');
4 var mongoUtils = require('gitter-web-persistence-utils/lib/mongo-utils');
5 var persistence = require('gitter-web-persistence');
7 /**
8 * List of criteria functions to be used by feature toggles
9 */
10 var Criteria = {
11 /* Allow a certain percentage of users */
12 percentageOfUsers: function(userDetails, percent) {
13 var user = userDetails.user;
14 if (!user) return false;
15 var timestamp = Math.round(mongoUtils.getTimestampFromObjectId(user._id) / 1000) || 0;
16 return timestamp % 100 < percent || undefined;
19 /* Allow a hash of usernames */
20 allowUsernames: function(userDetails, usernameHash) {
21 var user = userDetails.user;
22 if (!user) return false;
23 if (!usernameHash) return undefined;
24 return !!usernameHash[user.username] || undefined;
27 disableBrowser: function(userDetails, browsers) {
28 if (!browsers) return undefined;
30 var agent = userDetails.req.getParsedUserAgent();
32 var disabledVersion = browsers[agent.family];
33 if (!disabledVersion) return undefined;
35 if (disabledVersion === 'all') return false;
36 if (agent.major <= disabledVersion) return false;
37 return undefined;
40 bucket: function(userDetails, bucket) {
41 var user = userDetails.user;
42 if (!user) return false;
43 var userId = user._id;
45 // This is copied from gitter-env.
46 var lastChar = userId.toString().slice(-1);
47 var userBucket = parseInt(lastChar + '', 16) % 2 ? 'A' : 'B';
49 return userBucket === bucket || undefined;
52 createdAfter: function(userDetails, timestamp) {
53 var user = userDetails.user;
54 if (!user) return false;
55 var userTimestamp = mongoUtils.getTimestampFromObjectId(user._id);
56 return userTimestamp > timestamp || undefined;
59 bucketCreatedAfter: function(userDetails, opts) {
60 return (
61 (Criteria.bucket(userDetails, opts.bucket) &&
62 Criteria.createdAfter(userDetails, opts.createdAfter)) ||
63 undefined
67 /* Enabled criteria */
68 enabled: function(/*user*/) {
69 return true;
73 function getFeatures(callback) {
74 persistence.FeatureToggle.find({})
75 .lean()
76 .exec()
77 .then(function(togglesList) {
78 if (!togglesList || togglesList.length === 0) {
79 return callback({});
82 var featureToggles = togglesList.reduce(function(memo, toggle) {
83 memo[toggle.name] = { criteria: toggle.criteria };
84 return memo;
85 }, {});
87 // NOTE: this callback doesn't have an err.
88 callback(featureToggles);
89 });
92 fflip.config({
93 criteria: Criteria,
94 features: getFeatures,
95 reload: 60, // Reload features every 60 seconds
96 maxCookieAge: 31 * 86400 * 1000,
97 useVetoVoting: true
98 });
100 module.exports = [
101 fflip.express_middleware,
102 function(req, res, next) {
103 // Only logged in users get features
104 if (!req.user) return next();
106 req.fflip.setForUser({ user: req.user, req: req });
107 next();