From 4a7c1c42ee9cc6daf61032b769a0dfa18d8c7693 Mon Sep 17 00:00:00 2001 From: Matt Belford Date: Wed, 12 Aug 2020 14:25:49 -0600 Subject: [PATCH] fix: add float support fixes #5 --- src/base-validators/integer.test.ts | 49 ++------------------ src/base-validators/integer.ts | 57 ++--------------------- src/base-validators/number.test.ts | 53 +++++++++++++++++++++ src/base-validators/number.ts | 71 +++++++++++++++++++++++++++++ src/index.ts | 12 ++++- 5 files changed, 143 insertions(+), 99 deletions(-) create mode 100644 src/base-validators/number.test.ts create mode 100644 src/base-validators/number.ts diff --git a/src/base-validators/integer.test.ts b/src/base-validators/integer.test.ts index f871b02..d3fb181 100644 --- a/src/base-validators/integer.test.ts +++ b/src/base-validators/integer.test.ts @@ -1,53 +1,10 @@ import validator, { IntegerValidationError } from "./integer"; +import { NumberValidationError } from "./number"; describe("validator", () => { it("can validate and invalidate a integer", () => { - expect(validator({ type: "integer" }, "im a integer")).toBeInstanceOf(IntegerValidationError); expect(validator({ type: "integer" }, 123)).toBe(true); - expect(validator({ type: "integer" }, {})).toBeInstanceOf(IntegerValidationError); - expect(validator({ type: "integer" }, true)).toBeInstanceOf(IntegerValidationError); - expect(validator({ type: "integer" }, false)).toBeInstanceOf(IntegerValidationError); - expect(validator({ type: "integer" }, null)).toBeInstanceOf(IntegerValidationError); - expect(validator({ type: "integer" }, 123.123)).toBeInstanceOf(IntegerValidationError); - }); - - it("multipleOf", () => { - expect(validator({ type: "integer", multipleOf: 10 }, 100)).toBe(true); - expect(validator({ type: "integer", multipleOf: 100 }, 10)).toBeInstanceOf(IntegerValidationError); - }); - - it("maximum", () => { - expect(validator({ type: "integer", maximum: 10 }, 10)).toBe(true); - expect(validator({ type: "integer", maximum: 10 }, 11)).toBeInstanceOf(IntegerValidationError); - }); - - it("exclusiveMaximum", () => { - expect(validator({ type: "integer", exclusiveMaximum: 10 }, 9)).toBe(true); - expect(validator({ type: "integer", exclusiveMaximum: 10 }, 10)).toBeInstanceOf(IntegerValidationError); - }); - - it("minimum", () => { - expect(validator({ type: "integer", minimum: 10 }, 10)).toBe(true); - expect(validator({ type: "integer", minimum: 10 }, 9)).toBeInstanceOf(IntegerValidationError); - }); - - it("exclusiveMinimum", () => { - expect(validator({ type: "integer", exclusiveMinimum: 10 }, 11)).toBe(true); - expect(validator({ type: "integer", exclusiveMinimum: 10 }, 10)).toBeInstanceOf(IntegerValidationError); - }); - - it("const", () => { - expect(validator({ type: "integer", const: 123 }, 123)).toBe(true); - expect(validator({ type: "integer", const: 456 }, 123)).toBeInstanceOf(IntegerValidationError); - }); - - it("enum", () => { - expect(validator({ type: "integer", enum: [123] }, 123)).toBe(true); - expect(validator({ type: "integer", enum: [123] }, 456)).toBeInstanceOf(IntegerValidationError); - - expect(validator({ type: "integer", enum: [123, 456] }, 123)).toBe(true); - expect(validator({ type: "integer", enum: [123, 456] }, 1)).toBeInstanceOf(IntegerValidationError); - - expect(validator({ type: "integer", enum: ["foo", "123", "bar"] }, 123)).toBeInstanceOf(IntegerValidationError); + expect(validator({ type: "integer" }, "this is an integer")).toBeInstanceOf(NumberValidationError); + expect(validator({ type: "integer" }, 123.10)).toBeInstanceOf(IntegerValidationError); }); }); diff --git a/src/base-validators/integer.ts b/src/base-validators/integer.ts index f7519fa..c2b0f17 100644 --- a/src/base-validators/integer.ts +++ b/src/base-validators/integer.ts @@ -1,5 +1,6 @@ import ValidationError from "../validation-error"; import { JSONSchemaObject } from "@json-schema-tools/meta-schema"; +import NumberValidator, { NumberValidationError } from "./number"; export class IntegerValidationError implements Error { public name = "IntegerValidationError"; @@ -21,63 +22,15 @@ const isInt = (num: number) => { } export default (schema: JSONSchemaObject, d: any): true | ValidationError => { - if (typeof d !== "number") { - return new IntegerValidationError(schema, d, "Not a number type"); + + const validNumber = NumberValidator(schema, d); + if (validNumber !== true) { + return validNumber; } if (!isInt(d)) { return new IntegerValidationError(schema, d, "provided number is a float, not an integer"); } - if (schema.multipleOf) { - if (!isInt(d / schema.multipleOf)) { - return new IntegerValidationError(schema, d, `number is not a multiple of ${schema.multipleOf}`); - } - } - - if (schema.maximum) { - if (d > schema.maximum) { - return new IntegerValidationError(schema, d, `number exceeds maximum of ${schema.maximum}`); - } - } - - if (schema.exclusiveMaximum) { - if (d >= schema.exclusiveMaximum) { - return new IntegerValidationError( - schema, - d, - `number is greater than or equal to exclusive maximum of ${schema.exclusiveMaximum}` - ); - } - } - - if (schema.minimum) { - if (d < schema.minimum) { - return new IntegerValidationError(schema, d, `number is less than minimum of ${schema.minimum}`); - } - } - - if (schema.exclusiveMinimum) { - if (d <= schema.exclusiveMinimum) { - return new IntegerValidationError( - schema, - d, - `number is less than or equal to exclusive minimum of ${schema.exclusiveMinimum}` - ); - } - } - - if (schema.const) { - if (d !== schema.const) { - return new IntegerValidationError(schema, d, `must be: ${schema.const}`); - } - } - - if (schema.enum) { - if (schema.enum.indexOf(d) === -1) { - return new IntegerValidationError(schema, d, `must be one of: ${schema.enum}`); - } - } - return true; } diff --git a/src/base-validators/number.test.ts b/src/base-validators/number.test.ts new file mode 100644 index 0000000..00951de --- /dev/null +++ b/src/base-validators/number.test.ts @@ -0,0 +1,53 @@ +import validator, { NumberValidationError } from "./number"; + +describe("validator", () => { + it("can validate and invalidate a number", () => { + expect(validator({ type: "number" }, "im a number")).toBeInstanceOf(NumberValidationError); + expect(validator({ type: "number" }, 123)).toBe(true); + expect(validator({ type: "number" }, {})).toBeInstanceOf(NumberValidationError); + expect(validator({ type: "number" }, true)).toBeInstanceOf(NumberValidationError); + expect(validator({ type: "number" }, false)).toBeInstanceOf(NumberValidationError); + expect(validator({ type: "number" }, null)).toBeInstanceOf(NumberValidationError); + expect(validator({ type: "number" }, 123.123)).toBe(true); + }); + + it("multipleOf", () => { + expect(validator({ type: "number", multipleOf: 10 }, 100)).toBe(true); + expect(validator({ type: "number", multipleOf: 100 }, 10)).toBeInstanceOf(NumberValidationError); + }); + + it("maximum", () => { + expect(validator({ type: "number", maximum: 10 }, 10)).toBe(true); + expect(validator({ type: "number", maximum: 10 }, 11)).toBeInstanceOf(NumberValidationError); + }); + + it("exclusiveMaximum", () => { + expect(validator({ type: "number", exclusiveMaximum: 10 }, 9)).toBe(true); + expect(validator({ type: "number", exclusiveMaximum: 10 }, 10)).toBeInstanceOf(NumberValidationError); + }); + + it("minimum", () => { + expect(validator({ type: "number", minimum: 10 }, 10)).toBe(true); + expect(validator({ type: "number", minimum: 10 }, 9)).toBeInstanceOf(NumberValidationError); + }); + + it("exclusiveMinimum", () => { + expect(validator({ type: "number", exclusiveMinimum: 10 }, 11)).toBe(true); + expect(validator({ type: "number", exclusiveMinimum: 10 }, 10)).toBeInstanceOf(NumberValidationError); + }); + + it("const", () => { + expect(validator({ type: "number", const: 123 }, 123)).toBe(true); + expect(validator({ type: "number", const: 456 }, 123)).toBeInstanceOf(NumberValidationError); + }); + + it("enum", () => { + expect(validator({ type: "number", enum: [123] }, 123)).toBe(true); + expect(validator({ type: "number", enum: [123] }, 456)).toBeInstanceOf(NumberValidationError); + + expect(validator({ type: "number", enum: [123, 456] }, 123)).toBe(true); + expect(validator({ type: "number", enum: [123, 456] }, 1)).toBeInstanceOf(NumberValidationError); + + expect(validator({ type: "number", enum: ["foo", "123", "bar"] }, 123)).toBeInstanceOf(NumberValidationError); + }); +}); diff --git a/src/base-validators/number.ts b/src/base-validators/number.ts new file mode 100644 index 0000000..8378437 --- /dev/null +++ b/src/base-validators/number.ts @@ -0,0 +1,71 @@ +import ValidationError from "../validation-error"; +import { JSONSchemaObject } from "@json-schema-tools/meta-schema"; + +export class NumberValidationError implements Error { + public name = "NumberValidationError"; + public message: string; + + constructor(schema: JSONSchemaObject, data: any, reason: string) { + this.message = [ + "invalid data provided is not a valid integer", + `reason: ${reason}`, + ].join("\n"); + } +} +export default (schema: JSONSchemaObject, d: any): true | ValidationError => { + if (typeof d !== "number") { + return new NumberValidationError(schema, d, "Not a number type"); + } + + if (schema.multipleOf) { + if (d % schema.multipleOf !== 0) { + return new NumberValidationError(schema, d, `number is not a multiple of ${schema.multipleOf}`); + } + } + + if (schema.maximum) { + if (d > schema.maximum) { + return new NumberValidationError(schema, d, `number exceeds maximum of ${schema.maximum}`); + } + } + + if (schema.exclusiveMaximum) { + if (d >= schema.exclusiveMaximum) { + return new NumberValidationError( + schema, + d, + `number is greater than or equal to exclusive maximum of ${schema.exclusiveMaximum}` + ); + } + } + + if (schema.minimum) { + if (d < schema.minimum) { + return new NumberValidationError(schema, d, `number is less than minimum of ${schema.minimum}`); + } + } + + if (schema.exclusiveMinimum) { + if (d <= schema.exclusiveMinimum) { + return new NumberValidationError( + schema, + d, + `number is less than or equal to exclusive minimum of ${schema.exclusiveMinimum}` + ); + } + } + + if (schema.const) { + if (d !== schema.const) { + return new NumberValidationError(schema, d, `must be: ${schema.const}`); + } + } + + if (schema.enum) { + if (schema.enum.indexOf(d) === -1) { + return new NumberValidationError(schema, d, `must be one of: ${schema.enum}`); + } + } + + return true; +} diff --git a/src/index.ts b/src/index.ts index 28ed149..e407e3f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,10 +2,15 @@ import { JSONSchema, JSONSchemaObject } from "@json-schema-tools/meta-schema"; import traverse from "@json-schema-tools/traverse"; import StringValidator, { StringValidationError } from "./base-validators/string"; import BooleanValidator, { BooleanValidationError } from "./base-validators/boolean"; +import NumberValidator, { NumberValidationError } from "./base-validators/number"; import IntegerValidator, { IntegerValidationError } from "./base-validators/integer"; // import all the different validation errors -type ValidationError = StringValidationError | BooleanValidationError | IntegerValidationError; +type ValidationError = + StringValidationError | + BooleanValidationError | + IntegerValidationError | + NumberValidationError; export class ValidationErrors implements Error { public name = "ValidationErrors"; @@ -43,6 +48,11 @@ const validator = (schema: JSONSchema, data: any): true | ValidationErrors => { if (valid !== true) { errors.push(valid); } + } else if (schema.type === "number") { + const valid = NumberValidator(schema, data); + if (valid !== true) { + errors.push(valid); + } } if (errors.length === 0) {