From 7409a5a00a34d1e3acd896b080026c0204760a2b Mon Sep 17 00:00:00 2001 From: Seyi Adebajo Date: Mon, 18 Jun 2018 13:35:39 -0700 Subject: [PATCH] adds more error propagation to schema validation --- .../datasets/compliance/metadata-schema.ts | 38 +++++++++++++++---- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/wherehows-web/app/utils/datasets/compliance/metadata-schema.ts b/wherehows-web/app/utils/datasets/compliance/metadata-schema.ts index f39d44667d..13e6cc9b3f 100644 --- a/wherehows-web/app/utils/datasets/compliance/metadata-schema.ts +++ b/wherehows-web/app/utils/datasets/compliance/metadata-schema.ts @@ -99,7 +99,16 @@ const complianceMetadataTaxonomy: Array = [ * @param {string | Array} expectedType the pattern string to match against * @returns {boolean} */ -const valueEquiv = (value: any, expectedType: string | Array): boolean => expectedType.includes(typeOf(value)); +const isValueEquiv = (value: any, expectedType: string | Array): true => { + const valueType = typeOf(value); + const isValueOfExpectedType = expectedType.includes(valueType); + + if (!isValueOfExpectedType) { + throw new Error(`Expected ${value} to be of type(s) ${expectedType}, got ${valueType}`); + } + + return isValueOfExpectedType; +}; /** * Extracts the type key and the pattern string from the string mapping into a tuple pair @@ -119,27 +128,35 @@ const typePatternMap = (metadataType: IMetadataType): [string, string | Array) => (metadataType: IMetadataType): boolean => { const [name, type] = typePatternMap(metadataType); const value = object[name]; - const rootValueEquiv = object.hasOwnProperty(name) && valueEquiv(value, type); + const isRootValueEquiv = object.hasOwnProperty(name) && isValueEquiv(value, type); const innerType = metadataType['@props']; + if (!isRootValueEquiv) { + throw new Error(`Expected "${name}" to be a key on object`); + } + if (type.includes('object') && isObject(value)) { // recurse on object properties - return rootValueEquiv && keysEquiv(value, innerType!); + return isRootValueEquiv && keysEquiv(value, innerType!); } if (type.includes('array') && Array.isArray(value)) { const { length } = value; + if (!length) { + throw new Error(`Expected array for ${name} to not be empty`); + } + return ( // recursively reduce on array elements // ensure the array contains at least on element - rootValueEquiv && + isRootValueEquiv && length > 0 && - arrayReduce((isEquiv: boolean, value: any) => isEquiv && keysEquiv(value, innerType!), rootValueEquiv)(value) + arrayReduce((isEquiv: boolean, value: any) => isEquiv && keysEquiv(value, innerType!), isRootValueEquiv)(value) ); } - return rootValueEquiv; + return isRootValueEquiv; }; /** @@ -149,7 +166,7 @@ const keyValueHasMatch = (object: IObject) => (metadataType: IMetadataType) * @return {boolean} * @throws {Error} if object keys do not match type @names */ -const keysMatchNames = (object: IObject, typeMaps: Array): boolean => { +const keysMatchNames = (object: IObject, typeMaps: Array): true => { const objectKeys = Object.keys(object).sort(); const typeKeys = arrayMap((typeMap: IMetadataType) => typeMap['@name'])(typeMaps).sort(); const objectKeysSerialized = objectKeys.toString(); @@ -157,7 +174,12 @@ const keysMatchNames = (object: IObject, typeMaps: Array): b const match = objectKeysSerialized === typeKeysSerialized; if (!match) { - throw new Error(`Extra attributes found: ${difference(objectKeys, typeKeys).join(', ')}`); + throw new Error( + `Expected attributes ${typeKeys.join(', ')} on object. Found additional attributes ${difference( + objectKeys, + typeKeys + ).join(', ')}` + ); } return match;