Build a dashboard widget extension that calls the Analytics Service



Feature availability: The Analytics Marketplace extension is available to all VSTS accounts and provides access to several useful
widgets, such as Cumulative Flow, Velocity, and Lead and Cycle Time. The Analytics OData endpoint, which provides a concise model over the VSTS suite for Work Item Tracking, Test, Version Control, and
Build, is in a closed preview and only available to select VSTS customers at this time.

If you are looking for information about the Azure Analysis Services, see Azure Analysis Services or What is Azure Analysis Services?.

Building an dashboard widget extension that calls the Analytics Service is identical to building any other extension. However, in this early stage, a few things have to be done manually at this point - and then there's working with the returned data. This documentation will show you how to do both.

The following topics describe how to build an extension and a widet extension:

For the purposes of this example we'll use the open source C3 charting library which sits on top of the D3 graphics library.

At the end of this walk-through you will have a working dashboard widget which allows you to decompose work in several different ways in a column chart.

Set up the project

Before your begin, download the following items:

Create the widget page

  1. Create a new folder called "AnalyticsWidgetExtensionSample"
  2. Unzip the Client SDK and copy the VSS.SDK.js file into the AnalyticsWidgetExtensionSample\sdk\scripts folder
  3. Unzip D3 and copy the d3.min.js file into the AnalyticsWidgetExtensionSample\scripts folder
  4. Unzip C3 and copy the c3.min.js file into the AnalyticsWidgetExtensionSample\scripts folder
  5. Add an empty file called analytics.js to the AnalyticsWidgetExtensionSample\scripts folder - this is where the code for the widget will go
  6. Add another folder called images to the AnalyticsWidgetExtensionSample folder and copy an image you would like to use as your extension image (see this topic for details on the image)
  7. Copy the c3.min.css file into the AnalyticsWidgetExtensionSample\content folder
  8. Add a file called widget.css into the content folder
  9. In the AnalyticsWidgetExtensionSample folder create a new web page called Analytics.html

At this point the folder structure should look like the following:


Next copy the following HTML to the Analytics.html file:

<!DOCTYPE html>
<html xmlns="">
    <link href="content/c3.min.css" rel="stylesheet" type="text/css" />
    <link href="content/widget.css" rel="stylesheet" type="text/css" />
    <div id="title"></div>
    <div id="chart" style="width:300px; height:275px;"></div>
    <script src="sdk/scripts/VSS.SDK.js"></script>
    <script src="scripts/d3.min.js" charset="utf-8"></script>
    <script src="scripts/c3.min.js"></script>
    <script src="scripts/analytics.js"></script>

This code simply sets up the structure of the page - we'll write the code in a separate javascript file. The title div is the title of the widget and the chart div will hold the chart. The size dimensions allow for room around the chart for asthetics.

Add the following css to the widget.css file:

 #title {
    font-size: 16px; 
    font-weight: normal; 
    color: #424245; 
    margin-bottom: 15px;
    padding-left: 15px; 
    padding-top: 10px;

 #chart {
    margin-left: 10px;

Next, add the following code to the analytics.js file:

        explicitNotifyLoaded: true,
        usePlatformStyles: true
    VSS.require(["TFS/Dashboards/WidgetHelpers", "VSS/Authentication/Services"], function (WidgetHelpers, VSS_Auth_Service) {
        //Register the widget
        //Widget load function
        //Widget reload function
    //Reusable function to call the analytics service and build the chart

The init method does just what it says, it initializes the SDK. The second block of code you will make changes to over the next several steps and it does the heavy lifting.

Register the widget

To register the widget, update the analytics.js as shown here, replacing the //Register the widget comment:

VSS.require(["TFS/Dashboards/WidgetHelpers", "VSS/Authentication/Services"], function (WidgetHelpers, VSS_Auth_Service) {
    VSS.register("analyticsWidget", function () {
        //Widget load function
        //Widget reload function
//Reusable function to call the analytics service and build the chart

The register method takes the name of the widget, as defined in the manifest which hasn't been created yet, and a function to execute.

Retrieve data and build the chart

The next step is to retrieve data from the analytics service. To do that we first need an OAuth access token that we can use to authenticate against the service. Once we have the token, we need to append it to the header of our request and we're good to go. Update the code in the analytics.js file by replacing the //Reusable function to call the analytics service comment:

function createChart(VSS_Auth_Service, WidgetHelpers) {
    VSS.getAccessToken().then(function (token) {
        authToken = VSS_Auth_Service.authTokenManager.getAuthorizationHeader(token);

        var accountName = VSS.getWebContext();
        var projectName = VSS.getWebContext();
        var urlString = "https://" + accountName + "" + projectName
            + "/_odata/WorkItems?$apply=groupby((WorkItemType), aggregate(Count with sum as Count))";

            type: "GET",
            url: urlString,
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            beforeSend: function (xhr) {
                xhr.setRequestHeader('Authorization', authToken);
        .success(function (data, textStatus, jqXHR) {
            //success function
        .error(function (jqXHR, textStatus, errorThrown) {
            return WidgetHelpers.WidgetStatusHelper.Failure(errorThrown);

VSS.getAccessToken retrieves the authentication token for us. Once we have the token, an authorization header is created based on the token.

The next three lines are used to construct the analytics service URL to retrieve the data. This initial URL will return a count of work items for the account and project the widget is in, grouped by work item type.

The ajax call actually retrieves the data from the analytics service. The beforeSend function adds the authetication token to the header. The error function called is boilerplate code which allows any errors to be passed to the widget infrastructure for handling. The success function is where the the chart is built.

Replace the //success function comment with the following code which actually draws the chart:

var jsonData = data.value;

var chart = c3.generate({
    bindto: '#chart',
    data: {
        json: jsonData,
        keys: {
            x: 'WorkItemType',
            value: ['Count']
        type: 'bar',
        labels: true
    axis: {
        x: {
            type: 'category'
        y: {
            label: {
                text: 'Work Item Count',
                position: 'outer-middle'
    legend: {
        position: 'bottom'

C3 has a full reference on their site, but this code does the following:

  1. Sets the div in which to draw the chart
  2. Sets the data to use in the chart
  3. Sets the x axis category (WorkItemType) and the y axis data (Count)
  4. Sets the chart type to a bar
  5. Says that the x axis is a category type rather than a numerical type
  6. Sets the y axis label and the legend position

Hook the chart up to the widget

At this point in the process, we have code to retrieve the data and draw a chart but we don't have the functions that will be called by the extension framework which will trigger the data load.

Replace the //Widget load function comment with the following code:

return {
    load: function (widgetSettings) {
        var titleDiv = $("#title");
        titleDiv.text("Count of Work Items by Type");

        createChart(VSS_Auth_Service, WidgetHelpers);

        return WidgetHelpers.WidgetStatusHelper.Success();
    //Widget reload function

This function simply sets the title of the widget and calls the createChart function. For the moment, we'll leave the reload method alone. It's time to put the finishing touches on the widget and see what it looks like.

Add an extension manifest

For detailed information on extension manifests for widgets, see can be found Add a dashboard widget.

Add a file called vss-extension.json to the root of the project. Add the following to this file:

  "manifestVersion": 1,
  "id": "sampleODataWidget",
  "version": "0.1.0",
  "name": "Sample OData Widget",
  "publisher": "[your publisher name here]",
  "description": "A javascript based OData widget sample",
  "public": false,
  "targets": [
      "id": "Microsoft.VisualStudio.Services"
  "icons": {
    "default": "images/ODataLogo-96.png"
  "categories": [
    "Plan and track"
  "tags": [
  "branding": {
    "color": "blue",
    "theme": "light"
  "scopes": [
  "contributions": [
      "id": "analyticsWidget",
      "type": "ms.vss-dashboards-web.widget",
      "targets": [
      "properties": {
        "title": "Analytics Widget",
        "name": "Analytics Widget",
        "uri": "Analytics.html",
        "description": "Demonstration widget which returns the count of work items by work item type",
        "previewImageUrl": "images/gallery-image.png",
        "supportedSizes": [
            "rowSpan": 2,
            "columnSpan": 2
        "supportedScopes": [ "project_team" ]
  "files": [
      "path": "Analytics.html",
      "addressable": true
      "path": "sdk/scripts",
      "addressable": true
      "path": "images",
      "addressable": true
      "path": "scripts",
      "addressable": true
      "path": "content",
      "addressable": true

Remember to replace the author value denoted by [your publisher name here] with your publisher name. For details on extension manifests, see Extension manifest reference.

There are a few key things to note here:

  • This extension will show up in the widget gallery as denoted by this target: ms.vss-dashboards-web.widget-catalog
  • The scope is which is the OAuth scope required to access the analytics service
  • The supported size is 330x330 which is a 2x2 sized tile (160+160+10 pixels for spacing)

At this point the extension can be compiled, deployed, installed and executed following the steps described in Write your first extension for VSTS starting from the Create your publisher section.

You should end up with a widget that looks like this:


Continue to the next topic, Add configuration to the analytics chart to learn how to give users the ability to customize your extension.