UNPKG

jsforce

Version:

Salesforce API Library for JavaScript

1,434 lines (1,327 loc) 371 kB
!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}]