mirror of
https://github.com/postmanlabs/openapi-to-postman.git
synced 2022-11-29 22:05:00 +03:00
Supporting XML schemas as well, refactoring, moving tests around
This commit is contained in:
@@ -185,9 +185,10 @@ $ openapi2postmanv2 --test
|
||||
| request.url.params | parameter (`in = query`) | - | {"key": param.name, "value": [here](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#style-examples)}|
|
||||
| api_key in (query or header) | components.securitySchemes.api_key | - ||
|
||||
|
||||
|
||||
>`option : 'schemaFaker'(boolean) whether to use json-schema-faker for schema conversion`
|
||||
>`option : 'requestNameSource'(string) The strategy to use to generate request names. url: use the request's URL as the name, fallback: Use the summary/operationId/URL (in that order) Default: fallback`
|
||||
Usage Options:
|
||||
* `'schemaFaker'(boolean): whether to use json-schema-faker for schema conversion`
|
||||
* `'requestNameSource'(string): The strategy to use to generate request names. url: use the request's URL as the name, fallback: Use the summary/operationId/URL (in that order) Default: fallback`
|
||||
* `'indentCharacter' (string): The character to use per level of indentation for JSON/XML data. Default: ' ' (space)`
|
||||
|
||||
### Header/Path param conversion example
|
||||
|
||||
|
||||
@@ -58,7 +58,8 @@ var sdk = require('postman-collection'),
|
||||
// set default options
|
||||
util.options = {
|
||||
requestNameSource: (options.hasOwnProperty('requestNameSource') ? options.requestNameSource : 'fallback'),
|
||||
schemaFaker: (options.hasOwnProperty('schemaFaker') ? options.schemaFaker : true)
|
||||
schemaFaker: (options.hasOwnProperty('schemaFaker') ? options.schemaFaker : true),
|
||||
indentCharacter: (options.hasOwnProperty('indentCharacter') ? options.indentCharacter : ' ')
|
||||
};
|
||||
|
||||
// @TODO - Have to handle global level security scheme
|
||||
|
||||
84
lib/util.js
84
lib/util.js
@@ -3,8 +3,13 @@ const sdk = require('postman-collection'),
|
||||
parse = require('./parse.js'),
|
||||
deref = require('./deref.js'),
|
||||
_ = require('lodash'),
|
||||
xmlFaker = require('./xmlSchemaFaker.js'),
|
||||
openApiErr = require('./error.js'),
|
||||
{ Node, Trie } = require('./trie.js'),
|
||||
SCHEMA_FORMATS = {
|
||||
DEFAULT: 'default', // used for non-request-body data and json
|
||||
XML: 'xml' // used for request-body XMLs
|
||||
},
|
||||
URLENCODED = 'application/x-www-form-urlencoded',
|
||||
APP_JSON = 'application/json',
|
||||
APP_JS = 'application/javascript',
|
||||
@@ -35,9 +40,11 @@ schemaFaker.option({
|
||||
* removes things that might make schemaFaker crash
|
||||
* @param {*} oldSchema the schema to fake
|
||||
* @param {*} components list of predefined components (with schemas)
|
||||
* @param {string} schemaFormat default or xml
|
||||
* @param {string} indentCharacter char for 1 unit of indentation
|
||||
* @returns {object} fakedObject
|
||||
*/
|
||||
function safeSchemaFaker(oldSchema, components) {
|
||||
function safeSchemaFaker(oldSchema, components, schemaFormat, indentCharacter) {
|
||||
var prop,
|
||||
schema = deref.resolveRefs(oldSchema, components);
|
||||
|
||||
@@ -54,6 +61,10 @@ function safeSchemaFaker(oldSchema, components) {
|
||||
}
|
||||
|
||||
try {
|
||||
if (schemaFormat === SCHEMA_FORMATS.XML) {
|
||||
return xmlFaker(null, schema, indentCharacter);
|
||||
}
|
||||
// for JSON, the indentCharacter will be applied in the JSON.stringify step later on
|
||||
return schemaFaker(schema);
|
||||
}
|
||||
catch (e) {
|
||||
@@ -101,12 +112,11 @@ module.exports = {
|
||||
* something that can be added to the collection
|
||||
* TODO: Figure out better description
|
||||
* @param {object} serverVariables - Object containing the server variables at the root/path-item level
|
||||
* @param {string} level - root / path-item level
|
||||
* @param {string} keyName
|
||||
* @param {string} serverUrl - URL from the server object
|
||||
*
|
||||
* @returns {object} modified collection after the addition of the server variables
|
||||
*/
|
||||
convertToPmCollectionVariables: function(serverVariables, level, serverUrl = '') {
|
||||
convertToPmCollectionVariables: function(serverVariables, keyName, serverUrl = '') {
|
||||
var variables = [];
|
||||
if (serverVariables) {
|
||||
_.forOwn(serverVariables, (value, key) => {
|
||||
@@ -117,9 +127,9 @@ module.exports = {
|
||||
}));
|
||||
});
|
||||
}
|
||||
else {
|
||||
if (keyName) {
|
||||
variables.push(new sdk.Variable({
|
||||
id: level,
|
||||
id: keyName,
|
||||
value: serverUrl,
|
||||
type: 'string'
|
||||
}));
|
||||
@@ -131,6 +141,7 @@ module.exports = {
|
||||
* Parses an OAS string/object as a YAML or JSON
|
||||
* @param {YAML/JSON} openApiSpec - The OAS 3.x specification specified in either YAML or JSON
|
||||
* @returns {Object} - Contains the parsed JSON-version of the OAS spec, or an error
|
||||
* @no-unit-test
|
||||
*/
|
||||
parseSpec: function (openApiSpec) {
|
||||
var openApiObj = openApiSpec,
|
||||
@@ -350,14 +361,16 @@ module.exports = {
|
||||
* @param {string} type - Level at the tree root/path level. Can be method/root/param.
|
||||
* method: request(operation)-level, root: spec-level, param: url-level
|
||||
* @param {Array<object>} providedPathVars - Array of path variables
|
||||
* @param {object} commonPathVariables - Object of path variables taken from the specification
|
||||
* @param {object|array} commonPathVars - Object of path variables taken from the specification
|
||||
* @returns {Array<object>} returns an array of sdk.Variable
|
||||
*/
|
||||
convertPathVariables: function(type, providedPathVars, commonPathVariables) {
|
||||
convertPathVariables: function(type, providedPathVars, commonPathVars) {
|
||||
var variables = providedPathVars;
|
||||
// converting the base uri path variables, if any
|
||||
// commonPathVars is an object for type = root/method
|
||||
// array otherwise
|
||||
if (type === 'root' || type === 'method') {
|
||||
_.forOwn(commonPathVariables, (value, key) => {
|
||||
_.forOwn(commonPathVars, (value, key) => {
|
||||
variables.push({
|
||||
key: key,
|
||||
value: type === 'root' ? '{{' + key + '}}' : value.default,
|
||||
@@ -367,11 +380,12 @@ module.exports = {
|
||||
});
|
||||
}
|
||||
else {
|
||||
_.forEach(commonPathVariables, (variable) => {
|
||||
_.forEach(commonPathVars, (variable) => {
|
||||
variables.push({
|
||||
key: variable.name,
|
||||
// we only fake the schema for param-level pathVars
|
||||
value: this.options.schemaFaker ? safeSchemaFaker(variable.schema || {}, this.components) : '',
|
||||
value: this.options.schemaFaker ?
|
||||
safeSchemaFaker(variable.schema || {}, this.components, SCHEMA_FORMATS.DEFAULT) : '',
|
||||
description: variable.description || ''
|
||||
});
|
||||
});
|
||||
@@ -405,7 +419,7 @@ module.exports = {
|
||||
* @returns {string} space-separated string
|
||||
*/
|
||||
insertSpacesInName: function (string) {
|
||||
if (!string) {
|
||||
if (!string || (typeof string !== 'string')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -421,6 +435,7 @@ module.exports = {
|
||||
* @param {*} openapi object with root-level data like pathVariables baseurl
|
||||
* @param {*} child object is of type itemGroup or request
|
||||
* @returns {*} Postman itemGroup or request
|
||||
* @no-unit-test
|
||||
*/
|
||||
convertChildToItemGroup: function(openapi, child) {
|
||||
var resource = child,
|
||||
@@ -491,6 +506,7 @@ module.exports = {
|
||||
* @returns {object} The authHelper to use while constructing the Postman Request. This is
|
||||
* not directly supported in the SDK - the caller needs to determine the header/body based on the return
|
||||
* value
|
||||
* @no-unit-test
|
||||
*/
|
||||
getAuthHelper: function(openapi, securitySet) {
|
||||
var securityDef,
|
||||
@@ -572,13 +588,13 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
|
||||
responseBody = this.convertToPmBodyData(contentObj[cTypeHeader]);
|
||||
|
||||
responseBody = this.convertToPmBodyData(contentObj[cTypeHeader], cTypeHeader, this.options.indentCharacter);
|
||||
if (cTypeHeader === APP_JSON) {
|
||||
responseBody = JSON.stringify(responseBody, null, this.options.indentCharacter);
|
||||
}
|
||||
return {
|
||||
contentTypeHeader: cTypeHeader,
|
||||
// what if it's not JSON?
|
||||
// TODO: The indentation must be an option
|
||||
responseBody: JSON.stringify(responseBody, null, 4)
|
||||
responseBody: responseBody
|
||||
};
|
||||
},
|
||||
|
||||
@@ -586,6 +602,7 @@ module.exports = {
|
||||
* Create parameters specific for a request
|
||||
* @param {*} localParams parameters array
|
||||
* @returns {Object} with three arrays of query, header and path as keys.
|
||||
* @no-unit-test
|
||||
*/
|
||||
getParametersForPathItem: function(localParams) {
|
||||
var tempParam,
|
||||
@@ -637,14 +654,16 @@ module.exports = {
|
||||
/**
|
||||
* converts one of the eamples or schema in Media Type object to postman data
|
||||
* @param {*} bodyObj is MediaTypeObject
|
||||
* @param {string} indentCharacter is needed for XML/JSON bodies only
|
||||
* @returns {*} postman body data
|
||||
*/
|
||||
// TODO: We also need to accept the content type
|
||||
// and generate the body accordingly
|
||||
// right now, even if the content-type was XML, we'll generate
|
||||
// a JSON example/schema
|
||||
convertToPmBodyData: function(bodyObj) {
|
||||
var bodyData = '';
|
||||
convertToPmBodyData: function(bodyObj, contentType, indentCharacter = '') {
|
||||
var bodyData = '',
|
||||
schemaType = SCHEMA_FORMATS.DEFAULT;
|
||||
|
||||
if (bodyObj.example) {
|
||||
bodyData = bodyObj.example;
|
||||
@@ -661,7 +680,16 @@ module.exports = {
|
||||
if (bodyObj.schema.hasOwnProperty('$ref')) {
|
||||
bodyObj.schema = this.getRefObject(bodyObj.schema.$ref);
|
||||
}
|
||||
bodyData = this.options.schemaFaker ? safeSchemaFaker(bodyObj.schema || {}, this.components) : '';
|
||||
if (this.options.schemaFaker) {
|
||||
if (contentType === APP_XML || contentType === TEXT_XML) {
|
||||
schemaType = SCHEMA_FORMATS.XML;
|
||||
}
|
||||
bodyData = safeSchemaFaker(bodyObj.schema || {}, this.components, schemaType, indentCharacter);
|
||||
}
|
||||
else {
|
||||
// do not fake if the option is false
|
||||
bodyData = '';
|
||||
}
|
||||
}
|
||||
return bodyData;
|
||||
},
|
||||
@@ -681,7 +709,8 @@ module.exports = {
|
||||
// check for existence of schema
|
||||
if (param.hasOwnProperty('schema')) {
|
||||
// fake data generated
|
||||
paramValue = this.options.schemaFaker ? safeSchemaFaker(param.schema, this.components) : '';
|
||||
paramValue = this.options.schemaFaker ?
|
||||
safeSchemaFaker(param.schema, this.components, SCHEMA_FORMATS.DEFAULT) : '';
|
||||
// paramType = param.schema.type;
|
||||
return this.convertParamsWithStyle(param, paramValue);
|
||||
}
|
||||
@@ -850,7 +879,8 @@ module.exports = {
|
||||
reqHeader;
|
||||
|
||||
if (header.hasOwnProperty('schema')) {
|
||||
fakeData = this.options.schemaFaker ? JSON.stringify(safeSchemaFaker(header.schema || {}, this.components)) : '';
|
||||
fakeData = this.options.schemaFaker ?
|
||||
JSON.stringify(safeSchemaFaker(header.schema || {}, this.components, SCHEMA_FORMATS.DEFAULT)) : '';
|
||||
}
|
||||
else {
|
||||
fakeData = '';
|
||||
@@ -893,7 +923,7 @@ module.exports = {
|
||||
if (contentObj[URLENCODED].hasOwnProperty('schema') && contentObj[URLENCODED].schema.hasOwnProperty('$ref')) {
|
||||
contentObj[URLENCODED].schema = this.getRefObject(contentObj[URLENCODED].schema.$ref);
|
||||
}
|
||||
bodyData = this.convertToPmBodyData(contentObj[URLENCODED]);
|
||||
bodyData = this.convertToPmBodyData(contentObj[URLENCODED], URLENCODED);
|
||||
encoding = contentObj[URLENCODED].encoding ? contentObj[URLENCODED].encoding : {};
|
||||
// create query parameters and add it to the request body object
|
||||
_.forOwn(bodyData, (value, key) => {
|
||||
@@ -936,7 +966,7 @@ module.exports = {
|
||||
}
|
||||
else if (contentObj.hasOwnProperty(FORM_DATA)) {
|
||||
rDataMode = 'formdata';
|
||||
bodyData = this.convertToPmBodyData(contentObj[FORM_DATA]);
|
||||
bodyData = this.convertToPmBodyData(contentObj[FORM_DATA], FORM_DATA);
|
||||
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) => {
|
||||
@@ -992,7 +1022,7 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
|
||||
bodyData = this.convertToPmBodyData(contentObj[bodyType]);
|
||||
bodyData = this.convertToPmBodyData(contentObj[bodyType], bodyType, this.options.indentCharacter);
|
||||
|
||||
updateOptions = {
|
||||
mode: rDataMode,
|
||||
@@ -1075,6 +1105,7 @@ module.exports = {
|
||||
/**
|
||||
* @param {*} $ref reference object
|
||||
* @returns {Object} reference object from the saved components
|
||||
* @no-unit-tests
|
||||
*/
|
||||
getRefObject: function($ref) {
|
||||
var refObj, savedSchema;
|
||||
@@ -1104,7 +1135,8 @@ module.exports = {
|
||||
* function to convert an openapi path item to postman item
|
||||
* @param {*} openapi openapi object with root properties
|
||||
* @param {*} operationItem path operationItem from tree structure
|
||||
* @returns {Object} postman request Item
|
||||
* @returns {Object} postman request Item
|
||||
* @no-unit-test
|
||||
*/
|
||||
convertRequestToItem: function(openapi, operationItem) {
|
||||
var reqName,
|
||||
|
||||
111
lib/xmlSchemaFaker.js
Normal file
111
lib/xmlSchemaFaker.js
Normal file
@@ -0,0 +1,111 @@
|
||||
/* eslint-disable */
|
||||
const _ = require('lodash');
|
||||
|
||||
function convertSchemaToXML(name, schema, attribute, indentChar, inden) {
|
||||
var tagPrefix = '',
|
||||
cInden = _.times(inden, _.constant(indentChar)).join('');
|
||||
name = _.get(schema, 'xml.name', name || 'element');
|
||||
if (_.get(schema, 'xml.prefix')) {
|
||||
tagPrefix = schema.xml.prefix + ':';
|
||||
}
|
||||
if (['integer','string'].includes(schema.type)) {
|
||||
if (schema.type === 'integer') {
|
||||
actualValue = '(integer)';
|
||||
}
|
||||
else if (schema.type === 'string') {
|
||||
actualValue = '(string)';
|
||||
}
|
||||
if (attribute) {
|
||||
return actualValue;
|
||||
}
|
||||
else {
|
||||
var retVal = '\n' + cInden + '<' + tagPrefix+name;
|
||||
if (_.get(schema, 'xml.namespace')) {
|
||||
retVal += ' xmlns:' + tagPrefix.slice(0,-1) + '="'+schema.xml.namespace+'"'
|
||||
}
|
||||
retVal += '>' + actualValue + '</' + tagPrefix+name + '>';
|
||||
}
|
||||
}
|
||||
else if (schema.type === 'object') {
|
||||
// go through all properties
|
||||
var retVal = '\n' + cInden + `<${tagPrefix+name}`, propVal, attributes = [], childNodes = '';
|
||||
if (_.get(schema, 'xml.namespace')) {
|
||||
retVal += ' xmlns:' + tagPrefix.slice(0,-1) + '="'+schema.xml.namespace+'"'
|
||||
}
|
||||
_.forOwn(schema.properties, (value, key) => {
|
||||
propVal = convertSchemaToXML(key, value, _.get(value, 'xml.attribute'), indentChar, inden + 1);
|
||||
if (_.get(value, 'xml.attribute')) {
|
||||
attributes.push(key + '="' + propVal + '"');
|
||||
}
|
||||
else {
|
||||
childNodes += propVal;
|
||||
}
|
||||
});
|
||||
if (attributes.length > 0) {
|
||||
retVal += ' ' + attributes.join(' ');
|
||||
}
|
||||
retVal += '>';
|
||||
retVal += childNodes;
|
||||
retVal += '\n</' + name + '>';
|
||||
}
|
||||
else if (schema.type === 'array') {
|
||||
// schema.items must be an object
|
||||
var isWrapped = _.get(schema, 'xml.wrapped'),
|
||||
extraIndent = isWrapped ? 1 : 0;
|
||||
var arrayElemName = _.get(schema, 'items.xml.name', name, 'arrayItem');
|
||||
var contents = convertSchemaToXML(arrayElemName, schema.items, false, indentChar, inden + extraIndent) +
|
||||
convertSchemaToXML(arrayElemName, schema.items, false, indentChar, inden + extraIndent);
|
||||
if (isWrapped) {
|
||||
return '\n' + cInden + '<' + name + '>' + contents + '\n' + cInden + '</' + name + '>';
|
||||
}
|
||||
else {
|
||||
return contents;
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
module.exports = function(name, schema, indentCharacter) {
|
||||
// substring(1) to trim the leading newline
|
||||
return convertSchemaToXML(name, schema, false, indentCharacter, 0).substring(1);
|
||||
};
|
||||
/*
|
||||
a = convertSchemaToXML('Person',{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "integer",
|
||||
"format": "int32",
|
||||
"xml": {
|
||||
"attribute": true
|
||||
}
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"xml": {
|
||||
"namespace": "http://example.com/schema/sample",
|
||||
"prefix": "sample"
|
||||
}
|
||||
},
|
||||
"animals": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"xml": {
|
||||
"name": "animal"
|
||||
}
|
||||
},
|
||||
"xml": {
|
||||
"name": "aliens",
|
||||
"wrapped": true
|
||||
}
|
||||
}
|
||||
},
|
||||
xml: {
|
||||
namespace: "www.kane.com",
|
||||
"prefix": "M"
|
||||
}
|
||||
}, false, 0);
|
||||
|
||||
console.log(a);
|
||||
*/
|
||||
File diff suppressed because it is too large
Load Diff
40
test/unit/deref.test.js
Normal file
40
test/unit/deref.test.js
Normal file
@@ -0,0 +1,40 @@
|
||||
var expect = require('chai').expect,
|
||||
deref = require('../../lib/deref.js');
|
||||
|
||||
describe('DEREF FUNCTION TESTS ', function() {
|
||||
it('resolveRefs Function should return schema with resolved references.', function(done) {
|
||||
var schema = {
|
||||
$ref: '#/components/schemas/schema1'
|
||||
},
|
||||
components = {
|
||||
schemas: {
|
||||
schema1: {
|
||||
anyOf: [{
|
||||
$ref: '#/components/schemas/schema2'
|
||||
},
|
||||
{
|
||||
$ref: '#/components/schemas/schema3'
|
||||
}
|
||||
]
|
||||
},
|
||||
schema2: {
|
||||
type: 'object',
|
||||
required: [
|
||||
'id'
|
||||
],
|
||||
properties: {
|
||||
id: {
|
||||
type: 'integer',
|
||||
format: 'int64'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
output = deref.resolveRefs(schema, components);
|
||||
expect(output).to.deep.include({ type: 'object',
|
||||
required: ['id'],
|
||||
properties: { id: { default: '<long>', type: 'string' } } });
|
||||
done();
|
||||
});
|
||||
});
|
||||
1373
test/unit/util.test.js
Normal file
1373
test/unit/util.test.js
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user