09 June 2008

Retrieving Areas from TFS

As a matter of self-inflicted education I’ve decided to create a simple Visual Studio Add-In for exporting and importing Areas and Iterations.  I’m not sure how much practical use such a utility will receive but it offers several areas for advancing my knowledge and, hopefully, helping a few other people out along the way.

Although I haven’t completed the utility yet, I have completed some of the initial utility code.  I thought I would create a few posts as I progress in case anyone can make use of the examples.  For this post, I am posting the code used to retrieve the list of “areas” from a Team Foundation Server

With that said, here is some example code for saving the list of Areas defined for a given Team Foundation Server and Team Project.  The code is a little verbose but should be relatively simple to follow.

using System;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Server;

namespace TestConsole
{
public class ExportAreas
{
private TeamFoundationServer _teamFoundationServer;
private ICommonStructureService _commonStructureService;

/// <summary>
/// Exports the "areas" defined within the specified team foundation server.
/// </summary>
/// <param name="teamFoundationServer">The team foundation server.</param>
/// <param name="projectName">The name of the team project to export areas from.</param>
/// <param name="filename">The filename to save the exported areas to.</param>
public void Export(string teamFoundationServer, string projectName, string filename)
{
_teamFoundationServer =
TeamFoundationServerFactory.GetServer(teamFoundationServer);

_commonStructureService = (
ICommonStructureService)_teamFoundationServer.GetService(typeof(ICommonStructureService));

List<HierarchyNode> nodeList = new List<HierarchyNode>();

// Locate root structure node for "Area"
NodeInfo areaNode = GetAreaRootNode(projectName);

//String node = _commonStructureService.CreateNode("TEST Node...", areaUri);

// Retrieve the Area nodes XML
XmlElement nodes = _commonStructureService.GetNodesXml(new string[] { areaNode.Uri }, true);

// Recursively build out the list of Area nodes
BuildHierarchicalList(nodes.ChildNodes[0], nodeList, 0);

// Save the areas to the designated text file
StreamWriter writer = new StreamWriter(filename, false);

foreach (HierarchyNode node in nodeList)
{
writer.WriteLine(
new string('\t', node.IndentLevel) + node.Name);
}

writer.Close();
}

/// <summary>
/// Recursively builds a hierarchical list based on the root node passed in.
/// </summary>
/// <param name="rootNode">The root node to start with.</param>
/// <param name="targetList">The target list to add each <see cref="HierarchyNode"/> instance to. This
/// list must be instantaited on the first call.</param>
/// <param name="indentLevel">The indent level - pass in 0 for the initial call.</param>
private void BuildHierarchicalList(XmlNode rootNode, List<HierarchyNode> targetList, int indentLevel)
{
HierarchyNode listNode;

if (rootNode.Name == "Children")
{
for (int index = 0; index < rootNode.ChildNodes.Count; index++)
{
BuildHierarchicalList(rootNode.ChildNodes[index], targetList, indentLevel);
}
}
else
{
listNode =
new HierarchyNode()
{
Uri = rootNode.Attributes[
"NodeID"].Value,
Name = rootNode.Attributes[
"Name"].Value,
ParentUri = GetAttribute(rootNode,
"ParentID"),
Path = rootNode.Attributes[
"Path"].Value,
ProjectUri = rootNode.Attributes[
"ProjectID"].Value,
StructureType = rootNode.Attributes[
"StructureType"].Value,
IndentLevel = indentLevel
};

targetList.Add(listNode);

if (rootNode.HasChildNodes)
{
for (int index = 0; index < rootNode.ChildNodes.Count; index++)
{
BuildHierarchicalList(rootNode.ChildNodes[index], targetList, indentLevel + 1);
}
}
}
}

/// <summary>
/// Gets the specified attribute from <paramref name="node"/>..
/// </summary>
/// <param name="node">The node containing the attribute collection.</param>
/// <param name="attributeName">The name of the attribute to retrieve.</param>
/// <returns>The value of the specified attribute if it exists; otherwise, an empty string is returned.</returns>
private string GetAttribute(XmlNode node, string attributeName)
{
for (int index = 0; index < node.Attributes.Count; index++)
{
if (string.Equals(node.Attributes[index].Name, attributeName, StringComparison.InvariantCultureIgnoreCase))
{
return node.Attributes[index].Value;
}
}

return string.Empty;
}

/// <summary>
/// Gets the area root node.
/// </summary>
/// <param name="projectName">The name of the team project to retrieve the root node from.</param>
/// <returns>A <see cref="NodeInfo"/> object referencing the root area node.</returns>
private NodeInfo GetAreaRootNode(string projectName)
{
ProjectInfo projectInfo = _commonStructureService.GetProjectFromName(projectName);

return LocateStructureByType(projectInfo.Uri, "ProjectModelHierarchy");
}

/// <summary>
/// Locates the desired structure based on structure type.
/// </summary>
/// <param name="projectUri">The project URI to retrieve the structure from.</param>
/// <param name="structureType">Type of the structure to retrieve.</param>
/// <returns>A <see cref="NodeInfo"/> object referencing the root of the desired structure.</returns>
private NodeInfo LocateStructureByType(String projectUri, String structureType)
{
NodeInfo[] nodes = _commonStructureService.ListStructures(projectUri);

foreach (NodeInfo node in nodes)
{
if (node.StructureType == structureType)
{
return node;
}
}

return null;
}

}

/// <summary>
/// This class is used to hold information pertaining to exported areas or iterations.
/// </summary>
class HierarchyNode
{
public string Uri { get; set; }
public string Name { get; set; }
public string ParentUri { get; set; }
public string Path { get; set; }
public string ProjectUri { get; set; }
public string StructureType { get; set; }
public int IndentLevel { get; set; }
}
}

To use the above sample code, just instantiate the ExportAreas class and call the Export method passing in the desired argument values.

2 comments:

Anonymous said...

Jeff,

Have a look at this tool
http://msmvps.com/blogs/vstsblog/archive/2007/07/07/copy-area-and-interation-structure-using-the-area-import-export-tool.aspx

Unknown said...

Eugene, thanks for the link. I've seen other examples regarding Areas and Iterations as well. This exercise is basically an excuse to create a simple TFS utility and provide integration with Visual Studio as an add-in. I realize I'm re-inventing the wheel (to a certain extent) but it seems like a great way to sharpen some skills :-)

Post a Comment