@redocly/openapi-core
Version:
See https://github.com/Redocly/redocly-cli
106 lines • 4.07 kB
JavaScript
import Ajv2020 from '@redocly/ajv/dist/2020.js';
import AjvDraft4 from '@redocly/ajv/dist/draft4.js';
import addFormats from 'ajv-formats';
import { escapePointerFragment } from '../ref-utils.js';
const ajvInstances = {};
export function releaseAjvInstance() {
ajvInstances['2020'] = undefined;
ajvInstances['draft4'] = undefined;
}
function getSchemaIdKey(dialect) {
return dialect === 'draft4' ? 'id' : '$id';
}
function getDialectBySpecVersion(specVersion) {
if (specVersion === 'oas2' || specVersion === 'oas3_0')
return 'draft4';
return '2020';
}
function getAjv(resolve, dialect) {
if (!ajvInstances[dialect]) {
const schemaIdKey = getSchemaIdKey(dialect);
const options = {
schemaId: schemaIdKey,
meta: true,
allErrors: true,
strictSchema: false,
inlineRefs: false,
validateSchema: false,
discriminator: true,
allowUnionTypes: true,
validateFormats: true,
passContext: true,
loadSchemaSync(base, $ref, $id) {
const decodedBase = decodeURI(base.split('#')[0]);
const resolvedRef = resolve({ $ref }, decodedBase);
if (!resolvedRef || !resolvedRef.location)
return false;
return {
[schemaIdKey]: encodeURI(resolvedRef.location.source.absoluteRef) + '#' + $id,
...resolvedRef.node,
};
},
logger: false,
};
ajvInstances[dialect] = dialect === '2020' ? new Ajv2020(options) : new AjvDraft4(options);
addFormats(ajvInstances[dialect]);
}
return ajvInstances[dialect];
}
function getAjvValidator(schema, loc, resolve, allowAdditionalProperties, dialect) {
const ajv = getAjv(resolve, dialect);
const $id = encodeURI(loc.absolutePointer);
const schemaIdKey = getSchemaIdKey(dialect);
if (!ajv.getSchema($id)) {
ajv.setDefaultUnevaluatedProperties(allowAdditionalProperties);
ajv.addSchema({
[schemaIdKey]: $id,
...schema,
}, $id);
}
return ajv.getSchema($id);
}
export function validateJsonSchema(data, schema, options) {
const { schemaLoc, instancePath, resolve, allowAdditionalProperties, ajvContext, specVersion } = options;
const dialect = getDialectBySpecVersion(specVersion);
const validate = getAjvValidator(schema, schemaLoc, resolve, allowAdditionalProperties, dialect);
if (!validate)
return { valid: true, errors: [] }; // unresolved refs are reported
const dataCxt = {
instancePath,
parentData: { fake: {} },
parentDataProperty: 'fake',
rootData: {},
dynamicAnchors: {},
};
const valid = validate.call(ajvContext ?? {}, data, dataCxt);
return {
valid: !!valid,
errors: (validate.errors || []).map(beatifyErrorMessage),
};
function beatifyErrorMessage(error) {
let message = error.message;
const suggest = error.keyword === 'enum' ? error.params.allowedValues : undefined;
if (suggest) {
message += ` ${suggest.map((e) => `"${e}"`).join(', ')}`;
}
if (error.keyword === 'type') {
message = `type ${message}`;
}
const relativePath = error.instancePath.substring(instancePath.length + 1);
const propName = relativePath.substring(relativePath.lastIndexOf('/') + 1);
if (propName) {
message = `\`${propName}\` property ${message}`;
}
if (error.keyword === 'additionalProperties' || error.keyword === 'unevaluatedProperties') {
const property = error.params.additionalProperty || error.params.unevaluatedProperty;
message = `${message} \`${property}\``;
error.instancePath += '/' + escapePointerFragment(property);
}
return {
...error,
message,
suggest,
};
}
}
//# sourceMappingURL=ajv.js.map