-
Notifications
You must be signed in to change notification settings - Fork 25
User Guide
The Rally REST API for Node.js provides an intuitive interface for accessing your Rally Data.
It provides a rich set of capabilities for querying, along with methods for creating, reading, updating, and deleting individual items.
The toolkit is distributed as an NPM module: https://npmjs.org/package/rally.
Simply install it from the command line or add it to the dependencies section of your package.json.
npm install rally
or
"dependencies": {
"rally": "0.2.x"
}
To get started simply require the rally module and instantiate it with your options:
var rally = require('rally'),
restApi = rally({
user: 'userName', //required if no api key, defaults to process.env.RALLY_USERNAME
pass: 'password', //required if no api key, defaults to process.env.RALLY_PASSWORD
apiKey: '_12fj83fjk...', //preferred, required if no user/pass, defaults to process.env.RALLY_API_KEY
apiVersion: 'v2.0', //this is the default and may be omitted
server: 'https://rally1.rallydev.com', //this is the default and may be omitted
requestOptions: {
headers: {
'X-RallyIntegrationName': 'My cool node.js program', //while optional, it is good practice to
'X-RallyIntegrationVendor': 'My company', //provide this header information
'X-RallyIntegrationVersion': '1.0'
}
//any additional request options (proxy options, timeouts, etc.)
}
});
Most of the parameters above have sane defaults and may be omitted. The requestOptions will be passed through to the excellent request module, which is used internally for all underlying i/o.
The toolkit exposes create, get, update, del and query methods for interacting with Rally. All methods return promises and also accept a standard node-style callback function.
This example demonstrates creating an object using the callback-style syntax:
restApi.create({
type: 'defect', //the type to create
data: {
Name: 'My Defect' //the data with which to populate the new object
},
fetch: ['FormattedID'], //the fields to be returned on the created object
scope: {
workspace: '/workspace/12345' //optional, only required if creating in non-default workspace
},
requestOptions: {} //optional additional options to pass through to request
}, function(error, result) {
if(error) {
console.log(error);
} else {
console.log(result.Object);
}
});
This example demonstrates reading an object using the promise-style syntax:
restApi.get({
ref: objectToRead, //may be a ref ('/defect/1234') or an object with a _ref property
fetch: ['FormattedID', 'Name'], //fields to fetch
scope: {
workspace: '/workspace/12345' //optional, only required if reading in non-default workspace
},
requestOptions: {} //optional additional options to pass through to request
}).then(function(result) {
console.log(result.Object);
}).fail(function(errors) {
console.log(errors);
});
This example demonstrates updating an object using the callback-style syntax:
restApi.update({
ref: objectToUpdate, //may be a ref ('/defect/1234') or an object with a _ref property
data: {
Name: 'My Updated Defect' //the data with which to update the specified object
},
fetch: ['FormattedID', 'Name'], //fields to fetch
scope: {
workspace: '/workspace/12345' //optional, only required if updating in non-default workspace
},
requestOptions: {} //optional additional options to pass through to request
}, function(error, result) {
if(error) {
console.log(error);
} else {
console.log(result.Object);
}
});
This example demonstrates deleting an object using the promise-style syntax:
restApi.del({
ref: objectToDelete, //may be a ref ('/defect/1234') or an object with a _ref property
scope: {
workspace: '/workspace/12345' //optional, only required if deleting in non-default workspace
},
requestOptions: {} //optional additional options to pass through to request
}).then(function(result) {
console.log(result);
}).fail(function(errors) {
console.log(errors);
});
This example demonstrates how to query for objects using the callback-style syntax:
restApi.query({
type: 'hierarchicalrequirement', //the type to query
start: 1, //the 1-based start index, defaults to 1
pageSize: 2, //the page size (1-200, defaults to 200)
limit: 10, //the maximum number of results to return- enables auto paging
order: 'Rank', //how to sort the results
fetch: ['FormattedID', 'Name', 'ScheduleState', 'Children'], //the fields to retrieve
query: queryUtils.where('DirectChildrenCount', '>', 0), //optional filter
scope: {
workspace: '/workspace/1234' //specify to query entire workspace
project: '/project/2345' //specify to query a specific project
up: false //true to include parent project results, false otherwise
down: true //true to include child project results, false otherwise
},
requestOptions: {} //optional additional options to pass through to request
}, function(error, result) {
if(error) {
console.log(error);
} else {
console.log(result.Results);
}
});
This example demonstrates how to query for an object collection using the promise-style syntax:
restApi.query({
ref: '/hierarchicalrequirement/1234/defects', //the collection to query- in this case the defects on a particular story
start: 1, //the 1-based start index, defaults to 1
pageSize: 200, //the page size (1-200, defaults to 200)
limit: Infinity, //the maximum number of results to return- enables auto paging
order: 'Rank', //how to sort the results
fetch: ['FormattedID', 'Name', 'State'], //the fields to retrieve
query: queryUtils.where('State', '<', 'Closed'), //optional filter
requestOptions: {} //optional additional options to pass through to request
}).then(function(result) {
console.log(result);
}).fail(function(errors) {
console.log(errors);
});
This example demonstrates how to add two defects to a story's Defects collection using the promise-style syntax:
restApi.add({
ref: '/hierarchicalrequirement/1234', //the object which owns the collection- in this case the parent story
collection: 'Defects', //the name of the collection being added to
data: [
{_ref: '/defect/2345'}, //add this existing defect
{Name: 'New Defect'} //add a new defect
],
fetch: ['FormattedID', 'Name', 'State'], //the fields to retrieve in the results
requestOptions: {} //optional additional options to pass through to request
}).then(function(result) {
console.log(result);
}).fail(function(errors) {
console.log(errors);
});
This example demonstrates how to remove a defect from a story's Defects collection using the callback-style syntax:
restApi.remove({
ref: '/hierarchicalrequirement/1234', //the object which owns the collection- in this case the parent story
collection: 'Defects', //the name of the collection being added to
data: [
{_ref: '/defect/2345'}, //remove this defect
],
requestOptions: {} //optional additional options to pass through to request
}, function(error, result) {
if(error) {
console.log(error);
} else {
console.log(result);
}
});
rally.util.ref contains methods for working with refs. As shown in the examples above refs represent the unique address of an object in Rally. Refs are generally of the form '/type/id' and are either a simple string ('/defect/1234') or an object containing a _ref property ({_ref: '/defect/1234'}). They may be either relative or fully qualified.
var rally = require('rally'),
refUtils = rally.util.ref;
//get a relative ref - returns /defect/1234
var relativeRef = refUtils.getRelative('https://rally1.rallydev.com/slm/webservice/v2.0/defect/1234');
//get the type from a ref - returns defect
var type = refUtils.getType({_ref: '/defect/1234'});
//get the id from a ref - returns 1234
var id = refUtils.getId('/defect/1234');
rally.util.query contains methods which simplify the building of complex queries in the Rally Web Services API's query syntax. This utility is most often used in conjunction with the toolkit's query method.
var rally = require('rally'),
queryUtils = rally.util.query;
//build a simple query
var query = queryUtils.where('ScheduleState', '<', 'Accepted');
//and
query = query.and('Owner', '=', '/user/1234');
//or
query = query.or('Name', 'contains', 'Foo');
//toQueryString - returns (((ScheduleState < Accepted) AND (Owner = /user/1234)) OR (Name contains Foo))
var queryString = query.toQueryString()
The following code takes the basic operations described above and illustrates how to create, read, update, and delete a defect using the toolkit in a promise-based style. This code is also available in the examples directory, along with its callback-style counterpart.
var rally = require('rally'),
restApi = rally(),
refUtils = rally.util.ref;
function createDefect() {
console.log('Creating defect...');
return restApi.create({
type: 'defect',
data: {
Name: 'My Defect',
Environment: 'Test'
}
});
}
function readDefect(result) {
console.log('Defect created:', refUtils.getRelative(result.Object));
console.log('Reading defect...');
return restApi.get({
ref: result.Object,
fetch: ['FormattedID', 'Name']
});
}
function updateDefect(result) {
console.log('Defect read:', result.Object.FormattedID, '-', result.Object.Name);
console.log('Updating defect...');
return restApi.update({
ref: result.Object,
data: {
Name: 'My Updated Defect'
},
fetch: ['Name']
});
}
function deleteDefect(result) {
console.log('Defect updated:', result.Object.Name);
console.log('Deleting defect...');
return restApi.del({
ref: result.Object
});
}
function onSuccess(result) {
console.log('Success!', result);
}
function onError(errors) {
console.log('Failure!', errors);
}
createDefect()
.then(readDefect)
.then(updateDefect)
.then(deleteDefect)
.then(onSuccess)
.fail(onError);
The following code takes the basic operations described above and illustrates how to query for epic stories and then load the children of the first result using the toolkit in a callback-based style. This code is also available in the examples directory, along with its promise-style counterpart.
var rally = require('rally'),
restApi = rally(),
queryUtils = rally.util.query;
function onError(error) {
console.log('Failure!', error);
}
function queryChildren(result) {
restApi.query({
ref: result.Results[0].Children,
start: 1,
limit: Infinity,
order: 'Rank',
fetch: ['FormattedID', 'Name', 'ScheduleState']
}, function(error, result) {
if(error) {
onError(error);
} else {
console.log('Success!', result)
}
});
}
function queryEpicStories(callback) {
restApi.query({
type: 'hierarchicalrequirement',
start: 1,
pageSize: 2,
limit: 10,
order: 'DragAndDropRank',
fetch: ['FormattedID', 'Name', 'ScheduleState', 'Children'],
query: queryUtils.where('DirectChildrenCount', '>', 0)
}, function(error, result) {
if(error) {
onError(error);
} else {
callback(result);
}
});
}
queryEpicStories(queryChildren);