diff --git a/docs/validation.md b/docs/validation.md index 4536ddb..0598585 100644 --- a/docs/validation.md +++ b/docs/validation.md @@ -62,9 +62,14 @@ Validations marked with __**__ accept a value or an [attribute reference](#attri - `lowerCase`: all characters must be lower cased - `upperCase`: all characters must be upper cased - `email`: is a valid email (default: `false`) +- `guid`: is a valid guid. You can pass a boolean or the [options object accepted by Joi](https://github.com/hapijs/joi/blob/v10.2.0/API.md#stringguid---aliases-uuid) (default: `false`) ```javascript const User = attributes({ + id: { + type: String, + guid: true + }, initials: { type: String, upperCase: true, diff --git a/new-docs/validation/string-validations.md b/new-docs/validation/string-validations.md index 1a6df12..6a24877 100644 --- a/new-docs/validation/string-validations.md +++ b/new-docs/validation/string-validations.md @@ -11,9 +11,14 @@ - `lowerCase`: all characters must be lower cased - `upperCase`: all characters must be upper cased - `email`: is a valid email (default: `false`) +- `guid`: is a valid guid. You can pass a boolean or the [options object accepted by Joi](https://github.com/hapijs/joi/blob/v10.2.0/API.md#stringguid---aliases-uuid) (default: `false`) ```javascript const User = attributes({ + id: { + type: String, + guid: true + }, initials: { type: String, upperCase: true, diff --git a/src/validation/string.js b/src/validation/string.js index 1ba009f..df8c41b 100644 --- a/src/validation/string.js +++ b/src/validation/string.js @@ -1,4 +1,5 @@ const joi = require('joi'); +const { isPlainObject } = require('lodash'); const { mapToJoi, equalOption } = require('./utils'); module.exports = { @@ -12,6 +13,7 @@ module.exports = { ['lowerCase', 'lowercase'], ['upperCase', 'uppercase'], ['email', 'email'], + ['guid', 'guid', isPlainObject], ['required', 'required'] ], createJoiSchema(typeDescriptor) { diff --git a/src/validation/utils.js b/src/validation/utils.js index 5aff74a..589e917 100644 --- a/src/validation/utils.js +++ b/src/validation/utils.js @@ -1,20 +1,26 @@ const joi = require('joi'); -const { isPlainObject } = require('lodash'); +const { isPlainObject, isFunction } = require('lodash'); exports.mapToJoi = function mapToJoi(typeDescriptor, { initial, mappings }) { return mappings.reduce((joiSchema, [optionName, joiMethod, passValueToJoi]) => { - if(typeDescriptor[optionName] === undefined) { + const attributeDescriptor = typeDescriptor[optionName]; + + if(attributeDescriptor === undefined) { return joiSchema; } - if(passValueToJoi) { - return joiSchema[joiMethod](typeDescriptor[optionName]); + if(shouldPassValueToJoi(passValueToJoi, attributeDescriptor)) { + return joiSchema[joiMethod](attributeDescriptor); } return joiSchema[joiMethod](); }, initial); }; +function shouldPassValueToJoi(passValueToJoi, attributeDescriptor) { + return passValueToJoi && (!isFunction(passValueToJoi) || passValueToJoi(attributeDescriptor)); +} + function mapValueOrReference(valueOrReference) { if(isPlainObject(valueOrReference)) { return joi.ref(valueOrReference.attr); @@ -25,15 +31,15 @@ function mapValueOrReference(valueOrReference) { exports.mapToJoiWithReference = function mapToJoiWithReference(typeDescriptor, { initial, mappings }) { return mappings.reduce((joiSchema, [optionName, joiMethod]) => { - var optionValue = typeDescriptor[optionName]; + var attributeDescriptor = typeDescriptor[optionName]; - if(optionValue === undefined) { + if(attributeDescriptor === undefined) { return joiSchema; } - optionValue = mapValueOrReference(optionValue); + attributeDescriptor = mapValueOrReference(attributeDescriptor); - return joiSchema[joiMethod](optionValue); + return joiSchema[joiMethod](attributeDescriptor); }, initial); }; diff --git a/test/unit/validation/string.spec.js b/test/unit/validation/string.spec.js index 5f29365..d7a83f9 100644 --- a/test/unit/validation/string.spec.js +++ b/test/unit/validation/string.spec.js @@ -443,5 +443,79 @@ describe('validation', () => { }); }); }); + + describe('guid', () => { + context('when validating as a generic guid', () => { + var User; + + beforeEach(() => { + User = attributes({ + id: { + type: String, + guid: true + } + })(class User {}); + }); + + context('when value is a valid guid', () => { + it('is valid', () => { + const user = new User({ + id: '759535af-3314-4ace-81b9-a519c29d0e17' + }); + + assertValid(user); + }); + }); + + context('when value is not a valid guid', () => { + it('is not valid and has errors set', () => { + const user = new User({ + id: 'Not a valid guid' + }); + + assertInvalid(user, 'id'); + }); + }); + }); + + context('when validating a specific guid version', () => { + var User; + + beforeEach(() => { + User = attributes({ + id: { + type: String, + guid: { + version: ['uuidv4'] + } + } + })(class User {}); + }); + + context('when value is a valid guid', () => { + it('is valid', () => { + const uuidv4 = 'f35e1cf1-4ac9-4fbb-9c06-151dc8ff9107'; + + const user = new User({ + id: uuidv4 + }); + + assertValid(user); + }); + }); + + context('when value is not a valid guid', () => { + it('is not valid and has errors set', () => { + const uuidv1 = 'c130564e-36d9-11e9-b210-d663bd873d93'; + + const user = new User({ + id: uuidv1 + }); + + assertInvalid(user, 'id'); + }); + }); + }); + }); }); });