1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-01-01 00:08:27 +01:00

Merge pull request #256 from Unleash/metrics_validation

Add metrics validation to avoid NaN #253
This commit is contained in:
Ivar Conradi Østhus 2017-08-09 06:47:39 +02:00 committed by GitHub
commit 810b414945
6 changed files with 114 additions and 8 deletions

View File

@ -5,6 +5,7 @@
- disable edit of built-in strategies - disable edit of built-in strategies
- Strip uknown fields in client requests. - Strip uknown fields in client requests.
- Disable x-powered-by header - Disable x-powered-by header
- Improved client-metrics validation to avoid NaN
- Add posibility to inject custom logger provider - Add posibility to inject custom logger provider
## 3.0.0-alpha.1 ## 3.0.0-alpha.1

View File

@ -335,3 +335,45 @@ test('should have correct values for lastHour', t => {
metrics.destroy(); metrics.destroy();
clock.restore(); clock.restore();
}); });
test('should not fail when toggle metrics is missing yes/no field', t => {
const store = new EventEmitter();
const metrics = new UnleashClientMetrics(store);
store.emit('metrics', {
appName,
instanceId,
bucket: {
start: new Date(),
stop: new Date(),
toggles: {
toggleX: {
yes: 123,
no: 0,
},
},
},
});
metrics.addPayload({
appName,
instanceId,
bucket: {
start: new Date(),
stop: new Date(),
toggles: {
toggleX: {
blue: 10,
green: 10,
},
},
},
});
t.is(metrics.globalCount, 123);
t.deepEqual(metrics.getTogglesMetrics().lastMinute.toggleX, {
yes: 123,
no: 0,
});
metrics.destroy();
});

View File

@ -25,7 +25,7 @@ module.exports = class UnleashClientMetrics {
Object.keys(toggles).forEach(toggleName => { Object.keys(toggles).forEach(toggleName => {
this.lastHourProjection.substract( this.lastHourProjection.substract(
toggleName, toggleName,
toggles[toggleName] this.createCountObject(toggles[toggleName])
); );
}); });
}); });
@ -33,7 +33,7 @@ module.exports = class UnleashClientMetrics {
Object.keys(toggles).forEach(toggleName => { Object.keys(toggles).forEach(toggleName => {
this.lastMinuteProjection.substract( this.lastMinuteProjection.substract(
toggleName, toggleName,
toggles[toggleName] this.createCountObject(toggles[toggleName])
); );
}); });
}); });
@ -91,6 +91,12 @@ module.exports = class UnleashClientMetrics {
return this.apps[appName]; return this.apps[appName];
} }
createCountObject(entry) {
const yes = typeof entry.yes == 'number' ? entry.yes : 0;
const no = typeof entry.no == 'number' ? entry.no : 0;
return { yes, no };
}
addBucket(app, bucket) { addBucket(app, bucket) {
let count = 0; let count = 0;
// TODO stop should be createdAt // TODO stop should be createdAt
@ -99,10 +105,10 @@ module.exports = class UnleashClientMetrics {
const toggleNames = Object.keys(toggles); const toggleNames = Object.keys(toggles);
toggleNames.forEach(n => { toggleNames.forEach(n => {
const entry = toggles[n]; const countObj = this.createCountObject(toggles[n]);
this.lastHourProjection.add(n, entry); this.lastHourProjection.add(n, countObj);
this.lastMinuteProjection.add(n, entry); this.lastMinuteProjection.add(n, countObj);
count += entry.yes + entry.no; count += countObj.yes + countObj.no;
}); });
this.lastHourList.add(toggles, stop); this.lastHourList.add(toggles, stop);

View File

@ -2,13 +2,18 @@
const joi = require('joi'); const joi = require('joi');
const countSchema = joi.object().options({ stripUnknown: true }).keys({
yes: joi.number().required().min(0).default(0),
no: joi.number().required().min(0).default(0),
});
const clientMetricsSchema = joi.object().options({ stripUnknown: true }).keys({ const clientMetricsSchema = joi.object().options({ stripUnknown: true }).keys({
appName: joi.string().required(), appName: joi.string().required(),
instanceId: joi.string().required(), instanceId: joi.string().required(),
bucket: joi.object().required().keys({ bucket: joi.object().required().keys({
start: joi.date().required(), start: joi.date().required(),
stop: joi.date().required(), stop: joi.date().required(),
toggles: joi.object(), toggles: joi.object().pattern(/.*/, countSchema),
}), }),
}); });

View File

@ -31,7 +31,7 @@ test('should validate client metrics', t => {
.expect(400); .expect(400);
}); });
test('should accept client metrics', t => { test('should accept empty client metrics', t => {
t.plan(0); t.plan(0);
const { request } = getSetup(); const { request } = getSetup();
return request return request
@ -47,3 +47,47 @@ test('should accept client metrics', t => {
}) })
.expect(202); .expect(202);
}); });
test('should accept client metrics with yes/no', t => {
t.plan(0);
const { request } = getSetup();
return request
.post('/api/client/metrics')
.send({
appName: 'demo',
instanceId: '1',
bucket: {
start: Date.now(),
stop: Date.now(),
toggles: {
toggleA: {
yes: 200,
no: 0,
},
},
},
})
.expect(202);
});
test('should not accept client metrics without yes/no', t => {
t.plan(0);
const { request } = getSetup();
return request
.post('/api/client/metrics')
.send({
appName: 'demo',
instanceId: '1',
bucket: {
start: Date.now(),
stop: Date.now(),
toggles: {
toggleA: {
blue: 200,
green: 0,
},
},
},
})
.expect(400);
});

View File

@ -0,0 +1,8 @@
{
"appName": "appName",
"instanceId": "instanceId",
"sdkVersion": "test:123",
"strategies": ["default", "some-strategy-1"],
"started": "2016-11-03T07:16:43.572Z",
"interval": 10000
}