UNPKG

@postman/wsdl-to-postman

Version:

Convert a given WSDL specification (1.1) to Postman Collection

728 lines (680 loc) 24.6 kB
const _ = require('lodash'), { SecurityAssertionsHelper } = require('./security/SecurityAssertionsHelper'), { getArrayFrom, getStringArrayFrom } = require('./utils/objectUtils'), { removeLineBreak } = require('./utils/textUtils'), SchemaBuilderXSD = require('../lib/utils/SchemaBuilderXSD').SchemaBuilderXSD, PARSER_ATTRIBUTE_NAME_PLACE_HOLDER = '@_', WsdlError = require('./WsdlError'), UserError = require('./UserError'), { NameSpace } = require('./WSDLObject'), XML_NAMESPACE_DECLARATION = 'xmlns', TARGET_NAMESPACE_SPEC = 'targetNamespace', SERVICE_TAG = 'service', DOCUMENTATION_TAG = 'documentation', BINDING_TAG = 'binding', SCHEMA_TAG = 'schema', SCHEMA_NS_URL = 'http://www.w3.org/2001/XMLSchema', SCHEMA_NS_URL_XSD = 'http://www.w3.org/2001/XMLSchema.xsd', THIS_NS_PREFIX = 'tns:', ATTRIBUTE_TYPES = 'types', OPERATION_TAG = 'operation', IMPORT_TAG = 'import', INCLUDE_TAG = 'include', ATTRIBUTE_NAMESPACE = 'namespace', ATTRIBUTE_SCHEMALOCATION = 'schemaLocation', ATTRIBUTE_LOCATION = 'location', { DOC_HAS_NO_BINDINGS_MESSAGE, DOC_HAS_NO_BINDINGS_OPERATIONS_MESSAGE } = require('./constants/messageConstants'), { getXMLNodeByName, getXMLAttributeByName, XML_NAMESPACE_SEPARATOR, getQNamePrefixFilter, getQNameLocal, getQNamePrefix } = require('./utils/XMLParsedUtils'); /** * Gets a node (tag) by prefix and name * @param {object} parentNode the xml node to search in for tag * @param {string} prefix prefix e.g. principal for wsdl root and schema prefix for schemas * @param {string} nodeName the tag name we are looking for * @returns {object} the parsed object */ function getNodeByName(parentNode, prefix, nodeName) { return getXMLNodeByName(parentNode, prefix, nodeName); } /** * Gets a node (tag) by prefix and name * @param {parentNode} parentNode the xml node to search in for tag * @param {nodeName} nodeName the tag name we are looking for * @returns {object} the parsed object */ function getNodeByQNameLocal(parentNode, nodeName) { let key = Object.keys(parentNode).find((key) => { if (key.includes(':')) { let localName = getQNameLocal(key); return nodeName === localName; } return key === nodeName; }); return parentNode[key]; } /** * Gets a node (tag) by prefix and name but returns array if it is an array * @param {parentNode} parentNode the xml node to search in for tag * @param {nodeName} nodeName the tag name we are looking for * @returns {object} the parsed object */ function getNodeByQNameLocalArray(parentNode, nodeName) { let result = [], key = Object.keys(parentNode).filter((key) => { if (key.includes(':')) { let localName = getQNameLocal(key); return nodeName === localName; } return key === nodeName; }); key.forEach((item) => { result.push(parentNode[item]); }); return result; } /** * Gets an ATTRIBUTE (attr) by name * @param {parentNode} parentNode the xml node to search in for tag * @param {attName} attName the attribute name we are looking for * @returns {object} the parsed object */ function getAttributeByName(parentNode, attName) { return getXMLAttributeByName(parentNode, PARSER_ATTRIBUTE_NAME_PLACE_HOLDER, attName); } /** * Get the bindngs operations of the document * always returns an array * @param {boolean} isDefault if is the default namespace * @param {string} key the namespace qname * @param {string} prefixFilter qname plus : * @param {string} url namespace url * @param {string} tnsDefinitionURL targetnamespace url * @returns {NameSpace} the generated NameSpace */ function buildNamespace(isDefault, key, prefixFilter, url, tnsDefinitionURL) { let namespace = new NameSpace(); namespace.isDefault = isDefault; namespace.key = key; namespace.prefixFilter = prefixFilter; namespace.url = url; namespace.tnsDefinitionURL = tnsDefinitionURL; return namespace; } /** * Finds the namespace for all the wsdl document * creates a namespace object with the found information * @param {object} parsedXml the binding operation object * @param {string} wsdlRoot the root tag for the document * @returns {NameSpace} the new NameSpace object */ function getPrincipalPrefix(parsedXml, wsdlRoot) { if (!parsedXml || typeof parsedXml !== 'object') { throw new UserError('Provided WSDL definition is invalid XML.'); } let namespace = Object.keys(parsedXml).find((key) => { if (key.includes(wsdlRoot)) { return true; } }); if (namespace === undefined) { throw new UserError('Definition doesn\'t contain any principal prefix.'); } return getQNamePrefixFilter(namespace); } /** * Finds the namespace lookin by url * creates a namespace object with the found information * @param {object} parsedXml the binding operation object * @param {string} url the url to look for * @param {string} wsdlRoot the root tag for the document * @returns {NameSpace} the new NameSpace object */ function getNamespaceByURL(parsedXml, url, wsdlRoot) { if (!url) { throw new UserError('Definition contains element for which namespace URL is not defined.'); } if (!parsedXml || typeof parsedXml !== 'object') { throw new UserError('Provided WSDL definition is invalid XML.'); } try { const principalPrefix = getPrincipalPrefix(parsedXml, wsdlRoot), definitions = getNodeByName(parsedXml, principalPrefix, wsdlRoot); let namespaceKey = Object.keys(definitions) .find((key) => { return definitions[key] === url; }); if (namespaceKey) { let namespace = namespaceKey.replace(PARSER_ATTRIBUTE_NAME_PLACE_HOLDER, ''), res = getQNameLocal(namespace); return buildNamespace(res === XML_NAMESPACE_DECLARATION, res, res + XML_NAMESPACE_SEPARATOR, url); } return null; } catch (error) { if (error instanceof UserError) { throw error; } throw new WsdlError('Cannot get namespace from object'); } } /** * Finds the namespace lookgin by its key * creates a namespace object with the found information * @param {object} parsedXml the binding operation object * @param {string} key the key to look for * @param {string} wsdlRoot the root tag for the document * @returns {NameSpace} the new NameSpace object */ function getNamespaceByKey(parsedXml, key, wsdlRoot) { if (!key) { throw new UserError('Definition contains element for which namespace key is not defined.'); } if (!parsedXml || typeof parsedXml !== 'object') { throw new UserError('Provided WSDL definition is invalid XML.'); } try { const principalPrefix = getPrincipalPrefix(parsedXml, wsdlRoot), definitions = getNodeByName(parsedXml, principalPrefix, wsdlRoot); let res = getAttributeByName(definitions, key), foundKey = getQNameLocal(key); return buildNamespace(key === XML_NAMESPACE_DECLARATION, foundKey, key + XML_NAMESPACE_SEPARATOR, res); } catch (error) { if (error instanceof UserError) { throw error; } throw new WsdlError('Cannot get namespace from object'); } } /** * Finds the namespace lookin by url * creates a namespace object with the found information * @param {object} parsedXml the binding operation object * @param {string} defaultTnsKey default prefix key for tns namespace * @param {object} targetNamespace target namespace object * @param {array} allNameSpaces all namespaces object * @param {string} wsdlRoot the root tag for the document * @returns {NameSpace} the new NameSpace object */ function getTnsNamespace(parsedXml, defaultTnsKey, targetNamespace, allNameSpaces, wsdlRoot) { let tnsNamespace = getNamespaceByKey(parsedXml, defaultTnsKey, wsdlRoot); if (!_.get(tnsNamespace, 'url')) { tnsNamespace = allNameSpaces.find((ns) => { return ns.url === _.get(targetNamespace, 'url') && ns.key !== _.get(targetNamespace, 'key'); }); } return tnsNamespace; } /** * Finds all the namespaces of the document * creates a namespace array object with the found information * @param {object} parsedXml the binding operation object * @param {string} wsdlRoot the root tag for the document * @returns {Array} the new [NameSpace] object */ function getAllNamespaces(parsedXml, wsdlRoot) { if (!parsedXml || typeof parsedXml !== 'object') { throw new UserError('Provided WSDL definition is invalid XML.'); } try { const principalPrefix = getPrincipalPrefix(parsedXml, wsdlRoot), definitions = getNodeByName(parsedXml, principalPrefix, wsdlRoot); let namespace = Object.keys(definitions) .filter( (key) => { return key.startsWith(PARSER_ATTRIBUTE_NAME_PLACE_HOLDER + XML_NAMESPACE_DECLARATION) || key === PARSER_ATTRIBUTE_NAME_PLACE_HOLDER + TARGET_NAMESPACE_SPEC; } ) .map((key) => { return getNamespaceByKey(parsedXml, key.replace(PARSER_ATTRIBUTE_NAME_PLACE_HOLDER, ''), wsdlRoot); }); return namespace; } catch (error) { if (error instanceof UserError) { throw error; } throw new WsdlError('Cannot get namespaces from object'); } } /** * Finds all the services of the document * creates a namespace array object with the found information * @param {object} parsedXml the binding operation object * @param {string} wsdlRoot the root tag for the document * @returns {Array} the [services] object */ function getServices(parsedXml, wsdlRoot) { if (!parsedXml || typeof parsedXml !== 'object') { throw new UserError('Provided WSDL definition is invalid XML.'); } try { const principalPrefix = getPrincipalPrefix(parsedXml, wsdlRoot), definitions = getNodeByName(parsedXml, principalPrefix, wsdlRoot); return getArrayFrom(getNodeByName(definitions, principalPrefix, SERVICE_TAG)); } catch (error) { if (error instanceof UserError) { throw error; } throw new WsdlError('Cannot get services from object'); } } /** * Get the bindngs of the document * always returns an array * @param {object} parsedXml the binding operation object * @param {string} wsdlRoot the root tag for the document * @param {WSDLObject} wsdlObject the object we are building * @returns {Array} the information of the bindings */ function getBindings(parsedXml, wsdlRoot, wsdlObject) { if (!parsedXml || typeof parsedXml !== 'object') { throw new UserError('Provided WSDL definition is invalid XML.'); } try { const principalPrefix = getPrincipalPrefix(parsedXml, wsdlRoot), definitions = getNodeByName(parsedXml, principalPrefix, wsdlRoot), bindings = getArrayFrom(getNodeByName(definitions, principalPrefix, BINDING_TAG)); if (!bindings) { wsdlObject.log.logError(DOC_HAS_NO_BINDINGS_MESSAGE); return []; } return bindings; } catch (error) { if (error instanceof UserError) { throw error; } throw new WsdlError('Cannot get bindings from object'); } } /** * Finds all the elements from the types * @param {object} parsedXml the binding operation object * @param {string} principalPrefix the wsdl prefix * @param {string} wsdlRoot the wsdl root * @param {Namespace} wsdlObject the wsdl memory parsed object * @returns {Array} the [elements] object */ function getElementsFromWSDL(parsedXml, principalPrefix, wsdlRoot, wsdlObject) { const builder = new SchemaBuilderXSD(); return builder.getElements( parsedXml, principalPrefix, wsdlRoot, wsdlObject, PARSER_ATTRIBUTE_NAME_PLACE_HOLDER ); } /** * Finds the schema namespace from the definitio of types * creates a namespace array object with the found information * @param {object} parsedXml the binding operation object * @param {string} wsdlRoot the root tag for the document * @param {string} principalPrefix the principal prefix * @param {string} globalSchemaNamespace documents schema namespace globally declared * @returns {Array} the [elements] object */ function getSchemaNamespacesFromTypesDefinitions(parsedXml, wsdlRoot, principalPrefix, globalSchemaNamespace) { let localSchemaNamespaces = []; const descriptions = getNodeByName(parsedXml, principalPrefix, wsdlRoot), types = getArrayFrom(getNodeByName(descriptions, principalPrefix, ATTRIBUTE_TYPES)); if (!types) { return localSchemaNamespaces; } types.forEach((type) => { let schemaPrefixes = getStringArrayFrom(Object.keys(type).filter((key) => { if (key.includes(SCHEMA_TAG)) { return true; } }).map((schemaTag) => { return getQNamePrefix(schemaTag); })); schemaPrefixes.forEach((schemaTag) => { let schemaNode = getNodeByName(type, schemaTag, schemaTag === '' ? SCHEMA_TAG : XML_NAMESPACE_SEPARATOR + SCHEMA_TAG); if (schemaNode) { let urlAtribute, targetNamespace, prefixFilter; urlAtribute = schemaTag === '' ? schemaTag : XML_NAMESPACE_SEPARATOR + schemaTag; nameSpaceURL = getAttributeByName(schemaNode, XML_NAMESPACE_DECLARATION + urlAtribute); targetNamespace = getAttributeByName(schemaNode, TARGET_NAMESPACE_SPEC); prefixFilter = schemaTag === '' ? '' : schemaTag + XML_NAMESPACE_SEPARATOR; if (globalSchemaNamespace && globalSchemaNamespace.prefixFilter !== prefixFilter || !globalSchemaNamespace) { localSchemaNamespaces.push(buildNamespace(false, schemaTag, prefixFilter, nameSpaceURL, targetNamespace)); } } }); }); return localSchemaNamespaces; } /** * Finds the schema namespace, could be on root or * defined in the types schema tag * creates a namespace array object with the found information * @param {object} parsedXml the binding operation object * @param {string} wsdlRoot the root tag for the document * @param {object} tnsNamespace this namespace from wsdl * @returns {Array} the [elements] object */ function getSchemaNamespace(parsedXml, wsdlRoot, tnsNamespace) { const principalPrefix = getPrincipalPrefix(parsedXml, wsdlRoot); let globalSchemaNamespace, localSchemaNamespaces; globalSchemaNamespace = getNamespaceByURL(parsedXml, SCHEMA_NS_URL, wsdlRoot); if (globalSchemaNamespace) { globalSchemaNamespace.tnsDefinitionURL = _.get(tnsNamespace, 'url'); } localSchemaNamespaces = getSchemaNamespacesFromTypesDefinitions(parsedXml, wsdlRoot, principalPrefix, globalSchemaNamespace); if (!globalSchemaNamespace) { globalSchemaNamespace = localSchemaNamespaces[0]; } return { globalSchemaNamespace, localSchemaNamespaces }; } /** * Get the bindngs operations of the document * always returns an array * @param {object} binding the binding operation object * @param {object} principalPrefix the principal prefix * @param {object} wsdlObject wsdl object we are building * @returns {Array} the information of the operation bindings */ function getBindingOperation(binding, principalPrefix, wsdlObject) { if (!binding || typeof binding !== 'object') { throw new UserError('Definition contains invalid binding.'); } try { const operations = getArrayFrom(getNodeByName(binding, principalPrefix, OPERATION_TAG)); if (!operations) { wsdlObject.log.logError(DOC_HAS_NO_BINDINGS_OPERATIONS_MESSAGE); return []; } return operations; } catch (error) { if (error instanceof UserError) { throw error; } throw new WsdlError('Cannot get binding operations from object'); } } /** * Get security content from parsedXml * @param {string} parsedXml Parsed xml document content * @param {*} securityNS Namespace for security data in document * @param {*} wsdlRoot Wsdl document root tag * @returns {Array} Security policies in document */ function getSecurity(parsedXml, securityNS, wsdlRoot) { if (!securityNS) { return undefined; } if (!parsedXml || typeof parsedXml !== 'object') { throw new UserError('Provided WSDL definition is invalid XML.'); } try { const principalPrefix = getPrincipalPrefix(parsedXml, wsdlRoot), definitions = getNodeByName(parsedXml, principalPrefix, wsdlRoot); return getArrayFrom(getNodeByName(definitions, securityNS.prefixFilter, 'Policy')); } catch (error) { if (error instanceof UserError) { throw error; } throw new WsdlError('Cannot get security from object'); } } /** * Returns a wsdlObject with security data added * @param {object} wsdlObject wsdlObject * @param {*} parsedXml Parsed xml document content * @param {*} wsdlRoot Wsdl root tag * @returns {object} New wsdlObject with security data */ function assignSecurity(wsdlObject, parsedXml, wsdlRoot) { let newWsdlObject = { ...wsdlObject }; const helper = new SecurityAssertionsHelper(), securityContent = getSecurity(parsedXml, wsdlObject.securityPolicyNamespace, wsdlRoot), securityAssertions = helper.getSecurityAssertions(securityContent, wsdlObject.securityPolicyNamespace); newWsdlObject.securityPolicyArray = securityAssertions; return newWsdlObject; } /** * if the element is a object then the parser populated * the propety #text then the node is the string * also replaces tabs and new lines from the string * @param {*} node the element the return as array * @returns {string} the documentation */ function getDocumentationStringFromNode(node) { if (typeof node === 'object') { return removeLineBreak(node['#text']); } return removeLineBreak(node); } /** * Finds all the services of the document * creates a namespace array object with the found information * @param {object} parsedXml the binding operation object * @param {string} wsdlRoot the root tag for the document * @param {object} wsdlObject the current wsdlObject * @returns {Array} the [services] object */ function getWSDLDocumentation(parsedXml, wsdlRoot, wsdlObject) { if (!parsedXml || typeof parsedXml !== 'object') { throw new UserError('Provided WSDL definition is invalid XML.'); } try { const principalPrefix = getPrincipalPrefix(parsedXml, wsdlRoot), definitions = getNodeByName(parsedXml, principalPrefix, wsdlRoot); let documentation = getNodeByName(definitions, principalPrefix, DOCUMENTATION_TAG); return documentation ? getDocumentationStringFromNode(documentation) : ''; } catch (error) { wsdlObject.log.logError('Cannot get documentation from wsdl'); return ''; } } /** * Gets the service documentation if is a node or a string * if not found any returns empty string * @param {object} service the service from the wsdl * @param {string} principalPrefix principal document prefix * @returns {string} service from documentation */ function getServiceDocumentation(service, principalPrefix) { if (!service || typeof service !== 'object') { return ''; } try { let documentation = getNodeByName(service, principalPrefix, DOCUMENTATION_TAG); return documentation ? getDocumentationStringFromNode(documentation) : ''; } catch (error) { if (error instanceof UserError) { throw error; } throw new WsdlError('Cannot get documentation from object'); } } /** * Removes xml separator from string * @param {string} elementName the root tag for the document to remove xml ns separator * @returns {string} the elementName without ":" */ function excludeSeparatorFromName(elementName) { return elementName ? elementName.split(XML_NAMESPACE_SEPARATOR).reverse()[0] : elementName; } /** * Gets the import nodes from a parsed wsdl from root or schema * @param {object} xmlParsed the parsed document * @param {string} principalPrefix principal prefix of the document * @param {string} wsdlRoot the root tag for the document to remove xml ns separator * @param {string} tag the tag import or include * @returns {Array} all the import nodes */ function getWSDLImportsOrIncludes(xmlParsed, principalPrefix, wsdlRoot, tag) { let types, gI, globalImports = []; if (getNodeByName(xmlParsed[principalPrefix + wsdlRoot], principalPrefix, tag)) { gI = getNodeByName(xmlParsed[principalPrefix + wsdlRoot], principalPrefix, tag); if (Array.isArray(gI)) { globalImports = globalImports.concat(gI); } else { globalImports.push(getNodeByName(xmlParsed[principalPrefix + wsdlRoot], principalPrefix, tag)); } } types = getArrayFrom(getNodeByName(xmlParsed[principalPrefix + wsdlRoot], principalPrefix, ATTRIBUTE_TYPES)); if (types && types.length > 0) { types.forEach((type) => { let schemaProperties = Object.keys(type).filter((propertyName) => { return propertyName.includes(SCHEMA_TAG); }); if (schemaProperties && schemaProperties.length > 0) { let schemas = schemaProperties.map((schemaProperty) => { return type[schemaProperty]; }); schemas.filter((schema) => { if (_.isArray(schema)) { // for each element of schema, identify import tags and add it to globalImports _.forEach(schema, (element) => { _.forEach(element, (elementValue, elementName) => { if (elementName.includes(tag)) { globalImports.push(elementValue); } }); }); } else { let importProperties = Object.keys(schema).filter((propertyName) => { return propertyName.includes(tag); }); if (importProperties) { let imports = getArrayFrom(schema[importProperties]); if (imports && importProperties.length > 0) { globalImports = globalImports.concat(imports); } return schema[importProperties]; } } }); } }); } return globalImports; } /** * Gets the import nodes from a parsed wsdl from root or schema * @param {object} xmlParsed the parsed document * @param {string} principalPrefix principal prefix of the document * @param {string} wsdlRoot the root tag for the document to remove xml ns separator * @returns {Array} all the import nodes */ function getWSDLImports(xmlParsed, principalPrefix, wsdlRoot) { return getWSDLImportsOrIncludes(xmlParsed, principalPrefix, wsdlRoot, IMPORT_TAG); } /** * Gets the import nodes from a parsed wsdl from root or schema * @param {object} xmlParsed the parsed document * @param {string} principalPrefix principal prefix of the document * @param {string} wsdlRoot the root tag for the document to remove xml ns separator * @returns {Array} all the include nodes */ function getWSDLIncludes(xmlParsed, principalPrefix, wsdlRoot) { return getWSDLImportsOrIncludes(xmlParsed, principalPrefix, wsdlRoot, INCLUDE_TAG); } /** * returns if document has import nodes in root or schema * @param {object} xmlParsed the parsed document * @param {string} principalPrefix principal prefix of the document * @param {string} wsdlRoot the root tag for the document to remove xml ns separator * @returns {boolean} if the document has imports */ function wsdlHasImports(xmlParsed, principalPrefix, wsdlRoot) { return getWSDLImports(xmlParsed, principalPrefix, wsdlRoot).length > 0 || getWSDLIncludes(xmlParsed, principalPrefix, wsdlRoot).length > 0; } /** * Gets the namespace prefix of the sent schema * @param {object} parsedXML the parsed schema * @returns {object} the root */ function getSchemaPrefixFromParsedSchema(parsedXML) { let res = '', property = Object.keys(parsedXML).filter((key) => { return key.includes(SCHEMA_TAG); }); if (property && property.length > 0) { res = getQNamePrefix(property[0]); } return res === '' ? res : res + XML_NAMESPACE_SEPARATOR; } module.exports = { getPrincipalPrefix, getNamespaceByURL, getNamespaceByKey, getTnsNamespace, getAllNamespaces, XML_NAMESPACE_SEPARATOR, XML_NAMESPACE_DECLARATION, TARGET_NAMESPACE_SPEC, SERVICE_TAG, IMPORT_TAG, SCHEMA_TAG, ATTRIBUTE_TYPES, ATTRIBUTE_NAMESPACE, SCHEMA_NS_URL, ATTRIBUTE_SCHEMALOCATION, ATTRIBUTE_LOCATION, SCHEMA_NS_URL_XSD, getServices, getArrayFrom, getBindings, getElementsFromWSDL, getSchemaNamespace, getNodeByName, getAttributeByName, THIS_NS_PREFIX, getBindingOperation, assignSecurity, getSecurity, getDocumentationStringFromNode, getWSDLDocumentation, excludeSeparatorFromName, getWSDLImports, wsdlHasImports, getNodeByQNameLocal, getServiceDocumentation, getSchemaPrefixFromParsedSchema, getWSDLIncludes, getNodeByQNameLocalArray };