mirror of
https://github.com/postmanlabs/openapi-to-postman.git
synced 2022-11-29 22:05:00 +03:00
Merge pull request #482 from postmanlabs/feature/validateTransaction-fixes
Fixed issue where newly converted collection had mismatches from validateTransaction() API.
This commit is contained in:
81
lib/deref.js
81
lib/deref.js
@@ -16,7 +16,17 @@ const _ = require('lodash'),
|
||||
'date-time': '<dateTime>',
|
||||
password: '<password>'
|
||||
},
|
||||
boolean: '<boolean>'
|
||||
boolean: '<boolean>',
|
||||
array: '<array>',
|
||||
object: '<object>'
|
||||
},
|
||||
SCHEMA_TYPES = {
|
||||
array: 'array',
|
||||
boolean: 'boolean',
|
||||
integer: 'integer',
|
||||
number: 'number',
|
||||
object: 'object',
|
||||
string: 'string'
|
||||
},
|
||||
PARAMETER_SOURCE = {
|
||||
REQUEST: 'REQUEST',
|
||||
@@ -141,14 +151,6 @@ module.exports = {
|
||||
return { value: ERR_TOO_MANY_LEVELS };
|
||||
}
|
||||
|
||||
// Update max stack reached for all current refs that's being resolved
|
||||
if (!_.isEmpty(this._currentRefStack)) {
|
||||
_.forEach(this._currentRefStack, (refKey) => {
|
||||
_.set(schemaResolutionCache, [refKey, 'maxStack'],
|
||||
Math.max(_.get(schemaResolutionCache, [refKey, 'maxStack'], 0), stack));
|
||||
});
|
||||
}
|
||||
|
||||
if (!schema) {
|
||||
return { value: '<Error: Schema not found>' };
|
||||
}
|
||||
@@ -177,10 +179,6 @@ module.exports = {
|
||||
return this.resolveAllOf(schema.allOf, parameterSourceOption, components, schemaResolutionCache, resolveFor,
|
||||
resolveTo, stack, _.cloneDeep(seenRef), stackLimit);
|
||||
}
|
||||
if (schema.additionalProperties && !_.isBoolean(schema.additionalProperties)) {
|
||||
schema.additionalProperties = this.resolveRefs(schema.additionalProperties, parameterSourceOption,
|
||||
components, schemaResolutionCache, resolveFor, resolveTo, stack, _.cloneDeep(seenRef), stackLimit);
|
||||
}
|
||||
if (schema.$ref && _.isFunction(schema.$ref.split)) {
|
||||
let refKey = schema.$ref,
|
||||
outerProperties = concreteUtils.getOuterPropsIfIsSupported(schema);
|
||||
@@ -201,20 +199,6 @@ module.exports = {
|
||||
return { value: 'reference ' + schema.$ref + ' not found in the OpenAPI spec' };
|
||||
}
|
||||
|
||||
if (_.get(schemaResolutionCache, [refKey, 'schema'])) {
|
||||
// maxStack for cached schema is how deep of nesting level we reached while resolving that schema
|
||||
let maxStack = _.get(schemaResolutionCache, [refKey, 'maxStack'], 0),
|
||||
// resLevel of perticuler cached schema is nesting level at which schema was resolved
|
||||
resLevel = _.get(schemaResolutionCache, [refKey, 'resLevel'], stackLimit);
|
||||
|
||||
/**
|
||||
* use cached schema if it was resolved at level lower or equal then at current stack level or
|
||||
* if cached schema is resolved fully (it does not contain ERR_TOO_MANY_LEVELS value in sub schema)
|
||||
*/
|
||||
if (resLevel <= stack || maxStack < stackLimit) {
|
||||
return schemaResolutionCache[refKey].schema;
|
||||
}
|
||||
}
|
||||
// something like #/components/schemas/PaginationEnvelope/properties/page
|
||||
// will be resolved - we don't care about anything after the components part
|
||||
// splitRef.slice(1) will return ['components', 'schemas', 'PaginationEnvelope', 'properties', 'page']
|
||||
@@ -235,32 +219,33 @@ module.exports = {
|
||||
resolvedSchema = concreteUtils.addOuterPropsToRefSchemaIfIsSupported(resolvedSchema, outerProperties);
|
||||
}
|
||||
if (resolvedSchema) {
|
||||
// add current ref that's being resolved in ref stack
|
||||
!_.isArray(this._currentRefStack) && (this._currentRefStack = []);
|
||||
this._currentRefStack.push(refKey);
|
||||
|
||||
let refResolvedSchema = this.resolveRefs(resolvedSchema, parameterSourceOption,
|
||||
components, schemaResolutionCache, resolveFor, resolveTo, stack, _.cloneDeep(seenRef), stackLimit);
|
||||
|
||||
// remove current ref that's being resolved from stack as soon as resolved
|
||||
_.isArray(this._currentRefStack) && (this._currentRefStack.pop());
|
||||
|
||||
if (refResolvedSchema && refResolvedSchema.value !== ERR_TOO_MANY_LEVELS) {
|
||||
_.set(schemaResolutionCache, [refKey, 'resLevel'], stack);
|
||||
_.set(schemaResolutionCache, [refKey, 'schema'], refResolvedSchema);
|
||||
}
|
||||
|
||||
return refResolvedSchema;
|
||||
}
|
||||
return { value: 'reference ' + schema.$ref + ' not found in the OpenAPI spec' };
|
||||
}
|
||||
if (concreteUtils.compareTypes(schema.type, 'objects') || schema.hasOwnProperty('properties')) {
|
||||
if (concreteUtils.compareTypes(schema.type, SCHEMA_TYPES.object) || schema.hasOwnProperty('properties') ||
|
||||
schema.hasOwnProperty('additionalProperties')) {
|
||||
// go through all props
|
||||
schema.type = 'object';
|
||||
if (schema.hasOwnProperty('properties')) {
|
||||
schema.type = SCHEMA_TYPES.object;
|
||||
if (_.has(schema, 'properties') || _.has(schema, 'additionalProperties')) {
|
||||
// shallow cloning schema object except properties object
|
||||
let tempSchema = _.omit(schema, 'properties');
|
||||
tempSchema.properties = {};
|
||||
let tempSchema = _.omit(schema, ['properties', 'additionalProperties']);
|
||||
|
||||
if (_.has(schema, 'additionalProperties')) {
|
||||
// don't resolve boolean values
|
||||
if (_.isBoolean(schema.additionalProperties)) {
|
||||
tempSchema.additionalProperties = schema.additionalProperties;
|
||||
}
|
||||
else {
|
||||
tempSchema.additionalProperties = this.resolveRefs(schema.additionalProperties, parameterSourceOption,
|
||||
components, schemaResolutionCache, resolveFor, resolveTo, stack, _.cloneDeep(seenRef), stackLimit);
|
||||
}
|
||||
}
|
||||
|
||||
!_.isEmpty(schema.properties) && (tempSchema.properties = {});
|
||||
for (prop in schema.properties) {
|
||||
if (schema.properties.hasOwnProperty(prop)) {
|
||||
/* eslint-disable max-depth */
|
||||
@@ -288,10 +273,10 @@ module.exports = {
|
||||
|
||||
// Override deefault value to appropriate type representation for parameter resolution to schema
|
||||
if (resolveFor === 'CONVERSION' && resolveTo === 'schema') {
|
||||
schema.default = '<object>';
|
||||
schema.default = type.object;
|
||||
}
|
||||
}
|
||||
else if (concreteUtils.compareTypes(schema.type, 'array') && schema.items) {
|
||||
else if (concreteUtils.compareTypes(schema.type, SCHEMA_TYPES.array) && schema.items) {
|
||||
/*
|
||||
For VALIDATION - keep minItems and maxItems properties defined by user in schema as is
|
||||
FOR CONVERSION -
|
||||
@@ -350,11 +335,11 @@ module.exports = {
|
||||
}
|
||||
else {
|
||||
return {
|
||||
type: 'object'
|
||||
type: SCHEMA_TYPES.object
|
||||
};
|
||||
}
|
||||
if (!schema.type) {
|
||||
schema.type = 'string';
|
||||
schema.type = SCHEMA_TYPES.string;
|
||||
}
|
||||
|
||||
// Discard format if not supported by both json-schema-faker and ajv or pattern is also defined
|
||||
|
||||
@@ -915,7 +915,7 @@ module.exports = {
|
||||
convertPathVariables: function(type, providedPathVars, commonPathVars, components, options, schemaCache) {
|
||||
options = _.merge({}, defaultOptions, options);
|
||||
|
||||
var variables = providedPathVars;
|
||||
var variables = [];
|
||||
// converting the base uri path variables, if any
|
||||
// commonPathVars is an object for type = root/method
|
||||
// array otherwise
|
||||
@@ -948,7 +948,8 @@ module.exports = {
|
||||
});
|
||||
}
|
||||
|
||||
return variables;
|
||||
// keep already provided varables (server variables) at last
|
||||
return _.concat(variables, providedPathVars);
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -1429,8 +1430,13 @@ module.exports = {
|
||||
else if (bodyObj.schema) {
|
||||
if (bodyObj.schema.hasOwnProperty('$ref')) {
|
||||
let outerProps = concreteUtils.getOuterPropsIfIsSupported(bodyObj.schema),
|
||||
resolvedSchema;
|
||||
|
||||
// skip beforehand resolution for OAS 3.0
|
||||
if (outerProps) {
|
||||
resolvedSchema = this.getRefObject(bodyObj.schema.$ref, components, options);
|
||||
bodyObj.schema = concreteUtils.addOuterPropsToRefSchemaIfIsSupported(resolvedSchema, outerProps);
|
||||
bodyObj.schema = concreteUtils.addOuterPropsToRefSchemaIfIsSupported(resolvedSchema, outerProps);
|
||||
}
|
||||
}
|
||||
if (options.schemaFaker) {
|
||||
if (this.getHeaderFamily(contentType) === HEADER_TYPE.XML) {
|
||||
@@ -1438,7 +1444,7 @@ module.exports = {
|
||||
}
|
||||
// Do not fake schemas if the complexity score is 10
|
||||
if (options.complexityScore === 10) {
|
||||
schemaType = bodyObj.schema.type;
|
||||
schemaType = _.get(this.getRefObject(bodyObj.schema.$ref, components, options), 'type');
|
||||
if (schemaType === 'object') {
|
||||
return {
|
||||
value: '<Error: Spec size too large, skipping faking of schemas>'
|
||||
@@ -1760,12 +1766,14 @@ module.exports = {
|
||||
// handling for the urlencoded media type
|
||||
if (contentObj.hasOwnProperty(URLENCODED)) {
|
||||
rDataMode = 'urlencoded';
|
||||
if (contentObj[URLENCODED].hasOwnProperty('schema') && contentObj[URLENCODED].schema.hasOwnProperty('$ref')) {
|
||||
contentObj[URLENCODED].schema = this.getRefObject(contentObj[URLENCODED].schema.$ref, components, options);
|
||||
}
|
||||
bodyData = this.convertToPmBodyData(contentObj[URLENCODED], requestType, URLENCODED,
|
||||
PARAMETER_SOURCE.REQUEST, options.indentCharacter, components, options, schemaCache);
|
||||
encoding = contentObj[URLENCODED].encoding ? contentObj[URLENCODED].encoding : {};
|
||||
|
||||
if (contentObj[URLENCODED].hasOwnProperty('schema') && contentObj[URLENCODED].schema.hasOwnProperty('$ref')) {
|
||||
contentObj[URLENCODED].schema = this.getRefObject(contentObj[URLENCODED].schema.$ref, components, options);
|
||||
}
|
||||
|
||||
// create query parameters and add it to the request body object
|
||||
_.forOwn(bodyData, (value, key) => {
|
||||
|
||||
@@ -2054,6 +2062,10 @@ module.exports = {
|
||||
options = _.merge({}, defaultOptions, options);
|
||||
var refObj, savedSchema;
|
||||
|
||||
if (typeof $ref !== 'string') {
|
||||
return { value: `Invalid $ref: ${$ref} was found` };
|
||||
}
|
||||
|
||||
savedSchema = $ref.split('/').slice(1).map((elem) => {
|
||||
// https://swagger.io/docs/specification/using-ref#escape
|
||||
// since / is the default delimiter, slashes are escaped with ~1
|
||||
|
||||
@@ -424,9 +424,10 @@ paths:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: integer
|
||||
format: int32
|
||||
properties:
|
||||
prop123:
|
||||
type: integer
|
||||
format: int32
|
||||
security:
|
||||
- api_key: []
|
||||
/store/order:
|
||||
|
||||
@@ -34,7 +34,20 @@
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"readOnly": true
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"tag": {
|
||||
"type": "string",
|
||||
"writeOnly": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +100,14 @@ describe('DEREF FUNCTION TESTS ', function() {
|
||||
name: {
|
||||
type: 'string'
|
||||
}
|
||||
},
|
||||
additionalProperties: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
hello: {
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -119,7 +127,8 @@ describe('DEREF FUNCTION TESTS ', function() {
|
||||
_.cloneDeep(componentsAndPaths), {}, 'VALIDATION'),
|
||||
output_emptyObject = deref.resolveRefs(schemaWithEmptyObject, parameterSource, _.cloneDeep(componentsAndPaths)),
|
||||
output_additionalProps = deref.resolveRefs(schemaWithAdditionPropRef, parameterSource,
|
||||
_.cloneDeep(componentsAndPaths), {}, 'VALIDATION');
|
||||
_.cloneDeep(componentsAndPaths), {}, 'VALIDATION'),
|
||||
output_additionalPropsOverride;
|
||||
|
||||
expect(output).to.deep.include({ type: 'object',
|
||||
required: ['id'],
|
||||
@@ -170,42 +179,15 @@ describe('DEREF FUNCTION TESTS ', function() {
|
||||
// additionalProperties $ref should be resolved
|
||||
expect(output_additionalProps).to.deep.include(componentsAndPaths.components.schemas.schemaAdditionalProps);
|
||||
|
||||
done();
|
||||
});
|
||||
// add default to above resolved schema
|
||||
output_additionalProps.additionalProperties.properties.hello.default = '<string>';
|
||||
|
||||
it('should populate schemaResolutionCache having key as the ref provided', function (done) {
|
||||
var schema = {
|
||||
$ref: '#/components/schema/request'
|
||||
},
|
||||
componentsAndPaths = {
|
||||
components: {
|
||||
schema: {
|
||||
request: {
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
example: 'example name'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
concreteUtils: schemaUtils30X
|
||||
},
|
||||
parameterSource = 'REQUEST',
|
||||
schemaResolutionCache = {},
|
||||
resolvedSchema = deref.resolveRefs(schema, parameterSource, componentsAndPaths, schemaResolutionCache);
|
||||
expect(_.get(schemaResolutionCache, ['#/components/schema/request', 'schema'])).to.deep.equal(resolvedSchema);
|
||||
expect(resolvedSchema).to.deep.equal({
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: {
|
||||
type: 'string',
|
||||
example: 'example name',
|
||||
default: '<string>'
|
||||
}
|
||||
}
|
||||
});
|
||||
output_additionalPropsOverride = deref.resolveRefs(schemaWithAdditionPropRef, parameterSource,
|
||||
_.cloneDeep(componentsAndPaths), {}, 'VALIDATION');
|
||||
|
||||
// override should not affect newly resolved schema
|
||||
expect(output_additionalPropsOverride).to.deep.include(
|
||||
componentsAndPaths.components.schemas.schemaAdditionalProps);
|
||||
done();
|
||||
});
|
||||
|
||||
@@ -273,90 +255,6 @@ describe('DEREF FUNCTION TESTS ', function() {
|
||||
expect(output.pattern).to.eql(schema.pattern);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should correctly resolve schema from schemaResoltionCache based on schema resolution level', function (done) {
|
||||
let schema = {
|
||||
$ref: '#/components/schemas/schemaUsed'
|
||||
},
|
||||
consumerSchema = {
|
||||
type: 'object',
|
||||
properties: { level2: {
|
||||
type: 'object',
|
||||
properties: { level3: {
|
||||
type: 'object',
|
||||
properties: { level4: {
|
||||
type: 'object',
|
||||
properties: { level5: {
|
||||
type: 'object',
|
||||
properties: { level6: {
|
||||
type: 'object',
|
||||
properties: { level7: {
|
||||
type: 'object',
|
||||
properties: { level8: {
|
||||
type: 'object',
|
||||
properties: { level9: { $ref: '#/components/schemas/schemaUsed' } }
|
||||
} }
|
||||
} }
|
||||
} }
|
||||
} }
|
||||
} }
|
||||
} }
|
||||
} }
|
||||
},
|
||||
componentsAndPaths = {
|
||||
components: {
|
||||
schemas: {
|
||||
schemaUsed: {
|
||||
'type': 'object',
|
||||
'required': [
|
||||
'id',
|
||||
'name'
|
||||
],
|
||||
'properties': {
|
||||
'id': {
|
||||
'type': 'integer',
|
||||
'format': 'int64'
|
||||
},
|
||||
'name': {
|
||||
'type': 'string'
|
||||
},
|
||||
'tag': {
|
||||
'type': 'string'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
concreteUtils: schemaUtils30X
|
||||
},
|
||||
parameterSource = 'REQUEST',
|
||||
schemaResoltionCache = {},
|
||||
resolvedConsumerSchema,
|
||||
resolvedSchema;
|
||||
|
||||
resolvedConsumerSchema = deref.resolveRefs(consumerSchema, parameterSource, componentsAndPaths,
|
||||
schemaResoltionCache);
|
||||
|
||||
// Consumer schema contains schema at nesting level 9, which results in impartial resolution of schema
|
||||
expect(_.get(schemaResoltionCache, ['#/components/schemas/schemaUsed', 'resLevel'])).to.eql(9);
|
||||
expect(_.get(resolvedConsumerSchema, _.join(_.map(_.range(1, 10), (ele) => {
|
||||
return `properties.level${ele}`;
|
||||
}), '.'))).to.not.deep.equal(componentsAndPaths.components.schemas.schemaUsed);
|
||||
expect(_.get(schemaResoltionCache, ['#/components/schemas/schemaUsed', 'schema'])).to.not.deep
|
||||
.equal(componentsAndPaths.components.schemas.schemaUsed);
|
||||
resolvedSchema = deref.resolveRefs(schema, parameterSource, componentsAndPaths, schemaResoltionCache);
|
||||
// Restoring the original format as it is deleted if not supported by json-schema-faker and ajv
|
||||
resolvedSchema.properties.id.format = 'int64';
|
||||
|
||||
/**
|
||||
* Even though schema cache contains schemaUsed as impartially cached,resolution were it's used again will
|
||||
* depend on ongoing resolution level and schema is cached again if it's updated.
|
||||
*/
|
||||
expect(resolvedSchema).to.deep.equal(componentsAndPaths.components.schemas.schemaUsed);
|
||||
expect(_.get(schemaResoltionCache, ['#/components/schemas/schemaUsed', 'schema'])).to.deep
|
||||
.equal(componentsAndPaths.components.schemas.schemaUsed);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveAllOf Function', function () {
|
||||
|
||||
Reference in New Issue
Block a user