UNPKG

@postman/wsdl-to-postman

Version:

Convert a given WSDL specification (1.1) to Postman Collection

223 lines (200 loc) 6.56 kB
const { XSDValidationError } = require('./XSDValidationError'), xmllint = require('xmllint'), xpathTool = require('xpath'), xmldom = require('xmldom').DOMParser, xmlSerializer = require('xmldom').XMLSerializer, INVALID_TYPE = 'is not a valid value of the atomic type', NOT_EXPECTED = 'This element is not expected', NOT_ALLOWED = 'Character content is not allowed', ERROR_STRING_PLACE = 'Schemas validity error : '; /** * Class to generate and expose the same xmlNode format than libxmljs packages */ class XMLElement { constructor(xmlNode, textValueComponent = '') { this.textValueComponent = textValueComponent; this.node = xmlNode; this.nodeString = new xmlSerializer().serializeToString(this.node); } name() { return this.node.nodeName; } path() { const result = this.node ? this.getPath(this.node) : null; return result; } prevElement() { const prevElement = this.getPrevious(this.node); return prevElement ? new XMLElement(prevElement) : null; } parent() { const parentNode = this.node.parentNode; return new XMLElement(parentNode); } text() { return this.node.textContent; } toString() { const nodeString = this.nodeString; return nodeString; } childNodes() { const nodesArray = Array.prototype.slice.call(this.node.childNodes), XmlElements = nodesArray.map((node) => { return new XMLElement(node); }); return XmlElements; } getPrevious(node) { const prevElement = node.previousSibling, prevElementName = prevElement ? prevElement.nodeName : null; if (prevElementName === '#text') { return this.getPrevious(prevElement); } return prevElement; } getPath(node, accumulator = []) { accumulator = accumulator.length === 0 ? [node.nodeName] : accumulator; const parentNode = node.parentNode, parentName = parentNode.nodeName, indexComponent = this.getIndexComponent(accumulator, parentNode); accumulator[0] = `${accumulator[0]}${indexComponent}`; if (parentName !== '#document') { return this.getPath(parentNode, [parentName, ...accumulator]); } return `/${accumulator.join('/')}`; } getIndexComponent(accumulator, parentNode) { const currentElementXpath = `./${accumulator[0]}`, completeLocalXpath = `./${accumulator.slice(1).join('/')}${this.textValueComponent}`, sameNameSiblings = xpathTool.select(currentElementXpath, parentNode); let indexComponent = ''; if (sameNameSiblings.length > 1) { indexComponent = sameNameSiblings.map((element, index) => { let result; if (this.textValueComponent && accumulator.length === 1) { result = this.textValueComponent.includes(element.textContent) ? [element.textContent] : []; } else { result = xpathTool.select(completeLocalXpath, element); } // let result = accumulator.length === 1 ? // : // xpathTool.select(completeLocalXpath, element); return result.length > 0 ? `[${index + 1}]` : null; }).find((indexComponent) => { return indexComponent; }); } return indexComponent; } } /** * Class to validate XML against an XSD schema. * Facade to xmllint package */ class XMLLintFacade { /** * Validates the xml against schema * @param {string} xml the xml to validate * @param {string} schema the xsd schema to use in validation * @returns {object} the parsed object */ validate(xml, schema) { var res; try { res = xmllint.validateXML({ xml: xml, schema: schema }); return res; } catch (e) { /** * Schema is invalid according to xmllint. * Hence until mismatches are confirmed, we won't be throwing validation errors. * (For non supported keywords like "anyAtomicType", schema is declared invalid by library) */ return []; } } /** * Takes in the xmllint errors and convert them * into a known XSDValidationError object * @param {object} errors the result for xmllint validation * @param {string} schema the xsd schema to use in validation * @returns {Array} array of XSDValidationError */ mapErrors(errors) { let adaptedErrors = []; if (!errors || !errors.errors) { return []; } errors.errors.forEach((error) => { let code, message, str1, valError; if (error.includes(INVALID_TYPE)) { code = 1824; message = error.split(ERROR_STRING_PLACE)[1]; str1 = message.split('Element \'message\': ')[1].split('\'')[1]; valError = new XSDValidationError(code, message + '\n', str1); adaptedErrors.push(valError); } else if (error.includes(NOT_EXPECTED)) { code = 1871; message = error.split(ERROR_STRING_PLACE)[1]; valError = new XSDValidationError(code, message + '\n', undefined); adaptedErrors.push(valError); } else if (error.includes(NOT_ALLOWED)) { code = 1841; message = error.split(ERROR_STRING_PLACE)[1]; valError = new XSDValidationError(code, message + '\n', undefined); adaptedErrors.push(valError); } }); return adaptedErrors; } parseXmlString(xmlString) { return new XMLElement(new xmldom().parseFromString(xmlString).childNodes[0]); } findByXpathInXmlString(xmlString, xpath) { const document = new xmldom().parseFromString(xmlString), textValuePattern = /\[text\(\)=".*"\]/, textValueInXpath = xpath.match(textValuePattern), textValueComponent = textValueInXpath ? textValueInXpath[0] : '', elements = xpathTool.select(xpath, document).map((xmlNode) => { return new XMLElement(xmlNode, textValueComponent); }); return elements; } getElementText(element) { const text = element.length > 0 ? element[0].text() : ''; return text; } getChildrenAsString(element) { const children = Object.values(element[0].childNodes()).filter((node) => { let nodeName = node.name(); return nodeName && nodeName !== '#text'; }), childrenAsString = children.map((node) => { return node.node.toString(); }).join('\n'); return childrenAsString; } getChildrenElements(node) { const children = node.childNodes().filter((child) => { return child.name() !== '#text'; }); return children; } } module.exports = { XMLLintFacade };