@postman/wsdl-to-postman
Version:
Convert a given WSDL specification (1.1) to Postman Collection
643 lines (597 loc) • 23.5 kB
JavaScript
const
_ = require('lodash'),
{
getNodeByName,
getAttributeByName,
THIS_NS_PREFIX,
getDocumentationStringFromNode,
SERVICE_TAG
} = require('./WsdlParserCommon'),
{
getArrayFrom
} = require('./utils/objectUtils'),
{
getHttpVerb,
POST_METHOD
} = require('../lib/utils/httpUtils'),
WSDL_ROOT = 'description',
WSDL_NS_URL = 'http://www.w3.org/ns/wsdl',
SOAP_NS_URL = 'http://www.w3.org/ns/wsdl/soap',
SOAP_NS_KEY = 'xmlns:wsoap',
HTTP_NS_KEY = 'xmlns:whttp',
SOAP_12_NS_URL = 'http://www.w3.org/ns/wsdl/soap',
HTTP_NS_URL = 'http://www.w3.org/ns/wsdl/http',
ATTRIBUTE_TYPE = 'type',
TNS_NS_KEY = 'xmlns:tns',
ATTRIBUTE_METHOD_DEFAULT = 'methodDefault',
ATTRIBUTE_METHOD = 'method',
ATTRIBUTE_REF = 'ref',
ATTRIBUTE_NAME = 'name',
ATTRIBUTE_VERSION = 'version',
OPERATION_TAG = 'operation',
ENDPOINT_TAG = 'endpoint',
BINDING_TAG = 'binding',
INTERNAL_FAULT_TAG = 'fault',
DOCUMENTATION_TAG = 'documentation',
NAME_TAG = 'name',
INPUT_TAG = 'input',
HEADER_TAG = 'header',
OUTPUT_TAG = 'output',
OUTFAULT_TAG = 'outfault',
ADDRESS_TAG = 'address',
INTERFACE_TAG = 'interface',
ATTRIBUTE_ELEMENT = 'element',
ATTRIBUTE_LOCATION = 'location',
XML_POLICY = 'http://schemas.xmlsoap.org/ws/2004/09/policy',
SCHEMA_NS_URL = 'http://www.w3.org/2001/XMLSchema',
TARGETNAMESPACE_KEY = 'targetNamespace',
{
HTTP_PROTOCOL,
SOAP_PROTOCOL,
SOAP12_PROTOCOL
} = require('./constants/processConstants'),
WsdlError = require('./WsdlError'),
UserError = require('./UserError'),
{
XML_NAMESPACE_SEPARATOR,
getQNameLocal
} = 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'),
{
createErrorElement
} = require('./utils/WSDLElementUtils');
class WsdlInformationService20 {
constructor() {
this.version = '2.0';
this.InputTagName = INPUT_TAG;
this.HeaderTagName = HEADER_TAG;
this.NameTag = NAME_TAG;
this.OutputTagName = OUTPUT_TAG;
this.FaultTagName = OUTFAULT_TAG;
this.SOAPNamesapceURL = SOAP_NS_URL;
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.HTTPNamespaceKey = HTTP_NS_KEY;
this.HTTPNamespaceURL = HTTP_NS_URL;
this.THISNamespaceKey = TNS_NS_KEY;
this.TargetNamespaceKey = TARGETNAMESPACE_KEY;
this.XMLPolicyURL = XML_POLICY;
this.AbstractInterfaceTag = INTERFACE_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
* @param {object} tnsNamespace tns namespace object
* @returns {string} the fault tag name
*/
getBindingOperationName(bindingOperation, tnsNamespace) {
let tnsNamespacePrefix = (tnsNamespace && tnsNamespace.key === 'string') ? tnsNamespace.key + ':' : THIS_NS_PREFIX;
return getAttributeByName(bindingOperation, ATTRIBUTE_REF).replace(tnsNamespacePrefix, '');
}
/**
* gets the interface name corresponding to the sent binding
* @param {string} binding the binding object from wsdl
* @param {object} tnsNamespace tns namespace object
* @returns {string} the interface name
*/
getAbstractDefinitionName(binding, tnsNamespace) {
let tnsNamespacePrefix = (tnsNamespace && tnsNamespace.key === 'string') ? tnsNamespace.key + ':' : THIS_NS_PREFIX,
attribute = getAttributeByName(binding, INTERFACE_TAG);
if (typeof attribute === 'string') {
return attribute.replace(tnsNamespacePrefix, '');
}
return '';
}
/**
* 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);
}
/**
* 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() {
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(bindingOperation, bindingTagInfo.protocolPrefix + ':action');
}
}
catch (error) {
return '';
}
return '';
}
/**
* Returns an xpath for an operation
* @param {string} bindingName binding name
* @param {string} operationName self operation name
* @param {string} wsdlNamespaceUrl wsdl url namespace
* @param {string} tnsNamespace tnsNamespace information
* @returns {object} object with xpath and url
*/
getOperationxPathInfo(bindingName, operationName, wsdlNamespaceUrl, tnsNamespace) {
return {
xpath: `//${WSDL_ROOT}//${BINDING_TAG}[@${ATTRIBUTE_NAME}="${bindingName}"]` +
`//${OPERATION_TAG}[@${ATTRIBUTE_REF}="${tnsNamespace.key + ':' + operationName}"]`,
wsdlNamespaceUrl: wsdlNamespaceUrl
};
}
/**
* Gets the service url
* if the service port is not defined return empty string
* @param {object} serviceEndpoint the service endpoint 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
* @returns {string} the service url
*/
getServiceURL(serviceEndpoint, bindingOperation, bindingTagInfo, decodeFunction) {
if (!_.isObject(serviceEndpoint)) {
return '';
}
let serviceLocation = this.getLocationFromBindingOperation(bindingOperation, bindingTagInfo);
serviceLocation = serviceLocation ? serviceLocation : '';
return decodeFunction(getAttributeByName(serviceEndpoint, ADDRESS_TAG) + serviceLocation);
}
/**
* 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 {*} serviceEndpoint the service->endpoint element tag information
* @returns {string} the service port name
*/
getPortName(serviceEndpoint) {
return serviceEndpoint ? getAttributeByName(serviceEndpoint, NAME_TAG) : '';
}
/**
* Looks up for the style property in the binding operation tag
* @param {string} operation the binding operation object
* @param {string} bindingTagInfo the binding's binding son tag information
* @returns {string} the style's property's value property
*/
getLocationFromBindingOperation(operation, bindingTagInfo) {
if (!operation || typeof operation !== 'object') {
throw new UserError('Definition contains invalid operation.');
}
try {
if (bindingTagInfo.protocol === HTTP_PROTOCOL) {
return getAttributeByName(operation, bindingTagInfo.protocolPrefix +
XML_NAMESPACE_SEPARATOR + ATTRIBUTE_LOCATION);
}
}
catch (error) {
return '';
}
return '';
}
/**
* Gets the documentation from the operation in a interface tag
* if not found returns empty string
* @param {*} interfaceOperation the wsdl interface operation object from interface tag
* @param {string} principalPrefix the documents principal prefix
* @returns {string} the documentation value
*/
getOperationDocumentation(interfaceOperation, principalPrefix) {
try {
let documentation = getNodeByName(interfaceOperation, principalPrefix, DOCUMENTATION_TAG);
return documentation ? getDocumentationStringFromNode(documentation) : '';
}
catch (error) {
return '';
}
}
/**
* gets the next information from the binding tag:
* name, protocol, protocol prefix and the default verb
* @param {*} binding the binding node to look up for information
* @param {NameSpace} soapNamespace the documents found namespace
* @param {NameSpace} soap12Namespace the documentos found namespace
* @param {NameSpace} httpNamespace the documentos found namespace
* @param {Array} allNameSpaces All namespaces present in wsdl definition
* @returns {object} the protocol information
*/
getBindingInfoFromBindingTag(binding, soapNamespace, soap12Namespace, httpNamespace, allNameSpaces) {
const type = getAttributeByName(binding, ATTRIBUTE_TYPE);
let protocol = '',
verb = '',
readVerb = '',
bindingName = '',
protocolPrefix = '';
if ((soapNamespace && type === SOAP_NS_URL) || (soap12Namespace && type === SOAP_12_NS_URL)) {
let namespaceKey = soapNamespace ? soapNamespace.key : soap12Namespace.key,
version = getAttributeByName(binding, namespaceKey + XML_NAMESPACE_SEPARATOR + ATTRIBUTE_VERSION);
/**
* 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.
*/
_.forEach(allNameSpaces, (namespace) => {
if (_.get(soap12Namespace, 'url', _.get(soapNamespace, 'url')) === namespace.url &&
namespaceKey !== namespace.key) {
let currVersion = getAttributeByName(binding, namespace.key + XML_NAMESPACE_SEPARATOR + ATTRIBUTE_VERSION);
if (currVersion) {
version = currVersion;
namespaceKey = namespace.key;
return false;
}
}
});
if (version === '1.2') {
protocol = SOAP12_PROTOCOL;
verb = POST_METHOD;
protocolPrefix = namespaceKey;
}
else {
protocol = SOAP_PROTOCOL;
verb = POST_METHOD;
protocolPrefix = namespaceKey;
}
}
else if (httpNamespace && type === HTTP_NS_URL) {
protocol = HTTP_PROTOCOL;
readVerb = getAttributeByName(binding, httpNamespace.key + XML_NAMESPACE_SEPARATOR +
ATTRIBUTE_METHOD_DEFAULT);
protocolPrefix = httpNamespace.key;
/**
* 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.
*/
_.forEach(allNameSpaces, (namespace) => {
if (_.get(httpNamespace, 'url') === namespace.url && _.get(httpNamespace, 'key') !== namespace.key) {
let currReadVerb = getAttributeByName(binding, namespace.key + XML_NAMESPACE_SEPARATOR +
ATTRIBUTE_METHOD_DEFAULT);
if (currReadVerb) {
readVerb = currReadVerb;
protocolPrefix = namespace.key;
return false;
}
}
});
if (!readVerb) {
verb = undefined;
}
else {
verb = getHttpVerb(readVerb);
}
}
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
};
}
/**
* Looks up for the service port by binding name
* @param {string} bindingName the object to populate
* @param {array} services the object to populate
* @param {string} principalPrefix the object to populate
* @param {WSDLObject} wsdlObject object we are building
* @returns {object} parsed service port 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,
endpoint = null,
i = 0;
for (i = 0; i < services.length; i++) {
const endpoints = getArrayFrom(getNodeByName(services[i], principalPrefix, ENDPOINT_TAG));
if (!endpoints) {
break;
}
endpoint = endpoints.find((endpoint) => {
return getQNameLocal(getAttributeByName(endpoint, BINDING_TAG)) === bindingName;
});
if (endpoint) {
foundService = services[i];
break;
}
}
if (!endpoint) {
wsdlObject.log
.logError(`${DOC_HAS_NO_SERVICE_PORT_MESSAGE_1} ${bindingName} ${DOC_HAS_NO_SERVICE_PORT_MESSAGE_2}`);
return;
}
return {
service: foundService,
port: endpoint
};
}
catch (error) {
if (error instanceof UserError) {
throw error;
}
throw new WsdlError('Cannot get service endpoint from WSDL');
}
}
/**
* Looks up for the interface operation by portype name and operation name
* @param {string} interfaceName the interface 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(interfaceName, operationName,
parsedXml, principalPrefix) {
let interfaceFound, operationReturn, operations, localInterfaceName;
if (!parsedXml || typeof parsedXml !== 'object') {
throw new UserError('Provided WSDL definition is invalid XML.');
}
if (!interfaceName) {
throw new UserError('Definition contains interface without required property "name".');
}
if (!operationName) {
throw new UserError('Definition contains operation without required property "name".');
}
try {
localInterfaceName = getQNameLocal(interfaceName);
const definitions = getNodeByName(parsedXml, principalPrefix, WSDL_ROOT),
arrayInterfaces = getArrayFrom(getNodeByName(definitions, principalPrefix, INTERFACE_TAG));
interfaceFound = arrayInterfaces.find((actualInterface) => {
return getAttributeByName(actualInterface, NAME_TAG) === localInterfaceName;
});
operations = getArrayFrom(getNodeByName(interfaceFound, principalPrefix, OPERATION_TAG));
operationReturn = operations.find((operation) => {
return getAttributeByName(operation, NAME_TAG) === getQNameLocal(operationName);
}) || {};
return operationReturn;
}
catch (error) {
if (error instanceof UserError) {
throw error;
}
throw new WsdlError('Cannot get portType from WSDL');
}
}
/**
* Looks up for the interface operation by portype name and operation name
* @param {string} interfaceName the interface 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
*/
getInterfaceByInterfaceName(interfaceName, parsedXml, principalPrefix) {
let WSDLFoundInterface;
if (!parsedXml || typeof parsedXml !== 'object') {
throw new UserError('Provided WSDL definition is invalid XML.');
}
if (!interfaceName) {
throw new UserError('Definition contains interface without required property "name".');
}
try {
const definitions = getNodeByName(parsedXml, principalPrefix, WSDL_ROOT),
arrayInterfaces = getArrayFrom(getNodeByName(definitions, principalPrefix, INTERFACE_TAG));
WSDLFoundInterface = arrayInterfaces.find((actualInterface) => {
return getAttributeByName(actualInterface, NAME_TAG) === interfaceName;
});
return WSDLFoundInterface;
}
catch (error) {
if (error instanceof UserError) {
throw error;
}
throw new WsdlError('Cannot get interface from WSDL');
}
}
/**
* finds the element from the interface operation object
* @param {object} parsedXML the parsed xml object from the parser
* @param {object} interfaceOperation interface 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
* @param {string} portTypeInterfaceName interface name
* @returns {object} the WSDLObject
*/
getElementFromPortTypeInterfaceOperation(parsedXML, interfaceOperation, elementsFromWSDL,
principalPrefix, tag, tnsNamespace, portTypeInterfaceName) {
let interfaceElement = this.getInterfaceByInterfaceName(portTypeInterfaceName,
parsedXML, principalPrefix);
if (tag === OUTFAULT_TAG) {
return this.getElementFromInterfaceOperationFault(interfaceElement,
interfaceOperation, elementsFromWSDL, principalPrefix, tag);
}
return this.getElementFromInterfaceOperationInOut(interfaceOperation, elementsFromWSDL, principalPrefix, tag);
}
/**
* 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 interface operation object when is called
* for input or output
* @param {object} interfaceOperation interface 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
* @returns {object} the WSDLObject
*/
getElementFromInterfaceOperationInOut(interfaceOperation, elementsFromWSDL, principalPrefix, tag) {
const information = interfaceOperation[principalPrefix + tag];
let elementName, element,
elements = [];
if (information) {
elementName = getAttributeByName(information, ATTRIBUTE_ELEMENT);
if (Array.isArray(elementsFromWSDL)) {
element = elementsFromWSDL.find((element) => {
let fixedName = getQNameLocal(elementName);
return element.name === fixedName;
});
}
if (element === undefined) {
elements.push(createErrorElement({}, elementName));
}
else {
elements.push(element);
}
return elements;
}
return [];
}
/**
* finds the element from the interface operation object when is called
* with fault tag
* @param {object} interfaceElement the interface from wsdl
* @param {object} interfaceOperation interface 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
* @returns {object} the WSDLObject
*/
getElementFromInterfaceOperationFault(interfaceElement, interfaceOperation, elementsFromWSDL, principalPrefix, tag) {
let faults,
information = interfaceOperation[principalPrefix + tag];
if (!information) {
return [];
}
const faultReference = getAttributeByName(information, ATTRIBUTE_REF);
if (faultReference) {
faults = getArrayFrom(getNodeByName(interfaceElement, principalPrefix, INTERNAL_FAULT_TAG));
if (faults) {
let foundFault = faults.find((fault) => {
let fixedName = getQNameLocal(faultReference);
return getAttributeByName(fault, ATTRIBUTE_NAME) === fixedName;
});
if (foundFault) {
let elementName = getAttributeByName(foundFault, ATTRIBUTE_ELEMENT),
element = elementsFromWSDL.find((element) => {
let fixedName = getQNameLocal(elementName);
return element.name === fixedName;
});
return [element];
}
}
}
return [];
}
/**
* gets the http method for the operation
* @returns {undefined} notghin
*/
getMimeContentInfo() {
return undefined;
}
}
module.exports = {
WsdlInformationService20
};