UNPKG

@squiz/json-schema-library

Version:

Customizable and hackable json-validator and json-schema utilities for traversal, data generation and validation

143 lines (121 loc) 4.19 kB
import { errorOrPromise } from "./utils/filter"; import flattenArray from "./utils/flattenArray"; import getTypeOf from "./getTypeOf"; import settings from "./config/settings"; import { JSONSchema, JSONPointer, JSONError, isJSONError } from "./types"; import { Draft as Core } from "./draft"; const { DECLARATOR_ONEOF } = settings; /** * Returns a ranking for the data and given schema * * @param core * @param - json schema type: object * @param data * @param [pointer] * @return ranking value (higher is better) */ function fuzzyObjectValue( core: Core, one: JSONSchema, data: { [p: string]: any }, pointer?: JSONPointer ) { if (data == null || one.properties == null) { return -1; } let value = 0; const keys = Object.keys(one.properties); for (let i = 0; i < keys.length; i += 1) { const key = keys[i]; if (data[key] != null && core.isValid(data[key], one.properties[key], pointer)) { value += 1; } } return value; } /** * Selects and returns a oneOf schema for the given data * * @param core * @param data * @param [schema] - current json schema containing property oneOf * @param [pointer] - json pointer to data * @return oneOf schema or an error */ export default function resolveOneOf( core: Core, data: any, schema: JSONSchema = core.rootSchema, pointer: JSONPointer = "#" ): JSONSchema | JSONError { // !keyword: oneOfProperty // an additional <DECLARATOR_ONEOF> (default `oneOfProperty`) on the schema will exactly determine the // oneOf value (if set in data) // @fixme // abort if no data is given an DECLARATOR_ONEOF is set (used by getChildSchemaSelection) // this case (data != null) should not be necessary if (data != null && schema[DECLARATOR_ONEOF]) { const errors = []; const oneOfProperty = schema[DECLARATOR_ONEOF]; const oneOfValue = data[schema[DECLARATOR_ONEOF]]; if (oneOfValue === undefined) { return core.errors.missingOneOfPropertyError({ property: oneOfProperty, pointer }); } for (let i = 0; i < schema.oneOf.length; i += 1) { const one = core.resolveRef(schema.oneOf[i]); const oneOfPropertySchema = core.step(oneOfProperty, one, data, pointer); if (isJSONError(oneOfPropertySchema)) { return oneOfPropertySchema; } let result = flattenArray(core.validate(oneOfValue, oneOfPropertySchema, pointer)); result = result.filter(errorOrPromise); if (result.length > 0) { errors.push(...result); } else { return one; // return resolved schema } } return core.errors.oneOfPropertyError({ property: oneOfProperty, value: oneOfValue, pointer, errors }); } // keyword: oneOf const matches = []; for (let i = 0; i < schema.oneOf.length; i += 1) { const one = core.resolveRef(schema.oneOf[i]); if (core.isValid(data, one, pointer)) { matches.push(one); } } if (matches.length === 1) { return matches[0]; } // fuzzy match oneOf if (getTypeOf(data) === "object") { let schemaOfItem; let fuzzyGreatest = 0; for (let i = 0; i < schema.oneOf.length; i += 1) { const one = core.resolveRef(schema.oneOf[i]); const fuzzyValue = fuzzyObjectValue(core, one, data); if (fuzzyGreatest < fuzzyValue) { fuzzyGreatest = fuzzyValue; schemaOfItem = schema.oneOf[i]; } } if (schemaOfItem === undefined) { return core.errors.oneOfError({ value: JSON.stringify(data), pointer, oneOf: schema.oneOf }); } return schemaOfItem; } if (matches.length > 1) { return core.errors.multipleOneOfError({ matches, data, pointer }); } return core.errors.oneOfError({ value: JSON.stringify(data), pointer, oneOf: schema.oneOf }); }