lib/swaggerValidator/ajvDiscriminatorMap.ts (99 lines of code) (raw):
import { Ajv, ErrorObject, ValidateFunction } from "ajv";
import { JsonLoader } from "../swagger/jsonLoader";
import { Schema } from "../swagger/swaggerTypes";
export const ajvEnableDiscriminatorMap = (ajv: Ajv, loader: JsonLoader) => {
ajv.addKeyword("discriminatorMap", {
errors: "full",
metaSchema: { type: "object", additionalProperty: { type: "object,null" } },
compile(schemas: { [key: string]: Schema }, parentSchema: Schema) {
const compiled: { [key: string]: ValidateFunction | null } = {};
const schemaMap: { [key: string]: Schema } = {};
for (const value of Object.keys(schemas)) {
compiled[value] = schemas[value] === null ? null : ajv.compile(schemas[value]);
schemaMap[value] =
schemas[value] === null ? parentSchema : loader.resolveRefObj(schemas[value]);
}
const discriminator = parentSchema.discriminator;
if (discriminator === undefined) {
throw new Error("Discriminator is absent");
}
const validated = new WeakSet<Schema>();
const allowedValues = Object.keys(schemas);
return function v(this: any, data: any, dataPath?: string) {
if (data === null || data === undefined || typeof data !== "object") {
// Should be validated by other schema property.
return true;
}
dataPath = dataPath ?? "";
const discriminatorValue = data[discriminator];
const validate =
discriminatorValue !== undefined && discriminatorValue !== null
? compiled[discriminatorValue]
: undefined;
if (validated.has(data)) {
return true;
}
validated.add(data);
const errors: ErrorObject[] = [];
const sch = schemaMap[discriminatorValue] ?? parentSchema;
if (validate === undefined || validate === null) {
(v as any).errors = [
{
keyword: "discriminatorMap",
data,
dataPath,
params: { allowedValues, discriminatorValue },
schemaPath: "/discriminator",
schema: schemas,
parentSchema,
_realSchema: schemas,
_realData: data,
} as ErrorObject,
];
return false;
}
if (validate !== undefined && validate !== null) {
const valid = validate.call(this, data);
if (!valid && validate.errors) {
for (const err of validate.errors) {
err.dataPath = dataPath + err.dataPath;
if ((err as any)._realSchema === undefined) {
(err as any)._realSchema = err.schema;
(err as any)._realData = err.data;
}
}
errors.push(...validate.errors);
}
}
if (
sch.properties !== undefined &&
sch.additionalProperties === undefined &&
parentSchema.properties !== undefined &&
parentSchema.additionalProperties === undefined
) {
// We validate { additionalProperties: false } manually if it's not set
for (const key of Object.keys(data)) {
if (!(key in sch.properties)) {
errors.push({
keyword: "additionalProperties",
data,
dataPath,
params: { additionalProperty: key },
schemaPath: "/additionalProperties",
schema: false,
parentSchema,
_realSchema: false,
_realData: data,
} as ErrorObject);
}
}
}
if (errors.length > 0) {
(v as any).errors = errors;
return false;
} else {
(v as any).errors = null;
return true;
}
};
},
});
};