1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-04-19 01:17:18 +02:00

fix: Upgrade to @hapi/joi (#543)

This commit is contained in:
Ivar Conradi Østhus 2020-01-02 19:23:52 +01:00
parent 4089a40fa3
commit b532c86695
20 changed files with 100 additions and 108 deletions

View File

@ -1,5 +1,9 @@
# Changelog
## 3.2.23
- fix: upgrade to @hapi/joi to version 16.1.8
## 3.2.22
- fix: add appName as label in usage metrics

View File

@ -1,13 +0,0 @@
'use strict';
class ValidationError extends Error {
constructor(message) {
super();
Error.captureStackTrace(this, this.constructor);
this.name = this.constructor.name;
this.message = message;
}
}
module.exports = ValidationError;

View File

@ -1,6 +1,6 @@
'use strict';
const joi = require('joi');
const joi = require('@hapi/joi');
const { nameType } = require('./util');
const nameSchema = joi.object().keys({ name: nameType });

View File

@ -2,7 +2,6 @@
const test = require('ava');
const { featureShema } = require('./feature-schema');
const joi = require('joi');
test('should require URL firendly name', t => {
const toggle = {
@ -11,7 +10,7 @@ test('should require URL firendly name', t => {
strategies: [{ name: 'default' }],
};
const { error } = joi.validate(toggle, featureShema);
const { error } = featureShema.validate(toggle);
t.deepEqual(error.details[0].message, '"name" must be URL friendly');
});
@ -22,7 +21,7 @@ test('should be valid toggle name', t => {
strategies: [{ name: 'default' }],
};
const { value } = joi.validate(toggle, featureShema);
const { value } = featureShema.validate(toggle);
t.deepEqual(value, toggle);
});
@ -40,7 +39,7 @@ test('should strip extra variant fields', t => {
],
};
const { value } = joi.validate(toggle, featureShema);
const { value } = featureShema.validate(toggle);
t.notDeepEqual(value, toggle);
t.falsy(value.variants[0].unkown);
});
@ -64,7 +63,7 @@ test('should be possible to define variant overrides', t => {
],
};
const { value, error } = joi.validate(toggle, featureShema);
const { value, error } = featureShema.validate(toggle);
t.deepEqual(value, toggle);
t.falsy(error);
});
@ -88,9 +87,12 @@ test('variant overrides must have corect shape', async t => {
};
try {
await joi.validate(toggle, featureShema);
await featureShema.validateAsync(toggle);
} catch (error) {
t.is(error.details[0].message, '"overrides" must be an array');
t.is(
error.details[0].message,
'"variants[0].overrides" must be an array'
);
}
});
@ -112,7 +114,7 @@ test('should keep constraints', t => {
],
};
const { value, error } = joi.validate(toggle, featureShema);
const { value, error } = featureShema.validate(toggle);
t.deepEqual(value, toggle);
t.falsy(error);
});

View File

@ -1,7 +1,6 @@
'use strict';
const Controller = require('../controller');
const joi = require('joi');
const {
FEATURE_CREATED,
@ -56,7 +55,7 @@ class FeatureController extends Controller {
const name = req.body.name;
try {
await joi.validate({ name }, nameSchema);
await nameSchema.validateAsync({ name });
await this.validateUniqueName(name);
res.status(201).end();
} catch (error) {
@ -87,11 +86,11 @@ class FeatureController extends Controller {
try {
await this.validateUniqueName(toggleName);
const featureToggle = await joi.validate(req.body, featureShema);
const value = await featureShema.validateAsync(req.body);
await this.eventStore.store({
type: FEATURE_CREATED,
createdBy: userName,
data: featureToggle,
data: value,
});
res.status(201).end();
} catch (error) {
@ -108,7 +107,7 @@ class FeatureController extends Controller {
try {
await this.featureToggleStore.getFeature(featureName);
await joi.validate(updatedFeature, featureShema);
await featureShema.validateAsync(updatedFeature);
await this.eventStore.store({
type: FEATURE_UPDATED,
createdBy: userName,

View File

@ -1,6 +1,6 @@
'use strict';
const joi = require('joi');
const joi = require('@hapi/joi');
const { nameType } = require('./util');
const applicationSchema = joi

View File

@ -1,6 +1,5 @@
'use strict';
const joi = require('joi');
const Controller = require('../controller');
const ClientMetrics = require('../../client-metrics');
const schema = require('./metrics-schema');
@ -80,7 +79,7 @@ class MetricsController extends Controller {
const input = Object.assign({}, req.body, {
appName: req.params.appName,
});
const { value: applicationData, error } = joi.validate(input, schema);
const { value: applicationData, error } = schema.validate(input);
if (error) {
this.logger.warn('Invalid application data posted', error);

View File

@ -1,6 +1,6 @@
'use strict';
const joi = require('joi');
const joi = require('@hapi/joi');
const { nameType } = require('./util');
const strategySchema = joi.object().keys({

View File

@ -1,7 +1,6 @@
'use strict';
const Controller = require('../controller');
const joi = require('joi');
const eventType = require('../../event-type');
const NameExistsError = require('../../error/name-exists-error');
@ -65,12 +64,12 @@ class StrategyController extends Controller {
async createStrategy(req, res) {
try {
const newStrategy = await joi.validate(req.body, strategySchema);
await this._validateStrategyName(newStrategy);
const value = await strategySchema.validateAsync(req.body);
await this._validateStrategyName(value);
await this.eventStore.store({
type: eventType.STRATEGY_CREATED,
createdBy: extractUser(req),
data: newStrategy,
data: value,
});
res.status(201).end();
} catch (error) {
@ -82,14 +81,14 @@ class StrategyController extends Controller {
const input = req.body;
try {
const updatedStrategy = await joi.validate(input, strategySchema);
const value = await strategySchema.validateAsync(input);
const strategy = await this.strategyStore.getStrategy(input.name);
await this._validateEditable(strategy);
await this.eventStore.store({
type: eventType.STRATEGY_UPDATED,
createdBy: extractUser(req),
data: updatedStrategy,
data: value,
});
res.status(200).end();
} catch (error) {

View File

@ -59,7 +59,7 @@ test('require a name when creating a new stratey', t => {
.send({})
.expect(400)
.expect(res => {
t.true(res.body.name === 'ValidationError');
t.true(res.body.details[0].message === '"name" is required');
});
});
@ -73,7 +73,10 @@ test('require parameters array when creating a new stratey', t => {
.send({ name: 'TestStrat' })
.expect(400)
.expect(res => {
t.true(res.body.name === 'ValidationError');
t.deepEqual(
res.body.details[0].message,
'"parameters" is required'
);
});
});

View File

@ -1,34 +1,23 @@
'use strict';
const joi = require('joi');
const joi = require('@hapi/joi');
const customJoi = joi.extend(j => ({
type: 'isUrlFriendly',
base: j.string(),
name: 'string',
language: {
isUrlFriendly: 'must be URL friendly',
messages: {
'isUrlFriendly.base': '"{{#label}}" must be URL friendly',
},
validate(value, helpers) {
// Base validation regardless of the rules applied
if (encodeURIComponent(value) !== value) {
// Generate an error, state and options need to be passed
return { value, errors: helpers.error('isUrlFriendly.base') };
}
},
rules: [
{
name: 'isUrlFriendly',
validate(params, value, state, options) {
if (encodeURIComponent(value) !== value) {
// Generate an error, state and options need to be passed
return this.createError(
'string.isUrlFriendly',
{ v: value },
state,
options
);
}
return value; // Everything is OK
},
},
],
}));
const nameType = customJoi
.string()
.isUrlFriendly()
.min(2)
.max(100)
@ -41,6 +30,7 @@ const handleErrors = (res, logger, error) => {
return res.status(404).end();
case 'NameExistsError':
case 'ValidationError':
error.isJoi = true;
return res
.status(400)
.json(error)

View File

@ -1,6 +1,6 @@
'use strict';
const joi = require('joi');
const joi = require('@hapi/joi');
const countSchema = joi
.object()

View File

@ -1,7 +1,5 @@
'use strict';
const joi = require('joi');
const Controller = require('../controller');
const { clientMetricsSchema } = require('./metrics-schema');
@ -19,7 +17,7 @@ class ClientMetricsController extends Controller {
const data = req.body;
const clientIp = req.ip;
const { error, value } = joi.validate(data, clientMetricsSchema);
const { error, value } = clientMetricsSchema.validate(data);
if (error) {
this.logger.warn('Invalid metrics posted', error);

View File

@ -2,7 +2,6 @@
const test = require('ava');
const supertest = require('supertest');
const joi = require('joi');
const store = require('./../../../test/fixtures/store');
const getLogger = require('../../../test/fixtures/no-logger');
const getApp = require('../../app');
@ -132,7 +131,7 @@ test('shema allow empty strings', t => {
stop: '2019-05-06T09:30:50.515Z',
},
};
const { error, value } = joi.validate(data, clientMetricsSchema);
const { error, value } = clientMetricsSchema.validate(data);
t.falsy(error);
t.is(value.bucket.toggles.Demo2.yes, 0);
t.is(value.bucket.toggles.Demo2.no, 0);
@ -148,7 +147,7 @@ test('shema allow yes=<string nbr>', t => {
stop: '2019-05-06T09:30:50.515Z',
},
};
const { error, value } = joi.validate(data, clientMetricsSchema);
const { error, value } = clientMetricsSchema.validate(data);
t.falsy(error);
t.is(value.bucket.toggles.Demo2.yes, 12);
t.is(value.bucket.toggles.Demo2.no, 256);

View File

@ -1,6 +1,6 @@
'use strict';
const joi = require('joi');
const joi = require('@hapi/joi');
const clientRegisterSchema = joi
.object()

View File

@ -1,7 +1,5 @@
'use strict';
const joi = require('joi');
const Controller = require('../controller');
const { clientRegisterSchema: schema } = require('./register-schema');
@ -17,19 +15,19 @@ class RegisterController extends Controller {
async handleRegister(req, res) {
const data = req.body;
const { value: clientRegistration, error } = joi.validate(data, schema);
const { value, error } = schema.validate(data);
if (error) {
this.logger.warn('Invalid client data posted', error);
return res.status(400).json(error);
}
clientRegistration.clientIp = req.ip;
value.clientIp = req.ip;
try {
await this.clientApplicationsStore.upsert(clientRegistration);
await this.clientInstanceStore.insert(clientRegistration);
const { appName, instanceId } = clientRegistration;
await this.clientApplicationsStore.upsert(value);
await this.clientInstanceStore.insert(value);
const { appName, instanceId } = value;
this.logger.info(
`New client registration: appName=${appName}, instanceId=${instanceId}`
);

View File

@ -1,6 +1,6 @@
'use strict';
const joi = require('joi');
const joi = require('@hapi/joi');
const fs = require('fs');
const mime = require('mime');
const { featureShema } = require('./routes/admin-api/feature-schema');
@ -52,7 +52,7 @@ class StateService {
async import({ data, userName, dropBeforeImport }) {
const { eventStore } = this.config.stores;
const importData = await joi.validate(data, dataSchema);
const importData = await dataSchema.validateAsync(data);
if (importData.features) {
this.logger.info(

View File

@ -1,7 +1,7 @@
'use strict';
const gravatar = require('gravatar');
const Joi = require('joi');
const Joi = require('@hapi/joi');
module.exports = class User {
constructor({ name, email, permissions, imageUrl } = {}) {

View File

@ -72,7 +72,7 @@
"errorhandler": "^1.5.1",
"express": "^4.17.1",
"gravatar": "^1.8.0",
"joi": "^14.3.1",
"@hapi/joi": "^16.1.8",
"js-yaml": "^3.12.2",
"knex": "^0.20.0",
"log4js": "^6.0.0",

View File

@ -439,6 +439,44 @@
dependencies:
arrify "^1.0.1"
"@hapi/address@^2.1.2":
version "2.1.4"
resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5"
integrity sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ==
"@hapi/formula@^1.2.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@hapi/formula/-/formula-1.2.0.tgz#994649c7fea1a90b91a0a1e6d983523f680e10cd"
integrity sha512-UFbtbGPjstz0eWHb+ga/GM3Z9EzqKXFWIbSOFURU0A/Gku0Bky4bCk9/h//K2Xr3IrCfjFNhMm4jyZ5dbCewGA==
"@hapi/hoek@^8.2.4", "@hapi/hoek@^8.3.0":
version "8.5.0"
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.5.0.tgz#2f9ce301c8898e1c3248b0a8564696b24d1a9a5a"
integrity sha512-7XYT10CZfPsH7j9F1Jmg1+d0ezOux2oM2GfArAzLwWe4mE2Dr3hVjsAL6+TFY49RRJlCdJDMw3nJsLFroTc8Kw==
"@hapi/joi@^16.1.8":
version "16.1.8"
resolved "https://registry.yarnpkg.com/@hapi/joi/-/joi-16.1.8.tgz#84c1f126269489871ad4e2decc786e0adef06839"
integrity sha512-wAsVvTPe+FwSrsAurNt5vkg3zo+TblvC5Bb1zMVK6SJzZqw9UrJnexxR+76cpePmtUZKHAPxcQ2Bf7oVHyahhg==
dependencies:
"@hapi/address" "^2.1.2"
"@hapi/formula" "^1.2.0"
"@hapi/hoek" "^8.2.4"
"@hapi/pinpoint" "^1.0.2"
"@hapi/topo" "^3.1.3"
"@hapi/pinpoint@^1.0.2":
version "1.0.2"
resolved "https://registry.yarnpkg.com/@hapi/pinpoint/-/pinpoint-1.0.2.tgz#025b7a36dbbf4d35bf1acd071c26b20ef41e0d13"
integrity sha512-dtXC/WkZBfC5vxscazuiJ6iq4j9oNx1SHknmIr8hofarpKUZKmlUVYVIhNVzIEgK5Wrc4GMHL5lZtt1uS2flmQ==
"@hapi/topo@^3.1.3":
version "3.1.6"
resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-3.1.6.tgz#68d935fa3eae7fdd5ab0d7f953f3205d8b2bfc29"
integrity sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ==
dependencies:
"@hapi/hoek" "^8.3.0"
"@istanbuljs/load-nyc-config@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.0.0.tgz#10602de5570baea82f8afbfa2630b24e7a8cfe5b"
@ -2801,10 +2839,6 @@ hasha@^5.0.0:
is-stream "^1.1.0"
type-fest "^0.3.0"
hoek@6.x.x:
version "6.1.2"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-6.1.2.tgz#99e6d070561839de74ee427b61aa476bd6bddfd6"
homedir-polyfill@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.1.tgz#4c2bbc8a758998feebf5ed68580f76d46768b4bc"
@ -3253,12 +3287,6 @@ isarray@1.0.0, isarray@~1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
isemail@3.x.x:
version "3.2.0"
resolved "https://registry.yarnpkg.com/isemail/-/isemail-3.2.0.tgz#59310a021931a9fb06bbb51e155ce0b3f236832c"
dependencies:
punycode "2.x.x"
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
@ -3350,14 +3378,6 @@ jest-docblock@^21.0.0:
version "21.2.0"
resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-21.2.0.tgz#51529c3b30d5fd159da60c27ceedc195faf8d414"
joi@^14.3.1:
version "14.3.1"
resolved "https://registry.yarnpkg.com/joi/-/joi-14.3.1.tgz#164a262ec0b855466e0c35eea2a885ae8b6c703c"
dependencies:
hoek "6.x.x"
isemail "3.x.x"
topo "3.x.x"
js-string-escape@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef"
@ -4853,15 +4873,15 @@ pump@^3.0.0:
end-of-stream "^1.1.0"
once "^1.3.1"
punycode@2.x.x, punycode@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
punycode@^1.4.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
integrity sha1-wNWmOycYgArY4esPpSachN1BhF4=
punycode@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
qs@6.7.0:
version "6.7.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
@ -5879,12 +5899,6 @@ toidentifier@1.0.0:
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553"
integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==
topo@3.x.x:
version "3.0.3"
resolved "https://registry.yarnpkg.com/topo/-/topo-3.0.3.tgz#d5a67fb2e69307ebeeb08402ec2a2a6f5f7ad95c"
dependencies:
hoek "6.x.x"
tough-cookie@~2.4.3:
version "2.4.3"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.4.3.tgz#53f36da3f47783b0925afa06ff9f3b165280f781"