Skip to content

Commit

Permalink
Merge pull request #74 from Kashoo/issue-72-type-id-validator
Browse files Browse the repository at this point in the history
Issue 72: Type identifier property validator
  • Loading branch information
ziemek authored Jan 18, 2017
2 parents 7d9477e + 04029b5 commit 021c990
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 25 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
### Added
- [#66](https://github.com/Kashoo/synctos/issues/66): Modular document definition files
- [#69](https://github.com/Kashoo/synctos/issues/69): Helper function to determine whether a document is missing or deleted
- [#72](https://github.com/Kashoo/synctos/issues/72): New property validation type for type identifier properties

## [1.5.0] - 2016-12-14
### Added
Expand Down
41 changes: 20 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,19 @@ Validation for complex data types, which allow for nesting of child properties a
}
```

The following predefined property validators may also be useful:

* `typeIdValidator`: A property validator that is suitable for application to the property that specifies the type of a document. Its constraints include ensuring the value is a string, is neither null nor undefined, is not an empty string and cannot be modified. An example:

```
propertyValidators: {
type: typeIdValidator,
foobar: {
type: 'string'
}
}
```

### Definition file

A document definitions file specifies all the document types that belong to a single Sync Gateway bucket/database. Such a file can contain either a plain JavaScript object or a JavaScript function that returns the documents' definitions wrapped in an object.
Expand All @@ -433,10 +446,7 @@ For example, a document definitions file implemented as an object:
return oldDoc ? oldDoc.type === docType : doc.type === docType;
},
propertyValidators: {
type: {
type: 'string',
required: true
},
type: typeIdValidator,
myProp1: {
type: 'integer'
}
Expand All @@ -451,10 +461,7 @@ For example, a document definitions file implemented as an object:
return oldDoc ? oldDoc.type === docType : doc.type === docType;
},
propertyValidators: {
type: {
type: 'string',
required: true
},
type: typeIdValidator,
myProp2: {
type: 'datetime'
}
Expand All @@ -471,10 +478,6 @@ function() {
view: 'view',
write: 'write'
};
var typePropertyValidator = {
type: 'string',
required: true
};
function myDocTypeFilter(doc, oldDoc, docType) {
return oldDoc ? oldDoc.type === docType : doc.type === docType;
Expand All @@ -485,7 +488,7 @@ function() {
channels: sharedChannels,
typeFilter: myDocTypeFilter,
propertyValidators: {
type: typePropertyValidator,
type: typeIdValidator,
myProp1: {
type: 'integer'
}
Expand All @@ -495,7 +498,7 @@ function() {
channels: sharedChannels,
typeFilter: myDocTypeFilter,
propertyValidators: {
type: typePropertyValidator,
type: typeIdValidator,
myProp2: {
type: 'datetime'
}
Expand All @@ -518,7 +521,7 @@ Document definitions are also modular. By invoking the `importDocumentDefinition
channels: sharedChannels,
typeFilter: myDocTypeFilter,
propertyValidators: {
type: typePropertyValidator,
type: typeIdValidator,
myProp1: {
type: 'integer'
}
Expand All @@ -533,7 +536,7 @@ Document definitions are also modular. By invoking the `importDocumentDefinition
channels: sharedChannels,
typeFilter: myDocTypeFilter,
propertyValidators: {
type: typePropertyValidator,
type: typeIdValidator,
myProp2: {
type: 'datetime'
}
Expand All @@ -549,10 +552,6 @@ function() {
view: 'view',
write: 'write'
};
var typePropertyValidator = {
type: 'string',
required: true
};
function myDocTypeFilter(doc, oldDoc, docType) {
return oldDoc ? oldDoc.type === docType : doc.type === docType;
Expand All @@ -569,7 +568,7 @@ As you can see, the fragments can also reference functions (e.g. `myDocTypeFilte

### Helper functions

Document definitions have access to some useful predefined functions that can help to reduce code duplication for common operations:
Custom code (e.g. type filters, custom validation functions, custom actions) within document definitions have access to some useful predefined functions for common operations:

* `isDocumentMissingOrDeleted(candidate)`: Determines whether the given `candidate` document is either missing (i.e. `null` or `undefined`) or deleted (i.e. its `_deleted` property is `true`). Useful in cases where, for example, the old document (i.e. `oldDoc` parameter) is non-existant or deleted and you want to treat both cases as equivalent.
* `isValueNullOrUndefined(value)`: Determines whether the given `value` parameter is either `null` or `undefined`. In many cases, it is useful to treat both states the same.
Expand Down
17 changes: 13 additions & 4 deletions etc/sync-function-template.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,25 @@ function synctos(doc, oldDoc) {
return isValueNullOrUndefined(candidate) || candidate._deleted;
}

// A property validator that is suitable for use on type identifier properties. Ensures the value is a string, is neither null nor
// undefined, is not an empty string and cannot be modified.
var typeIdValidator = {
type: 'string',
required: true,
mustNotBeEmpty: true,
immutable: true
};

// A type filter that matches on the document's type property
function simpleTypeFilter(doc, oldDoc, currentDocType) {
function simpleTypeFilter(doc, oldDoc, candidateDocType) {
if (oldDoc) {
if (doc._deleted) {
return oldDoc.type === currentDocType;
return oldDoc.type === candidateDocType;
} else {
return doc.type === oldDoc.type && oldDoc.type === currentDocType;
return doc.type === oldDoc.type && oldDoc.type === candidateDocType;
}
} else {
return doc.type === currentDocType;
return doc.type === candidateDocType;
}
}

Expand Down
13 changes: 13 additions & 0 deletions test/resources/type-id-validator-doc-definitions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
typeIdDoc: {
channels: {
write: [ 'write' ]
},
typeFilter: function(doc) {
return doc._id === 'typeIdDoc';
},
propertyValidators: {
typeIdProp: typeIdValidator
}
}
}
58 changes: 58 additions & 0 deletions test/type-id-validator-spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
var expect = require('expect.js');
var testHelper = require('../etc/test-helper.js');
var errorFormatter = testHelper.validationErrorFormatter;

describe('Type identifier validator', function() {
beforeEach(function() {
testHelper.init('build/sync-functions/test-type-id-validator-sync-function.js');
});

it('allows a valid string value', function() {
var doc = {
_id: 'typeIdDoc',
typeIdProp: 'my-doc-type'
};

testHelper.verifyDocumentCreated(doc);
});

it('rejects a non-string value', function() {
var doc = {
_id: 'typeIdDoc',
typeIdProp: 15
};

testHelper.verifyDocumentNotCreated(doc, 'typeIdDoc', [ errorFormatter.typeConstraintViolation('typeIdProp', 'string') ]);
});

it('rejects an empty string value', function() {
var doc = {
_id: 'typeIdDoc',
typeIdProp: ''
};

testHelper.verifyDocumentNotCreated(doc, 'typeIdDoc', [ errorFormatter.mustNotBeEmptyViolation('typeIdProp') ]);
});

it('rejects a null value', function() {
var doc = {
_id: 'typeIdDoc',
typeIdProp: null
};

testHelper.verifyDocumentNotCreated(doc, 'typeIdDoc', [ errorFormatter.requiredValueViolation('typeIdProp') ]);
});

it('rejects a value that has been modified', function() {
var doc = {
_id: 'typeIdDoc',
typeIdProp: 'my-modified-doc-type'
};
var oldDoc = {
_id: 'typeIdDoc',
typeIdProp: 'my-doc-type'
};

testHelper.verifyDocumentNotReplaced(doc, oldDoc, 'typeIdDoc', [ errorFormatter.immutableItemViolation('typeIdProp') ]);
});
});

0 comments on commit 021c990

Please sign in to comment.