Classes
This is the base class for all analytics query classes. For the most part, you are better off using one of the sub-classes but if you want more direct access, you can use this class as follows.
Usage
First, you need to "require" the desired analytics query class(es). In these examples, we're going to require a mock for the XMLHttpResponse Object but you will simply pass in the browser's XMHttpRequest Object (or the equivalent from node-XMLHttpRequest if running on node.js)
{XHRMock} = require('../../mock/XHRMock')
rally_analytics = require('../')
Then, you need to set the config Object.
config =
'X-RallyIntegrationName': 'My Chart'
'X-RallyIntegrationVendor': 'My Company'
'X-RallyIntegrationVersion': '0.1.0'
username: null # if running in browser, will prompt
password: null # if running in Node.js will look for RALLY_USER/RALLY_PASSWORD environment variables
workspaceOID: 12345
additionalHeaders: [
someHeader: 'Some Value'
]
Which you can then use when instantiating a query.
query = new rally_analytics.AnalyticsQuery(config, 'hello')
query.XHRClass = XHRMock # Not required to hit actual Rally Analytics API
Then you must set the query. find
is required but you can also specify sort, fields, etc. Notice how you can chain these calls.
query.find({Project: 1234, Tag: 'Expedited', _At: '2012-01-01'}).fields(['ScheduleState'])
Of course you need to have a callback.
callback = () ->
console.log(this.allResults.length) # will spit back 5 from our XHRMock
# 5
Finally, call getPage()
query.getAll(callback)
Properties you can inspect or set
- username default null
- password default null
- protocol default "https"
- server default "rally1.rallydev.com"
- service default "analytics"
- version defaults to latest current version
- endpoint defaults to "artifact/snapshot/query.js"
- XHRClass defaults to the local context XMLHttpResquest. Set to mock for testing.
Properties you should only inspect
Note, the signature of the callback is callback(snapshots, startOn, endBefore, this). On the last page, endBefore will be the ETLDate. For earlier pages, it will be the ValidFrom of the last row in lastPageResults, which is why the sort order must be {ValidFrom: 1}. startOn will be the @upToDate value, so be sure to set it before calling getPage() for the first time. It will update it after that first call to getPage().
The last parameter allows you to inspect the contents of this object. It has these potentially interesting properties.
- upToDate the endBefore of the prior call or as set when calling the constructor
- ETLDate the ETLDate of the response in the first page
- lastResponseText the string containing the most recent response/page
- lastResponse the parsed JSON Object of the most recent response/page
- lastPageResults the Results from the most recent page
- allResults the Results from all pages concatenated together
- lastPageMeta the meta data included at the top of the most recent page
- allMeta the meta data from all pages concatentated together including errors and warnings
Instance Methods
constructor(config, @upToDate)
resetFind()
find(@_find)
sort()
fields(additionalFields)
hydrate(@_hydrate)
start(@_startIndex)
startIndex(@_startIndex)
pagesize(@_pageSize)
pageSize(@_pageSize)
auth(@username, @password)
debug()
getBaseURL()
getQueryString()
getURL()
getAll(callback)
hasMorePages()
getPage(@_callback)
To help you write performant queries against the non-traditional data model of Rally's Analytics engine, we provide a guided mode for composing queries. Like the raw AnalyticsQuery, you start by creating a GuidedAnalyticsQuery Object.
query = new rally_analytics.GuidedAnalyticsQuery(config)
query.XHRClass = XHRMock # Not required to hit real Rally Analytics API
Scope
Then you must specify at least one highly selective criteria using the scope method:
query.scope('Project', 1234) # or [1234, 5678]
query.scope('_ProjectHierarchy', 1234) # or [1234, 5678], also accepts 'ProjectHierarchy'
query.scope('Iteration', 1234) # or [1234, 5678]
query.scope('Release', 1234) # or [1234, 5678]
query.scope('_ItemHierarchy', 1234) # also accepts 'ItemHierarchy'
query.scope('Tags', 'Top 10') # or ['Top 10', 'Expedite'], also accepts Tag
The 'ProjectHierarchy' scope is not necessarily highly selective. So you should make sure that you either have some other criteria or that you don't have too many Projects in scope beneith the specified Project(s).
Alternatively, you can specify your scope in one big object:
query.scope({
_ProjectHierarchy: 1234,
Iteration: [1234, 5678],
})
Type
You can optionally limit your query to one or more work item types. Defaults to all types.
query.type('Defect') # alteratively ['Defect', 'HierarchicalRequirement']
Note, a change is expected to be made such that the Analytics API will require ObjectIDs of the types. When that happens, we may update this REST toolkit to hide that from you but you'll need to update to the latest version.
Leaf Nodes Only
You can also specify that you only want leaf nodes to be returned by the query.
query.leafOnly()
It will expand to a clause like:
{
'$or': [
{_TypeHierarchy: "HierarchicalRequirement", Children: null},
{_TypeHierarchy:"PortfolioItem", Children: null, UserStories: null}
]
}
Additional Criteria
You can also specify additional critaria. This can be useful for defining "sub-classes" of work items.
query.additionalCriteria({Environment: 'Production'})
Chaining
Chaining is supported, so you could say:
query = new rally_analytics.GuidedAnalyticsQuery(config)
query.XHRClass = XHRMock # Not required to hit real Rally Analytics API
query.scope('_ProjectHierarchy', 1234)
.type('HierarchicalRequirement')
.leafOnly()
.additionalCriteria({Blocked: true})
query.find()
console.log(JSON.stringify(query._find, undefined, 2))
# {
# "$and": [
# {
# "_ProjectHierarchy": 1234
# },
# {
# "_TypeHierarchy": "HierarchicalRequirement"
# },
# {
# "$or": [
# {
# "_TypeHierarchy": -51038,
# "Children": null
# },
# {
# "_TypeHierarchy": -51078,
# "Children": null,
# "UserStories": null
# },
# {
# "_TypeHierarchy": {
# "$nin": [
# -51038,
# -51078
# ]
# }
# }
# ]
# },
# {
# "Blocked": true
# }
# ]
# }
Instance Methods
constructor(config, upToDate)
generateFind()
find()
resetScope()
scope(key, value)
resetType()
type(type)
resetAdditionalCriteria()
additionalCriteria(criteria)
leafOnly()
getPage(callback)
This pattern will tell you what a set of Artfacts looked like at particular moments in time
query = new rally_analytics.AtAnalyticsQuery(config, 'hello', '2012-01-01T12:34:56.789Z')
query.XHRClass = XHRMock # Not required to hit real Rally Analytics API
It will expand to a query like this:
query.scope('_ProjectHierarchy', 1234)
query.find()
console.log(JSON.stringify(query._find, undefined, 2))
# {
# "$and": [
# {
# "_ProjectHierarchy": 1234
# },
# {
# "_At": "2012-01-01T12:34:56.789Z"
# }
# ]
# }
Instance Methods
constructor(config, upToDate, zuluDateString)
This pattern is not implemented at this time but the intention is for it to tell you what a
set of Artfacts looked like at particular moments in time. In the mean time, use the "Between"
pattern defined above combined with the Lumenize snapshotArray_To_AtArray
function.
Eventually, this will be the ideal pattern to use for Burn charts, CFD charts, and most time-series charts
It's the same as the 'At' pattern except that the second parameter can be a list of timestamps.
query = new rally_analytics.AtArrayAnalyticsQuery(config, ['2012-01-01T12:34:56.789Z', '2012-01-02T12:34:56.789Z', ...])
Altneratively, we may make it possible to submit a ChartTimeIterator spec.
The way to implement this in the short term is to use the Between pattern and wrap in the Lumenize
snapshotArray_To_AtArray
transformation.
Note: it's tempting to try to make this more efficient by just looking for snapshots where the fields of interest change. However, that approach would not pick up on the deletions/restores nor the changing of the work item so it no longer meets the other criteria.
Note: when/if there is server-side support for finding the results at these points in time, this query pattern will be updated to take advantage of it.
Instance Methods
constructor(config, upToDate, arrayOfZuluDates)
This pattern will return all of the snapshots active in a particular timebox. The results are in the form expected by the
Lumenize function snapshotArray_To_AtArray
, which will tell you what each work item looked like at a provided list of
datetimes. This is the current recommended approach for most time-series charts. The burncalculator and cfdcalculator
use this approach. Note: the 'AtArray' approach will supercede this for time-series charts at some point in the future.
query = new rally_analytics.BetweenAnalyticsQuery(config, '2012-01-01T12:34:56.789Z', '2012-01-10T12:34:56.789Z')
query.XHRClass = XHRMock # Not required to hit real Rally Analytics API
It will expand to a query like this:
query.scope('_ProjectHierarchy', 1234)
query.find()
console.log(JSON.stringify(query._find, undefined, 2))
# {
# "$and": [
# {
# "_ProjectHierarchy": 1234
# },
# {
# "_ValidFrom": {
# "$lt": "2012-01-10T12:34:56.789Z"
# },
# "_ValidTo": {
# "$gt": "2012-01-01T12:34:56.789Z"
# }
# }
# ]
# }
Instance Methods
constructor(config, startOn, endBefore)
This pattern will only return snapshots where the specified clause is true. This is useful for Cycle Time calculations as well as calculating Flow Efficiency or Blocked Time.
query = new rally_analytics.TimeInStateAnalyticsQuery(config, 'hello', {KanbanState: {$gte: 'In Dev', $lt: 'Accepted'}})
query.XHRClass = XHRMock # Not required to hit real Rally Analytics API
Instance Methods
constructor(config, upToDate, predicate)
This pattern will return the snapshots where the _PreviousValue matches the first query clause parameter and the "current" value matches the second query clause parameter. In other words, it finds particular transitions. It is useful for Throughput/Velocity calculations.
!TODO: Indent below to make sure it works and add example query = new TransitionsAnalyticsQuery(config, {ScheduleState: {$lt: 'Accepted'}}, {ScheduleState: {$gte: 'Accepted'}} ) query.XHRClass = XHRMock # Not required to hit real Rally Analytics API
The first predicate is actually converted such that any non-operator key is prepended with "PreviousValues.". In the example above, "{ScheduleState: {$lt: 'Accepted'}}" becomes "{'PreviousValues.ScheduleState': {$lt: 'Accepted'}}". So this will return the snapshots that made this particular transition from before state to after state.
Note, you should also run the query swapping the two predicates and subtract the two calculations before reporting a Thoughput or Velocity result. Without doing so, any story that crosses the boudary multiple times would get double, triple, etc. counted.
In a future version, you may be able to specify aggregation functions ($count, $sum, $push, etc.) on a particular field when making this query, because when you use this pattern, you are usually interested in the sum or count and not the actual snapshots. In the mean time, if you are only interested in the count, simply specify pagesize of 1 and inspect the TotalResultCount in the top section of the response.
There is a good reason that Throughput and Velocity are defined with two predicates rather than just specifying the line to the left of "Accepted". Let's say, work is not really "Accepted" until the Ready flag is checked. You could write that query like so:
!TODO: Indent below to make sure it works and add example query = new rally_analytics.TransitionsAnalyticsQuery(config, {$or: [{KanbanState: {$lt: 'Accepted'}}, {KanbanState: 'Accepted', Ready: false}]}, {$or: [{KanbanState: 'Accepted', Ready: true}, {KanbanState: {$gt: 'Accepted'}}]} ) query.XHRClass = XHRMock # Not required to hit real Rally Analytics API
It will expand to a query like this:
!TODO: Indent below to make sure it works and add example query.scope('ProjectHierarchy', 1234) query.find() console.log(JSON.stringify(query.find, undefined, 2))