diff --git a/README.md b/README.md index 695c0ee..641875f 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,9 @@ $ npm run bench ``` # Table of contents -- [Key features](#key-features) + +- [fastest-validator ![NPM version](https://www.npmjs.com/package/fastest-validator) [![Tweet](https://img.shields.io/twitter/url/http/shields.io.svg?style=social)](https://twitter.com/intent/tweet?text=The%20fastest%20JS%20validator%20library%20for%20NodeJS&url=https://github.com/icebob/fastest-validator&via=Icebobcsi&hashtags=nodejs,javascript)](#fastest-validator-img-src%22httpswwwnpmjscompackagefastest-validator%22-alt%22npm-version%22-img-src%22httpsimgshieldsiotwitterurlhttpshieldsiosvgstylesocial%22-alt%22tweet%22) + - [Key features](#key-features) - [How fast?](#how-fast) - [Table of contents](#table-of-contents) - [Installation](#installation) @@ -70,29 +72,31 @@ $ npm run bench - [Properties](#properties) - [`boolean`](#boolean) - [Properties](#properties-1) - - [`date`](#date) + - [`class`](#class) - [Properties](#properties-2) - - [`email`](#email) + - [`date`](#date) - [Properties](#properties-3) - - [`enum`](#enum) + - [`email`](#email) - [Properties](#properties-4) - - [`equal`](#equal) + - [`enum`](#enum) - [Properties](#properties-5) - - [`forbidden`](#forbidden) + - [`equal`](#equal) - [Properties](#properties-6) + - [`forbidden`](#forbidden) + - [Properties](#properties-7) - [`function`](#function) - [`luhn`](#luhn) - [`mac`](#mac) - [`multi`](#multi) - [`number`](#number) - - [Properties](#properties-7) - - [`object`](#object) - [Properties](#properties-8) - - [`string`](#string) + - [`object`](#object) - [Properties](#properties-9) + - [`string`](#string) + - [Properties](#properties-10) - [`url`](#url) - [`uuid`](#uuid) - - [Properties](#properties-10) + - [Properties](#properties-11) - [Custom validator](#custom-validator) - [Custom validation for built-in rules](#custom-validation-for-built-in-rules) - [Custom error messages (l10n)](#custom-error-messages-l10n) @@ -460,6 +464,24 @@ v.validate({ status: "true" }, { }); // Valid ``` +## `class` +This is a `Class` validator to check the value is an instance of a Class. + +```js +const schema = { + rawData: { type: "class", instanceOf: Buffer } +} + +v.validate({ rawData: Buffer.from([1, 2, 3]) }, schema); // Valid +v.validate({ rawData: 100 }, schema); // Fail +``` + +### Properties +Property | Default | Description +-------- | -------- | ----------- +`instanceOf` | `null` | Checked Class. + + ## `date` This is a `Date` validator. diff --git a/examples/full.js b/examples/full.js index 25d0838..feb891d 100644 --- a/examples/full.js +++ b/examples/full.js @@ -41,6 +41,7 @@ const schema = { phone: { type: "string", length: 15, custom: v => v.startsWith("+") ? true : [{ type: "phoneNumber" }] }, action: "function", created: "date", + raw: { type: "class", instanceOf: Buffer }, now: { type: "date", convert: true } }; @@ -91,7 +92,8 @@ const obj = { phone: "+36-70-123-4567", action: () => {}, created: new Date(), - now: Date.now() + now: Date.now(), + raw: Buffer.from([1,2,3]) }; const res = v.validate(obj, schema); diff --git a/index.d.ts b/index.d.ts index 8362979..b432aab 100644 --- a/index.d.ts +++ b/index.d.ts @@ -6,6 +6,7 @@ declare module 'fastest-validator' { 'any' | 'array' | 'boolean' + | 'class' | 'custom' | 'date' | 'email' @@ -90,6 +91,21 @@ declare module 'fastest-validator' { convert?: boolean; } + /** + * Validation schema definition for "class" built-in validator + * @see https://github.com/icebob/fastest-validator#class + */ + interface RuleClass extends RuleCustom { + /** + * Name of built-in validator + */ + type: 'class'; + /** + * Checked Class + */ + instanceOf?: T; + } + /** * Validation schema definition for "date" built-in validator * @see https://github.com/icebob/fastest-validator#date @@ -662,6 +678,7 @@ declare module 'fastest-validator' { RuleAny | RuleArray | RuleBoolean + | RuleClass | RuleDate | RuleEmail | RuleEqual @@ -745,7 +762,7 @@ declare module 'fastest-validator' { messages?: MessagesType, /** - * Default settings for rules + * Default settings for rules */ defaults?: { [key in ValidationRuleName]: ValidationSchema @@ -856,6 +873,7 @@ declare module 'fastest-validator' { RuleAny, RuleArray, RuleBoolean, + RuleClass, RuleDate, RuleEmail, RuleEnum, diff --git a/lib/messages.js b/lib/messages.js index 5f04d08..d463b00 100644 --- a/lib/messages.js +++ b/lib/messages.js @@ -62,4 +62,6 @@ module.exports = { uuid: "The '{field}' field must be a valid UUID.", uuidVersion: "The '{field}' field must be a valid UUID version provided.", + + classInstanceOf: "The '{field}' field must be an instance of the '{expected}' class." }; diff --git a/lib/rules/class.js b/lib/rules/class.js new file mode 100644 index 0000000..5455855 --- /dev/null +++ b/lib/rules/class.js @@ -0,0 +1,29 @@ +"use strict"; + +/** Signature: function(value, field, parent, errors, context) + */ +module.exports = function({ schema, messages, customValidation }, path, context) { + const src = []; + + if (context.customs[path]) { + context.customs[path].instanceOf = schema.instanceOf; + } else { + context.customs[path] = { instanceOf: schema.instanceOf }; + } + + const className = schema.instanceOf.name ? schema.instanceOf.name : ""; + + src.push(` + if (!(value instanceof context.customs["${path}"].instanceOf)) + ${this.makeError({ type: "classInstanceOf", actual: "value", expected: "'" + className + "'", messages })} + `); + + src.push(` + ${customValidation("value")} + return value; + `); + + return { + source: src.join("\n") + }; +}; diff --git a/lib/validator.js b/lib/validator.js index 529325d..28089e6 100644 --- a/lib/validator.js +++ b/lib/validator.js @@ -9,6 +9,7 @@ function loadRules() { any: require("./rules/any"), array: require("./rules/array"), boolean: require("./rules/boolean"), + class: require("./rules/class"), custom: require("./rules/custom"), date: require("./rules/date"), email: require("./rules/email"), diff --git a/test/rules/class.spec.js b/test/rules/class.spec.js new file mode 100644 index 0000000..9f4d862 --- /dev/null +++ b/test/rules/class.spec.js @@ -0,0 +1,17 @@ +"use strict"; + +const Validator = require("../../lib/validator"); +const v = new Validator(); + +describe("Test rule: class", () => { + + it("should value instanceOf Buffer", () => { + const check = v.compile({ rawData: { type: "class", instanceOf: Buffer } }); + const message = "The 'rawData' field must be an instance of the 'Buffer' class."; + + expect(check({ rawData: "1234"})).toEqual([{ type: "classInstanceOf", field: "rawData", actual: "1234", expected: "Buffer", message }]); + expect(check({ rawData: [1, 2, 3]})).toEqual([{ type: "classInstanceOf", field: "rawData", actual: [1, 2, 3], expected: "Buffer", message }]); + expect(check({ rawData: Buffer.from([1, 2, 3]) })).toEqual(true); + expect(check({ rawData: Buffer.alloc(3) })).toEqual(true); + }); +}); diff --git a/test/typescript/rules/class.spec.ts b/test/typescript/rules/class.spec.ts new file mode 100644 index 0000000..e99b48d --- /dev/null +++ b/test/typescript/rules/class.spec.ts @@ -0,0 +1,18 @@ +/// // here we make a reference to exists module definition +import ValidatorType from 'fastest-validator'; // here we importing type definition of default export + +const Validator: typeof ValidatorType = require('../../../index'); // here we importing real Validator Constructor +const v: ValidatorType = new Validator(); + +describe("Test rule: class", () => { + + it("should value instanceOf Buffer", () => { + const check = v.compile({ rawData: { type: "class", instanceOf: Buffer } }); + const message = "The 'rawData' field must be an instance of the 'Buffer' class."; + + expect(check({ rawData: "1234"})).toEqual([{ type: "classInstanceOf", field: "rawData", actual: "1234", expected: "Buffer", message }]); + expect(check({ rawData: [1, 2, 3]})).toEqual([{ type: "classInstanceOf", field: "rawData", actual: [1, 2, 3], expected: "Buffer", message }]); + expect(check({ rawData: Buffer.from([1, 2, 3]) })).toEqual(true); + expect(check({ rawData: Buffer.alloc(3) })).toEqual(true); + }); +}); diff --git a/test/typescript/validator.spec.ts b/test/typescript/validator.spec.ts index 9cdf32b..e51fb79 100644 --- a/test/typescript/validator.spec.ts +++ b/test/typescript/validator.spec.ts @@ -14,7 +14,7 @@ describe('TypeScript Definitions', () => { expect(v.validate).toBeInstanceOf(Function); expect(v.add).toBeInstanceOf(Function); - expect(Object.keys(v.rules)).toHaveProperty('length', 18); + expect(Object.keys(v.rules)).toHaveProperty('length', 19); }); it('should create instance with custom messages', () => { diff --git a/test/validator.spec.js b/test/validator.spec.js index e748cf4..a4b3b3b 100644 --- a/test/validator.spec.js +++ b/test/validator.spec.js @@ -12,7 +12,7 @@ describe("Test constructor", () => { expect(v.validate).toBeInstanceOf(Function); expect(v.add).toBeInstanceOf(Function); - expect(Object.keys(v.rules).length).toBe(18); + expect(Object.keys(v.rules).length).toBe(19); }); it("should create instance with custom messages", () => { @@ -299,7 +299,7 @@ describe("Test compile (integration test)", () => { expect(res).toBe(true); expect(customValidator.mock.calls[0][2]).toBe("customValue"); }); - }); */ + }); */ }); describe("Test aliases", () => { @@ -364,7 +364,7 @@ describe("Test custom validation for built-in rules", () => { it("should compile without error", () => { - + check = v.compile({ num: { type: "number", @@ -399,7 +399,7 @@ describe("Test default settings", () => { strict: "remove" } } - }); + }); it("should consider default settings", () => { const check = v.compile({ @@ -452,4 +452,4 @@ describe("Test default settings", () => { } }); }); -}); \ No newline at end of file +});