Wednesday, September 14, 2016

VSTS Extension Work Item Limits

An error was recently pointed out on one of the extension that I’ve been working on as part of the ALM Rangers.

Looking at the browser logs we noticed something like this:
"An undefined error occurred while attempting to connect to the server. Status code 0: error."

image_thumb2

Very descriptive, right?!
It turns out that the root of the error lies in the fact that we were hitting limits with regards to work item queries. It looks like VSTS/TFS API only allows us to "read" in the region of about 300 work items at a go. It appears that there are teams out there that need up to a few thousand at a go.

How did I fix this? A great little JavaScript library called Q.
It was a fairly simple change as we all use the promise pattern for asynchronous calls to the services. Q is a chaining library that allows me to create a bunch of promises and wait for them to complete execution.

Practically this means breaking the list of work item id’s that I need into smaller chunks and then fetching these chunks. We could have simply written a recursive loop that would perform the same, but I’m lazy.

The code change looked like this:

From simply calling the API with all the work item id’s:

client.getWorkItems(backlogIds, null, asOfDate, WorkItemContracts.WorkItemExpand.Relations).then(backlogWorkItems => {
  //process
  defer.resolve(result);
});

To:

var loadSpecs = new Array<IPromise<any>>();
var spliceSize = 100;
var backlogSection = backlog.splice(0, spliceSize);

while (backlogSection.length > 0) {
   loadSpecs.push(this.GetWorkItemDetails(backlogSection, asOfDate));
   var backlogSection = backlog.splice(0, spliceSize);
}

Q.all(loadSpecs).done(all => {
  //combine “all” the results into one
   defer.resolve(result);
});

The GetWorkItemDetails method simple returns the promise from client.getWorkItems:

public GetWorkItemDetails(backlogItems: number[], asOf: Date): IPromise<any> {
   var client = WorkItemRestClient.getClient();
   var defer = $.Deferred<any>();
   client.getWorkItems(backlogItems, null, asOf,
   WorkItemContracts.WorkItemExpand.Relations)
        .then(backlogWorkItems =>   
   {
      //process
     
defer.resolve(result);
   });
   return defer;
}

This may not be the neatest, and please do not criticize my Javascript skills (I’m not a JS developer Surprised smile ), but it works and was a lot quicker to “fix” than expected.

For the full source code feel free to go and have a look at the github repo, in fact why not join in and make it better ! Open-mouthed smile