From c2ea710003161d8ac29526ec8cc69d7113f39a8d Mon Sep 17 00:00:00 2001 From: Moses3301 Date: Tue, 1 Dec 2020 14:56:50 +0200 Subject: [PATCH] added: [N4S] allOf compound rule (#533) --- jsconfig.json | 1 + packages/n4s/docs/compound.md | 36 ++++++++++ packages/n4s/docs/template.md | 2 +- .../enforce/compounds/__tests__/allOf.test.js | 65 +++++++++++++++++++ .../enforce/compounds/__tests__/anyOf.test.js | 2 - packages/n4s/src/enforce/compounds/allOf.js | 10 +++ .../n4s/src/enforce/compounds/compounds.js | 6 +- .../__tests__/test_types/fixtures/enforce.ts | 4 ++ .../__tests__/test_types/fixtures/rules.ts | 4 ++ packages/vest/src/typings/enforce.d.ts | 2 + 10 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 packages/n4s/src/enforce/compounds/__tests__/allOf.test.js create mode 100644 packages/n4s/src/enforce/compounds/allOf.js diff --git a/jsconfig.json b/jsconfig.json index 537567dbe..dd6776f81 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -20,6 +20,7 @@ "runAnyoneMethods": ["./packages/anyone/src/runner/runAnyoneMethods.js"], "bindLazyRule": ["./packages/n4s/src/enforce/bindLazyRule.js"], "anyOf": ["./packages/n4s/src/enforce/compounds/anyOf.js"], + "allOf": ["./packages/n4s/src/enforce/compounds/allOf.js"], "oneOf": ["./packages/n4s/src/enforce/compounds/oneOf.js"], "compounds": ["./packages/n4s/src/enforce/compounds/compounds.js"], "isArrayOf": ["./packages/n4s/src/enforce/compounds/isArrayOf.js"], diff --git a/packages/n4s/docs/compound.md b/packages/n4s/docs/compound.md index 85a781546..8af31bbb2 100644 --- a/packages/n4s/docs/compound.md +++ b/packages/n4s/docs/compound.md @@ -3,6 +3,7 @@ Alongside the list of rules that only accept data provided by the user, enforce also supports compound rules - these are rules that accept other rules as their arguments. These rules let you validate more complex scenarios with the ergonomics of enforce. - [enforce.anyOf() - either/or validations](#anyof) +- [enforce.allOf() - all/and validations](#allof) - [enforce.shape() - Object's shape matching](#shape) - [enforce.optional() - nullable keys](#optional) - [enforec.loose() - loose shape matching](#loose) @@ -16,6 +17,41 @@ Sometimes a value has more than one valid possibilities, `any` lets us validate enforce(value).anyOf(enforce.isString(), enforce.isArray()).isNotEmpty(); // A valid value would either an array or a string. ``` +## enforce.allOf() - all/and validations :id=allof + +`allOf` lets us validate that a value passes _all_ of the supplied rules or templates. + +enforce(value).allOf( + enforce.isArray(), + enforce.longerThan(2) +); + +This can be even more useful when combined with shapes and templates: + +```js +const User = enforce.template( + enforce.loose({ + id: enforce.isNumber() + name: enforce.shape({ + first: enforce.isString(), + last: enforce.isString(), + middle: enforce.optional(enforce.isString()), + }), + }) +); + +const DisabledAccount = enforce.template( + enforce.loose({ + disabled: enforce.equals(true) + }) +) + +enforce(value).allOf( + User, + DisabledAccount +); +// A valid is string and longer then 5. +``` ## enforce.shape() - Lean schema validation. :id=shape diff --git a/packages/n4s/docs/template.md b/packages/n4s/docs/template.md index cf029b53b..302da59fe 100644 --- a/packages/n4s/docs/template.md +++ b/packages/n4s/docs/template.md @@ -26,7 +26,7 @@ Username('1234'); // throws Username('ab'); // throws ``` -You can also use templates inside other compound rules, such as `shape`, `isArrayOf` or `anyOf`. +You can also use templates inside other compound rules, such as `shape`, `isArrayOf` ,`anyOf` or `allOf`. ```js enforce({ diff --git a/packages/n4s/src/enforce/compounds/__tests__/allOf.test.js b/packages/n4s/src/enforce/compounds/__tests__/allOf.test.js new file mode 100644 index 000000000..e6bbe2914 --- /dev/null +++ b/packages/n4s/src/enforce/compounds/__tests__/allOf.test.js @@ -0,0 +1,65 @@ +import allOf from 'allOf'; +import enforce from 'enforce'; + +describe('allOf validation', () => { + describe('Base behavior', () => { + it('Should fail when at least one rule fails', () => { + expect(allOf('test', enforce.isString(), enforce.longerThan(10))).toBe( + false + ); + }); + it('Should succeed when all of the rules apply', () => { + expect(allOf('test', enforce.isString(), enforce.longerThan(3))).toBe( + true + ); + }); + it('Should pass when no rules are provided', () => { + expect(allOf(3)).toBe(true); + }); + }); + + describe('As part of enforce', () => { + const User = enforce.template( + enforce.loose({ + id: enforce.isNumber(), + name: enforce.shape({ + first: enforce.isString(), + last: enforce.isString(), + middle: enforce.optional(enforce.isString()), + }), + }) + ); + + const DisabledAccount = enforce.template( + enforce.loose({ + disabled: enforce.equals(true), + }) + ); + + it('Should validate allof the rules correctly', () => { + enforce({ + id: 123, + name: { first: 'Albert', last: 'Einstein' }, + disabled: true, + }).allOf(User, DisabledAccount); + }); + + it('Should throw if one of the rules fail', () => { + expect(() => { + enforce({ + id: 123, + name: { first: 'Albert', last: 0 }, + disabled: true, + }).allOf(User, DisabledAccount); + }).toThrow(); + + expect(() => { + enforce({ + id: 123, + name: { first: 'Albert', last: 'Einstein' }, + disabled: false, + }).allOf(User, DisabledAccount); + }).toThrow(); + }); + }); +}); diff --git a/packages/n4s/src/enforce/compounds/__tests__/anyOf.test.js b/packages/n4s/src/enforce/compounds/__tests__/anyOf.test.js index eaebc668f..0779d4df8 100644 --- a/packages/n4s/src/enforce/compounds/__tests__/anyOf.test.js +++ b/packages/n4s/src/enforce/compounds/__tests__/anyOf.test.js @@ -26,7 +26,6 @@ describe('AnyOf validation', () => { expect(anyOf(5)).toBe(true); }); }); - describe('As part of enforce', () => { it('Should validate anyof the rules correctly', () => { enforce(77).anyOf( @@ -34,7 +33,6 @@ describe('AnyOf validation', () => { enforce.isNumber(), enforce.isUndefined() ); - expect(() => enforce({ test: 4 }).anyOf(enforce.isNumber(), enforce.isUndefined()) ).toThrow(); diff --git a/packages/n4s/src/enforce/compounds/allOf.js b/packages/n4s/src/enforce/compounds/allOf.js new file mode 100644 index 000000000..cd43821b1 --- /dev/null +++ b/packages/n4s/src/enforce/compounds/allOf.js @@ -0,0 +1,10 @@ +import runLazyRules from 'runLazyRules'; +import { withFirst } from 'withArgs'; + +function allOf(value, rules) { + return ( + !rules.length || rules.every(ruleGroup => runLazyRules(ruleGroup, value)) + ); +} + +export default withFirst(allOf); diff --git a/packages/n4s/src/enforce/compounds/compounds.js b/packages/n4s/src/enforce/compounds/compounds.js index 78ba91afd..6d5f09a5e 100644 --- a/packages/n4s/src/enforce/compounds/compounds.js +++ b/packages/n4s/src/enforce/compounds/compounds.js @@ -1,3 +1,4 @@ +import allOf from 'allOf'; import anyOf from 'anyOf'; import isArrayOf from 'isArrayOf'; import oneOf from 'oneOf'; @@ -5,10 +6,11 @@ import optional from 'optional'; import { shape, loose } from 'shape'; export default { + allOf, anyOf, isArrayOf, loose, + oneOf, optional, - shape, - oneOf + shape }; diff --git a/packages/vest/src/__tests__/test_types/fixtures/enforce.ts b/packages/vest/src/__tests__/test_types/fixtures/enforce.ts index 416f8a789..79b382d20 100644 --- a/packages/vest/src/__tests__/test_types/fixtures/enforce.ts +++ b/packages/vest/src/__tests__/test_types/fixtures/enforce.ts @@ -88,3 +88,7 @@ enforce // anyOf enforce(1).anyOf(enforce.isNumber(), enforce.isArray()); enforce.anyOf(enforce.isNumber(), enforce.isArray()).test(1); + +// allOf +enforce('hello').allOf(enforce.isString(), enforce.longerThan(3)); +enforce.allOf(enforce.isString(), enforce.longerThan(3)).test('hello'); \ No newline at end of file diff --git a/packages/vest/src/__tests__/test_types/fixtures/rules.ts b/packages/vest/src/__tests__/test_types/fixtures/rules.ts index 81ce6d048..afef4eb00 100644 --- a/packages/vest/src/__tests__/test_types/fixtures/rules.ts +++ b/packages/vest/src/__tests__/test_types/fixtures/rules.ts @@ -103,12 +103,16 @@ enforce(0).shorterThanOrEquals; enforce.shorterThanOrEquals; enforce(0).startsWith; enforce.startsWith; +enforce(0).allOf; +enforce.allOf; enforce(0).anyOf; enforce.anyOf; enforce(0).isArrayOf; enforce.isArrayOf; enforce(0).loose; enforce.loose; +enforce(0).oneOf; +enforce.oneOf; enforce(0).optional; enforce.optional; enforce(0).shape; diff --git a/packages/vest/src/typings/enforce.d.ts b/packages/vest/src/typings/enforce.d.ts index a0100a911..d03e6856f 100644 --- a/packages/vest/src/typings/enforce.d.ts +++ b/packages/vest/src/typings/enforce.d.ts @@ -88,6 +88,7 @@ interface IEnforceRules { ) => RuleReturn; isArrayOf: CompoundListOfRules; anyOf: CompoundListOfRules; + allOf: CompoundListOfRules; oneOf: CompoundListOfRules; } @@ -195,6 +196,7 @@ type TEnforceLazy = { optional: LazyCopmoundListOfRules; isArrayOf: LazyCopmoundListOfRules; anyOf: LazyCopmoundListOfRules; + allOf: LazyCopmoundListOfRules; oneOf: LazyCopmoundListOfRules; };