Thursday, April 15, 2010

TFS2010: Who am I? (getting logged in user info)

When you login to TFS using object model, sometimes it may be useful to get information about logged in user (such as domain, account, email etc.).

In TFS 2008 object model, one would get the logged in user display name, domain name, account name and email as following:

    1 TeamFoundationServer server = new TeamFoundationServer("tfsserver",

    2        new System.Net.NetworkCredential("domain_name\\user_name", "pwd"));

    3 server.EnsureAuthenticated();

    4 

    5 Console.Write(server.AuthenticatedUserDisplayName);

    6 // returns user's display name

    7 

    8 Console.Write(server.AuthenticatedUserName);

    9 // returns domain_name\user_name

   10 

   11 // AuthenticatedUserIdentity is Microsoft.TeamFoundation.Server.Identity

   12 Console.Write(server.AuthenticatedUserIdentity.Domain + "\\" +

   13         server.AuthenticatedUserIdentity.AccountName);

   14 // returns domain_name\user_name  

   15        

   16 Console.Write(server.AuthenticatedUserIdentity.MailAddress); 

   17 // returns user’s email  

In TFS 2010 OM, AuthenticatedUserDisplayName, AuthenticatedUserName and AuthenticatedUserIdentity properties are all deprecated, and instead AuthorizedIdentity property is introduced on Team Project Collection class. The code below makes use of the new property to retrieve logged in user information:

    1 TfsTeamProjectCollection collection = new TfsTeamProjectCollection(

    2        new Uri(http://tfsserver:8080/tfs/defaultcollection,

    3        new System.Net.NetworkCredential("domain_name\\user_name", "pwd"));

    4 collection.EnsureAuthenticated();

    5 

    6 // AuthorizedIdentity is Microsoft.TeamFoundation.Framework.Client.TeamFoundationIdentity

    7 Console.Write(collection.AuthorizedIdentity.DisplayName);

    8 // returns user's display name

    9 

   10 Console.Write(collection.AuthorizedIdentity.GetAttribute("Domain", "default"));

   11 // returns domain_name

   12 

   13 Console.Write(collection.AuthorizedIdentity.GetAttribute("Account", "default"));

   14 // returns user_name

   15 

   16 Console.Write(collection.AuthorizedIdentity.GetAttribute("Mail", "default")); 

   17 // returns user’s email

Mirror from my MSDN blog

Tuesday, April 13, 2010

TFS2010: Getting connected in new object model

When starting up with TFS 2010 object model, there are few gotchas to be aware of (especially if you did coding for TFS OM 2005/2008 in the past). See my previous post for high-level details:

In 2008 one would establish connection to TFS as follows

   1 string[] servers = RegisteredServers.GetServerNames();

   2 

   3 // select one of the servers (in real life combo box?)

   4 string serverName = servers[0];

   5 

   6 TeamFoundationServer server = new TeamFoundationServer(serverName,

   7         new System.Net.NetworkCredential(userName, password));

   8 

   9 server.EnsureAuthenticated();

In Team Foundation Server 2010 the code becomes:

   1 RegisteredTfsCollection[] projects = RegisteredTfsConnections.GetProjectCollections();

   2 List<string> servers = new List<string>();

   3 foreach (RegisteredTfsCollection collection in collections)

   4 {

   5    if (!collection.Offline)

   6      results.Add(collection.Uri.ToString());

   7 }

   8 

   9 // select one of the servers (in real life combo box?)

  10 string serverName = servers[0];

  11

  12 TfsTeamProjectCollection server = new TfsTeamProjectCollection(new Uri(tfsServerName),

  13                 new System.Net.NetworkCredential(userName, password));

  14 

  15 server.EnsureAuthenticated();

Should be no big surprises considering the changes in 2010 – one server w\projects became multiple projects collections. One thing to be aware of is the change in the arguments you pass to constructor of TfsTeamProjectCollection; in 2005/2008 one was able to pass server name (i.e. “tfserver”) as retrieved by using RegisteredServers class. In 2010 that becomes impossible since the server may contain multiple collections; thus you have to pass full URI (e.g. “http://tfsserver:8080/tfs/DefaultCollection”).

Mirror from my MSDN blog

Sunday, September 28, 2008

How to deal with areas or iterations using API

One relatively obscure area of TFS object model is area/iteration manipulation. Prompted by the question on how to delete area (btw, did you know how to use DeleteBranches for that) I decided to do a quick primer on area/iteration API and its usage.

First, how does one get areas/iteration list given the project (or parent node)? If you even started thinking about the areas or iterations, you will need to use ICommonStructureService service. This service encapsulates functionality related to TFS artifacts such as projects, areas and iterations (those are the common structures in its name).

Another important thing to know about this service is that most of its methods require artifact URIs as parameters. URI would consist of protocol (vstfs), type and unique identifier for the artifact; for example, for area it would be similar to "vstfs:///Classification/Node/[Guid]" (where Guid is unique ID for that area).

Let's start with a simple task of getting project's areas and iterations:

Dictionary<string, string> GetProjectAreas(string projectName)
{
    ICommonStructureService commonStructure = 
        _teamFoundationServer.GetService(typeof(ICommonStructureService)) 
            as ICommonStructureService;
    ProjectInfo project = commonStructure.GetProjectFromName(projectName);
    Dictionary<string, string> results = new Dictionary<string, string>();
 
    foreach (NodeInfo node in commonStructure.ListStructures(project.Uri))
    {
        // here will be more code
    }
    return results;
}

Note several aspects: creation of ICommonStructureService, conversion from project name to project URI and call to ListStructures method. Most important of those is ListStructures method – given project URI, it returns array of NodeInfo structures that holds all top level areas and iterations defined. NodeInfo itself provides host of properties such as node name, node path, node URI, parent URI and node structure type. Using the latter one may distinguish area node from iteration node (area StructureType is “ProjectModelHierarchy”, whereas for iteration it is “ProjectLifecycle”).

Common Structure Service (CSS) provides couple of handy methods to get NodeInfo structure either by node path (GetNodeFromPath) or by node URI (GetNode). However, most frequent task is to get subtree of nodes (areas of iterations) given project root node. For the top level nodes it can be easily done by using ListStructures; but to drill down into the tree one must use different approach, namely GetNodesXml method of CSS.

Code example below iterates over all root areas in project, and calls recursive function to retrieve all child areas and put them into dictionary of pairs (<area path>, <area URI>). First, I will change GetProjectAreas function code to include new function:

    foreach (NodeInfo node in commonStructure.ListStructures(project.Uri))
    {
        // more code
        if (node.StructureType != "ProjectModelHierarchy")
            continue;
        XmlElement nodeElement = 
            commonStructure.GetNodesXml(new string[] { node.Uri }, true);
        AddChildNodes(
            node.Name, nodeElement.ChildNodes[0], results);
    }

And now, iterate over the results recursively:

static void AddChildNodes(
    string parentPath, XmlNode parentNode, Dictionary<string, string> results)
{            
    results.Add(parentPath, parentNode.Attributes["NodeID"].Value);
 
    if (parentNode.ChildNodes[0] == null)
        return;
 
    foreach (XmlNode node in parentNode.ChildNodes[0].ChildNodes)
    {
        string nodePath = node.Attributes["Path"].Value;
        AddChildNodes(nodePath, node, results);                
    }
}

The example shows most features of GetNodesXml. It takes two parameters: array of root node URIs and boolean specifying whether to retrieve data for child nodes.

Then GetNodesXml returns hierarchical XML document, with every node in the return result represented as separate child “Node” XmlElement, with all properties as XML attributes (the attributes names and values correspond to NodeInfo properties). Raw XML for for area “Area 0” in project “Test Project” node will look similar to this (I removed actual GUID values for clarity):

<Node
  NodeID="vstfs:///Classification/Node/[guid 1]"
  Name="Area 0"
  ParentID="vstfs:///Classification/Node/[guid 2]"
  Path="\Test Project\Area\Area 0"
  ProjectID="vstfs:///Classification/TeamProject/[guid 3]"
  StructureType="ProjectModelHierarchy" />

Hierarchical means that if “Area 0”, for example, has any child areas, they will be contained in its Node XmlElement. Thus with the help of GetNodersXml it is possible to get the whole areas/iterations tree for specific project.

And to finalize review of CSS functionality that is relevant to areas/iterations, let’s have a look at other parts of C[R]UD.

How to create area/iteration? There is method CreateNode (that takes parent node URI and new node name); additionally, there is ImportBranch method that takes new node in the form of XmlElement  (XML should conform to CSS format, same as used by GetNodesXml above).

How to update area/iteration? For the purpose of rename one may use RenameNode method (supplying node URI and new name); to move the node (and all child nodes) one may use MoveBranch method (supplying node and parent URIs)

How to delete area (or iteration)? First, you need to get area/iteration URI (and that is covered in the beginning of the post). Next, you need to call method DeleteBranches of ICommonStructureService.


Sound simple, eh? However, the method takes two parameters: while first is easy to understand –  the array of node URI to delete, the second (named reclassifyUri) is far less obvious.

To understand how that parameter works, it would be helpful to refer to one of my past posts that talks about deleting area/iteration in Team Explorer UI.

Now, reclassifyUri is exactly that - the URI of the node to use in reassigning all work items associated with areas/iterations that are being deleted (or as MSDN succinctly puts it "URI of the node to which artifacts are reclassified").

In that post I tried to give quick survey of basics related to areas/iterations API. It is worth noting that CSS can be used for another important purpose of listing projects (for example, as demonstrated in James Manning blog post).

To conclude, some related links that may be of interest (mostly tools with the source code):