@jsforce/jsforce-node
Version:
Salesforce API Library for JavaScript
262 lines (261 loc) • 8.91 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SOAP = exports.castTypeUsingSchema = void 0;
/**
* @file Manages method call to SOAP endpoint
* @author Shinichi Tomita <shinichi.tomita@gmail.com>
*/
const http_api_1 = __importDefault(require("./http-api"));
const function_1 = require("./util/function");
const get_body_size_1 = require("./util/get-body-size");
/**
*
*/
function getPropsSchema(schema, schemaDict) {
if (schema.extends && schemaDict[schema.extends]) {
const extendSchema = schemaDict[schema.extends];
return {
...getPropsSchema(extendSchema, schemaDict),
...schema.props,
};
}
return schema.props;
}
function isNillValue(value) {
return (value == null ||
((0, function_1.isMapObject)(value) &&
(0, function_1.isMapObject)(value.$) &&
value.$['xsi:nil'] === 'true'));
}
/**
*
*/
function castTypeUsingSchema(value, schema, schemaDict = {}) {
if (Array.isArray(schema)) {
const nillable = schema.length === 2 && schema[0] === '?';
const schema_ = nillable ? schema[1] : schema[0];
if (value == null) {
return nillable ? null : [];
}
return (Array.isArray(value) ? value : [value]).map((v) => castTypeUsingSchema(v, schema_, schemaDict));
}
else if ((0, function_1.isMapObject)(schema)) {
// if schema is Schema Definition, not schema element
if ('type' in schema && 'props' in schema && (0, function_1.isMapObject)(schema.props)) {
const props = getPropsSchema(schema, schemaDict);
return castTypeUsingSchema(value, props, schemaDict);
}
const nillable = '?' in schema;
const schema_ = '?' in schema ? schema['?'] : schema;
if (nillable && isNillValue(value)) {
return null;
}
const obj = (0, function_1.isMapObject)(value) ? value : {};
return Object.keys(schema_).reduce((o, k) => {
const s = schema_[k];
const v = obj[k];
const nillable = (Array.isArray(s) && s.length === 2 && s[0] === '?') ||
((0, function_1.isMapObject)(s) && '?' in s) ||
(typeof s === 'string' && s.startsWith('?'));
if (typeof v === 'undefined' && nillable) {
return o;
}
return {
...o,
[k]: castTypeUsingSchema(v, s, schemaDict),
};
}, obj);
}
else {
const nillable = typeof schema === 'string' && schema.startsWith('?');
const type = typeof schema === 'string'
? nillable
? schema.substring(1)
: schema
: 'any';
switch (type) {
case 'string':
return isNillValue(value) ? (nillable ? null : '') : String(value);
case 'number':
return isNillValue(value) ? (nillable ? null : 0) : Number(value);
case 'boolean':
return isNillValue(value)
? nillable
? null
: false
: value === 'true';
case 'null':
return null;
default: {
if (schemaDict[type]) {
const cvalue = castTypeUsingSchema(value, schemaDict[type], schemaDict);
const isEmpty = (0, function_1.isMapObject)(cvalue) && Object.keys(cvalue).length === 0;
return isEmpty && nillable ? null : cvalue;
}
return value;
}
}
}
}
exports.castTypeUsingSchema = castTypeUsingSchema;
/**
* @private
*/
function lookupValue(obj, propRegExps) {
const regexp = propRegExps.shift();
if (!regexp) {
return obj;
}
if ((0, function_1.isMapObject)(obj)) {
for (const prop of Object.keys(obj)) {
if (regexp.test(prop)) {
return lookupValue(obj[prop], propRegExps);
}
}
return null;
}
}
/**
* @private
*/
function toXML(name, value) {
if ((0, function_1.isObject)(name)) {
value = name;
name = null;
}
if (Array.isArray(value)) {
return value.map((v) => toXML(name, v)).join('');
}
else {
const attrs = [];
if (value === null) {
attrs.push('xsi:nil="true"');
value = '';
}
else if ((0, function_1.isMapObject)(value)) {
const elems = [];
for (const k of Object.keys(value)) {
const v = value[k];
if (k.startsWith('@')) {
const kk = k.substring(1);
attrs.push(`${kk}="${v}"`);
}
else {
elems.push(toXML(k, v));
}
}
value = elems.join('');
}
else {
value = String(value)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
const startTag = name
? '<' + name + (attrs.length > 0 ? ' ' + attrs.join(' ') : '') + '>'
: '';
const endTag = name ? '</' + name + '>' : '';
return startTag + value + endTag;
}
}
/**
* 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")
*/
class SOAP extends http_api_1.default {
_endpointUrl;
_xmlns;
constructor(conn, options) {
super(conn, options);
this._endpointUrl = options.endpointUrl;
this._xmlns = options.xmlns || 'urn:partner.soap.sforce.com';
}
/**
* Invoke SOAP call using method and arguments
*/
async invoke(method, args, schema, schemaDict) {
const res = await this.request({
method: 'POST',
url: this._endpointUrl,
headers: {
'Content-Type': 'text/xml',
SOAPAction: '""',
},
_message: { [method]: args },
});
return schema ? castTypeUsingSchema(res, schema, schemaDict) : res;
}
/** @override */
beforeSend(request) {
request.body = this._createEnvelope(request._message);
const headers = request.headers || {};
const bodySize = (0, get_body_size_1.getBodySize)(request.body, request.headers);
if (request.method === 'POST' &&
!('transfer-encoding' in headers) &&
!('content-length' in headers) &&
!!bodySize) {
this._logger.debug(`missing 'content-length' header, setting it to: ${bodySize}`);
headers['content-length'] = String(bodySize);
}
request.headers = headers;
}
/** @override **/
isSessionExpired(response) {
return (response.statusCode === 500 &&
/<faultcode>[a-zA-Z]+:INVALID_SESSION_ID<\/faultcode>/.test(response.body));
}
/** @override **/
parseError(body) {
const error = lookupValue(body, [/:Envelope$/, /:Body$/, /:Fault$/]);
return {
errorCode: error.faultcode,
message: error.faultstring,
};
}
/** @override **/
async getResponseBody(response) {
const body = await super.getResponseBody(response);
return lookupValue(body, [/:Envelope$/, /:Body$/, /.+/]);
}
/**
* @private
*/
_createEnvelope(message) {
const header = {};
const conn = this._conn;
if (conn.accessToken) {
header.SessionHeader = { sessionId: 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('');
}
}
exports.SOAP = SOAP;
exports.default = SOAP;
;