Skip to content

Commit

Permalink
Merge pull request #205 from postmanlabs/release/1.1.13
Browse files Browse the repository at this point in the history
Release/1.1.13
  • Loading branch information
abhijitkane authored Apr 21, 2020
2 parents 0b696be + 55c6586 commit bab369a
Show file tree
Hide file tree
Showing 17 changed files with 1,017 additions and 106 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
# OpenAPI-Postman Changelog
#### v1.1.13 (April 21, 2020)
* Added support for detailed validation body mismatches with option detailedBlobValidation.
* Fix for [#8098](https://github.com/postmanlabs/postman-app-support/issues/8098) - Unable to validate schema with type array.
* Fixed URIError for invalid URI in transaction.
* Fix for [#152](https://github.com/postmanlabs/openapi-to-postman/issues/152) - Path references not resolved due to improver handling of special characters.
* Fix for [#160](https://github.com/postmanlabs/openapi-to-postman/issues/160) - Added handling for variables in local servers not a part of a URL segment. All path servers to be added as collection variables.
* Unresolved refs will not be stored in schemaResolutionCache.

#### v1.1.12 (Mar 26, 2020)
* Fix for https://github.com/postmanlabs/openapi-to-postman/issues/133 and https://github.com/postmanlabs/openapi-to-postman/issues/101
Expand Down
49 changes: 49 additions & 0 deletions lib/ajvValidationError.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
var _ = require('lodash');

/**
* This function generates reason and reasonCodes (used in mismatch objects) using Ajv Validation Error
*
* @param {Object} ajvValidationErrorObj Ajv Validation Error Object (reference: https://ajv.js.org/#validation-errors)
* @param {Object} data data needed for generation of mismatch Object
*
* @returns {Object} mismatchObj with reason and reasonCode properties
*/
function ajvValidationError(ajvValidationErrorObj, data) {
var mismatchPropName = _.get(ajvValidationErrorObj, 'dataPath', '').slice(1),
mismatchObj = {
reason: `The ${data.humanPropName} property "${mismatchPropName}" ` +
`${ajvValidationErrorObj.message}`,
reasonCode: 'INVALID_TYPE'
};

switch (ajvValidationErrorObj.keyword) {

case 'additionalProperties':
mismatchObj.reasonCode = 'MISSING_IN_SCHEMA';
break;

case 'dependencies':
mismatchObj.reason = `The ${data.humanPropName} property "${mismatchPropName}" ` +
`should have property "${_.get(ajvValidationErrorObj, 'params.missingProperty')}" when property ` +
`"${_.get(ajvValidationErrorObj, 'params.property')}" is present`;
break;

case 'required':
mismatchObj.reasonCode = 'MISSING_IN_REQUEST';
mismatchObj.reason = `The ${data.humanPropName} property "${mismatchPropName}" should have required ` +
`property "${_.get(ajvValidationErrorObj, 'params.missingProperty')}"`;
break;

case 'propertyNames':
mismatchObj.reason = `The ${data.humanPropName} property "${mismatchPropName}" contains invalid ` +
`property named "${_.get(ajvValidationErrorObj, 'params.propertyName')}"`;
break;

default:
break;
}

return mismatchObj;
}

module.exports = ajvValidationError;
76 changes: 55 additions & 21 deletions lib/deref.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,23 +54,28 @@ module.exports = {
* @param {string} parameterSourceOption tells that the schema object is of request or response
* @param {*} components components in openapi spec.
* @param {object} schemaResolutionCache stores already resolved references
* @param {*} resolveFor - resolve refs for validation/conversion (value to be one of VALIDATION/CONVERSION)
* @param {*} stack counter which keeps a tab on nested schemas
* @param {*} seenRef References that are repeated. Used to identify circular references.
* @returns {*} schema - schema that adheres to all individual schemas in schemaArr
*/
resolveAllOf: function (schemaArr, parameterSourceOption, components, schemaResolutionCache, stack = 0, seenRef) {
resolveAllOf: function (schemaArr, parameterSourceOption, components, schemaResolutionCache,
resolveFor = 'CONVERSION', stack = 0, seenRef) {

if (!(schemaArr instanceof Array)) {
return null;
}

if (schemaArr.length === 1) {
// for just one entry in allOf, don't need to enforce type: object restriction
return this.resolveRefs(schemaArr[0], parameterSourceOption, components, schemaResolutionCache, stack, seenRef);
return this.resolveRefs(schemaArr[0], parameterSourceOption, components, schemaResolutionCache, resolveFor,
stack, seenRef);
}

// generate one object for each schema
let indivObjects = schemaArr.map((schema) => {
return this.resolveRefs(schema, parameterSourceOption, components, schemaResolutionCache, stack, seenRef);
return this.resolveRefs(schema, parameterSourceOption, components, schemaResolutionCache, resolveFor,
stack, seenRef);
}).filter((schema) => {
return schema.type === 'object';
}),
Expand Down Expand Up @@ -107,33 +112,39 @@ module.exports = {
* @param {string} parameterSourceOption tells that the schema object is of request or response
* @param {*} components components in openapi spec.
* @param {object} schemaResolutionCache stores already resolved references
* @param {*} resolveFor - resolve refs for validation/conversion (value to be one of VALIDATION/CONVERSION)
* @param {*} stack counter which keeps a tab on nested schemas
* @param {*} seenRef - References that are repeated. Used to identify circular references.
* @returns {*} schema satisfying JSON-schema-faker.
*/
resolveRefs: function (schema, parameterSourceOption, components, schemaResolutionCache, stack = 0, seenRef = {}) {
var resolvedSchema, prop, splitRef;

resolveRefs: function (schema, parameterSourceOption, components, schemaResolutionCache,
resolveFor = 'CONVERSION', stack = 0, seenRef = {}) {
var resolvedSchema, prop, splitRef,
ERR_TOO_MANY_LEVELS = '<Error: Too many levels of nesting to fake this schema>';

stack++;
schemaResolutionCache = schemaResolutionCache || {};

if (stack > 20) {
return { value: '<Error: Too many levels of nesting to fake this schema>' };
return { value: ERR_TOO_MANY_LEVELS };
}

if (!schema) {
return { value: '<Error: Schema not found>' };
}

if (schema.anyOf) {
return this.resolveRefs(schema.anyOf[0], parameterSourceOption, components, schemaResolutionCache, stack,
_.cloneDeep(seenRef));
return this.resolveRefs(schema.anyOf[0], parameterSourceOption, components, schemaResolutionCache, resolveFor,
stack, _.cloneDeep(seenRef));
}
if (schema.oneOf) {
return this.resolveRefs(schema.oneOf[0], parameterSourceOption, components, schemaResolutionCache, stack,
_.cloneDeep(seenRef));
return this.resolveRefs(schema.oneOf[0], parameterSourceOption, components, schemaResolutionCache, resolveFor,
stack, _.cloneDeep(seenRef));
}
if (schema.allOf) {
return this.resolveAllOf(schema.allOf, parameterSourceOption, components, schemaResolutionCache, stack,
_.cloneDeep(seenRef));
return this.resolveAllOf(schema.allOf, parameterSourceOption, components, schemaResolutionCache, resolveFor,
stack, _.cloneDeep(seenRef));
}
if (schema.$ref && _.isFunction(schema.$ref.split)) {
let refKey = schema.$ref;
Expand All @@ -160,12 +171,27 @@ module.exports = {
// will be resolved - we don't care about anything after the components part
// splitRef.slice(1) will return ['components', 'schemas', 'PaginationEnvelope', 'properties', 'page']
// not using _.get here because that fails if there's a . in the property name (Pagination.Envelope, for example)
resolvedSchema = this._getEscaped(components, splitRef.slice(1));

splitRef = splitRef.slice(1).map((elem) => {
// https://swagger.io/docs/specification/using-ref#escape
// since / is the default delimiter, slashes are escaped with ~1
return decodeURIComponent(
elem
.replace(/~1/g, '/')
.replace(/~0/g, '~')
);
});

resolvedSchema = this._getEscaped(components, splitRef);

if (resolvedSchema) {
let refResolvedSchema = this.resolveRefs(resolvedSchema, parameterSourceOption,
components, schemaResolutionCache, stack, _.cloneDeep(seenRef));
schemaResolutionCache[refKey] = refResolvedSchema;
components, schemaResolutionCache, resolveFor, stack, _.cloneDeep(seenRef));

if (refResolvedSchema && refResolvedSchema.value !== ERR_TOO_MANY_LEVELS) {
schemaResolutionCache[refKey] = refResolvedSchema;
}

return refResolvedSchema;
}
return { value: 'reference ' + schema.$ref + ' not found in the OpenAPI spec' };
Expand Down Expand Up @@ -196,7 +222,7 @@ module.exports = {
}
/* eslint-enable */
tempSchema.properties[prop] = this.resolveRefs(property,
parameterSourceOption, components, schemaResolutionCache, stack, _.cloneDeep(seenRef));
parameterSourceOption, components, schemaResolutionCache, resolveFor, stack, _.cloneDeep(seenRef));
}
}
return tempSchema;
Expand All @@ -207,15 +233,19 @@ module.exports = {
}
else if (schema.type === 'array' && schema.items) {

// This nonsense is needed because the schemaFaker doesn't respect options.maxItems/minItems
schema.maxItems = 2;
schema.minItems = 2;
// For VALIDATION - keep minItems and maxItems properties defined by user in schema as is
// FOR CONVERSION - need to set both properties to 2 for schema faking
if (resolveFor === 'CONVERSION') {
// This is needed because the schemaFaker doesn't respect options.maxItems/minItems
schema.maxItems = 2;
schema.minItems = 2;
}
// have to create a shallow clone of schema object,
// so that the original schema.items object will not change
// without this, schemas with circular references aren't faked correctly
let tempSchema = _.omit(schema, 'items');
tempSchema.items = this.resolveRefs(schema.items, parameterSourceOption,
components, schemaResolutionCache, stack, _.cloneDeep(seenRef));
components, schemaResolutionCache, resolveFor, stack, _.cloneDeep(seenRef));
return tempSchema;
}
else if (!schema.hasOwnProperty('default')) {
Expand Down Expand Up @@ -251,7 +281,11 @@ module.exports = {
if (!schema.type) {
schema.type = 'string';
}
delete schema.format;

// For VALIDATION - Keep format as is to perform ajv validation on format
if (resolveFor !== 'VALIDATION') {
delete schema.format;
}
}

return schema;
Expand Down
9 changes: 9 additions & 0 deletions lib/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,15 @@ module.exports = {
description: 'MISSING_IN_SCHEMA indicates that an extra parameter was included in the request. For most ' +
'use cases, this need not be considered an error.',
external: true
},
{
name: 'Show detailed body validation messages',
id: 'detailedBlobValidation',
type: 'boolean',
default: false,
description: 'Determines whether to show detailed mismatch information for application/json content ' +
'in the request/response body',
external: true
}
];

Expand Down
Loading

0 comments on commit bab369a

Please sign in to comment.