mirror of
https://github.com/Unleash/unleash.git
synced 2026-10-23 12:26:25 +00:00
feat: make client features endpoint memoizable (#734)
This commit is contained in:
committed by
Ivar Conradi Østhus
parent
5f83fbc43d
commit
44af41547e
@@ -90,6 +90,12 @@ function defaultOptions() {
|
||||
version,
|
||||
secureHeaders: process.env.SECURE_HEADERS || false,
|
||||
enableOAS: process.env.ENABLE_OAS || false,
|
||||
experimental: {
|
||||
clientFeatureMemoize: {
|
||||
enabled: process.env.CLIENT_FEATURE_MEMOIZE || false,
|
||||
maxAge: process.env.CLIENT_FEATURE_MAXAGE || 1000,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
import { handleErrors } from '../admin-api/util';
|
||||
|
||||
const memoizee = require('memoizee');
|
||||
|
||||
const Controller = require('../controller');
|
||||
|
||||
const version = 1;
|
||||
@@ -16,20 +18,43 @@ const FEATURE_COLUMNS_CLIENT = [
|
||||
];
|
||||
|
||||
class FeatureController extends Controller {
|
||||
constructor({ featureToggleService }, getLogger) {
|
||||
constructor({ featureToggleService }, { getLogger, experimental }) {
|
||||
super();
|
||||
this.toggleService = featureToggleService;
|
||||
this.logger = getLogger('client-api/feature.js');
|
||||
this.get('/', this.getAll);
|
||||
this.get('/:featureName', this.getFeatureToggle);
|
||||
if (experimental && experimental.clientFeatureMemoize) {
|
||||
this.cache = experimental.clientFeatureMemoize.enabled;
|
||||
this.cachedFeatures = memoizee(
|
||||
(query, fields) =>
|
||||
this.toggleService.getFeatures(query, fields),
|
||||
{
|
||||
promise: true,
|
||||
maxAge: experimental.clientFeatureMemoize.maxAge,
|
||||
normalizer(args) {
|
||||
// args is arguments object as accessible in memoized function
|
||||
return JSON.stringify(args[0]);
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async getAll(req, res) {
|
||||
try {
|
||||
const features = await this.toggleService.getFeatures(
|
||||
req.query,
|
||||
FEATURE_COLUMNS_CLIENT,
|
||||
);
|
||||
let features;
|
||||
if (this.cache) {
|
||||
features = await this.cachedFeatures(
|
||||
req.query,
|
||||
FEATURE_COLUMNS_CLIENT,
|
||||
);
|
||||
} else {
|
||||
features = await this.toggleService.getFeatures(
|
||||
req.query,
|
||||
FEATURE_COLUMNS_CLIENT,
|
||||
);
|
||||
}
|
||||
res.json({ version, features });
|
||||
} catch (e) {
|
||||
handleErrors(res, this.logger, e);
|
||||
|
||||
@@ -3,10 +3,12 @@
|
||||
const test = require('ava');
|
||||
const supertest = require('supertest');
|
||||
const { EventEmitter } = require('events');
|
||||
const sinon = require('sinon');
|
||||
const store = require('../../../test/fixtures/store');
|
||||
const getLogger = require('../../../test/fixtures/no-logger');
|
||||
const getApp = require('../../app');
|
||||
const { createServices } = require('../../services');
|
||||
const FeatureController = require('./feature');
|
||||
|
||||
const eventBus = new EventEmitter();
|
||||
|
||||
@@ -40,6 +42,52 @@ test('should get empty getFeatures via client', t => {
|
||||
});
|
||||
});
|
||||
|
||||
test('if caching is enabled should memoize', t => {
|
||||
const getFeatures = sinon.fake.returns([]);
|
||||
|
||||
const featureToggleService = {
|
||||
getFeatures,
|
||||
};
|
||||
const controller = new FeatureController(
|
||||
{ featureToggleService },
|
||||
{
|
||||
getLogger,
|
||||
experimental: {
|
||||
clientFeatureMemoize: {
|
||||
enabled: true,
|
||||
maxAge: 10000,
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
controller.getAll({ query: {} }, { json: () => {} });
|
||||
controller.getAll({ query: {} }, { json: () => {} });
|
||||
t.is(getFeatures.callCount, 1);
|
||||
});
|
||||
|
||||
test('if caching is not enabled all calls goes to service', t => {
|
||||
const getFeatures = sinon.fake.returns([]);
|
||||
|
||||
const featureToggleService = {
|
||||
getFeatures,
|
||||
};
|
||||
const controller = new FeatureController(
|
||||
{ featureToggleService },
|
||||
{
|
||||
getLogger,
|
||||
experimental: {
|
||||
clientFeatureMemoize: {
|
||||
enabled: false,
|
||||
maxAge: 10000,
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
controller.getAll({ query: {} }, { json: () => {} });
|
||||
controller.getAll({ query: {} }, { json: () => {} });
|
||||
t.is(getFeatures.callCount, 2);
|
||||
});
|
||||
|
||||
test('fetch single feature', t => {
|
||||
t.plan(1);
|
||||
const { request, featureToggleStore, base } = getSetup();
|
||||
|
||||
@@ -13,10 +13,7 @@ class ClientApi extends Controller {
|
||||
const { getLogger } = config;
|
||||
|
||||
this.get('/', this.index);
|
||||
this.use(
|
||||
'/features',
|
||||
new FeatureController(services, getLogger).router,
|
||||
);
|
||||
this.use('/features', new FeatureController(services, config).router);
|
||||
this.use('/metrics', new MetricsController(services, getLogger).router);
|
||||
this.use(
|
||||
'/register',
|
||||
|
||||
@@ -22,10 +22,7 @@ class IndexRouter extends Controller {
|
||||
|
||||
// legacy support (remove in 4.x)
|
||||
if (config.enableLegacyRoutes) {
|
||||
const featureController = new FeatureController(
|
||||
services,
|
||||
config.getLogger,
|
||||
);
|
||||
const featureController = new FeatureController(services, config);
|
||||
this.use('/api/features', featureController.router);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user