Microsoft Team Foundation Server (TFS) is used for defect tracking in many organizations. Defects can be logged by the client through various channels including phone calls and email messages. There are plugins available to convert an email from Microsoft Outlook to TFS as a work item. But, there is no plugin available for the integration between Lotus Notes-based email services with TFS. This article addresses the integration issue between Lotus Notes and TFS.
Introduction
Customer Helpdesk receives numerous tickets, which will be categorized and managed in one of the defect management tools like TFS or HP’s Application Lifecycle Manager (ALM). Most of the times, the tickets will be received through an email and the Support team will create a defect or ticket from the email content and assign to specific/relevant groups. For Outlook, a plugin is available to integrate with TFS. This helps the Support Group to create a defect in TFS using few clicks on Outlook! Lotus Notes, which is another widely-used email client, no plugins are available to facilitate this functionality. It involves manual effort to create the defect and copy the content from email to the defect, and so on.
This article attempts to provide a solution for the Lotus Notes to TFS work item integration process. We adopt a two-phased approach - first phase is a solution outside Lotus Notes which provides a separate GUI to list all the emails and create different work items in TFS; in the second Phase, we will look into the creation of a Java Plugin for Lotus Notes which invokes the .NET service to pass the details to TFS. As a Microsoft technology evangelist, I will be taking you through Phase 1 and only explaining the approach for Phase 2. Anyone from the Java community can surely adopt the Phase 2 approach and develop a Lotus Notes plugin, which can seamlessly integrate Lotus Notes with TFS.
Phase I: WPF application
In Phase 1, we will create a standalone Windows application, which extracts the email information from Lotus Notes and writes to TFS as a work item. Following diagram depicts the process flow.
Assumption: The system, on which the solution will be deployed, will have Lotus Notes installed and configured
Create a new WPF Application
Open Visual Studio and create a new WPF application. Let us start the development by integrating Lotus Notes initially. Lotus Notes provides COM-based assemblies for .NET to access emails. These assemblies will be installed in the client system, where Lotus Notes is installed and configured.
Add Lotus Domino Objects Reference
To add Lotus Domino Objects Reference:
-
Right-click on the project name in Solution explorer and select Add → Reference.
-
Select COM section in the Reference Manager window and select the assembly 'Lotus Domino Objects.’
-
Click OK to add the reference to the project.
Add TFS References
Add the following TFS assembly references to the project. These assemblies will be available at:
C:\Program Files\Microsoft Visual Studio 12.0\Common7\IDE\Reference Assemblies\v2.0\
-
Microsoft.TeamFoundation.Common.dll
-
Microsoft.TeamFoundation.Client.dll
-
Microsoft.TeamFoundation.WorkItemTracking.Client.dll
Retrieve Inbox from Lotus Notes
-
Retrieve the inbox details from Lotus Notes.
-
Use the Lotus Notes database name and current user password to connect to Lotus Notes.
-
Provide the Lotus Notes password for the current user to retrieve the email files.
Lotus Notes Database Settings
Lotus Notes database details can be obtained from the Lotus Notes configuration.
-
Open the Lotus Notes and navigate to File -> Preferences -> Locations.
-
Select the current location and click Edit. The Edit Location dialog box appears.
-
Click the Mail tab and check the configured value for ‘Mail File’ option. This indicates the Lotus Notes’ database name.
Design WPF Screen
Design a UI to capture the database name and password from user, which is unique to a user. Let us define a simple UI with two text boxes to accept the details and a Get Emails button to invoke the function which retrieves the inbox details depending on the user entry.
Now, add a Data Grid to the main window to display the emails from Inbox. We define the columns for displaying the fields; ‘From’ or the sender, date and subject of the email.
Following XAML code snippet defines the simple data grid named ‘mailGrid’.
<DataGrid HorizontalAlignment="Left" Height="429" Margin="21,183,0,0" VerticalAlignment="Top" AutoGenerateColumns="False" Width="911" x:Name="mailGrid" ItemsSource="{Binding}" >
<DataGrid.Columns>
<DataGridTextColumn Header="From" Binding="{Binding From}" Width="180" />
<DataGridTextColumn Header="Date" Binding="{Binding Date}" Width="100" />
<DataGridTextColumn Header="Subject" Binding="{Binding Subject}" Width="*"/>
<font face="Courier New"> </DataGrid.Columns></font>
<font face="Courier New"> </DataGrid></font>
Retrieve Notes’ Inbox
We use various classes and constructs from the Domino’s COM assembly for retrieving the emails from Lotus Notes. NoteSession is the main class in the Dominos assembly. Create an object of the NoteSession class and initialize the same with Password.
_lotesNotesSession = new NotesSession();
_lotesNotesSession.Initialize(pwd.Password);
Invoke the GetDatabase method by passing the database name. Lotus Notes’ database object have different views defined for Inbox, Sent, Outbox, and so on. Get the Inbox view to retrieve the mails.
_localDatabase = _lotesNotesSession.GetDatabase("", dbName.Text, false);
_mailView = _localDatabase.GetView("($Inbox)");
GetDatabase method takes three parameters.
-
Server name: Specify the server name, when you need emails directly from server.
-
File name: Specify the database name of Lotus Notes client, where emails from the client will be available.
-
Create on fail: Boolean value
We use the database name of the local instance of Lotus Notes to retrieve emails, which are sync to the local Lotus Notes database.
Get all entries from the mail view and retrieve the NoteDocument from each entry
NotesViewEntryCollection notesViewCollection = _mailView.AllEntries;
---------------------
NotesViewEntry viewEntry = notesViewCollection.GetNthEntry(rowCount);
NotesDocument document = viewEntry.Document;
Retrieve the NoteItem collection from document, which represents the email components.
object documentItems = document.Items;
Array itemArray1 = (System.Array)documentItems;
---------------------
NotesItem notesItem = (NotesItem)itemArray1.GetValue(itemCount);
Retrieve the data from NotesItem
if (notesItem.Name == "INetFrom")
{
dr["From"] = notesItem.Text;
}
if (notesItem.Name == "PostedDate")
dr["Date"] = notesItem.Text;
if (notesItem.Name == "Subject")
dr["Subject"] = notesItem.Text;
if (notesItem.Name == "Body")
dr["Body"] = notesItem.Text;
Following is the complete code for retrieving the email data from local Lotus Notes database.
private void btnGetMails_Click(object sender, RoutedEventArgs e)
{
List<string> senders = new List<string>();
dtmails.Rows.Clear();
_lotesNotesSession = new NotesSession();
_lotesNotesSession.Initialize(pwd.Password);
_localDatabase = _lotesNotesSession.GetDatabase("", dbName.Text, false);
_mailView = _localDatabase.GetView("($Inbox)");
NotesViewEntryCollection notesViewCollection = _mailView.AllEntries;
for (int rowCount = 1; rowCount <= notesViewCollection.Count; rowCount++)
{
NotesViewEntry viewEntry = notesViewCollection.GetNthEntry(rowCount);
NotesDocument document = viewEntry.Document;
object documentItems = document.Items;
Array itemArray1 = (System.Array)documentItems;
DataRow dr = dtmails.NewRow();
for (int itemCount = 0; itemCount < itemArray1.Length; itemCount++)
{
NotesItem notesItem = (NotesItem)itemArray1.GetValue(itemCount);
if (notesItem.Text != null)
{
if (notesItem.Name == "INetFrom")
{
dr["From"] = notesItem.Text;
if (!senders.Contains(notesItem.Text))
senders.Add(notesItem.Text);
}
if (notesItem.Name == "PostedDate")
dr["Date"] = notesItem.Text;
if (notesItem.Name == "Subject")
dr["Subject"] = notesItem.Text;
if (notesItem.Name == "Body")
dr["Body"] = notesItem.Text;
}
}
if (!string.IsNullOrEmpty(dr["From"].ToString()))
dtmails.Rows.Add(dr);
}
mailGrid.DataContext = dtmails;
senders.Sort();
senderName.DataContext = senders;
}
Let us do some modification to our application to display the entire mail body in a separate window. Define another Window to display the details of the Mail body along with a close button.
<Grid>
<ScrollViewer>
<TextBlock x:Name="mailData" Padding="20,0,0,0">
</TextBlock>
</ScrollViewer>
<Button Content="Close" HorizontalAlignment="Left" Height="24" Margin="657,536,0,0" VerticalAlignment="Top" Width="90" x:Name="btnClose" Click="btnClose_Click"/>
</Grid>
When the window loads, set the mailData textblock with the data from Utilities class. Utilities class will be a static class defined to pass data between two windows and for defining static methods. MailBody property of Utilities class will be set from the main window.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
mailData.Text = Utilities.MailBody;
}
Now, let us mke some changes to our main window to invoke the new window with selected email details. Add one template column to the datagrid with a button.
<DataGridTemplateColumn Width="60" Header="View">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Click="ShowDetails">Details</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Define the ShowDetails method in the code behind of main window. ShowDetails will set the MailBody property of the Utilities class with selected email’s body and invokes the MailDetails window as a dialog.
void ShowDetails(object sender, RoutedEventArgs e)
{
if(mailGrid.SelectedItems.Count>0)
{
System.Data.DataRowView dataView = (System.Data.DataRowView)mailGrid.SelectedItems[0];
DataRow data = dataView.Row;
Utilities.MailBody = data["Body"].ToString();
MailDetails obj = new MailDetails();
obj.ShowDialog();
}
}
Run the project and specify Lotus Notes’ database and password to retrieve email details. Following screen shows the main window with loaded Inbox content.
As you observe, apart from what we discussed, we added few filters on the top of the Inbox view to filter the records or search the records based on sender and date. Filter implementation is explained in the following code snippet.
private void btnFilter_Click(object sender, RoutedEventArgs e)
{
string query = string.Empty;
if (senderName.SelectedIndex > -1)
query += " From ='" + senderName.SelectedValue.ToString() + "'";
if(_operator.SelectedIndex>-1)
{
if (senderName.SelectedIndex > -1)
query += " AND ";
query += " Date " + _operator.Text + " '" + sentDate.SelectedDate.ToString() + "'";
}
if(!string.IsNullOrEmpty(query))
{
var filteredData = dtmails.Select(query);
mailGrid.DataContext = null;
if(filteredData.Count()>0)
mailGrid.DataContext = filteredData.CopyToDataTable();
}
}
Create Work Item in TFS
Emails from Lotus Notes will be retrieved and displayed in the application with search options. Now, let us connect to TFS project and create work items based on selected emails in the application.
Assumption: - User should have access to TFS.
Design WPF Screen
Design the UI to capture the TFS server information for connection. Once the user specifies the TFS server details, display the associates’ project collection, projects and work items for user to select.
User should provide the TFS server details and click the Reload button to load the Project Collection details. When a project collection selection changes, system will query TFS to get corresponding Project information. Depending on Project selection, work item names will be populated.
Utilities Methods for TFS
Define the Utility methods to retrieve Project Collection details from TFS. GetProjectCollection method will connect to the specified TFS server and will retrieve the Project collection details. This will get the Project collections where the user have access to.
Get an instance of TfsConfigurationServer by passing the TFS server details to GetConfigurationServer method of TfsConfigurationServerFactory. Get the Team project collection catalog from the TfsConfigurationServer. From catalog retrieve the Team Project Collection Name.
public static List<string> GetProjectCollection(string tfsServerName)
{
List<string> projCollection = new List<string>();
Uri tfsUri = new Uri(tfsServerName);
TfsConfigurationServer configurationServer =
TfsConfigurationServerFactory.GetConfigurationServer(tfsUri);
configurationServer.EnsureAuthenticated();
ReadOnlyCollection<CatalogNode> collectionNodes =
configurationServer.CatalogNode.QueryChildren(
new[] { CatalogResourceTypes.ProjectCollection },
false, CatalogQueryOptions.None);
foreach (CatalogNode collectionNode in collectionNodes)
{
Guid collectionId = new Guid(collectionNode.Resource.Properties["InstanceId"]);
TfsTeamProjectCollection teamProjectCollection =
configurationServer.GetTeamProjectCollection(collectionId);
string colName = teamProjectCollection.Name;
if (colName.IndexOf("\\") > -1)
colName = colName.Substring(colName.IndexOf("\\") + 1);
projCollection.Add(colName);
}
return projCollection;
}
Now, define another utility method to retrieve the list of Team projects under a specified team project collection. Pass the Project collection Uri to GetTeamProjectCollection method of TfsTeamProjectCollectionFactory. Retrieve the Team Project catalog from the team project collection object.
public static List<string> GetProjects(string tfsServerName, string projectCollName)
{
List<string> projects = new List<string>();
Uri collectionUri = new Uri(tfsServerName + "\\" + projectCollName);
var TeamProjectCollection =
TfsTeamProjectCollectionFactory.GetTeamProjectCollection(collectionUri);
ReadOnlyCollection<CatalogNode> projectNodes =
TeamProjectCollection.CatalogNode.QueryChildren(
new[] { CatalogResourceTypes.TeamProject },
false, CatalogQueryOptions.None);
foreach (CatalogNode projectNode in projectNodes)
{
projects.Add(projectNode.Resource.DisplayName);
}
return projects;
}
Define another Utility method to retrieve the work items defined under a selected Team project. Get the Team project collection by passing the project collection Uri. From team project collection object, get the WorkItemStore service, which in turn contains the projects under the specified team project collection. From the project, retrieve the work item types.
public static List<string> GetWorkItemTypes(string projectName, string tfsServerName, string projectCollName)
{
List<string> workItems = new List<string>();
Uri collectionUri = new Uri(tfsServerName + "\\" + projectCollName);
TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(collectionUri);
WorkItemStore workItemStore = tpc.GetService<WorkItemStore>();
Project teamProject = workItemStore.Projects[projectName];
var workItemTypes = teamProject.WorkItemTypes;
foreach (WorkItemType wt in workItemTypes)
{
workItems.Add(wt.Name);
}
return workItems;
}
Binding TFS details to WPF controls
Bind the Project collection drop-down
projectCollection.DataContext = Utilities.GetProjectCollection(tfsServer.Text);
Bind the Project drop-down.
projects.DataContext = Utilities.GetProjects(tfsServer.Text, projectCollection.SelectedValue.ToString());
Bind the work item details on change of project
workItems.DataContext = Utilities.GetWorkItemTypes(projects.SelectedValue.ToString(), tfsServer.Text, projectCollection.SelectedValue.ToString());
Run and Verify the Application
Run the application and provide the Lotus Notes database name and password. Invoke the Inbox loading using the Get Emails button. Select one or many emails by holding the Shift key for creating the work items in TFS.
Specify the TFS server name and click the Reload button to load the project collection details. Select one of the Team project collections, which loads the projects under the selected project collection. Select one of the projects from Project drop-down list to load the corresponding work item types.
Select the type of work item that needs to be created and click Create Work Item button to create the work item under the selected project in TFS.
After the completion of work item creation, it will display the pop-up window displaying the number of work items created.
Verify the newly created work items in TFS to confirm the completion of work item creation from an email from Lotus Notes.
Phase 2: Lotus Notes Plugin
Above solution can be modified as a .NET service with different data formats like JSON, XML and SOAP. Java-based Lotus Notes plugin can invoke the service to push the email details directly to TFS. Following diagram depicts the final expected integration of Lotus Notes with TFS.
Java experts from the community can think of implementing phase 2 of this solution for seamless integration of Lotus Note with TFS.