mirror of
https://github.com/postmanlabs/openapi-to-postman.git
synced 2022-11-29 22:05:00 +03:00
Correct JSONPaths for schema/txn objects
This commit is contained in:
@@ -1687,7 +1687,7 @@ module.exports = {
|
||||
// look for specific method
|
||||
if (pathItemObject[method.toLowerCase()]) {
|
||||
matchedPath = pathItemObject[method.toLowerCase()];
|
||||
matchedPathJsonPath = `$.paths[${path}].${method}.`.replace(/\.\./g, '.');
|
||||
matchedPathJsonPath = `$.paths[${path}]`;
|
||||
|
||||
if (!matchedPath.parameters) {
|
||||
matchedPath.parameters = [];
|
||||
@@ -1697,14 +1697,15 @@ module.exports = {
|
||||
matchedPath.parameters = _.map(matchedPath.parameters, (commonParam) => {
|
||||
// for path-specifix params that are added to the path, have a way to identify them
|
||||
// when the schemaPath is required
|
||||
commonParam.pathPrefix = `${matchedPathJsonPath}.parameters.`.replace(/\.\./g, '.');
|
||||
// method is lowercased because OAS methods are always lowercase
|
||||
commonParam.pathPrefix = `${matchedPathJsonPath}.${method.toLowerCase()}.parameters`;
|
||||
|
||||
return commonParam;
|
||||
}).concat(
|
||||
_.map(pathItemObject.parameters || [], (commonParam) => {
|
||||
// for common params that are added to the path, have a way to identify them
|
||||
// when the schemaPath is required
|
||||
commonParam.pathPrefix = '$.parameters.';
|
||||
commonParam.pathPrefix = matchedPathJsonPath + '.parameters';
|
||||
return commonParam;
|
||||
})
|
||||
);
|
||||
@@ -1712,7 +1713,7 @@ module.exports = {
|
||||
retVal.push({
|
||||
name: matchedPath.operationId || matchedPath.summary || (method + ' ' + path),
|
||||
path: matchedPath,
|
||||
jsonPath: matchedPathJsonPath,
|
||||
jsonPath: matchedPathJsonPath + '.' + method.toLowerCase(),
|
||||
pathVariables: pathItemValues
|
||||
});
|
||||
}
|
||||
@@ -1726,23 +1727,59 @@ module.exports = {
|
||||
|
||||
checkValueAgainstSchema: function (property, jsonPathPrefix, value, schemaPathPrefix, schema,
|
||||
components, options, callback) {
|
||||
if (schema.hasOwnProperty('$ref')) {
|
||||
schema = this.getRefObject(schema.$ref, components, options);
|
||||
}
|
||||
|
||||
if (!schemaTypeToJsValidator[schema.type](value)) {
|
||||
callback(null, [{
|
||||
property,
|
||||
transactionJsonPath: jsonPathPrefix,
|
||||
schemaJsonPath: schemaPathPrefix.replace(/\.\./g, '.'),
|
||||
reasonCode: 'INVALID_TYPE',
|
||||
reason: `Value must be a token of type ${schema.type}, found ${value}`
|
||||
}]);
|
||||
let mismatches = [];
|
||||
|
||||
if (schema) {
|
||||
if (!schemaTypeToJsValidator[schema.type](value)) {
|
||||
// if type didn't match, no point checking for AJV
|
||||
return callback(null, [{
|
||||
property,
|
||||
transactionJsonPath: jsonPathPrefix,
|
||||
schemaJsonPath: schemaPathPrefix,
|
||||
reasonCode: 'INVALID_TYPE',
|
||||
reason: `Value must be a token of type ${schema.type}, found ${value}`
|
||||
}]);
|
||||
}
|
||||
|
||||
// only do AJV if type is array or object
|
||||
// simpler cases are handled by a type check
|
||||
if (schema.type === 'array' || schema.type === 'object') {
|
||||
let ajv = new Ajv({ unknownFormats: ['int32', 'int64'], allErrors: true }),
|
||||
validate = ajv.compile(schema),
|
||||
res = validate(value);
|
||||
if (!res) {
|
||||
mismatches.push({
|
||||
property: property,
|
||||
transactionJsonPath: jsonPathPrefix,
|
||||
schemaJsonPath: schemaPathPrefix,
|
||||
reasonCode: 'INVALID_TYPE',
|
||||
reason: 'The property didn\'t match the specified schema'
|
||||
});
|
||||
|
||||
// only return AJV mismatches
|
||||
return callback(null, mismatches);
|
||||
}
|
||||
// result passed. No AJV mismatch
|
||||
}
|
||||
// Schema was not AJV or object
|
||||
}
|
||||
// Schema not defined
|
||||
return callback(null, []);
|
||||
|
||||
// if (!schemaTypeToJsValidator[schema.type](value)) {
|
||||
// callback(null, [{
|
||||
// property,
|
||||
// transactionJsonPath: jsonPathPrefix,
|
||||
// schemaJsonPath: schemaPathPrefix,
|
||||
// reasonCode: 'INVALID_TYPE',
|
||||
// reason: `Value must be a token of type ${schema.type}, found ${value}`
|
||||
// }]);
|
||||
// }
|
||||
// TODO: Further checks for object type
|
||||
else {
|
||||
callback(null, []);
|
||||
}
|
||||
// else {
|
||||
// callback(null, []);
|
||||
// }
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1783,7 +1820,8 @@ module.exports = {
|
||||
// extra pathVar present in given request.
|
||||
mismatches.push({
|
||||
property: mismatchProperty,
|
||||
transactionJsonPath: transactionPathPrefix + '[' + pathVar.key + ']',
|
||||
// not adding the pathVar name to the jsonPath because URL is just a string
|
||||
transactionJsonPath: transactionPathPrefix,
|
||||
schemaJsonPath: null,
|
||||
reasonCode: 'MISSING_IN_SCHEMA',
|
||||
reason: `The path variable ${pathVar.key} was not found in the schema`
|
||||
@@ -1791,9 +1829,12 @@ module.exports = {
|
||||
return cb(null);
|
||||
}
|
||||
|
||||
this.checkValueAgainstSchema(mismatchProperty, transactionPathPrefix + '[' + pathVar.key + ']',
|
||||
this.checkValueAgainstSchema(mismatchProperty,
|
||||
transactionPathPrefix,
|
||||
pathVar.value,
|
||||
schemaPathVar.pathPrefix + schemaPathVar.name, schemaPathVar.schema, components, options, cb);
|
||||
schemaPathVar.pathPrefix + '[?(@.name==\'' + schemaPathVar.name + '\')]',
|
||||
deref.resolveRefs(schemaPathVar.schema, 'request', components),
|
||||
components, options, cb);
|
||||
|
||||
}, (err, res) => {
|
||||
if (err) {
|
||||
@@ -1825,13 +1866,13 @@ module.exports = {
|
||||
mismatches = [];
|
||||
// 1. for each header, find relevant schemaPath property
|
||||
|
||||
async.map(headers, (pHeader, cb) => {
|
||||
return async.map(headers, (pHeader, cb) => {
|
||||
schemaHeader = _.find(schemaHeaders, (header) => { return header.name === pHeader.key; });
|
||||
if (!schemaHeader) {
|
||||
// no schema header found
|
||||
mismatches.push({
|
||||
property: mismatchProperty,
|
||||
transactionJsonPath: transactionPathPrefix + '[' + pHeader.key + ']',
|
||||
transactionJsonPath: transactionPathPrefix + '[?(@.key==\'' + pHeader.key + '\')]',
|
||||
schemaJsonPath: null,
|
||||
reasonCode: 'MISSING_IN_SCHEMA',
|
||||
reason: `The header ${pHeader.key} was not found in the schema`
|
||||
@@ -1841,10 +1882,10 @@ module.exports = {
|
||||
|
||||
// header found in spec. check header's schema
|
||||
this.checkValueAgainstSchema(mismatchProperty,
|
||||
transactionPathPrefix + '[' + pHeader.key + ']',
|
||||
transactionPathPrefix + '[?(@.key==\'' + pHeader.key + '\')]',
|
||||
pHeader.value,
|
||||
schemaHeader.pathPrefix + '[' + schemaHeader.name + ']',
|
||||
schemaHeader.schema,
|
||||
schemaHeader.pathPrefix + '[?(@.name==\'' + schemaHeader.name + '\')]',
|
||||
deref.resolveRefs(schemaHeader.schema, 'request', components),
|
||||
components, options,
|
||||
cb
|
||||
);
|
||||
@@ -1854,13 +1895,13 @@ module.exports = {
|
||||
mismatches.push({
|
||||
property: mismatchProperty,
|
||||
transactionJsonPath: null,
|
||||
schemaJsonPath: header.pathPrefix + header.name,
|
||||
schemaJsonPath: header.pathPrefix + '[?(@.name==\'' + header.name + '\')]',
|
||||
reasonCode: 'MISSING_IN_REQUEST',
|
||||
reason: `The requried header ${header.name} was not found in the transaction`
|
||||
});
|
||||
}
|
||||
});
|
||||
callback(null, _.concat(_.flatten(res), mismatches));
|
||||
return callback(null, _.concat(_.flatten(res), mismatches));
|
||||
});
|
||||
},
|
||||
|
||||
@@ -1906,18 +1947,21 @@ module.exports = {
|
||||
transactionPathPrefix + '/' + pHeader.key,
|
||||
pHeader.value,
|
||||
schemaPathPrefix + responsePathPrefix + '.headers[' + pHeader.key + ']',
|
||||
schemaHeader.schema,
|
||||
deref.resolveRefs(schemaHeader.schema, 'response', components),
|
||||
components,
|
||||
options,
|
||||
cb
|
||||
);
|
||||
}, (err, res) => {
|
||||
_.each(_.filter(schemaHeaders, (h) => { return h.required; }), (header) => {
|
||||
_.each(_.filter(schemaHeaders, (h, hName) => {
|
||||
h.name = hName;
|
||||
return h.required;
|
||||
}), (header) => {
|
||||
if (!_.find(headers, (param) => { return param.key === header.name; })) {
|
||||
mismatches.push({
|
||||
property: mismatchProperty,
|
||||
transactionJsonPath: null,
|
||||
schemaJsonPath: header.pathPrefix + header.name,
|
||||
schemaJsonPath: schemaPathPrefix + '.headers[\'' + header.name + '\']',
|
||||
reasonCode: 'MISSING_IN_REQUEST',
|
||||
reason: `The requried header ${header.name} was not found in the transaction`
|
||||
});
|
||||
@@ -1942,16 +1986,25 @@ module.exports = {
|
||||
validate = ajv.compile(jsonSchemaBody),
|
||||
res = validate(JSON.parse(requestBody.raw));
|
||||
if (!res) {
|
||||
_.each(validate.errors, (error) => {
|
||||
// error.keyword can be https://ajv.js.org/keywords.html
|
||||
mismatches.push({
|
||||
property: 'REQUEST_BODY',
|
||||
transactionJsonPath: transactionPathPrefix + error.dataPath,
|
||||
schemaJsonPath: schemaPathPrefix + 'requestBody.content.application.json.schema.' + error.schemaPath,
|
||||
reasonCode: error.keyword.toUpperCase(),
|
||||
reason: error.message
|
||||
});
|
||||
mismatches.push({
|
||||
property: 'REQUEST_BODY',
|
||||
transactionJsonPath: transactionPathPrefix,
|
||||
schemaJsonPath: schemaPathPrefix + 'requestBody.content.application.json.schema',
|
||||
reasonCode: 'INVALID_TYPE',
|
||||
reason: 'The request body didn\'t match the specified schema'
|
||||
});
|
||||
|
||||
// Not validating parts of the body for now
|
||||
// _.each(validate.errors, (error) => {
|
||||
// // error.keyword can be https://ajv.js.org/keywords.html
|
||||
// mismatches.push({
|
||||
// property: 'REQUEST_BODY',
|
||||
// transactionJsonPath: transactionPathPrefix + error.dataPath,
|
||||
// schemaJsonPath: schemaPathPrefix + 'requestBody.content.application.json.schema.' + error.schemaPath,
|
||||
// reasonCode: error.keyword.toUpperCase(),
|
||||
// reason: error.message
|
||||
// });
|
||||
// });
|
||||
return callback(null, mismatches);
|
||||
}
|
||||
}
|
||||
@@ -1980,10 +2033,10 @@ module.exports = {
|
||||
|
||||
|
||||
return this.checkValueAgainstSchema(mismatchProperty,
|
||||
transactionPathPrefix + '/body',
|
||||
transactionPathPrefix + '.body',
|
||||
body,
|
||||
schemaPathPrefix,
|
||||
schemaContent,
|
||||
schemaPathPrefix + '.content[application/json].schema',
|
||||
deref.resolveRefs(schemaContent, 'response', components),
|
||||
components,
|
||||
options,
|
||||
callback
|
||||
@@ -2053,15 +2106,14 @@ module.exports = {
|
||||
async.parallel({
|
||||
headers: (cb) => {
|
||||
this.checkResponseHeaders(thisSchemaResponse, response.header,
|
||||
transactionPathPrefix + '.' + response.id + '.headers.',
|
||||
schemaPathPrefix + '.' + responsePathPrefix, components, options, cb);
|
||||
transactionPathPrefix + '.' + response.id + '.header',
|
||||
schemaPathPrefix + '.responses.' + responsePathPrefix, components, options, cb);
|
||||
},
|
||||
body: (cb) => {
|
||||
// assume it's JSON at this point
|
||||
|
||||
this.checkResponseBody(thisSchemaResponse, response.header,
|
||||
transactionPathPrefix + '.' + response.id + '.body.',
|
||||
schemaPathPrefix + '.' + responsePathPrefix, components, options, cb);
|
||||
this.checkResponseBody(thisSchemaResponse, response.body,
|
||||
transactionPathPrefix + '.' + response.id + '.body',
|
||||
schemaPathPrefix + '.responses.' + responsePathPrefix, components, options, cb);
|
||||
}
|
||||
}, (err, result) => {
|
||||
return responseCallback(null, {
|
||||
|
||||
@@ -262,7 +262,7 @@ class SchemaPack {
|
||||
componentsAndPaths, options, cb);
|
||||
},
|
||||
headers: function(cb) {
|
||||
schemaUtils.checkRequestHeaders(transaction.request.header, '$.request.headers', matchedPath.path,
|
||||
schemaUtils.checkRequestHeaders(transaction.request.header, '$.request.header', matchedPath.path,
|
||||
componentsAndPaths, options, cb);
|
||||
},
|
||||
requestBody: function(cb) {
|
||||
@@ -273,17 +273,9 @@ class SchemaPack {
|
||||
schemaUtils.checkResponses(transaction.response, '$.responses', matchedPath.jsonPath,
|
||||
matchedPath.path, componentsAndPaths, options, cb);
|
||||
}
|
||||
// responseHeaders: function(cb) {
|
||||
// schemaUtils.checkResponseHeaders(transaction.response.code, transaction.response.headers,
|
||||
// '$.response.headers', matchedPath.jsonPath, matchedPath.path, cb);
|
||||
// },
|
||||
// responseBody: function(cb) {
|
||||
// schemaUtils.checkResponseBody(transaction.response.code, transaction.response.headers,
|
||||
// '$.response.body', matchedPath.jsonPath, matchedPath.path, cb);
|
||||
// }
|
||||
}, (err, result) => {
|
||||
console.log('Checked all properties for txn ' + transaction.id + ' for path ' + matchedPath.name);
|
||||
let allMismatches = _.concat(result.headers, result.path, result.requestBody, result.responseHeaders),
|
||||
let allMismatches = _.concat(result.headers, result.path, result.requestBody),
|
||||
retVal = { matched: true }; // no mismatch
|
||||
|
||||
if (allMismatches.length > 0) {
|
||||
|
||||
@@ -17,7 +17,7 @@ describe('The converter must validate a history request against the schema', fun
|
||||
it('correctly', function(done) {
|
||||
let schemaPack = new Converter.SchemaPack({ type: 'json', data: openapi }, {});
|
||||
schemaPack.validateTransaction(historyRequest, (err, result) => {
|
||||
debugger;
|
||||
console.log('Final result: ', JSON.stringify(result, null, 2));
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user