/**
* @file Represents Salesforce SObject
* @author Shinichi Tomita <shinichi.tomita@gmail.com>
*/
'use strict';
var _ = require('lodash/core'),
Record = require('./record'),
Query = require('./query'),
Cache = require('./cache'),
QuickAction = require('./quick-action');
/**
* A class for organizing all SObject access
*
* @constructor
*/
var SObject = module.exports = 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);
};
/**
* Synonym of SObject#create()
*
* @method SObject#insert
* @param {Record|Array.<Record>} records - A record or array of records to create
* @param {Callback.<RecordResult|Array.<RecordResult>>} [callback] - Callback function
* @returns {Promise.<RecordResult|Array.<RecordResult>>}
*/
/**
* Create records
*
* @method SObject#create
* @param {Record|Array.<Record>} records - A record or array of records to create
* @param {Object} [options] - Options for rest api.
* @param {Callback.<RecordResult|Array.<RecordResult>>} [callback] - Callback function
* @returns {Promise.<RecordResult|Array.<RecordResult>>}
*/
SObject.prototype.insert =
SObject.prototype.create = function(records, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
return this._conn.create(this.type, records, options, callback);
};
/**
* Retrieve specified records
*
* @param {String|Array.<String>} ids - A record ID or array of record IDs
* @param {Object} [options] - Options for rest api.
* @param {Callback.<Record|Array.<Record>>} [callback] - Callback function
* @returns {Promise.<Record|Array.<Record>>}
*/
SObject.prototype.retrieve = function(ids, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
return this._conn.retrieve(this.type, ids, options, callback);
};
/**
* Update records
*
* @param {Record|Array.<Record>} records - A record or array of records to update
* @param {Object} [options] - Options for rest api.
* @param {Callback.<RecordResult|Array.<RecordResult>>} [callback] - Callback function
* @returns {Promise.<RecordResult|Array.<RecordResult>>}
*/
SObject.prototype.update = function(records, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
return this._conn.update(this.type, records, options, callback);
};
/**
* Upsert records
*
* @param {Record|Array.<Record>} records - Record or array of records to upsert
* @param {String} extIdField - External ID field name
* @param {Object} [options] - Options for rest api.
* @param {Callback.<RecordResult|Array.<RecordResult>>} [callback] - 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
* @param {Callback.<RecordResult|Array.<RecordResult>>} [callback] - Callback function
* @returns {Promise.<RecordResult|Array.<RecordResult>>}
*/
/**
* Synonym of SObject#destroy()
*
* @method SObject#del
* @param {String|Array.<String>} ids - A ID or array of IDs to delete
* @param {Callback.<RecordResult|Array.<RecordResult>>} [callback] - Callback function
* @returns {Promise.<RecordResult|Array.<RecordResult>>}
*/
/**
* Delete records
*
* @method SObject#destroy
* @param {String|Array.<String>} ids - A ID or array of IDs to delete
* @param {Object} [options] - Options for rest api.
* @param {Callback.<RecordResult|Array.<RecordResult>>} [callback] - Callback function
* @returns {Promise.<RecordResult|Array.<RecordResult>>}
*/
SObject.prototype["delete"] =
SObject.prototype.del =
SObject.prototype.destroy = function(ids, options, callback) {
if (typeof options === 'function') {
callback = options;
options = {};
}
return this._conn.destroy(this.type, ids, options, 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}
*/
SObject.prototype.record = function(id) {
return new Record(this._conn, this.type, id);
};
/**
* Find and fetch records which matches given conditions
*
* @param {Object|String} [conditions] - Conditions in JSON object (MongoDB-like), or raw SOQL WHERE clause string.
* @param {Object|Array.<String>|String} [fields] - Fields to fetch. Format can be in JSON object (MongoDB-like), array of field names, or comma-separated field names.
* @param {Object} [options] - Query options.
* @param {Number} [options.limit] - Maximum number of records the query will return.
* @param {Number} [options.offset] - Offset number where begins returning results.
* @param {Number} [options.skip] - Synonym of options.offset.
* @param {Callback.<Array.<Record>>} [callback] - Callback function
* @returns {Query.<Array.<Record>>}
*/
SObject.prototype.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);
query.setResponseTarget(Query.ResponseTargets.Records);
if (callback) { query.run(callback); }
return query;
};
/**
* Fetch one record which matches given conditions
*
* @param {Object|String} [conditions] - Conditions in JSON object (MongoDB-like), or raw SOQL WHERE clause string.
* @param {Object|Array.<String>|String} [fields] - Fields to fetch. Format can be in JSON object (MongoDB-like), array of field names, or comma-separated field names.
* @param {Object} [options] - Query options.
* @param {Number} [options.limit] - Maximum number of records the query will return.
* @param {Number} [options.offset] - Offset number where begins returning results.
* @param {Number} [options.skip] - Synonym of options.offset.
* @param {Callback.<Record>} [callback] - Callback function
* @returns {Query.<Record>}
*/
SObject.prototype.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;
};
/**
* Find and fetch records only by specifying fields to fetch.
*
* @param {Object|Array.<String>|String} [fields] - Fields to fetch. Format can be in JSON object (MongoDB-like), array of field names, or comma-separated field names.
* @param {Callback.<Array.<Record>>} [callback] - Callback function
* @returns {Query.<Array.<Record>>}
*/
SObject.prototype.select = function(fields, callback) {
return this.find(null, fields, null, callback);
};
/**
* Count num of records which matches given conditions
*
* @param {Object|String} [conditions] - Conditions in JSON object (MongoDB-like), or raw SOQL WHERE clause string.
* @param {Callback.<Number>} [callback] - Callback function
* @returns {Query.<Number>}
*/
SObject.prototype.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;
};
/**
* Call Bulk#load() to execute bulkload, returning batch object
*
* @param {String} operation - Bulk load operation ('insert', 'update', 'upsert', 'delete', or 'hardDelete')
* @param {Object} [options] - Options for bulk loading operation
* @param {String} [options.extIdField] - External ID field name (used when upsert operation).
* @param {Array.<Record>|stream.Stream|String} [input] - Input source for bulkload. Accepts array of records, CSv string, and CSV data input stream.
* @param {Callback.<Array.<RecordResult>>} [callback] - Callback function
* @returns {Bulk~Batch}
*/
SObject.prototype.bulkload = function(operation, options, input, callback) {
return this._conn.bulk.load(this.type, operation, options, input, callback);
};
/**
* Synonym of SObject#createBulk()
*
* @method SObject#insertBulk
* @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}
*/
/**
* Bulkly insert input data using bulk API
*
* @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
* @returns {Bulk~Batch}
*/
SObject.prototype.updateBulk = function(input, callback) {
return this.bulkload("update", input, callback);
};
/**
* Bulkly upsert records by input data using bulk API
*
* @param {Array.<Record>|stream.Stream|String} [input] - Input source for bulk upsert. Accepts array of records, CSv string, and CSV data input stream.
* @param {String} [options.extIdField] - External ID field name
* @param {Callback.<Array.<RecordResult>>} [callback] - Callback function
* @returns {Bulk~Batch}
*/
SObject.prototype.upsertBulk = function(input, extIdField, callback) {
return this.bulkload("upsert", { extIdField: extIdField }, input, callback);
};
/**
* Synonym of SObject#destroyBulk()
*
* @method SObject#deleteBulk
* @param {Array.<Record>|stream.Stream|String} [input] - Input source for bulk delete. Accepts array of records, CSv string, and CSV data input stream.
* @param {Callback.<Array.<RecordResult>>} [callback] - Callback function
* @returns {Bulk~Batch}
*/
/**
* Bulkly delete records specified by input data using bulk API
*
* @method SObject#destroyBulk
* @param {Array.<Record>|stream.Stream|String} [input] - Input source for bulk delete. Accepts array of records, CSv string, and CSV data input stream.
* @param {Callback.<Array.<RecordResult>>} [callback] - Callback function
* @returns {Bulk~Batch}
*/
SObject.prototype.deleteBulk =
SObject.prototype.destroyBulk = function(input, callback) {
return this.bulkload("delete", input, callback);
};
/**
* Synonym of SObject#destroyHardBulk()
*
* @method SObject#deleteHardBulk
* @param {Array.<Record>|stream.Stream|String} [input] - Input source for bulk delete. Accepts array of records, CSv string, and CSV data input stream.
* @param {Callback.<Array.<RecordResult>>} [callback] - Callback function
* @returns {Bulk~Batch}
*/
/**
* Bulkly hard delete records specified in input data using bulk API
*
* @method SObject#destroyHardBulk
* @param {Array.<Record>|stream.Stream|String} [input] - Input source for bulk delete. Accepts array of records, CSv string, and CSV data input stream.
* @param {Callback.<Array.<RecordResult>>} [callback] - Callback function
* @returns {Bulk~Batch}
*/
SObject.prototype.deleteHardBulk =
SObject.prototype.destroyHardBulk = function(input, callback) {
return this.bulkload("hardDelete", input, 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
* @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
* @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 layout information for SObject
*
* @param {String} [layoutName] - Name of named layout. (e.g. UserAlt in User SObject)
* @param {Callback.<LayoutInfo>} [callback] - Callback function
* @returns {Promise.<LayoutInfo>}
*/
SObject.prototype.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);
};
/**
* @typedef {Object} CompactLayoutInfo
* @prop {Array.<Object>} compactLayouts - Array of compact layouts
* @prop {String} defaultCompactLayoutId - ID of default compact layout
* @prop {Array.<Object>} recordTypeCompactLayoutMappings - Array of record type mappings
*/
/**
* Describe compact layout information defined for SObject
*
* @param {Callback.<CompactLayoutInfo>} [callback] - Callback function
* @returns {Promise.<CompactLayoutInfo>}
*/
SObject.prototype.compactLayouts = function(callback) {
var url = "/sobjects/" + this.type + "/describe/compactLayouts";
return this._conn.request(url, callback);
};
/**
* @typedef {Object} ApprovalLayoutInfo
* @prop {Array.<Object>} approvalLayouts - Array of approval layouts
*/
/**
* Describe compact layout information defined for SObject
*
* @param {Callback.<ApprovalLayoutInfo>} [callback] - Callback function
* @returns {Promise.<ApprovalLayoutInfo>}
*/
SObject.prototype.approvalLayouts = function(callback) {
var url = "/sobjects/" + this.type + "/describe/approvalLayouts";
return this._conn.request(url, callback);
};
/**
* Returns the list of list views for the SObject
*
* @param {Callback.<ListViewsInfo>} [callback] - Callback function
* @returns {Promise.<ListViewsInfo>}
*/
SObject.prototype.listviews = function(callback) {
var url = this._conn._baseUrl() + '/sobjects/' + this.type + '/listviews';
return this._conn.request(url, callback);
};
/**
* Returns the list view info in specifed view id
*
* @param {String} id - List view ID
* @returns {ListView}
*/
SObject.prototype.listview = function(id) {
return new ListView(this._conn, this.type, id);
};
/**
* Returns all registered quick actions for the SObject
*
* @param {Callback.<Array.<QuickAction~QuickActionInfo>>} [callback] - Callback function
* @returns {Promise.<Array.<QuickAction~QuickActionInfo>>}
*/
SObject.prototype.quickActions = function(callback) {
return this._conn.request("/sobjects/" + this.type + "/quickActions").thenCall(callback);
};
/**
* Get reference for specified quick aciton in the SObject
*
* @param {String} actionName - Name of the quick action
* @returns {QuickAction}
*/
SObject.prototype.quickAction = function(actionName) {
return new QuickAction(this._conn, "/sobjects/" + this.type + "/quickActions/" + actionName);
};
/**
* A class for organizing list view information
*
* @protected
* @class ListView
* @param {Connection} conn - Connection instance
* @param {SObject} type - SObject type
* @param {String} id - List view ID
*/
var ListView = function(conn, type, id) {
this._conn = conn;
this.type = type;
this.id = id;
};
/**
* Executes query for the list view and returns the resulting data and presentation information.
*
* @param {Callback.<ListViewResultInfo>} [callback] - Callback function
* @returns {Promise.<ListViewResultInfo>}
*/
ListView.prototype.results = function(callback) {
var url = this._conn._baseUrl() + '/sobjects/' + this.type + '/listviews/' + this.id + '/results';
return this._conn.request(url, callback);
};
/**
* Returns detailed information about a list view
*
* @param {Callback.<ListViewDescribeInfo>} [callback] - Callback function
* @returns {Promise.<ListViewDescribeInfo>}
*/
ListView.prototype.describe = function(callback) {
var url = this._conn._baseUrl() + '/sobjects/' + this.type + '/listviews/' + this.id + '/describe';
return this._conn.request(url, callback);
};
/**
* Explain plan for executing list view
*
* @param {Callback.<ExplainInfo>} [callback] - Callback function
* @returns {Promise.<ExplainInfo>}
*/
ListView.prototype.explain = function(callback) {
var url = "/query/?explain=" + this.id;
return this._conn.request(url, callback);
};