decorated-ajv
Version:
AJV decorated with error handling and formats
259 lines (258 loc) • 10.2 kB
JavaScript
import { __assign, __awaiter, __generator, __rest } from "tslib";
import Ajv from "ajv";
import addErrors from "ajv-errors";
import addFormats from "ajv-formats";
import standaloneCode from "ajv/dist/standalone";
import { uniq } from "lodash";
import fetch from "cross-fetch";
var downloadSchemaFromWeb = function (url) { return __awaiter(void 0, void 0, void 0, function () {
var response;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4 /*yield*/, fetch(url)];
case 1:
response = _a.sent();
return [4 /*yield*/, response.json()];
case 2: return [2 /*return*/, _a.sent()];
}
});
}); };
var loadedSchemas = {};
var loadSchema = function (id) { return __awaiter(void 0, void 0, void 0, function () {
var _a, _b;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
if (!!loadedSchemas[id]) return [3 /*break*/, 2];
_a = loadedSchemas;
_b = id;
return [4 /*yield*/, downloadSchemaFromWeb(id)];
case 1:
_a[_b] = _c.sent();
_c.label = 2;
case 2: return [2 /*return*/, loadedSchemas[id]];
}
});
}); };
export var getAjv = function (options) {
var ajv = new Ajv(__assign(__assign({ loadSchema: loadSchema }, options), { allErrors: true }));
addFormats(ajv);
addErrors(ajv);
return ajv;
};
export var getValidator = function (schema, ajv) { return __awaiter(void 0, void 0, void 0, function () {
var _ajv, validator;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_ajv = ajv || getAjv();
return [4 /*yield*/, _ajv.compileAsync(schema)];
case 1:
validator = _a.sent();
return [2 /*return*/, validator];
}
});
}); };
export var getCompiledValidator = function (schema, options) {
if (options === void 0) { options = {}; }
return __awaiter(void 0, void 0, void 0, function () {
var _options, ajv, validate, moduleCode;
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
_options = __assign({}, options);
if (_options.code === undefined) {
_options.code = {};
}
_options.code.source = true;
ajv = getAjv(_options);
return [4 /*yield*/, getValidator(schema, ajv)];
case 1:
validate = _a.sent();
moduleCode = standaloneCode(ajv, validate);
return [2 /*return*/, moduleCode];
}
});
});
};
/**
* @param schema a valid JSON Schema. is ignored if validate function is passed
* @param data
* @param validate
*/
export var validate = function (schema, data, validate) { return __awaiter(void 0, void 0, void 0, function () {
var _validate, _a, _b, violations;
return __generator(this, function (_c) {
switch (_c.label) {
case 0:
_a = validate;
if (_a) return [3 /*break*/, 4];
if (!(typeof schema == "function")) return [3 /*break*/, 1];
_b = schema;
return [3 /*break*/, 3];
case 1: return [4 /*yield*/, getValidator(schema)];
case 2:
_b = _c.sent();
_c.label = 3;
case 3:
_a = (_b);
_c.label = 4;
case 4:
_validate = _a;
if (!_validate(data)) {
violations = generateViolations(_validate.errors);
return [2 /*return*/, violations];
}
return [2 /*return*/, []];
}
});
}); };
var generateViolations = function (errors) {
var filteredErrors = errors;
try {
filteredErrors = filterErrors(errors);
}
catch (e) {
// don't do anything
}
return filteredErrors.map(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
function (_a) {
var instancePath = _a.instancePath, message = _a.message, schemaPath = _a.schemaPath, keyword = _a.keyword, context = __rest(_a, ["instancePath", "message", "schemaPath", "keyword"]);
return ({
path: (instancePath.match(/([^\\/])+/g) || []).join("."),
message: message,
context: context
});
});
};
var filterErrors = function (errors) {
var filteredErrors = errors.map(function (e) {
e.params.__filter = {};
return e;
});
filteredErrors = extractErrorMessageKeyword(filteredErrors);
var siblings = getSiblings(filteredErrors);
applyOneOfOrAnyOf(filteredErrors, "oneOf", siblings);
applyOneOfOrAnyOf(filteredErrors, "anyOf", siblings);
deleteErrorsForKey(filteredErrors, "allOf");
deleteErrorsForKey(filteredErrors, "if");
deleteErrorsForKey(filteredErrors, "then");
deleteErrorsForKey(filteredErrors, "else");
var remainingErrors = filteredErrors.filter(function (e) { var _a, _b; return !((_b = (_a = e.params) === null || _a === void 0 ? void 0 : _a.__filter) === null || _b === void 0 ? void 0 : _b.delete); });
if (remainingErrors.length == 0) {
remainingErrors = [filteredErrors[0]];
}
return remainingErrors.map(function (e) {
delete e.params.__filter;
return e;
});
};
var extractErrorMessageKeyword = function (errors) {
var allErrors = [];
errors.forEach(function (error) {
if (error.keyword == "errorMessage") {
if (error.params.errors) {
var originalErrors = error.params.errors;
delete error.params.errors;
var originalErrorsWithFilter = originalErrors.map(function (e) {
e.params.__filter = {
delete: true,
errorMessage: error.schemaPath
};
return e;
});
allErrors.push.apply(allErrors, originalErrorsWithFilter);
}
}
allErrors.push(error);
});
return allErrors;
};
var getSiblings = function (errors) {
var siblings = {};
errors.forEach(function (error) {
if (error.keyword != "errorMessage") {
var schemaPath_1 = error.schemaPath;
var parentSchemaPath_1 = schemaPath_1.substring(0, schemaPath_1.lastIndexOf("/"));
errors.forEach(function (_error) {
if (_error.keyword != "errorMessage") {
if (!_error.schemaPath.startsWith(schemaPath_1) &&
_error.schemaPath.startsWith(parentSchemaPath_1)) {
if (!siblings[schemaPath_1]) {
siblings[schemaPath_1] = [];
}
siblings[schemaPath_1].push(_error.schemaPath);
}
}
});
}
});
return siblings;
};
var applyOneOfOrAnyOf = function (errors, type, siblings) {
deleteErrorsForKey(errors, type);
var typeSchemaPaths = uniq(errors.filter(function (e) { return e.keyword == type; }).map(function (e) { return e.schemaPath; }));
var typeSchemaPathToErrorsMap = {};
errors.forEach(function (error) {
typeSchemaPaths.forEach(function (typeSp) {
if (error.schemaPath != typeSp && error.schemaPath.startsWith(typeSp)) {
if (!error.params.__filter[type]) {
error.params.__filter[type] = [];
}
error.params.__filter[type].push(typeSp);
if (!typeSchemaPathToErrorsMap[typeSp]) {
typeSchemaPathToErrorsMap[typeSp] = [];
}
typeSchemaPathToErrorsMap[typeSp].push(error);
}
});
});
Object.keys(typeSchemaPathToErrorsMap).forEach(function (typeSp) {
var typeErrors = typeSchemaPathToErrorsMap[typeSp];
if (siblings[typeSp] && siblings[typeSp].length > 0) {
typeErrors.forEach(function (e) {
e.params.__filter.delete = true;
});
}
else {
var typeErrorsByIndex_1 = {};
typeErrors.forEach(function (e) {
var index = e.schemaPath.substring(typeSp.length + 1, e.schemaPath.indexOf("/", typeSp.length + 1));
if (!typeErrorsByIndex_1[index]) {
typeErrorsByIndex_1[index] = [];
}
typeErrorsByIndex_1[index].push(e);
});
var typeErrorSchemaPathsByIndex_1 = {};
Object.keys(typeErrorsByIndex_1).forEach(function (index) {
var schemaPathWithIndexLength = "".concat(typeSp, "/").concat(index, "/").length;
typeErrorSchemaPathsByIndex_1[index] = uniq(typeErrorsByIndex_1[index].map(function (e) {
return e.schemaPath.substring(schemaPathWithIndexLength, e.schemaPath.indexOf("/", schemaPathWithIndexLength));
}));
});
var allIndexes = Object.keys(typeErrorSchemaPathsByIndex_1);
var indexWithMinimumErrors_1 = allIndexes[0];
allIndexes.forEach(function (i) {
if (typeErrorSchemaPathsByIndex_1[i].length <
typeErrorSchemaPathsByIndex_1[indexWithMinimumErrors_1].length) {
indexWithMinimumErrors_1 = i;
}
});
allIndexes.forEach(function (i) {
if (i != indexWithMinimumErrors_1) {
typeErrorsByIndex_1[i].forEach(function (e) {
e.params.__filter.delete = true;
});
}
});
}
});
};
var deleteErrorsForKey = function (errors, key) {
errors.forEach(function (e) {
if (e.keyword == key) {
e.params.__filter.delete = true;
}
});
};