diff --git a/lib/schemaUtils.js b/lib/schemaUtils.js index e203f1d35..a3fd027c1 100644 --- a/lib/schemaUtils.js +++ b/lib/schemaUtils.js @@ -1220,17 +1220,21 @@ module.exports = { * resolve references while generating params. * @param {object} options - a standard list of options that's globally passed around. Check options.js for more. * @param {object} schemaCache - object storing schemaFaker and schmeResolution caches - * @return {object} responseBody, contentType header needed + * @return {Array[Object]} Array of responseBody, contentType and headers needed */ convertToPmResponseBody: function(contentObj, components, options, schemaCache) { options = _.merge({}, defaultOptions, options); - var responseBody, cTypeHeader, hasComputedType, cTypes; + var cTypeHeader, + hasComputedType, + pmBodies = [], + responseBodies; + if (!contentObj) { - return { + return [{ contentTypeHeader: null, responseBody: '' - }; + }]; } let headers = Object.keys(contentObj); @@ -1247,32 +1251,42 @@ module.exports = { // if no JSON or XML, take whatever we have if (!hasComputedType) { - cTypes = Object.keys(contentObj); - if (cTypes.length > 0) { - cTypeHeader = cTypes[0]; + if (headers.length > 0) { + cTypeHeader = headers[0]; hasComputedType = true; } else { // just an empty object - can't convert anything - return { + return [{ contentTypeHeader: null, responseBody: '' - }; + }]; } } - responseBody = this.convertToPmBodyData(contentObj[cTypeHeader], REQUEST_TYPE.EXAMPLE, cTypeHeader, + responseBodies = this.convertToPmBodyData(contentObj[cTypeHeader], REQUEST_TYPE.EXAMPLE, cTypeHeader, PARAMETER_SOURCE.RESPONSE, options.indentCharacter, components, options, schemaCache); - if (this.getHeaderFamily(cTypeHeader) === HEADER_TYPE.JSON) { - responseBody = JSON.stringify(responseBody, null, options.indentCharacter); - } - else if (typeof responseBody !== 'string') { - // since the collection v2 schema only supports body being a string - responseBody = ''; + + responseBodies.forEach((responseBody) => { + if (this.getHeaderFamily(cTypeHeader) === HEADER_TYPE.JSON) { + responseBody = JSON.stringify(responseBody, null, options.indentCharacter); + } + else if (typeof responseBody !== 'string') { + // since the collection v2 schema only supports body being a string + responseBody = ''; + } + pmBodies.push({ + contentTypeHeader: cTypeHeader, + responseBody: responseBody + }); + }); + + if (_.isEmpty(responseBodies)) { + pmBodies.push({ + contentTypeHeader: cTypeHeader, + responseBody: '' + }); } - return { - contentTypeHeader: cTypeHeader, - responseBody: responseBody - }; + return pmBodies; }, /** @@ -1313,23 +1327,18 @@ module.exports = { getExampleData: function(exampleObj, components, options) { options = _.merge({}, defaultOptions, options); - var example, - exampleKey; - + var example; if (typeof exampleObj !== 'object') { return ''; } - exampleKey = Object.keys(exampleObj)[0]; - example = exampleObj[exampleKey]; // return example value if present else example is returned - - if (example.hasOwnProperty('$ref')) { + if (exampleObj.hasOwnProperty('$ref')) { example = this.getRefObject(example.$ref, components, options); } - if (example.hasOwnProperty('value')) { - example = example.value; + if (exampleObj.hasOwnProperty('value')) { + example = exampleObj.value; } return example; @@ -1356,9 +1365,10 @@ module.exports = { indentCharacter, components, options, schemaCache) { options = _.merge({}, defaultOptions, options); - var bodyData = '', + var bodyData = [], schemaFormat = SCHEMA_FORMATS.DEFAULT, schemaType, + example_names, resolveTo = this.resolveToExampleOrSchema(requestType, options.requestParametersResolution, options.exampleParametersResolution); @@ -1376,15 +1386,21 @@ module.exports = { catch (e) {} } } - bodyData = bodyObj.example; // return example value if present else example is returned - if (bodyData.hasOwnProperty('value')) { - bodyData = bodyData.value; + if (bodyObj.example.hasOwnProperty('value')) { + bodyData.push(bodyObj.example.value); + } + else { + bodyData.push(bodyObj.example); } } + else if (!_.isEmpty(bodyObj.examples) && (resolveTo === 'example' || !bodyObj.schema)) { - // take one of the examples as the body and not all - bodyData = this.getExampleData(bodyObj.examples, components, options); + example_names = Object.keys(bodyObj.examples); + example_names.forEach((example_name) => { + const example = this.getExampleData(bodyObj.examples[example_name], components, options); + bodyData.push(example); + }); } else if (bodyObj.schema) { if (bodyObj.schema.hasOwnProperty('$ref')) { @@ -1398,24 +1414,24 @@ module.exports = { if (options.complexityScore === 10) { schemaType = bodyObj.schema.type; if (schemaType === 'object') { - return { + return [{ value: '' - }; + }]; } if (schemaType === 'array') { - return [ + return [[ '' - ]; + ]]; } - return ''; + return ['']; } // Do not fake the bodyData if the complexity is 10. - bodyData = safeSchemaFaker(bodyObj.schema || {}, resolveTo, PROCESSING_TYPE.CONVERSION, parameterSourceOption, - components, schemaFormat, indentCharacter, schemaCache, options.stackLimit); + bodyData.push(safeSchemaFaker(bodyObj.schema || {}, resolveTo, PROCESSING_TYPE.CONVERSION, + parameterSourceOption, components, schemaFormat, indentCharacter, schemaCache, options.stackLimit)); } else { // do not fake if the option is false - bodyData = ''; + bodyData = []; } } return bodyData; @@ -1691,8 +1707,10 @@ module.exports = { if (contentObj[URLENCODED].hasOwnProperty('schema') && contentObj[URLENCODED].schema.hasOwnProperty('$ref')) { contentObj[URLENCODED].schema = this.getRefObject(contentObj[URLENCODED].schema.$ref, components, options); } + + // There is going to be a max of one requestBody bodyData = this.convertToPmBodyData(contentObj[URLENCODED], requestType, URLENCODED, - PARAMETER_SOURCE.REQUEST, options.indentCharacter, components, options, schemaCache); + PARAMETER_SOURCE.REQUEST, options.indentCharacter, components, options, schemaCache)[0]; encoding = contentObj[URLENCODED].encoding ? contentObj[URLENCODED].encoding : {}; // create query parameters and add it to the request body object _.forOwn(bodyData, (value, key) => { @@ -1750,8 +1768,10 @@ module.exports = { } else if (contentObj.hasOwnProperty(FORM_DATA)) { rDataMode = 'formdata'; + + // There is going to be a max of one requestBody bodyData = this.convertToPmBodyData(contentObj[FORM_DATA], requestType, FORM_DATA, - PARAMETER_SOURCE.REQUEST, options.indentCharacter, components, options, schemaCache); + PARAMETER_SOURCE.REQUEST, options.indentCharacter, components, options, schemaCache)[0]; encoding = contentObj[FORM_DATA].encoding ? contentObj[FORM_DATA].encoding : {}; // create the form parameters and add it to the request body object _.forOwn(bodyData, (value, key) => { @@ -1832,8 +1852,9 @@ module.exports = { } } + // There is going to be a max of one requestBody bodyData = this.convertToPmBodyData(contentObj[bodyType], requestType, bodyType, - PARAMETER_SOURCE.REQUEST, options.indentCharacter, components, options, schemaCache); + PARAMETER_SOURCE.REQUEST, options.indentCharacter, components, options, schemaCache)[0]; updateOptions = { mode: rDataMode, @@ -1863,18 +1884,19 @@ module.exports = { * resolve references while generating params. * @param {object} options - a standard list of options that's globally passed around. Check options.js for more. * @param {object} schemaCache - object storing schemaFaker and schmeResolution caches - * @returns {Object} postman response + * @returns {Array[Object]} Array of postman response objects */ convertToPmResponse: function(response, code, originalRequest, components, options, schemaCache) { options = _.merge({}, defaultOptions, options); var responseHeaders = [], previewLanguage = 'text', - responseBodyWrapper, + responseBodyWrappers, header, - sdkResponse; + sdkResponse, + sdkResponses = []; if (!response) { - return null; + return []; } _.forOwn(response.headers, (value, key) => { if (_.toLower(key) !== 'content-type') { @@ -1895,42 +1917,45 @@ module.exports = { } }); - responseBodyWrapper = this.convertToPmResponseBody(response.content, components, options, schemaCache); - - if (responseBodyWrapper.contentTypeHeader) { - // we could infer the content-type header from the body - responseHeaders.push({ key: 'Content-Type', value: responseBodyWrapper.contentTypeHeader }); - if (this.getHeaderFamily(responseBodyWrapper.contentTypeHeader) === HEADER_TYPE.JSON) { - previewLanguage = PREVIEW_LANGUAGE.JSON; - } - else if (this.getHeaderFamily(responseBodyWrapper.contentTypeHeader) === HEADER_TYPE.XML) { - previewLanguage = PREVIEW_LANGUAGE.XML; + responseBodyWrappers = this.convertToPmResponseBody(response.content, components, options, schemaCache); + responseBodyWrappers.forEach((responseBodyWrapper) => { + if (responseBodyWrapper.contentTypeHeader) { + // we could infer the content-type header from the body + responseHeaders.push({ key: 'Content-Type', value: responseBodyWrapper.contentTypeHeader }); + if (this.getHeaderFamily(responseBodyWrapper.contentTypeHeader) === HEADER_TYPE.JSON) { + previewLanguage = PREVIEW_LANGUAGE.JSON; + } + else if (this.getHeaderFamily(responseBodyWrapper.contentTypeHeader) === HEADER_TYPE.XML) { + previewLanguage = PREVIEW_LANGUAGE.XML; + } } - } - else if (response.content && Object.keys(response.content).length > 0) { - responseHeaders.push({ key: 'Content-Type', value: Object.keys(response.content)[0] }); - if (this.getHeaderFamily(Object.keys(response.content)[0]) === HEADER_TYPE.JSON) { - previewLanguage = PREVIEW_LANGUAGE.JSON; + else if (response.content && Object.keys(response.content).length > 0) { + responseHeaders.push({ key: 'Content-Type', value: Object.keys(response.content)[0] }); + if (this.getHeaderFamily(Object.keys(response.content)[0]) === HEADER_TYPE.JSON) { + previewLanguage = PREVIEW_LANGUAGE.JSON; + } + else if (this.getHeaderFamily(Object.keys(response.content)[0]) === HEADER_TYPE.XML) { + previewLanguage = PREVIEW_LANGUAGE.XML; + } } - else if (this.getHeaderFamily(Object.keys(response.content)[0]) === HEADER_TYPE.XML) { - previewLanguage = PREVIEW_LANGUAGE.XML; + else { + responseHeaders.push({ key: 'Content-Type', value: TEXT_PLAIN }); } - } - else { - responseHeaders.push({ key: 'Content-Type', value: TEXT_PLAIN }); - } - code = code.replace(/X/g, '0'); - - sdkResponse = new sdk.Response({ - name: response.description, - code: code === 'default' ? 500 : Number(code), - header: responseHeaders, - body: responseBodyWrapper.responseBody, - originalRequest: originalRequest + code = code.replace(/X/g, '0'); + + sdkResponse = new sdk.Response({ + name: response.description, + code: code === 'default' ? 500 : Number(code), + header: responseHeaders, + body: responseBodyWrapper.responseBody, + originalRequest: originalRequest + }); + sdkResponse._postman_previewlanguage = previewLanguage; + + sdkResponses.push(sdkResponse); }); - sdkResponse._postman_previewlanguage = previewLanguage; - return sdkResponse; + return sdkResponses; }, /** @@ -2328,7 +2353,7 @@ module.exports = { let thisOriginalRequest = {}, responseAuthHelper, authQueryParams, - convertedResponse; + convertedResponses; if (options.includeAuthInfoInExample) { responseAuthHelper = this.getResponseAuthHelper(authHelper); @@ -2389,9 +2414,11 @@ module.exports = { // 'Exception:', e); thisOriginalRequest.body = {}; } - convertedResponse = this.convertToPmResponse(swagResponse, code, thisOriginalRequest, + convertedResponses = this.convertToPmResponse(swagResponse, code, thisOriginalRequest, components, options, schemaCache); - convertedResponse && item.responses.add(convertedResponse); + convertedResponses.forEach((convertedResponse) => { + item.responses.add(convertedResponse); + }); }); } diff --git a/test/data/valid_openapi/example-spec.yaml b/test/data/valid_openapi/example-spec.yaml new file mode 100644 index 000000000..874e32e4c --- /dev/null +++ b/test/data/valid_openapi/example-spec.yaml @@ -0,0 +1,72 @@ +openapi: "3.0.0" + +info: + title: ExampleApi + description: Example Api + version: 1.0.0 + contact: + name: support + email: support@example.com + +servers: + - url: http://example.com + description: example server + +paths: + /users: + post: + description: Create User + summary: Create User + security: + - {} + requestBody: + description: User + content: + application/json: + schema: + type: object + properties: + id: + type: integer + name: + type: string + examples: + John-Ok-Example: + summary: Success create of John User + description: Success create of John User + value: + id: 1 + name: John Doe + Jane-Ok-Example: + summary: Success create of Jane User + description: Success create of Jane User + value: + id: 2 + name: Jane Doe + + responses: + '201': + description: Return a User Object + content: + application/json: + schema: + type: object + properties: + id: + type: integer + name: + type: string + examples: + John-Ok-Example: + summary: Success create of John User + description: Success create of John User + value: + id: 1 + name: John Doe + Jane-Ok-Example: + summary: Success create of Jane User + description: Success create of Jane User + value: + id: 2 + name: Jane Doe + \ No newline at end of file diff --git a/test/unit/base.test.js b/test/unit/base.test.js index a66e12772..df236a319 100644 --- a/test/unit/base.test.js +++ b/test/unit/base.test.js @@ -967,6 +967,17 @@ describe('CONVERT FUNCTION TESTS ', function() { done(); }); }); + it('[GitHub #350] - The converter should resolve multiple examples', function (done) { + var emptyAuthSpec = path.join(__dirname, VALID_OPENAPI_PATH + '/example-spec.yaml'), + openapi = fs.readFileSync(emptyAuthSpec, 'utf8'); + Converter.convert({ type: 'string', data: openapi }, { requestNameSource: 'URL' }, + (err, conversionResult) => { + expect(err).to.be.null; + let response = conversionResult.output[0].data.item[0].response; + expect(response.length).to.equal(2); + done(); + }); + }); }); describe('requestNameSource option', function() { diff --git a/test/unit/util.test.js b/test/unit/util.test.js index 7c11546b2..c0c917ed8 100644 --- a/test/unit/util.test.js +++ b/test/unit/util.test.js @@ -653,7 +653,7 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { format: 'int32' } }, - retValSchema = SchemaUtils.convertToPmBodyData(bodyWithSchema, 'ROOT', 'application/json'); + retValSchema = SchemaUtils.convertToPmBodyData(bodyWithSchema, 'ROOT', 'application/json')[0]; expect(retValSchema).to.be.equal(''); }); @@ -664,7 +664,7 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { value: 'This is a sample value' } }, - retValExample = SchemaUtils.convertToPmBodyData(bodyWithExample, 'application/json'); + retValExample = SchemaUtils.convertToPmBodyData(bodyWithExample, 'application/json')[0]; expect(retValExample).to.equal('This is a sample value'); }); @@ -681,7 +681,7 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { } }, retValExamples = SchemaUtils.convertToPmBodyData(bodyWithExamples, 'ROOT', 'application/json', - 'request', ' ', null, { requestParametersResolution: 'example' }); + 'request', ' ', null, { requestParametersResolution: 'example' })[0]; expect(retValExamples.foo).to.equal(1); expect(retValExamples.bar).to.equal(2); }); @@ -704,7 +704,7 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { } } }, - { requestParametersResolution: 'example' }); + { requestParametersResolution: 'example' })[0]; expect(retValExample).to.equal('Hello'); }); @@ -725,7 +725,7 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { } } } - }, { requestParametersResolution: 'example' }); + }, { requestParametersResolution: 'example' })[0]; expect(retValExample.name).to.equal('Example'); }); }); @@ -1678,7 +1678,7 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { it('with undefined ContentObj', function() { var contentObj, pmResponseBody; - pmResponseBody = SchemaUtils.convertToPmResponseBody(contentObj).responseBody; + pmResponseBody = SchemaUtils.convertToPmResponseBody(contentObj)[0].responseBody; expect(pmResponseBody).to.equal(''); }); it('with Content-Type application/json', function() { @@ -1703,7 +1703,7 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { } }, pmResponseBody; - pmResponseBody = JSON.parse(SchemaUtils.convertToPmResponseBody(contentObj).responseBody); + pmResponseBody = JSON.parse(SchemaUtils.convertToPmResponseBody(contentObj)[0].responseBody); expect(pmResponseBody.id).to.equal(''); expect(pmResponseBody.name).to.equal(''); }); @@ -1729,7 +1729,7 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { } }, pmResponseBody; - pmResponseBody = JSON.parse(SchemaUtils.convertToPmResponseBody(contentObj).responseBody); + pmResponseBody = JSON.parse(SchemaUtils.convertToPmResponseBody(contentObj)[0].responseBody); expect(pmResponseBody.id).to.equal(''); expect(pmResponseBody.name).to.equal(''); }); @@ -1755,7 +1755,7 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { } }, pmResponseBody; - pmResponseBody = JSON.parse(SchemaUtils.convertToPmResponseBody(contentObj).responseBody); + pmResponseBody = JSON.parse(SchemaUtils.convertToPmResponseBody(contentObj)[0].responseBody); expect(pmResponseBody.id).to.equal(''); expect(pmResponseBody.name).to.equal(''); }); @@ -1775,7 +1775,7 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { pmResponseBody; pmResponseBody = SchemaUtils.convertToPmResponseBody(contentObj, {}, { indentCharacter: '\t' - }).responseBody; + })[0].responseBody; expect(pmResponseBody).to.equal('{\n\t"id": ""\n}'); }); it('with Content-Type text/plain', function() { @@ -1787,7 +1787,7 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { } }, pmResponseBody; - pmResponseBody = SchemaUtils.convertToPmResponseBody(contentObj).responseBody; + pmResponseBody = SchemaUtils.convertToPmResponseBody(contentObj)[0].responseBody; expect(typeof pmResponseBody).to.equal('string'); }); it('with Content-Type application/xml', function() { @@ -1815,7 +1815,7 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { pmResponseBody; pmResponseBody = SchemaUtils.convertToPmResponseBody(contentObj, {}, { indentCharacter: ' ' - }).responseBody; + })[0].responseBody; expect(pmResponseBody).to.equal( [ '', @@ -1835,7 +1835,7 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { } }, pmResponseBody; - pmResponseBody = SchemaUtils.convertToPmResponseBody(contentObj).responseBody; + pmResponseBody = SchemaUtils.convertToPmResponseBody(contentObj)[0].responseBody; expect(typeof pmResponseBody).to.equal('string'); }); it('with Content-Type unsupported', function() { @@ -1860,9 +1860,9 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { } }, pmResponseBody; - pmResponseBody = SchemaUtils.convertToPmResponseBody(contentObj).responseBody; + pmResponseBody = SchemaUtils.convertToPmResponseBody(contentObj)[0].responseBody; expect(pmResponseBody).to.equal(''); - }); + })[0]; // things remaining application/xml, application/javascript }); }); @@ -1895,7 +1895,7 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { code = '20X', pmResponse, responseBody; - pmResponse = SchemaUtils.convertToPmResponse(response, code).toJSON(); + pmResponse = SchemaUtils.convertToPmResponse(response, code)[0].toJSON(); responseBody = JSON.parse(pmResponse.body); expect(pmResponse.name).to.equal(response.description); expect(pmResponse.code).to.equal(200); @@ -1935,7 +1935,7 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { code = '20X', pmResponse; - pmResponse = SchemaUtils.convertToPmResponse(response, code).toJSON(); + pmResponse = SchemaUtils.convertToPmResponse(response, code)[0].toJSON(); expect(pmResponse.body).to.equal('\n (integer)\n (string)\n'); expect(pmResponse.name).to.equal(response.description); expect(pmResponse.code).to.equal(200); @@ -1953,7 +1953,7 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { code = '201', pmResponse; - pmResponse = SchemaUtils.convertToPmResponse(response, code).toJSON(); + pmResponse = SchemaUtils.convertToPmResponse(response, code)[0].toJSON(); expect(pmResponse.name).to.equal(response.description); expect(pmResponse.code).to.equal(201); expect(pmResponse.body).to.equal(''); @@ -2002,7 +2002,7 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () { } } } - }); + })[0]; expect(pmResponse.headers.members[0].key).to.equal('Retry-After'); expect(pmResponse.headers.members[0].description).to.equal('Some description');