Nessa série de posts, vou demonstrar como capturar os eventos ocorridos nos Work Items do Team Foundation Server para fazer algum tipo de customização. Esse recurso é muito útil para realizar diversos tipos de atividades no TFS, dependendo apenas de sua criatividade.

Alguns exemplos interessantes (e também úteis) podem ser:

  • Alterar os status de work items filhos de um PBI quando este for fechado;
  • Sumarizar o esforço usado em todas as tarefas filhas de um PBI;
  • Criar mecanismos de notificações avançados;
  • Atualizar sistemas externos;

Existem vários plugins que fazer esse trabalho e nos ajudam muito no trabalho pesado. Mas a ideia é começar expondo essa API e permitir que você desenvolva para suas próprias necessidades. Depois, nos próximos posts faremos o uso do TFS Event Workflows, uma excelente ferramenta que nos permite realizar esse trabalho através de workflows.

Então, acompanhe a série de posts aqui e espero que gostem:

Criando o projeto

Para começar, precisamos criar um projeto do tipo Class Library no Visual Studio e fazer referência aos seguintes assemblies:

Microsoft.TeamFoundation.Common.dll
Microsoft.TeamFoundation.Framework.Server.dll
Microsoft.TeamFoundation.Server.Core.dll
Microsoft.TeamFoundation.WorkItemTracking.Client.dll
Microsoft.TeamFoundation.WorkItemTracking.Server.dll
Microsoft.VisualStudio.Services.Common.dll
Microsoft.VisualStudio.Services.WebApi.dll

Você os encontra nas seguintes pastas:

  • C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\ReferenceAssemblies\v2.0
  • C:\Program Files (x86)\Microsoft Visual Studio 12.0\Common7\IDE\ReferenceAssemblies\v4.5
  • C:\Program Files\Microsoft Team Foundation Server 12.0\Application Tier\Web Services\bin\Plugins (esta última se encontra onde o TFS está instalado)

Agora, crie uma classe que implemente a interface Microsoft.TeamFoundation.Framework.Server.ISubscriber. É nela que a “mágica” acontece. A estrutura desta interface é essa:

    
    public interface ISubscriber
    {
        string Name { get; }
        SubscriberPriority Priority { get; }

        EventNotificationStatus ProcessEvent(TeamFoundationRequestContext requestContext, NotificationType notificationType, object notificationEventArgs, out int statusCode, out string statusMessage, out ExceptionPropertyCollection properties);
        Type[] SubscribedTypes();
    }

Você precisa implementar esses membros para que a captura dos eventos possa acontecer. Em SubscribedTypes você registra quais os eventos que vai capturar. Perceba que é um array de tipos. Portanto, todo evento que o TFS expõe pode ser capturado.

No nosso exemplo, vou capturar apenas as alterações ocorridas em Work Items:

    
public Type[] SubscribedTypes()
{
    return new[] { typeof(WorkItemChangedEvent) };
}

Na propriedade Name, definimos o nome do nosso evento:

   
public string Name
{
    get
    {
        return "EventosTfs";
    }
}

A prioridade deixamos como normal:

 
public SubscriberPriority Priority
{
    get
    {
        return SubscriberPriority.Normal;
    }
}

Fica agora por conta do método ProcessEvent efetuar a sua customização.

No exemplo abaixo, fiz um exemplo muito simples que apenas grava em um arquivo de log as alterações que ocorreram no Work Item:

using System;
using System.IO;
using System.Linq;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Common;
using Microsoft.TeamFoundation.Framework.Server;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Server;

namespace L3.FundamentosEventosTfs
{
    public class WorkItemChangedEventHandler : ISubscriber
    {
        private const string TfsUri = "http://vsalm:8080/tfs/DefaultCollection";
        private const string LogFileName = "C:\\Temp\\EventosTfs.log";

        public Type[] SubscribedTypes()
        {
            return new[] { typeof(WorkItemChangedEvent) };
        }

        public EventNotificationStatus ProcessEvent(
            TeamFoundationRequestContext requestContext,
            NotificationType notificationType,
            object notificationEventArgs,
            out int statusCode,
            out string statusMessage,
            out ExceptionPropertyCollection properties)
        {
            statusCode = 0;
            statusMessage = string.Empty;
            properties = null;

            var typeNotification = notificationEventArgs.GetType();

            if (notificationType == NotificationType.Notification && notificationEventArgs is WorkItemChangedEvent)
            {
                var ev = notificationEventArgs as WorkItemChangedEvent;
                var id = ev.CoreFields.IntegerFields[0].NewValue;

                var tfs = new TfsTeamProjectCollection(new Uri(TfsUri));
                var store = (WorkItemStore)tfs.GetService(typeof(WorkItemStore));
                var workItem = store.GetWorkItem(id);

                using (var logFile = File.CreateText(LogFileName))
                {
                    logFile.WriteLine("{0} [{1:00000}] {2}", DateTime.Now, workItem.Id, workItem.Title);

                    foreach (var field in ev.ChangedFields.StringFields)
                    {
                        logFile.WriteLine("\r\n\tField    : {0}\r\n\tOld Value: {1}\r\n\tNew Value: {2}", field.Name, field.OldValue, field.NewValue);
                    }
                }
            }
            return EventNotificationStatus.ActionPermitted;
        }

        public string Name
        {
            get
            {
                return "EventosTfs";
            }
        }

        public SubscriberPriority Priority
        {
            get
            {
                return SubscriberPriority.Normal;
            }
        }
    }
}

Colocando para funcionar

Como esse nosso projeto é uma Class Library, na compilação será gerada uma dll. Para por tudo para funcionar, você deve copiar essa dll para a pasta “C:\Program Files\Microsoft Team Foundation Server 12.0\Application Tier\Web Services\bin\Plugins” no servidor TFS que ele faz o resto.

Para testar, crie ou modifique qualquer work item que o arquivo de log será gerado com as informações de modificações.

Mas só um log? Smiley triste

Sim. Esse é só o começo para se entender o funcionamento desse processo. Somente gravamos um log (desnecessário por sinal, já que o TFS faz esse trabalho), mas as possibilidades são infinitas. Depende apenas de sua criatividade.

Nos próximos posts faremos algo mais útil.

Link do Código-fonte de exemplo.

Até a próxima!

Publicado originalmente em http://do.net.br/2015/04/codificando-eventos-de-work-items-no-tfs-entendendo-a-api/

Heber Pereira