rallyappsdk2

Using collection summaries for multi-select fields


Using sdk2, I want to know how many tests are in a project, and how many of those tests are manual vs. automated. It seems like I should be able to use Collection Summaries to do this. The advantage of using summaries is that then I can get a count of tests back instead of a list of hundreds of tests, or making multiple queries (one for all tests, and one for manual tests).

But the example shows how to query for a summary of defects on a user story. It does now show if it is possible to get a summary of a multi-select or another type of field.

I tried to guess what the syntax might be below, but it didn't work:

Ext.create('Rally.data.wsapi.Store', {
    model: 'TestCase',
    fetch: ['Method:summary[Manual,Automated]'],  // This is the key statement: how to fetch a summary?
    pageSize: 1,
    autoLoad: true,
    listeners: {
        load: function(store, records) {
            var testcase = records[0];
            var methodInfo = testase.get('Method');
            var manualCount = methodInfo.Manual;
        }
    }
});

Is it possible to do this in a query with just one result using collection summaries?


Solution

  • This certainly would be useful for non-collection attributes such as TestCase.Method. WSAPI 2.0 Collection Summaries, however, were developed to provide a convenient way to get and summarize counts for Child Collections of Artifacts, without having to go back to WSAPI and query the collection itself. Because WSAPI 2.0 removed the ability to automatically hydrate Child Collections for performance reasons, the summary capability was important.

    Thus, the summary method works for summarizing attribute counts for a Child Collections of Objects on an Artifact, for example:

    1. Summarizing Tasks by State on Defects: https://rally1.rallydev.com/slm/webservice/v2.0/defect?fetch=Tasks:summary[State]&order=Rank
    2. Defects by Owner on Stories: https://rally1.rallydev.com/slm/webservice/v2.0/hierarchicalrequirement?fetch=Defects:summary[Owner]&order=ScheduleState

    To save loading of an entire store just to get attribute counts, you could set a filter on your store by TestCase Method, and use a unit page size to prevent loading the full set of records. Then use getTotalCount() to summarize the counts you want.

    However, this could get a bit cumbersome by having to load a WsapiStore and deal with a callback for each attribute you wish to summarize.

    By using Deft.js and Promises though, it is a bit more palatable. Here's a basic example that uses promises and Deft.Deferred to implement a _getCount(modelType, attribute, attrValue) function:

    Screenshot1

    Ext.define('CustomApp', {
        extend: 'Rally.app.App',
        componentCls: 'app',
    
        items: [
            {
                xtype: 'container',
                itemId: 'gridContainer',
                columnWidth: 1
            }
        ],
    
        _summaryGrid: null,
    
        launch: function() {
    
            this._summarizeTestCaseCounts();
    
        },
    
    
        _summarizeTestCaseCounts: function() {
    
            var me = this;
            var promises = [];
            var resultArray = [];
    
            promises.push(me._getCount('TestCase', 'Method', 'Manual'));
            promises.push(me._getCount('TestCase', 'LastVerdict', 'Failed'));
            promises.push(me._getCount('TestCase', 'LastVerdict', 'Pass'));
    
            Deft.Promise.all(promises).then({
                success: function(results) {
                    Ext.Array.each(results, function(result) {
                        resultArray.push(result);
                        console.log(result);
                    });
                    // Create grid from summarized results
                    me._makeGrid(resultArray);
                }
            });
    
        },
    
        _getCount: function(modelType, attribute, attrValue) {
            var deferred = Ext.create('Deft.Deferred');
    
            var artifactStore = Ext.create('Rally.data.wsapi.Store', {
                model: modelType,
                pagesize: 1,
                autoLoad: true,
                filters: [
                    {
                        property: attribute,
                        operator: '=',
                        value: attrValue
                    }
                ],
                sorters: [
                    {
                        property: 'FormattedID',
                        direction: 'ASC'
                    }
                ],
                listeners: {
                    load: function(store, records) {
                        var manualCount = store.getTotalCount();
                        result = {
                            "ModelType": modelType,
                            "Attribute": attribute,
                            "Value": attrValue,
                            "Count": manualCount
                        };
                        deferred.resolve(result);
                    }
                }
            });
    
            return deferred;
        },
    
        _makeGrid: function(results) {
    
            var me = this;
    
            if (me._summaryGrid) {
                me._summaryGrid.destroy();
            }
    
            var gridStore = Ext.create('Rally.data.custom.Store', {
                data: results,
                pageSize: 5,
                remoteSort: false
            });
    
            me._summaryGrid = Ext.create('Rally.ui.grid.Grid', {
                itemId: 'artifactGrid',
                store: gridStore,
    
                columnCfgs: [
                    {
                        text: 'Artifact', dataIndex: 'ModelType'
                    },
                    {
                        text: 'Attribute', dataIndex: 'Attribute'
                    },
                    {
                        text: 'Value', dataIndex: 'Value'
                    },
                    {
                        text: 'Count', dataIndex: 'Count'
                    }
                ]
            });
    
            me.down('#gridContainer').add(me._summaryGrid);
            me._summaryGrid.reconfigure(gridStore);
        }
    });
    

    As an aside, Matt Greer recently wrote a fantastic blog posting outlining his deep-dive on using Deft.js promises. It was very helpful to me in understanding how to use them when building Rally Apps.