jsforce
Version:
Salesforce API Library for JavaScript
1,434 lines (1,327 loc) • 371 kB
JavaScript
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var o;"undefined"!=typeof window?o=window:"undefined"!=typeof global?o=global:"undefined"!=typeof self&&(o=self);var f=o;f=f.jsforce||(f.jsforce={}),f=f.modules||(f.modules={}),f=f.api||(f.api={}),f.Metadata=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
(function (process,Buffer){
/*global process, Buffer */
/**
* @file Manages Salesforce Metadata API
* @author Shinichi Tomita <shinichi.tomita@gmail.com>
*/
var util = jsforce.require('util'),
events = jsforce.require('events'),
stream = jsforce.require('stream'),
Stream = stream.Stream,
_ = jsforce.require('underscore'),
Promise = require('../promise'),
SOAP = require('../soap');
/*--------------------------------------------*/
/**
* Class for Salesforce Metadata API
*
* @class
* @param {Connection} conn - Connection object
*/
var Metadata = module.exports = function(conn) {
this._conn = conn;
};
/**
* Polling interval in milliseconds
* @type {Number}
*/
Metadata.prototype.pollInterval = 1000;
/**
* Polling timeout in milliseconds
* @type {Number}
*/
Metadata.prototype.pollTimeout = 10000;
/**
* Call Metadata API SOAP endpoint
*
* @private
*/
Metadata.prototype._invoke = function(method, message, callback) {
var soapEndpoint = new SOAP({
sessionId: this._conn.accessToken,
serverUrl: this._conn.instanceUrl + "/services/Soap/m/" + this._conn.version,
xmlns: "http://soap.sforce.com/2006/04/metadata"
}, this._conn._transport);
return soapEndpoint.invoke(method, message).then(function(res) {
return res.result;
}).thenCall(callback);
};
/**
* @typedef {Object} Metadata~MetadataInfo
* @prop {String} fullName - The name of the component
*/
/**
* Asynchronously adds one or more new metadata components to the organization.
*
* @param {String} type - The type of metadata to create
* @param {Metadata~MetadataInfo|Array.<Metadata~MetadataInfo>} metadata - Metadata to create
* @param {Callback.<Metadata~AsyncResult|Array.<Metadata~AsyncResult>>} [callback] - Callback function
* @returns {Metadata~AsyncResultLocator}
*/
Metadata.prototype.createAsync = function(type, metadata, callback) {
if (Number(this._conn.version) > 30) {
throw new Error("Async metadata CRUD calls are not supported on ver 31.0 or later.");
}
var convert = function(md) {
md["@xsi:type"] = type;
return md;
};
var isArray = _.isArray(metadata);
metadata = isArray ? _.map(metadata, convert) : convert(metadata);
var res = this._invoke("create", { metadata: metadata });
return new AsyncResultLocator(this, res, isArray).thenCall(callback);
};
/**
* @private
*/
function convertToSaveResult(result) {
var saveResult = _.clone(result);
saveResult.success = saveResult.success === 'true';
return saveResult;
}
/**
* Synonym of Metadata#create().
*
* @method createSync
* @param {String} type - The type of metadata to create
* @param {Metadata~MetadataInfo|Array.<Metadata~MetadataInfo>} metadata - Metadata to create
* @param {Callback.<Metadata~SaveResult|Array.<Metadata~SaveResult>>} [callback] - Callback function
* @returns {Promise.<Metadata~SaveResult|Array.<Metadata~SaveResult>>}
*/
/**
* Synchronously adds one or more new metadata components to the organization.
*
* @method Metadata#create
* @param {String} type - The type of metadata to create
* @param {Metadata~MetadataInfo|Array.<Metadata~MetadataInfo>} metadata - Metadata to create
* @param {Callback.<Metadata~SaveResult|Array.<Metadata~SaveResult>>} [callback] - Callback function
* @returns {Promise.<Metadata~SaveResult|Array.<Metadata~SaveResult>>}
*/
Metadata.prototype.createSync =
Metadata.prototype.create = function(type, metadata, callback) {
var convert = function(md) {
md["@xsi:type"] = type;
return md;
};
var isArray = _.isArray(metadata);
metadata = isArray ? _.map(metadata, convert) : convert(metadata);
return this._invoke("createMetadata", { metadata: metadata }).then(function(results) {
return _.isArray(results) ? _.map(results, convertToSaveResult) : convertToSaveResult(results);
}).thenCall(callback);
};
/**
* @private
*/
function convertToMetadataInfo(rec) {
var metadataInfo = _.clone(rec);
delete metadataInfo.$;
return metadataInfo;
}
/**
* Synonym of Metadata#read()
*
* @method Metadata#readSync
* @param {String} type - The type of metadata to read
* @param {String|Array.<String>} fullNames - full name(s) of metadata objects to read
* @param {Callback.<Metadata~ReadResult|Array.<Metadata~ReadResult>>} [callback] - Callback function
* @returns {Promise.<Array.<Metadata~MetadataInfo|Array.<Metadata~MetadataInfo>>>}
*/
/**
* Synchronously read specified metadata components in the organization.
*
* @method Metadata#read
* @param {String} type - The type of metadata to read
* @param {String|Array.<String>} fullNames - full name(s) of metadata objects to read
* @param {Callback.<Metadata~ReadResult|Array.<Metadata~ReadResult>>} [callback] - Callback function
* @returns {Promise.<Array.<Metadata~MetadataInfo|Array.<Metadata~MetadataInfo>>>}
*/
Metadata.prototype.readSync =
Metadata.prototype.read = function(type, fullNames, callback) {
return this._invoke("readMetadata", { type: type, fullNames: fullNames }).then(function(res) {
return _.isArray(res.records) ? _.map(res.records, convertToMetadataInfo) : convertToMetadataInfo(res.records);
}).thenCall(callback);
};
/**
* @typedef {Object} Metadata~UpdateMetadataInfo
* @prop {String} currentName - The API name of the component or field before the update
* @prop {Metadata~MetadataInfo} metadata - Full specification of the component or field you wish to update
*/
/**
* Asynchronously updates one or more metadata components in the organization.
*
* @param {String} type - The type of metadata to update
* @param {Metadata~UpdateMetadataInfo|Array.<Metadata~UpdateMetadataInfo>} updateMetadata - Updating metadata
* @param {Callback.<Metadata~AsyncResult|Array.<Metadata~AsyncResult>>} [callback] - Callback function
* @returns {Metadata~AsyncResultLocator}
*/
Metadata.prototype.updateAsync = function(type, updateMetadata, callback) {
if (Number(this._conn.version) > 30) {
throw new Error("Async metadata CRUD calls are not supported on ver 31.0 or later.");
}
var convert = function(umd) {
umd.metadata["@xsi:type"] = type;
return umd;
};
var isArray = _.isArray(updateMetadata);
updateMetadata = isArray ? _.map(updateMetadata, convert) : convert(updateMetadata);
var res = this._invoke("update", { updateMetadata: updateMetadata });
return new AsyncResultLocator(this, res, isArray).thenCall(callback);
};
/**
* Synonym of Metadata#update().
*
* @method Metadata#updateSync
* @param {String} type - The type of metadata to update
* @param {Metadata~MetadataInfo|Array.<Metadata~MetadataInfo>} updateMetadata - Updating metadata
* @param {Callback.<Metadata~SaveResult|Array.<Metadata~SaveResult>>} [callback] - Callback function
* @returns {Promise.<Metadata~SaveResult|Array.<Metadata~SaveResult>>}
*/
/**
* Synchronously updates one or more metadata components in the organization.
*
* @method Metadata#update
* @param {String} type - The type of metadata to update
* @param {Metadata~MetadataInfo|Array.<Metadata~MetadataInfo>} updateMetadata - Updating metadata
* @param {Callback.<Metadata~SaveResult|Array.<Metadata~SaveResult>>} [callback] - Callback function
* @returns {Promise.<Metadata~SaveResult|Array.<Metadata~SaveResult>>}
*/
Metadata.prototype.updateSync =
Metadata.prototype.update = function(type, metadata, callback) {
var convert = function(md) {
md["@xsi:type"] = type;
return md;
};
var isArray = _.isArray(metadata);
metadata = isArray ? _.map(metadata, convert) : convert(metadata);
return this._invoke("updateMetadata", { metadata: metadata }).then(function(results) {
return _.isArray(results) ? _.map(results, convertToSaveResult) : convertToSaveResult(results);
}).thenCall(callback);
};
/**
* Upserts one or more components in your organization's data.
*
* @param {String} type - The type of metadata to upsert
* @param {Metadata~MetadataInfo|Array.<Metadata~MetadataInfo>} metadata - Upserting metadata
* @param {Callback.<Metadata~SaveResult|Array.<Metadata~SaveResult>>} [callback] - Callback function
* @returns {Promise.<Metadata~SaveResult|Array.<Metadata~SaveResult>>}
*/
Metadata.prototype.upsertSync =
Metadata.prototype.upsert = function(type, metadata, callback) {
var convert = function(md) {
md["@xsi:type"] = type;
return md;
};
var isArray = _.isArray(metadata);
metadata = isArray ? _.map(metadata, convert) : convert(metadata);
return this._invoke("upsertMetadata", { metadata: metadata }).then(function(results) {
return _.isArray(results) ? _.map(results, convertToSaveResult) : convertToSaveResult(results);
}).thenCall(callback);
};
/**
* Asynchronously deletes specified metadata components in the organization.
*
* @param {String} type - The type of metadata to delete
* @param {String|Metadata~MetadataInfo|Array.<String>|Array.<Metadata~MetadataInfo>} metadata - The fullName of metadata or metadata info to delete. If it is passed in fullName, the type parameter should not be empty.
* @param {Callback.<Metadata~AsyncResult|Array.<Metadata~AsyncResult>>} [callback] - Callback function
* @returns {Metadata~AsyncResultLocator}
*/
Metadata.prototype.deleteAsync = function(type, metadata, callback) {
if (Number(this._conn.version) > 30) {
throw new Error("Async metadata CRUD calls are not supported on ver 31.0 or later.");
}
var convert = function(md) {
if (_.isString(md)) {
md = { fullName : md };
}
md["@xsi:type"] = type;
return md;
};
var isArray = _.isArray(metadata);
metadata = isArray ? _.map(metadata, convert) : convert(metadata);
var res = this._invoke("delete", { metadata: metadata });
return new AsyncResultLocator(this, res, isArray).thenCall(callback);
};
/**
* Synonym of Metadata#delete().
*
* @deprecated
* @method Metadata#del
* @param {String} [type] - The type of metadata to delete
* @param {String|Metadata~MetadataInfo|Array.<String>|Array.<Metadata~MetadataInfo>} metadata - The fullName of metadata or metadata info to delete. If it is passed in fullName, the type parameter should not be empty.
* @param {Callback.<Metadata~AsyncResult|Array.<Metadata~AsyncResult>>} [callback] - Callback function
* @returns {Metadata~AsyncResultLocator}
*/
/**
* Synonym of Metadata#delete().
*
* @method Metadata#deleteSync
* @param {String} type - The type of metadata to delete
* @param {String|Array.<String>} fullNames - The fullName of metadata to delete.
* @param {Callback.<Metadata~SaveResult|Array.<Metadata~SaveResult>>} [callback] - Callback function
* @returns {Promise.<Metadata~SaveResult|Array.<Metadata~SaveResult>>}
*/
/**
* Synchronously deletes specified metadata components in the organization.
*
* @method Metadata#delete
* @param {String} type - The type of metadata to delete
* @param {String|Array.<String>} fullNames - The fullName of metadata to delete.
* @param {Callback.<Metadata~SaveResult|Array.<Metadata~SaveResult>>} [callback] - Callback function
* @returns {Promise.<Metadata~SaveResult|Array.<Metadata~SaveResult>>}
*/
Metadata.prototype.del =
Metadata.prototype.deleteSync =
Metadata.prototype["delete"] = function(type, fullNames, callback) {
return this._invoke("deleteMetadata", { type: type, fullNames: fullNames }).then(function(results) {
return _.isArray(results) ? _.map(results, convertToSaveResult) : convertToSaveResult(results);
}).thenCall(callback);
};
/**
* Rename fullname of a metadata component in the organization
*
* @param {String} type - The type of metadata to delete
* @param {String} oldFullName - The original fullName of metadata
* @param {String} newFullName - The new fullName of metadata
* @param {Callback.<Metadata~SaveResult>} [callback] - Callback function
* @returns {Promise.<Metadata~SaveResult>}
*/
Metadata.prototype.rename = function(type, oldFullName, newFullName, callback) {
return this._invoke("renameMetadata", { type: type, oldFullName: oldFullName, newFullName: newFullName }).then(function(result) {
return convertToSaveResult(result);
}).thenCall(callback);
};
/**
* Checks the status of asynchronous metadata calls
*
* @param {String|Array.<String>} ids - The asynchronous process ID(s)
* @param {Callback.<Metadata~AsyncResult|Array.<Metadata~AsyncResult>>} [callback] - Callback function
* @returns {Metadata~AsyncResultLocator}
*/
Metadata.prototype.checkStatus = function(ids, callback) {
var isArray = _.isArray(ids);
var res = this._invoke("checkStatus", { asyncProcessId: ids });
return new AsyncResultLocator(this, res, isArray).thenCall(callback);
};
/**
* @typedef {Object} Metadata~DescribeMetadataResult
* @prop {Array.<Object>} metadataObjects - One or more metadata components and their attributes
* @prop {Array.<String>} metadataObjects.childXmlNames - List of child sub-components for this component
* @prop {String} metadataObjects.directoryName - The name of the directory in the .zip file that contains this component
* @prop {Boolean} metadataObjects.inFolder - Indicates whether the component is in a folder or not
* @prop {Boolean} metadataObjects.metaFile - Indicates whether the component requires an accompanying metadata file
* @prop {String} metadataObjects.suffix - The file suffix for this component
* @prop {String} metadataObjects.xmlName - The name of the root element in the metadata file for this component
* @prop {String} organizationNamespace - The namespace of the organization
* @prop {Boolean} partialSaveAllowed - Indicates whether rollbackOnError is allowed or not
* @prop {Boolean} testRequired - Indicates whether tests are required or not
*/
/**
* Retrieves the metadata which describes your organization, including Apex classes and triggers,
* custom objects, custom fields on standard objects, tab sets that define an app,
* and many other components.
*
* @param {String} [version] - The API version for which you want metadata; for example, 29.0
* @param {Callback.<Metadata~DescribeMetadataResult>} [callback] - Callback function
* @returns {Promise.<Metadata~DescribeMetadataResult>}
*/
Metadata.prototype.describe = function(version, callback) {
if (!_.isString(version)) {
callback = version;
version = this._conn.version;
}
return this._invoke("describeMetadata", { asOfVersion: version }).then(function(res) {
res.metadataObjects = _.isArray(res.metadataObjects) ? res.metadataObjects : [ res.metadataObjects ];
res.metadataObjects = _.map(res.metadataObjects, function(mo) {
if (mo.childXmlNames) {
mo.childXmlNames = _.isArray(mo.childXmlNames) ? mo.childXmlNames: [ mo.childXmlNames ];
}
mo.inFolder = mo.inFolder === 'true';
mo.metaFile = mo.metaFile === 'true';
return mo;
});
res.partialSaveAllowed = res.partialSaveAllowed === 'true';
res.testRequired = res.testRequired === 'true';
return res;
}).thenCall(callback);
};
/**
* @typedef {Object} Metadata~ListMetadataQuery
* @prop {String} type - The metadata type, such as CustomObject, CustomField, or ApexClass
* @prop {String} [folder] - The folder associated with the component.
*/
/**
* @typedef {Object} Metadata~FileProperties
* @prop {String} type - The metadata type, such as CustomObject, CustomField, or ApexClass
* @prop {String} createdById - ID of the user who created the file
* @prop {String} createdByName - Name of the user who created the file
* @prop {String} createdDate - Date and time when the file was created
* @prop {String} fileName - Name of the file
* @prop {String} fullName - The file developer name used as a unique identifier for API access
* @prop {String} id - ID of the file
* @prop {String} lastModifiedById - ID of the user who last modified the file
* @prop {String} lastModifiedByName - Name of the user who last modified the file
* @prop {String} lastModifiedDate - Date and time that the file was last modified
* @prop {String} [manageableState] - Indicates the manageable state of the specified component if it is contained in a package
* @prop {String} [namespacePrefix] - The namespace prefix of the component
*/
/**
* Retrieves property information about metadata components in your organization
*
* @param {Metadata~ListMetadataQuery|Array.<Metadata~ListMetadataQuery>} queries - The criteria object(s) specifing metadata to list
* @param {String} [version] - The API version for which you want metadata; for example, 29.0
* @param {Callback.<Array.<Metadata~FileProperties>>} [callback] - Callback function
* @returns {Promise.<Array.<Metadata~FileProperties>>}
*/
Metadata.prototype.list = function(queries, version, callback) {
if (!_.isString(version)) {
callback = version;
version = this._conn.version;
}
if (!_.isArray(queries)) {
queries = [ queries ];
}
return this._invoke("listMetadata", { queries: queries, asOfVersion: version }, callback);
};
/**
* @typedef {Object} Metadata~RetrieveRequest
*/
/**
* Retrieves XML file representations of components in an organization
*
* @param {Metadata~RetrieveRequest} request - Options for determining which packages or files are retrieved
* @param {Callback.<Metadata~AsyncResult>} [callback] - Callback function
* @returns {Metadata~RetrieveResultLocator}
*/
Metadata.prototype.retrieve = function(request, callback) {
var res = this._invoke("retrieve", { request: request });
return new RetrieveResultLocator(this, res).thenCall(callback);
};
/**
* Checks the status of declarative metadata call retrieve() and returns the zip file contents
*
* @param {String} id - Async process id returned from previous retrieve request
* @param {Callback.<Metadata~RetrieveResult>} [callback] - Callback function
* @returns {Promise.<Metadata~RetrieveResult>}
*/
Metadata.prototype.checkRetrieveStatus = function(id, callback) {
return this._invoke("checkRetrieveStatus", { asyncProcessId: id }, callback);
};
/**
* Deploy components into an organization using zipped file representations
*
* @param {stream.Stream|Buffer} zipInput - Zipped file input source in readable stream or binary buffer
* @param {Object} [options] - Options used in deployment
* @param {Boolean} [options.allowMissingFiles] - Specifies whether a deploy succeeds even if files that are specified in package.xml but are not in the .zip file or not.
* @param {Boolean} [options.autoUpdatePackage] - If a file is in the .zip file but not specified in package.xml, specifies whether the file should be automatically added to the package or not.
* @param {Boolean} [options.checkOnly] - Indicates whether Apex classes and triggers are saved to the organization as part of the deployment (false) or not (true).
* @param {Boolean} [options.ignoreWarnings] - Indicates whether a warning should allow a deployment to complete successfully (true) or not (false). Defaults to false.
* @param {Boolean} [options.performRetrieve] - Indicates whether a retrieve() call is performed immediately after the deployment (true) or not (false).
* @param {Boolean} [options.purgeOnDelete] - If true, the deleted components in the destructiveChanges.xml manifest file aren't stored in the Recycle Bin.
* @param {Boolean} [options.rollbackOnError] - Indicates whether any failure causes a complete rollback (true) or not (false).
* @param {Boolean} [options.runAllTests] - If true, all Apex tests defined in the organization are run.
* @param {Array.<String>} [options.runTests] - A list of Apex tests to be run during deployment.
* @param {Boolean} [options.singlePackage] - Indicates whether the specified .zip file points to a directory structure with a single package (true) or a set of packages (false).
* @param {Callback.<Metadata~AsyncResult>} [callback] - Callback function
* @returns {Metadata~DeployResultLocator}
*/
Metadata.prototype.deploy = function(zipInput, options, callback) {
if (!options || _.isFunction(options)) {
callback = options;
options = {};
}
var deferred = Promise.defer();
if (zipInput instanceof Stream) {
var bufs = [];
zipInput.on('data', function(d) {
bufs.push(d);
});
zipInput.on('end', function() {
deferred.resolve(Buffer.concat(bufs));
});
zipInput.resume();
} else if (zipInput instanceof Buffer) {
deferred.resolve(zipInput);
} else {
throw "Unexpected zipInput type";
}
var self = this;
var res = deferred.promise.then(function(zipContentBuffer) {
var zipContentB64 = zipContentBuffer.toString('base64');
return self._invoke("deploy", { ZipFile: zipContentB64, DeployOptions: options }, callback);
});
return new DeployResultLocator(this, res).thenCall(callback);
};
/**
* Checks the status of declarative metadata call deploy()
*
* @param {String} id - Async process id returned from previous deploy request
* @param {Boolean} [includeDetails] - Sets the DeployResult object to include details information (default: false)
* @param {Callback.<Metadata~DeployResult>} [callback] - Callback function
* @returns {Promise.<Metadata~DeployResult>}
*/
Metadata.prototype.checkDeployStatus = function(id, includeDetails, callback) {
if (_.isObject(includeDetails) || _.isBoolean(includeDetails)) {
includeDetails = !!includeDetails;
} else {
callback = includeDetails;
includeDetails = false;
}
return this._invoke("checkDeployStatus", {
asyncProcessId: id,
includeDetails : includeDetails
}).then(function(res) {
res.done = res.done === 'true';
res.success = res.success === 'true';
res.checkOnly = res.checkOnly === 'true';
if (res.ignoreWarnings) {
res.ignoreWarnings = res.ignoreWarnings === 'true';
}
if (res.rollbackOnError) {
res.rollbackOnError = res.rollbackOnError === 'true';
}
res.numberComponentErrors = Number(res.numberComponentErrors);
res.numberComponentsDeployed = Number(res.numberComponentsDeployed);
res.numberComponentsTotal = Number(res.numberComponentsTotal);
res.numberTestErrors = Number(res.numberTestErrors);
res.numberTestsCompleted = Number(res.numberTestsCompleted);
res.numberTestsTotal = Number(res.numberTestsTotal);
return res;
}).thenCall(callback);
};
/*--------------------------------------------*/
/**
* @typedef {Object} Metadata~AsyncResult
* @prop {Boolean} done - Indicates whether the call has completed or not
* @prop {String} id - ID of the component being created, updated, deleted, deployed, or retrieved
* @prop {String} state - The state four possible values: Queued, InProgress, Completed, and Error.
* @prop {String} [statusCode] - If an error occurred during the create(), update(), or delete() call, a status code is returned
* @prop {String} [message] - Message corresponding to the statusCode field returned
*/
/**
* The locator class for Metadata API asynchronous call result
*
* @protected
* @class Metadata~AsyncResultLocator
* @extends events.EventEmitter
* @implements Promise.<Metadata~AsyncResult|Array.<Metadata~AsyncResult>>
* @param {Metadata} meta - Metadata API object
* @param {Promise.<Metadata~AsyncResult|Array.<Metadata~AsyncResult>>} results - Promise object for async result info
* @param {Boolean} [isArray] - Indicates whether the async request is given in array or single object
*/
var AsyncResultLocator = function(meta, results, isArray) {
this._meta = meta;
this._results = results;
this._isArray = isArray;
};
util.inherits(AsyncResultLocator, events.EventEmitter);
/**
* Promise/A+ interface
* http://promises-aplus.github.io/promises-spec/
*
* Delegate to deferred promise, return promise instance for batch result
*
* @method Metadata~AsyncResultLocator#then
*/
AsyncResultLocator.prototype.then = function(onResolve, onReject) {
var self = this;
return this._results.then(function(results) {
var convertType = function(res) {
if (res.$ && res.$["xsi:nil"] === 'true') {
return null;
}
res.done = res.done === 'true';
return res;
};
results = _.isArray(results) ? _.map(results, convertType) : convertType(results);
if (self._isArray && !_.isArray(results)) {
results = [ results ];
}
return onResolve(results);
}, onReject);
};
/**
* Promise/A+ extension
* Call "then" using given node-style callback function
*
* @method Metadata~AsyncResultLocator#thenCall
*/
AsyncResultLocator.prototype.thenCall = function(callback) {
return _.isFunction(callback) ? this.then(function(res) {
process.nextTick(function() {
callback(null, res);
});
}, function(err) {
process.nextTick(function() {
callback(err);
});
}) : this;
};
/**
* Check the status of async request
*
* @method Metadata~AsyncResultLocator#check
* @param {Callback.<Metadata~AsyncResult|Array.<Metadata~AsyncResult>>} [callback] - Callback function
* @returns {Promise.<Metadata~AsyncResult|Array.<Metadata~AsyncResult>>}
*/
AsyncResultLocator.prototype.check = function(callback) {
var self = this;
var meta = this._meta;
return this.then(function(results) {
var ids = _.isArray(results) ? _.map(results, function(res){ return res.id; }) : results.id;
return meta.checkStatus(ids);
}).thenCall(callback);
};
/**
* Polling until async call status becomes complete or error
*
* @method Metadata~AsyncResultLocator#poll
* @param {Number} interval - Polling interval in milliseconds
* @param {Number} timeout - Polling timeout in milliseconds
*/
AsyncResultLocator.prototype.poll = function(interval, timeout) {
var self = this;
var startTime = new Date().getTime();
var poll = function() {
var now = new Date().getTime();
if (startTime + timeout < now) {
self.emit('error', new Error("polling time out"));
return;
}
self.check().then(function(results) {
var done = true;
var resultArr = _.isArray(results) ? results : [ results ];
for (var i=0, len=resultArr.length; i<len; i++) {
var result = resultArr[i];
if (result && !result.done) {
done = false;
}
}
if (done) {
self.emit('complete', results);
} else {
setTimeout(poll, interval);
}
}, function(err) {
self.emit('error', err);
});
};
setTimeout(poll, interval);
};
/**
* Check and wait until the async requests become in completed status
*
* @method Metadata~AsyncResultLocator#complete
* @param {Callback.<Metadata~AsyncResult|Array.<Metadata~AsyncResult>>} [callback] - Callback function
* @returns {Promise.<Metadata~AsyncResult|Array.<Metadata~AsyncResult>>}
*/
AsyncResultLocator.prototype.complete = function(callback) {
var deferred = Promise.defer();
this.on('complete', function(results) {
deferred.resolve(results);
});
this.on('error', function(err) {
deferred.reject(err);
});
var meta = this._meta;
this.poll(meta.pollInterval, meta.pollTimeout);
return deferred.promise.thenCall(callback);
};
/*--------------------------------------------*/
/**
* The locator class to track retreive() Metadata API call result
*
* @protected
* @class Metadata~RetrieveResultLocator
* @extends Metadata~AsyncResultLocator
* @param {Metadata} meta - Metadata API object
* @param {Promise.<Metadata~AsyncResult>} result - Promise object for async result of retrieve call()
*/
var RetrieveResultLocator = function(meta, result) {
RetrieveResultLocator.super_.call(this, meta, result);
};
util.inherits(RetrieveResultLocator, AsyncResultLocator);
/**
* @typedef {Object} Metadata~RetrieveResult
* @prop {Array.<Metadata~FileProperties>} fileProperties - Contains information about the properties of each component in the .zip file, and the manifest file package.xml
* @prop {String} id - ID of the component being retrieved
* @prop {Array.<Object>} messages - Contains information about the success or failure of the retrieve() call
* @prop {String} zipFile - The zip file returned by the retrieve request. Base 64-encoded binary data
*/
/**
* Check and wait until the async request becomes in completed status,
* and retrieve the result data.
*
* @memthod Metadata~RetrieveResultLocator#complete
* @param {Callback.<Metadata~RetrieveResult>} [callback] - Callback function
* @returns {Promise.<Metadata~RetrieveResult>}
*/
RetrieveResultLocator.prototype.complete = function(callback) {
var meta = this._meta;
return RetrieveResultLocator.super_.prototype.complete.call(this).then(function(result) {
return meta.checkRetrieveStatus(result.id);
}).thenCall(callback);
};
/**
* Change the retrieved result to Node.js readable stream
*
* @method Metadata~RetrieveResultLocator#stream
* @returns {stream.Stream}
*/
RetrieveResultLocator.prototype.stream = function() {
var rstream = new Stream();
rstream.readable = true;
this.complete(function(err, result) {
if (err) {
rstream.emit('error', err);
} else {
rstream.emit('data', new Buffer(result.zipFile, 'base64'));
rstream.emit('end');
}
});
return rstream;
};
/*--------------------------------------------*/
/**
* The locator class to track deploy() Metadata API call result
*
* @protected
* @class Metadata~DeployResultLocator
* @extends Metadata~AsyncResultLocator
* @param {Metadata} meta - Metadata API object
* @param {Promise.<Metadata~AsyncResult>} result - Promise object for async result of deploy() call
*/
var DeployResultLocator = function(meta, result) {
DeployResultLocator.super_.call(this, meta, result);
};
util.inherits(DeployResultLocator, AsyncResultLocator);
/**
* @typedef {Object} Metadata~DeployResult
* @prop {String} id - ID of the component being deployed
* @prop {Boolean} checkOnly - Indicates whether this deployment is being used to check the validity of the deployed files without making any changes in the organization or not
* @prop {String} completedDate - Timestamp for when the deployment process ended
* @prop {String} createdDate - Timestamp for when the deploy() call was received
* @prop {Array.<Object>} [details] - Provides the details of a deployment that is in-progress or ended, if includeDetails is set to true in checkDeployStatus() call
* @prop {Boolean} done - Indicates whether the server finished processing the deploy() call for the specified id
* @prop {String} [errorMessage] - Message corresponding to the values in the errorStatusCode field
* @prop {String} [errorStatusCode] - If an error occurred during the deploy() call, a status code is returned, and the message corresponding to the status code is returned in the errorMessagefield
* @prop {Boolean} [ignoreWarnings] - Specifies whether a deployment should continue even if the deployment generates warnings
* @prop {String} lastModifiedDate - Timestamp of the last update for the deployment process
* @prop {Number} numberComponentErrors - The number of components that generated errors during this deployment
* @prop {Number} numberComponentsDeployed - The number of components deployed in the deployment process
* @prop {Number} numberComponentsTotal - The total number of components in the deployment
* @prop {Number} numberTestErrors - The number of Apex tests that have generated errors during this deployment
* @prop {Number} numberTestsCompleted - The number of completedApex tests for this deployment
* @prop {Number} numberTestsTotal - The total number of Apex tests for this deployment
* @prop {Boolean} [rollbackOnError] - Indicates whether any failure causes a complete rollback or not. Default is true.
* @prop {String} startDate - Timestamp for when the deployment process began
* @prop {String} status - Indicates the current state of the deployment
* @prop {Boolean} success - Indicates whether the deployment was successful or not
*/
/**
* Check and wait until the async request becomes in completed status,
* and retrieve the result data.
*
* @method Metadata~DeployResultLocator#complete
* @param {Callback.<Metadata~DeployResult>} [callback] - Callback function
* @returns {Promise.<Metadata~DeployResult>}
*/
DeployResultLocator.prototype.complete = function(includeDetails, callback) {
if (_.isFunction(includeDetails)) {
callback = includeDetails;
includeDetails = false;
}
var meta = this._meta;
return DeployResultLocator.super_.prototype.complete.call(this).then(function(result) {
return meta.checkDeployStatus(result.id, includeDetails);
}).thenCall(callback);
};
}).call(this,require('_process'),require("buffer").Buffer)
},{"../promise":5,"../soap":6,"_process":16,"buffer":10}],2:[function(require,module,exports){
/*global Sfdc */
var stream = jsforce.require('stream'),
_ = jsforce.require('underscore');
function parseHeaders(hs) {
var headers = {};
hs.split(/\n/).forEach(function(line) {
var pair = line.split(/\s*:\s*/);
var name = pair[0].toLowerCase();
var value = pair[1];
headers[name] = value;
});
return headers;
}
module.exports = {
supported: typeof Sfdc === 'object' && typeof Sfdc.canvas !== 'undefined',
createRequest: function(signedRequest) {
return function(params, callback) {
var response;
var str = new stream.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;
};
}
};
},{}],3:[function(require,module,exports){
/*global window, document */
var _index = 0;
module.exports = {
supported: typeof window !== 'undefined',
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];
};
};
}
};
},{}],4:[function(require,module,exports){
var stream = jsforce.require('stream');
module.exports = function(params, callback) {
var xhr = new XMLHttpRequest();
xhr.open(params.method, params.url);
if (params.headers) {
for (var header in params.headers) {
xhr.setRequestHeader(header, params.headers[header]);
}
}
xhr.setRequestHeader("Accept", "*/*");
var response;
var str = new stream.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 === "buffer" ? "binary" : encoding));
callback();
};
str.on('finish', function() {
if (!sent) {
xhr.send(bufs.join(''));
sent = true;
}
});
if (params.body || params.body === "" || !/^(put|post|patch)$/i.test(params.method)) {
xhr.send(params.body);
sent = true;
}
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
var headers = {
"content-type": xhr.getResponseHeader("content-type")
};
response = {
statusCode: xhr.status,
headers: headers,
body: xhr.response
};
if (!response.statusCode) {
response.statusCode = 400;
response.body = "Access Declined";
}
if (callback) {
callback(null, response, response.body);
}
str.end();
}
};
return str;
};
},{}],5:[function(require,module,exports){
(function (process){
/*global process*/
var Q = require('q'),
_ = jsforce.require('underscore')._;
/**
* Promises/A+ spec compliant class, with a little extension
* http://promises-aplus.github.io/promises-spec/
*
* @class Promise
* @constructor
* @param {Promise.<T>|T} o - Object to wrap with promise
* @template T
*/
var Promise = function(o) {
this._promise = Q(o);
};
/**
* @callback FulfilledCallback
* @param {T} result - Fulfilled value
* @returns {S}
* @template T,S
*/
/**
* @callback RejectedCallback
* @param {Error} reason - Rejected reason
* @returns {S}
* @template S
*/
/**
* The "then" method from the Promises/A+ specification
*
* @param {FulfilledCallback.<T, S1>} [onFulfilled]
* @param {RejectedCallback.<S2>} [onRejected]
* @returns {Promise.<S1|S2>}
*/
Promise.prototype.then = function() {
// Delegate Q promise implementation and wrap by our Promise instance
return new Promise(this._promise.then.apply(this._promise, arguments));
};
/**
* Call "then" using given node-style callback function
*
* @param {Callback.<T>} [callback] - Callback function
* @returns {Promise}
*/
Promise.prototype.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;
};
/**
* A sugar method, equivalent to promise.then(undefined, onRejected).
*
* @param {RejectedCallback.<S>} onRejected
* @returns {Promise.<S>}
*/
Promise.prototype.fail = function() {
return new Promise(this._promise.fail.apply(this._promise, arguments));
};
/**
* Alias for completion
*
* @param {FulfilledCallback.<T, S>} [onFulfilled]
* @returns {Promise.<S>}
*/
Promise.prototype.done = function() {
return new Promise(this._promise.done.apply(this._promise, arguments));
};
/**
* @param {...Promise.<*>} p
*/
Promise.when = function() {
return new Promise(Q.when.apply(Q, arguments));
};
/**
* Returns rejecting promise with given reason
*
* @param {Error} reason - Rejecting reason
* @returns {Promise}
*/
Promise.reject = function(reason) {
return new Promise(Q.reject(reason));
};
/**
* Returns a promise that is fulfilled with an array containing the fulfillment value of each promise,
* or is rejected with the same rejection reason as the first promise to be rejected.
*
* @param {Array.<Promise.<*>|*>} promises
* @returns {Promise.<Array.<*>>}
*/
Promise.all = function() {
return new Promise(Q.all.apply(Q, arguments));
};
/**
* Returns a deferred object
*
* @returns {Deferred}
*/
Promise.defer = function() {
return new Deferred();
};
/**
* Deferred object
*
* @protected
* @constructor
*/
var Deferred = function() {
this._deferred = Q.defer();
this.promise = new Promise(this._deferred.promise);
};
/**
* Resolve promise
* @param {*} result - Resolving result
*/
Deferred.prototype.resolve = function() {
return this._deferred.resolve.apply(this._promise, arguments);
};
/**
* Reject promise
* @param {Error} error - Rejecting reason
*/
Deferred.prototype.reject = function() {
return this._deferred.reject.apply(this._promise, arguments);
};
/**
*
*/
module.exports = Promise;
}).call(this,require('_process'))
},{"_process":16,"q":19}],6:[function(require,module,exports){
/**
* @file Manages method call to SOAP endpoint
* @author Shinichi Tomita <shinichi.tomita@gmail.com>
*/
var _ = jsforce.require('underscore'),
xml2js = require('xml2js'),
Transport = require('./transport');
/**
* Class for SOAP endpoint of Salesforce
*
* @protected
* @class
* @constructor
* @param {Object} options - SOAP endpoint setting options
* @param {String} options.serverUrl - SOAP endpoint URL
* @param {String} options.sessionId - Salesforce session ID
* @param {String} [options.xmlns] - XML namespace for method call (default is "urn:partner.soap.sforce.com")
* @param {Transport} [transport] - HTTP request transport instance
*/
var SOAP = module.exports = function(options, transport) {
this.serverUrl = options.serverUrl;
this.sessionId = options.sessionId;
this.xmlns = options.xmlns || 'urn:partner.soap.sforce.com';
this._transport = transport;
if (!this._transport) {
this._transport = options.proxyUrl ? new Transport() : new Transport.ProxyTransport(options.proxyUrl);
}
};
/**
* Invoke SOAP call using method and arguments
*
* @param {String} method - Method name
* @param {Object} args - Arguments for the method call
* @param {Callback.<Object>} [callback] - Callback function
* @returns {Promise.<Object>}
*/
SOAP.prototype.invoke = function(method, args, callback) {
var message = {};
message[method] = args;
var soapEnvelope = this._createEnvelope(message);
return this._transport.httpRequest({
method: 'POST',
url: this.serverUrl,
headers: {
'Content-Type': 'text/xml',
'SOAPAction': '""'
},
body: soapEnvelope
}).then(function(res) {
var ret = null;
xml2js.parseString(res.body, { explicitArray: false }, function(err, value) { ret = value; });
if (ret) {
var error = lookupValue(ret, [ /:Envelope$/, /:Body$/, /:Fault$/, /faultstring$/ ]);
if (error) {
throw new Error(error);
}
return lookupValue(ret, [ /:Envelope$/, /:Body$/, /.+/ ]);
}
throw new Error("invalid response");
}).thenCall(callback);
};
/**
* @private
*/
function lookupValue(obj, propRegExps) {
var regexp = propRegExps.shift();
if (!regexp) {
return obj;
}
else {
for (var prop in obj) {
if (regexp.test(prop)) {
return lookupValue(obj[prop], propRegExps);
}
}
return null;
}
}
/**
* @private
*/
function toXML(name, value) {
if (_.isObject(name)) {
value = name;
name = null;
}
if (_.isArray(value)) {
return _.map(value, function(v) { return toXML(name, v); }).join('');
} else {
var attrs = [];
var elems = [];
if (_.isObject(value)) {
for (var k in value) {
var v = value[k];
if (k[0] === '@') {
k = k.substring(1);
attrs.push(k + '="' + v + '"');
} else {
elems.push(toXML(k, v));
}
}
value = elems.join('');
} else {
value = String(value);
}
var startTag = name ? '<' + name + (attrs.length > 0 ? ' ' + attrs.join(' ') : '') + '>' : '';
var endTag = name ? '</' + name + '>' : '';
return startTag + value + endTag;
}
}
/**
* @private
*/
SOAP.prototype._createEnvelope = function(message) {
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 + '">',
'<SessionHeader>',
'<sessionId>' + this.sessionId + '</sessionId>',
'</SessionHeader>',
'</soapenv:Header>',
'<soapenv:Body xmlns="' + this.xmlns + '">',
toXML(message),
'</soapenv:Body>',
'</soapenv:Envelope>'
].join('');
};
},{"./transport":7,"xml2js":36}],7:[function(require,module,exports){
(function (process){
/*global process, Sfdc */
var util = jsforce.require('util'),
stream = jsforce.require('stream'),
Promise = require('./promise');
/* */
var nodeRequest = require('request'),
xhrRequest = require('./browser/request'),
canvas = require('./browser/canvas'),
jsonp = require('./browser/jsonp');
var request, baseUrl;
if (typeof window === 'undefined') {
var defaults = {
followAllRedirects: true
};
if (process.env.HTTP_PROXY) {
defaults.proxy = process.env.HTTP_PROXY;
}
if (process.env.HTTP_TIMEOUT) {
defaults.timeout = process.env.HTTP_TIMEOUT;
}
request = nodeRequest.defaults(defaults);
baseUrl = process.env.LOCATION_BASE_URL || "";
} else {
request = xhrRequest;
var apiHost = normalizeApiHost(window.location.host);
baseUrl = apiHost ? "https://" + apiHost : "";
}
/**
* Add stream() method to promise (and following promise chain), to access original request stream.
* @private
*/
function streamify(promise, factory) {
var _then = promise.then;
promise.then = function() {
factory();
var newPromise = _then.apply(promise, arguments);
return streamify(newPromise, factory);
};
promise.stream = factory;
return promise;
}
/**
* Normarize Salesforce API host name
* @private
*/
function normalizeApiHost(apiHost) {
var m = /(\w+)\.(visual\.force|salesforce)\.com$/.exec(apiHost)
if (m) {
apiHost = m[1] + ".salesforce.com";
}
return apiHost;
}
/**
* Class for HTTP request transport
*
* @class
* @protected
*/
var Transport = module.exports = function() {};
/**
* Make HTTP request, returns promise instead of stream
*
* @param {Object} params - HTTP request
* @param {Callback.<Object>} [callback] - Calback Function
* @param {Callback.<Object>} [options] - Options
* @returns {Promise.<Object>}
*/
Transport.prototype.httpRequest = function(params, callback, options) {
var deferred = Promise.defer();
var req;
var httpRequest = request;
if (options && options.jsonp && jsonp.supported) {
httpRequest = jsonp.createRequest(options.jsonp);
} else if (options && options.signedRequest && canvas.supported) {
httpRequest = canvas.createRequest(options.signedRequest);
}
var createRequest = function() {
if (!req) {
req = httpRequest(params, function(err, response) {
if (err) {
deferred.reject(err);
} else {
deferred.resolve(response);
}
});
}
return req;
};
return streamify(deferred.promise, createRequest).thenCall(callback);
};
/**
* Class for HTTP request transport using AJAX proxy service
*
* @class Transport~ProxyTransport
* @protected
* @extends Transport
* @param {String} proxyUrl - AJAX Proxy server URL
*/
var ProxyTransport = Transport.ProxyTransport = function(proxyUrl) {
this._proxyUrl = proxyUrl;
};
util.inherits(ProxyTransport, Transport);
/**
* Make HTTP request via AJAX proxy
*
* @method Transport~ProxyTransport#httpRequest
* @param {Object} params - HTTP request
* @param {Callback.<Object>} [callback] - Calback Function
* @returns {Promise.<Object>}
*/
ProxyTransport.prototype.httpRequest = function(params, callback) {
var url = params.url;
if (url.indexOf("/") === 0) {
url = baseUrl + url;
}
var proxyParams = {
method: params.method,
url: this._proxyUrl + '?' + Date.now() + "." + ("" + Math.random()).substring(2),
headers: {
'salesforceproxy-endpoint': url
}
};
if (params.body || params.body === "") {
proxyParams.body = params.body;
}
if (params.headers) {
for (var name in params.headers) {
proxyParams.headers[name] = params.headers[name];
}
}
return ProxyTransport.super_.prototype.httpRequest.call(this, proxyParams, callback);
};
}).call(this,require('_process'))
},{"./browser/canvas":2,"./browser/jsonp":3,"./browser/request":4,"./promise":5,"_process":16,"request":8}]