openapi-to-postmanv2
Version:
Convert a given OpenAPI specification to Postman Collection v2.0
473 lines (443 loc) • 17.1 kB
JavaScript
/* eslint-disable */
/**
* This code is stripped down version of https://github.com/ibm-maximo-dev/xml-js
* Only essential part to convert JSON to XML is used here.
*
* Ideally there should be no updates needed to this code as it already handles all types of data for JSON object.
* But in case this need updates, take a look at below file specifically from which this logic was inspired from.
*
* https://github.com/ibm-maximo-dev/xml-js/blob/master/lib/js2xml.js
*/
var currentElement, currentElementName;
const DEFAULT_OPTIONS = {
spaces: ' ',
compact: true
}
function isArray (value) {
if (Array.isArray) {
return Array.isArray(value);
}
// fallback for older browsers like IE 8
return Object.prototype.toString.call( value ) === '[object Array]';
}
// Options helper
const helper = {
copyOptions: function (options) {
var key, copy = {};
for (key in options) {
if (options.hasOwnProperty(key)) {
copy[key] = options[key];
}
}
return copy;
},
ensureFlagExists: function (item, options) {
if (!(item in options) || typeof options[item] !== 'boolean') {
options[item] = false;
}
},
ensureSpacesExists: function (options) {
if (!('spaces' in options) || (typeof options.spaces !== 'number' && typeof options.spaces !== 'string')) {
options.spaces = 0;
}
},
ensureAlwaysArrayExists: function (options) {
if (!('alwaysArray' in options) || (typeof options.alwaysArray !== 'boolean' && !isArray(options.alwaysArray))) {
options.alwaysArray = false;
}
},
ensureKeyExists: function (key, options) {
if (!(key + 'Key' in options) || typeof options[key + 'Key'] !== 'string') {
options[key + 'Key'] = options.compact ? '_' + key : key;
}
},
checkFnExists: function (key, options) {
return key + 'Fn' in options;
}
};
function validateOptions(userOptions) {
var options = helper.copyOptions(userOptions);
helper.ensureFlagExists('ignoreDeclaration', options);
helper.ensureFlagExists('ignoreInstruction', options);
helper.ensureFlagExists('ignoreAttributes', options);
helper.ensureFlagExists('ignoreText', options);
helper.ensureFlagExists('ignoreComment', options);
helper.ensureFlagExists('ignoreCdata', options);
helper.ensureFlagExists('ignoreDoctype', options);
helper.ensureFlagExists('compact', options);
helper.ensureFlagExists('indentText', options);
helper.ensureFlagExists('indentCdata', options);
helper.ensureFlagExists('indentAttributes', options);
helper.ensureFlagExists('indentInstruction', options);
helper.ensureFlagExists('fullTagEmptyElement', options);
helper.ensureFlagExists('noQuotesForNativeAttributes', options);
helper.ensureSpacesExists(options);
if (typeof options.spaces === 'number') {
options.spaces = Array(options.spaces + 1).join(' ');
}
helper.ensureKeyExists('declaration', options);
helper.ensureKeyExists('instruction', options);
helper.ensureKeyExists('attributes', options);
helper.ensureKeyExists('text', options);
helper.ensureKeyExists('comment', options);
helper.ensureKeyExists('cdata', options);
helper.ensureKeyExists('doctype', options);
helper.ensureKeyExists('type', options);
helper.ensureKeyExists('name', options);
helper.ensureKeyExists('elements', options);
helper.checkFnExists('doctype', options);
helper.checkFnExists('instruction', options);
helper.checkFnExists('cdata', options);
helper.checkFnExists('comment', options);
helper.checkFnExists('text', options);
helper.checkFnExists('instructionName', options);
helper.checkFnExists('elementName', options);
helper.checkFnExists('attributeName', options);
helper.checkFnExists('attributeValue', options);
helper.checkFnExists('attributes', options);
helper.checkFnExists('fullTagEmptyElement', options);
return options;
}
function writeIndentation(options, depth, firstLine) {
return (!firstLine && options.spaces ? '\n' : '') + Array(depth + 1).join(options.spaces);
}
function writeAttributes(attributes, options, depth) {
if (options.ignoreAttributes) {
return '';
}
if ('attributesFn' in options) {
attributes = options.attributesFn(attributes, currentElementName, currentElement);
}
var key,
attr,
attrName,
attrPlusValue,
quote,
result = [];
// NOTE: we don't the length of the element name, so this is the best we can do
var attrLineWidth = (depth + 1) * options.spaces;
var wrapped = false;
for (key in attributes) {
if (
attributes.hasOwnProperty(key) &&
attributes[key] !== null &&
attributes[key] !== undefined
) {
quote =
options.noQuotesForNativeAttributes &&
typeof attributes[key] !== "string"
? ""
: '"';
attr = "" + attributes[key]; // ensure number and boolean are converted to String
attr = attr.replace(/"/g, """);
attr = attr.replace(/</g, "<");
// edge case, graphite uses lots of && because of conditionals
attr = attr.replace(/&&/g, "&&");
attrName =
"attributeNameFn" in options
? options.attributeNameFn(
key,
attr,
currentElementName,
currentElement
)
: key;
attrPlusValue =
attrName +
"=" +
quote +
("attributeValueFn" in options
? options.attributeValueFn(
attr,
key,
currentElementName,
currentElement
)
: attr) +
quote;
attrLineWidth += attrPlusValue.length + 1;
if (options.indentAttributesWidth) {
if (
options.spaces &&
options.indentAttributes &&
options.indentAttributesWidth &&
attrLineWidth > options.indentAttributesWidth
) {
result.push(writeIndentation(options, depth + 1, false));
attrLineWidth =
(depth + 1) * options.spaces + attrPlusValue.length + 1;
wrapped = true;
} else {
result.push(" ");
}
} else {
result.push(
options.spaces && options.indentAttributes
? writeIndentation(options, depth + 1, false)
: " "
);
}
result.push(attrPlusValue);
}
}
if (
attributes &&
Object.keys(attributes).length &&
options.spaces &&
options.indentAttributes
) {
if (
options.indentAttributesWidth &&
!wrapped
) {
result.push(" ");
} else {
result.push(writeIndentation(options, depth, false));
}
}
return result.join('');
}
function writeDeclaration(declaration, options, depth) {
currentElement = declaration;
currentElementName = 'xml';
return options.ignoreDeclaration ? '' : '<?' + 'xml' + writeAttributes(declaration[options.attributesKey], options, depth) + '?>';
}
function writeInstruction(instruction, options, depth) {
if (options.ignoreInstruction) {
return '';
}
var key;
for (key in instruction) {
if (instruction.hasOwnProperty(key)) {
break;
}
}
var instructionName = 'instructionNameFn' in options ? options.instructionNameFn(key, instruction[key], currentElementName, currentElement) : key;
if (typeof instruction[key] === 'object') {
currentElement = instruction;
currentElementName = instructionName;
return '<?' + instructionName + writeAttributes(instruction[key][options.attributesKey], options, depth) + '?>';
} else {
var instructionValue = instruction[key] ? instruction[key] : '';
if ('instructionFn' in options) instructionValue = options.instructionFn(instructionValue, key, currentElementName, currentElement);
return '<?' + instructionName + (instructionValue ? ' ' + instructionValue : '') + '?>';
}
}
function writeComment(comment, options) {
return options.ignoreComment ? '' : '<!--' + ('commentFn' in options ? options.commentFn(comment, currentElementName, currentElement) : comment) + '-->';
}
function writeCdata(cdata, options) {
return options.ignoreCdata ? '' : '<![CDATA[' + ('cdataFn' in options ? options.cdataFn(cdata, currentElementName, currentElement) : cdata.replace(']]>', ']]]]><![CDATA[>')) + ']]>';
}
function writeDoctype(doctype, options) {
return options.ignoreDoctype ? '' : '<!DOCTYPE ' + ('doctypeFn' in options ? options.doctypeFn(doctype, currentElementName, currentElement) : doctype) + '>';
}
function writeText(text, options) {
if (options.ignoreText) return '';
text = '' + text; // ensure Number and Boolean are converted to String
text = text.replace(/&/g, '&'); // desanitize to avoid double sanitization
text = text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
return 'textFn' in options ? options.textFn(text, currentElementName, currentElement) : text;
}
function hasContent(element, options) {
var i;
if (element.elements && element.elements.length) {
for (i = 0; i < element.elements.length; ++i) {
switch (element.elements[i][options.typeKey]) {
case 'text':
if (options.indentText) {
return true;
}
break; // skip to next key
case 'cdata':
if (options.indentCdata) {
return true;
}
break; // skip to next key
case 'instruction':
if (options.indentInstruction) {
return true;
}
break; // skip to next key
case 'doctype':
case 'comment':
case 'element':
return true;
default:
return true;
}
}
}
return false;
}
function writeElement(element, options, depth) {
currentElement = element;
currentElementName = element.name;
var xml = [], elementName = 'elementNameFn' in options ? options.elementNameFn(element.name, element) : element.name;
xml.push('<' + elementName);
if (element[options.attributesKey]) {
xml.push(writeAttributes(element[options.attributesKey], options, depth));
}
var withClosingTag = element[options.elementsKey] && element[options.elementsKey].length || element[options.attributesKey] && element[options.attributesKey]['xml:space'] === 'preserve';
if (!withClosingTag) {
if ('fullTagEmptyElementFn' in options) {
withClosingTag = options.fullTagEmptyElementFn(element.name, element);
} else {
withClosingTag = options.fullTagEmptyElement;
}
}
if (withClosingTag) {
xml.push('>');
if (element[options.elementsKey] && element[options.elementsKey].length) {
xml.push(writeElements(element[options.elementsKey], options, depth + 1));
currentElement = element;
currentElementName = element.name;
}
xml.push(options.spaces && hasContent(element, options) ? '\n' + Array(depth + 1).join(options.spaces) : '');
xml.push('</' + elementName + '>');
} else {
xml.push('/>');
}
return xml.join('');
}
function writeElements(elements, options, depth, firstLine) {
return elements.reduce(function (xml, element) {
var indent = writeIndentation(options, depth, firstLine && !xml);
switch (element.type) {
case 'element': return xml + indent + writeElement(element, options, depth);
case 'comment': return xml + indent + writeComment(element[options.commentKey], options);
case 'doctype': return xml + indent + writeDoctype(element[options.doctypeKey], options);
case 'cdata': return xml + (options.indentCdata ? indent : '') + writeCdata(element[options.cdataKey], options);
case 'text': return xml + (options.indentText ? indent : '') + writeText(element[options.textKey], options);
case 'instruction':
var instruction = {};
instruction[element[options.nameKey]] = element[options.attributesKey] ? element : element[options.instructionKey];
return xml + (options.indentInstruction ? indent : '') + writeInstruction(instruction, options, depth);
}
}, '');
}
function hasContentCompact(element, options, anyContent) {
var key;
for (key in element) {
if (element.hasOwnProperty(key)) {
switch (key) {
case options.parentKey:
case options.attributesKey:
break; // skip to next key
case options.textKey:
if (options.indentText || anyContent) {
return true;
}
break; // skip to next key
case options.cdataKey:
if (options.indentCdata || anyContent) {
return true;
}
break; // skip to next key
case options.instructionKey:
if (options.indentInstruction || anyContent) {
return true;
}
break; // skip to next key
case options.doctypeKey:
case options.commentKey:
return true;
default:
return true;
}
}
}
return false;
}
function writeElementCompact(element, name, options, depth, indent) {
currentElement = element;
currentElementName = name;
var elementName = 'elementNameFn' in options ? options.elementNameFn(name, element) : name;
if (typeof element === 'undefined' || element === null || element === '') {
return 'fullTagEmptyElementFn' in options && options.fullTagEmptyElementFn(name, element) || options.fullTagEmptyElement ? '<' + elementName + '></' + elementName + '>' : '<' + elementName + '/>';
}
var xml = [];
if (name) {
xml.push('<' + elementName);
if (typeof element !== 'object') {
xml.push('>' + writeText(element, options) + '</' + elementName + '>');
return xml.join('');
}
if (element[options.attributesKey]) {
xml.push(writeAttributes(element[options.attributesKey], options, depth));
}
var withClosingTag = hasContentCompact(element, options, true) || element[options.attributesKey] && element[options.attributesKey]['xml:space'] === 'preserve';
if (!withClosingTag) {
if ('fullTagEmptyElementFn' in options) {
withClosingTag = options.fullTagEmptyElementFn(name, element);
} else {
withClosingTag = options.fullTagEmptyElement;
}
}
if (withClosingTag) {
xml.push('>');
} else {
xml.push('/>');
return xml.join('');
}
}
xml.push(writeElementsCompact(element, options, depth + 1, false));
currentElement = element;
currentElementName = name;
if (name) {
xml.push((indent ? writeIndentation(options, depth, false) : '') + '</' + elementName + '>');
}
return xml.join('');
}
function writeElementsCompact(element, options, depth, firstLine) {
var i, key, nodes, xml = [];
for (key in element) {
if (element.hasOwnProperty(key)) {
nodes = isArray(element[key]) ? element[key] : [element[key]];
for (i = 0; i < nodes.length; ++i) {
switch (key) {
case options.declarationKey: xml.push(writeDeclaration(nodes[i], options, depth)); break;
case options.instructionKey: xml.push((options.indentInstruction ? writeIndentation(options, depth, firstLine) : '') + writeInstruction(nodes[i], options, depth)); break;
case options.attributesKey: case options.parentKey: break; // skip
case options.textKey: xml.push((options.indentText ? writeIndentation(options, depth, firstLine) : '') + writeText(nodes[i], options)); break;
case options.cdataKey: xml.push((options.indentCdata ? writeIndentation(options, depth, firstLine) : '') + writeCdata(nodes[i], options)); break;
case options.doctypeKey: xml.push(writeIndentation(options, depth, firstLine) + writeDoctype(nodes[i], options)); break;
case options.commentKey: xml.push(writeIndentation(options, depth, firstLine) + writeComment(nodes[i], options)); break;
default: xml.push(writeIndentation(options, depth, firstLine) + writeElementCompact(nodes[i], key, options, depth, hasContentCompact(nodes[i], options)));
}
firstLine = firstLine && !xml.length;
}
}
}
return xml.join('');
}
module.exports = function (js, indentChar) {
// If provided data is not JSON, return it as is. XML string could be already present as example.
if (typeof js !== 'object') {
return js;
}
try {
let options = DEFAULT_OPTIONS;
// Override indent character if defined.
indentChar && (options.spaces = indentChar);
options = validateOptions(options);
var xml = [];
currentElement = js;
currentElementName = '_root_';
if (options.compact) {
xml.push(writeElementsCompact(js, options, 0, true));
} else {
if (js[options.declarationKey]) {
xml.push(writeDeclaration(js[options.declarationKey], options, 0));
}
if (js[options.elementsKey] && js[options.elementsKey].length) {
xml.push(writeElements(js[options.elementsKey], options, 0, !xml.length));
}
}
return xml.join('');
} catch (err) {
console.error(err);
// Handle failures gracefully via returning the message inside body rather than failing entire collection
return '<js2xmlError>Failed to generate XML data from provided Example.</js2xmlError>'
}
};