@redocly/ajv
Version:
Another JSON Schema Validator
139 lines • 6.49 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const codegen_1 = require("../../compile/codegen");
const types_1 = require("../discriminator/types");
const compile_1 = require("../../compile");
const ref_error_1 = require("../../compile/ref_error");
const util_1 = require("../../compile/util");
const error = {
message: ({ params: { discrError, tagName } }) => discrError === types_1.DiscrError.Tag
? `tag "${tagName}" must be string`
: `value of tag "${tagName}" must be in oneOf or anyOf`,
params: ({ params: { discrError, tag, tagName } }) => (0, codegen_1._) `{error: ${discrError}, tag: ${tagName}, tagValue: ${tag}}`,
};
function getDiscriminatorPropertyFromAllOf(sch, tagName) {
var _a;
if (!sch.allOf || !Array.isArray(sch.allOf)) {
return undefined;
}
for (const subschema of sch.allOf) {
if ((_a = subschema === null || subschema === void 0 ? void 0 : subschema.properties) === null || _a === void 0 ? void 0 : _a[tagName]) {
return subschema.properties[tagName];
}
}
return undefined;
}
const def = {
keyword: "discriminator",
type: "object",
schemaType: "object",
error,
code(cxt) {
const { gen, data, schema, parentSchema, it } = cxt;
const keyword = parentSchema.oneOf ? "oneOf" : parentSchema.anyOf ? "anyOf" : undefined;
if (!it.opts.discriminator) {
throw new Error("discriminator: requires discriminator option");
}
const tagName = schema.propertyName;
if (typeof tagName != "string")
throw new Error("discriminator: requires propertyName");
if (!keyword)
throw new Error("discriminator: requires oneOf or anyOf composite keyword");
const parentSchemaVariants = parentSchema[keyword];
const valid = gen.let("valid", false);
const tag = gen.const("tag", (0, codegen_1._) `${data}${(0, codegen_1.getProperty)(tagName)}`);
gen.if((0, codegen_1._) `typeof ${tag} == "string"`, () => validateMapping(), () => cxt.error(false, { discrError: types_1.DiscrError.Tag, tag, tagName }));
cxt.ok(valid);
function validateMapping() {
const mapping = getMapping();
gen.if(false);
for (const tagValue in mapping) {
gen.elseIf((0, codegen_1._) `${tag} === ${tagValue}`);
gen.assign(valid, applyTagSchema(mapping[tagValue]));
}
gen.else();
cxt.error(false, { discrError: types_1.DiscrError.Mapping, tag, tagName });
gen.endIf();
}
function applyTagSchema(schemaProp) {
const _valid = gen.name("valid");
const schCxt = cxt.subschema({ keyword, schemaProp }, _valid);
cxt.mergeEvaluated(schCxt, codegen_1.Name);
return _valid;
}
function getMapping() {
var _a;
const discriminatorMapping = {};
const topRequired = hasRequired(parentSchema);
let tagRequired = true;
for (let i = 0; i < parentSchemaVariants.length; i++) {
let sch = parentSchemaVariants[i];
const schRef = sch === null || sch === void 0 ? void 0 : sch.$ref;
if (schRef && schema.mapping) {
const { mapping } = schema;
const matchedKeys = Object.keys(mapping).filter((key) => mapping[key] === sch.$ref);
if (matchedKeys.length) {
for (const key of matchedKeys) {
addMapping(key, i);
}
continue;
}
}
if (schRef && !(0, util_1.schemaHasRulesButRef)(sch, it.self.RULES)) {
sch = compile_1.resolveRef.call(it.self, it.schemaEnv.root, it.baseId, schRef);
if (sch instanceof compile_1.SchemaEnv)
sch = sch.schema;
if (sch === undefined)
throw new ref_error_1.default(it.opts.uriResolver, it.baseId, schRef);
}
let propSch = (_a = sch === null || sch === void 0 ? void 0 : sch.properties) === null || _a === void 0 ? void 0 : _a[tagName];
if (!propSch && (sch === null || sch === void 0 ? void 0 : sch.allOf)) {
propSch = getDiscriminatorPropertyFromAllOf(sch, tagName);
}
if (typeof propSch != "object") {
throw new Error(`discriminator: ${keyword} subschemas (or referenced schemas) must have "properties/${tagName}" or match mapping`);
}
tagRequired = tagRequired && (topRequired || hasRequired(sch));
addMappings(propSch, i);
}
if (!tagRequired)
throw new Error(`discriminator: "${tagName}" must be required`);
return discriminatorMapping;
function hasRequired(sch) {
if (Array.isArray(sch.required) && sch.required.includes(tagName)) {
return true;
}
if (sch.allOf && Array.isArray(sch.allOf)) {
for (const subschema of sch.allOf) {
const subSch = subschema;
if (Array.isArray(subSch.required) && subSch.required.includes(tagName)) {
return true;
}
}
}
return false;
}
function addMappings(sch, i) {
if (sch.const) {
addMapping(sch.const, i);
}
else if (sch.enum) {
for (const tagValue of sch.enum) {
addMapping(tagValue, i);
}
}
else {
throw new Error(`discriminator: "properties/${tagName}" must have "const" or "enum"`);
}
}
function addMapping(tagValue, i) {
if (typeof tagValue != "string" || tagValue in discriminatorMapping) {
throw new Error(`discriminator: "${tagName}" values must be unique strings`);
}
discriminatorMapping[tagValue] = i;
}
}
},
};
exports.default = def;
//# sourceMappingURL=index.js.map