nforce-tooling
Version:
Force.com Tooling API plugin for nforce
449 lines (322 loc) • 15 kB
JavaScript
var _ = require('lodash');
module.exports = function(nforce, pluginName) {
if (!pluginName) pluginName = 'tooling';
// throws if the plugin already exists
var plugin = nforce.plugin(pluginName);
// returns a record based upons its ID
plugin.fn('getRecord', function(args, callback) {
var validator = validate(args, ['id', 'type']);
var opts = this._getOpts(args, callback);
if (validator.error) return callback(new Error(validator.message), null);
opts.uri = opts.oauth.instance_url + '/services/data/' + this.apiVersion
+ '/tooling/sobjects/' + args.type + '/' + args.id;
opts.method = 'GET';
return this._apiRequest(opts, opts.callback);
});
// returns 'describe' metadata at all levels for the specified object including fields, URLs, and child relationships.
plugin.fn('getDescribe', function(args, callback) {
var validator = validate(args, ['type']);
var opts = this._getOpts(args, callback);
if (validator.error) return callback(new Error(validator.message), null);
opts.uri = opts.oauth.instance_url + '/services/data/' + this.apiVersion
+ '/tooling/sobjects/' + args.type + '/describe';
opts.method = 'GET';
return this._apiRequest(opts, opts.callback);
});
// returns metadata for a specific custom field on a custom object
plugin.fn('getCustomField', function(args, callback) {
var validator = validate(args, ['id']);
var opts = this._getOpts(args, callback);
if (validator.error) return callback(new Error(validator.message), null);
opts.uri = opts.oauth.instance_url + '/services/data/' + this.apiVersion
+ '/tooling/sobjects/CustomField/' + args.id;
opts.method = 'GET';
return this._apiRequest(opts, opts.callback);
});
// Returns the high-level metadata for the specified object
plugin.fn('getObject', function(args, callback) {
var validator = validate(args, ['type']);
var opts = this._getOpts(args, callback);
if (validator.error) return callback(new Error(validator.message), null);
opts.uri = opts.oauth.instance_url + '/services/data/' + this.apiVersion
+ '/tooling/sobjects/' + args.type;
opts.method = 'GET';
return this._apiRequest(opts, opts.callback);
});
// returns the available Tooling API objects and their metadata.
plugin.fn('getObjects', function(args, callback) {
var opts = this._getOpts(args, callback);
opts.uri = opts.oauth.instance_url + '/services/data/' + this.apiVersion
+ '/tooling/sobjects/';
opts.method = 'GET';
return this._apiRequest(opts, opts.callback);
});
// creates a MetadataContainer to hold deployment objects
plugin.fn('createContainer', function(args, callback) {
var validator = validate(args, ['name']);
var opts = this._getOpts(args, callback);
if (validator.error) return callback(new Error(validator.message), null);
opts.uri = opts.oauth.instance_url + '/services/data/' + this.apiVersion
+ '/tooling/sobjects/MetadataContainer';
opts.method = 'POST';
opts.body = JSON.stringify({name: args.name});
return this._apiRequest(opts, opts.callback);
});
// returns a MetadataContainer by id
plugin.fn('getContainer', function(args, callback) {
var validator = validate(args, ['id']);
var opts = this._getOpts(args, callback);
if (validator.error) return callback(new Error(validator.message), null);
opts.uri = opts.oauth.instance_url + '/services/data/' + this.apiVersion
+ '/tooling/sobjects/MetadataContainer/' + args.id;
opts.method = 'GET';
return this._apiRequest(opts, opts.callback);
});
// returns a MetadataContainer by id
plugin.fn('addContainerArtifact', function(args, callback) {
var validator = validate(args, ['id', 'artifact']);
var opts = this._getOpts(args, callback);
var body = {};
if (validator.error) return callback(new Error(validator.message), null);
// add the container to the artifact
args.artifact.metadataContainerId = args.id;
// create the body for the request without any null properties and type
_.pairs(args.artifact).forEach(function(entry) {
if (!_.isNull(_.last(entry)) && _.first(entry) !== 'type') {
body[_.first(entry)] = _.last(entry);
}
});
opts.uri = opts.oauth.instance_url + '/services/data/' + this.apiVersion
+ '/tooling/sobjects/' + args.artifact.type;
opts.method = 'POST'
opts.body = JSON.stringify(body);
return this._apiRequest(opts, opts.callback);
});
// deletes a MetadataContainer by id
plugin.fn('deleteContainer', function(args, callback) {
var validator = validate(args, ['id']);
var opts = this._getOpts(args, callback);
if (validator.error) return callback(new Error(validator.message), null);
opts.uri = opts.oauth.instance_url + '/services/data/' + this.apiVersion
+ '/tooling/sobjects/MetadataContainer/' + args.id;
opts.method = 'DELETE';
return this._apiRequest(opts, opts.callback);
});
// deploys a MetadataContainer and all objects
plugin.fn('deployContainer', function(args, callback) {
var validator = validate(args, ['id']);
var opts = this._getOpts(args, callback);
var body = {
isCheckOnly: true,
metadataContainerId: args.id
};
if (validator.error) return callback(new Error(validator.message), null);
// look for passed argements
if (!_.isUndefined(args.isCheckOnly)) body.isCheckOnly = args.isCheckOnly;
opts.uri = opts.oauth.instance_url + '/services/data/' + this.apiVersion
+ '/tooling/sobjects/ContainerAsyncRequest';
opts.method = 'POST';
opts.body = JSON.stringify(body);
return this._apiRequest(opts, opts.callback);
});
// checks the current status of a deployment
plugin.fn('getContainerDeployStatus', function(args, callback) {
var validator = validate(args, ['id']);
var opts = this._getOpts(args, callback);
if (validator.error) return callback(new Error(validator.message), null);
opts.uri = opts.oauth.instance_url + '/services/data/' + this.apiVersion
+ '/tooling/sobjects/ContainerAsyncRequest/' + args.id;
opts.method = 'GET';
return this._apiRequest(opts, opts.callback);
});
// inserts a new record
plugin.fn('insert', function(args, callback) {
var validator = validate(args, ['type' , 'object']);
var opts = this._getOpts(args, callback);
if (validator.error) return callback(new Error(validator.message), null);
opts.uri = opts.oauth.instance_url + '/services/data/' + this.apiVersion
+ '/tooling/sobjects/' + args.type;
opts.method = 'POST';
opts.body = JSON.stringify(args.object);
return this._apiRequest(opts, opts.callback);
});
// updates an existing record
plugin.fn('update', function(args, callback) {
var validator = validate(args, ['id', 'type', 'object']);
var opts = this._getOpts(args, callback);
if (validator.error) return callback(new Error(validator.message), null);
opts.uri = opts.oauth.instance_url + '/services/data/' + this.apiVersion
+ '/tooling/sobjects/' + args.type + '/' + args.id;
opts.method = 'PATCH';
opts.body = JSON.stringify(args.object);
this._apiRequest(opts, function(err, results) {
if (err) { return callback(err, null); }
if (!err) { return callback(null, {success: true}); }
});
});
// deletes a record by id
plugin.fn('delete', function(args, callback) {
var validator = validate(args, ['id', 'type']);
var opts = this._getOpts(args, callback);
if (validator.error) return callback(new Error(validator.message), null);
opts.uri = opts.oauth.instance_url + '/services/data/' + this.apiVersion
+ '/tooling/sobjects/' + args.type + '/' + args.id;
opts.method = 'DELETE';
this._apiRequest(opts, function(err, results) {
if (err) { return callback(err, null); }
if (!err) { return callback(null, {success: true}); }
});
});
plugin.fn('query', function(args, callback) {
var validator = validate(args, ['q']);
var opts = this._getOpts(args, callback);
if (validator.error) return callback(new Error(validator.message), null);
opts.uri = opts.oauth.instance_url + '/services/data/' + this.apiVersion
+ '/tooling/query?q=' + encodeURIComponent(args.q);
opts.method = 'GET';
return this._apiRequest(opts, opts.callback);
});
// executes a chunk of Apex code anonymously.
plugin.fn('executeAnonymous', function(args, callback) {
var validator = validate(args, ['code']);
var opts = this._getOpts(args, callback);
if (validator.error) return callback(new Error(validator.message), null);
opts.uri = opts.oauth.instance_url + '/services/data/' + this.apiVersion
+ '/tooling/executeAnonymous/?anonymousBody=' + encodeURIComponent(args.code);
opts.method = 'GET';
return this._apiRequest(opts, opts.callback);
});
// rreturns a raw debug log by ID.
plugin.fn('getApexLog', function(args, callback) {
var validator = validate(args, ['id']);
var opts = this._getOpts(args, callback);
if (validator.error) return callback(new Error(validator.message), null);
opts.uri = opts.oauth.instance_url + '/services/data/' + this.apiVersion
+ '/tooling/sobjects/ApexLog/' + args.id + '/Body',
opts.method = 'GET'
return this._apiRequest(opts, opts.callback);
});
// returns the apex code coverages for the entire org
plugin.fn('getApexOrgWideCoverage', function(callback) {
var q = 'select PercentCovered from ApexOrgWideCoverage';
var opts = this._getOpts(callback);
opts.uri = opts.oauth.instance_url + '/services/data/' + this.apiVersion
+ '/tooling/query?q=' + encodeURIComponent(q);
opts.method = 'GET';
this._apiRequest(opts, function(err, results) {
if (err) { return callback(err, null); }
if (!err) { return callback(null, results.records[0].PercentCovered); }
});
});
// returns the apex code coverages for a specific apex class or trigger
plugin.fn('getApexClassOrTriggerCoverage', function(args, callback) {
var validator = validate(args, ['name']);
var opts = this._getOpts(args, callback);
var q = "select Coverage, NumLinesCovered, TestMethodName, ApexClassOrTrigger.Id, ";
q += "ApexClassOrTrigger.Name, NumLinesUncovered from ApexCodeCoverage ";
q += "where ApexClassOrTrigger.Name = '"+args.name+"'";
if (validator.error) return callback(new Error(validator.message), null);
opts.uri = opts.oauth.instance_url + '/services/data/' + this.apiVersion
+ '/tooling/query?q=' + encodeURIComponent(q);
opts.method = 'GET';
this._apiRequest(opts, function(err, results) {
if (err) { return callback(err, null); }
if (!err) {
// ensure that tests have been run and codecoverage exists
if (results.size != 0) {
var record = results.records[0];
var obj = {
PercentCovered: record.NumLinesCovered / (record.NumLinesUncovered + record.NumLinesCovered)
}
return callback(null, _.extend(obj, record));
} else {
return callback(null, new Error("No test coverage data exists. Run tests for this Apex class or Trigger."));
}
}
});
});
// runs specified test synchronously
plugin.fn('runTestsAsync', function(args, callback) {
var validator = validate(args, ['ids']);
var opts = this._getOpts(args, callback);
if (validator.error) return callback(new Error(validator.message), null);
opts.uri = opts.oauth.instance_url + '/services/data/' + this.apiVersion
+ '/tooling/runTestsAsynchronous/?classids=' + args.ids,
opts.method = 'GET';
this._apiRequest(opts, function(err, results) {
if (err) { return callback(err, null); }
if (!err) { return callback(null, results); }
});
});
// runs specified test synchronously with post
plugin.fn('runTestsAsyncPost', function(args, callback) {
var validator = validate(args, ['ids']);
var opts = this._getOpts(args, callback);
if (validator.error) return callback(new Error(validator.message), null);
opts.uri = opts.oauth.instance_url + '/services/data/' + this.apiVersion
+ '/tooling/runTestsAsynchronous/',
opts.method = 'POST';
opts.body = '{"classids":"' + args.ids + '"}';
this._apiRequest(opts, function(err, results) {
if (err) { return callback(err, null); }
if (!err) { return callback(null, results); }
});
});
// checks the run tests status of a specific job
plugin.fn('getAsyncTestStatus', function(args, callback) {
var validator = validate(args, ['id']);
var opts = this._getOpts(args, callback);
var q = "select Id, Status, ApexClassId FROM ApexTestQueueItem where ParentJobId = '"+args.id+"'";
if (validator.error) return callback(new Error(validator.message), null);
opts.uri = opts.oauth.instance_url + '/services/data/' + this.apiVersion
+ '/tooling/query?q=' + encodeURIComponent(q);
opts.method = 'GET';
return this._apiRequest(opts, opts.callback);
});
// returned the results of all runTests
plugin.fn('getAsyncTestResults', function(args, callback) {
var validator = validate(args, ['ids']);
// enclose each id with a single quote for sql
var sqlIds = "'" + args.ids.join("','") + "'";
var opts = this._getOpts(args, callback);
var q = "select StackTrace, Message, AsyncApexJobId, MethodName, Outcome, ApexClass.Id, ApexClass.Name ";
q += "from ApexTestResult where QueueItemId IN ("+sqlIds+")";
if (validator.error) return callback(new Error(validator.message), null);
opts.uri = opts.oauth.instance_url + '/services/data/' + this.apiVersion
+ '/tooling/query?q=' + encodeURIComponent(q);
opts.method = 'GET';
return this._apiRequest(opts, opts.callback);
});
// factory to create an empty deployment artifact
plugin.fn('createDeployArtifact', function(type, fields) {
// for convenience, set up the available properties
var obj = {
type: type,
body: null,
content: null,
contentEntityId: null,
metadata: null,
metadataContainerId: null,
symbolTable: null
}
return _.extend(obj, fields);
});
// utility method to validate inputs
function validate(args, required) {
var result = {
error: false,
message: 'No errors'
}
// ensure required properties were passed in the arguments hash
if (required) {
var keys = _.keys(args);
required.forEach(function(field) {
if(!_.contains(keys, field)) {
result.error = true;
result.message = 'The following values must be passed: ' + required.join(', ');
}
})
}
return result;
}
}