Skip to content

Commit 63d7835

Browse files
authored
Merge pull request #327 from Kashoo/324-any-type
Issue 324: Support for any JSON data type
2 parents 3368678 + d20f35e commit 63d7835

File tree

7 files changed

+189
-1
lines changed

7 files changed

+189
-1
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). All notable c
44
## [Unreleased]
55
### Added
66
- [#323](https://github.com/Kashoo/synctos/issues/323): Option to ignore item validation errors when value is unchanged
7+
- [#324](https://github.com/Kashoo/synctos/issues/324): Validation type that accepts any type of value
78

89
## [2.5.0] - 2018-05-30
910
### Added

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,7 @@ Validation for simple data types (e.g. integers, floating point numbers, strings
473473
* `supportedContentTypes`: An array of content/MIME types that are allowed for the attachment's contents (e.g. "image/png", "text/html", "application/xml"). Takes precedence over the document-wide `supportedContentTypes` constraint for the referenced attachment. No restriction by default.
474474
* `maximumSize`: The maximum file size, in bytes, of the attachment. May not be greater than 20MB (20,971,520 bytes), as Couchbase Server/Sync Gateway sets that as the hard limit per document or attachment. Takes precedence over the document-wide `maximumIndividualSize` constraint for the referenced attachment. Unlimited by default.
475475
* `regexPattern`: A regular expression pattern that must be satisfied by the value. Takes precedence over the document-wide `attachmentConstraints.filenameRegexPattern` constraint for the referenced attachment. No restriction by default.
476+
* `any`: The value may be any JSON data type: number, string, boolean, array or object. No additional parameters.
476477

477478
##### Complex type validation
478479

src/validation/document-definitions-validator.spec.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,12 @@ describe('Document definitions validator:', () => {
222222
type: 'object',
223223
mustEqual: (a, b, c, d) => d,
224224
propertyValidators: { } // Must specify at least one property validator
225+
},
226+
anyProperty: {
227+
type: 'any',
228+
minimumValue: 32, // Not supported by the "any" type
229+
mustNotBeEmpty: false, // Not supported by the "any" type
230+
regexPattern: /^foo$/ // Not supported by the "any" type
225231
}
226232
}
227233
}
@@ -310,7 +316,10 @@ describe('Document definitions validator:', () => {
310316
'myDoc1.propertyValidators.nestedObject.propertyValidators.arrayProperty.arrayElementsValidator.propertyValidators.invalidMustEqualConstraintProperty.mustEqual: \"mustEqual\" must be an object',
311317
'myDoc1.propertyValidators.nestedObject.propertyValidators.arrayProperty.arrayElementsValidator.propertyValidators.emptyPropertyValidatorsProperty.propertyValidators: \"propertyValidators\" must have at least 1 children',
312318
'myDoc1.propertyValidators.nestedObject.propertyValidators.arrayProperty.arrayElementsValidator.propertyValidators.noTypeProperty.type: "type" is required',
313-
'myDoc1.propertyValidators.nestedObject.propertyValidators.unrecognizedTypeProperty.type: "type" must be one of [array, attachmentReference, boolean, date, datetime, enum, float, hashtable, integer, object, string, time, timezone, uuid]',
319+
'myDoc1.propertyValidators.nestedObject.propertyValidators.arrayProperty.arrayElementsValidator.propertyValidators.anyProperty.minimumValue: \"minimumValue\" is not allowed',
320+
'myDoc1.propertyValidators.nestedObject.propertyValidators.arrayProperty.arrayElementsValidator.propertyValidators.anyProperty.mustNotBeEmpty: \"mustNotBeEmpty\" is not allowed',
321+
'myDoc1.propertyValidators.nestedObject.propertyValidators.arrayProperty.arrayElementsValidator.propertyValidators.anyProperty.regexPattern: \"regexPattern\" is not allowed',
322+
'myDoc1.propertyValidators.nestedObject.propertyValidators.unrecognizedTypeProperty.type: \"type\" must be one of [any, array, attachmentReference, boolean, date, datetime, enum, float, hashtable, integer, object, string, time, timezone, uuid]',
314323
'myDoc1.expiry: \"expiry\" with value \"20180415T1357-0700\" fails to match the required pattern: /^\\d{4}-(((0[13578]|1[02])-(0[1-9]|[12]\\d|3[01]))|((0[469]|11)-(0[1-9]|[12]\\d|30))|(02-(0[1-9]|[12]\\d)))T([01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(Z|[+-]([01]\\d|2[0-3]):[0-5]\\d)$/',
315324
]);
316325
});

src/validation/property-validator-schema.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ const timeOnlySchema = joi.string().regex(/^((([01]\d|2[0-3])(:[0-5]\d)(:[0-5]\d
3030
const timezoneSchema = joi.string().regex(/^(Z|([+-])([01]\d|2[0-3]):([0-5]\d))$/);
3131

3232
const typeEqualitySchemas = {
33+
any: joi.any(),
3334
string: joi.string(),
3435
integer: integerSchema,
3536
float: joi.number(),
@@ -51,6 +52,9 @@ const validPropertyTypes = Object.keys(typeEqualitySchemas).sort();
5152
const schema = joi.object().keys({
5253
type: dynamicConstraintSchema(joi.string().only(validPropertyTypes)).required()
5354
})
55+
.when(
56+
joi.object().unknown().keys({ type: 'any' }),
57+
{ then: makeTypeConstraintsSchema('any') })
5458
.when(
5559
joi.object().unknown().keys({ type: 'string' }),
5660
{ then: makeTypeConstraintsSchema('string') })
@@ -107,6 +111,7 @@ module.exports = exports = schema;
107111
// references between the complex types (e.g. "array", "object", "hashtable") and the main "propertyValidators" schema
108112
function typeSpecificConstraintSchemas() {
109113
return {
114+
any: { },
110115
string: {
111116
mustNotBeEmpty: dynamicConstraintSchema(joi.boolean()),
112117
mustBeTrimmed: dynamicConstraintSchema(joi.boolean()),

templates/sync-function/document-properties-validation-module.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ function documentPropertiesValidationModule(utils, simpleTypeFilter, typeIdValid
179179
}
180180

181181
switch (validatorType) {
182+
case 'any':
183+
// Any type of value is allowed - no further validation required
184+
break;
182185
case 'string':
183186
if (typeof itemValue !== 'string') {
184187
validationErrors.push('item "' + buildItemPath(itemStack) + '" must be a string');

test/any-type.spec.js

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
const testFixtureMaker = require('../src/testing/test-fixture-maker');
2+
const errorFormatter = require('../src/testing/validation-error-formatter');
3+
4+
describe('Any validation type:', () => {
5+
const testFixture = testFixtureMaker.initFromSyncFunction('build/sync-functions/test-any-type-sync-function.js');
6+
7+
afterEach(() => {
8+
testFixture.resetTestEnvironment();
9+
});
10+
11+
describe('for array elements', () => {
12+
it('allows string, number, boolean, array and object values in an array', () => {
13+
const doc = {
14+
_id: 'my-doc',
15+
type: 'anyTypeDoc',
16+
arrayProp: [
17+
'a-string',
18+
-117.8,
19+
true,
20+
[ 'foo', 'bar' ],
21+
{ baz: 'qux' }
22+
]
23+
};
24+
25+
testFixture.verifyDocumentCreated(doc);
26+
});
27+
28+
it('respects universal constraints (e.g. "required")', () => {
29+
const doc = {
30+
_id: 'my-doc',
31+
type: 'anyTypeDoc',
32+
arrayProp: [
33+
'',
34+
0,
35+
null
36+
]
37+
};
38+
39+
testFixture.verifyDocumentNotCreated(doc, 'anyTypeDoc', errorFormatter.requiredValueViolation('arrayProp[2]'));
40+
});
41+
});
42+
43+
describe('for hashtable elements', () => {
44+
it('allows string, number, boolean, array and object values in a hashtable', () => {
45+
const doc = {
46+
_id: 'my-doc',
47+
type: 'anyTypeDoc',
48+
hashtableProp: {
49+
1: 'another-string',
50+
2: 13,
51+
3: false,
52+
4: [ 0, 1, 2 ],
53+
5: { }
54+
}
55+
};
56+
57+
testFixture.verifyDocumentCreated(doc);
58+
});
59+
60+
it('respects universal constraints (e.g. "immutableWhenSet")', () => {
61+
const oldDoc = {
62+
_id: 'my-doc',
63+
type: 'anyTypeDoc',
64+
hashtableProp: {
65+
1: 1.9,
66+
2: true,
67+
3: 'one-more-string',
68+
4: null // Can be changed since it doesn't have a value yet
69+
}
70+
};
71+
72+
const doc = {
73+
_id: 'my-doc',
74+
type: 'anyTypeDoc',
75+
hashtableProp: {
76+
1: 85, // Changed
77+
2: true,
78+
3: 'one-more-string',
79+
4: [ ]
80+
}
81+
};
82+
83+
testFixture.verifyDocumentNotReplaced(
84+
doc,
85+
oldDoc,
86+
'anyTypeDoc',
87+
errorFormatter.immutableItemViolation('hashtableProp[1]'));
88+
});
89+
});
90+
91+
describe('for object properties', () => {
92+
it('allows a string value', () => {
93+
const doc = {
94+
_id: 'my-doc',
95+
type: 'anyTypeDoc',
96+
anyProp: 'a-string'
97+
};
98+
99+
testFixture.verifyDocumentCreated(doc);
100+
});
101+
102+
it('allows a numeric value', () => {
103+
const doc = {
104+
_id: 'my-doc',
105+
type: 'anyTypeDoc',
106+
anyProp: -115.8
107+
};
108+
109+
testFixture.verifyDocumentCreated(doc);
110+
});
111+
112+
it('allows a boolean value', () => {
113+
const doc = {
114+
_id: 'my-doc',
115+
type: 'anyTypeDoc',
116+
anyProp: false
117+
};
118+
119+
testFixture.verifyDocumentCreated(doc);
120+
});
121+
122+
it('allows an array value', () => {
123+
const doc = {
124+
_id: 'my-doc',
125+
type: 'anyTypeDoc',
126+
anyProp: [ 'foo', 'bar' ]
127+
};
128+
129+
testFixture.verifyDocumentCreated(doc);
130+
});
131+
132+
it('allows an object value', () => {
133+
const doc = {
134+
_id: 'my-doc',
135+
type: 'anyTypeDoc',
136+
anyProp: { foo: 'bar' }
137+
};
138+
139+
testFixture.verifyDocumentCreated(doc);
140+
});
141+
});
142+
});
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
function() {
2+
return {
3+
anyTypeDoc: {
4+
typeFilter: simpleTypeFilter,
5+
channels: { write: 'write' },
6+
propertyValidators: {
7+
arrayProp: {
8+
type: 'array',
9+
arrayElementsValidator: {
10+
type: 'any',
11+
required: true
12+
}
13+
},
14+
hashtableProp: {
15+
type: 'hashtable',
16+
hashtableValuesValidator: {
17+
type: 'any',
18+
immutableWhenSet: true
19+
}
20+
},
21+
anyProp: {
22+
type: 'any'
23+
}
24+
}
25+
}
26+
};
27+
}

0 commit comments

Comments
 (0)