Skip to content

Commit 7db5d97

Browse files
Validate track properties object types
1 parent aac1303 commit 7db5d97

11 files changed

Lines changed: 172 additions & 66 deletions

client/__tests__/allTreatments.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ describe('get-all-treatments', () => {
262262

263263
test('should be 400 if options.properties is invalid (GET)', async () => {
264264
const expected = [
265-
'options.properties must only contain boolean, string, or number values.'
265+
'properties must only contain boolean, string, or number values.'
266266
];
267267
const response = await request(app)
268268
.get('/client/get-all-treatments?keys=[{"matchingKey":"test","trafficType":"localhost"}]&options={"properties":{"invalid":[1,2,3]}}')
@@ -272,7 +272,7 @@ describe('get-all-treatments', () => {
272272

273273
test('should be 400 if options.properties is invalid (POST)', async () => {
274274
const expected = [
275-
'options.properties must only contain boolean, string, or number values.'
275+
'properties must only contain boolean, string, or number values.'
276276
];
277277
const response = await request(app)
278278
.post('/client/get-all-treatments?keys=[{"matchingKey":"test","trafficType":"localhost"}]')

client/__tests__/allTreatmentsWithConfig.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ describe('get-all-treatments-with-config', () => {
270270

271271
test('should be 400 if options.properties is invalid (GET)', async () => {
272272
const expected = [
273-
'options.properties must only contain boolean, string, or number values.'
273+
'properties must only contain boolean, string, or number values.'
274274
];
275275
const response = await request(app)
276276
.get('/client/get-all-treatments-with-config?keys=[{"matchingKey":"test","trafficType":"localhost"}]&options={"properties":{"invalid":[1,2,3]}}')
@@ -280,7 +280,7 @@ describe('get-all-treatments-with-config', () => {
280280

281281
test('should be 400 if options.properties is invalid (POST)', async () => {
282282
const expected = [
283-
'options.properties must only contain boolean, string, or number values.'
283+
'properties must only contain boolean, string, or number values.'
284284
];
285285
const response = await request(app)
286286
.post('/client/get-all-treatments-with-config?keys=[{"matchingKey":"test","trafficType":"localhost"}]')

client/__tests__/track.test.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,20 +145,30 @@ describe('track', () => {
145145

146146
test('should be 400 if properties is invalid', async () => {
147147
const expected = [
148-
'Input must be a plain object.'
148+
'properties must be a plain object.'
149149
];
150150
const response = await request(app)
151151
.get('/client/track?key=my-key&event-type=my-event&traffic-type=my-traffic&value=1&properties=lalala')
152152
.set('Authorization', 'test');
153153
expectErrorContaining(response, 400, expected);
154154
});
155155

156+
test('should be 400 if properties is invalid', async () => {
157+
const expected = [
158+
'properties must only contain boolean, string, or number values.'
159+
];
160+
const response = await request(app)
161+
.get('/client/track?key=my-key&event-type=my-event&traffic-type=my-traffic&value=1&properties={"test":[]}')
162+
.set('Authorization', 'test');
163+
expectErrorContaining(response, 400, expected);
164+
});
165+
156166
test('should be 400 if there are multiple errors in every input', async () => {
157167
const expected = [
158168
'key too long, key must be 250 characters or less.',
159169
'you passed "@!test", event-type must adhere to the regular expression /^[a-zA-Z0-9][-_.:a-zA-Z0-9]{0,79}$/g. This means an event_type must be alphanumeric, cannot be more than 80 characters long, and can only include a dash, underscore, period, or colon as separators of alphanumeric characters.',
160170
'you passed an empty traffic-type, traffic-type must be a non-empty string.',
161-
'Input must be a plain object.',
171+
'properties must be a plain object.',
162172
'value must be null or number.'
163173
];
164174
const key = getLongKey();

client/__tests__/treatment.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ describe('get-treatment', () => {
250250

251251
test('should be 400 if options.properties is invalid (GET)', async () => {
252252
const expected = [
253-
'options.properties must only contain boolean, string, or number values.'
253+
'properties must only contain boolean, string, or number values.'
254254
];
255255
const response = await request(app)
256256
.get('/client/get-treatment?key=test&split-name=my-experiment&options={"properties":{"invalid":[1,2,3]}}')
@@ -260,7 +260,7 @@ describe('get-treatment', () => {
260260

261261
test('should be 400 if options.properties is invalid (POST)', async () => {
262262
const expected = [
263-
'options.properties must only contain boolean, string, or number values.'
263+
'properties must only contain boolean, string, or number values.'
264264
];
265265
const response = await request(app)
266266
.post('/client/get-treatment?key=test&split-name=my-experiment')

client/__tests__/treatmentWithConfig.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ describe('get-treatment-with-config', () => {
232232

233233
test('should be 400 if options.properties is invalid (GET)', async () => {
234234
const expected = [
235-
'options.properties must only contain boolean, string, or number values.'
235+
'properties must only contain boolean, string, or number values.'
236236
];
237237
const response = await request(app)
238238
.get('/client/get-treatment-with-config?key=test&split-name=my-experiment&options={"properties":{"invalid":[1,2,3]}}')
@@ -242,7 +242,7 @@ describe('get-treatment-with-config', () => {
242242

243243
test('should be 400 if options.properties is invalid (POST)', async () => {
244244
const expected = [
245-
'options.properties must only contain boolean, string, or number values.'
245+
'properties must only contain boolean, string, or number values.'
246246
];
247247
const response = await request(app)
248248
.post('/client/get-treatment-with-config?key=test&split-name=my-experiment')

client/__tests__/treatments.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,7 +270,7 @@ describe('get-treatments', () => {
270270

271271
test('should be 400 if options.properties is invalid (GET)', async () => {
272272
const expected = [
273-
'options.properties must only contain boolean, string, or number values.'
273+
'properties must only contain boolean, string, or number values.'
274274
];
275275
const response = await request(app)
276276
.get('/client/get-treatments?key=test&split-names=my-experiment&options={"properties":{"invalid":[1,2,3]}}')
@@ -280,7 +280,7 @@ describe('get-treatments', () => {
280280

281281
test('should be 400 if options.properties is invalid (POST)', async () => {
282282
const expected = [
283-
'options.properties must only contain boolean, string, or number values.'
283+
'properties must only contain boolean, string, or number values.'
284284
];
285285
const response = await request(app)
286286
.post('/client/get-treatments?key=test&split-names=my-experiment')

client/__tests__/treatmentsByFlagSets.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ describe('get-treatments-by-sets', () => {
276276

277277
test('should be 400 if options.properties is invalid (GET)', async () => {
278278
const expected = [
279-
'options.properties must only contain boolean, string, or number values.'
279+
'properties must only contain boolean, string, or number values.'
280280
];
281281
const response = await request(app)
282282
.get('/client/get-treatments-by-sets?key=test&flag-sets=set_green&options={"properties":{"invalid":[1,2,3]}}')
@@ -286,7 +286,7 @@ describe('get-treatments-by-sets', () => {
286286

287287
test('should be 400 if options.properties is invalid (POST)', async () => {
288288
const expected = [
289-
'options.properties must only contain boolean, string, or number values.'
289+
'properties must only contain boolean, string, or number values.'
290290
];
291291
const response = await request(app)
292292
.post('/client/get-treatments-by-sets?key=test&flag-sets=set_green')

client/__tests__/treatmentsWithConfig.test.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ describe('get-treatments-with-config', () => {
255255

256256
test('should be 400 if options.properties is invalid (GET)', async () => {
257257
const expected = [
258-
'options.properties must only contain boolean, string, or number values.'
258+
'properties must only contain boolean, string, or number values.'
259259
];
260260
const response = await request(app)
261261
.get('/client/get-treatments-with-config?key=test&split-names=my-experiment&options={"properties":{"invalid":[1,2,3]}}')
@@ -265,7 +265,7 @@ describe('get-treatments-with-config', () => {
265265

266266
test('should be 400 if options.properties is invalid (POST)', async () => {
267267
const expected = [
268-
'options.properties must only contain boolean, string, or number values.'
268+
'properties must only contain boolean, string, or number values.'
269269
];
270270
const response = await request(app)
271271
.post('/client/get-treatments-with-config?key=test&split-names=my-experiment')

client/client.router.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const attributesValidator = require('../utils/inputValidation/attributes');
88
const trafficTypeValidator = require('../utils/inputValidation/trafficType');
99
const eventTypeValidator = require('../utils/inputValidation/eventType');
1010
const valueValidator = require('../utils/inputValidation/value');
11-
const propertiesValidator = require('../utils/inputValidation/properties');
11+
const { validateProperties, validateEvaluationOptions } = require('../utils/inputValidation/properties');
1212
const keysValidator = require('../utils/inputValidation/keys');
1313
const clientController = require('./client.controller');
1414
const { parseValidators } = require('../utils/utils');
@@ -24,7 +24,7 @@ const treatmentValidation = (req, res, next) => {
2424
const bucketingKeyValidation = req.query['bucketing-key'] !== undefined ? keyValidator(req.query['bucketing-key'], 'bucketing-key') : null;
2525
const featureFlagNameValidation = splitValidator(req.query['split-name']);
2626
const attributesValidation = attributesValidator(req.query.attributes);
27-
const optionsValidation = propertiesValidator(req.query.options);
27+
const optionsValidation = validateEvaluationOptions(req.query.options);
2828

2929
const error = parseValidators([matchingKeyValidation, bucketingKeyValidation, featureFlagNameValidation, attributesValidation, optionsValidation]);
3030
if (error.length) {
@@ -58,7 +58,7 @@ const treatmentsValidation = (req, res, next) => {
5858
const bucketingKeyValidation = req.query['bucketing-key'] !== undefined ? keyValidator(req.query['bucketing-key'], 'bucketing-key') : null;
5959
const featureFlagsNameValidation = splitsValidator(req.query['split-names']);
6060
const attributesValidation = attributesValidator(req.query.attributes);
61-
const optionsValidation = propertiesValidator(req.query.options);
61+
const optionsValidation = validateEvaluationOptions(req.query.options);
6262

6363
const error = parseValidators([matchingKeyValidation, bucketingKeyValidation, featureFlagsNameValidation, attributesValidation, optionsValidation]);
6464
if (error.length) {
@@ -92,7 +92,7 @@ const flagSetsValidation = (req, res, next) => {
9292
const bucketingKeyValidation = req.query['bucketing-key'] !== undefined ? keyValidator(req.query['bucketing-key'], 'bucketing-key') : null;
9393
const flagSetNameValidation = flagSetsValidator(req.query['flag-sets']);
9494
const attributesValidation = attributesValidator(req.query.attributes);
95-
const optionsValidation = propertiesValidator(req.query.options);
95+
const optionsValidation = validateEvaluationOptions(req.query.options);
9696

9797
const error = parseValidators([matchingKeyValidation, bucketingKeyValidation, flagSetNameValidation, attributesValidation, optionsValidation]);
9898
if (error.length) {
@@ -126,7 +126,7 @@ const trackValidation = (req, res, next) => {
126126
const trafficTypeValidation = trafficTypeValidator(req.query['traffic-type']);
127127
const eventTypeValidation = eventTypeValidator(req.query['event-type']);
128128
const valueValidation = valueValidator(req.query.value);
129-
const propertiesValidation = propertiesValidator(req.query.properties);
129+
const propertiesValidation = validateProperties(req.query.properties);
130130

131131
const error = parseValidators([keyValidation, trafficTypeValidation, eventTypeValidation, valueValidation, propertiesValidation]);
132132
if (error.length) {
@@ -157,7 +157,7 @@ const trackValidation = (req, res, next) => {
157157
const allTreatmentValidation = (req, res, next) => {
158158
const keysValidation = keysValidator(req.query.keys);
159159
const attributesValidation = attributesValidator(req.query.attributes);
160-
const optionsValidation = propertiesValidator(req.query.options);
160+
const optionsValidation = validateEvaluationOptions(req.query.options);
161161

162162
const error = parseValidators([keysValidation, attributesValidation, optionsValidation]);
163163
if (error.length) {
Lines changed: 108 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,41 +1,35 @@
1-
const propertiesValidator = require('../properties');
1+
const { validateProperties, validateEvaluationOptions } = require('../properties');
22

3-
describe('properties validator', () => {
3+
describe('validateProperties', () => {
44
test('should return error on invalid properties', done => {
5-
const expected = 'Input must be a plain object.';
6-
7-
const result = propertiesValidator('test');
8-
5+
const expected = 'properties must be a plain object.';
6+
const result = validateProperties('test');
97
expect(result).toHaveProperty('valid', false);
108
expect(result).toHaveProperty('error', expected);
119
expect(result).not.toHaveProperty('value');
1210
done();
1311
});
1412

1513
test('should return error on invalid properties 2', done => {
16-
const expected = 'Input must be a plain object.';
17-
18-
const result = propertiesValidator('[]');
19-
14+
const expected = 'properties must be a plain object.';
15+
const result = validateProperties('[]');
2016
expect(result).toHaveProperty('valid', false);
2117
expect(result).toHaveProperty('error', expected);
2218
expect(result).not.toHaveProperty('value');
2319
done();
2420
});
2521

2622
test('should return error on invalid properties 3', done => {
27-
const expected = 'Input must be a plain object.';
28-
29-
const result = propertiesValidator('true');
30-
23+
const expected = 'properties must be a plain object.';
24+
const result = validateProperties('true');
3125
expect(result).toHaveProperty('valid', false);
3226
expect(result).toHaveProperty('error', expected);
3327
expect(result).not.toHaveProperty('value');
3428
done();
3529
});
3630

3731
test('should be valid when properties is an object', done => {
38-
const result = propertiesValidator('{"my-prop":true}');
32+
const result = validateProperties('{"my-prop":true}');
3933
expect(result).toHaveProperty('valid', true);
4034
expect(result).toHaveProperty('value', {
4135
'my-prop': true,
@@ -44,21 +38,116 @@ describe('properties validator', () => {
4438
done();
4539
});
4640

41+
test('should be valid when properties is an object', done => {
42+
const expected = 'properties must only contain boolean, string, or number values.';
43+
const result = validateProperties('{"my-prop":[]}');
44+
expect(result).toHaveProperty('valid', false);
45+
expect(result).toHaveProperty('error', expected);
46+
expect(result).not.toHaveProperty('value');
47+
done();
48+
});
49+
4750
test('should be valid when properties is empty object', done => {
48-
const result = propertiesValidator('{}');
49-
51+
const result = validateProperties('{}');
5052
expect(result).toHaveProperty('valid', true);
5153
expect(result).toHaveProperty('value', {});
5254
expect(result).not.toHaveProperty('error');
5355
done();
5456
});
5557

5658
test('should be valid when properties is null', done => {
57-
const result = propertiesValidator();
58-
59+
const result = validateProperties();
5960
expect(result).toHaveProperty('valid', true);
6061
expect(result).toHaveProperty('value', null);
6162
expect(result).not.toHaveProperty('error');
6263
done();
6364
});
6465
});
66+
67+
describe('validateEvaluationOptions', () => {
68+
test('should return error on invalid options', done => {
69+
const expected = 'options must be a plain object.';
70+
const result = validateEvaluationOptions('test');
71+
expect(result).toHaveProperty('valid', false);
72+
expect(result).toHaveProperty('error', expected);
73+
expect(result).not.toHaveProperty('value');
74+
done();
75+
});
76+
77+
test('should return error on invalid options 2', done => {
78+
const expected = 'options must be a plain object.';
79+
const result = validateEvaluationOptions('[]');
80+
expect(result).toHaveProperty('valid', false);
81+
expect(result).toHaveProperty('error', expected);
82+
expect(result).not.toHaveProperty('value');
83+
done();
84+
});
85+
86+
test('should return error on invalid options 3', done => {
87+
const expected = 'options must be a plain object.';
88+
const result = validateEvaluationOptions('true');
89+
expect(result).toHaveProperty('valid', false);
90+
expect(result).toHaveProperty('error', expected);
91+
expect(result).not.toHaveProperty('value');
92+
done();
93+
});
94+
95+
test('should be valid when options is an object', done => {
96+
const result = validateEvaluationOptions('{"foo":1}');
97+
expect(result).toHaveProperty('valid', true);
98+
expect(result).toHaveProperty('value', { foo: 1 });
99+
expect(result).not.toHaveProperty('error');
100+
done();
101+
});
102+
103+
test('should be valid when options is empty object', done => {
104+
const result = validateEvaluationOptions('{}');
105+
expect(result).toHaveProperty('valid', true);
106+
expect(result).toHaveProperty('value', {});
107+
expect(result).not.toHaveProperty('error');
108+
done();
109+
});
110+
111+
test('should be valid when options is null', done => {
112+
const result = validateEvaluationOptions();
113+
expect(result).toHaveProperty('valid', true);
114+
expect(result).toHaveProperty('value', null);
115+
expect(result).not.toHaveProperty('error');
116+
done();
117+
});
118+
119+
test('should return error if options.properties is not valid', done => {
120+
const expected = 'properties must be a plain object.';
121+
const result = validateEvaluationOptions('{"properties": "not-an-object"}');
122+
expect(result).toHaveProperty('valid', false);
123+
expect(result).toHaveProperty('error', expected);
124+
expect(result).not.toHaveProperty('value');
125+
done();
126+
});
127+
128+
test('should return error if options.properties has invalid value type', done => {
129+
const expected = 'properties must only contain boolean, string, or number values.';
130+
const result = validateEvaluationOptions('{"properties": {"foo": []}}');
131+
expect(result).toHaveProperty('valid', false);
132+
expect(result).toHaveProperty('error', expected);
133+
expect(result).not.toHaveProperty('value');
134+
done();
135+
});
136+
137+
test('should be valid if options.properties is valid', done => {
138+
const result = validateEvaluationOptions('{"properties": {"foo": 1, "bar": "baz", "baz": true}}');
139+
expect(result).toHaveProperty('valid', true);
140+
expect(result).toHaveProperty('value', { foo: 1, bar: 'baz', baz: true });
141+
expect(result).not.toHaveProperty('error');
142+
done();
143+
});
144+
145+
test('should be valid if options.properties is valid', done => {
146+
const expected = 'properties must only contain boolean, string, or number values.';
147+
const result = validateEvaluationOptions('{"properties": {"foo": [], "bar": "baz", "baz": true}}');
148+
expect(result).toHaveProperty('valid', false);
149+
expect(result).toHaveProperty('error', expected);
150+
expect(result).not.toHaveProperty('value');
151+
done();
152+
});
153+
});

0 commit comments

Comments
 (0)