Skip to content

Commit

Permalink
fix(findOctetSequencePaths): Show path to erring element (#190)
Browse files Browse the repository at this point in the history
* fix(src/plugins/utils/findOctetSequencePaths.js): Show path to erring element on exception

Recently, I ran across an issue where [if the validator throws an exception in a specific part of
the code, the path to the exception-causing code is
swallowed](#180). After a moderate amount of digging,
I decided that an option for providing more information about this code would be to put the path to
the problematic element or elements into the exception message. This exception message is displayed
whenever the code crashes out. So, for example, the previous error message output by lint-openapi
would say "Cannot read property 'type' of null", whereas the new error would list the full path to
the type that was causing the exception.

re #180
  • Loading branch information
distortedsignal committed Oct 23, 2020
1 parent 0c21837 commit 0247888
Show file tree
Hide file tree
Showing 2 changed files with 153 additions and 7 deletions.
42 changes: 35 additions & 7 deletions src/plugins/utils/findOctetSequencePaths.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand Down
118 changes: 118 additions & 0 deletions test/plugins/utils/find-octet-sequence-paths.test.js
Original file line number Diff line number Diff line change
@@ -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"'
);
});
});

0 comments on commit 0247888

Please sign in to comment.