UNPKG

ngx-soap

Version:

SOAP service for Angular

1,338 lines 278 kB
/* * Copyright (c) 2011 Vinay Pulim <vinay@milewise.com> * MIT Licensed * */ /*jshint proto:true*/ "use strict"; import { __awaiter } from "tslib"; import * as sax from 'sax'; import { NamespaceContext } from './nscontext'; import * as _ from 'lodash'; import * as utils from './utils'; import * as url from 'url'; import { ok as assert } from 'assert'; const stripBom = (x) => { // Catches EFBBBF (UTF-8 BOM) because the buffer-to-string // conversion translates it to FEFF (UTF-16 BOM) if (x.charCodeAt(0) === 0xFEFF) { return x.slice(1); } return x; }; const ɵ0 = stripBom; let TNS_PREFIX = utils.TNS_PREFIX; let findPrefix = utils.findPrefix; let Primitives = { string: 1, boolean: 1, decimal: 1, float: 1, double: 1, anyType: 1, byte: 1, int: 1, long: 1, short: 1, negativeInteger: 1, nonNegativeInteger: 1, positiveInteger: 1, nonPositiveInteger: 1, unsignedByte: 1, unsignedInt: 1, unsignedLong: 1, unsignedShort: 1, duration: 0, dateTime: 0, time: 0, date: 0, gYearMonth: 0, gYear: 0, gMonthDay: 0, gDay: 0, gMonth: 0, hexBinary: 0, base64Binary: 0, anyURI: 0, QName: 0, NOTATION: 0 }; function splitQName(nsName) { let i = typeof nsName === 'string' ? nsName.indexOf(':') : -1; return i < 0 ? { prefix: TNS_PREFIX, name: nsName } : { prefix: nsName.substring(0, i), name: nsName.substring(i + 1) }; } function xmlEscape(obj) { if (typeof (obj) === 'string') { if (obj.substr(0, 9) === '<![CDATA[' && obj.substr(-3) === "]]>") { return obj; } return obj .replace(/&/g, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/"/g, '&quot;') .replace(/'/g, '&apos;'); } return obj; } let trimLeft = /^[\s\xA0]+/; let trimRight = /[\s\xA0]+$/; function trim(text) { return text.replace(trimLeft, '').replace(trimRight, ''); } function deepMerge(destination, source) { return _.mergeWith(destination || {}, source, function (a, b) { return _.isArray(a) ? a.concat(b) : undefined; }); } let Element = function (nsName, attrs, options) { let parts = splitQName(nsName); this.nsName = nsName; this.prefix = parts.prefix; this.name = parts.name; this.children = []; this.xmlns = {}; this._initializeOptions(options); for (let key in attrs) { let match = /^xmlns:?(.*)$/.exec(key); if (match) { this.xmlns[match[1] ? match[1] : TNS_PREFIX] = attrs[key]; } else { if (key === 'value') { this[this.valueKey] = attrs[key]; } else { this['$' + key] = attrs[key]; } } } if (this.$targetNamespace !== undefined) { // Add targetNamespace to the mapping this.xmlns[TNS_PREFIX] = this.$targetNamespace; } }; const ɵ1 = Element; Element.prototype._initializeOptions = function (options) { if (options) { this.valueKey = options.valueKey || '$value'; this.xmlKey = options.xmlKey || '$xml'; this.ignoredNamespaces = options.ignoredNamespaces || []; } else { this.valueKey = '$value'; this.xmlKey = '$xml'; this.ignoredNamespaces = []; } }; Element.prototype.deleteFixedAttrs = function () { this.children && this.children.length === 0 && delete this.children; this.xmlns && Object.keys(this.xmlns).length === 0 && delete this.xmlns; delete this.nsName; delete this.prefix; delete this.name; }; Element.prototype.allowedChildren = []; Element.prototype.startElement = function (stack, nsName, attrs, options) { if (!this.allowedChildren) { return; } let ChildClass = this.allowedChildren[splitQName(nsName).name], element = null; if (ChildClass) { stack.push(new ChildClass(nsName, attrs, options)); } else { this.unexpected(nsName); } }; Element.prototype.endElement = function (stack, nsName) { if (this.nsName === nsName) { if (stack.length < 2) return; let parent = stack[stack.length - 2]; if (this !== stack[0]) { _.defaultsDeep(stack[0].xmlns, this.xmlns); // delete this.xmlns; parent.children.push(this); parent.addChild(this); } stack.pop(); } }; Element.prototype.addChild = function (child) { return; }; Element.prototype.unexpected = function (name) { throw new Error('Found unexpected element (' + name + ') inside ' + this.nsName); }; Element.prototype.description = function (definitions) { return this.$name || this.name; }; Element.prototype.init = function () { }; Element.createSubClass = function () { let root = this; let subElement = function () { root.apply(this, arguments); this.init(); }; // inherits(subElement, root); subElement.prototype.__proto__ = root.prototype; return subElement; }; let ElementElement = Element.createSubClass(); let AnyElement = Element.createSubClass(); let InputElement = Element.createSubClass(); let OutputElement = Element.createSubClass(); let SimpleTypeElement = Element.createSubClass(); let RestrictionElement = Element.createSubClass(); let ExtensionElement = Element.createSubClass(); let ChoiceElement = Element.createSubClass(); let EnumerationElement = Element.createSubClass(); let ComplexTypeElement = Element.createSubClass(); let ComplexContentElement = Element.createSubClass(); let SimpleContentElement = Element.createSubClass(); let SequenceElement = Element.createSubClass(); let AllElement = Element.createSubClass(); let MessageElement = Element.createSubClass(); let DocumentationElement = Element.createSubClass(); let SchemaElement = Element.createSubClass(); let TypesElement = Element.createSubClass(); let OperationElement = Element.createSubClass(); let PortTypeElement = Element.createSubClass(); let BindingElement = Element.createSubClass(); let PortElement = Element.createSubClass(); let ServiceElement = Element.createSubClass(); let DefinitionsElement = Element.createSubClass(); let ElementTypeMap = { types: [TypesElement, 'schema documentation'], schema: [SchemaElement, 'element complexType simpleType include import'], element: [ElementElement, 'annotation complexType'], any: [AnyElement, ''], simpleType: [SimpleTypeElement, 'restriction'], restriction: [RestrictionElement, 'enumeration all choice sequence'], extension: [ExtensionElement, 'all sequence choice'], choice: [ChoiceElement, 'element sequence choice any'], // group: [GroupElement, 'element group'], enumeration: [EnumerationElement, ''], complexType: [ComplexTypeElement, 'annotation sequence all complexContent simpleContent choice'], complexContent: [ComplexContentElement, 'extension'], simpleContent: [SimpleContentElement, 'extension'], sequence: [SequenceElement, 'element sequence choice any'], all: [AllElement, 'element choice'], service: [ServiceElement, 'port documentation'], port: [PortElement, 'address documentation'], binding: [BindingElement, '_binding SecuritySpec operation documentation'], portType: [PortTypeElement, 'operation documentation'], message: [MessageElement, 'part documentation'], operation: [OperationElement, 'documentation input output fault _operation'], input: [InputElement, 'body SecuritySpecRef documentation header'], output: [OutputElement, 'body SecuritySpecRef documentation header'], fault: [Element, '_fault documentation'], definitions: [DefinitionsElement, 'types message portType binding service import documentation'], documentation: [DocumentationElement, ''] }; function mapElementTypes(types) { let rtn = {}; types = types.split(' '); types.forEach(function (type) { rtn[type.replace(/^_/, '')] = (ElementTypeMap[type] || [Element])[0]; }); return rtn; } for (let n in ElementTypeMap) { let v = ElementTypeMap[n]; v[0].prototype.allowedChildren = mapElementTypes(v[1]); } MessageElement.prototype.init = function () { this.element = null; this.parts = null; }; SchemaElement.prototype.init = function () { this.complexTypes = {}; this.types = {}; this.elements = {}; this.includes = []; }; TypesElement.prototype.init = function () { this.schemas = {}; }; OperationElement.prototype.init = function () { this.input = null; this.output = null; this.inputSoap = null; this.outputSoap = null; this.style = ''; this.soapAction = ''; }; PortTypeElement.prototype.init = function () { this.methods = {}; }; BindingElement.prototype.init = function () { this.transport = ''; this.style = ''; this.methods = {}; }; PortElement.prototype.init = function () { this.location = null; }; ServiceElement.prototype.init = function () { this.ports = {}; }; DefinitionsElement.prototype.init = function () { if (this.name !== 'definitions') this.unexpected(this.nsName); this.messages = {}; this.portTypes = {}; this.bindings = {}; this.services = {}; this.schemas = {}; }; DocumentationElement.prototype.init = function () { }; SchemaElement.prototype.merge = function (source) { assert(source instanceof SchemaElement); if (this.$targetNamespace === source.$targetNamespace) { _.merge(this.complexTypes, source.complexTypes); _.merge(this.types, source.types); _.merge(this.elements, source.elements); _.merge(this.xmlns, source.xmlns); } return this; }; SchemaElement.prototype.addChild = function (child) { if (child.$name in Primitives) return; if (child.name === 'include' || child.name === 'import') { let location = child.$schemaLocation || child.$location; if (location) { this.includes.push({ namespace: child.$namespace || child.$targetNamespace || this.$targetNamespace, location: location }); } } else if (child.name === 'complexType') { this.complexTypes[child.$name] = child; } else if (child.name === 'element') { this.elements[child.$name] = child; } else if (child.$name) { this.types[child.$name] = child; } this.children.pop(); // child.deleteFixedAttrs(); }; //fix#325 TypesElement.prototype.addChild = function (child) { assert(child instanceof SchemaElement); let targetNamespace = child.$targetNamespace; if (!this.schemas.hasOwnProperty(targetNamespace)) { this.schemas[targetNamespace] = child; } else { console.error('Target-Namespace "' + targetNamespace + '" already in use by another Schema!'); } }; InputElement.prototype.addChild = function (child) { if (child.name === 'body') { this.use = child.$use; if (this.use === 'encoded') { this.encodingStyle = child.$encodingStyle; } this.children.pop(); } }; OutputElement.prototype.addChild = function (child) { if (child.name === 'body') { this.use = child.$use; if (this.use === 'encoded') { this.encodingStyle = child.$encodingStyle; } this.children.pop(); } }; OperationElement.prototype.addChild = function (child) { if (child.name === 'operation') { this.soapAction = child.$soapAction || ''; this.style = child.$style || ''; this.children.pop(); } }; BindingElement.prototype.addChild = function (child) { if (child.name === 'binding') { this.transport = child.$transport; this.style = child.$style; this.children.pop(); } }; PortElement.prototype.addChild = function (child) { if (child.name === 'address' && typeof (child.$location) !== 'undefined') { this.location = child.$location; } }; DefinitionsElement.prototype.addChild = function (child) { let self = this; if (child instanceof TypesElement) { // Merge types.schemas into definitions.schemas _.merge(self.schemas, child.schemas); } else if (child instanceof MessageElement) { self.messages[child.$name] = child; } else if (child.name === 'import') { self.schemas[child.$namespace] = new SchemaElement(child.$namespace, {}); self.schemas[child.$namespace].addChild(child); } else if (child instanceof PortTypeElement) { self.portTypes[child.$name] = child; } else if (child instanceof BindingElement) { if (child.transport === 'http://schemas.xmlsoap.org/soap/http' || child.transport === 'http://www.w3.org/2003/05/soap/bindings/HTTP/') self.bindings[child.$name] = child; } else if (child instanceof ServiceElement) { self.services[child.$name] = child; } else if (child instanceof DocumentationElement) { } this.children.pop(); }; MessageElement.prototype.postProcess = function (definitions) { let part = null; let child = undefined; let children = this.children || []; let ns = undefined; let nsName = undefined; let i = undefined; let type = undefined; for (i in children) { if ((child = children[i]).name === 'part') { part = child; break; } } if (!part) { return; } if (part.$element) { let lookupTypes = [], elementChildren; delete this.parts; nsName = splitQName(part.$element); ns = nsName.prefix; let schema = definitions.schemas[definitions.xmlns[ns]]; this.element = schema.elements[nsName.name]; if (!this.element) { // debug(nsName.name + " is not present in wsdl and cannot be processed correctly."); return; } this.element.targetNSAlias = ns; this.element.targetNamespace = definitions.xmlns[ns]; // set the optional $lookupType to be used within `client#_invoke()` when // calling `wsdl#objectToDocumentXML() this.element.$lookupType = part.$element; elementChildren = this.element.children; // get all nested lookup types (only complex types are followed) if (elementChildren.length > 0) { for (i = 0; i < elementChildren.length; i++) { lookupTypes.push(this._getNestedLookupTypeString(elementChildren[i])); } } // if nested lookup types where found, prepare them for furter usage if (lookupTypes.length > 0) { lookupTypes = lookupTypes. join('_'). split('_'). filter(function removeEmptyLookupTypes(type) { return type !== '^'; }); let schemaXmlns = definitions.schemas[this.element.targetNamespace].xmlns; for (i = 0; i < lookupTypes.length; i++) { lookupTypes[i] = this._createLookupTypeObject(lookupTypes[i], schemaXmlns); } } this.element.$lookupTypes = lookupTypes; if (this.element.$type) { type = splitQName(this.element.$type); let typeNs = schema.xmlns && schema.xmlns[type.prefix] || definitions.xmlns[type.prefix]; if (typeNs) { if (type.name in Primitives) { // this.element = this.element.$type; } else { // first check local mapping of ns alias to namespace schema = definitions.schemas[typeNs]; let ctype = schema.complexTypes[type.name] || schema.types[type.name] || schema.elements[type.name]; if (ctype) { this.parts = ctype.description(definitions, schema.xmlns); } } } } else { let method = this.element.description(definitions, schema.xmlns); this.parts = method[nsName.name]; } this.children.splice(0, 1); } else { // rpc encoding this.parts = {}; delete this.element; for (i = 0; part = this.children[i]; i++) { if (part.name === 'documentation') { // <wsdl:documentation can be present under <wsdl:message> continue; } assert(part.name === 'part', 'Expected part element'); nsName = splitQName(part.$type); ns = definitions.xmlns[nsName.prefix]; type = nsName.name; let schemaDefinition = definitions.schemas[ns]; if (typeof schemaDefinition !== 'undefined') { this.parts[part.$name] = definitions.schemas[ns].types[type] || definitions.schemas[ns].complexTypes[type]; } else { this.parts[part.$name] = part.$type; } if (typeof this.parts[part.$name] === 'object') { this.parts[part.$name].prefix = nsName.prefix; this.parts[part.$name].xmlns = ns; } this.children.splice(i--, 1); } } this.deleteFixedAttrs(); }; /** * Takes a given namespaced String(for example: 'alias:property') and creates a lookupType * object for further use in as first (lookup) `parameterTypeObj` within the `objectToXML` * method and provides an entry point for the already existing code in `findChildSchemaObject`. * * @method _createLookupTypeObject * @param {String} nsString The NS String (for example "alias:type"). * @param {Object} xmlns The fully parsed `wsdl` definitions object (including all schemas). * @returns {Object} * @private */ MessageElement.prototype._createLookupTypeObject = function (nsString, xmlns) { let splittedNSString = splitQName(nsString), nsAlias = splittedNSString.prefix, splittedName = splittedNSString.name.split('#'), type = splittedName[0], name = splittedName[1], lookupTypeObj = {}; lookupTypeObj.$namespace = xmlns[nsAlias]; lookupTypeObj.$type = nsAlias + ':' + type; lookupTypeObj.$name = name; return lookupTypeObj; }; /** * Iterates through the element and every nested child to find any defined `$type` * property and returns it in a underscore ('_') separated String (using '^' as default * value if no `$type` property was found). * * @method _getNestedLookupTypeString * @param {Object} element The element which (probably) contains nested `$type` values. * @returns {String} * @private */ MessageElement.prototype._getNestedLookupTypeString = function (element) { let resolvedType = '^', excluded = this.ignoredNamespaces.concat('xs'); // do not process $type values wich start with if (element.hasOwnProperty('$type') && typeof element.$type === 'string') { if (excluded.indexOf(element.$type.split(':')[0]) === -1) { resolvedType += ('_' + element.$type + '#' + element.$name); } } if (element.children.length > 0) { let self = this; element.children.forEach(function (child) { let resolvedChildType = self._getNestedLookupTypeString(child).replace(/\^_/, ''); if (resolvedChildType && typeof resolvedChildType === 'string') { resolvedType += ('_' + resolvedChildType); } }); } return resolvedType; }; OperationElement.prototype.postProcess = function (definitions, tag) { let children = this.children; for (let i = 0, child; child = children[i]; i++) { if (child.name !== 'input' && child.name !== 'output') continue; if (tag === 'binding') { this[child.name] = child; children.splice(i--, 1); continue; } let messageName = splitQName(child.$message).name; let message = definitions.messages[messageName]; message.postProcess(definitions); if (message.element) { definitions.messages[message.element.$name] = message; this[child.name] = message.element; } else { this[child.name] = message; } children.splice(i--, 1); } this.deleteFixedAttrs(); }; PortTypeElement.prototype.postProcess = function (definitions) { let children = this.children; if (typeof children === 'undefined') return; for (let i = 0, child; child = children[i]; i++) { if (child.name !== 'operation') continue; child.postProcess(definitions, 'portType'); this.methods[child.$name] = child; children.splice(i--, 1); } delete this.$name; this.deleteFixedAttrs(); }; BindingElement.prototype.postProcess = function (definitions) { let type = splitQName(this.$type).name, portType = definitions.portTypes[type], style = this.style, children = this.children; if (portType) { portType.postProcess(definitions); this.methods = portType.methods; for (let i = 0, child; child = children[i]; i++) { if (child.name !== 'operation') continue; child.postProcess(definitions, 'binding'); children.splice(i--, 1); child.style || (child.style = style); let method = this.methods[child.$name]; if (method) { method.style = child.style; method.soapAction = child.soapAction; method.inputSoap = child.input || null; method.outputSoap = child.output || null; method.inputSoap && method.inputSoap.deleteFixedAttrs(); method.outputSoap && method.outputSoap.deleteFixedAttrs(); } } } delete this.$name; delete this.$type; this.deleteFixedAttrs(); }; ServiceElement.prototype.postProcess = function (definitions) { let children = this.children, bindings = definitions.bindings; if (children && children.length > 0) { for (let i = 0, child; child = children[i]; i++) { if (child.name !== 'port') continue; let bindingName = splitQName(child.$binding).name; let binding = bindings[bindingName]; if (binding) { binding.postProcess(definitions); this.ports[child.$name] = { location: child.location, binding: binding }; children.splice(i--, 1); } } } delete this.$name; this.deleteFixedAttrs(); }; SimpleTypeElement.prototype.description = function (definitions) { let children = this.children; for (let i = 0, child; child = children[i]; i++) { if (child instanceof RestrictionElement) return this.$name + "|" + child.description(); } return {}; }; RestrictionElement.prototype.description = function (definitions, xmlns) { let children = this.children; let desc; for (let i = 0, child; child = children[i]; i++) { if (child instanceof SequenceElement || child instanceof ChoiceElement) { desc = child.description(definitions, xmlns); break; } } if (desc && this.$base) { let type = splitQName(this.$base), typeName = type.name, ns = xmlns && xmlns[type.prefix] || definitions.xmlns[type.prefix], schema = definitions.schemas[ns], typeElement = schema && (schema.complexTypes[typeName] || schema.types[typeName] || schema.elements[typeName]); desc.getBase = function () { return typeElement.description(definitions, schema.xmlns); }; return desc; } // then simple element let base = this.$base ? this.$base + "|" : ""; return base + this.children.map(function (child) { return child.description(); }).join(","); }; ExtensionElement.prototype.description = function (definitions, xmlns) { let children = this.children; let desc = {}; for (let i = 0, child; child = children[i]; i++) { if (child instanceof SequenceElement || child instanceof ChoiceElement) { desc = child.description(definitions, xmlns); } } if (this.$base) { let type = splitQName(this.$base), typeName = type.name, ns = xmlns && xmlns[type.prefix] || definitions.xmlns[type.prefix], schema = definitions.schemas[ns]; if (typeName in Primitives) { return this.$base; } else { let typeElement = schema && (schema.complexTypes[typeName] || schema.types[typeName] || schema.elements[typeName]); if (typeElement) { let base = typeElement.description(definitions, schema.xmlns); desc = _.defaultsDeep(base, desc); } } } return desc; }; EnumerationElement.prototype.description = function () { return this[this.valueKey]; }; ComplexTypeElement.prototype.description = function (definitions, xmlns) { let children = this.children || []; for (let i = 0, child; child = children[i]; i++) { if (child instanceof ChoiceElement || child instanceof SequenceElement || child instanceof AllElement || child instanceof SimpleContentElement || child instanceof ComplexContentElement) { return child.description(definitions, xmlns); } } return {}; }; ComplexContentElement.prototype.description = function (definitions, xmlns) { let children = this.children; for (let i = 0, child; child = children[i]; i++) { if (child instanceof ExtensionElement) { return child.description(definitions, xmlns); } } return {}; }; SimpleContentElement.prototype.description = function (definitions, xmlns) { let children = this.children; for (let i = 0, child; child = children[i]; i++) { if (child instanceof ExtensionElement) { return child.description(definitions, xmlns); } } return {}; }; ElementElement.prototype.description = function (definitions, xmlns) { let element = {}, name = this.$name; let isMany = !this.$maxOccurs ? false : (isNaN(this.$maxOccurs) ? (this.$maxOccurs === 'unbounded') : (this.$maxOccurs > 1)); if (this.$minOccurs !== this.$maxOccurs && isMany) { name += '[]'; } if (xmlns && xmlns[TNS_PREFIX]) { this.$targetNamespace = xmlns[TNS_PREFIX]; } let type = this.$type || this.$ref; if (type) { type = splitQName(type); let typeName = type.name, ns = xmlns && xmlns[type.prefix] || definitions.xmlns[type.prefix], schema = definitions.schemas[ns], typeElement = schema && (this.$type ? schema.complexTypes[typeName] || schema.types[typeName] : schema.elements[typeName]); if (ns && definitions.schemas[ns]) { xmlns = definitions.schemas[ns].xmlns; } if (typeElement && !(typeName in Primitives)) { if (!(typeName in definitions.descriptions.types)) { let elem = {}; definitions.descriptions.types[typeName] = elem; let description = typeElement.description(definitions, xmlns); if (typeof description === 'string') { elem = description; } else { Object.keys(description).forEach(function (key) { elem[key] = description[key]; }); } if (this.$ref) { element = elem; } else { element[name] = elem; } if (typeof elem === 'object') { elem.targetNSAlias = type.prefix; elem.targetNamespace = ns; } definitions.descriptions.types[typeName] = elem; } else { if (this.$ref) { element = definitions.descriptions.types[typeName]; } else { element[name] = definitions.descriptions.types[typeName]; } } } else { element[name] = this.$type; } } else { let children = this.children; element[name] = {}; for (let i = 0, child; child = children[i]; i++) { if (child instanceof ComplexTypeElement) { element[name] = child.description(definitions, xmlns); } } } return element; }; AllElement.prototype.description = SequenceElement.prototype.description = function (definitions, xmlns) { let children = this.children; let sequence = {}; for (let i = 0, child; child = children[i]; i++) { if (child instanceof AnyElement) { continue; } let description = child.description(definitions, xmlns); for (let key in description) { sequence[key] = description[key]; } } return sequence; }; ChoiceElement.prototype.description = function (definitions, xmlns) { let children = this.children; let choice = {}; for (let i = 0, child; child = children[i]; i++) { let description = child.description(definitions, xmlns); for (let key in description) { choice[key] = description[key]; } } return choice; }; MessageElement.prototype.description = function (definitions) { if (this.element) { return this.element && this.element.description(definitions); } let desc = {}; desc[this.$name] = this.parts; return desc; }; PortTypeElement.prototype.description = function (definitions) { let methods = {}; for (let name in this.methods) { let method = this.methods[name]; methods[name] = method.description(definitions); } return methods; }; OperationElement.prototype.description = function (definitions) { let inputDesc = this.input ? this.input.description(definitions) : null; let outputDesc = this.output ? this.output.description(definitions) : null; return { input: inputDesc && inputDesc[Object.keys(inputDesc)[0]], output: outputDesc && outputDesc[Object.keys(outputDesc)[0]] }; }; BindingElement.prototype.description = function (definitions) { let methods = {}; for (let name in this.methods) { let method = this.methods[name]; methods[name] = method.description(definitions); } return methods; }; ServiceElement.prototype.description = function (definitions) { let ports = {}; for (let name in this.ports) { let port = this.ports[name]; ports[name] = port.binding.description(definitions); } return ports; }; export let WSDL = function (definition, uri, options) { let self = this, fromFunc; this.uri = uri; this.callback = function () { }; this._includesWsdl = []; // initialize WSDL cache this.WSDL_CACHE = (options || {}).WSDL_CACHE || {}; this._initializeOptions(options); if (typeof definition === 'string') { definition = stripBom(definition); fromFunc = this._fromXML; } else if (typeof definition === 'object') { fromFunc = this._fromServices; } else { throw new Error('WSDL letructor takes either an XML string or service definition'); } Promise.resolve(true).then(() => { try { fromFunc.call(self, definition); } catch (e) { return self.callback(e.message); } self.processIncludes().then(() => { self.definitions.deleteFixedAttrs(); let services = self.services = self.definitions.services; if (services) { for (const name in services) { services[name].postProcess(self.definitions); } } let complexTypes = self.definitions.complexTypes; if (complexTypes) { for (const name in complexTypes) { complexTypes[name].deleteFixedAttrs(); } } // for document style, for every binding, prepare input message element name to (methodName, output message element name) mapping let bindings = self.definitions.bindings; for (let bindingName in bindings) { let binding = bindings[bindingName]; if (typeof binding.style === 'undefined') { binding.style = 'document'; } if (binding.style !== 'document') continue; let methods = binding.methods; let topEls = binding.topElements = {}; for (let methodName in methods) { if (methods[methodName].input) { let inputName = methods[methodName].input.$name; let outputName = ""; if (methods[methodName].output) outputName = methods[methodName].output.$name; topEls[inputName] = { "methodName": methodName, "outputName": outputName }; } } } // prepare soap envelope xmlns definition string self.xmlnsInEnvelope = self._xmlnsMap(); self.callback(null, self); }).catch(err => self.callback(err)); }); // process.nextTick(function() { // try { // fromFunc.call(self, definition); // } catch (e) { // return self.callback(e.message); // } // self.processIncludes(function(err) { // let name; // if (err) { // return self.callback(err); // } // self.definitions.deleteFixedAttrs(); // let services = self.services = self.definitions.services; // if (services) { // for (name in services) { // services[name].postProcess(self.definitions); // } // } // let complexTypes = self.definitions.complexTypes; // if (complexTypes) { // for (name in complexTypes) { // complexTypes[name].deleteFixedAttrs(); // } // } // // for document style, for every binding, prepare input message element name to (methodName, output message element name) mapping // let bindings = self.definitions.bindings; // for (let bindingName in bindings) { // let binding = bindings[bindingName]; // if (typeof binding.style === 'undefined') { // binding.style = 'document'; // } // if (binding.style !== 'document') // continue; // let methods = binding.methods; // let topEls = binding.topElements = {}; // for (let methodName in methods) { // if (methods[methodName].input) { // let inputName = methods[methodName].input.$name; // let outputName=""; // if(methods[methodName].output ) // outputName = methods[methodName].output.$name; // topEls[inputName] = {"methodName": methodName, "outputName": outputName}; // } // } // } // // prepare soap envelope xmlns definition string // self.xmlnsInEnvelope = self._xmlnsMap(); // self.callback(err, self); // }); // }); }; WSDL.prototype.ignoredNamespaces = ['tns', 'targetNamespace', 'typedNamespace']; WSDL.prototype.ignoreBaseNameSpaces = false; WSDL.prototype.valueKey = '$value'; WSDL.prototype.xmlKey = '$xml'; WSDL.prototype._initializeOptions = function (options) { this._originalIgnoredNamespaces = (options || {}).ignoredNamespaces; this.options = {}; let ignoredNamespaces = options ? options.ignoredNamespaces : null; if (ignoredNamespaces && (Array.isArray(ignoredNamespaces.namespaces) || typeof ignoredNamespaces.namespaces === 'string')) { if (ignoredNamespaces.override) { this.options.ignoredNamespaces = ignoredNamespaces.namespaces; } else { this.options.ignoredNamespaces = this.ignoredNamespaces.concat(ignoredNamespaces.namespaces); } } else { this.options.ignoredNamespaces = this.ignoredNamespaces; } this.options.valueKey = options.valueKey || this.valueKey; this.options.xmlKey = options.xmlKey || this.xmlKey; if (options.escapeXML !== undefined) { this.options.escapeXML = options.escapeXML; } else { this.options.escapeXML = true; } if (options.returnFault !== undefined) { this.options.returnFault = options.returnFault; } else { this.options.returnFault = false; } this.options.handleNilAsNull = !!options.handleNilAsNull; if (options.namespaceArrayElements !== undefined) { this.options.namespaceArrayElements = options.namespaceArrayElements; } else { this.options.namespaceArrayElements = true; } // Allow any request headers to keep passing through this.options.wsdl_headers = options.wsdl_headers; this.options.wsdl_options = options.wsdl_options; if (options.httpClient) { this.options.httpClient = options.httpClient; } // The supplied request-object should be passed through if (options.request) { this.options.request = options.request; } let ignoreBaseNameSpaces = options ? options.ignoreBaseNameSpaces : null; if (ignoreBaseNameSpaces !== null && typeof ignoreBaseNameSpaces !== 'undefined') { this.options.ignoreBaseNameSpaces = ignoreBaseNameSpaces; } else { this.options.ignoreBaseNameSpaces = this.ignoreBaseNameSpaces; } // Works only in client this.options.forceSoap12Headers = options.forceSoap12Headers; this.options.customDeserializer = options.customDeserializer; if (options.overrideRootElement !== undefined) { this.options.overrideRootElement = options.overrideRootElement; } this.options.useEmptyTag = !!options.useEmptyTag; }; WSDL.prototype.onReady = function (callback) { if (callback) this.callback = callback; }; WSDL.prototype._processNextInclude = function (includes) { return __awaiter(this, void 0, void 0, function* () { let self = this, include = includes.shift(), options; if (!include) return; // callback(); let includePath; if (!/^https?:/.test(self.uri) && !/^https?:/.test(include.location)) { // includePath = path.resolve(path.dirname(self.uri), include.location); } else { includePath = url.resolve(self.uri || '', include.location); } options = _.assign({}, this.options); // follow supplied ignoredNamespaces option options.ignoredNamespaces = this._originalIgnoredNamespaces || this.options.ignoredNamespaces; options.WSDL_CACHE = this.WSDL_CACHE; const wsdl = yield open_wsdl_recursive(includePath, options); self._includesWsdl.push(wsdl); if (wsdl.definitions instanceof DefinitionsElement) { _.mergeWith(self.definitions, wsdl.definitions, function (a, b) { return (a instanceof SchemaElement) ? a.merge(b) : undefined; }); } else { self.definitions.schemas[include.namespace || wsdl.definitions.$targetNamespace] = deepMerge(self.definitions.schemas[include.namespace || wsdl.definitions.$targetNamespace], wsdl.definitions); } return self._processNextInclude(includes); // open_wsdl_recursive(includePath, options, function(err, wsdl) { // if (err) { // return callback(err); // } // self._includesWsdl.push(wsdl); // if (wsdl.definitions instanceof DefinitionsElement) { // _.mergeWith(self.definitions, wsdl.definitions, function(a,b) { // return (a instanceof SchemaElement) ? a.merge(b) : undefined; // }); // } else { // self.definitions.schemas[include.namespace || wsdl.definitions.$targetNamespace] = deepMerge(self.definitions.schemas[include.namespace || wsdl.definitions.$targetNamespace], wsdl.definitions); // } // self._processNextInclude(includes, function(err) { // callback(err); // }); // }); }); }; WSDL.prototype.processIncludes = function () { return __awaiter(this, void 0, void 0, function* () { let schemas = this.definitions.schemas, includes = []; for (let ns in schemas) { let schema = schemas[ns]; includes = includes.concat(schema.includes || []); } return this._processNextInclude(includes); }); }; WSDL.prototype.describeServices = function () { let services = {}; for (let name in this.services) { let service = this.services[name]; services[name] = service.description(this.definitions); } return services; }; WSDL.prototype.toXML = function () { return this.xml || ''; }; WSDL.prototype.xmlToObject = function (xml, callback) { let self = this; let p = typeof callback === 'function' ? {} : sax.parser(true); let objectName = null; let root = {}; let schema = { Envelope: { Header: { Security: { UsernameToken: { Username: 'string', Password: 'string' } } }, Body: { Fault: { faultcode: 'string', faultstring: 'string', detail: 'string' } } } }; let stack = [{ name: null, object: root, schema: schema }]; let xmlns = {}; let refs = {}, id; // {id:{hrefs:[],obj:}, ...} p.onopentag = function (node) { let nsName = node.name; let attrs = node.attributes; let name = splitQName(nsName).name, attributeName, top = stack[stack.length - 1], topSchema = top.schema, elementAttributes = {}, hasNonXmlnsAttribute = false, hasNilAttribute = false, obj = {}; let originalName = name; if (!objectName && top.name === 'Body' && name !== 'Fault') { let message = self.definitions.messages[name]; // Support RPC/literal messages where response body contains one element named // after the operation + 'Response'. See http://www.w3.org/TR/wsdl#_names if (!message) { try { // Determine if this is request or response let isInput = false; let isOutput = false; if ((/Response$/).test(name)) { isOutput = true; name = name.replace(/Response$/, ''); } else if ((/Request$/).test(name)) { isInput = true; name = name.replace(/Request$/, ''); } else if ((/Solicit$/).test(name)) { isInput = true; name = name.replace(/Solicit$/, ''); } // Look up the appropriate message as given in the portType's operations let portTypes = self.definitions.portTypes; let portTypeNames = Object.keys(portTypes); // Currently this supports only one portType definition. let portType = portTypes[portTypeNames[0]]; if (isInput) { name = portType.methods[name].input.$name; } else { name = portType.methods[name].output.$name; } message = self.definitions.messages[name]; // 'cache' this alias to speed future lookups self.definitions.messages[originalName] = self.definitions.messages[name]; } catch (e) { if (self.options.returnFault) { p.onerror(e); } } } topSchema = message.description(self.definitions); objectName = originalName; } if (attrs.href) { id = attrs.href.substr(1); if (!refs[id]) { refs[id] = { hrefs: [], obj: null }; } refs[id].hrefs.push({ par: top.object, key: name, obj: obj }); } if (id = attrs.id) { if (!refs[id]) { refs[id] = { hrefs: [], obj: null }; } } //Handle element attributes for (attributeName in attrs) { if (/^xmlns:|^xmlns$/.test(attributeName)) { xmlns[splitQName(attributeName).name] = attrs[attributeName]; continue; } hasNonXmlnsAttribute = true; elementAttributes[attributeName] = attrs[attributeName]; } for (attributeName in elementAttributes) { let res = splitQName(attributeName); if (res.name === 'nil' && xmlns[res.prefix] === 'http://www.w3.org/2001/XMLSchema-instance' && elementAttributes[attributeName] && (elementAttributes[attributeName].toLowerCase() === 'true' || elementAttributes[attributeName] === '1')) { hasNilAttribute = true; break; } } if (hasNonXmlnsAttribute) { obj[self.options.attributesKey] = elementAttributes; } // Pick up the schema for the type specified in element's xsi:type attribute. let xsiTypeSchema; let xsiType = elementAttributes['xsi:type']; if (xsiType) { let type = splitQName(xsiType); let typeURI; if (type.prefix === TNS_PREFIX) { // In case of xsi:type = "MyType" typeURI = xmlns[type.prefix] || xmlns.xmlns; } else { typeURI = xmlns[type.prefix]; } let typeDef = self.findSchemaObject(typeURI, type.name); if (typeDef) { xsiTypeSchema = typeDef.description(self.definitions); } } if (topSchema && topSchema[name + '[]']) { name = name + '[]'; } stack.push({ name: originalName, object: obj, schema: (xsiTypeSchema || (topSchema && topSchema[name])), id: attrs.id, nil: hasNilAttribute }); }; p.onclosetag = function (nsName) { let cur = stack.pop(), obj = cur.object, top = stack[stack.length - 1], topObject = top.object, topSchema = top.schema, name = splitQName(nsName).name; if (typeof cur.schema === 'string' && (cur.schema === 'string' || cur.schema.split(':')[1] === 'string')) { if (typeof obj === 'object' && Object.keys(obj).length === 0) obj = cur.object = ''; } if (cur.nil === true) { if (self.options.handleNilAsNull) { obj = null; } else { return; } } if (_.isPlainObject(obj) && !Object.keys(obj).length) { obj = null; } if (topSchema && topSchema[name + '[]']) { if (!topObject[name]) { topObject[name] = []; } topObject[name].push(obj); } else if (name in topObject) { if (!Array.isArray(topObject[name])) { topObject[name] = [topObject[name]]; } topObject[name].push(obj); } else { topObject[name] = obj; } if (cur.id) { refs[cur.id].obj = obj; } }; p.oncdata = function (text) { let originalText = text; text = trim(text); if (!text.length) { return; } if (/<\?xml[\s\S]+\?>/.test(text)) { let top = stack[stack.length - 1]; let value = self.xmlToObject(text); if (top.object[self.options.attributesKey]) { top.object[self.options.valueKey] = value; } else { top.object = value; } } else { p.ontext(originalText); } }; p.onerror = function (e) { p.resume(); throw { Fault: { faultcode: 500, faultstring: 'Invalid XML', detail: new Error(e).message, statusCode: 500 } }; }; p.ontext = function (text) { let originalText = text; text = trim(text); if (!text.length) { return; } let top = stack[stack.length - 1]; let name = splitQName(top.schema).name, value; if (self.options && self.options.customDeserializer && self.options.customDeserializer[name]) { value = self.options.customDeserializer[name](text, top); } else { if (name === 'int' || name === 'integer') { value = parseInt(text, 10); } else if (name === 'bool' || name === 'boolean') { value = text.toLowerCase() === 'true' || text === '1'; } else if (name === 'dateTime' || name === 'dat