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:
- Codificando Eventos de Work Items no TFS – Entendendo a API
- Codificando Eventos de Work Items no TFS – Alterando o status de work items linkados
- Codificando Eventos de Work Items no TFS – TFS Event Workflows
- Codificando Eventos de Work Items no TFS – Sumarizando o esforço com o TFS Event Workflow
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?
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