diff --git a/README.md b/README.md index a7da48f..8999f19 100644 --- a/README.md +++ b/README.md @@ -292,7 +292,7 @@ The library contains several sanitizaters. **Please note, the sanitizers change ## Default values The most common sanitizer is the `default` property. With it, you can define a default value for all properties. If the property value is `null` or `undefined`, the validator set the defined default value into the property. -**Default value example**: +**Static Default value example**: ```js const schema = { roles: { type: "array", items: "string", default: ["user"] }, @@ -309,6 +309,27 @@ console.log(obj); status: true } */ +``` +**Dynamic Default value**: +Also you can use dynamic default value by defining a function that returns a value. For example, in the following code, if `createdAt` field not defined in object`, the validator sets the current time into the property: + +```js +const schema = { + createdAt: { + type: "date", + default: () => new Date() + } +}; + +const obj = {} + +v.validate(obj, schema); // Valid +console.log(obj); +/* +{ + createdAt: Date(2020-07-25T13:17:41.052Z) +} +*/ ``` # Shorthand definitions diff --git a/lib/validator.js b/lib/validator.js index 53e2522..0992238 100644 --- a/lib/validator.js +++ b/lib/validator.js @@ -93,9 +93,16 @@ class Validator { * @param {String?} resVar * @returns {String} */ - wrapRequiredCheckSourceCode(rule, innerSrc, resVar) { + wrapRequiredCheckSourceCode(rule, innerSrc, context, resVar) { const src = []; - const defaultValue = rule.schema.default != null ? JSON.stringify(rule.schema.default) : null; + let defaultValue = rule.schema.default != null ? JSON.stringify(rule.schema.default) : null; + if (typeof rule.schema.default === "function") { + if (!context.customs[rule.index]) context.customs[rule.index] = {}; + context.customs[rule.index].defaultFn = rule.schema.default; + + defaultValue = `context.customs[${rule.index}].defaultFn()`; + } + // Required, optional, forbidden src.push(` @@ -235,7 +242,7 @@ class Validator { ${innerSrc.replace(/%%INDEX%%/g, rule.index)} rule.cycleStack.pop(value); } - `, resVar)); + `, context, resVar)); } else { this.cache.set(rule.schema, rule); @@ -250,11 +257,11 @@ class Validator { res.source = res.source.replace(/%%INDEX%%/g, rule.index); const fn = new Function("value", "field", "parent", "errors", "context", res.source); context.fn[rule.index] = fn; - sourceCode.push(this.wrapRequiredCheckSourceCode(rule, innerSrc.replace(/%%INDEX%%/g, rule.index), resVar)); + sourceCode.push(this.wrapRequiredCheckSourceCode(rule, innerSrc.replace(/%%INDEX%%/g, rule.index), context, resVar)); sourceCode.push(this.makeCustomValidator({vName: resVar, path: customPath, schema: rule.schema, context, messages: rule.messages, ruleIndex: rule.index})); } else { - sourceCode.push(this.wrapRequiredCheckSourceCode(rule)); + sourceCode.push(this.wrapRequiredCheckSourceCode(rule, "", context)); } } diff --git a/test/integration.spec.js b/test/integration.spec.js index cbc583d..1e5768d 100644 --- a/test/integration.spec.js +++ b/test/integration.spec.js @@ -987,22 +987,21 @@ describe("Test strict schema restriction on sub-level", () => { describe("Test default value sanitizer", () => { const v = new Validator(); - let schema = { - id: { type: "number", default: 5 }, - name: { type: "string", default: "John" }, - age: { type: "number", optional: true, default: 33 }, - roles: { type: "array", items: "string", default: ["user"] }, - status: { type: "boolean", default: true } - }; - let check = v.compile(schema); - - it("should fill not defined properties", () => { - let obj = { + it("should fill not defined properties with static value", () => { + const schema = { + id: { type: "number", default: 5 }, + name: { type: "string", default: "John" }, + age: { type: "number", optional: true, default: 33 }, + roles: { type: "array", items: "string", default: ["user"] }, + status: { type: "boolean", default: true } + }; + const check = v.compile(schema); + const obj = { name: null, status: false }; - let res = check(obj); + const res = check(obj); expect(res).toBe(true); expect(obj).toEqual({ @@ -1013,5 +1012,23 @@ describe("Test default value sanitizer", () => { status: false }); }); + + it("should fill not defined properties with dynamic value", () => { + let number = { value: 0 }; + const check = v.compile({ + a: { + type: "number", + default: () => number.value++ + } + }); + + const o = {}; + + expect(check(o)).toBe(true); + expect(o.a).toBe(0); + delete o.a; + check(o); + expect(o.a).toBe(1); + }); });