UNPKG

jsforce2

Version:

Salesforce API Library for JavaScript

208 lines (193 loc) 5.35 kB
/** * @file Manages method call to SOAP endpoint * @author Shinichi Tomita <shinichi.tomita@gmail.com> */ 'use strict'; var inherits = require('inherits'), _ = require('lodash/core'), xml2js = require('xml2js'), HttpApi = require('./http-api'); /** * Class for SOAP endpoint of Salesforce * * @protected * @class * @constructor * @param {Connection} conn - Connection instance * @param {Object} options - SOAP endpoint setting options * @param {String} options.endpointUrl - SOAP endpoint URL * @param {String} [options.xmlns] - XML namespace for method call (default is "urn:partner.soap.sforce.com") */ var SOAP = module.exports = function(conn, options) { SOAP.super_.apply(this, arguments); this._endpointUrl = options.endpointUrl; this._xmlns = options.xmlns || 'urn:partner.soap.sforce.com'; }; inherits(SOAP, HttpApi); /** * Invoke SOAP call using method and arguments * * @param {String} method - Method name * @param {Object} args - Arguments for the method call * @param {Object} [schema] - Schema definition of response message * @param {Callback.<Object>} [callback] - Callback function * @returns {Promise.<Object>} */ SOAP.prototype.invoke = function(method, args, schema, callback) { if (typeof schema === 'function') { callback = schema; schema = null; } var message = {}; message[method] = args; return this.request({ method: 'POST', url: this._endpointUrl, headers: { 'Content-Type': 'text/xml', 'SOAPAction': '""' }, message: message }).then(function(res) { return schema ? convertType(res, schema) : res; }).thenCall(callback); }; /* @private */ function convertType(value, schema) { if (_.isArray(value)) { return value.map(function(v) { return convertType(v, schema && schema[0]) }); } else if (_.isObject(value)) { if (value.$ && value.$['xsi:nil'] === 'true') { return null; } else if (_.isArray(schema)) { return [ convertType(value, schema[0]) ]; } else { var o = {}; for (var key in value) { o[key] = convertType(value[key], schema && schema[key]); } return o; } } else { if (_.isArray(schema)) { return [ convertType(value, schema[0]) ]; } else if (_.isObject(schema)) { return {}; } else { switch(schema) { case 'string': return String(value); case 'number': return Number(value); case 'boolean': return value === 'true'; default: return value; } } } } /** @override **/ SOAP.prototype.beforeSend = function(request) { request.body = this._createEnvelope(request.message); }; /** @override **/ SOAP.prototype.isSessionExpired = function(response) { return response.statusCode === 500 && /<faultcode>[a-zA-Z]+:INVALID_SESSION_ID<\/faultcode>/.test(response.body); }; /** @override **/ SOAP.prototype.parseError = function(body) { var error = lookupValue(body, [ /:Envelope$/, /:Body$/, /:Fault$/ ]); return { errorCode: error.faultcode, message: error.faultstring }; }; /** @override **/ SOAP.prototype.getResponseBody = function(response) { var body = SOAP.super_.prototype.getResponseBody.call(this, response); return lookupValue(body, [ /:Envelope$/, /:Body$/, /.+/ ]); }; /** * @private */ function lookupValue(obj, propRegExps) { var regexp = propRegExps.shift(); if (!regexp) { return obj; } else { for (var prop in obj) { if (regexp.test(prop)) { return lookupValue(obj[prop], propRegExps); } } return null; } } /** * @private */ function toXML(name, value) { if (_.isObject(name)) { value = name; name = null; } if (_.isArray(value)) { return _.map(value, function(v) { return toXML(name, v); }).join(''); } else { var attrs = []; var elems = []; if (_.isObject(value)) { for (var k in value) { var v = value[k]; if (k[0] === '@') { k = k.substring(1); attrs.push(k + '="' + v + '"'); } else { elems.push(toXML(k, v)); } } value = elems.join(''); } else { value = String(value) .replace(/&/g, '&amp;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(/"/g, '&quot;') .replace(/'/g, '&apos;'); } var startTag = name ? '<' + name + (attrs.length > 0 ? ' ' + attrs.join(' ') : '') + '>' : ''; var endTag = name ? '</' + name + '>' : ''; return startTag + value + endTag; } } /** * @private */ SOAP.prototype._createEnvelope = function(message) { var header = {}; var conn = this._conn; if (conn.accessToken) { header.SessionHeader = { sessionId: this._conn.accessToken }; } if (conn.callOptions) { header.CallOptions = conn.callOptions; } return [ '<?xml version="1.0" encoding="UTF-8"?>', '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"', ' xmlns:xsd="http://www.w3.org/2001/XMLSchema"', ' xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">', '<soapenv:Header xmlns="' + this._xmlns + '">', toXML(header), '</soapenv:Header>', '<soapenv:Body xmlns="' + this._xmlns + '">', toXML(message), '</soapenv:Body>', '</soapenv:Envelope>' ].join(''); };