Skip to content

Commit

Permalink
Merge pull request #179 from Kashoo/issue-176-uuid
Browse files Browse the repository at this point in the history
Issue 176: Add UUID validation type
  • Loading branch information
ziemek authored Jan 24, 2018
2 parents e48b727 + 969a1c0 commit 1d3ecc9
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- [#108](https://github.com/Kashoo/synctos/issues/108): Finer grained control over whether null and missing values are accepted
- [#127](https://github.com/Kashoo/synctos/issues/127): Immutable constraints that treat null and missing values as different
- [#128](https://github.com/Kashoo/synctos/issues/128): Equality constraint that treats null and missing values as different
- [#176](https://github.com/Kashoo/synctos/issues/176): UUID data validation type

### Changed
- [#118](https://github.com/Kashoo/synctos/issues/118): Embed indent.js as a static dependency
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ Validation for simple data types (e.g. integers, floating point numbers, strings
* `maximumValueExclusive`: Reject dates that are greater than or equal to this. May be either an ISO 8601 date string without time and time zone components OR a JavaScript `Date` object. No restriction by default.
* `enum`: The value must be one of the specified predefined string and/or integer values. Additional parameters:
* `predefinedValues`: A list of strings and/or integers that are to be accepted. If this parameter is omitted from an `enum` property's configuration, that property will not accept a value of any kind. For example: `[ 1, 2, 3, 'a', 'b', 'c' ]`
* `uuid`: The value must be a string representation of a [universally unique identifier](https://en.wikipedia.org/wiki/Universally_unique_identifier) (UUID). A UUID may contain either uppercase or lowercase letters so that, for example, both "1511fba4-e039-42cc-9ac2-9f2fa29eecfc" and "DFF421EA-0AB2-45C9-989C-12C76E7282B8" are valid.
* `attachmentReference`: The value is the name of one of the document's file attachments. Note that, because the addition of an attachment is often a separate Sync Gateway API operation from the creation/replacement of the associated document, this validation type is only applied if the attachment is actually present in the document. However, since the sync function is run twice in such situations (i.e. once when the _document_ is created/replaced and once when the _attachment_ is created/replaced), the validation will be performed eventually. The top-level `allowAttachments` property should be `true` so that documents of this type can actually store attachments. Additional parameters:
* `supportedExtensions`: An array of case-insensitive file extensions that are allowed for the attachment's filename (e.g. "txt", "jpg", "pdf"). Takes precedence over the document-wide `supportedExtensions` constraint for the referenced attachment. No restriction by default.
* `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.
Expand Down
5 changes: 5 additions & 0 deletions samples/fragment-notification.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@
}
],
propertyValidators: {
eventId: {
type: 'uuid',
required: true,
immutable: true
},
sender: {
// Which Kashoo app/service generated the notification
type: 'string',
Expand Down
2 changes: 2 additions & 0 deletions src/document-definition-properties-validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ function validate(docDefinition, docPropertyValidatorDefinitions) {
break;
case 'enum':
break;
case 'uuid':
break;
case 'attachmentReference':
break;
case 'array':
Expand Down
9 changes: 9 additions & 0 deletions src/validation-error-formatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,15 @@ exports.unsupportedProperty = function(propertyPath) {
return 'property "' + propertyPath + '" is not supported';
};

/**
* Formats a message for the error that occurs when the format for a UUID is invalid.
*
* @param {string} itemPath The full path of the property or element in which the error occurs (e.g. "objectProp.arrayProp[10].uuidProp")
*/
exports.uuidFormatInvalid = function(propertyPath) {
return 'item "' + propertyPath + '" is not a valid UUID';
};

function getTypeDescription(type) {
switch (type) {
case 'array':
Expand Down
11 changes: 11 additions & 0 deletions templates/sync-function-validation-module.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ function() {
return regex.test(value);
}

function isUuid(value) {
var regex = /^[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$/;

return regex.test(value);
}

// A regular expression that matches one of the given file extensions
function buildSupportedExtensionsRegex(extensions) {
// Note that this regex uses double quotes rather than single quotes as a workaround to https://github.com/Kashoo/synctos/issues/116
Expand Down Expand Up @@ -297,6 +303,11 @@ function() {
validationErrors.push('item "' + buildItemPath(itemStack) + '" must be one of the predefined values: ' + enumPredefinedValues.toString());
}
break;
case 'uuid':
if (!isUuid(itemValue)) {
validationErrors.push('item "' + buildItemPath(itemStack) + '" is not a valid UUID');
}
break;
case 'object':
var childPropertyValidators = resolveValidationConstraint(validator.propertyValidators);
if (typeof itemValue !== 'object' || itemValue instanceof Array) {
Expand Down
11 changes: 11 additions & 0 deletions test/resources/uuid-doc-definitions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
myDocType: {
typeFilter: simpleTypeFilter,
channels: { write: 'write' },
propertyValidators: {
uuidProp: {
type: 'uuid'
}
}
}
}
13 changes: 11 additions & 2 deletions test/sample-notification.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ describe('Sample business notification doc definition', function() {
it('successfully creates a valid notification document', function() {
var doc = {
_id: 'biz.63.notification.5',
eventId: '082979cf-6990-44a6-bb62-9b9517c3052b',
sender: 'test-service',
type: 'invoice-payments',
subject: 'pay up!',
Expand All @@ -77,6 +78,7 @@ describe('Sample business notification doc definition', function() {
it('cannot create a notification document when the properties are invalid', function() {
var doc = {
_id: 'biz.13.notification.5',
eventId: 'not-a-uuid',
type: true ,
subject: '', // missing sender, empty subject
'whatsthis?': 'something I dont recognize!', // unrecognized property
Expand All @@ -98,13 +100,15 @@ describe('Sample business notification doc definition', function() {
errorFormatter.requiredValueViolation('actions[0].label'),
errorFormatter.requiredValueViolation('actions[1]'),
errorFormatter.unsupportedProperty('whatsthis?'),
errorFormatter.datetimeFormatInvalid('firstReadAt')
errorFormatter.datetimeFormatInvalid('firstReadAt'),
errorFormatter.uuidFormatInvalid('eventId')
]);
});

it('successfully replaces a valid notification document', function() {
var doc = {
_id: 'biz.7.notification.3',
eventId: '1d856cd8-a0db-473c-9ea0-20b3113e2571',
type: 'invoice-payments',
sender: 'test-service',
subject: 'a different subject',
Expand All @@ -116,6 +120,7 @@ describe('Sample business notification doc definition', function() {
};
var oldDoc = {
_id: 'biz.7.notification.3',
eventId: '1d856cd8-a0db-473c-9ea0-20b3113e2571',
type: 'invoice-payments',
sender: 'test-service',
subject: 'a different subject',
Expand All @@ -131,6 +136,7 @@ describe('Sample business notification doc definition', function() {
it('cannot replace a notification document when the properties are invalid', function() {
var doc = {
_id: 'biz.10.notification.3',
eventId: '692d8c84-8ff2-358-b806-edbf4c3c5813',
sender: '', // missing type, empty sender
message: '', // missing subject, empty message
createdAt: '2016-04-29T17:13:43.666Z', // changed createdAt
Expand All @@ -139,6 +145,7 @@ describe('Sample business notification doc definition', function() {
};
var oldDoc = { // valid oldDoc
_id: 'biz.10.notification.3',
eventId: '692d8c84-8ff2-358-b806-edbf4c3c5813',
type: 'invoice-payments',
sender: 'test-service',
subject: 'a different subject',
Expand All @@ -165,13 +172,15 @@ describe('Sample business notification doc definition', function() {
errorFormatter.immutableItemViolation('actions'),
errorFormatter.requiredValueViolation('actions[0].url'),
errorFormatter.mustNotBeEmptyViolation('actions[0].label'),
errorFormatter.immutableItemViolation('firstReadAt')
errorFormatter.immutableItemViolation('firstReadAt'),
errorFormatter.uuidFormatInvalid('eventId')
]);
});

it('successfully deletes a valid notification document', function() {
var oldDoc = {
_id: 'biz.71.notification.5',
eventId: '56be8a52-f050-4d72-b4cb-c4f6eb2ca3ed',
type: 'invoice-payments',
sender: 'test-service',
subject: 'pay up!',
Expand Down
68 changes: 68 additions & 0 deletions test/uuid.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
var testHelper = require('../src/test-helper.js');
var errorFormatter = testHelper.validationErrorFormatter;

describe('UUID validation type', function() {
beforeEach(function() {
testHelper.initSyncFunction('build/sync-functions/test-uuid-sync-function.js');
});

it('allows a valid UUID with lowercase letters', function() {
var doc = {
_id: 'my-doc',
type: 'myDocType',
uuidProp: '1511fba4-e039-42cc-9ac2-9f2fa29eecfc'
};

testHelper.verifyDocumentCreated(doc);
});

it('allows a valid UUID with uppercase letters', function() {
var doc = {
_id: 'my-doc',
type: 'myDocType',
uuidProp: 'DFF421EA-0AB2-45C9-989C-12C76E7282B8'
};

testHelper.verifyDocumentCreated(doc);
});

it('rejects a UUID with invalid characters', function() {
var doc = {
_id: 'my-doc',
type: 'myDocType',
uuidProp: 'g78d516e-cb95-4ef7-b593-2ee7ce375738'
};

testHelper.verifyDocumentNotCreated(doc, 'myDocType', [ errorFormatter.uuidFormatInvalid('uuidProp') ]);
});

it('rejects a UUID without hyphens', function() {
var doc = {
_id: 'my-doc',
type: 'myDocType',
uuidProp: '1511fba4e03942cc9ac29f2fa29eecfc'
};

testHelper.verifyDocumentNotCreated(doc, 'myDocType', [ errorFormatter.uuidFormatInvalid('uuidProp') ]);
});

it('rejects a UUID with too many characters', function() {
var doc = {
_id: 'my-doc',
type: 'myDocType',
uuidProp: '1511fba4-e039-42cc-9ac2-9f2fa29eecfc3'
};

testHelper.verifyDocumentNotCreated(doc, 'myDocType', [ errorFormatter.uuidFormatInvalid('uuidProp') ]);
});

it('rejects a UUID with too few characters', function() {
var doc = {
_id: 'my-doc',
type: 'myDocType',
uuidProp: '1511fba4-e03-42cc-9ac2-9f2fa29eecfc'
};

testHelper.verifyDocumentNotCreated(doc, 'myDocType', [ errorFormatter.uuidFormatInvalid('uuidProp') ]);
});
});

0 comments on commit 1d3ecc9

Please sign in to comment.