Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: added API to disable and enable validation #180

Merged
merged 1 commit into from Jun 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
30 changes: 30 additions & 0 deletions README.md
Expand Up @@ -263,6 +263,36 @@ class Plugin {
export default Plugin;
```

### Allow to disable and enable validation (the `validate` function do nothing)

This can be useful when you don't want to do validation for `production` builds.

```js
import { disableValidation, enableValidation, validate } from "schema-utils";

// Disable validation
disableValidation();
// Do nothing
validate(schema, options);

// Enable validation
enableValidation();
// Will throw an error if schema is not valid
validate(schema, options);

// Allow to undestand do you need validation or not
const need = needValidate();

console.log(need);
```

Also you can enable/disable validation using the `process.env.SKIP_VALIDATION` env variable.

Supported values (case insensitive):

- `yes`/`y`/`true`/`1`/`on`
- `no`/`n`/`false`/`0`/`off`

## Contributing

Please take a moment to read our contributing guidelines if you haven't yet done so.
Expand Down
11 changes: 10 additions & 1 deletion declarations/index.d.ts
@@ -1,3 +1,12 @@
import { validate } from "./validate";
import { ValidationError } from "./validate";
export { validate, ValidationError };
import { enableValidation } from "./validate";
import { disableValidation } from "./validate";
import { needValidate } from "./validate";
export {
validate,
ValidationError,
enableValidation,
disableValidation,
needValidate,
};
3 changes: 3 additions & 0 deletions declarations/validate.d.ts
Expand Up @@ -34,5 +34,8 @@ export function validate(
options: Array<object> | object,
configuration?: ValidationErrorConfiguration | undefined
): void;
export function enableValidation(): void;
export function disableValidation(): void;
export function needValidate(): boolean;
import ValidationError from "./ValidationError";
export { ValidationError };
16 changes: 14 additions & 2 deletions src/index.js
@@ -1,3 +1,15 @@
const { validate, ValidationError } = require("./validate");
const {
validate,
ValidationError,
enableValidation,
disableValidation,
needValidate,
} = require("./validate");

module.exports = { validate, ValidationError };
module.exports = {
validate,
ValidationError,
enableValidation,
disableValidation,
needValidate,
};
59 changes: 57 additions & 2 deletions src/validate.js
Expand Up @@ -55,7 +55,7 @@ const getAjv = memoize(() => {

/** @typedef {(JSONSchema4 | JSONSchema6 | JSONSchema7) & Extend} Schema */

/** @typedef {ErrorObject & { children?: Array<ErrorObject>}} SchemaUtilErrorObject */
/** @typedef {ErrorObject & { children?: Array<ErrorObject> }} SchemaUtilErrorObject */

/**
* @callback PostFormatter
Expand Down Expand Up @@ -87,13 +87,62 @@ function applyPrefix(error, idx) {
return error;
}

let skipValidation = false;

// We use `process.env.SKIP_VALIDATION` because you can have multiple `schema-utils` with different version,
// so we want to disable it globally, `process.env` doesn't supported by browsers, so we have the local `skipValidation` variables

// Enable validation
function enableValidation() {
skipValidation = false;

// Disable validation for any versions
if (process && process.env) {
process.env.SKIP_VALIDATION = "n";
}
}

// Disable validation
function disableValidation() {
skipValidation = true;

if (process && process.env) {
process.env.SKIP_VALIDATION = "y";
}
}

// Check if we need to confirm
function needValidate() {
if (skipValidation) {
return false;
}

if (process && process.env && process.env.SKIP_VALIDATION) {
const value = process.env.SKIP_VALIDATION.trim();

if (/^(?:y|yes|true|1|on)$/i.test(value)) {
return false;
}

if (/^(?:n|no|false|0|off)$/i.test(value)) {
return true;
}
}

return true;
}

/**
* @param {Schema} schema
* @param {Array<object> | object} options
* @param {ValidationErrorConfiguration=} configuration
* @returns {void}
*/
function validate(schema, options, configuration) {
if (!needValidate()) {
return;
}

let errors = [];

if (Array.isArray(options)) {
Expand Down Expand Up @@ -165,4 +214,10 @@ function filterErrors(errors) {
return newErrors;
}

export { validate, ValidationError };
export {
validate,
enableValidation,
disableValidation,
needValidate,
ValidationError,
};
18 changes: 18 additions & 0 deletions test/__snapshots__/api.test.js.snap
@@ -1,5 +1,23 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`api should allow to enable validation using "process.env.SKIP_VALIDATION" #2 1`] = `
"Invalid options object. NAME has been initialized using an options object that does not match the API schema.
- options has an unknown property 'foo'. These properties are valid:
object { name? }"
`;

exports[`api should allow to enable validation using "process.env.SKIP_VALIDATION" 1`] = `
"Invalid options object. NAME has been initialized using an options object that does not match the API schema.
- options has an unknown property 'foo'. These properties are valid:
object { name? }"
`;

exports[`api should allow to enable validation using API 1`] = `
"Invalid options object. NAME has been initialized using an options object that does not match the API schema.
- options has an unknown property 'foo'. These properties are valid:
object { name? }"
`;

exports[`api should get configuration from schema 1`] = `
"Invalid options object. CSS Loader has been initialized using an options object that does not match the API schema.
- options has an unknown property 'foo'. These properties are valid:
Expand Down
137 changes: 136 additions & 1 deletion test/api.test.js
@@ -1,4 +1,10 @@
import { validate, ValidationError } from "../src/index";
import {
validate,
ValidationError,
enableValidation,
disableValidation,
needValidate,
} from "../src/index";

import schema from "./fixtures/schema.json";
import schemaTitle from "./fixtures/schema-title.json";
Expand Down Expand Up @@ -196,4 +202,133 @@ describe("api", () => {
expect(error.message).toMatchSnapshot();
}
});

it('should allow to disable validation using "process.env.SKIP_VALIDATION"', () => {
const oldValue = process.env.SKIP_VALIDATION;

let errored;

process.env.SKIP_VALIDATION = "y";

try {
validate(schemaTitle, { foo: "bar" }, { name: "NAME" });
} catch (error) {
errored = error;
}

expect(errored).toBeUndefined();

process.env.SKIP_VALIDATION = oldValue;
});

it('should allow to disable validation using "process.env.SKIP_VALIDATION" #2', () => {
const oldValue = process.env.SKIP_VALIDATION;

let errored;

process.env.SKIP_VALIDATION = "YeS";

try {
validate(schemaTitle, { foo: "bar" }, { name: "NAME" });
} catch (error) {
errored = error;
}

expect(errored).toBeUndefined();

process.env.SKIP_VALIDATION = oldValue;
});

it('should allow to enable validation using "process.env.SKIP_VALIDATION"', () => {
const oldValue = process.env.SKIP_VALIDATION;

process.env.SKIP_VALIDATION = "n";

try {
validate(schemaTitle, { foo: "bar" }, { name: "NAME" });
} catch (error) {
if (error.name !== "ValidationError") {
throw error;
}

expect(error.message).toMatchSnapshot();
}

process.env.SKIP_VALIDATION = oldValue;
});

it('should allow to enable validation using "process.env.SKIP_VALIDATION" #2', () => {
const oldValue = process.env.SKIP_VALIDATION;

process.env.SKIP_VALIDATION = " FaLse ";

try {
validate(schemaTitle, { foo: "bar" }, { name: "NAME" });
} catch (error) {
if (error.name !== "ValidationError") {
throw error;
}

expect(error.message).toMatchSnapshot();
}

process.env.SKIP_VALIDATION = oldValue;
});

it("should allow to disable validation using API", () => {
let errored;

disableValidation();

try {
validate(schemaTitle, { foo: "bar" }, { name: "NAME" });
} catch (error) {
errored = error;
}

expect(errored).toBeUndefined();

enableValidation();
});

it("should allow to enable validation using API", () => {
disableValidation();
enableValidation();

try {
validate(schemaTitle, { foo: "bar" }, { name: "NAME" });
} catch (error) {
if (error.name !== "ValidationError") {
throw error;
}

expect(error.message).toMatchSnapshot();
}
});

it("should allow to enable and disable validation using API", () => {
enableValidation();
expect(needValidate()).toBe(true);

disableValidation();
expect(needValidate()).toBe(false);
enableValidation();

enableValidation();
enableValidation();
expect(needValidate()).toBe(true);

enableValidation();
disableValidation();
expect(needValidate()).toBe(false);
enableValidation();

enableValidation();
expect(process.env.SKIP_VALIDATION).toBe("n");

disableValidation();
expect(process.env.SKIP_VALIDATION).toBe("y");
enableValidation();
expect(process.env.SKIP_VALIDATION).toBe("n");
});
});