diff --git a/src/plugins/utils/findOctetSequencePaths.js b/src/plugins/utils/findOctetSequencePaths.js index caf255c05..bb5608e9d 100644 --- a/src/plugins/utils/findOctetSequencePaths.js +++ b/src/plugins/utils/findOctetSequencePaths.js @@ -6,7 +6,7 @@ // the path both as an array and a string and returns the path in the same // format received: // typeof(path) === 'array' => [[path1, get], [path2, get], ...] -// typeof(path) === 'string' => ['path1.get', path2.get, ...] +// typeof(path) === 'string' => ['path1.get', 'path2.get', ...] const findOctetSequencePaths = (resolvedSchema, path) => { if (!resolvedSchema) { @@ -35,12 +35,40 @@ function arrayOctetSequences(resolvedSchema, path) { const pathToSchema = Array.isArray(path) ? path.concat('items') : `${path}.items`; - if (arrayItems.type === 'string' && arrayItems.format === 'binary') { - arrayPathsToOctetSequence.push(pathToSchema); - } else if (arrayItems.type === 'object' || arrayItems.type === 'array') { - arrayPathsToOctetSequence.push( - ...findOctetSequencePaths(arrayItems, pathToSchema) - ); + try { + if (arrayItems.type === 'string' && arrayItems.format === 'binary') { + arrayPathsToOctetSequence.push(pathToSchema); + } else if (arrayItems.type === 'object' || arrayItems.type === 'array') { + arrayPathsToOctetSequence.push( + ...findOctetSequencePaths(arrayItems, pathToSchema) + ); + } + } catch (err) { + if (err instanceof TypeError) { + const escapedPaths = []; + const strEscaper = function(strToEscape) { + let newStr = ''; + for (let i = 0; i < strToEscape.length; i++) { + if (strToEscape.charAt(i) == '/') { + newStr = newStr + '\\/'; + } else { + newStr = newStr + strToEscape.charAt(i); + } + } + escapedPaths.push(newStr); + }; + path.forEach(strEscaper); + const e = new TypeError( + 'items.type and items.format must resolve for the path "' + + escapedPaths.join('/') + + '"' + ); + e.stack = err.stack; + e.original = err; + throw e; + } else { + throw err; + } } } return arrayPathsToOctetSequence; diff --git a/test/plugins/utils/find-octet-sequence-paths.test.js b/test/plugins/utils/find-octet-sequence-paths.test.js new file mode 100644 index 000000000..c67663436 --- /dev/null +++ b/test/plugins/utils/find-octet-sequence-paths.test.js @@ -0,0 +1,118 @@ +const expect = require('expect'); +const findOctetSequencePaths = require('../../../src/plugins/utils/findOctetSequencePaths') + .findOctetSequencePaths; + +function arrayEquals(a, b) { + return ( + Array.isArray(a) && + Array.isArray(b) && + a.length === b.length && + a.every((val, index) => val === b[index]) + ); +} + +describe('falsy values should return an empty array', function() { + describe('undefined should return an empty array', function() { + it('undefined is falsy', function() { + expect(arrayEquals(findOctetSequencePaths(undefined, []), [])); + }); + }); + + describe('null should return an empty array', function() { + it('null is falsy', function() { + expect(arrayEquals(findOctetSequencePaths(null, []), [])); + }); + }); + + describe('NaN should return an empty array', function() { + it('NaN is falsy', function() { + expect(arrayEquals(findOctetSequencePaths(NaN, []), [])); + }); + }); + + describe('0 should return an empty array', function() { + it('0 is falsy', function() { + expect(arrayEquals(findOctetSequencePaths(0, []), [])); + }); + }); + + describe('The Empty String should return an empty array', function() { + it('The Empty String is falsy', function() { + expect(arrayEquals(findOctetSequencePaths('', []), [])); + }); + }); + + describe('false should return an empty array', function() { + it('false is falsy', function() { + expect(findOctetSequencePaths(false, [])).toEqual([]); + }); + }); +}); + +describe('binary format string schemas should return the passed path', function() { + describe('binary format strings should include the path in string form', function() { + it('should return an array with string elements', function() { + const schemaObj = { type: 'string', format: 'binary' }; + const path = ['path1.get']; + + expect(arrayEquals(findOctetSequencePaths(schemaObj, path), path)); + }); + }); +}); + +describe('object-type schemas must extract values from the resolved schema', function() { + const schemaObj = { type: 'object' }; + const path = ['path1.get']; + it('falsy properties should not append to the path', function() { + schemaObj.properties = false; + + expect(arrayEquals(findOctetSequencePaths(schemaObj, path), path)); + }); + + it('truthy properties should be added to the octet paths', function() { + schemaObj.properties = { one: { type: 'string', format: 'binary' } }; + const expectedOut = ['path1.get', 'path1.get.properties.one']; + + expect(arrayEquals(findOctetSequencePaths(schemaObj, path), expectedOut)); + }); + + it('truthy properties should be recursively added to the octet paths', function() { + schemaObj.properties = { + one: { + type: 'object', + properties: { two: { type: 'string', format: 'binary' } } + } + }; + const expectedOut = [ + 'path1.get', + 'path1.get.properties.one', + 'path1.get.properties.one.properties.two' + ]; + + expect(arrayEquals(findOctetSequencePaths(schemaObj, path), expectedOut)); + }); +}); + +describe('array-type schemas require the proper values in proper fields', function() { + const schemaObj = { type: 'array', items: null }; + + it('should throw with a full path if the proper structure is not provided', function() { + const path = ['path1.get']; + + expect(function() { + findOctetSequencePaths(schemaObj, path); + }).toThrow( + 'items.type and items.format must resolve for the path "path1.get"' + ); + }); + + it('should escape forward-slashes in the path', function() { + const path = ['path1/get']; + + expect(function() { + findOctetSequencePaths(schemaObj, path); + }).toThrow( + 'items.type and items.format must resolve for the path "path1\\/get"' + ); + }); +});