-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
394 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
## `shape` | ||
#### Isomorphic and Async object validation library. | ||
|
||
##### `npm install --save @krab/shape'` | ||
##### `import Shape from '@krab/shape'` | ||
##### `const Shape = require('@krab/shape/common')` | ||
|
||
`shape` is used to vaidate data in an async manner. Doesn't matter if your checks are all synchronous, `shape` will check them in an async manner. This is very useful for validating data against external data sources. Example below: | ||
|
||
```js | ||
import Shape from '@krab/shape'; | ||
|
||
// using async-await | ||
async function createUser(data={}) { | ||
const validation = new Shape({ | ||
username: async (username) => { | ||
if (isString(username) && username.length > 0) { | ||
const existing = await findUserByUsername(username); | ||
return existing ? 'username taken' : null; | ||
} else { | ||
return 'invalid'; | ||
} | ||
}, | ||
|
||
password: (password) => { | ||
return isString(password) && password.length >= 7 ? null : 'invalid'; | ||
}, | ||
|
||
repeat_password: (repeat_password, {password}) => { | ||
return repeat_password === password ? null : 'invalid'; | ||
} | ||
}); | ||
|
||
const errors = await validation.errors(data); | ||
// Errors will be 'null' if all checks pass. | ||
// If any check fails, errors will be an object that contains error messages. | ||
// Validation will also fail for keys in data which are not specified when | ||
// defining a validation | ||
|
||
if (errors) { | ||
throw errors; | ||
} else { | ||
return await insertNewPostInDB(data); | ||
} | ||
} | ||
|
||
// using promises | ||
function createPost(data={}) { | ||
const validation = new Shape({ | ||
author_id: async (authorId) => { | ||
const author = await findUserById(authorId); | ||
return author ? null : `invalid author_id: ${authorId}`; | ||
}, | ||
|
||
title: (title) => { | ||
return isString(title) ? findPostByTitle(title).then((existing) => { | ||
return ( | ||
existing ? 'title already taken' : ( | ||
title.length === 0 ? 'title cannot be empty' : null | ||
) | ||
); | ||
}) : 'title must be string'; | ||
}, | ||
|
||
body: (body) => { | ||
if (!isArray(body) || body.filter((p) => isString(p)).length < body.length) { | ||
return 'body must be a list of paragraph strings'; | ||
} else if (body.length === 0) { | ||
return 'body cannot be empty'; | ||
} else { | ||
return null; | ||
} | ||
} | ||
}); | ||
|
||
return validation.errors(data).then((errors) => { | ||
if (errors) { | ||
return Promise.reject(errors); | ||
} else { | ||
return insertNewPostInDB(data); | ||
} | ||
}); | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
'use strict'; | ||
|
||
var Shape = require('./Shape').default; | ||
|
||
module.exports = Shape; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
'use strict'; | ||
|
||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
|
||
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; | ||
|
||
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /** | ||
* Promise based validation of input | ||
* | ||
const Shape = require('shape-errors'); | ||
const s = new Shape({ | ||
user_id: (userId) => userExists(userId).then((exists) => exists ? null : 'invalid'), | ||
name: (name, {user_id}) => findUserByName(name).then( | ||
(user) => user.id === user_id ? null : 'invalid' | ||
) | ||
}) | ||
s.errors(data).then(({result, errors}) => {}) | ||
*/ | ||
|
||
|
||
var _lodash = require('lodash'); | ||
|
||
var _isuseableobject = require('@krab/isuseableobject'); | ||
|
||
var _isuseableobject2 = _interopRequireDefault(_isuseableobject); | ||
|
||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
|
||
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } | ||
|
||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
|
||
var Shape = function () { | ||
function Shape() { | ||
var validations = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; | ||
|
||
_classCallCheck(this, Shape); | ||
|
||
this.validations = new Map(); | ||
|
||
this.addValidations(validations); | ||
} | ||
|
||
_createClass(Shape, [{ | ||
key: 'addValidations', | ||
value: function addValidations() { | ||
var _this = this; | ||
|
||
var validations = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; | ||
|
||
if ((0, _isuseableobject2.default)(validations)) { | ||
validations = (0, _lodash.toPlainObject)(validations); | ||
validations = Object.keys(validations).map(function (k) { | ||
return { key: k, validation: validations[k] }; | ||
}); | ||
} | ||
|
||
validations.forEach(function (_ref) { | ||
var key = _ref.key, | ||
validation = _ref.validation; | ||
|
||
_this.validations.set(key, validation); | ||
}); | ||
|
||
return this; | ||
} | ||
}, { | ||
key: 'addValidation', | ||
value: function addValidation(_ref2) { | ||
var key = _ref2.key, | ||
validation = _ref2.validation; | ||
|
||
this.validations.set(key, validation); | ||
return this; | ||
} | ||
}, { | ||
key: 'merge', | ||
value: function merge(validator) { | ||
var _this2 = this; | ||
|
||
Array.from(validator.validation.keys()).forEach(function (k) { | ||
_this2.validations.set(k, validator.validations.get(k)); | ||
}); | ||
|
||
return this; | ||
} | ||
}, { | ||
key: 'errors', | ||
value: function errors() { | ||
var _this3 = this; | ||
|
||
var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; | ||
|
||
var invalidInputKeysErr = Object.keys(input).filter(function (k) { | ||
return Array.from(_this3.validations.keys()).indexOf(k) === -1; | ||
}).reduce(function (err, k) { | ||
return _extends({}, err, _defineProperty({}, k, 'invalid key')); | ||
}, {}); | ||
|
||
return Promise.all(Array.from(this.validations.keys()).map(function (key) { | ||
var err = _this3.validations.get(key)(input[key], input, key); | ||
|
||
if (err instanceof Promise) { | ||
return err.then(function (err) { | ||
return { key: key, err: err }; | ||
}); | ||
} else if (err instanceof Shape) { | ||
return err.errors(input[key]).then(function (err) { | ||
return { key: key, err: err }; | ||
}); | ||
} else { | ||
return { key: key, err: err }; | ||
} | ||
})).then(function (checks) { | ||
var checksFailed = checks.filter(function (_ref3) { | ||
var err = _ref3.err; | ||
return !!err; | ||
}); | ||
var numInvalidInputKeysError = Object.keys(invalidInputKeysErr).length; | ||
|
||
if (checksFailed.length === 0 && numInvalidInputKeysError === 0) { | ||
return null; | ||
} else { | ||
return checks.reduce(function (all, _ref4) { | ||
var key = _ref4.key, | ||
err = _ref4.err; | ||
|
||
return (0, _lodash.assign)(all, _defineProperty({}, key, err)); | ||
}, invalidInputKeysErr); | ||
} | ||
}); | ||
} | ||
}]); | ||
|
||
return Shape; | ||
}(); | ||
|
||
exports.default = Shape; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
'use strict'; | ||
|
||
var Shape = require('./Shape').default; | ||
|
||
module.exports = Shape; |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,8 @@ | ||
{ | ||
"name": "vent", | ||
"version": "1.0.0", | ||
"description": "An isomorphic event-aggregator", | ||
"main": "index.js", | ||
"description": "An isomorphic & async object-validation library", | ||
"main": "lib/Shape.js", | ||
"scripts": { | ||
"test": "babel-node test/main.js", | ||
"repl": "babel-node", | ||
|
@@ -12,18 +12,20 @@ | |
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+ssh://[email protected]/kapv89/vent.git" | ||
"url": "git+ssh://[email protected]/kapv89/shape.git" | ||
}, | ||
"keywords": [ | ||
"events", | ||
"isomorphic" | ||
"async", | ||
"isomorphic", | ||
"object-validation", | ||
"validation" | ||
], | ||
"author": "Kapil Verma <[email protected]>", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/kapv89/vent/issues" | ||
"url": "https://github.com/kapv89/shape/issues" | ||
}, | ||
"homepage": "https://github.com/kapv89/vent#readme", | ||
"homepage": "https://github.com/kapv89/shape#readme", | ||
"devDependencies": { | ||
"babel-cli": "^6.26.0", | ||
"babel-eslint": "^8.2.3", | ||
|
@@ -35,6 +37,7 @@ | |
"eslint-plugin-react": "^7.7.0" | ||
}, | ||
"dependencies": { | ||
"@krab/isuseableobject": "^1.0.1", | ||
"lodash": "^4.17.5" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
/** | ||
* Promise based validation of input | ||
* | ||
const Shape = require('shape-errors'); | ||
const s = new Shape({ | ||
user_id: (userId) => userExists(userId).then((exists) => exists ? null : 'invalid'), | ||
name: (name, {user_id}) => findUserByName(name).then( | ||
(user) => user.id === user_id ? null : 'invalid' | ||
) | ||
}) | ||
s.errors(data).then(({result, errors}) => {}) | ||
*/ | ||
import {assign, toPlainObject} from 'lodash'; | ||
import isuseableobject from '@krab/isuseableobject'; | ||
|
||
export default class Shape { | ||
constructor(validations=[]) { | ||
this.validations = new Map(); | ||
|
||
this.addValidations(validations); | ||
} | ||
|
||
addValidations(validations=[]) { | ||
if (isuseableobject(validations)) { | ||
validations = toPlainObject(validations); | ||
validations = Object.keys(validations).map((k) => ({key: k, validation: validations[k]})); | ||
} | ||
|
||
validations.forEach(({key, validation}) => { | ||
this.validations.set(key, validation); | ||
}); | ||
|
||
return this; | ||
} | ||
|
||
addValidation({key, validation}) { | ||
this.validations.set(key, validation); | ||
return this; | ||
} | ||
|
||
merge(validator) { | ||
Array.from(validator.validation.keys()).forEach((k) => { | ||
this.validations.set(k, validator.validations.get(k)); | ||
}); | ||
|
||
return this; | ||
} | ||
|
||
errors(input={}) { | ||
const invalidInputKeysErr = Object.keys(input) | ||
.filter((k) => { | ||
return Array.from(this.validations.keys()).indexOf(k) === -1; | ||
}) | ||
.reduce((err, k) => ({ | ||
...err, | ||
[k]: 'invalid key' | ||
}), {}) | ||
; | ||
|
||
return Promise.all( | ||
Array.from(this.validations.keys()).map((key) => { | ||
const err = this.validations.get(key)(input[key], input, key); | ||
|
||
if (err instanceof Promise) { | ||
return err.then((err) => { | ||
return {key, err}; | ||
}); | ||
} else if (err instanceof Shape) { | ||
return err.errors(input[key]).then((err) => { | ||
return {key, err}; | ||
}); | ||
} else { | ||
return {key, err}; | ||
} | ||
}) | ||
).then((checks) => { | ||
const checksFailed = checks.filter(({err}) => !!err); | ||
const numInvalidInputKeysError = Object.keys(invalidInputKeysErr).length; | ||
|
||
if (checksFailed.length === 0 && numInvalidInputKeysError === 0) { | ||
return null; | ||
} else { | ||
return checks.reduce((all, {key, err}) => { | ||
return assign(all, {[key]: err}); | ||
}, invalidInputKeysErr); | ||
} | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
const Shape = require('./Shape').default; | ||
|
||
module.exports = Shape; |
Oops, something went wrong.