Retrieve Work Item Discussion History using Visual Studio Online's REST API

Published on
Reading time
Authors

At TechEd North America Microsoft announced the availability of a preview REST API for Visual Studio Online.  Anyone who has spent time around Team Foundation Server will know that it's always had a strongly-typed C# SDK which can be used to extract and manipulate work items held in TFS.  This REST API is new and will eventually be available for the on-premise hosted version of TFS.

In a lucky coincidence I'm doing some work that requires me to extract work item information for other purposes so I decided to give the API a whirl.  As you can see it went pretty well!

https://twitter.com/simonwaight/status/466767466803122178

Strictly speaking, it isn't a complete Work Item as by default Attachments and History are both excluded.  Interestingly both of these tend to be pain points for other traditional tools to deal with too 🙂.

Luckily the new REST API offers support for retrieval of "Updates" which represent the set of fields changed on each update to the Work Item.  This can be any combination of fields or comments directly added to the history by users.

The demo code below shows how you can build out the history so it displays "Discussion Only" items and excludes all others.  As it's early days for the API maybe we'll see the sort of logic covered here wrapped in an API call so you don't need to parse these objects locally.

Once the entire solution is ready I'll post the source up on Github.  Note you'll need to enable the "Alternate Credentials" on your user profile to get this code working.

RetrieveWorkItemHistory.cs
// utilising RestSharp and Json.NET - get via Nuget.

private WorkItem GetWorkItem(string workItemIdentifier)
{
  var restClient = new RestClient("https://account.visualstudio.com/defaultcollection");
  restClient.Authenticator = new HttpBasicAuthenticator("username", "password");

  // code removed here

  var changes = GetWorkItemHistoryComments(restClient, workItemIdentifier);

  // code removed here
}

private IEnumerable<WorkItemCommentChange> GetWorkItemHistoryComments(RestClient activeClient, string workItemIdentifier)
{
  var request = new RestRequest("/_apis/wit/workitems/{id}/updates");
  request.AddParameter("api-version", "1.0-preview");
  request.AddUrlSegment("id", workItemIdentifier);

  var response = activeClient.Execute(request);

  dynamic json = JObject.Parse(response.Content);

  var changes = new List<WorkItemCommentChange>();

  string revisionDate = string.Empty;
  string revisedBy = string.Empty;
  string comments = string.Empty;

  // each one of these represents a revision
  foreach(var val in json.value)
  {
    // a revision contains multiple fields
    foreach(var ufield in val.fields)
    {
      if (ufield.field.id == -4) // System.ChangedDate
      {
        revisionDate = ufield.updatedValue; // "02/01/2014 01:21:00" (MM/dd/yyyy and UTC)
      }

      if (ufield.field.id == 9) // System.ChangedBy
      {
        revisedBy = ufield.updatedValue; // "Simon Waight"
      }

      if(ufield.field.id == 54) // System.History
      {
        comments = ufield.updatedValue; // can be plain text or HTML
      }
    }
    // we only care about comments
    if(!string.IsNullOrEmpty(comments))
    {
      changes.Add(new WorkItemCommentChange
                    {
                      ChangeDate = DateTime.ParseExact(revisionDate,
                        "MM/dd/yyyy HH:mm:ss",
                        CultureInfo.InvariantCulture,
                        DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal),
                      ChangedBy = revisedBy,
                      CommentText = comments
                    });
      comments = revisedBy = revisionDate = string.Empty;
    }
  }
  return changes;
}

public class WorkItemCommentChange
{
  public DateTime ChangeDate { get; set; }
  public string ChangedBy { get; set; }
  public string CommentText { get; set; }
}