cache = function () { this._entries = {}; }
n/a
date = function (literal) { this._literal = literal; }
n/a
http_api = function (conn, options) { options = options || {}; this._conn = conn; this.on('resume', function(err) { conn.emit('resume', err); }); this._responseType = options.responseType; this._transport = options.transport || conn._transport; this._noContentResponse = options.noContentResponse; }
n/a
logger = function (logLevel) { if (typeof logLevel === 'string') { logLevel = LogLevels[logLevel]; } if (!logLevel) { logLevel = LogLevels.INFO; } this._logLevel = logLevel; }
n/a
function Promise(fn) { if (typeof this !== 'object') { throw new TypeError('Promises must be constructed via new'); } if (typeof fn !== 'function') { throw new TypeError('not a function'); } this._45 = 0; this._81 = 0; this._65 = null; this._54 = null; if (fn === noop) return; doResolve(fn, this); }
...
* http://promises-aplus.github.io/promises-spec/
*
* Delegate to deferred promise, return promise instance for batch result
*
* @method Chatter~Request#then
*/
Request.prototype.then = function(onResolve, onReject) {
return this.promise().then(onResolve, onReject);
};
/**
* Promise/A+ extension
* Call "then" using given node-style callback function
*
* @method Chatter~Request#thenCall
...
query = function (conn, config, options) { Query.super_.call(this, { objectMode: true }); this._conn = conn; if (_.isString(config)) { // if query config is string, it is given in SOQL. this._soql = config; } else if (config.locator && config.locator.indexOf("/") >= 0) { // if locator given in url for next records this._locator = config.locator.split("/").pop(); } else { this._config = config; this.select(config.fields); if (config.includes) { this.include(config.includes); } } this._options = _.defaults({ maxFetch: 10000, autoFetch: false, scanAll: false, responseTarget: ResponseTargets.QueryResult }, options || {}); this._executed = false; this._finished = false; this._chaining = false; this._deferred = Promise.defer(); var self = this; }
...
var count = -1;
async.waterfall([
function(next) {
conn.sobject(config.bigTable).count({}, next);
},
function(_count, next) {
count = _count;
conn.bulk.query("SELECT Id, Name FROM " + config.bigTable)
.on('record', function(rec) { records.push(rec); })
.on('error', function(err) { next(err); })
.stream().pipe(fstream)
.on('finish', function() { next(null, records); });
}
], function(err, records) {
if (err) { throw err; }
...
quick_action = function (conn, path) { this._conn = conn; this._path = path; }
n/a
record = function (conn, type, id) { this._conn = conn; this.type = type; this.id = id; }
...
});
/**
*
*/
describe("update account", function() {
it("should update successfully", function(done) {
conn.sobject('Account').record(account.Id).update({ Name : "Hello2
x22; }, function(err, ret) {
if (err) { throw err; }
assert.ok(ret.success);
}.check(done));
});
describe("then retrieve the account", function() {
it("should return updated account object", function(done) {
...
record_stream = function () { RecordStream.super_.call(this, { objectMode: true }); }
n/a
soap = function (conn, options) { SOAP.super_.apply(this, arguments); this._endpointUrl = options.endpointUrl; this._xmlns = options.xmlns || 'urn:partner.soap.sforce.com'; }
n/a
sobject = function (conn, type) { this._conn = conn; this.type = type; var cacheOptions = { key: "describe." + this.type }; this.describe$ = conn.cache.makeCacheable(this.describe, this, cacheOptions); this.describe = conn.cache.makeResponseCacheable(this.describe, this, cacheOptions); cacheOptions = { key: "layouts." + this.type }; this.layouts$ = conn.cache.makeCacheable(this.layouts, this, cacheOptions); this.layouts = conn.cache.makeResponseCacheable(this.layouts, this, cacheOptions); cacheOptions = { key: "compactLayouts." + this.type }; this.compactLayouts$ = conn.cache.makeCacheable(this.compactLayouts, this, cacheOptions); this.compactLayouts = conn.cache.makeResponseCacheable(this.compactLayouts, this, cacheOptions); cacheOptions = { key: "approvalLayouts." + this.type }; this.approvalLayouts$ = conn.cache.makeCacheable(this.approvalLayouts, this, cacheOptions); this.approvalLayouts = conn.cache.makeResponseCacheable(this.approvalLayouts, this, cacheOptions); }
...
};
Channel.prototype.push = function(events, callback) {
var isArray = _.isArray(events);
events = isArray ? events : [ events ];
var conn = this._streaming._conn;
if (!this._id) {
this._id = conn.sobject('StreamingChannel').findOne({ Name: this._name },
x27;Id')
.then(function(rec) { return rec.Id });
}
return this._id.then(function(id) {
var channelUrl = '/sobjects/StreamingChannel/' + id + '/push';
return conn.requestPost(channelUrl, { pushEvents: events });
}).then(function(rets) {
return isArray ? rets : rets[0];
...
cache = function () { this._entries = {}; }
n/a
clear = function (key) { for (var k in this._entries) { if (!key || k.indexOf(key) === 0) { this._entries[k].clear(); } } }
...
/**
* Initialize tooling API
* @protected
*/
Tooling.prototype.initialize = function() {
this.sobjects = {};
this.cache.clear();
this.cache.get('describeGlobal').on('value', _.bind(function(res) {
if (res.result) {
var types = _.map(res.result.sobjects, function(so) { return so.name; });
types.forEach(this.sobject, this);
}
}, this));
};
...
get = function (key) { if (key && this._entries[key]) { return this._entries[key]; } else { var entry = new CacheEntry(); this._entries[key] = entry; return entry; } }
...
},
getConnectionTypes: function() {
return Transport.getConnectionTypes();
},
selectTransport: function(transportTypes) {
Transport.get(this, transportTypes, this._disabled, function(transport) {
this.debug('Selected ? transport for ?', transport.connectionType, URI.stringify(transport.endpoint));
if (transport === this._transport) return;
if (this._transport) this._transport.close();
this._transport = transport;
this.connectionType = transport.connectionType;
...
makeCacheable = function (fn, scope, options) { var cache = this; options = options || {}; var $fn = function() { var args = Array.prototype.slice.apply(arguments); var callback = args.pop(); if (!_.isFunction(callback)) { args.push(callback); } var key = _.isString(options.key) ? options.key : _.isFunction(options.key) ? options.key.apply(scope, args) : createCacheKey(options.namespace, args); var entry = cache.get(key); if (!_.isFunction(callback)) { // if callback is not given in last arg, return cached result (immediate). var value = entry.get(); if (!value) { throw new Error('Function call result is not cached yet.'); } if (value.error) { throw value.error; } return value.result; } entry.get(function(value) { callback(value.error, value.result); }); if (!entry.fetching) { // only when no other client is calling function entry.fetching = true; args.push(function(err, result) { entry.set({ error: err, result: result }); }); fn.apply(scope || this, args); } }; $fn.clear = function() { var key = _.isString(options.key) ? options.key : _.isFunction(options.key) ? options.key.apply(scope, arguments) : createCacheKey(options.namespace, arguments); cache.clear(key); }; return $fn; }
...
}, this);
this.cache = new Cache();
var cacheOptions = {
key: function(type) { return type ? "describe." + type : "describe"; }
};
this.describe$ = this.cache.makeCacheable(this.describe, this, cacheOptions);
this.describe = this.cache.makeResponseCacheable(this.describe, this, cacheOptions);
this.describeSObject$ = this.describe$;
this.describeSObject = this.describe;
cacheOptions = { key: 'describeGlobal' };
this.describeGlobal$ = this.cache.makeCacheable(this.describeGlobal, this, cacheOptions);
this.describeGlobal = this.cache.makeResponseCacheable(this.describeGlobal, this, cacheOptions);
...
makeResponseCacheable = function (fn, scope, options) { var cache = this; options = options || {}; return function() { var args = Array.prototype.slice.apply(arguments); var callback = args.pop(); if (!_.isFunction(callback)) { args.push(callback); callback = null; } var key = _.isString(options.key) ? options.key : _.isFunction(options.key) ? options.key.apply(scope, args) : createCacheKey(options.namespace, args); var entry = cache.get(key); entry.fetching = true; if (callback) { args.push(function(err, result) { entry.set({ error: err, result: result }); callback(err, result); }); } var ret, error; try { ret = fn.apply(scope || this, args); } catch(e) { error = e; } if (ret && _.isFunction(ret.then)) { // if the returned value is promise if (!callback) { return ret.then(function(result) { entry.set({ error: undefined, result: result }); return result; }, function(err) { entry.set({ error: err, result: undefined }); throw err; }); } else { return ret; } } else { entry.set({ error: error, result: ret }); if (error) { throw error; } return ret; } }; }
...
this.cache = new Cache();
var cacheOptions = {
key: function(type) { return type ? "describe." + type : "describe"; }
};
this.describe$ = this.cache.makeCacheable(this.describe, this, cacheOptions);
this.describe = this.cache.makeResponseCacheable(this.describe, this, cacheOptions);
this.describeSObject$ = this.describe$;
this.describeSObject = this.describe;
cacheOptions = { key: 'describeGlobal' };
this.describeGlobal$ = this.cache.makeCacheable(this.describeGlobal, this, cacheOptions);
this.describeGlobal = this.cache.makeResponseCacheable(this.describeGlobal, this, cacheOptions);
...
createRequest = function (signedRequest) { return function(params, callback) { var response; var str = new Duplex(); str._read = function(size) { if (response) { str.push(response.body); } }; var bufs = []; var sent = false; str._write = function(chunk, encoding, callback) { bufs.push(chunk.toString(encoding)); callback(); }; str.on('finish', function() { if (!sent) { send(bufs.join('')); sent = true; } }); if (params.body || params.body === "" || !/^(put|post|patch)$/i.test(params.method)) { send(params.body); sent = true; } function send(body) { var settings = { client: signedRequest.client, method: params.method, data: body }; if (params.headers) { settings.headers = {}; for (var name in params.headers) { if (name.toLowerCase() === 'content-type') { settings.contentType = params.headers[name]; } else { settings.headers[name] = params.headers[name]; } } } settings.success = function(data) { var headers = parseHeaders(data.responseHeaders); var body = data.payload; if (!_.isString(body)) { body = JSON.stringify(body); } response = { statusCode : data.status, headers: headers, body: body }; if (callback) { callback(null, response, response.body); } str.end(); }; settings.failure = function(err) { if (callback) { callback(err); } }; Sfdc.canvas.client.ajax(params.url, settings); } return str; }; }
...
this._jsonpParam = jsonpParam;
};
inherits(JsonpTransport, Transport);
/** @protected **/
JsonpTransport.prototype._getHttpRequestModule = function() {
return jsonp.createRequest(this._jsonpParam);
};
JsonpTransport.supported = jsonp.supported;
/**
* Class for Sfdc Canvas request transport
...
function parseCSV(str, options) { options = _.extend({}, options, { columns: true }); return csvParseSync(str, options); }
...
});
if (ret.error) { throw ret.error; }
return ret.result;
}
/** @private */
function parseCSV(str) {
return require('./csv').parseCSV(str);
}
/** @private */
function parseText(str) { return str; }
/**
...
function parseCSVStream(options) { options = _.extend({}, options, { columns: true }); return csvParse(options); }
...
RecordStream.map(function(record) {
return convertRecordForSerialization(record, options);
}),
CSV.serializeCSVStream(options)
);
},
parse: function(options) {
return CSV.parseCSVStream(options);
}
};
/**
* @private
*/
var DataStreamConverters = RecordStream.DataStreamConverters = {
...
function serializeCSVStream(options) { options = _.extend({}, options, { header: true }); return csvStringify(options); }
...
var CSVStreamConverter = {
serialize: function(options) {
options = options || {};
return createPipelineStream(
RecordStream.map(function(record) {
return convertRecordForSerialization(record, options);
}),
CSV.serializeCSVStream(options)
);
},
parse: function(options) {
return CSV.parseCSVStream(options);
}
};
...
function toCSV(records, options) { options = _.extend({}, options, { header: true }); return csvStringifySync(records, options); }
n/a
date = function (literal) { this._literal = literal; }
n/a
LAST_N_DAYS = function (num) { return new SfDate(literal + ":" + num); }
...
*
*/
describe("Query with Date field for date literal", function() {
var soql = SOQLBuilder.createSOQL({
table: "Opportunity",
conditions: {
$and : [
{ CloseDate: { $gte : SfDate.LAST_N_DAYS(10) } },
{ CloseDate: { $lte : SfDate.TOMORROW } },
{ CloseDate: { $gt : SfDate.toDateLiteral(new Date(1288958400000)) }},
{ CreatedDate: { $lt : SfDate.toDateTimeLiteral('2010-11-02T04:45:04+09:00') }}
]
}
});
...
LAST_N_FISCAL_QUARTERS = function (num) { return new SfDate(literal + ":" + num); }
n/a
LAST_N_FISCAL_YEARS = function (num) { return new SfDate(literal + ":" + num); }
n/a
LAST_N_MONTHS = function (num) { return new SfDate(literal + ":" + num); }
n/a
LAST_N_QUARTERS = function (num) { return new SfDate(literal + ":" + num); }
n/a
LAST_N_WEEKS = function (num) { return new SfDate(literal + ":" + num); }
n/a
LAST_N_YEARS = function (num) { return new SfDate(literal + ":" + num); }
n/a
NEXT_N_DAYS = function (num) { return new SfDate(literal + ":" + num); }
n/a
NEXT_N_FISCAL_QUARTERS = function (num) { return new SfDate(literal + ":" + num); }
n/a
NEXT_N_FISCAL_YEARS = function (num) { return new SfDate(literal + ":" + num); }
n/a
NEXT_N_MONTHS = function (num) { return new SfDate(literal + ":" + num); }
n/a
NEXT_N_QUARTERS = function (num) { return new SfDate(literal + ":" + num); }
n/a
NEXT_N_WEEKS = function (num) { return new SfDate(literal + ":" + num); }
n/a
NEXT_N_YEARS = function (num) { return new SfDate(literal + ":" + num); }
n/a
parseDate = function (str) { var d = new Date(); var regexp = /^([\d]{4})-?([\d]{2})-?([\d]{2})(T([\d]{2}):?([\d]{2}):?([\d]{2})(.([\d]{3}))?(Z|([\+\-])([\d]{2}):?([\d]{2})))?$/; var m = str.match(regexp); if (m) { d = new Date(0); if (!m[4]) { d.setFullYear(parseInt(m[1], 10)); d.setDate(parseInt(m[3], 10)); d.setMonth(parseInt(m[2], 10) - 1); d.setHours(0); d.setMinutes(0); d.setSeconds(0); d.setMilliseconds(0); } else { d.setUTCFullYear(parseInt(m[1], 10)); d.setUTCDate(parseInt(m[3], 10)); d.setUTCMonth(parseInt(m[2], 10) - 1); d.setUTCHours(parseInt(m[5], 10)); d.setUTCMinutes(parseInt(m[6], 10)); d.setUTCSeconds(parseInt(m[7], 10)); d.setUTCMilliseconds(parseInt(m[9] || '0', 10)); if (m[10] && m[10] !== 'Z') { var offset = parseInt(m[12],10) * 60 + parseInt(m[13], 10); d.setTime((m[11] === '+' ? -1 : 1) * offset * 60 * 1000 +d.getTime()); } } return d; } else { throw new Error("Invalid date format is specified : " + str); } }
...
* @param {String|Number|Date} date - Input date
* @returns {SfDate} - Salesforce date literal with ISO8601 date format
*/
SfDate.toDateLiteral = function(date) {
if (_.isNumber(date)) {
date = new Date(date);
} else if (_.isString(date)) {
date = SfDate.parseDate(date);
}
var yy = date.getFullYear();
var mm = date.getMonth()+1;
var dd = date.getDate();
var dstr = [ yy, zeropad(mm), zeropad(dd) ].join("-");
return new SfDate(dstr);
};
...
toDateLiteral = function (date) { if (_.isNumber(date)) { date = new Date(date); } else if (_.isString(date)) { date = SfDate.parseDate(date); } var yy = date.getFullYear(); var mm = date.getMonth()+1; var dd = date.getDate(); var dstr = [ yy, zeropad(mm), zeropad(dd) ].join("-"); return new SfDate(dstr); }
...
describe("Query with Date field for date literal", function() {
var soql = SOQLBuilder.createSOQL({
table: "Opportunity",
conditions: {
$and : [
{ CloseDate: { $gte : SfDate.LAST_N_DAYS(10) } },
{ CloseDate: { $lte : SfDate.TOMORROW } },
{ CloseDate: { $gt : SfDate.toDateLiteral(new Date(1288958400000)) }},
{ CreatedDate: { $lt : SfDate.toDateTimeLiteral('2010-11-02T04:45:04+09:00') }}
]
}
});
it("should equal to soql", function() {
assert.ok(soql ===
...
toDateTimeLiteral = function (date) { if (_.isNumber(date)) { date = new Date(date); } else if (_.isString(date)) { date = SfDate.parseDate(date); } var yy = date.getUTCFullYear(); var mm = date.getUTCMonth()+1; var dd = date.getUTCDate(); var hh = date.getUTCHours(); var mi = date.getUTCMinutes(); var ss = date.getUTCSeconds(); var dtstr = [ yy, zeropad(mm), zeropad(dd) ].join("-") + "T" + [ zeropad(hh), zeropad(mi), zeropad(ss) ].join(":") + "Z"; return new SfDate(dtstr); }
...
var soql = SOQLBuilder.createSOQL({
table: "Opportunity",
conditions: {
$and : [
{ CloseDate: { $gte : SfDate.LAST_N_DAYS(10) } },
{ CloseDate: { $lte : SfDate.TOMORROW } },
{ CloseDate: { $gt : SfDate.toDateLiteral(new Date(1288958400000)) }},
{ CreatedDate: { $lt : SfDate.toDateTimeLiteral('2010-11-02T04:45:04+09:00
') }}
]
}
});
it("should equal to soql", function() {
assert.ok(soql ===
"SELECT Id FROM Opportunity " +
...
toJSON = function () { return this._literal; }
n/a
toString = function () { return this._literal; }
...
var deferred = Promise.defer();
if (_.isObject(zipInput) && _.isFunction(zipInput.pipe)) {
var bufs = [];
zipInput.on('data', function(d) {
bufs.push(d);
});
zipInput.on('end', function() {
deferred.resolve(Buffer.concat(bufs).toString('base64'));
});
// zipInput.resume();
} else if (zipInput instanceof Buffer) {
deferred.resolve(zipInput.toString('base64'));
} else if (zipInput instanceof String || typeof zipInput === 'string') {
deferred.resolve(zipInput);
} else {
...
http_api = function (conn, options) { options = options || {}; this._conn = conn; this.on('resume', function(err) { conn.emit('resume', err); }); this._responseType = options.responseType; this._transport = options.transport || conn._transport; this._noContentResponse = options.noContentResponse; }
n/a
SessionRefreshDelegate = function (conn, refreshFn) { this._conn = conn; this._refreshFn = refreshFn; this._refreshing = false; }
...
// Allow to delegate connection refresh to outer function
var self = this;
var refreshFn = options.refreshFn;
if (!refreshFn && this.oauth2.clientId && this.oauth2.clientSecret) {
refreshFn = oauthRefreshFn;
}
if (refreshFn) {
this._refreshDelegate = new HttpApi.SessionRefreshDelegate(this, refreshFn);
}
var cacheOptions = {
key: function(type) { return type ? "describe." + type : "describe"; }
};
this.describe$ = this.cache.makeCacheable(this.describe, this, cacheOptions);
this.describe = this.cache.makeResponseCacheable(this.describe, this, cacheOptions);
...
function EventEmitter() { EventEmitter.init.call(this); }
n/a
beforeSend = function (request) { request.headers = request.headers || {}; if (this._conn.accessToken) { request.headers.Authorization = "Bearer " + this._conn.accessToken; } if (this._conn.callOptions) { var callOptions = []; for (var name in this._conn.callOptions) { callOptions.push(name + "=" + this._conn.callOptions[name]); } request.headers["Sforce-Call-Options"] = callOptions.join(', '); } }
...
if (refreshDelegate && refreshDelegate._refreshing) {
refreshDelegate.once('resume', onResume);
return deferred.promise.thenCall(callback);
}
// hook before sending
self.beforeSend(request);
self.emit('request', request);
logger.debug("<request> method=" + request.method + ", url=" + request.url);
var requestTime = Date.now();
return this._transport.httpRequest(request).then(function(response) {
var responseTime = Date.now();
...
getError = function (response, body) { var error; try { error = this.parseError(body || this.parseResponseBody(response)); } catch(e) {} error = _.isObject(error) && _.isString(error.message) ? error : { errorCode: 'ERROR_HTTP_' + response.statusCode, message : response.body }; var err = new Error(error.message); err.name = error.errorCode; for (var key in error) { err[key] = error[key]; } return err; }
...
// Refresh token if session has been expired and requires authentication
// when session refresh delegate is available
if (self.isSessionExpired(response) && refreshDelegate) {
refreshDelegate.refresh(requestTime, onResume);
return deferred.promise;
}
if (self.isErrorResponse(response)) {
var err = self.getError(response);
throw err;
}
return self.getResponseBody(response);
}, function(err) {
var responseTime = Date.now();
logger.debug("elappsed time : " + (responseTime - requestTime) + "msec");
logger.error(err);
...
getRefreshDelegate = function () { return this._conn._refreshDelegate; }
...
* @param {Callback.<Object>} callback - Callback function
* @returns {Promise.<Object>} -
*/
HttpApi.prototype.request = function(request, callback) {
var self = this;
var conn = this._conn;
var logger = conn._logger;
var refreshDelegate = this.getRefreshDelegate();
// remember previous instance url in case it changes after a refresh
var lastInstanceUrl = conn.instanceUrl;
var deferred = Promise.defer();
var onResume = function(err) {
if (err) {
...
getResponseBody = function (response) { if (response.statusCode === 204) { // No Content return this._noContentResponse; } var body = this.parseResponseBody(response); var err; if (this.hasErrorInResponseBody(body)) { err = this.getError(response, body); throw err; } if (response.statusCode === 300) { // Multiple Choices err = new Error('Multiple records found'); err.name = "MULTIPLE_CHOICES"; err.content = body; throw err; } return body; }
...
refreshDelegate.refresh(requestTime, onResume);
return deferred.promise;
}
if (self.isErrorResponse(response)) {
var err = self.getError(response);
throw err;
}
return self.getResponseBody(response);
}, function(err) {
var responseTime = Date.now();
logger.debug("elappsed time : " + (responseTime - requestTime) + "msec");
logger.error(err);
throw err;
})
.thenCall(callback);
...
getResponseContentType = function (response) { return this._responseType || response.headers && response.headers["content-type"]; }
...
return this._responseType || response.headers && response.headers["content-type"];
};
/**
*
*/
HttpApi.prototype.parseResponseBody = function(response) {
var contentType = this.getResponseContentType(response);
var parseBody = /^(text|application)\/xml(;|$)/.test(contentType) ? parseXML :
/^application\/json(;|$)/.test(contentType) ? parseJSON :
/^text\/csv(;|$)/.test(contentType) ? parseCSV :
parseText;
try {
return parseBody(response.body);
} catch(e) {
...
hasErrorInResponseBody = function (body) { return false; }
...
*/
HttpApi.prototype.getResponseBody = function(response) {
if (response.statusCode === 204) { // No Content
return this._noContentResponse;
}
var body = this.parseResponseBody(response);
var err;
if (this.hasErrorInResponseBody(body)) {
err = this.getError(response, body);
throw err;
}
if (response.statusCode === 300) { // Multiple Choices
err = new Error('Multiple records found');
err.name = "MULTIPLE_CHOICES";
err.content = body;
...
isErrorResponse = function (response) { return response.statusCode >= 400; }
...
self.emit('response', response);
// Refresh token if session has been expired and requires authentication
// when session refresh delegate is available
if (self.isSessionExpired(response) && refreshDelegate) {
refreshDelegate.refresh(requestTime, onResume);
return deferred.promise;
}
if (self.isErrorResponse(response)) {
var err = self.getError(response);
throw err;
}
return self.getResponseBody(response);
}, function(err) {
var responseTime = Date.now();
logger.debug("elappsed time : " + (responseTime - requestTime) + "msec");
...
isSessionExpired = function (response) { return response.statusCode === 401; }
...
var responseTime = Date.now();
logger.debug("elappsed time : " + (responseTime - requestTime) + "msec");
logger.debug("<response> status=" + response.statusCode + ", url=" + request.url);
self.emit('response', response);
// Refresh token if session has been expired and requires authentication
// when session refresh delegate is available
if (self.isSessionExpired(response) && refreshDelegate) {
refreshDelegate.refresh(requestTime, onResume);
return deferred.promise;
}
if (self.isErrorResponse(response)) {
var err = self.getError(response);
throw err;
}
...
parseError = function (body) { var errors = body; return _.isArray(errors) ? errors[0] : errors; }
...
/**
* Get error message in response
* @protected
*/
HttpApi.prototype.getError = function(response, body) {
var error;
try {
error = this.parseError(body || this.parseResponseBody(response));
} catch(e) {}
error = _.isObject(error) && _.isString(error.message) ? error : {
errorCode: 'ERROR_HTTP_' + response.statusCode,
message : response.body
};
var err = new Error(error.message);
err.name = error.errorCode;
...
parseResponseBody = function (response) { var contentType = this.getResponseContentType(response); var parseBody = /^(text|application)\/xml(;|$)/.test(contentType) ? parseXML : /^application\/json(;|$)/.test(contentType) ? parseJSON : /^text\/csv(;|$)/.test(contentType) ? parseCSV : parseText; try { return parseBody(response.body); } catch(e) { return response.body; } }
...
* Get response body
* @protected
*/
HttpApi.prototype.getResponseBody = function(response) {
if (response.statusCode === 204) { // No Content
return this._noContentResponse;
}
var body = this.parseResponseBody(response);
var err;
if (this.hasErrorInResponseBody(body)) {
err = this.getError(response, body);
throw err;
}
if (response.statusCode === 300) { // Multiple Choices
err = new Error('Multiple records found');
...
request = function (request, callback) { var self = this; var conn = this._conn; var logger = conn._logger; var refreshDelegate = this.getRefreshDelegate(); // remember previous instance url in case it changes after a refresh var lastInstanceUrl = conn.instanceUrl; var deferred = Promise.defer(); var onResume = function(err) { if (err) { deferred.reject(err); return; } // check to see if the token refresh has changed the instance url if(lastInstanceUrl !== conn.instanceUrl){ // if the instance url has changed // then replace the current request urls instance url fragment // with the updated instance url request.url = request.url.replace(lastInstanceUrl,conn.instanceUrl); } self.request(request).then(function(response) { deferred.resolve(response); }, function(err) { deferred.reject(err); }); }; if (refreshDelegate && refreshDelegate._refreshing) { refreshDelegate.once('resume', onResume); return deferred.promise.thenCall(callback); } // hook before sending self.beforeSend(request); self.emit('request', request); logger.debug("<request> method=" + request.method + ", url=" + request.url); var requestTime = Date.now(); return this._transport.httpRequest(request).then(function(response) { var responseTime = Date.now(); logger.debug("elappsed time : " + (responseTime - requestTime) + "msec"); logger.debug("<response> status=" + response.statusCode + ", url=" + request.url); self.emit('response', response); // Refresh token if session has been expired and requires authentication // when session refresh delegate is available if (self.isSessionExpired(response) && refreshDelegate) { refreshDelegate.refresh(requestTime, onResume); return deferred.promise; } if (self.isErrorResponse(response)) { var err = self.getError(response); throw err; } return self.getResponseBody(response); }, function(err) { var responseTime = Date.now(); logger.debug("elappsed time : " + (responseTime - requestTime) + "msec"); logger.error(err); throw err; }) .thenCall(callback); }
...
* @param {Callback.<Analytics~ReportResult>} [callback] - Callback function
* @returns {Promise.<Analytics~ReportResult>}
*/
ReportInstance.prototype.retrieve = function(callback) {
var conn = this._conn,
report = this._report;
var url = [ conn._baseUrl(), "analytics", "reports", report.id, "instances", this.id ].join('
;/');
return conn.request(url).thenCall(callback);
};
/**
* Report object in Analytics API
*
* @protected
* @class Analytics~Report
...
createRequest = function (jsonpParam, timeout) { jsonpParam = jsonpParam || 'callback'; timeout = timeout || 10000; return function(params, callback) { if (params.method.toUpperCase() !== 'GET') { return callback(new Error('JSONP only supports GET request.')); } var cbFuncName = '_jsforce_jsonpCallback_' + (++_index); var callbacks = window; var url = params.url; url += url.indexOf('?')>0 ? '&' : '?'; url += jsonpParam + '=' + cbFuncName; var script = document.createElement('script'); script.type = 'text/javascript'; script.src = url; document.documentElement.appendChild(script); var pid = setTimeout(function() { cleanup(); callback(new Error("JSONP call time out.")); }, timeout); callbacks[cbFuncName] = function(res) { cleanup(); callback(null, { statusCode: 200, headers: { "content-type": "application/json" }, body: JSON.stringify(res) }); }; var cleanup = function() { clearTimeout(pid); document.documentElement.removeChild(script); delete callbacks[cbFuncName]; }; }; }
...
this._jsonpParam = jsonpParam;
};
inherits(JsonpTransport, Transport);
/** @protected **/
JsonpTransport.prototype._getHttpRequestModule = function() {
return jsonp.createRequest(this._jsonpParam);
};
JsonpTransport.supported = jsonp.supported;
/**
* Class for Sfdc Canvas request transport
...
logger = function (logLevel) { if (typeof logLevel === 'string') { logLevel = LogLevels[logLevel]; } if (!logLevel) { logLevel = LogLevels.INFO; } this._logLevel = logLevel; }
n/a
debug = function (message) { this.log(level, message); }
...
this._jobInfo = this._waitAssign().then(function() {
return bulk._request({
method : 'GET',
path : "/job/" + self.id,
responseType: "application/xml"
});
}).then(function(res) {
logger.debug(res.jobInfo);
self.id = res.jobInfo.id;
self.type = res.jobInfo.object;
self.operation = res.jobInfo.operation;
self.state = res.jobInfo.state;
return res.jobInfo;
});
return this._jobInfo.thenCall(callback);
...
error = function (message) { this.log(level, message); }
...
"url": "git://github.com/jsforce/jsforce.git"
},
"scripts": {
"build": "gulp build",
"build:all": "gulp build:all",
"build:test": "gulp build:test",
"doc": "jsdoc lib -d doc --recurse --lenient",
"prepublish": "node -e \"if(process.env.npm_package_version!==require('./lib/VERSION')){console
.error('The pacakge.json version is not matching to ./lib/VERSION.js');process
.exit(1)}\"",
"test": "npm run test:node",
"test:all": "npm run test:node && npm run test:browser",
"test:browser": "testem",
"test:node": "./test/bin/run-test"
},
"version": "1.7.2"
}
...
fatal = function (message) { this.log(level, message); }
n/a
info = function (message) { this.log(level, message); }
...
var job = this.job;
var batchId = this.id;
if (!jobId || !batchId) {
throw new Error("Batch not started.");
}
return job.info().then(function(jobInfo) {
return bulk._request({
method : 'GET',
path : "/job/" + jobId + "/batch/" + batchId + "/result"
});
}).then(function(res) {
var results;
if (job.operation === 'query') {
...
log = function (level, message) { if (this._logLevel <= level) { if (level < LogLevels.ERROR) { console.log(message); } else { console.error(message); } } }
...
value = Math.abs(value)
if (isNaN(value) || value === Infinity) {
m = isNaN(value) ? 1 : 0
e = eMax
} else {
e = Math.floor(Math.log(value) / Math.LN2)
if (value * (c = Math.pow(2, -e)) < 1) {
e--
c *= 2
}
if (e + eBias >= 1) {
value += rt / c
} else {
...
warn = function (message) { this.log(level, message); }
...
} else if (keys.length > 1) {
proxy = env[name];
}
proxy = proxy || env['CGI_' + upcase];
} else {
proxy = env[name] || env[upcase];
if (proxy && !env[name])
console.warn('The environment variable ' + upcase +
' is discouraged. Use ' + name + '.');
}
return proxy;
}
}), {
get: function(dispatcher, allowed, disabled, callback, context) {
...
function Promise(fn) { if (typeof this !== 'object') { throw new TypeError('Promises must be constructed via new'); } if (typeof fn !== 'function') { throw new TypeError('not a function'); } this._45 = 0; this._81 = 0; this._65 = null; this._54 = null; if (fn === noop) return; doResolve(fn, this); }
...
* http://promises-aplus.github.io/promises-spec/
*
* Delegate to deferred promise, return promise instance for batch result
*
* @method Chatter~Request#then
*/
Request.prototype.then = function(onResolve, onReject) {
return this.promise().then(onResolve, onReject);
};
/**
* Promise/A+ extension
* Call "then" using given node-style callback function
*
* @method Chatter~Request#thenCall
...
function noop() {}
n/a
all = function (arr) { var args = Array.prototype.slice.call(arr); return new Promise(function (resolve, reject) { if (args.length === 0) return resolve([]); var remaining = args.length; function res(i, val) { if (val && (typeof val === 'object' || typeof val === 'function')) { if (val instanceof Promise && val.then === Promise.prototype.then) { while (val._81 === 3) { val = val._65; } if (val._81 === 1) return res(i, val._65); if (val._81 === 2) reject(val._65); val.then(function (val) { res(i, val); }, reject); return; } else { var then = val.then; if (typeof then === 'function') { var p = new Promise(then.bind(val)); p.then(function (val) { res(i, val); }, reject); return; } } } args[i] = val; if (--remaining === 0) { resolve(args); } } for (var i = 0; i < args.length; i++) { res(i, args[i]); } }); }
...
options = options || {};
var self = this;
var isArray = _.isArray(ids);
ids = isArray ? ids : [ ids ];
if (ids.length > self.maxRequest) {
return Promise.reject(new Error("Exceeded max limit of concurrent call")).thenCall(callback);
}
return Promise.all(
_.map(ids, function(id) {
if (!id) { return Promise.reject(new Error('Invalid record ID. Specify valid record ID value')).thenCall(callback); }
var url = [ self._baseUrl(), "sobjects", type, id ].join('/');
return self.request({ method: 'GET', url: url, headers: options.headers });
})
).then(function(results) {
return !isArray && _.isArray(results) ? results[0] : results;
...
defer = function () { return new Deferred(); }
...
* @param {String} [batchId] - Batch ID (if already available)
*/
var Batch = function(job, batchId) {
Batch.super_.call(this, { objectMode: true });
this.job = job;
this.id = batchId;
this._bulk = job._bulk;
this._deferred = Promise.defer();
this._setupDataStreams();
};
inherits(Batch, stream.Writable);
/**
...
race = function (values) { return new Promise(function (resolve, reject) { values.forEach(function(value){ Promise.resolve(value).then(resolve, reject); }); }); }
n/a
reject = function (value) { return new Promise(function (resolve, reject) { reject(value); }); }
...
}
var rdeferred = Promise.defer();
this._result = rdeferred.promise;
this._result.then(function(res) {
self._deferred.resolve(res);
}, function(err) {
self._deferred.reject(err);
});
this.once('response', function(res) {
rdeferred.resolve(res);
});
this.once('error', function(err) {
rdeferred.reject(err);
});
...
resolve = function (value) { if (value instanceof Promise) return value; if (value === null) return NULL; if (value === undefined) return UNDEFINED; if (value === true) return TRUE; if (value === false) return FALSE; if (value === 0) return ZERO; if (value === '') return EMPTYSTRING; if (typeof value === 'object' || typeof value === 'function') { try { var then = value.then; if (typeof then === 'function') { return new Promise(then.bind(value)); } } catch (ex) { return new Promise(function (resolve, reject) { reject(ex); }); } } return valuePromise(value); }
...
* Wait till the job is assigned to server
*
* @method Bulk~Job#info
* @param {Callback.<Bulk~JobInfo>} [callback] - Callback function
* @returns {Promise.<Bulk~JobInfo>}
*/
Job.prototype._waitAssign = function(callback) {
return (this.id ? Promise.resolve({ id: this.id }) : this.open()).thenCall(callback);
};
/**
* List all registered batch info in job
*
* @method Bulk~Job#list
...
catch = function (onRejected) { return this.then(null, onRejected); }
...
var rets = rrets[0], statusField = rrets[1];
leadIds = rets.map(function(r){ return r.id; });
convertedStatus = statusField.picklist.picklistValues
.filter(function(pv){ return pv.converted === 'true'; })
.map(function(pv){ return pv.fullName; })[0];
done();
})
.catch(done);
});
it("should convert lead", function(done) {
conn.soap.convertLead({
leadId: leadIds[0],
convertedStatus: convertedStatus
}, function(err, ret) {
...
fail = function (onRejected) { return this.then(null, onRejected); }
...
success: ret.Success === "true",
errors: ret.Error ? [ ret.Error ] : []
};
});
}
self.emit('response', results);
return results;
}).fail(function(err) {
self.emit('error', err);
throw err;
}).thenCall(callback);
};
/**
* Fetch query result as a record stream
...
then = function (onFulfilled, onRejected) { if (this.constructor !== Promise) { return safeThen(this, onFulfilled, onRejected); } var res = new Promise(noop); handle(this, new Handler(onFulfilled, onRejected, res)); return res; }
...
method : 'POST',
path : "/job",
body : body,
headers : {
"Content-Type" : "application/xml; charset=utf-8"
},
responseType: "application/xml"
}).then(function(res) {
self.emit("open", res.jobInfo);
self.id = res.jobInfo.id;
self.state = res.jobInfo.state;
return res.jobInfo;
}, function(err) {
self.emit("error", err);
throw err;
...
thenCall = function (callback) { if (_.isFunction(callback)) { this.then(function(res) { process.nextTick(function() { callback(null, res); }); }, function(err) { process.nextTick(function() { callback(err); }); }); } return this; }
...
* @param {Callback.<Analytics~ReportResult>} [callback] - Callback function
* @returns {Promise.<Analytics~ReportResult>}
*/
ReportInstance.prototype.retrieve = function(callback) {
var conn = this._conn,
report = this._report;
var url = [ conn._baseUrl(), "analytics", "reports", report.id, "instances", this.id ].join('
;/');
return conn.request(url).thenCall(callback);
};
/**
* Report object in Analytics API
*
* @protected
* @class Analytics~Report
...
query = function (conn, config, options) { Query.super_.call(this, { objectMode: true }); this._conn = conn; if (_.isString(config)) { // if query config is string, it is given in SOQL. this._soql = config; } else if (config.locator && config.locator.indexOf("/") >= 0) { // if locator given in url for next records this._locator = config.locator.split("/").pop(); } else { this._config = config; this.select(config.fields); if (config.includes) { this.include(config.includes); } } this._options = _.defaults({ maxFetch: 10000, autoFetch: false, scanAll: false, responseTarget: ResponseTargets.QueryResult }, options || {}); this._executed = false; this._finished = false; this._chaining = false; this._deferred = Promise.defer(); var self = this; }
...
var count = -1;
async.waterfall([
function(next) {
conn.sobject(config.bigTable).count({}, next);
},
function(_count, next) {
count = _count;
conn.bulk.query("SELECT Id, Name FROM " + config.bigTable)
.on('record', function(rec) { records.push(rec); })
.on('error', function(err) { next(err); })
.stream().pipe(fstream)
.on('finish', function() { next(null, records); });
}
], function(err, records) {
if (err) { throw err; }
...
function Readable(options) { Duplex = Duplex || require('./_stream_duplex'); if (!(this instanceof Readable)) return new Readable(options); this._readableState = new ReadableState(options, this); // legacy this.readable = true; if (options && typeof options.read === 'function') this._read = options.read; Stream.call(this); }
n/a
_execute = function (options) { var self = this; var logger = this._conn._logger; var responseTarget = options.responseTarget; var autoFetch = options.autoFetch; var maxFetch = options.maxFetch; var scanAll = options.scanAll; return Promise.resolve( self._locator ? self._conn._baseUrl() + "/query/" + self._locator : self.toSOQL().then(function(soql) { self.totalFetched = 0; logger.debug("SOQL = " + soql); return self._conn._baseUrl() + "/" + (scanAll ? "queryAll" : "query") + "?q=" + encodeURIComponent(soql); }) ).then(function(url) { return self._conn.request({ method: 'GET', url: url, headers: options.headers }); }).then(function(data) { self.emit("fetch"); self.totalSize = data.totalSize; var res; switch(responseTarget) { case ResponseTargets.SingleRecord: res = data.records && data.records.length > 0 ? data.records[0] : null; break; case ResponseTargets.Records: res = data.records; break; case ResponseTargets.Count: res = data.totalSize; break; default: res = data; } // only fire response event when it should be notified per fetch if (responseTarget !== ResponseTargets.Records) { self.emit("response", res, self); } // streaming record instances var numRecords = (data.records && data.records.length) || 0; for (var i=0; i<numRecords; i++) { if (self.totalFetched >= maxFetch) { self._finished = true; break; } var record = data.records[i]; self.push(record); self.emit('record', record, self.totalFetched++, self); } if (data.nextRecordsUrl) { self._locator = data.nextRecordsUrl.split('/').pop(); } self._finished = self._finished || data.done || !autoFetch; if (self._finished) { self.push(null); } else { self._execute(options); } return res; }); }
...
});
// flag to prevent re-execution
this._executed = true;
// start actual query
logger.debug('>>> Query start >>>');
this._execute(options).then(function() {
logger.debug('*** Query finished ***');
}).fail(function(err) {
logger.debug('--- Query error ---');
self.emit('error', err);
});
// return Query instance for chaining
...
_expandFields = function () { if (this._soql) { return Promise.reject(new Error("Cannot expand fields for the query which has already built SOQL.")); } var self = this; var logger = self._conn._logger; var conn = this._conn; var table = this._config.table; var fields = this._config.fields || []; logger.debug('_expandFields: table = ' + table + ', fields = ' + fields.join(', ')); return Promise.all([ Promise.resolve(self._parent ? findRelationTable(table) : table) .then(function(table) { return Promise.all( _.map(fields, function(field) { return expandAsteriskField(table, field); }) ).then(function(expandedFields) { self._config.fields = _.flatten(expandedFields); }); }), Promise.all( _.map(self._children || [], function(childQuery) { return childQuery._expandFields(); }) ) ]); function findRelationTable(rname) { var ptable = self._parent._config.table; logger.debug('finding table for relation "' + rname + '" in "' + ptable + '"...'); return describeCache(ptable).then(function(sobject) { var upperRname = rname.toUpperCase(); var childRelation = _.find(sobject.childRelationships, function(cr) { return (cr.relationshipName || '').toUpperCase() === upperRname; }); return childRelation ? childRelation.childSObject : Promise.reject(new Error("No child relationship found: " + rname )); }); } function describeCache(table) { logger.debug('describe cache: '+table); var deferred = Promise.defer(); conn.describe$(table, function(err, sobject) { logger.debug('... done.'); if (err) { deferred.reject(err); } else { deferred.resolve(sobject); } }); return deferred.promise; } function expandAsteriskField(table, field) { logger.debug('expanding field "'+ field + '" in "' + table + '"...'); var fpath = field.split('.'); return fpath[fpath.length - 1] === '*' ? describeCache(table).then(function(sobject) { logger.debug('table '+table+'has been described'); if (fpath.length > 1) { var rname = fpath.shift(); var rfield = _.find(sobject.fields, function(f) { return f.relationshipName && f.relationshipName.toUpperCase() === rname.toUpperCase(); }); if (rfield) { var rtable = rfield.referenceTo.length === 1 ? rfield.referenceTo[0] : 'Name'; return expandAsteriskField(rtable, fpath.join('.')).then(function(fpaths) { return _.map(fpaths, function(fpath) { return rname + '.' + fpath; }); }); } else { return []; } } else { return _.map(sobject.fields, function(f) { return f.name; }); } }) : Promise.resolve([ field ]); } }
...
_.map(fields, function(field) { return expandAsteriskField(table, field); })
).then(function(expandedFields) {
self._config.fields = _.flatten(expandedFields);
});
}),
Promise.all(
_.map(self._children || [], function(childQuery) {
return childQuery._expandFields();
})
)
]);
function findRelationTable(rname) {
var ptable = self._parent._config.table;
logger.debug('finding table for relation "' + rname + '" in "' + ptable + '"...
x27;);
...
_read = function (size) { if (!this._finished && !this._executed) { this.execute({ autoFetch: true }); } }
n/a
addListener = function (e, fn) { if (e === 'record') { var self = this; this.on('readable', function() { while(self.read() !== null) {} // discard buffered records }); } return Query.super_.prototype.on.call(this, e, fn); }
n/a
autoFetch = function (autoFetch) { this._options.autoFetch = autoFetch; return this; }
n/a
del = function (type, callback) { if (typeof type === 'function') { callback = type; type = null; } type = type || (this._config && this._config.table); if (!type) { throw new Error("SOQL based query needs SObject type information to bulk delete."); } var batch = this._conn.sobject(type).deleteBulk(); var deferred = Promise.defer(); var handleError = function(err) { if (err.name === 'ClientInputError') { deferred.resolve([]); } // if batch input receives no records else { deferred.reject(err); } }; this.on('error', handleError) .pipe(batch) .on('response', function(res) { deferred.resolve(res); }) .on('error', handleError); return deferred.promise.thenCall(callback); }
n/a
delete = function (type, callback) { if (typeof type === 'function') { callback = type; type = null; } type = type || (this._config && this._config.table); if (!type) { throw new Error("SOQL based query needs SObject type information to bulk delete."); } var batch = this._conn.sobject(type).deleteBulk(); var deferred = Promise.defer(); var handleError = function(err) { if (err.name === 'ClientInputError') { deferred.resolve([]); } // if batch input receives no records else { deferred.reject(err); } }; this.on('error', handleError) .pipe(batch) .on('response', function(res) { deferred.resolve(res); }) .on('error', handleError); return deferred.promise.thenCall(callback); }
...
/**
*
*/
describe("delete account info", function() {
it("should not get any account for delete account id", function(done) {
async.waterfall([
function(cb) {
conn.apex.delete('/JSforceTestApexRest/' + accountId, cb);
},
function(ret, cb) {
conn.sobject('Account').find({ Id: accountId }, cb);
}
], function(err, records) {
if (err) { throw err; }
assert.ok(records.length === 0);
...
destroy = function (type, callback) { if (typeof type === 'function') { callback = type; type = null; } type = type || (this._config && this._config.table); if (!type) { throw new Error("SOQL based query needs SObject type information to bulk delete."); } var batch = this._conn.sobject(type).deleteBulk(); var deferred = Promise.defer(); var handleError = function(err) { if (err.name === 'ClientInputError') { deferred.resolve([]); } // if batch input receives no records else { deferred.reject(err); } }; this.on('error', handleError) .pipe(batch) .on('response', function(res) { deferred.resolve(res); }) .on('error', handleError); return deferred.promise.thenCall(callback); }
...
* @returns {Promise.<RecordResult>}
*/
RecordReference.prototype.destroy = function(options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
return this._conn.destroy(this.type, this.id, options, callback);
};
/**
* Get blob field as stream
*
* @param {String} fieldName - Blob field name
* @returns {stream.Stream}
...
exec = function (options, callback) { var self = this; var logger = this._conn._logger; var deferred = this._deferred; if (this._executed) { deferred.reject(new Error("re-executing already executed query")); return this; } if (this._finished) { deferred.reject(new Error("executing already closed query")); return this; } if (typeof options === "function") { callback = options; options = {}; } options = options || {}; options = { headers: options.headers || self._options.headers, responseTarget: options.responseTarget || self._options.responseTarget, autoFetch: options.autoFetch || self._options.autoFetch, maxFetch: options.maxFetch || self._options.maxFetch, scanAll: options.scanAll || self._options.scanAll }; // callback and promise resolution; var promiseCallback = function(err, res) { if (_.isFunction(callback)) { try { res = callback(err, res); err = null; } catch(e) { err = e; } } if (err) { deferred.reject(err); } else { deferred.resolve(res); } }; this.once('response', function(res) { promiseCallback(null, res); }); this.once('error', function(err) { promiseCallback(err); }); // collect fetched records in array // only when response target is Records and // either callback or chaining promises are available to this query. this.once('fetch', function() { if (options.responseTarget === ResponseTargets.Records && (self._chaining || callback)) { logger.debug('--- collecting all fetched records ---'); var records = []; var onRecord = function(record) { records.push(record); }; self.on('record', onRecord); self.once('end', function() { self.removeListener('record', onRecord); self.emit('response', records, self); }); } }); // flag to prevent re-execution this._executed = true; // start actual query logger.debug('>>> Query start >>>'); this._execute(options).then(function() { logger.debug('*** Query finished ***'); }).fail(function(err) { logger.debug('--- Query error ---'); self.emit('error', err); }); // return Query instance for chaining return this; }
...
mapped[prop] = noeval ? record[prop] : evalMapping(record[prop], rec);
}
return mapped;
});
function evalMapping(value, mapping) {
if (_.isString(value)) {
var m = /^\$\{(\w+)\}$/.exec(value);
if (m) { return mapping[m[1]]; }
return value.replace(/\$\{(\w+)\}/g, function($0, prop) {
var v = mapping[prop];
return _.isNull(v) || _.isUndefined(v) ? "" : String(v);
});
} else {
return value;
...
execute = function (options, callback) { var self = this; var logger = this._conn._logger; var deferred = this._deferred; if (this._executed) { deferred.reject(new Error("re-executing already executed query")); return this; } if (this._finished) { deferred.reject(new Error("executing already closed query")); return this; } if (typeof options === "function") { callback = options; options = {}; } options = options || {}; options = { headers: options.headers || self._options.headers, responseTarget: options.responseTarget || self._options.responseTarget, autoFetch: options.autoFetch || self._options.autoFetch, maxFetch: options.maxFetch || self._options.maxFetch, scanAll: options.scanAll || self._options.scanAll }; // callback and promise resolution; var promiseCallback = function(err, res) { if (_.isFunction(callback)) { try { res = callback(err, res); err = null; } catch(e) { err = e; } } if (err) { deferred.reject(err); } else { deferred.resolve(res); } }; this.once('response', function(res) { promiseCallback(null, res); }); this.once('error', function(err) { promiseCallback(err); }); // collect fetched records in array // only when response target is Records and // either callback or chaining promises are available to this query. this.once('fetch', function() { if (options.responseTarget === ResponseTargets.Records && (self._chaining || callback)) { logger.debug('--- collecting all fetched records ---'); var records = []; var onRecord = function(record) { records.push(record); }; self.on('record', onRecord); self.once('end', function() { self.removeListener('record', onRecord); self.emit('response', records, self); }); } }); // flag to prevent re-execution this._executed = true; // start actual query logger.debug('>>> Query start >>>'); this._execute(options).then(function() { logger.debug('*** Query finished ***'); }).fail(function(err) { logger.debug('--- Query error ---'); self.emit('error', err); }); // return Query instance for chaining return this; }
...
if (err.name !== 'PollingTimeout') {
cleanup();
}
};
batch.on('response', cleanup);
batch.on('error', cleanupOnError);
batch.on('queue', function() { batch.poll(self.pollInterval, self.pollTimeout); });
return batch.execute(input, callback);
};
/**
* Execute bulk query and get record stream
*
* @param {String} soql - SOQL to execute in bulk job
* @returns {RecordStream.Parsable} - Record stream, convertible to CSV data stream
...
explain = function (callback) { var self = this; var logger = this._conn._logger; return self.toSOQL().then(function(soql) { logger.debug("SOQL = " + soql); var url = "/query/?explain=" + encodeURIComponent(soql); return self._conn.request(url); }).thenCall(callback); }
...
});
/**
*
*/
describe("explain query plan of report", function() {
it("should get explain result", function(done) {
conn.analytics.report(reportId).explain(function(err, result) {
if (err) { throw err; }
assert.ok(_.isArray(result.plans));
for (var i=0; i<result.plans.length; i++) {
var plan = result.plans[i];
assert.ok(_.isNumber(plan.cardinality));
assert.ok(_.isArray(plan.fields));
assert.ok(_.isString(plan.leadingOperationType));
...
filter = function (fn) { return this.pipe(RecordStream.map(fn)); }
...
var name = protocol.replace(/:$/, '').toLowerCase() + '_proxy',
upcase = name.toUpperCase(),
env = process.env,
keys, proxy;
if (name === 'http_proxy' && env.REQUEST_METHOD) {
keys = Object.keys(env).filter(function(k) { return /^http_proxy$/i.test(k) });
if (keys.length === 1) {
if (keys[0] === name && env[upcase] === undefined)
proxy = env[name];
} else if (keys.length > 1) {
proxy = env[name];
}
proxy = proxy || env['CGI_' + upcase];
...
include = function (childRelName, conditions, fields, options) { if (this._soql) { throw Error("Cannot include child relationship into the query which has already built SOQL."); } if (_.isObject(childRelName)) { var includes = childRelName; for (var crname in includes) { var config = includes[crname]; this.include(crname, config.conditions, config.fields, config); } return; } var childConfig = { table: childRelName, conditions: conditions, fields: fields, limit: options && options.limit, offset: options && (options.offset || options.skip), sort: options && options.sort }; if (!_.isArray(this._config.includes)) this._config.includes = []; this._config.includes.push(childConfig); var childQuery = new SubQuery(this._conn, this, childConfig); this._children = this._children || []; this._children.push(childQuery); return childQuery; }
...
this._soql = config;
} else if (config.locator && config.locator.indexOf("/") >= 0) { // if locator given in url for next records
this._locator = config.locator.split("/").pop();
} else {
this._config = config;
this.select(config.fields);
if (config.includes) {
this.include(config.includes);
}
}
this._options = _.defaults({
maxFetch: 10000,
autoFetch: false,
scanAll: false,
responseTarget: ResponseTargets.QueryResult
...
limit = function (limit) { if (this._soql) { throw Error("Cannot set limit for the query which has already built SOQL."); } this._config.limit = limit; return this; }
...
/**
*
*/
describe("find records with multiple sort options and limit option", function() {
it("should return sorted records", function(done) {
Opportunity.find({}, { "Owner.Name" : 1, CloseDate : 1 })
.sort({ "Owner.Name" : 1, CloseDate : -1 })
.limit(10)
.exec(function(err, records) {
if (err) { throw err; }
assert.ok(_.isArray(records));
assert.ok(records.length > 0);
assert.ok(records.length < 11);
for (var i=0; i<records.length - 1; i++) {
var r1 = records[i], r2 = records[i+1];
...
map = function (fn) { return this.pipe(RecordStream.map(fn)); }
...
});
}).then(function(res) {
var results;
if (job.operation === 'query') {
var conn = bulk._conn;
var resultIds = res['result-list'].result;
results = res['result-list'].result;
results = _.map(_.isArray(results) ? results : [ results ], function(id) {
return {
id: id,
batchId: batchId,
jobId: jobId
};
});
} else {
...
maxFetch = function (maxFetch) { this._options.maxFetch = maxFetch; return this; }
n/a
offset = function (offset) { if (this._soql) { throw Error("Cannot set skip/offset for the query which has already built SOQL."); } this._config.offset = offset; return this; }
n/a
on = function (e, fn) { if (e === 'record') { var self = this; this.on('readable', function() { while(self.read() !== null) {} // discard buffered records }); } return Query.super_.prototype.on.call(this, e, fn); }
...
return this._conn.request(url).thenCall(callback);
};
/*--------------------------------------------*/
/*
* Register hook in connection instantiation for dynamically adding this API module features
*/
jsforce.on('connection:new', function(conn) {
conn.analytics = new Analytics(conn);
});
module.exports = Analytics;
},{}]},{},[1])(1)
});
...
orderby = function (sort, dir) { if (this._soql) { throw Error("Cannot set sort for the query which has already built SOQL."); } if (_.isString(sort) && _.isString(dir)) { sort = [ [ sort, dir ] ]; } this._config.sort = sort; return this; }
n/a
run = function (options, callback) { var self = this; var logger = this._conn._logger; var deferred = this._deferred; if (this._executed) { deferred.reject(new Error("re-executing already executed query")); return this; } if (this._finished) { deferred.reject(new Error("executing already closed query")); return this; } if (typeof options === "function") { callback = options; options = {}; } options = options || {}; options = { headers: options.headers || self._options.headers, responseTarget: options.responseTarget || self._options.responseTarget, autoFetch: options.autoFetch || self._options.autoFetch, maxFetch: options.maxFetch || self._options.maxFetch, scanAll: options.scanAll || self._options.scanAll }; // callback and promise resolution; var promiseCallback = function(err, res) { if (_.isFunction(callback)) { try { res = callback(err, res); err = null; } catch(e) { err = e; } } if (err) { deferred.reject(err); } else { deferred.resolve(res); } }; this.once('response', function(res) { promiseCallback(null, res); }); this.once('error', function(err) { promiseCallback(err); }); // collect fetched records in array // only when response target is Records and // either callback or chaining promises are available to this query. this.once('fetch', function() { if (options.responseTarget === ResponseTargets.Records && (self._chaining || callback)) { logger.debug('--- collecting all fetched records ---'); var records = []; var onRecord = function(record) { records.push(record); }; self.on('record', onRecord); self.once('end', function() { self.removeListener('record', onRecord); self.emit('response', records, self); }); } }); // flag to prevent re-execution this._executed = true; // start actual query logger.debug('>>> Query start >>>'); this._execute(options).then(function() { logger.debug('*** Query finished ***'); }).fail(function(err) { logger.debug('--- Query error ---'); self.emit('error', err); }); // return Query instance for chaining return this; }
...
var len = queue.length;
while(len) {
currentQueue = queue;
queue = [];
while (++queueIndex < len) {
if (currentQueue) {
currentQueue[queueIndex].run();
}
}
queueIndex = -1;
len = queue.length;
}
currentQueue = null;
draining = false;
...
scanAll = function (scanAll) { this._options.scanAll = scanAll; return this; }
...
*/
Connection.prototype.queryAll = function(soql, options, callback) {
if (typeof options === 'function') {
callback = options;
options = undefined;
}
var query = new Query(this, soql, options);
query.scanAll(true);
if (callback) {
query.run(callback);
}
return query;
};
/**
...
select = function (fields) { if (this._soql) { throw Error("Cannot set select fields for the query which has already built SOQL."); } fields = fields || '*'; if (_.isString(fields)) { fields = fields.split(/\s*,\s*/); } else if (_.isObject(fields) && !_.isArray(fields)) { var _fields = []; for (var k in fields) { if (fields[k]) { _fields.push(k); } } fields = _fields; } this._config.fields = fields; return this; }
...
this._conn = conn;
if (_.isString(config)) { // if query config is string, it is given in SOQL.
this._soql = config;
} else if (config.locator && config.locator.indexOf("/") >= 0) { // if locator given in url for next records
this._locator = config.locator.split("/").pop();
} else {
this._config = config;
this.select(config.fields);
if (config.includes) {
this.include(config.includes);
}
}
this._options = _.defaults({
maxFetch: 10000,
autoFetch: false,
...
setResponseTarget = function (responseTarget) { if (responseTarget in ResponseTargets) { this._options.responseTarget = responseTarget; } return this; }
...
includes: options.includes,
table: this.type,
conditions: conditions,
limit: options.limit,
offset: options.offset || options.skip
};
var query = new Query(this._conn, config, options);
query.setResponseTarget(Query.ResponseTargets.Records);
if (callback) { query.run(callback); }
return query;
};
/**
* Fetch one record which matches given conditions
*
...
skip = function (offset) { if (this._soql) { throw Error("Cannot set skip/offset for the query which has already built SOQL."); } this._config.offset = offset; return this; }
n/a
sort = function (sort, dir) { if (this._soql) { throw Error("Cannot set sort for the query which has already built SOQL."); } if (_.isString(sort) && _.isString(dir)) { sort = [ [ sort, dir ] ]; } this._config.sort = sort; return this; }
...
/**
*
*/
describe("find records with sort option", function() {
it("should return sorted records", function(done) {
Opportunity.find({}, { CloseDate : 1 })
.sort("CloseDate", "desc")
.exec(function(err, records) {
if (err) { throw err; }
assert.ok(_.isArray(records));
assert.ok(records.length > 0);
for (var i=0; i<records.length - 1; i++) {
assert.ok(records[i].CloseDate >= records[i+1].CloseDate);
}
...
stream = function (type, options) { type = type || 'csv'; var converter = DataStreamConverters[type]; if (!converter) { throw new Error('Converting [' + type + '] data stream is not supported.'); } if (!this._dataStream) { this._dataStream = new PassThrough(); this.pipe(converter.serialize(options)) .pipe(this._dataStream); } return this._dataStream; }
...
/**
* @private
*/
Batch.prototype._setupDataStreams = function() {
var batch = this;
var converterOptions = { nullValue : '#N/A' };
this._uploadStream = new RecordStream.Serializable();
this._uploadDataStream = this._uploadStream.stream('csv', converterOptions);
this._downloadStream = new RecordStream.Parsable();
this._downloadDataStream = this._downloadStream.stream('csv', converterOptions);
this.on('finish', function() {
batch._uploadStream.end();
});
this._uploadDataStream.once('readable', function() {
...
then = function (onResolved, onReject) { this._chaining = true; if (!this._finished && !this._executed) { this.execute(); } return this._deferred.promise.then.apply(this._deferred.promise, arguments); }
...
method : 'POST',
path : "/job",
body : body,
headers : {
"Content-Type" : "application/xml; charset=utf-8"
},
responseType: "application/xml"
}).then(function(res) {
self.emit("open", res.jobInfo);
self.id = res.jobInfo.id;
self.state = res.jobInfo.state;
return res.jobInfo;
}, function(err) {
self.emit("error", err);
throw err;
...
thenCall = function (callback) { if (_.isFunction(callback)) { this.then(function(res) { process.nextTick(function() { callback(null, res); }); }, function(err) { process.nextTick(function() { callback(err); }); }); } return this; }
...
* @param {Callback.<Analytics~ReportResult>} [callback] - Callback function
* @returns {Promise.<Analytics~ReportResult>}
*/
ReportInstance.prototype.retrieve = function(callback) {
var conn = this._conn,
report = this._report;
var url = [ conn._baseUrl(), "analytics", "reports", report.id, "instances", this.id ].join('
;/');
return conn.request(url).thenCall(callback);
};
/**
* Report object in Analytics API
*
* @protected
* @class Analytics~Report
...
toSOQL = function (callback) { var self = this; return Promise.resolve(self._soql || self._expandFields().then(function() { return SOQLBuilder.createSOQL(self._config); }) ).thenCall(callback); }
...
var autoFetch = options.autoFetch;
var maxFetch = options.maxFetch;
var scanAll = options.scanAll;
return Promise.resolve(
self._locator ?
self._conn._baseUrl() + "/query/" + self._locator :
self.toSOQL().then(function(soql) {
self.totalFetched = 0;
logger.debug("SOQL = " + soql);
return self._conn._baseUrl() + "/" + (scanAll ? "queryAll" : "query") + "?q=" + encodeURIComponent
(soql);
})
).then(function(url) {
return self._conn.request({
method: 'GET',
...
update = function (mapping, type, callback) { if (typeof type === 'function') { callback = type; type = null; } type = type || (this._config && this._config.table); if (!type) { throw new Error("SOQL based query needs SObject type information to bulk update."); } var updateStream = _.isFunction(mapping) ? RecordStream.map(mapping) : RecordStream.recordMapStream(mapping); var batch = this._conn.sobject(type).updateBulk(); var deferred = Promise.defer(); var handleError = function(err) { if (err.name === 'ClientInputError') { deferred.resolve([]); } // if batch input receives no records else { deferred.reject(err); } }; this.on('error', handleError) .pipe(updateStream) .on('error', handleError) .pipe(batch) .on('response', function(res) { deferred.resolve(res); }) .on('error', handleError); return deferred.promise.thenCall(callback); }
...
RecordReference.prototype.update = function(record, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
record = _.clone(record);
record.Id = this.id;
return this._conn.update(this.type, record, options, callback);
};
/**
* Synonym of Record#destroy()
*
* @method RecordReference#delete
* @param {Object} [options] - Options for rest api.
...
where = function (conditions) { if (this._soql) { throw Error("Cannot set where conditions for the query which has already built SOQL."); } this._config.conditions = conditions; return this; }
n/a
quick_action = function (conn, path) { this._conn = conn; this._path = path; }
n/a
defaultValues = function (contextId, callback) { if (typeof contextId === 'function') { callback = contextId; contextId = null; } var url = this._path + "/defaultValues"; if (contextId) { url += "/" + contextId; } return this._conn.request(url).thenCall(callback); }
...
});
/**
*
*/
describe("get default values of the action", function() {
it("should return default field values", function(done) {
action.defaultValues(function(err, res) {
if (err) { throw err; }
assert.ok(_.isObject(res));
// assert.ok(res.Subject === null);
assert.ok(res.Description === null);
assert.ok(res.WhoId === null);
assert.ok(res.WhatId === null);
}.check(done));
...
describe = function (callback) { var url = this._path + "/describe"; return this._conn.request(url).thenCall(callback); }
...
/**
* Describe SObject metadata
*
* @param {Callback.<DescribeSObjectResult>} [callback] - Callback function
* @returns {Promise.<DescribeSObjectResult>}
*/
SObject.prototype.describe = function(callback) {
return this._conn.describe(this.type, callback);
};
/**
* Get record representation instance by given id
*
* @param {String} id - A record ID
* @returns {RecordReference}
...
execute = function (contextId, record, callback) { var body = { contextId: contextId, record: record }; return this._conn.requestPost(this._path, body).thenCall(callback); }
...
if (err.name !== 'PollingTimeout') {
cleanup();
}
};
batch.on('response', cleanup);
batch.on('error', cleanupOnError);
batch.on('queue', function() { batch.poll(self.pollInterval, self.pollTimeout); });
return batch.execute(input, callback);
};
/**
* Execute bulk query and get record stream
*
* @param {String} soql - SOQL to execute in bulk job
* @returns {RecordStream.Parsable} - Record stream, convertible to CSV data stream
...
record = function (conn, type, id) { this._conn = conn; this.type = type; this.id = id; }
...
});
/**
*
*/
describe("update account", function() {
it("should update successfully", function(done) {
conn.sobject('Account').record(account.Id).update({ Name : "Hello2
x22; }, function(err, ret) {
if (err) { throw err; }
assert.ok(ret.success);
}.check(done));
});
describe("then retrieve the account", function() {
it("should return updated account object", function(done) {
...
blob = function (fieldName) { var url = [ this._conn._baseUrl(), 'sobjects', this.type, this.id, fieldName ].join('/'); return this._conn.request(url).stream(); }
n/a
del = function (options, callback) { if (typeof options === 'function') { callback = options; options = {}; } return this._conn.destroy(this.type, this.id, options, callback); }
n/a
delete = function (options, callback) { if (typeof options === 'function') { callback = options; options = {}; } return this._conn.destroy(this.type, this.id, options, callback); }
...
/**
*
*/
describe("delete account info", function() {
it("should not get any account for delete account id", function(done) {
async.waterfall([
function(cb) {
conn.apex.delete('/JSforceTestApexRest/' + accountId, cb);
},
function(ret, cb) {
conn.sobject('Account').find({ Id: accountId }, cb);
}
], function(err, records) {
if (err) { throw err; }
assert.ok(records.length === 0);
...
destroy = function (options, callback) { if (typeof options === 'function') { callback = options; options = {}; } return this._conn.destroy(this.type, this.id, options, callback); }
...
* @returns {Promise.<RecordResult>}
*/
RecordReference.prototype.destroy = function(options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
return this._conn.destroy(this.type, this.id, options, callback);
};
/**
* Get blob field as stream
*
* @param {String} fieldName - Blob field name
* @returns {stream.Stream}
...
retrieve = function (options, callback) { if (typeof options === 'function') { callback = options; options = {}; } return this._conn.retrieve(this.type, this.id, options, callback); }
...
}
self.check(function(err, res) {
if (err) {
self.emit('error', err);
} else {
if (res.state === "Failed") {
if (parseInt(res.numberRecordsProcessed, 10) > 0) {
self.retrieve();
} else {
self.emit('error', new Error(res.stateMessage));
}
} else if (res.state === "Completed") {
self.retrieve();
} else {
self.emit('progress', res);
...
update = function (record, options, callback) { if (typeof options === 'function') { callback = options; options = {}; } record = _.clone(record); record.Id = this.id; return this._conn.update(this.type, record, options, callback); }
...
RecordReference.prototype.update = function(record, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
record = _.clone(record);
record.Id = this.id;
return this._conn.update(this.type, record, options, callback);
};
/**
* Synonym of Record#destroy()
*
* @method RecordReference#delete
* @param {Object} [options] - Options for rest api.
...
record_stream = function () { RecordStream.super_.call(this, { objectMode: true }); }
n/a
Parsable = function () { Parsable.super_.call(this); this._dataStream = null; }
...
* @private
*/
Batch.prototype._setupDataStreams = function() {
var batch = this;
var converterOptions = { nullValue : '#N/A' };
this._uploadStream = new RecordStream.Serializable();
this._uploadDataStream = this._uploadStream.stream('csv', converterOptions);
this._downloadStream = new RecordStream.Parsable();
this._downloadDataStream = this._downloadStream.stream('csv', converterOptions);
this.on('finish', function() {
batch._uploadStream.end();
});
this._uploadDataStream.once('readable', function() {
batch.job.open().then(function() {
...
Serializable = function () { Serializable.super_.call(this); this._dataStream = null; }
...
/**
* @private
*/
Batch.prototype._setupDataStreams = function() {
var batch = this;
var converterOptions = { nullValue : '#N/A' };
this._uploadStream = new RecordStream.Serializable();
this._uploadDataStream = this._uploadStream.stream('csv', converterOptions);
this._downloadStream = new RecordStream.Parsable();
this._downloadDataStream = this._downloadStream.stream('csv', converterOptions);
this.on('finish', function() {
batch._uploadStream.end();
});
...
filter = function (fn) { var filterStream = new RecordStream.Serializable(); filterStream._transform = function(record, enc, callback) { if (fn(record)) { this.push(record); } callback(); }; return filterStream; }
...
var name = protocol.replace(/:$/, '').toLowerCase() + '_proxy',
upcase = name.toUpperCase(),
env = process.env,
keys, proxy;
if (name === 'http_proxy' && env.REQUEST_METHOD) {
keys = Object.keys(env).filter(function(k) { return /^http_proxy$/i.test(k) });
if (keys.length === 1) {
if (keys[0] === name && env[upcase] === undefined)
proxy = env[name];
} else if (keys.length > 1) {
proxy = env[name];
}
proxy = proxy || env['CGI_' + upcase];
...
map = function (fn) { var mapStream = new RecordStream.Serializable(); mapStream._transform = function(record, enc, callback) { var rec = fn(record) || record; // if not returned record, use same record this.push(rec); callback(); }; return mapStream; }
...
});
}).then(function(res) {
var results;
if (job.operation === 'query') {
var conn = bulk._conn;
var resultIds = res['result-list'].result;
results = res['result-list'].result;
results = _.map(_.isArray(results) ? results : [ results ], function(id) {
return {
id: id,
batchId: batchId,
jobId: jobId
};
});
} else {
...
recordMapStream = function (record, noeval) { return RecordStream.map(function(rec) { var mapped = { Id: rec.Id }; for (var prop in record) { mapped[prop] = noeval ? record[prop] : evalMapping(record[prop], rec); } return mapped; }); function evalMapping(value, mapping) { if (_.isString(value)) { var m = /^\$\{(\w+)\}$/.exec(value); if (m) { return mapping[m[1]]; } return value.replace(/\$\{(\w+)\}/g, function($0, prop) { var v = mapping[prop]; return _.isNull(v) || _.isUndefined(v) ? "" : String(v); }); } else { return value; } } }
...
callback = type;
type = null;
}
type = type || (this._config && this._config.table);
if (!type) {
throw new Error("SOQL based query needs SObject type information to bulk update.");
}
var updateStream = _.isFunction(mapping) ? RecordStream.map(mapping) : RecordStream.recordMapStream
(mapping);
var batch = this._conn.sobject(type).updateBulk();
var deferred = Promise.defer();
var handleError = function(err) {
if (err.name === 'ClientInputError') { deferred.resolve([]); } // if batch input receives no records
else { deferred.reject(err); }
};
this.on('error', handleError)
...
function Transform(options) { if (!(this instanceof Transform)) return new Transform(options); Duplex.call(this, options); this._transformState = new TransformState(this); var stream = this; // start out asking for a readable event once data is transformed. this._readableState.needReadable = true; // we have implemented the _read method, and done the other things // that Readable wants before the first _read call, so unset the // sync guard flag. this._readableState.sync = false; if (options) { if (typeof options.transform === 'function') this._transform = options.transform; if (typeof options.flush === 'function') this._flush = options.flush; } // When the writable side finishes, then flush out anything remaining. this.once('prefinish', function () { if (typeof this._flush === 'function') this._flush(function (er, data) { done(stream, er, data); });else done(stream); }); }
n/a
_transform = function (record, enc, callback) { this.emit('record', record); this.push(record); callback(); }
n/a
filter = function (fn) { return this.pipe(RecordStream.filter(fn)); }
...
var name = protocol.replace(/:$/, '').toLowerCase() + '_proxy',
upcase = name.toUpperCase(),
env = process.env,
keys, proxy;
if (name === 'http_proxy' && env.REQUEST_METHOD) {
keys = Object.keys(env).filter(function(k) { return /^http_proxy$/i.test(k) });
if (keys.length === 1) {
if (keys[0] === name && env[upcase] === undefined)
proxy = env[name];
} else if (keys.length > 1) {
proxy = env[name];
}
proxy = proxy || env['CGI_' + upcase];
...
map = function (fn) { return this.pipe(RecordStream.map(fn)); }
...
});
}).then(function(res) {
var results;
if (job.operation === 'query') {
var conn = bulk._conn;
var resultIds = res['result-list'].result;
results = res['result-list'].result;
results = _.map(_.isArray(results) ? results : [ results ], function(id) {
return {
id: id,
batchId: batchId,
jobId: jobId
};
});
} else {
...
soap = function (conn, options) { SOAP.super_.apply(this, arguments); this._endpointUrl = options.endpointUrl; this._xmlns = options.xmlns || 'urn:partner.soap.sforce.com'; }
n/a
super_ = function (conn, options) { options = options || {}; this._conn = conn; this.on('resume', function(err) { conn.emit('resume', err); }); this._responseType = options.responseType; this._transport = options.transport || conn._transport; this._noContentResponse = options.noContentResponse; }
n/a
_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(''); }
...
}
}
}
}
/** @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);
};
...
beforeSend = function (request) { request.body = this._createEnvelope(request.message); }
...
if (refreshDelegate && refreshDelegate._refreshing) {
refreshDelegate.once('resume', onResume);
return deferred.promise.thenCall(callback);
}
// hook before sending
self.beforeSend(request);
self.emit('request', request);
logger.debug("<request> method=" + request.method + ", url=" + request.url);
var requestTime = Date.now();
return this._transport.httpRequest(request).then(function(response) {
var responseTime = Date.now();
...
getResponseBody = function (response) { var body = SOAP.super_.prototype.getResponseBody.call(this, response); return lookupValue(body, [ /:Envelope$/, /:Body$/, /.+/ ]); }
...
refreshDelegate.refresh(requestTime, onResume);
return deferred.promise;
}
if (self.isErrorResponse(response)) {
var err = self.getError(response);
throw err;
}
return self.getResponseBody(response);
}, function(err) {
var responseTime = Date.now();
logger.debug("elappsed time : " + (responseTime - requestTime) + "msec");
logger.error(err);
throw err;
})
.thenCall(callback);
...
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
*/
Metadata.prototype._invoke = function(method, message, callback) {
var soapEndpoint = new SOAP(this._conn, {
xmlns: "http://soap.sforce.com/2006/04/metadata",
endpointUrl: this._conn.instanceUrl + "/services/Soap/m/" + this._conn.version
});
return soapEndpoint.invoke(method, message).then(function(res) {
return res.result;
}).thenCall(callback);
};
/**
* @typedef {Object} Metadata~MetadataInfo
...
isSessionExpired = function (response) { return response.statusCode === 500 && /<faultcode>[a-zA-Z]+:INVALID_SESSION_ID<\/faultcode>/.test(response.body); }
...
var responseTime = Date.now();
logger.debug("elappsed time : " + (responseTime - requestTime) + "msec");
logger.debug("<response> status=" + response.statusCode + ", url=" + request.url);
self.emit('response', response);
// Refresh token if session has been expired and requires authentication
// when session refresh delegate is available
if (self.isSessionExpired(response) && refreshDelegate) {
refreshDelegate.refresh(requestTime, onResume);
return deferred.promise;
}
if (self.isErrorResponse(response)) {
var err = self.getError(response);
throw err;
}
...
parseError = function (body) { var error = lookupValue(body, [ /:Envelope$/, /:Body$/, /:Fault$/ ]); return { errorCode: error.faultcode, message: error.faultstring }; }
...
/**
* Get error message in response
* @protected
*/
HttpApi.prototype.getError = function(response, body) {
var error;
try {
error = this.parseError(body || this.parseResponseBody(response));
} catch(e) {}
error = _.isObject(error) && _.isString(error.message) ? error : {
errorCode: 'ERROR_HTTP_' + response.statusCode,
message : response.body
};
var err = new Error(error.message);
err.name = error.errorCode;
...
sobject = function (conn, type) { this._conn = conn; this.type = type; var cacheOptions = { key: "describe." + this.type }; this.describe$ = conn.cache.makeCacheable(this.describe, this, cacheOptions); this.describe = conn.cache.makeResponseCacheable(this.describe, this, cacheOptions); cacheOptions = { key: "layouts." + this.type }; this.layouts$ = conn.cache.makeCacheable(this.layouts, this, cacheOptions); this.layouts = conn.cache.makeResponseCacheable(this.layouts, this, cacheOptions); cacheOptions = { key: "compactLayouts." + this.type }; this.compactLayouts$ = conn.cache.makeCacheable(this.compactLayouts, this, cacheOptions); this.compactLayouts = conn.cache.makeResponseCacheable(this.compactLayouts, this, cacheOptions); cacheOptions = { key: "approvalLayouts." + this.type }; this.approvalLayouts$ = conn.cache.makeCacheable(this.approvalLayouts, this, cacheOptions); this.approvalLayouts = conn.cache.makeResponseCacheable(this.approvalLayouts, this, cacheOptions); }
...
};
Channel.prototype.push = function(events, callback) {
var isArray = _.isArray(events);
events = isArray ? events : [ events ];
var conn = this._streaming._conn;
if (!this._id) {
this._id = conn.sobject('StreamingChannel').findOne({ Name: this._name },
x27;Id')
.then(function(rec) { return rec.Id });
}
return this._id.then(function(id) {
var channelUrl = '/sobjects/StreamingChannel/' + id + '/push';
return conn.requestPost(channelUrl, { pushEvents: events });
}).then(function(rets) {
return isArray ? rets : rets[0];
...
approvalLayouts = function (callback) { var url = "/sobjects/" + this.type + "/describe/approvalLayouts"; return this._conn.request(url, callback); }
...
/**
*
*/
describe("list approval layout for SObject", function() {
it("should return Account approval layout information", function(done) {
Account.approvalLayouts(function(err, res) {
if (err) { throw err; }
assert.ok(_.isArray(res.approvalLayouts));
res.approvalLayouts.forEach(function(alayout) {
assert.ok(alayout.id === null || _.isString(alayout.id));
assert.ok(_.isString(alayout.name));
assert.ok(_.isString(alayout.label));
assert.ok(_.isArray(alayout.layoutItems));
...
bulkload = function (operation, options, input, callback) { return this._conn.bulk.load(this.type, operation, options, input, callback); }
...
* @method SObject#createBulk
* @param {Array.<Record>|stream.Stream|String} [input] - Input source for bulk insert. Accepts array of records, CSv string
, and CSV data input stream.
* @param {Callback.<Array.<RecordResult>>} [callback] - Callback function
* @returns {Bulk~Batch}
*/
SObject.prototype.insertBulk =
SObject.prototype.createBulk = function(input, callback) {
return this.bulkload("insert", input, callback);
};
/**
* Bulkly update records by input data using bulk API
*
* @param {Array.<Record>|stream.Stream|String} [input] - Input source for bulk update Accepts array of records, CSv string
, and CSV data input stream.
* @param {Callback.<Array.<RecordResult>>} [callback] - Callback function
...
compactLayouts = function (callback) { var url = "/sobjects/" + this.type + "/describe/compactLayouts"; return this._conn.request(url, callback); }
...
});
/**
*
*/
describe("list compact layout for SObject", function() {
it("should return Account comact layout information", function(done) {
Account.compactLayouts(function(err, res) {
if (err) { throw err; }
assert.ok(_.isArray(res.compactLayouts));
res.compactLayouts.forEach(function(clayout) {
assert.ok(clayout.id === null || _.isString(clayout.id));
assert.ok(_.isString(clayout.objectType));
assert.ok(_.isArray(clayout.actions));
assert.ok(_.isArray(clayout.fieldItems));
...
count = function (conditions, callback) { if (typeof conditions === 'function') { callback = conditions; conditions = {}; } var query = this.find(conditions, { "count()" : true }); query.setResponseTarget("Count"); if (callback) { query.run(callback); } return query; }
...
it("should get a record stream and file output", function(done) {
var file = __dirname + "/data/BulkQuery_export.csv";
var fstream = fs.createWriteStream(file);
var records = [];
var count = -1;
async.waterfall([
function(next) {
conn.sobject(config.bigTable).count({}, next);
},
function(_count, next) {
count = _count;
conn.bulk.query("SELECT Id, Name FROM " + config.bigTable)
.on('record', function(rec) { records.push(rec); })
.on('error', function(err) { next(err); })
.stream().pipe(fstream)
...
create = function (records, options, callback) { if (typeof options === 'function') { callback = options; options = {}; } return this._conn.create(this.type, records, options, callback); }
...
initialize: function(endpoint, options) {
this.info('New client created for ?', endpoint);
options = options || {};
validateOptions(options, ['interval', 'timeout', 'endpoints', 'proxy', 'retry',
x27;scheduler', 'websocketExtensions', 'tls', 'ca']);
this._channels = new Channel.Set();
this._dispatcher = Dispatcher.create(this, endpoint || this.DEFAULT_ENDPOINT, options
);
this._messageId = 0;
this._state = this.UNCONNECTED;
this._responseCallbacks = {};
this._advice = {
...
createBulk = function (input, callback) { return this.bulkload("insert", input, callback); }
n/a
del = function (ids, options, callback) { if (typeof options === 'function') { callback = options; options = {}; } return this._conn.destroy(this.type, ids, options, callback); }
n/a
delete = function (ids, options, callback) { if (typeof options === 'function') { callback = options; options = {}; } return this._conn.destroy(this.type, ids, options, callback); }
...
/**
*
*/
describe("delete account info", function() {
it("should not get any account for delete account id", function(done) {
async.waterfall([
function(cb) {
conn.apex.delete('/JSforceTestApexRest/' + accountId, cb);
},
function(ret, cb) {
conn.sobject('Account').find({ Id: accountId }, cb);
}
], function(err, records) {
if (err) { throw err; }
assert.ok(records.length === 0);
...
deleteBulk = function (input, callback) { return this.bulkload("delete", input, callback); }
...
callback = type;
type = null;
}
type = type || (this._config && this._config.table);
if (!type) {
throw new Error("SOQL based query needs SObject type information to bulk delete.");
}
var batch = this._conn.sobject(type).deleteBulk();
var deferred = Promise.defer();
var handleError = function(err) {
if (err.name === 'ClientInputError') { deferred.resolve([]); } // if batch input receives no records
else { deferred.reject(err); }
};
this.on('error', handleError)
.pipe(batch)
...
deleteHardBulk = function (input, callback) { return this.bulkload("hardDelete", input, callback); }
n/a
deleted = function (start, end, callback) { return this._conn.deleted(this.type, start, end, callback); }
...
*
* @param {String|Date} start - start date or string representing the start of the interval
* @param {String|Date} end - start date or string representing the end of the interval, must be > start
* @param {Callback.<DeletedRecordsInfo>} [callback] - Callback function
* @returns {Promise.<DeletedRecordsInfo>}
*/
SObject.prototype.deleted = function (start, end, callback) {
return this._conn.deleted(this.type, start, end, callback);
};
/**
* @typedef {Object} LayoutInfo
* @prop {Array.<Object>} layouts - Array of layouts
* @prop {Array.<Object>} recordTypeMappings - Array of record type mappings
*/
...
describe = function (callback) { return this._conn.describe(this.type, callback); }
...
/**
* Describe SObject metadata
*
* @param {Callback.<DescribeSObjectResult>} [callback] - Callback function
* @returns {Promise.<DescribeSObjectResult>}
*/
SObject.prototype.describe = function(callback) {
return this._conn.describe(this.type, callback);
};
/**
* Get record representation instance by given id
*
* @param {String} id - A record ID
* @returns {RecordReference}
...
destroy = function (ids, options, callback) { if (typeof options === 'function') { callback = options; options = {}; } return this._conn.destroy(this.type, ids, options, callback); }
...
* @returns {Promise.<RecordResult>}
*/
RecordReference.prototype.destroy = function(options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
return this._conn.destroy(this.type, this.id, options, callback);
};
/**
* Get blob field as stream
*
* @param {String} fieldName - Blob field name
* @returns {stream.Stream}
...
destroyBulk = function (input, callback) { return this.bulkload("delete", input, callback); }
n/a
destroyHardBulk = function (input, callback) { return this.bulkload("hardDelete", input, callback); }
n/a
find = function (conditions, fields, options, callback) { if (typeof conditions === 'function') { callback = conditions; conditions = {}; fields = null; options = null; } else if (typeof fields === 'function') { callback = fields; fields = null; options = null; } else if (typeof options === 'function') { callback = options; options = null; } options = options || {}; var config = { fields: fields, includes: options.includes, table: this.type, conditions: conditions, limit: options.limit, offset: options.offset || options.skip }; var query = new Query(this._conn, config, options); query.setResponseTarget(Query.ResponseTargets.Records); if (callback) { query.run(callback); } return query; }
...
]);
function findRelationTable(rname) {
var ptable = self._parent._config.table;
logger.debug('finding table for relation "' + rname + '" in "' + ptable + '"...
x27;);
return describeCache(ptable).then(function(sobject) {
var upperRname = rname.toUpperCase();
var childRelation = _.find(sobject.childRelationships, function(cr) {
return (cr.relationshipName || '').toUpperCase() === upperRname;
});
return childRelation ? childRelation.childSObject :
Promise.reject(new Error("No child relationship found: " + rname ));
});
}
...
findOne = function (conditions, fields, options, callback) { if (typeof conditions === 'function') { callback = conditions; conditions = {}; fields = null; options = null; } else if (typeof fields === 'function') { callback = fields; fields = null; options = null; } else if (typeof options === 'function') { callback = options; options = null; } options = _.extend(options || {}, { limit: 1 }); var query = this.find(conditions, fields, options); query.setResponseTarget(Query.ResponseTargets.SingleRecord); if (callback) { query.run(callback); } return query; }
...
};
Channel.prototype.push = function(events, callback) {
var isArray = _.isArray(events);
events = isArray ? events : [ events ];
var conn = this._streaming._conn;
if (!this._id) {
this._id = conn.sobject('StreamingChannel').findOne({ Name: this._name },
x27;Id')
.then(function(rec) { return rec.Id });
}
return this._id.then(function(id) {
var channelUrl = '/sobjects/StreamingChannel/' + id + '/push';
return conn.requestPost(channelUrl, { pushEvents: events });
}).then(function(rets) {
return isArray ? rets : rets[0];
...
insert = function (records, options, callback) { if (typeof options === 'function') { callback = options; options = {}; } return this._conn.create(this.type, records, options, callback); }
n/a
insertBulk = function (input, callback) { return this.bulkload("insert", input, callback); }
n/a
layouts = function (layoutName, callback) { if (typeof layoutName === 'function') { callback = layoutName; layoutName = null; } var url = "/sobjects/" + this.type + "/describe/" + (layoutName ? "namedLayouts/"+layoutName : "layouts"); return this._conn.request(url, callback); }
...
});
/**
*
*/
describe("list layout for SObject", function() {
it("should return Account layout information", function(done) {
Account.layouts(function(err, res) {
if (err) { throw err; }
assert.ok(_.isArray(res.layouts));
res.layouts.forEach(function(layout) {
assert.ok(layout.id === null || _.isString(layout.id));
assert.ok(_.isObject(layout.buttonLayoutSection));
assert.ok(_.isArray(layout.detailLayoutSections));
assert.ok(_.isArray(layout.editLayoutSections));
...
listview = function (id) { return new ListView(this._conn, this.type, id); }
...
});
/**
*
*/
describe("describe list view", function() {
it("should return described list view info for given list view id", function(done) {
Account.listview(listviewId).describe(function(err, result) {
if (err) { throw err; }
assert.ok(_.isObject(result));
assert.ok(_.isString(result.id));
assert.ok(_.isString(result.sobjectType));
assert.ok(_.isString(result.query) || result.query === null);
assert.ok(_.isArray(result.columns));
assert.ok(_.isArray(result.orderBy));
...
listviews = function (callback) { var url = this._conn._baseUrl() + '/sobjects/' + this.type + '/listviews'; return this._conn.request(url, callback); }
...
var listviewId;
/**
*
*/
describe("listup list views", function() {
it("should return list views definitions on the sobject", function(done) {
Account.listviews(function(err, result) {
if (err) { throw err; }
assert.ok(_.isObject(result));
assert.ok(_.isArray(result.listviews));
for (var i=0, len=result.listviews.length; i<len; i++) {
var listview = result.listviews[i];
assert.ok(_.isString(listview.id));
assert.ok(_.isString(listview.label));
...
quickAction = function (actionName) { return new QuickAction(this._conn, "/sobjects/" + this.type + "/quickActions/" + actionName); }
...
var action;
/**
*
*/
describe("get a global action", function() {
it("should return a global quick action reference", function() {
action = conn.quickAction('LogACall');
assert.ok(action instanceof QuickAction);
});
});
/**
*
*/
...
quickActions = function (callback) { return this._conn.request("/sobjects/" + this.type + "/quickActions").thenCall(callback); }
...
/**
*
*/
describe("list global actions", function() {
it("should return global actions", function(done) {
conn.quickActions(function(err, results) {
if (err) { throw err; }
assert.ok(_.isArray(results));
results.forEach(function(res) {
assert.ok(_.isString(res.type));
assert.ok(_.isString(res.name));
assert.ok(_.isString(res.label));
assert.ok(_.isObject(res.urls));
...
recent = function (callback) { return this._conn.recent(this.type, callback); }
...
/**
* Retrieve recently accessed records
*
* @param {Callback.<Array.<RecordResult>>} [callback] - Callback function
* @returns {Promise.<Array.<RecordResult>>}
*/
SObject.prototype.recent = function (callback) {
return this._conn.recent(this.type, callback);
};
/**
* Retrieve the updated records
*
* @param {String|Date} start - start date or string representing the start of the interval
* @param {String|Date} end - start date or string representing the end of the interval, must be > start
...
record = function (id) { return new Record(this._conn, this.type, id); }
...
});
/**
*
*/
describe("update account", function() {
it("should update successfully", function(done) {
conn.sobject('Account').record(account.Id).update({ Name : "Hello2
x22; }, function(err, ret) {
if (err) { throw err; }
assert.ok(ret.success);
}.check(done));
});
describe("then retrieve the account", function() {
it("should return updated account object", function(done) {
...
retrieve = function (ids, options, callback) { if (typeof options === 'function') { callback = options; options = {}; } return this._conn.retrieve(this.type, ids, options, callback); }
...
}
self.check(function(err, res) {
if (err) {
self.emit('error', err);
} else {
if (res.state === "Failed") {
if (parseInt(res.numberRecordsProcessed, 10) > 0) {
self.retrieve();
} else {
self.emit('error', new Error(res.stateMessage));
}
} else if (res.state === "Completed") {
self.retrieve();
} else {
self.emit('progress', res);
...
select = function (fields, callback) { return this.find(null, fields, null, callback); }
...
this._conn = conn;
if (_.isString(config)) { // if query config is string, it is given in SOQL.
this._soql = config;
} else if (config.locator && config.locator.indexOf("/") >= 0) { // if locator given in url for next records
this._locator = config.locator.split("/").pop();
} else {
this._config = config;
this.select(config.fields);
if (config.includes) {
this.include(config.includes);
}
}
this._options = _.defaults({
maxFetch: 10000,
autoFetch: false,
...
update = function (records, options, callback) { if (typeof options === 'function') { callback = options; options = {}; } return this._conn.update(this.type, records, options, callback); }
...
RecordReference.prototype.update = function(record, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
record = _.clone(record);
record.Id = this.id;
return this._conn.update(this.type, record, options, callback);
};
/**
* Synonym of Record#destroy()
*
* @method RecordReference#delete
* @param {Object} [options] - Options for rest api.
...
updateBulk = function (input, callback) { return this.bulkload("update", input, callback); }
...
type = null;
}
type = type || (this._config && this._config.table);
if (!type) {
throw new Error("SOQL based query needs SObject type information to bulk update.");
}
var updateStream = _.isFunction(mapping) ? RecordStream.map(mapping) : RecordStream.recordMapStream(mapping);
var batch = this._conn.sobject(type).updateBulk();
var deferred = Promise.defer();
var handleError = function(err) {
if (err.name === 'ClientInputError') { deferred.resolve([]); } // if batch input receives no records
else { deferred.reject(err); }
};
this.on('error', handleError)
.pipe(updateStream)
...
updated = function (start, end, callback) { return this._conn.updated(this.type, start, end, callback); }
...
*
* @param {String|Date} start - start date or string representing the start of the interval
* @param {String|Date} end - start date or string representing the end of the interval, must be > start
* @param {Callback.<UpdatedRecordsInfo>} [callback] - Callback function
* @returns {Promise.<UpdatedRecordsInfo>}
*/
SObject.prototype.updated = function (start, end, callback) {
return this._conn.updated(this.type, start, end, callback);
};
/**
* Retrieve the deleted records
*
* @param {String|Date} start - start date or string representing the start of the interval
* @param {String|Date} end - start date or string representing the end of the interval, must be > start
...
upsert = function (records, extIdField, options, callback) { if (typeof options === 'function') { callback = options; options = {}; } return this._conn.upsert(this.type, records, extIdField, options, callback); }
...
* @returns {Promise.<RecordResult|Array.<RecordResult>>}
*/
SObject.prototype.upsert = function(records, extIdField, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
return this._conn.upsert(this.type, records, extIdField, options, callback);
};
/**
* Synonym of SObject#destroy()
*
* @method SObject#delete
* @param {String|Array.<String>} ids - A ID or array of IDs to delete
...
upsertBulk = function (input, extIdField, callback) { return this.bulkload("upsert", { extIdField: extIdField }, input, callback); }
n/a
function createSOQL(query) { var soql = [ "SELECT ", createFieldsClause(query.fields, query.includes), " FROM ", query.table ].join(""); var cond = createConditionClause(query.conditions); if (cond) { soql += " WHERE " + cond; } var orderby = createOrderByClause(query.sort); if (orderby) { soql += " ORDER BY " + orderby; } if (query.limit) { soql += " LIMIT " + query.limit; } if (query.offset) { soql += " OFFSET " + query.offset; } return soql; }
...
*
* @param {Callback.<String>} [callback] - Callback function
* @returns {Promise.<String>}
*/
Query.prototype.toSOQL = function(callback) {
var self = this;
return Promise.resolve(self._soql ||
self._expandFields().then(function() { return SOQLBuilder.createSOQL(self._config); })
).thenCall(callback);
};
/**
* Create data stream of queried records.
* Automatically resume query if paused.
*
...