Introduction
Our development teams have been using the Microsoft Team Foundation Server (TFS) and, thus far, have been happy with it. We did, however, have one major problem: there is no Web interface to the work item tracking system for our customers to use to submit defects. At first our options seemed somewhat limited; we could have our customer’s email defects to us, but they have a funny way of getting lost that way. We could use a different defect tracking system for our customers and then enter them into the TFS, but the double entry is a pain not to mention keeping the defect status up to date. We could scrap the TFS defect tracking, but our teams are using it effectively. None of these solutions was palatable to us. After a bit of research, we began to explore the TFS API and found that integrating custom applications with TFS was simple, almost trivial. So we chose to implement a few Web forms to allow customers to submit defects to the TFS. This allowed us to create a very simple interface that hid the complexity and workflow of the work item tracking from our customers while keeping all the benefits of the system for our internal use.
In this brief tutorial, I will walk you through the steps toward creating your own Web based interface for work item tracking in TFS.
Implementation
First, you'll need to locate your assemblies for the team foundation server API. They should be located in: %PROGRAM FILES%\Microsoft Visual Studio 8\Common7\IDE\PrivateAssemblies.
You'll need to reference the Microsoft.TeamFoundation.WorkItemTracking.Client
assembly.
We created a simple class to serve as a hook to the work item data store. For our example, we will be creating two static
properties; one to get a handle to the project and one to get a work item of type Bug
. This provides a convenient mechanism for grabbing a project or bug type reference.
internal class DataManager
{
internal static Project DevelopmentProject
{
get
{
NetworkCredential account =
new NetworkCredential([account],[password],[domain]);
TeamFoundationServer server = new TeamFoundationServer(ServerName,account);
server.Authenticate();
WorkItemStore store = new WorkItemStore(server);
return store.Projects[ProjectName];
}
}
internal static WorkItemType BugType
{
get
{
return DevelopmentProject.WorkItemTypes["Bug"];
}
}
}
The next step was to create a simple interface to submit bugs. Figure 1 shows our initial interface. Again, the idea was to make something remarkably simple that our customers could understand without needing training.
Figure 1: Submit a Bug
To get the list of allowed severities, we simply needed to bind the drop down list to the allowed values property of the severity field:
Severity.DataSource = DataManager.BugType.FieldDefinitions["Severity"].AllowedValues;
Severity.DataBind();
Upon submit (after validating the input fields, of course), saving the new issue is a simple matter of creating a new work item, setting the field values, and saving the work item.
WorkItem newBug = new WorkItem(DataManager.BugType);
newBug.Title = Title.Text;
newBug.Fields["Symptom"].Value = Description.Text;
newBug.Fields["Severity"].Value = Severity.SelectedItem.Text;
newBug.Fields["Steps to Reproduce"].Value = Reproduce.Text;
newBug.Save();
Of course, if you're going to let them submit a bug, you may as well let them view the list of bugs. The list interface we created is illustrated in Figure 2. We followed the sample principle – simple is better.
Figure 2: Issue List
Getting the issues list from the TFS is also trivial. We simply needed to execute a query against the work item store and bind the results to a grid. If you aren't sure about the syntax of the SQL query, you may want to create a query using the GUI in the TFS client and then copy the syntax of the query. You will also need to use a name value collection (we chose a Hashtable) to add your parameter collection to the query. Execute the query against the work item store of the project. The resulting WorkItemCollection
can be bound directly to your grid.
Hashtable parameters = new Hashtable();
parameters.Add("project", [TFS Project Name]);
string query = "SELECT [System.Id], [System.WorkItemType],
[System.AssignedTo], [System.CreatedBy], [Microsoft.VSTS.Common.Priority],
[System.Title] FROM WorkItems WHERE [System.TeamProject] = @project
AND [System.WorkItemType] = 'Bug'
ORDER BY [System.WorkItemType], [System.Title],
[Microsoft.VSTS.Common.Priority], [System.Id]";
WorkItemCollection items = DataManager.DevelopmentProject.Store.Query(query,parameters);
Defects.DataSource = items;
Defects.DataBind();
Finally, we needed to create a basic interface for viewing the issue details. Again, we created a very simple interface that displayed the issue details in a manner that would be simple for our customers to understand. This screen is illustrated in Figure 3.
Figure 3: Issue Detail
To display the issue detail, we simply need to get the working item from the project’s work item data store using the work item ID passed via the query string. Each field that we want to display is read from the work item. We also display the history of a work item. In hind sight, we should have used a repeater to display this information to appropriately separate the display from the control logic. (I'll log that as an issue using this very simple interface!)
int id = int.Parse(Request.QueryString["IssueID"]);
WorkItem bug = DataManager.DevelopmentProject.Store.GetWorkItem(id);
title.Text = bug.Title;
status.Text = (string) bug.Fields["State"].Value;
triage.Text = (string) bug.Fields["Triage"].Value;
description.Text = (string) bug.Fields["Symptom"].Value;
reproduce.Text = (string) bug.Fields["Steps To Reproduce"].Value;
StringBuilder versionHistory = new StringBuilder();
foreach (Revision issue in bug.Revisions)
{
versionHistory.Insert(0,
string.Format("By:{0}<br>On: {1}<br>{2}<br><hr>",
issue.Fields["Changed By"].Value,
issue.Fields["Changed Date"].Value,
issue.Fields["History"].OriginalValue));
}
history.Text = versionHistory.ToString();
That’s really all there is to it. With three simple Web forms and some trivial code, we were able to create a Web based interface for our users to submit and view issues. Because of the overall simplicity, our users like this system more so than our previous, completely Web based, issue tracking system.
End Notes
About Connecting to the Server
When integrating to the team foundation server, you may experience a couple of issues.
- Connecting to the server – While using the server name was sufficient for our developers to connect to the team foundation server, when we rolled out to production, we needed to connect to the server using the following syntax: http://[server name]:8080.
- Connection credentials – While developing the Web forms, our developers needed to only supply the server name, and not the credentials. This did not work in production because the account running the application did not have access to the team foundation server. Using the
NetworkCredential
class with a configurable user name and password that could access the server worked. Using Windows Authentication with impersonation should also work. - The Network Service account running the application did not have access to the team foundation work item cache directory. This can be fixed by:
- Granting read/write access to the directory to the Network Service
- Running the application using an account that does have access to the service
- Configuring the application to use a different directory for the work item cache to which the Network Service does have read/write access
About the Source Code
The source code relies on a custom configuration section to store the connection information for the team foundation server. We do this so that the connection data can be encrypted if need be. In the Web.Config file, edit the TeamFoundationIntegration
configuration section to supply your own credentials.
The source code is written using the “Web Application” project type in Visual Studio 2005. This project type does not ship natively with Visual Studio 2005, but may be downloaded free of charge from Microsoft.
The source code is intended to provide you with a starting point for creating your own Web interface to TFS and is not intended to be a “shrink-wrapped” solution. Decide which fields are important to you and your customers and modify the code as necessary.
About Team Foundation Server Licensing
It is important that you and your organization identify your licensing needs. The only recommendation I can make is to look into the "Device CAL". Beyond that, I cannot make any licensing recommendations for you. If you have questions about licensing issues, contact Microsoft. I can tell you that my organization contacted Microsoft with licensing questions and were very happy with the results.