Skip to content

Commit

Permalink
Implement Structure#validate method (#25)
Browse files Browse the repository at this point in the history
  • Loading branch information
talyssonoc authored Jan 17, 2017
1 parent fe9afdd commit 2586344
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 26 deletions.
46 changes: 46 additions & 0 deletions docs/validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,3 +255,49 @@ errors; /*
]
*/
```

### Validate raw data

In addition to the _instance_ `validate()` method, Structure also adds a _static_ `validate()` to your structure classes that receives a _raw object_ or a _structure instance_ as parameter and has the same return type of the [normal validation](#validation):

```javascript
const User = attributes({
name: {
type: String,
minLength: 10
},
age: {
type: Number,
required: true
}
})(class User { });

// Using a raw object
const rawData = {
name: 'John'
};

const { valid, errors } = User.validate(rawData);

valid; // false
errors; /*
[
{ message: '"name" length must be at least 10 characters long', path: 'name' },
{ message: '"age" is required', path: 'age' }
]
*/

// Using a structure instance
const user = new User({
name: 'Some long name'
});

const validation = User.validate(user);

validation.valid; // false
validation.errors; /*
[
{ message: '"age" is required', path: 'age' }
]
*/
```
8 changes: 7 additions & 1 deletion src/attributesDecorator.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ const { getInitialValues } = require('./initialValueCreation');
const { SCHEMA } = require('./symbols');
const {
attributesDescriptor,
validationDescriptor,
serializationDescriptor
} = require('./propertyDescriptors');

const {
validationDescriptor,
staticValidationDescriptor
} = require('./validation');

const define = Object.defineProperty;

function attributesDecorator(declaredSchema) {
Expand Down Expand Up @@ -40,6 +44,8 @@ function attributesDecorator(declaredSchema) {
value: declaredSchema
});

define(WrapperClass, 'validate', staticValidationDescriptor);

define(WrapperClass.prototype, SCHEMA, {
value: declaredSchema
});
Expand Down
20 changes: 1 addition & 19 deletions src/propertyDescriptors.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { SCHEMA, ATTRIBUTES, VALIDATE } = require('./symbols');
const { SCHEMA, ATTRIBUTES } = require('./symbols');
const { NON_OBJECT_ATTRIBUTES } = require('./errorMessages');
const { serialize } = require('./serialization');

Expand Down Expand Up @@ -30,24 +30,6 @@ exports.attributesDescriptor = {
}
};

exports.validationDescriptor = {
value: function validate() {
const validation = this[SCHEMA][VALIDATE];
const serializedStructure = this.toJSON();

const errors = validation.validate(serializedStructure);

if(errors) {
return {
valid: false,
errors
};
}

return { valid: true };
}
};

exports.serializationDescriptor = {
value: function toJSON() {
return serialize(this);
Expand Down
45 changes: 39 additions & 6 deletions src/validation/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
const joi = require('joi');

const { SCHEMA, VALIDATE } = require('../symbols');

const validations = [
require('./string'),
require('./number'),
Expand All @@ -10,7 +12,7 @@ const validations = [
const nestedValidation = require('./nested');
const arrayValidation = require('./array');

function validationForAttribute(typeDescriptor) {
exports.validationForAttribute = function validationForAttribute(typeDescriptor) {
if(typeDescriptor.itemType !== undefined) {
return arrayValidation(typeDescriptor, typeDescriptor.itemType);
}
Expand All @@ -22,7 +24,7 @@ function validationForAttribute(typeDescriptor) {
}

return validation.createJoiSchema(typeDescriptor);
}
};

const mapDetail = ({ message, path }) => ({ message, path });

Expand All @@ -32,7 +34,7 @@ const validatorOptions = {
allowUnknown: false
};

function validationForSchema(schema) {
exports.validationForSchema = function validationForSchema(schema) {
const schemaValidation = {};

Object.keys(schema).forEach((attributeName) => {
Expand All @@ -54,7 +56,38 @@ function validationForSchema(schema) {
return validationErrors;
}
};
}
};

exports.validationForAttribute = validationForAttribute;
exports.validationForSchema = validationForSchema;
exports.validationDescriptor = {
value: function validate() {
const validation = this[SCHEMA][VALIDATE];
const serializedStructure = this.toJSON();

return validateData(validation, serializedStructure);
}
};

exports.staticValidationDescriptor = {
value: function validate(data) {
if(data[SCHEMA]) {
data = data.toJSON();
}

const validation = this[SCHEMA][VALIDATE];

return validateData(validation, data);
}
};

function validateData(validation, data) {
const errors = validation.validate(data);

if(errors) {
return {
valid: false,
errors
};
}

return { valid: true };
}
81 changes: 81 additions & 0 deletions test/unit/validation/staticMethod.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
const { attributes } = require('../../../src');
const { expect } = require('chai');

describe('validation', () => {
describe('Using structure static method', () => {
var User;

before(() => {
User = attributes({
name: {
type: String,
required: true
},
age: {
type: Number,
min: 21
}
})(class User { });
});

context('when data is valid', () => {
it('returns valid as true and no errors', () => {
const { valid, errors } = User.validate({
name: 'The name',
age: 25
});

expect(valid).to.be.true;
expect(errors).to.be.undefined;
});
});

context('when data is invalid', () => {
it('returns valid as false and array of errors', () => {
const { valid, errors } = User.validate({
age: 10
});

expect(valid).to.be.false;
expect(errors).to.be.instanceOf(Array);
expect(errors).to.have.lengthOf(2);
expect(errors[0].path).to.equal('name');
expect(errors[1].path).to.equal('age');
});
});

context('when passed data is a structure', () => {
context('when structure is valid', () => {
it('returns valid as true and no errors', () => {
const user = new User({
name: 'Something',
age: 18
});

const { valid, errors } = User.validate(user);

expect(valid).to.be.false;
expect(errors).to.be.instanceOf(Array);
expect(errors).to.have.lengthOf(1);
expect(errors[0].path).to.equal('age');
});
});

context('when structure is invalid', () => {
it('returns valid as false and array of errors', () => {
const user = new User({
age: 10
});

const { valid, errors } = User.validate(user);

expect(valid).to.be.false;
expect(errors).to.be.instanceOf(Array);
expect(errors).to.have.lengthOf(2);
expect(errors[0].path).to.equal('name');
expect(errors[1].path).to.equal('age');
});
});
});
});
});

0 comments on commit 2586344

Please sign in to comment.