Welcome to EMC Consulting Blogs Sign in | Join | Help

Crispin Parker's Blog

About Scrum for Team System and .Net development

Scrum for Team System - Create a custom work item changed event processor.

Scrum for Team System - Create a custom work item changed event processor.

Sharp eyed and observant Scrum for Team System (SfTS) users will have noticed that the changing a SBI work remaining value affects the linked PBI work remaining value. However, it is sometimes a bit of a surprise to users that this behaviour is not part of the default TFS feature set...

The ability to automatically alter related TFS work items is a custom feature added by the Scrum for Team System process template. And I bet you didn't know that this feature is also extensible did you?

In this blog I'm going to give an example of how to create a custom "work item changed" event processor and add it to the SfTS event processors collection.

The demonstration scenario:

For the purpose of this demonstration, I want to add some new behaviour to my SfTS Team Projects. The new behaviour will propagate the description value of my SBIs to the description value of the linked Bug work items. This means that whatever description changes I make to the SBIs will be visible in the parent bug description field.

Step 1 - Setting up the new process handler project:

Note: You will need to have Team Explorer 2008 installed on your development system.

  • Create a new Visual Studio class library project, entitled "DescriptionChangedProcessor".
  • Make a local copy of the "Conchango.TeamSystem.SubscribedEventHandler.Infrastructure.dll". This assembly is found on the TFS application tier.

    The default installation location for this assembly is:
    1. C:\Inetpub\wwwroot\ScrumforTeamSystem\bin
  • Add the following project references:
    1. Microsoft.TeamFoundation.Client
    2. Microsoft.TeamFoundation.WorkItemTracking.Client
    3. Conchango.TeamSystem.SubscribedEventHandler.Infrastructure.dll

Step 2 - Creating the event handler instance class:

  • Create a new public class in the project entitled: "EventProcessor"
  • Set the new class to implement the "Conchango.TeamSystem.SubscribedEventHandler.Infrastructure .Interfaces.ITSEventProcessor" interface. This interface defines a single method: "Process"; the method accepts a single parameter: tfsEvent of type ITSEvent.
  • Implement the ITSEventProcessor interface by adding the "Process" method.

    public void Process(ITSEvent tfsEvent)
    {
    }

  • The first thing we need to do in the "Process" method is check that the received event is a work item changed event for a "Sprint Backlog Item". To do this, add two new private methods to our ["EventProcessor"] class:

    private static bool TryGetTypedEvent<T>(ITSEvent tfsEvent, out T typedEvent)
    {
      typedEvent =
        typeof(T).IsAssignableFrom(tfsEvent.EventSourceType)
          ? (T)tfsEvent.EventSource
          : default(T);

      return
    !typedEvent.Equals(default(T));
    }

    private static bool IsWorkItemOfType(
      WorkItemChangedEvent workItemChangedEvent,
      string workItemTypeName)
    {
      return
        Helpers.GetCoreFieldString(
          ConstantStrings.WorkItemTypeCoreField,
          workItemChangedEvent)
          .Equals(workItemTypeName);
    }

  • The first of the above check methods ["TryGetTypedEvent"] compares the type of the output parameter ["typedEvent"] to the type of the event source. If the types match the method returns true and sets the output parameter, otherwise the method returns false and set the out parameter to null.

    The second method ["IsWorkItemOfType"] returns a Boolean to indicate if the changed work item is an instance of the specified work item type.

  • With our check methods in place, we can now validate the ["tfsEvent"] parameter.

    public void Process(ITSEvent tfsEvent)
    {
      WorkItemChangedEvent wicEvent;
      if (!TryGetTypedEvent(tfsEvent, out wicEvent) ||
          !IsWorkItemOfType(wicEvent, "Sprint Backlog Item"))
      {
        return;
      }
    }

  • The next part of the ["Process"] method needs to determine whether the changed SBI is linked to a Bug work item. To do this we need to connect to TFS and retrieve any linked Bug work items.

    public void Process(ITSEvent tfsEvent)
    {
      WorkItemChangedEvent wicEvent;
      if (!TryGetTypedEvent(tfsEvent, out wicEvent) ||
          !IsWorkItemOfType(wicEvent, "Sprint Backlog Item"))
      {
        return;
      }

      var tfs = new TeamFoundationServer(
        tfsEvent.ServerIdentity.Url,
        CredentialCache.DefaultCredentials);

      var
    workItemStore =
        (WorkItemStore)tfs.GetService(typeof(WorkItemStore));

      var sourceWorkItemId =
        Helpers.GetCoreFieldInt(ConstantStrings.WorkItemIdCoreField, wicEvent);

      var sourceWorkItem = workItemStore.GetWorkItem(sourceWorkItemId);

      var parentBug =
        Helpers.GetRelatedWorkItems(sourceWorkItem)
          .Where(wi => wi.Type.Name.Equals("Bug"))
          .FirstOrDefault();

      if (parentBug == null)
      {
        return;
      }
    }
  • Now we have both the child and parent work items, we can update the bug to include the child description text. So add a new method to handle the update.

    private static void UpdateBugDescription(
      WorkItem parentWorkItem,
      WorkItem sourceWorkItem)
    {
      var prefix = string.Concat("<-- Linked Task: '", sourceWorkItem.Id, "': ");
      var suffix = " -->";
      var regEx = new Regex(string.Format(@"({0}[\s\S]*{1})", prefix, suffix));

      var textToInsert =
        string.Concat(
          new object[]
          {
            prefix,
            Environment.NewLine,
            sourceWorkItem.Description,
            Environment.NewLine,
            suffix
          });

      parentWorkItem.Description =
        regEx.IsMatch(parentWorkItem.Description)
          ? regEx.Replace(parentWorkItem.Description, textToInsert)
          : string.Concat(
              parentWorkItem.Description,
              Environment.NewLine,
              textToInsert,
              Environment.NewLine);
    }

    The above ["UpdateBugDescription"] example surrounds the child work item description text with some comment markers. Then checks to see if the child description text already exists; if it does exist, the previous text is replaced, otherwise the child description is appended.

  • Adding a call to the update method and saving the bug (if dirty) completes the code.

    public void Process(ITSEvent tfsEvent)
    {
      WorkItemChangedEvent wicEvent;
      if (!TryGetTypedEvent(tfsEvent, out wicEvent) ||
          !IsWorkItemOfType(wicEvent, "Sprint Backlog Item"))
      {
        return;
      }

      var tfs = new TeamFoundationServer(
        tfsEvent.ServerIdentity.Url,
        CredentialCache.DefaultCredentials);

      var
    workItemStore =
        (WorkItemStore)tfs.GetService(typeof(WorkItemStore));

      var sourceWorkItemId =
        Helpers.GetCoreFieldInt(ConstantStrings.WorkItemIdCoreField, wicEvent);

      var sourceWorkItem = workItemStore.GetWorkItem(sourceWorkItemId);

      var parentBug =
        Helpers.GetRelatedWorkItems(sourceWorkItem)
        .Where(wi => wi.Type.Name.Equals("Bug"))
        .FirstOrDefault();

      if (parentBug == null)
      {
        return;
      }

      UpdateBugDescription(parentBug, sourceWorkItem);

      if (parentBug.IsDirty)
      {
        parentWorkItem.Save();
      }
    }

Step 3 - Copy the assembly into the SfTS bin folder.

 

  • With our new event processor all ready to go, the next step is to copy an instance of our new assembly ["DescriptionChangeProcessor.dll"] to the bin folder of the Scrum for Team System web application on the TFS application tier.

    The default installation location of the "bin" folder is:
    • C:\Inetpub\wwwroot\ScrumforTeamSystem\bin

Step 4 – Update the SfTS web.config.

  • Finally, we need to tell the Scrum for Team System web application that we have a new event processor. To do this we will need to edit the web applications "web.config" file.

    The default installation location of the "web.config" file is:

  •  
  • In the configuration file, find the following xml fragment:

    <setting name="EventServiceAssembles" serializeAs="Xml">
      <
    value>
        <
    ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
          <
    string>Conchango...StateTransitions.dll</string>
          <
    string>Conchango...RefreshPBIWorkRemaining.dll</string>
        </
    ArrayOfString>
      </
    value>
    </
    setting>

  • And add a new xml string node as shown below:

    <setting name="EventServiceAssembles" serializeAs="Xml">
      <
    value>
        <
    ArrayOfString xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
          <
    string>Conchango...StateTransitions.dll</string>
          <
    string>Conchango...RefreshPBIWorkRemaining.dll</string>
          <
    string>DescriptionChangedProcessor.dll</string>
        </
    ArrayOfString>
      </
    value>
    </
    setting>
     

The source code for the demo assembly is attached below.

Disclaimers, omissions and “get out jail” cards.

  • This example is for demonstration purposes only and is not the desired default behaviour for the Scrum for Team System template.
  • Making changes to your Scrum for Team System installation configuration is done completely at your own risk.
  • The above code doesn’t handle the changing a work item links, so linking and unlinking SBIs to bugs will not updated the description text.

 

Where to go next?

Please see the www.scrumforteamsystem.com web site for more information.

Published 06 April 2009 17:55 by crispin.parker

Attachment(s): DescriptionChnagedProcessor.zip

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

No Comments

Leave a Comment

(required) 
(optional)
(required) 
Submit

This Blog

Syndication

News

Powered by Community Server (Personal Edition), by Telligent Systems