Skip to content
krmorse edited this page Dec 17, 2014 · 22 revisions

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.

Setup

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"
}

Usage

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.

Create an Object

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);
    }
});

Read an 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);
});

Update an Object

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);
    }
});

Delete an 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);
});

Query a Type

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);
    }
});

Query a Collection

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);
});

Add to a Collection

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);
});

Remove from a Collection

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);
    }
});

Utilities

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()

Full Examples

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);