Joe White’s Blog

Life, .NET, and Cats


Microsoft.TeamFoundationServer.Client: getting started, and getting sprint dates

There’s a new REST API in TFS 2015. It’s completely undocumented at the moment, although Visual Studio Online’s REST API is well-documented and they probably overlap a lot.

There’s a NuGet package called Microsoft.TeamFoundationServer.Client that provides a .NET client library for the TFS REST API. Unfortunately, it’s also completely undocumented, and in this case there is no other documentation to refer to — which makes it hard to figure out where to start.

But you’re in luck: I’ve been tinkering with it a bit, and I’m going to start blogging what I’ve learned about how to use the darned thing.

Connecting to work item tracking

Unlike the old-school TFS API, there isn’t some central class you have to create and authenticate to and then query for services.

Instead, you instantiate the client for the particular service you want, and hand it an object with authentication information. Then you start calling methods. I’m guessing that each method corresponds to a single HTTP request, but I haven’t verified that.

Let’s say we want to do something with work item tracking — basically anything relating to work items, including things like saved queries, area paths and iterations, work-item edit history… all that stuff. For that, we need to instantiate WorkItemTrackingHttpClient.

using Microsoft.TeamFoundation.WorkItemTracking.WebApi;
using Microsoft.VisualStudio.Services.Common;

var client = new WorkItemTrackingHttpClient(
    new Uri("http://myserver:8080/tfs"),
    new VssCredentials(true));

VssCredentials

VssCredentials tells the library how you’re going to authenticate. There are a bunch of constructor overloads, and I’m not clear on when you would use some of them. But here are the most obvious ways I see to authenticate:

Authenticate using my current Windows credentials

new VssCredentials(true)

This is probably simplest in most cases. Just pass true for the useDefaultCredentials constructor parameter.

Authenticate using a specific username and password

Maybe you’ve got a username and password in your web.config (stored in encrypted form, of course), and you want to use those to authenticate against TFS.

new VssCredentials(new WindowsCredential(new NetworkCredential(userName, password)))

It’s possible there’s a way to do that without chaining quite so many objects together. Or maybe not; you get used to things being unnecessarily complicated when you’re working with TFS.

Getting a list of sprints

Sometimes you need to query the work items for the current sprint. So let’s start by getting the list of sprints.

Sprints are just iterations (with some extra metadata that describes their start and finish dates), so we’ll query the iteration tree.

using Microsoft.TeamFoundation.WorkItemTracking.WebApi.Models;

var rootIterationNode = await client.GetClassificationNodeAsync(
    teamProjectName,
    TreeStructureGroup.Iterations,
    depth: int.MaxValue);

Each team project has its own areas and iterations, so you have to pass in the name of your team project. I assume you already know your teamProjectName and have it ready to pass in. (You can also pass in the project’s Guid if you happen to have it.)

GetClassificationNodeAsync returns a WorkItemClassificationNode:

public class WorkItemClassificationNode : WorkItemTrackingResource
{
    public WorkItemClassificationNode();

    public IDictionary<string, object> Attributes { get; set; }
    public IEnumerable<WorkItemClassificationNode> Children { get; set; }
    public int Id { get; set; }
    public string Name { get; set; }
    public TreeNodeStructureType StructureType { get; set; }
}

The method returns the root node for the iteration tree; as far as I know, the root’s Name always matches the name of the team project. Then you’ve got its children, and you can recursively walk your way through the tree.

Be careful — if a node has no children, Children is null rather than an empty list. (Makes sense for the JSON, but the API really should have smoothed out that wrinkle.)

You’ll notice that this class doesn’t have properties for StartDate and FinishDate for sprints. (I think that’s because you can also ask this method for area paths, where start- and finish-date properties would make no sense at all.) The object hides the dates inside the Attributes dictionary, under the keys startDate and finishDate; the values are DateTimes. Once again, beware: if a node has no attributes (e.g., if it represents an iteration that isn’t a sprint), the Attributes dictionary is null.

Here’s some sample code to get the start and finish dates (if present) from an iteration. If they’re present, it’s a sprint.

if (iterationNode.Attributes != null) {
    object startDateValue;
    object finishDateValue;
    iterationNode.Attributes.TryGetValue("startDate", out startDateValue);
    iterationNode.Attributes.TryGetValue("finishDate", out finishDateValue);
    var startDate = startDateValue as DateTime?;
    var finishDate = finishDateValue as DateTime?;
    if (startDate.HasValue && finishDate.HasValue) {
        // do something with startDate.Value and finishDate.Value
    }
}

Or you could do like I do, and write a GetValueOrDefault extension method for Dictionary<TKey, TValue>, at which point it becomes:

if (iterationNode.Attributes != null) {
    var startDate = iterationNode.Attributes.GetValueOrDefault("startDate") as DateTime?;
    var finishDate = iterationNode.Attributes.GetValueOrDefault("finishDate") as DateTime?;
    if (startDate.HasValue && finishDate.HasValue) {
        // do something with startDate.Value and finishDate.Value
    }
}

Other notes

If you want to load both iterations and area paths, you can save yourself a round-trip by calling client.GetRootNodesAsync, which returns them both in a single call (it returns a list of WorkItemClassificationNode instead of a single node). Remember to specify the depth parameter, or it’ll only return the root iteration and the root area path, without their children.

Further reading

The Visual Studio Online API Overview has a good conceptual overview of some of the terms you’ll see; for example, it explains the difference between “revisions” and “updates” when you’re looking at work item history. Worth checking out.

If I blog more about Microsoft.TeamFoundationServer.Client, you’ll be able to find it under my microsoft.teamfoundationserver.client tag.

One Response to “Microsoft.TeamFoundationServer.Client: getting started, and getting sprint dates”

  1. Kristian Leonhard Says:

    Any idea how to create a new work item using the Microsoft.TeamFoundationServer.Client?

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>


Joe White's Blog copyright © 2004-2011. Portions of the site layout use Yahoo! YUI Reset, Fonts, and Grids.
Proudly powered by WordPress. Entries (RSS) and Comments (RSS). Privacy policy