UNPKG

@postman/wsdl-to-postman

Version:

Convert a given WSDL specification (1.1) to Postman Collection

709 lines (660 loc) 25.2 kB
const _ = require('lodash'), { getHttpVerb, POST_METHOD } = require('../lib/utils/httpUtils'), WsdlError = require('./WsdlError'), UserError = require('./UserError'), WSDL_ROOT = 'definitions', PORT_TYPE_TAG = 'portType', OPERATION_TAG = 'operation', BINDING_TAG = 'binding', PORT_TAG = 'port', ADDRESS_TAG = 'address', NAME_TAG = 'name', LOCATION_TAG = 'location', DOCUMENTATION_TAG = 'documentation', MESSAGE_TAG = 'message', PART_TAG = 'part', WSDL_NS_URL = 'http://schemas.xmlsoap.org/wsdl/', SOAP_NS_URL = 'http://schemas.xmlsoap.org/wsdl/soap/', SOAP_NS_KEY = 'xmlns:soap', HTTP_NS_URL = 'http://schemas.xmlsoap.org/wsdl/http/', HTTP_NS_KEY = 'xmlns:http', SOAP_12_NS_URL = 'http://schemas.xmlsoap.org/wsdl/soap12/', SCHEMA_NS_URL = 'http://www.w3.org/2001/XMLSchema', SOAP_TRANSPORT_URL = 'http://schemas.xmlsoap.org/soap/http', TNS_NS_KEY = 'xmlns:tns', XMLNS_ATTRIBUTE = 'xmlns', ATTRIBUTE_NAME = 'name', ATTRIBUTE_STYLE = 'style', ATTRIBUTE_VERB = 'verb', ATTRIBUTE_MESSAGE = 'message', ATTRIBUTE_ELEMENT = 'element', TARGETNAMESPACE_KEY = 'targetNamespace', ATTRIBUTE_LOCATION = 'location', ATTRIBUTE_TYPE = 'type', ATTRIBUTE_TRANSPORT = 'transport', ATTRIBUTE_PART = 'part', XML_POLICY = 'http://schemas.xmlsoap.org/ws/2004/09/policy', INPUT_TAG = 'input', HEADER_TAG = 'header', OUTPUT_TAG = 'output', FAULT_TAG = 'fault', MIME_CONTENT_TAG = 'mime:content', MIME_XML_TAG = 'mime:mimeXml', URL_ENCODED_TAG = 'http:urlEncoded', ATTRIBUTE_SOAP_ACTION = 'soapAction', ATTRIBUTE_METHOD = 'method', { getArrayFrom } = require('./utils/objectUtils'), { getPrincipalPrefix, getNodeByName, getAttributeByName, THIS_NS_PREFIX, getDocumentationStringFromNode, excludeSeparatorFromName, SERVICE_TAG } = require('./WsdlParserCommon'), { getQNamePrefixFilter, getQNamePrefix, getQNameLocal, XML_NAMESPACE_SEPARATOR } = require('./utils/XMLParsedUtils'), { DOC_HAS_NO_SERVICE_MESSAGE, DOC_HAS_NO_SERVICE_PORT_MESSAGE_1, DOC_HAS_NO_SERVICE_PORT_MESSAGE_2 } = require('./constants/messageConstants'), { HTTP_PROTOCOL, SOAP_PROTOCOL, SOAP12_PROTOCOL, MIME_TYPE_CONTENT, MIME_TYPE_XML, URL_ENCODED_GET, EMPTY_ELEMENT_BY_DEFAULT } = require('./constants/processConstants'), { createErrorElement, createEmptyElement } = require('./utils/WSDLElementUtils'), { URLWithParamsHelper } = require('./utils/URLWithParamsHelper'), { BindingExtraInformation } = require('./WSDLObject'); class WsdlInformationService11 { constructor() { this.version = '1.1'; this.InputTagName = INPUT_TAG; this.HeaderTagName = HEADER_TAG; this.NameTag = NAME_TAG; this.OutputTagName = OUTPUT_TAG; this.FaultTagName = FAULT_TAG; this.SOAPNamesapceURL = SOAP_NS_URL; this.SOAP12NamesapceURL = SOAP_12_NS_URL; this.SOAPNamespaceKey = SOAP_NS_KEY; this.SchemaNamespaceURL = SCHEMA_NS_URL; this.RootTagName = WSDL_ROOT; this.WSDLNamespaceURL = WSDL_NS_URL; this.HTTPNamespaceURL = HTTP_NS_URL; this.HTTPNamespaceKey = HTTP_NS_KEY; this.THISNamespaceKey = TNS_NS_KEY; this.TargetNamespaceKey = TARGETNAMESPACE_KEY; this.XMLPolicyURL = XML_POLICY; this.AbstractInterfaceTag = PORT_TYPE_TAG; this.ConcreteInterfaceTag = BINDING_TAG; this.ConcreteServiceTag = SERVICE_TAG; } /** * gets the binding operation name from the binding object * @param {object} bindingOperation the WSDL Binding operation * @returns {string} the fault tag name */ getBindingOperationName(bindingOperation) { return getAttributeByName(bindingOperation, ATTRIBUTE_NAME); } /** * gets the port type name corresponding to the sent binding * found by the tag type * @param {string} binding the binding object from wsdl * @param {object} tnsNamespace tns namespace object * @returns {string} the porttype name */ getAbstractDefinitionName(binding, tnsNamespace) { let tnsNamespacePrefix = (tnsNamespace && tnsNamespace.key === 'string') ? tnsNamespace.key + ':' : THIS_NS_PREFIX, attribute = getAttributeByName(binding, ATTRIBUTE_TYPE); if (typeof attribute === 'string') { return attribute.replace(tnsNamespacePrefix, ''); } return ''; } getOperationxPathInfo(bindingName, operationName, wsdlNamespaceUrl) { return { xpath: `//${WSDL_ROOT}//${BINDING_TAG}[@${ATTRIBUTE_NAME}="${bindingName}"]` + `//${OPERATION_TAG}[@${ATTRIBUTE_NAME}="${operationName}"]`, wsdlNamespaceUrl: wsdlNamespaceUrl }; } /** * Gets the service url * if the service port is not defined return empty string * @param {object} servicePort the service port element tag information * @param {object} bindingOperation the binding operation element tag information * @param {object} bindingTagInfo the binding information for the binding * @param {function} decodeFunction a function to decode the url * @param {object} mimeContentInput input information binding details * @param {object} elementInput the schema input of the service * @returns {string} the service url */ getServiceURL(servicePort, bindingOperation, bindingTagInfo, decodeFunction, mimeContentInput, elementInput) { if (!servicePort) { return ''; } let url, keys = Object.keys(servicePort), addressKey = keys.find((key) => { if (key.includes(ADDRESS_TAG)) { return true; } }), serviceLocation = this.getLocationFromBindingOperation(bindingOperation, bindingTagInfo), address = servicePort[addressKey] || {}; serviceLocation = serviceLocation ? serviceLocation : ''; url = getAttributeByName(address, LOCATION_TAG) + serviceLocation; if (mimeContentInput && mimeContentInput.mimeType === URL_ENCODED_GET) { const helper = new URLWithParamsHelper(), parametersURL = helper.convertInputToURLParams(elementInput); url += '?' + parametersURL; } return decodeFunction(url); } /** * Gets the name of the service if is not defined * return empty string * @param {*} service the service element tag information * @returns {string} the service name */ getServiceName(service) { return service ? getAttributeByName(service, NAME_TAG) : ''; } /** * Gets the name of the service port if is not defined * return empty string * @param {*} servicePort the service->port element tag information * @returns {string} the service port name */ getPortName(servicePort) { return servicePort ? getAttributeByName(servicePort, NAME_TAG) : ''; } /** * gets the documentation information from the porttypeoperation * @param {object} portTypeOperation port type operation object * @param {*} principalPrefix the prefix for the wsdl * @returns {string} the documentation */ getOperationDocumentation(portTypeOperation, principalPrefix) { try { let documentation = getNodeByName(portTypeOperation, principalPrefix, DOCUMENTATION_TAG); return documentation ? getDocumentationStringFromNode(documentation) : ''; } catch (error) { return ''; } } /** * finds the element from the port type operation object * @param {object} parsedXml the content file in javascript object representation * @param {object} portTypeOperation por type operation to find the element * @param {object} elementsFromWSDL all the elements of the document * @param {string} principalPrefix the principal prefix of the document * @param {string} tag the tag for search of could be input output fault * @param {object} tnsNamespace tns namespace object * @returns {object} the WSDLObject */ getElementFromPortTypeInterfaceOperation(parsedXml, portTypeOperation, elementsFromWSDL, principalPrefix, tag, tnsNamespace) { let information = getNodeByName(portTypeOperation, principalPrefix, tag), messageName, elementName, elementsArray = []; if (information) { if (!Array.isArray(information)) { information = [information]; } information.forEach((informationTag) => { let foundElement; messageName = getAttributeByName(informationTag, ATTRIBUTE_MESSAGE); elementName = this.getMessageElementNameByMessageName(parsedXml, principalPrefix, messageName, tnsNamespace); if (elementName === EMPTY_ELEMENT_BY_DEFAULT) { elementsArray.push(createEmptyElement(elementName)); return; } elementName = excludeSeparatorFromName(elementName); if (Array.isArray(elementsFromWSDL)) { foundElement = elementsFromWSDL.find((element) => { return element.name === elementName; }); } if (foundElement === undefined) { elementsArray.push(createErrorElement({}, elementName)); } else { elementsArray.push(foundElement); } }); return elementsArray; } return []; } /** * finds the element from the port type operation object * @param {object} parsedXml the content file in javascript object representation * @param {object} bindingOperation binding operation to find the element * @param {object} elementsFromWSDL all the elements of the document * @param {string} principalPrefix the principal prefix of the document * @param {string} protocolPrefix the binding information for the binding * @param {string} inputTag the tag for search of input * @param {string} headerTag the tag for search of header * @param {object} tnsNamespace tns namespace object * @returns {object} the WSDLObject */ getElementFromBindingOperation(parsedXml, bindingOperation, elementsFromWSDL, principalPrefix, protocolPrefix, inputTag, headerTag, tnsNamespace) { let inputInformation = getNodeByName(bindingOperation, principalPrefix, inputTag), headerInformation = getNodeByName(inputInformation, protocolPrefix, headerTag), messageName, elementName, elementsArray = []; if (headerInformation) { if (!Array.isArray(headerInformation)) { headerInformation = [headerInformation]; } headerInformation.forEach((header) => { let foundElement; messageName = getAttributeByName(header, ATTRIBUTE_MESSAGE); elementName = this.getMessageElementNameByMessageName(parsedXml, principalPrefix, messageName, tnsNamespace); if (elementName === EMPTY_ELEMENT_BY_DEFAULT) { elementsArray.push(createEmptyElement(elementName)); return; } elementName = excludeSeparatorFromName(elementName); if (Array.isArray(elementsFromWSDL)) { foundElement = elementsFromWSDL.find((element) => { return element.name === elementName; }); } if (foundElement === undefined) { elementsArray.push(createErrorElement({}, elementName)); } else { elementsArray.push(foundElement); } }); return elementsArray; } return []; } /** * finds the element from the port type operation object * @param {object} parsedXml the content file in javascript object representation * @param {string} principalPrefix the principal prefix of the document * @param {string} messageName the name too look up for * @param {object} tnsNamespace tns namespace object * @returns {object} the message from WSDLObject */ getMessageElementNameByMessageName(parsedXml, principalPrefix, messageName, tnsNamespace) { let definitions = getNodeByName(parsedXml, principalPrefix, WSDL_ROOT), tnsNamespacePrefix = (tnsNamespace && typeof (tnsNamespace.key) === 'string') ? tnsNamespace.key + ':' : THIS_NS_PREFIX, part, elementName, messageOnlyName = getQNameLocal(messageName), messages = getArrayFrom(getNodeByName(definitions, principalPrefix, MESSAGE_TAG)), foundMessage = Array.isArray(messages) && messages.find((message) => { return getAttributeByName(message, ATTRIBUTE_NAME) === messageOnlyName; }); if (foundMessage) { part = getNodeByName(foundMessage, principalPrefix, PART_TAG); elementName = this.resolvePartName(part); if (!elementName || Array.isArray(elementName)) { return messageName.replace(tnsNamespacePrefix, ''); } elementName = elementName.replace(tnsNamespacePrefix, ''); } return elementName; } resolvePartName(partElement) { let partIsEmpty = !partElement; if (partIsEmpty) { return EMPTY_ELEMENT_BY_DEFAULT; } return getAttributeByName(partElement, ATTRIBUTE_ELEMENT); } /** * Looks up for the service by binding name * @param {string} bindingName the object to populate * @param {array} services the object to populate * @param {string} principalPrefix the principal wsdl prefix * @param {WSDLObject} wsdlObject the wsdl object we are building * @returns {object} parsedXML service representation */ getServiceAndExpossedInfoByBindingName(bindingName, services, principalPrefix, wsdlObject) { if (!bindingName) { throw new UserError('Definition contains binding without required property "name".'); } if (principalPrefix === undefined || principalPrefix === null) { throw new UserError('Definition doesn\'t contain any principal prefix.'); } if (!services) { wsdlObject.log.logError(DOC_HAS_NO_SERVICE_MESSAGE + ' ' + bindingName); return; } try { let foundService = null, port = null, i = 0; for (i = 0; i < services.length; i++) { const ports = getArrayFrom(getNodeByName(services[i], principalPrefix, PORT_TAG)); if (!ports) { break; } port = ports.find((port) => { return getQNameLocal(getAttributeByName(port, BINDING_TAG)) === bindingName; }); if (port) { foundService = services[i]; break; } } if (!port) { wsdlObject.log .logError(`${DOC_HAS_NO_SERVICE_PORT_MESSAGE_1} ${bindingName} ${DOC_HAS_NO_SERVICE_PORT_MESSAGE_2}`); return; } return { service: foundService, port: port }; } catch (error) { if (error instanceof UserError) { throw error; } throw new WsdlError('Cannot get service port from WSDL'); } } /** * Looks up for the port type operation by portype name and operation name * @param {string} portTypeName the port type name to look for * @param {string} operationName the operation name to look for * @param {object} parsedXml the parsed xml object from the parser * @param {string} principalPrefix the principal wsdl prefix * @returns {object} parsedXML porttype operation representation */ getAbstractOperationByName(portTypeName, operationName, parsedXml, principalPrefix) { let portTypeFound, operationReturn, operations, localPortname; if (!parsedXml || typeof parsedXml !== 'object') { throw new UserError('Provided WSDL definition is invalid XML.'); } if (!portTypeName) { throw new UserError('Definition contains portType without required property "name".'); } if (!operationName) { throw new UserError('Definition contains operation without required property "name".'); } try { localPortname = getQNameLocal(portTypeName); const definitions = getNodeByName(parsedXml, principalPrefix, WSDL_ROOT), arrayPortTypes = getArrayFrom(getNodeByName(definitions, principalPrefix, PORT_TYPE_TAG)); portTypeFound = arrayPortTypes && arrayPortTypes.find((portType) => { return getAttributeByName(portType, NAME_TAG) === localPortname; }); operations = getArrayFrom(getNodeByName(portTypeFound, principalPrefix, OPERATION_TAG)); operationReturn = operations && operations.find((operation) => { return getAttributeByName(operation, NAME_TAG) === operationName; }) || {}; return operationReturn; } catch (error) { if (error instanceof UserError) { throw error; } throw new WsdlError('Cannot get port type from WSDL'); } } /** * Looks up for the style property in the binding operation tag * @param {string} bindingOperation the binding operation object * @param {string} bindingTagInfo the binding's binding son tag information * @returns {string} the style's property's value property */ getStyleFromBindingOperation(bindingOperation, bindingTagInfo) { if (!bindingOperation || typeof bindingOperation !== 'object') { throw new UserError('Definition contains invalid binding operation.'); } try { if (bindingTagInfo.protocol === SOAP_PROTOCOL || bindingTagInfo.protocol === SOAP12_PROTOCOL) { return getAttributeByName(getNodeByName(bindingOperation, bindingTagInfo.protocolPrefix, OPERATION_TAG), ATTRIBUTE_STYLE); } } catch (error) { return ''; } return ''; } /** * Looks up for the soapAction property in the binding operation tag * @param {string} bindingOperation the binding operation object * @param {string} bindingTagInfo the binding's binding son tag information * @returns {string} the style's property's value property */ getSOAPActionFromBindingOperation(bindingOperation, bindingTagInfo) { if (!bindingOperation || typeof bindingOperation !== 'object') { throw new UserError('Definition contains invalid binding operation.'); } try { if (bindingTagInfo.protocol === SOAP_PROTOCOL || bindingTagInfo.protocol === SOAP12_PROTOCOL) { return getAttributeByName(getNodeByName(bindingOperation, bindingTagInfo.protocolPrefix, OPERATION_TAG), ATTRIBUTE_SOAP_ACTION); } } catch (error) { return ''; } return ''; } /** * Looks up for the style property in the binding operation tag * @param {string} bindingOperation the binding operation object * @param {string} bindingTagInfo the binding's binding son tag information * @returns {string} the style's property's value property */ getLocationFromBindingOperation(bindingOperation, bindingTagInfo) { if (!bindingOperation || typeof bindingOperation !== 'object') { throw new UserError('Definition contains invalid binding operation.'); } try { if (bindingTagInfo.protocol === HTTP_PROTOCOL) { return getAttributeByName(getNodeByName(bindingOperation, bindingTagInfo.protocolPrefix, OPERATION_TAG), ATTRIBUTE_LOCATION); } } catch (error) { return ''; } return ''; } /** * Get the port type operations of the document * always returns an array * @param {object} parsedXml the binding operation object * @returns {Array} the information of the por type operations */ getPortTypeOperations(parsedXml) { if (!parsedXml || typeof parsedXml !== 'object') { throw new UserError('Provided WSDL definition is invalid XML.'); } try { let operations = []; const principalPrefix = getPrincipalPrefix(parsedXml, WSDL_ROOT), definitions = parsedXml[principalPrefix + WSDL_ROOT], portTtypes = definitions[principalPrefix + PORT_TYPE_TAG]; if (portTtypes && Array.isArray(portTtypes)) { operations = portTtypes.map((portType) => { return portType[principalPrefix + OPERATION_TAG]; }, []); return operations.flat(); } return portTtypes[principalPrefix + OPERATION_TAG]; } catch (error) { if (error instanceof UserError) { throw error; } throw new WsdlError('Cannot get portypes from WSDL'); } } /** * gets the next information from the binding tag: * name, protocol, protocol prefix and the default verb * @param {object} binding the WSDL's binding object * @param {NameSpace} soapNamespace the soap namespace information * @param {NameSpace} soap12Namespace the sopa 1.2 namespace information * @param {NameSpace} httpNamespace the http namespace information * @param {Array} allNameSpaces All namespaces present in wsdl definition * @returns {object} a new object with the information */ getBindingInfoFromBindingTag(binding, soapNamespace, soap12Namespace, httpNamespace, allNameSpaces) { if (!binding || typeof binding !== 'object') { throw new UserError('Definition contains invalid binding.'); } let protocol = '', verb = '', protocolPrefix = '', bindingName = '', namespace = Object.keys(binding).find((key) => { if (key.includes(BINDING_TAG)) { return true; } }), isProtocolSoap12, isProtocolSoap, isProtocolHttp; if (namespace === undefined) { throw new UserError('Definition contains a binding for which namespace could not be found.'); } const protocolKey = getQNamePrefix(namespace); protocolPrefix = getQNamePrefixFilter(namespace); /** * There can be multiple namespaces defined with same binding URL. * In such cases detect binding namespaces by going through all namespaces and * identifying correct one via URL. */ isProtocolSoap12 = soap12Namespace && (protocolKey === soap12Namespace.key || _.some(allNameSpaces, (namespace) => { return soap12Namespace.url === namespace.url && soap12Namespace.key !== namespace.key; })); if (!isProtocolSoap12) { isProtocolSoap = soapNamespace && (protocolKey === soapNamespace.key || _.some(allNameSpaces, (namespace) => { return soapNamespace.url === namespace.url && soapNamespace.key !== namespace.key; })); } if (!isProtocolSoap12 && !isProtocolSoap) { isProtocolHttp = httpNamespace && (protocolKey === httpNamespace.key || _.some(allNameSpaces, (namespace) => { return httpNamespace.url === namespace.url && httpNamespace.key !== namespace.key; })); } if (isProtocolSoap12) { protocol = SOAP12_PROTOCOL; verb = POST_METHOD; } else if (isProtocolSoap) { protocol = SOAP_PROTOCOL; verb = POST_METHOD; } else if (isProtocolHttp) { protocol = HTTP_PROTOCOL; verb = getHttpVerb(getAttributeByName(binding[namespace], ATTRIBUTE_VERB)); } else if (protocolKey === '') { const innerBinding = getNodeByName(binding, protocolPrefix, BINDING_TAG), transport = getAttributeByName(innerBinding, ATTRIBUTE_TRANSPORT), xmlns = getAttributeByName(innerBinding, XMLNS_ATTRIBUTE); if (transport === SOAP_TRANSPORT_URL && xmlns === SOAP_NS_URL) { protocol = SOAP_PROTOCOL; verb = POST_METHOD; } else if (transport === SOAP_TRANSPORT_URL && xmlns === SOAP_12_NS_URL) { protocol = SOAP12_PROTOCOL; verb = POST_METHOD; } } else { throw new UserError('Definition contains a binding for which protocol could not be determined.'); } bindingName = getAttributeByName(binding, ATTRIBUTE_NAME); return { protocol: protocol, protocolPrefix: protocolPrefix, verb: verb, bindingName: bindingName }; } /** * gets the http method for the operation * @param {object} bindingOperation the binding operation object * @param {object} httpNamespace the wsdl http namespace object * @returns {string} the verb */ getOperationHttpMethod(bindingOperation, httpNamespace) { let readVerb = getAttributeByName(bindingOperation, httpNamespace.key + XML_NAMESPACE_SEPARATOR + ATTRIBUTE_METHOD); return getHttpVerb(readVerb); } /** * gets the operation's mime content information * @param {object} bindingOperation the binding operation object * @param {string} principalPrefix the wsdl http namespace object * @param {string} tag the tag for input or output * @returns {string} the verb */ getMimeContentInfo(bindingOperation, principalPrefix, tag) { try { const input = getNodeByName(bindingOperation, principalPrefix, tag); let mimeXMLTag, urlEncoded, mimeContentTag = getNodeByName(input, '', MIME_CONTENT_TAG); if (mimeContentTag) { let mime = new BindingExtraInformation(); mime.mimeType = MIME_TYPE_CONTENT; mime.type = getAttributeByName(mimeContentTag, ATTRIBUTE_TYPE); return mime; } urlEncoded = getNodeByName(input, '', URL_ENCODED_TAG); if (urlEncoded !== undefined) { let mime = new BindingExtraInformation(); mime.mimeType = URL_ENCODED_GET; return mime; } mimeXMLTag = getNodeByName(input, '', MIME_XML_TAG); if (mimeXMLTag) { let mime = new BindingExtraInformation(); mime.mimeType = MIME_TYPE_XML; mime.part = getAttributeByName(mimeXMLTag, ATTRIBUTE_PART); return mime; } } catch (error) { return undefined; } } } module.exports = { WsdlInformationService11 };