Skip to content

Commit

Permalink
updated README
Browse files Browse the repository at this point in the history
  • Loading branch information
kapv89 committed Apr 13, 2018
1 parent e6d5198 commit 4f3385c
Show file tree
Hide file tree
Showing 9 changed files with 394 additions and 8 deletions.
84 changes: 84 additions & 0 deletions README.md
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);
}
});
}
```
5 changes: 5 additions & 0 deletions common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use strict';

var Shape = require('./Shape').default;

module.exports = Shape;
142 changes: 142 additions & 0 deletions lib/Shape.js
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;
5 changes: 5 additions & 0 deletions lib/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
'use strict';

var Shape = require('./Shape').default;

module.exports = Shape;
8 changes: 8 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 10 additions & 7 deletions package.json
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",
Expand All @@ -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",
Expand All @@ -35,6 +37,7 @@
"eslint-plugin-react": "^7.7.0"
},
"dependencies": {
"@krab/isuseableobject": "^1.0.1",
"lodash": "^4.17.5"
}
}
91 changes: 91 additions & 0 deletions src/Shape.js
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);
}
});
}
}
3 changes: 3 additions & 0 deletions src/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const Shape = require('./Shape').default;

module.exports = Shape;
Loading

0 comments on commit 4f3385c

Please sign in to comment.