UNPKG

strong-soap

Version:
904 lines (888 loc) 33.2 kB
// Copyright IBM Corp. 2016,2019. All Rights Reserved. // Node module: strong-soap // This file is licensed under the MIT License. // License text available at https://opensource.org/licenses/MIT 'use strict'; function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; } function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } } function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; } var xmlBuilder = require('xmlbuilder'); var sax = require('sax'); var stream = require('stream'); var assert = require('assert'); var selectn = require('selectn'); var debug = require('debug')('strong-soap:xmlhandler'); var descriptor = require('./xsd/descriptor'); var ElementDescriptor = descriptor.ElementDescriptor; var AttributeDescriptor = descriptor.AttributeDescriptor; var TypeDescriptor = descriptor.TypeDescriptor; var QName = require('./qname'); var helper = require('./helper'); var NamespaceContext = require('./nscontext'); class XMLHandler { /** * @param {Object} [schemas] * @param {Object} [options] * @param {boolean} [options.enforceRestrictions] * @param {string} [options.valueKey] * @param {string} [options.xmlKey] * @param {string} [options.attributesKey] * @param {string} [options.xsiTypeKey] * @param {Object} [options.date] * @param {Object} [options.date.timezone] * @param {boolean} [options.date.timezone.enabled] */ constructor(schemas, options) { this.schemas = schemas || {}; this.options = options || {}; this.options.valueKey = this.options.valueKey || '$value'; this.options.xmlKey = this.options.xmlKey || '$xml'; this.options.attributesKey = this.options.attributesKey || '$attributes'; this.options.xsiTypeKey = this.options.xsiTypeKey || '$xsiType'; this.options.date = this.options.date || {}; this.options.date.timezone = this.options.date.timezone || {}; this.options.date.timezone.enabled = typeof this.options.date.timezone.enabled === 'boolean' ? this.options.date.timezone.enabled : true; } jsonToXml(node, nsContext, descriptor, val) { if (node == null) { node = xmlBuilder.begin({ version: '1.0', encoding: 'UTF-8', standalone: true }); } if (nsContext == null) { nsContext = new NamespaceContext(); } var name; let nameSpaceContextCreated = false; if (descriptor instanceof AttributeDescriptor) { val = toXmlDateOrTime(descriptor, val, this.options.date); name = descriptor.qname.name; if (descriptor.form === 'unqualified') { node.attribute(name, val); } else if (descriptor.qname) { let mapping = declareNamespace(nsContext, node, descriptor.qname.prefix, descriptor.qname.nsURI); let prefix = mapping ? mapping.prefix : descriptor.qname.prefix; let attrName = prefix ? prefix + ':' + name : name; node.attribute(attrName, val); } return node; } if (descriptor instanceof ElementDescriptor) { name = descriptor.qname.name; let isSimple = descriptor.isSimple; let attrs = null; if (descriptor.isMany) { if (Array.isArray(val)) { for (let i = 0, n = val.length; i < n; i++) { node = this.jsonToXml(node, nsContext, descriptor, val[i]); } return node; } } if (val !== null && typeof val === "object") { // check for $attributes field if (typeof val[this.options.attributesKey] !== "undefined") { attrs = val[this.options.attributesKey]; } // add any $value field as xml element value if (typeof val[this.options.valueKey] !== "undefined") { val = val[this.options.valueKey]; } } let element; let elementName; let xmlns; if (descriptor.form === 'unqualified') { elementName = name; nsContext.pushContext(); nameSpaceContextCreated = true; } else if (descriptor.qname) { nsContext.pushContext(); nameSpaceContextCreated = true; // get the mapping for the namespace uri let mapping = nsContext.getPrefixMapping(descriptor.qname.nsURI); let newlyDeclared = false; // if namespace not declared, declare it if (mapping === null || mapping.declared === false) { newlyDeclared = true; mapping = declareNamespace(nsContext, null, descriptor.qname.prefix, descriptor.qname.nsURI); } // add the element to a parent node let prefix = mapping ? mapping.prefix : descriptor.qname.prefix; elementName = prefix ? prefix + ':' + name : name; // if namespace is newly declared add the xmlns attribute if (newlyDeclared) { xmlns = prefix ? 'xmlns:' + prefix : 'xmlns'; } } // add the element to a parent node if (isSimple && /<!\[CDATA/.test(val)) { element = node.element(elementName); val = val.replace("<![CDATA[", ""); val = val.replace("]]>", ""); element.cdata(val); } else if (isSimple && typeof val !== "undefined" && val !== null && typeof val[this.options.xmlKey] !== "undefined") { val = val[this.options.xmlKey]; element = node.element(elementName); val = toXmlDateOrTime(descriptor, val, this.options.date); element.raw(val); } else { // Enforce the type restrictions if configured for such if (this.options.enforceRestrictions && descriptor.type) { const schema = this.schemas[descriptor.type.nsURI]; if (schema) { let type = schema.simpleTypes[descriptor.type.name]; // if type not available in global schema // , check if it is embedded in descriptor as an anonymous type if (!type && descriptor.type.anonymous) { type = descriptor.type.anonymous; } if (type) { const restriction = type.restriction; if (restriction) { val = restriction.enforce(val); } } } } val = toXmlDateOrTime(descriptor, val, this.options.date); element = isSimple ? node.element(elementName, val) : node.element(elementName); } if (xmlns && descriptor.qname.nsURI) { if (typeof element.attribute === 'function') { element.attribute(xmlns, descriptor.qname.nsURI); } } if (val == null) { if (descriptor.isNillable) { // Set xsi:nil = true declareNamespace(nsContext, element, 'xsi', helper.namespaces.xsi); if (typeof element.attribute === 'function') { element.attribute('xsi:nil', true); } } } if (isSimple) { if (attrs !== null) { // add each field in $attributes object as xml element attribute if (typeof attrs === "object") { //add $attributes. Attribute can be an attribute defined in XSD or an xsi:type this.addAttributes(element, nsContext, descriptor, val, attrs); } } if (nameSpaceContextCreated) { nsContext.popContext(); } return node; } else if (val != null) { let attrs = val[this.options.attributesKey]; if (typeof attrs === 'object') { for (let p in attrs) { let child = attrs[p]; if (p === this.options.xsiTypeKey) { if (descriptor instanceof ElementDescriptor) { if (descriptor.refOriginal) { if (descriptor.refOriginal.typeDescriptor) { if (descriptor.refOriginal.typeDescriptor.inheritance) { let extension = descriptor.refOriginal.typeDescriptor.inheritance[child.type]; if (extension) { descriptor.elements = descriptor.elements.concat(extension.elements); } } } } } } } } } //val is not an object - simple or date types if (val != null && (typeof val !== 'object' || val instanceof Date)) { // for adding a field value nsContext.popContext() shouldnt be called val = toXmlDateOrTime(descriptor, val, this.options.date); element.text(val); //add $attributes. Attribute can be an attribute defined in XSD or an xsi:type. //e.g of xsi:type <name xmlns=".." xmlns:xsi="..." xmlns:ns="..." xsi:type="ns:string">some name</name> if (attrs != null) { this.addAttributes(element, nsContext, descriptor, val, attrs); } if (nameSpaceContextCreated) { nsContext.popContext(); } return node; } this.mapObject(element, nsContext, descriptor, val, attrs); if (nameSpaceContextCreated) { nsContext.popContext(); } return node; } if (descriptor == null || descriptor === undefined || descriptor instanceof TypeDescriptor) { this.mapObject(node, nsContext, descriptor, val); return node; } return node; } /** * Check if the attributes have xsi:type and return the xsi type descriptor if exists * @param {*} descriptor The current descriptor * @param {*} attrs An object of attribute values */ getXsiType(descriptor, attrs) { var xsiTypeDescriptor; if (attrs != null && typeof attrs === "object") { for (let p in attrs) { let child = attrs[p]; // if field is $xsiType add xsi:type attribute if (p === this.options.xsiTypeKey) { let xsiType; if (typeof child === "object" && typeof child.type !== "undefined") { // $xsiType has two fields - type, xmlns xsiType = QName.parse(child.type, child.xmlns); } else { xsiType = QName.parse(child); } var schema = this.schemas[xsiType.nsURI]; if (schema) { var xsiTypeInfo = schema.complexTypes[xsiType.name] || schema.simpleTypes[xsiType.name]; // The type might not be described // describe() takes wsdl definitions xsiTypeDescriptor = xsiTypeInfo && xsiTypeInfo.describe({ schemas: this.schemas }); } break; } } } return xsiTypeDescriptor; } _sortKeys(val, elementOrder) { function compare(n1, n2, order) { let i1 = order.indexOf(n1); if (i1 === -1) i1 = order.length; let i2 = order.indexOf(n2); if (i2 === -1) i2 = order.length; return i1 - i2; } const keys = Object.keys(val); var names = [].concat(keys).sort((n1, n2) => { let result = compare(n1, n2, elementOrder); if (result === 0) { result = compare(n1, n2, keys); } return result; }); return names; } /** * Map a JSON object into an XML type * @param {XMLElement} node The root node * @param {NamespaceContext} nsContext Namespace context * @param {TypeDescriptor|ElementDescriptor} descriptor * @param {Object} val * @returns {*} */ mapObject(node, nsContext, descriptor, val, attrs) { if (val == null) return node; if (typeof val !== 'object' || val instanceof Date) { val = toXmlDateOrTime(descriptor, val, this.options.date); node.text(val); return node; } // First try to see if a subtype should be used var xsiType = this.getXsiType(descriptor, attrs); descriptor = xsiType || descriptor; var elements = {}, attributes = {}; var elementOrder = []; if (descriptor != null) { for (let i = 0, n = descriptor.elements.length; i < n; i++) { let elementDescriptor = descriptor.elements[i]; let elementName = elementDescriptor.qname.name; elements[elementName] = elementDescriptor; elementOrder.push(elementName); } } if (descriptor != null) { for (let a in descriptor.attributes) { let attributeDescriptor = descriptor.attributes[a]; let attributeName = attributeDescriptor.qname.name; attributes[attributeName] = attributeDescriptor; } } // handle later if value is an array if (!Array.isArray(val)) { const names = this._sortKeys(val, elementOrder); var _iterator = _createForOfIteratorHelper(names), _step; try { for (_iterator.s(); !(_step = _iterator.n()).done;) { let p = _step.value; if (p === this.options.attributesKey) continue; let child = val[p]; let childDescriptor = elements[p] || attributes[p]; if (childDescriptor == null) { if (this.options.ignoreUnknownProperties) continue;else childDescriptor = new ElementDescriptor(QName.parse(p), null, 'unqualified', Array.isArray(child)); } if (childDescriptor) { this.jsonToXml(node, nsContext, childDescriptor, child); } } } catch (err) { _iterator.e(err); } finally { _iterator.f(); } } this.addAttributes(node, nsContext, descriptor, val, attrs); return node; } addAttributes(node, nsContext, descriptor, val, attrs) { var attrDescriptors = descriptor && descriptor.attributes || []; var attributes = {}; for (var i = 0; i < attrDescriptors.length; i++) { var qname = attrDescriptors[i].qname; attributes[qname.name] = attrDescriptors[i]; } if (attrs != null && typeof attrs === 'object') { for (let p in attrs) { let child = attrs[p]; // if field is $xsiType add xsi:type attribute if (p === this.options.xsiTypeKey) { let xsiType; if (typeof child === 'object' && typeof child.type !== 'undefined') { // $xsiType has two fields - type, xmlns xsiType = QName.parse(child.type, child.xmlns); } else { xsiType = QName.parse(child); } declareNamespace(nsContext, node, 'xsi', helper.namespaces.xsi); let mapping = declareNamespace(nsContext, node, xsiType.prefix, xsiType.nsURI); let prefix = mapping ? mapping.prefix : xsiType.prefix; node.attribute('xsi:type', prefix ? prefix + ':' + xsiType.name : xsiType.name); continue; } let childDescriptor = attributes[p]; if (childDescriptor == null) { if (this.options.ignoreUnknownProperties) continue;else { childDescriptor = new AttributeDescriptor(QName.parse(p), null, 'unqualified'); } } this.jsonToXml(node, nsContext, childDescriptor, child); } } } static createSOAPEnvelope(prefix, nsURI) { prefix = prefix || 'soap'; var doc = xmlBuilder.create(prefix + ':Envelope', { version: '1.0', encoding: 'UTF-8', standalone: true }); nsURI = nsURI || 'http://schemas.xmlsoap.org/soap/envelope/'; doc.attribute('xmlns:' + prefix, nsURI); let header = doc.element(prefix + ':Header'); let body = doc.element(prefix + ':Body'); return { body: body, header: header, doc: doc }; } static createSOAPEnvelopeDescriptor(prefix, nsURI, parameterDescriptor) { prefix = prefix || 'soap'; nsURI = nsURI || 'http://schemas.xmlsoap.org/soap/envelope/'; var descriptor = new TypeDescriptor(); var envelopeDescriptor = new ElementDescriptor(new QName(nsURI, 'Envelope', prefix), null, 'qualified', false); descriptor.addElement(envelopeDescriptor); var headerDescriptor = new ElementDescriptor(new QName(nsURI, 'Header', prefix), null, 'qualified', false); var bodyDescriptor = new ElementDescriptor(new QName(nsURI, 'Body', prefix), null, 'qualified', false); envelopeDescriptor.addElement(headerDescriptor); envelopeDescriptor.addElement(bodyDescriptor); if (parameterDescriptor && parameterDescriptor.body) { bodyDescriptor.add(parameterDescriptor.body); } if (parameterDescriptor && parameterDescriptor.headers) { bodyDescriptor.add(parameterDescriptor.headers); } if (parameterDescriptor && parameterDescriptor.faults) { var xsdStr = new QName(helper.namespaces.xsd, 'string', 'xsd'); var faultDescriptor = new ElementDescriptor(new QName(nsURI, 'Fault', prefix), null, 'qualified', false); faultDescriptor.addElement(new ElementDescriptor(nsURI, 'faultcode', xsdStr, 'qualified', false)); faultDescriptor.addElement(new ElementDescriptor(nsURI, 'faultstring', xsdStr, 'qualified', false)); faultDescriptor.addElement(new ElementDescriptor(nsURI, 'faultactor', xsdStr, 'qualified', false)); var detailDescriptor = new ElementDescriptor(nsURI, 'detail', null, 'qualified', false); faultDescriptor.addElement(detailDescriptor); for (var f in parameterDescriptor.faults) { detailDescriptor.add(parameterDescriptor.faults[f]); } } return descriptor; } /** * Parse XML string or stream into the XMLBuilder tree * @param root The root node * @param xml XML string or stream * @param cb * @returns {*} */ static parseXml(root, xml, cb) { let parser; let stringMode = true; debug('XMLHandler parseXML. root: %j xml: %j', root, xml); if (typeof xml === 'string') { stringMode = true; parser = sax.parser(true, { opt: { xmlns: true } }); } else if (xml instanceof stream.Readable) { stringMode = false; parser = sax.createStream(true, { opt: { xmlns: true } }); } if (!root) { root = xmlBuilder.begin(); } let current = root; let stack = [root]; parser.onerror = function (e) { // an error happened. if (cb) process.nextTick(cb); }; parser.ontext = function (text) { // got some text. t is the string of text. if (current.isDocument) return; text = text.trim(); if (text) { current.text(text); } }; parser.oncdata = function (text) { if (current.isDocument) return; text = text.trim(); if (text) { current.cdata(text); } }; parser.onopentag = function (node) { // opened a tag. node has "name" and "attributes" let element = current.element(node.name); if (node.attributes) { element.attribute(node.attributes); } stack.push(element); current = element; }; parser.onclosetag = function (nsName) { var top = stack.pop(); assert(top === current); assert(top.name === nsName); current = stack[stack.length - 1]; }; parser.onend = function () { if (cb) process.nextTick(function () { // parser stream is done, and ready to have more stuff written to it. cb && cb(null, root); }); }; if (stringMode) { parser.write(xml).close(); } else { xml.pipe(parser); } return root; } _processText(top, val) { // The parent element has xsi:nil = true if (top.object === null) return; // Top object has no other elements or attributes if (top.object === undefined) { top.object = val; } else if (top.object.constructor === Object) { // Top object already has attributes or elements let value = top.object[this.options.valueKey]; if (value !== undefined) { top.object[this.options.valueKey] = value + val; } else { top.object[this.options.valueKey] = val; } } else { // Top object is other simple types, such as string or date top.object = top.object + val; } } xmlToJson(nsContext, xml, descriptor) { var self = this; var p = sax.parser(true); nsContext = nsContext || new NamespaceContext(); var root = {}; var refs = {}, id; // {id: {hrefs:[], obj:}, ...} var stack = [{ name: null, object: root, descriptor: descriptor }]; var options = this.options; p.onopentag = function (node) { nsContext.pushContext(); var top = stack[stack.length - 1]; var descriptor = top.descriptor; var nsName = node.name; var attrs = node.attributes; var obj = undefined; var elementAttributes = null; // Register namespaces 1st for (let a in attrs) { if (/^xmlns:|^xmlns$/.test(a)) { let prefix = a === 'xmlns' ? '' : a.substring(6); nsContext.addNamespace(prefix, attrs[a]); } } // Handle regular attributes for (let a in attrs) { if (/^xmlns:|^xmlns$/.test(a)) continue; let qname = QName.parse(a); var isXsiType = false; var xsiType = null; var xsiXmlns = null; if (nsContext.getNamespaceURI(qname.prefix) === helper.namespaces.xsi) { // Handle xsi:* if (qname.name == 'nil') { // xsi:nil if (attrs[a] === 'true') { obj = null; } continue; } else if (qname.name === 'type') { // xsi:type isXsiType = true; xsiType = attrs[a]; xsiType = QName.parse(xsiType); attrs[a] = xsiType.name; if (xsiType.prefix) { xsiXmlns = nsContext.getNamespaceURI(xsiType.prefix); } } } let attrName = qname.name; elementAttributes = elementAttributes || {}; let attrDescriptor = descriptor && descriptor.findAttribute(qname.name); let attrValue = parseValue(attrs[a], attrDescriptor); // if element attribute is xsi:type add $xsiType field if (isXsiType) { // $xsiType object has two fields - type and xmlns xsiType = {}; xsiType.type = attrs[a]; xsiType.xmlns = xsiXmlns; elementAttributes[options.xsiTypeKey] = xsiType; } else { elementAttributes[attrName] = attrs[a]; } } if (elementAttributes) { obj = {}; obj[self.options.attributesKey] = elementAttributes; } var elementQName = QName.parse(nsName); elementQName.nsURI = nsContext.getNamespaceURI(elementQName.prefix); // SOAP href (#id) if (attrs.href != null) { id = attrs.href.substr(1); if (refs[id] === undefined) { refs[id] = { hrefs: [], object: null }; } refs[id].hrefs.push({ parent: top.object, key: elementQName.name, object: obj }); } id = attrs.id; if (id != null) { if (refs[id] === undefined) refs[id] = { hrefs: [], object: null }; } stack.push({ name: elementQName.name, object: obj, descriptor: descriptor && descriptor.findElement(elementQName.name), id: attrs.id }); }; p.onclosetag = function (nsName) { var elementName = QName.parse(nsName).name; nsContext.popContext(); var current = stack.pop(); var top = stack[stack.length - 1]; if (top.object === undefined) { top.object = {}; } if (top.object !== null) { if (typeof top.object === 'object' && elementName in top.object) { // The element exist already, let's create an array let val = top.object[elementName]; if (Array.isArray(val)) { // Add to the existing array val.push(current.object); } else { // Convert the element value to an array top.object[elementName] = [val, current.object]; } } else { if (current.descriptor && current.descriptor.isMany) { top.object[elementName] = [current.object]; } else { top.object[elementName] = current.object; } } } if (current.id != null) { refs[current.id].object = current.object; } }; p.oncdata = function (text) { text = text && text.trim(); if (!text.length) return; if (/<\?xml[\s\S]+\?>/.test(text)) { text = self.xmlToJson(null, text); } // add contents of CDATA to the xml output as a text p.handleJsonObject(text); }; p.handleJsonObject = function (text) { var top = stack[stack.length - 1]; self._processText(top, text); }; p.ontext = function (text) { text = text && text.trim(); if (!text.length) return; var top = stack[stack.length - 1]; var descriptor = top.descriptor; var value = parseValue(text, descriptor); self._processText(top, value); }; p.write(xml).close(); // merge obj with href var merge = function merge(href, obj) { for (var j in obj) { if (obj.hasOwnProperty(j)) { href.object[j] = obj[j]; } } }; // MultiRef support: merge objects instead of replacing for (let n in refs) { var ref = refs[n]; for (var i = 0; i < ref.hrefs.length; i++) { merge(ref.hrefs[i], ref.object); } } if (root.Envelope) { var body = root.Envelope.Body; if (root.Envelope.Body !== undefined && root.Envelope.Body !== null) { if (body.Fault !== undefined && body.Fault !== null) { //check if fault is soap 1.1 fault var errorMessage = getSoap11FaultErrorMessage(body.Fault); //check if fault is soap 1.2 fault if (errorMessage == null) { errorMessage = getSoap12FaultErrorMessage(body.Fault); } //couldn't process error message for neither soap 1.1 nor soap 1.2 fault. Nothing else can be done at this point. Send a generic error message. if (errorMessage == null) { errorMessage = 'Error occurred processing Fault response.'; } var error = new Error(errorMessage); error.root = root; throw error; } } return root.Envelope; } return root; } } function getSoap11FaultErrorMessage(faultBody) { var errorMessage = null; var faultcode = selectn('faultcode.$value', faultBody) || selectn('faultcode', faultBody); if (faultcode) { //soap 1.1 fault errorMessage = ' '; //All of the soap 1.1 fault elements should contain string value except detail element which may be a complex type or plain text (string) if (typeof faultcode == 'string') { errorMessage = 'faultcode: ' + faultcode; } var faultstring = selectn('faultstring.$value', faultBody) || selectn('faultstring', faultBody); if (faultstring && typeof faultstring == 'string') { errorMessage = errorMessage + ' faultstring: ' + faultstring; } var faultactor = selectn('faultactor.$value', faultBody) || selectn('faultactor', faultBody); if (faultactor && typeof faultactor == 'string') { errorMessage = errorMessage + ' faultactor: ' + faultactor; } var detail = selectn('detail.$value', faultBody) || selectn('detail', faultBody); if (detail != null) { if (typeof detail == 'string') { //plain text errorMessage = errorMessage + ' detail: ' + detail; } else { //XML type defined in wsdl errorMessage = errorMessage + ' detail: ' + JSON.stringify(detail); } } } return errorMessage; } function getSoap12FaultErrorMessage(faultBody) { var errorMessage = null; let code = selectn('Code', faultBody) || selectn('Code', faultBody); if (code) { //soap 1.2 fault elements have child elements. Hence use JSON.stringify to formulate the error message. errorMessage = ' '; errorMessage = errorMessage + 'Code: ' + JSON.stringify(code); var value = selectn('Value.$value', faultBody) || selectn('Value', faultBody); if (value) { errorMessage = errorMessage + ' ' + 'Value: ' + JSON.stringify(value); } var subCode = selectn('Subcode.$value', faultBody) || selectn('Subcode', faultBody); if (subCode) { errorMessage = errorMessage + ' ' + 'Subcode: ' + JSON.stringify(subCode); } var reason = selectn('reason.$value', faultBody) || selectn('Reason', faultBody); if (reason) { errorMessage = errorMessage + ' ' + 'Reason: ' + JSON.stringify(reason); } var node = selectn('Node.$value', faultBody) || selectn('Node', faultBody); if (node) { errorMessage = errorMessage + ' ' + 'Node: ' + JSON.stringify(node); } var role = selectn('Role.$value', faultBody) || selectn('Role', faultBody); if (role) { errorMessage = errorMessage + ' ' + 'Role: ' + JSON.stringify(role); } var detail = selectn('Detail.$value', faultBody) || selectn('Detail', faultBody); if (detail != null) { if (typeof detail == 'string') { //plain text errorMessage = errorMessage + ' Detail: ' + detail; } else { //XML type defined in wsdl errorMessage = errorMessage + ' Detail: ' + JSON.stringify(detail); } } } return errorMessage; } function declareNamespace(nsContext, node, prefix, nsURI) { var mapping = nsContext.declareNamespace(prefix, nsURI); if (!mapping) { return false; } else if (node) { if (typeof node.attribute === 'function') { // Some types of node such as XMLDummy does not allow attribute node.attribute('xmlns:' + mapping.prefix, mapping.uri); } return mapping; } return mapping; } function parseValue(text, descriptor) { if (typeof text !== 'string') return text; var value = text; var jsType = descriptor && descriptor.jsType; if (jsType === Date) { var dateText = text; // Checks for xs:date with tz, drops the tz // because xs:date doesn't have a time to offset // and JS Date object doesn't store an arbitrary tz if (dateText.length === 16) { dateText = text.substr(0, 10); } value = new Date(dateText); } else if (jsType === Boolean) { if (text === 'true' || text === '1') { value = true; } else { value = false; } } else if (typeof jsType === 'function') { value = jsType(text); } return value; } /** * * @param {string | Date} date * @param {Object} [options] * @param {Object} [options.timezone] * @param {boolean} [options.timezone.enabled] * @returns */ function toXmlDate(date, options) { try { const isoStr = new Date(date).toISOString(); const formattedDate = isoStr.split('T')[0]; const withTimezone = options && options.timezone && typeof options.timezone.enabled === 'boolean' ? options.timezone.enabled : true; if (!withTimezone) { return formattedDate; } return formattedDate + 'Z'; } catch (err) { return date; } } function toXmlTime(date) { try { const isoStr = new Date(date).toISOString(); return isoStr.split('T')[1]; } catch (err) { return date; } } function toXmlDateTime(date) { try { return new Date(date).toISOString(); } catch (err) { return date; } } /** * * @param {object} descriptor * @param {* | null} val * @param {Object} options * @param {Object} options.timezone * @param {boolean} options.timezone.enabled * @returns {string} */ function toXmlDateOrTime(descriptor, val, options) { if (!descriptor || !descriptor.type || val === null) return val; if (descriptor.type.name === 'date') { return toXmlDate(val, options); } else if (descriptor.type.name === 'time') { return toXmlTime(val); } else if (descriptor.type.name === 'dateTime') { return toXmlDateTime(val); } return val; } module.exports = XMLHandler; // Exported function for testing module.exports.parseValue = parseValue; module.exports.toXmlDate = toXmlDate; module.exports.toXmlTime = toXmlTime; module.exports.toXmlDateTime = toXmlDateTime; //# sourceMappingURL=xmlHandler.js.map