mirror of
https://github.com/Unleash/unleash.git
synced 2024-12-22 19:07:54 +01:00
fix: when payload type is 'json' validate value on toggle variable validation (#1704)
* fix: when payload type is 'json' validate value on toggle variable validation * test: add missing feature toggle creation with variant type json Ref https://github.com/Unleash/unleash/pull/1704#discussion_r896476042 * refractor: remove verbose comment on validateJsonString Ref https://github.com/Unleash/unleash/pull/1704#discussion_r896482210 * test: add missing feature toggle creation with variant type string Ref https://github.com/Unleash/unleash/pull/1704#discussion_r896476042 * refractor: move variant value joi validation Ref https://github.com/Unleash/unleash/pull/1704#discussion_r896478563
This commit is contained in:
parent
12fa0b8231
commit
fb711b4d4a
@ -1,6 +1,7 @@
|
|||||||
import joi from 'joi';
|
import joi from 'joi';
|
||||||
import { ALL_OPERATORS } from '../util/constants';
|
import { ALL_OPERATORS } from '../util/constants';
|
||||||
import { nameType } from '../routes/util';
|
import { nameType } from '../routes/util';
|
||||||
|
import { validateJsonString } from '../util/validateJsonString';
|
||||||
|
|
||||||
export const nameSchema = joi
|
export const nameSchema = joi
|
||||||
.object()
|
.object()
|
||||||
@ -27,6 +28,26 @@ export const strategiesSchema = joi.object().keys({
|
|||||||
parameters: joi.object(),
|
parameters: joi.object(),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const variantValueSchema = joi
|
||||||
|
.string()
|
||||||
|
.required()
|
||||||
|
// perform additional validation
|
||||||
|
// when provided 'type' is 'json'
|
||||||
|
.when('type', {
|
||||||
|
is: 'json',
|
||||||
|
then: joi.custom((val, helper) => {
|
||||||
|
const isValidJsonString = validateJsonString(val);
|
||||||
|
if (isValidJsonString === false) {
|
||||||
|
return helper.error('invalidJsonString');
|
||||||
|
}
|
||||||
|
return val;
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.messages({
|
||||||
|
invalidJsonString:
|
||||||
|
"'value' must be a valid json string when 'type' is json",
|
||||||
|
});
|
||||||
|
|
||||||
export const variantsSchema = joi.object().keys({
|
export const variantsSchema = joi.object().keys({
|
||||||
name: nameType,
|
name: nameType,
|
||||||
weight: joi.number().min(0).max(1000).required(),
|
weight: joi.number().min(0).max(1000).required(),
|
||||||
@ -35,7 +56,7 @@ export const variantsSchema = joi.object().keys({
|
|||||||
.object()
|
.object()
|
||||||
.keys({
|
.keys({
|
||||||
type: joi.string().required(),
|
type: joi.string().required(),
|
||||||
value: joi.string().required(),
|
value: variantValueSchema,
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional(),
|
||||||
stickiness: joi.string().default('default'),
|
stickiness: joi.string().default('default'),
|
||||||
|
22
src/lib/util/validateJsonString.test.ts
Normal file
22
src/lib/util/validateJsonString.test.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { validateJsonString } from './validateJsonString';
|
||||||
|
|
||||||
|
test('should return true for valid json string', () => {
|
||||||
|
const input = '{"test":1,"nested":[{"test1":{"testinner":true}}]}';
|
||||||
|
expect(validateJsonString(input)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return false for invalid json string (missing starting {)', () => {
|
||||||
|
// missing starting {
|
||||||
|
const input = '"test":1,"nested":[{"test1":{"testinner":true}}]}';
|
||||||
|
expect(validateJsonString(input)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return false for invalid json string (plain string)', () => {
|
||||||
|
const input = 'not a json';
|
||||||
|
expect(validateJsonString(input)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should return false for invalid json string (null as a string)', () => {
|
||||||
|
const input = 'null';
|
||||||
|
expect(validateJsonString(input)).toBe(false);
|
||||||
|
});
|
12
src/lib/util/validateJsonString.ts
Normal file
12
src/lib/util/validateJsonString.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
export const validateJsonString = (value: string): boolean => {
|
||||||
|
// from https://stackoverflow.com/a/20392392
|
||||||
|
try {
|
||||||
|
const parsedStr = JSON.parse(value);
|
||||||
|
if (parsedStr && typeof parsedStr === 'object') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (err) {}
|
||||||
|
|
||||||
|
// an error is considered a non valid json
|
||||||
|
return false;
|
||||||
|
};
|
@ -261,6 +261,76 @@ test('creates new feature toggle with createdBy unknown', async () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('create new feature toggle with variant type json', async () => {
|
||||||
|
return app.request
|
||||||
|
.post('/api/admin/features')
|
||||||
|
.send({
|
||||||
|
name: 'com.test.featureWithJson',
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
name: 'variantTestJson',
|
||||||
|
weight: 1,
|
||||||
|
payload: {
|
||||||
|
type: 'json',
|
||||||
|
value: '{"test": true, "user": [{"jsonValid": 1}]}',
|
||||||
|
},
|
||||||
|
weightType: 'variable',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.set('Content-Type', 'application/json')
|
||||||
|
.expect(201);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('create new feature toggle with variant type string', async () => {
|
||||||
|
return app.request
|
||||||
|
.post('/api/admin/features')
|
||||||
|
.send({
|
||||||
|
name: 'com.test.featureWithString',
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
name: 'variantTestString',
|
||||||
|
weight: 1,
|
||||||
|
payload: {
|
||||||
|
type: 'string',
|
||||||
|
value: 'my string # here',
|
||||||
|
},
|
||||||
|
weightType: 'variable',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.set('Content-Type', 'application/json')
|
||||||
|
.expect(201);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('refuses to create a new feature toggle with variant when type is json but value provided is not a valid json', async () => {
|
||||||
|
return app.request
|
||||||
|
.post('/api/admin/features')
|
||||||
|
.send({
|
||||||
|
name: 'com.test.featureInvalidValue',
|
||||||
|
variants: [
|
||||||
|
{
|
||||||
|
name: 'variantTest',
|
||||||
|
weight: 1,
|
||||||
|
payload: {
|
||||||
|
type: 'json',
|
||||||
|
value: 'this should be a # valid json', // <--- testing value
|
||||||
|
},
|
||||||
|
weightType: 'variable',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.set('Content-Type', 'application/json')
|
||||||
|
.expect(400)
|
||||||
|
.expect((res) => {
|
||||||
|
expect(res.body.isJoi).toBe(true);
|
||||||
|
expect(res.body.details[0].type).toBe('invalidJsonString');
|
||||||
|
expect(res.body.details[0].message).toBe(
|
||||||
|
`'value' must be a valid json string when 'type' is json`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('require new feature toggle to have a name', async () => {
|
test('require new feature toggle to have a name', async () => {
|
||||||
expect.assertions(0);
|
expect.assertions(0);
|
||||||
return app.request
|
return app.request
|
||||||
|
Loading…
Reference in New Issue
Block a user