Skip to content

Commit

Permalink
refactor: Upgrade "ajv" to v7 and "ajv-keywords" to v4 (#8703)
Browse files Browse the repository at this point in the history
  • Loading branch information
fredericbarthelet committed Jan 5, 2021
1 parent b093609 commit 1af73ba
Show file tree
Hide file tree
Showing 6 changed files with 26 additions and 89 deletions.
4 changes: 2 additions & 2 deletions lib/classes/ConfigSchemaHandler/index.js
@@ -1,6 +1,6 @@
'use strict';

const Ajv = require('ajv');
const Ajv = require('ajv').default;
const _ = require('lodash');
const schema = require('../../configSchema');
const normalizeAjvErrors = require('./normalizeAjvErrors');
Expand Down Expand Up @@ -98,7 +98,7 @@ class ConfigSchemaHandler {
this.relaxProviderSchema();
}

const ajv = new Ajv({ allErrors: true, coerceTypes: 'array', verbose: true });
const ajv = new Ajv({ allErrors: true, coerceTypes: 'array', verbose: true, strict: false });
require('ajv-keywords')(ajv, 'regexp');
// Workaround https://github.com/ajv-validator/ajv/issues/1255
normalizeSchemaObject(this.schema, this.schema);
Expand Down
20 changes: 8 additions & 12 deletions lib/classes/ConfigSchemaHandler/normalizeAjvErrors.js
@@ -1,12 +1,10 @@
'use strict';

const _ = require('lodash');
const resolveDataPathSize = require('./resolveDataPathSize');

const isEventTypeDataPath = RegExp.prototype.test.bind(/^\.functions\[[^\]]+\]\.events\[\d+\]$/);
const isEventTypeDataPath = RegExp.prototype.test.bind(/^\/functions\/[^/]+\/events\/\d+$/);
const oneOfPathPattern = /\/(?:anyOf|oneOf)(?:\/\d+\/|$)/;
const isAnyOfPathTypePostfix = RegExp.prototype.test.bind(/^\/\d+\/type$/);
const dataPathPropertyBracketsPattern = /\['([a-zA-Z_0-9]+)'\]/g;
const oneOfKeywords = new Set(['anyOf', 'oneOf']);

const filterIrreleventEventConfigurationErrors = (resultErrorsSet) => {
Expand Down Expand Up @@ -105,11 +103,7 @@ const filterIrrelevantAnyOfErrors = (resultErrorsSet) => {
let currentDataPath = oneOfPathErrors[0].dataPath;
Object.values(
_.groupBy(oneOfPathErrors, ({ dataPath }) => {
if (
dataPath !== currentDataPath &&
!dataPath.startsWith(`${currentDataPath}.`) &&
!dataPath.startsWith(`${currentDataPath}[`)
) {
if (dataPath !== currentDataPath && !dataPath.startsWith(`${currentDataPath}/`)) {
currentDataPath = dataPath;
}
return currentDataPath;
Expand Down Expand Up @@ -160,7 +154,9 @@ const filterIrrelevantAnyOfErrors = (resultErrorsSet) => {
let deepestDataPathSize = 0;
for (const dataPathOneOfPathVariantErrors of dataPathOneOfPathVariants) {
dataPathOneOfPathVariantErrors.deepestDataPathSize = Math.max(
...dataPathOneOfPathVariantErrors.map(({ dataPath }) => resolveDataPathSize(dataPath))
...dataPathOneOfPathVariantErrors.map(
({ dataPath }) => (dataPath.match(/\//g) || []).length
)
);
if (dataPathOneOfPathVariantErrors.deepestDataPathSize > deepestDataPathSize) {
deepestDataPathSize = dataPathOneOfPathVariantErrors.deepestDataPathSize;
Expand Down Expand Up @@ -201,15 +197,15 @@ const filterIrrelevantAnyOfErrors = (resultErrorsSet) => {
const normalizeDataPath = (dataPath) => {
if (!dataPath) return 'root';

// This regex helps replace functions['someFunc'].foo with functions.someFunc.foo
return `'${dataPath.slice(1).replace(dataPathPropertyBracketsPattern, '.$1')}'`;
// This code removes leading / and replaces / with . in error path indication
return `'${dataPath.slice(1).replace(/\//g, '.')}'`;
};

const improveMessages = (resultErrorsSet) => {
for (const error of resultErrorsSet) {
switch (error.keyword) {
case 'additionalProperties':
if (error.dataPath === '.functions') {
if (error.dataPath === '/functions') {
error.message = `name '${error.params.additionalProperty}' must be alphanumeric`;
} else {
error.message = `unrecognized property '${error.params.additionalProperty}'`;
Expand Down
41 changes: 0 additions & 41 deletions lib/classes/ConfigSchemaHandler/resolveDataPathSize.js

This file was deleted.

4 changes: 2 additions & 2 deletions package.json
Expand Up @@ -26,8 +26,8 @@
"@serverless/components": "^3.4.3",
"@serverless/enterprise-plugin": "^4.4.0",
"@serverless/utils": "^2.1.0",
"ajv": "^6.12.6",
"ajv-keywords": "^3.5.2",
"ajv": "^7.0.3",
"ajv-keywords": "^4.0.0",
"archiver": "^5.1.0",
"aws-sdk": "^2.819.0",
"bluebird": "^3.7.2",
Expand Down
Expand Up @@ -2,7 +2,7 @@

const { expect } = require('chai');

const Ajv = require('ajv');
const Ajv = require('ajv').default;
const memoize = require('memoizee');
const normalizeAjvErrors = require('../../../../../lib/classes/ConfigSchemaHandler/normalizeAjvErrors');

Expand Down Expand Up @@ -185,7 +185,7 @@ describe('#normalizeAjvErrors', () => {
it('should report error for unrecognized deep level property', () =>
expect(
errors.some((error) => {
if (error.dataPath !== '.package') return false;
if (error.dataPath !== '/package') return false;
if (error.keyword !== 'additionalProperties') return false;
error.isExpected = true;
return true;
Expand All @@ -194,7 +194,7 @@ describe('#normalizeAjvErrors', () => {
it('should report error for invalid function name', () =>
expect(
errors.some((error) => {
if (error.dataPath !== '.functions') return false;
if (error.dataPath !== '/functions') return false;
if (error.keyword !== 'additionalProperties') return false;
error.isExpected = true;
return true;
Expand All @@ -203,7 +203,7 @@ describe('#normalizeAjvErrors', () => {
it('should report error for unrecognized event', () =>
expect(
errors.some((error) => {
if (error.dataPath !== ".functions['foo'].events[0]") return false;
if (error.dataPath !== '/functions/foo/events/0') return false;
if (error.keyword !== 'anyOf') return false;
error.isExpected = true;
return true;
Expand All @@ -212,7 +212,7 @@ describe('#normalizeAjvErrors', () => {
it('should report error for unrecognized property at event type configuration level', () =>
expect(
errors.some((error) => {
if (error.dataPath !== ".functions['foo'].events[1]") return false;
if (error.dataPath !== '/functions/foo/events/1') return false;
if (error.keyword !== 'additionalProperties') return false;
error.isExpected = true;
return true;
Expand All @@ -231,7 +231,7 @@ describe('#normalizeAjvErrors', () => {
// method: GET # Should be additionally indented
expect(
errors.some((error) => {
if (error.dataPath !== ".functions['foo'].events[2]") return false;
if (error.dataPath !== '/functions/foo/events/2') return false;
if (error.keyword !== 'additionalProperties') return false;
error.isExpected = true;
return true;
Expand All @@ -244,7 +244,7 @@ describe('#normalizeAjvErrors', () => {
() =>
expect(
errors.some((error) => {
if (error.dataPath !== ".functions['foo'].events[3].http") return false;
if (error.dataPath !== '/functions/foo/events/3/http') return false;
if (error.keyword !== 'additionalProperties') return false;
error.isExpected = true;
return true;
Expand All @@ -257,7 +257,7 @@ describe('#normalizeAjvErrors', () => {
() =>
expect(
errors.some((error) => {
if (error.dataPath !== ".functions['foo'].events[4].http") return false;
if (error.dataPath !== '/functions/foo/events/4/http') return false;
if (error.keyword !== 'pattern') return false;
error.isExpected = true;
return true;
Expand All @@ -270,7 +270,7 @@ describe('#normalizeAjvErrors', () => {
() =>
expect(
errors.some((error) => {
if (error.dataPath !== ".functions['foo'].events[5].http") return false;
if (error.dataPath !== '/functions/foo/events/5/http') return false;
if (error.keyword !== 'required') return false;
error.isExpected = true;
return true;
Expand All @@ -283,7 +283,7 @@ describe('#normalizeAjvErrors', () => {
() =>
expect(
errors.some((error) => {
if (error.dataPath !== '.provider.deploymentBucket.maxPreviousDeploymentArtifacts') {
if (error.dataPath !== '/provider/deploymentBucket/maxPreviousDeploymentArtifacts') {
return false;
}
if (error.keyword !== 'type') return false;
Expand All @@ -298,7 +298,7 @@ describe('#normalizeAjvErrors', () => {
() =>
expect(
errors.some((error) => {
if (error.dataPath !== '.custom.someCustom') {
if (error.dataPath !== '/custom/someCustom') {
return false;
}
if (error.keyword !== 'anyOf') return false;
Expand All @@ -315,23 +315,23 @@ describe('#normalizeAjvErrors', () => {
it('should report "additionalProperties" error with meaningful message', () =>
expect(
errors.find((error) => {
if (error.dataPath !== '.package') return false;
if (error.dataPath !== '/package') return false;
if (error.keyword !== 'additionalProperties') return false;
return true;
}).message
).to.include('unrecognized property '));
it('should report invalid function name error with meaningful message', () =>
expect(
errors.find((error) => {
if (error.dataPath !== '.functions') return false;
if (error.dataPath !== '/functions') return false;
if (error.keyword !== 'additionalProperties') return false;
return true;
}).message
).to.include('must be alphanumeric'));
it('should report unrecognized event error with a meaningful message', () =>
expect(
errors.find((error) => {
if (error.dataPath !== ".functions['foo'].events[0]") return false;
if (error.dataPath !== '/functions/foo/events/0') return false;
if (error.keyword !== 'anyOf') return false;
return true;
}).message
Expand Down

This file was deleted.

0 comments on commit 1af73ba

Please sign in to comment.