in Desenvolvimento

Criando uma aplicação que receba os alertas do Team Foundation 2012

O Team Foundation Server 2012 tem diversos recursos novos, desde possibilidade de escolha entre o source control (TFS ou Git), até o novo portal, totalmente refeito, e com excelente usabilidade. Um dos recursos que já existia, mais teve uma excelente melhoria, foram os alertas no Team Foundation. Com eles é possível configurar alertas para work items, code reviews, checking (source control files) e builds. Criando os alertas O gerenciamento dos alertas é feito em um Team Project no TFS Web Acess utilizando o menu do usuário, podemos acessar parte avançada de gerenciamento dos Alertas: image image Na parte de gerenciamento avançado é possível criar e configurar diferentes alertas e regras: image   Existem diversos tipos de alertas que podem ser customizados, como por exemplo: ser avisado de um checkin em um branch, ou em um determinado arquivo, estas são as principais situações onde se é possível configurar um alerta: Code Review image Build image Checkin image Work Item image Existem 3 diferentes formatos de entrega desse alerta, Html, Plain Text e SOAP. image As versões Html e Plain Text são entregues via SMTP para o email do usuário que está configurado no perfil em questão. Já a versão SOAP é entregue via uma requisição SOAP feita para um WebServer que está preparado e esperando receber este alerta. Quando escolhemos este formato (SOAP) é possível configurar um endereço http que está esperando a notificação do alerta. image Neste post vamos criar uma aplicação (WCF + Hosting) que receba as notificações do TFS, trate e as armazene. Começando: Para começar nosso projeto, vamos criar um console application. image Após criar o projeto, é necessário referenciar o assembly do System.ServiceModel: imageAgora podemos começar a desenvolver nosso serviço que receberá as notificações do Team Foundation Server 2012, para isso, vou criar a interface do nosso serviço:

using System.ServiceModel;

namespace TFSServiceAlerts
{
    [ServiceContract(Namespace = "http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03")]
    interface IEventServiceListen
    {
        [OperationContract(Action = "http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03/Notify")]
        [XmlSerializerFormat(Style = OperationFormatStyle.Document)]
        void Notify(string eventXml, string tfsIdentityXml);
    }
}

Com essa interface nos podemos criar a classe que implementará o serviço que receberá as notificações do TFS:

using System;

namespace TFSServiceAlerts
{
    public class EventServiceListen:IEventServiceListen
    {
        public void Notify(string eventXml, string tfsIdentityXml)
        {
            throw new NotImplementedException();
        }
    }
}

Nosso próximo passo é criar o arquivo app.config, é nele que colocaremos as propriedades do serviço que essa aplicação irá hospedar. Para isso deixaremos o arquivo App.config da seguinte maneira:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding name="TfsService">
          <security mode="None" />
        </binding>
      </wsHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="TfsServiceBehavior">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <services>
      <service behaviorConfiguration="TfsServiceBehavior" name="TFSServiceAlerts.EventServiceListen">
        <endpoint address="" binding="wsHttpBinding" bindingConfiguration="TfsService"
          contract="TFSServiceAlerts.IEventServiceListen" />
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8731/TfsService" />
          </baseAddresses>
        </host>
      </service>
    </services>
  </system.serviceModel>
</configuration>

Feito isso, já podemos hospedar nosso serviço, para isso, vamos alterar a classe Program.cs, utilizando o um ServiceHost, deixado ela da seguinte maneira:

using System;
using System.ServiceModel;

namespace TFSServiceAlerts
{
    class Program
    {
        static void Main(string[] args)
        {
            using (var host = new ServiceHost(typeof(EventServiceListen)))
            {
                host.Open();
                Console.WriteLine("Serviço iniciado");
                Console.ReadLine();
            }
        }
    }
}

Com essa estrutura pronta, já podemos configurar um alerta do formato SOAP, e direcionar o endereço para o endpoint que configuramos no serviço. Nossa aplicação está pronta para receber as notificações. Claro que ainda não temos nenhuma implementação para tratar os alertas, para isso, vamos criar duas classes que servirão para tratar o xml da requisição, que chega como o parametro: eventXml. CheckinEvent.cs

using System.Collections.Generic;
using System.Linq;
using System.Xml.Serialization;

namespace TFSServiceAlerts
{
    [XmlRoot]
    public class CheckinEvent
    {
        public bool AllChangesIncluded { get; set; }
        public string Subscriber { get; set; }
        public string Owner { get; set; }
        public string OwnerDisplay { get; set; }
        public string CreationDate { get; set; }
        public string Comment { get; set; }
        public string TimeZone { get; set; }
        public string TimeZoneOffset { get; set; }
        public string TeamProject { get; set; }
        public string PolicyOverrideComment { get; set; }
        public string Title { get; set; }
        public string ContentTitle { get; set; }
        public string Committer { get; set; }
        public string CommitterDisplay { get; set; }
        public int Number { get; set; }

        [XmlArray]
        [XmlArrayItem("CheckinNote", typeof(NameValuePair))]
        public List<NameValuePair> CheckinNotes { get; set; }

        [XmlArray]
        [XmlArrayItem("Artifact", typeof(ClientArtifact))]
        public List<ClientArtifact> Artifacts { get; set; }

    }

    public class NameValuePair
    {
        [XmlAttribute("name")]
        public string Name { get; set; }

        [XmlAttribute("val")]
        public string Value { get; set; }
    }

    public class ClientArtifact
    {
        public string Url { get; set; }

        [XmlAttribute]
        public string ArtifactType { get; set; }

        [XmlAttribute]
        public string ServerItem { get; set; }

        [XmlAttribute]
        public string Item { get; set; }

        [XmlAttribute]
        public string Folder { get; set; }

        [XmlAttribute]
        public string TeamProject { get; set; }

        [XmlAttribute]
        public string ItemRevision { get; set; }

        [XmlAttribute]
        public string ChangeType { get; set; }
    }
}

A outra classe que criaremos servirá para deserializar os eventos de Build. BuildCompletionEvent.cs

using System.Xml.Serialization;

namespace TFSServiceAlerts
{
    [XmlRoot]
    public class BuildCompletionEvent
    {
        public string TeamFoundationServerUrl { get; set; }
        public string TeamProject { get; set; }
        public string Id { get; set; }
        public string Url { get; set; }
        public string Title { get; set; }
        public string CompletionStatus { get; set; }
        public string Subscriber { get; set; }
        public string Configuration { get; set; }
        public string RequestedBy { get; set; }
        public string TimeZone { get; set; }
        public string TimeZoneOffset { get; set; }
        public string BuildStartTime { get; set; }
        public string BuildCompleteTime { get; set; }
        public string BuildMachine { get; set; }
    }
}

Com estas duas classes já podemos deserializar o xml que vem como parâmetro de nossa requisição, deixando o serviço responsável por receber as notificações da seguinte maneira:

using System;
using System.IO;
using System.Xml.Linq;
using System.Xml.Serialization;

namespace TFSServiceAlerts
{
    public class EventServiceListen:IEventServiceListen
    {
        public void Notify(string eventXml, string tfsIdentityXml)
        {
            var xml = XElement.Parse(eventXml);

            switch (xml.Name.LocalName)
            {
                case "CheckinEvent":
                    var checkinEvent = DeserializeXmlToType<CheckinEvent>(eventXml);
                    Console.WriteLine("Checkin");
                    Console.WriteLine("Autor:{0}",checkinEvent.Committer);
                    Console.WriteLine("Comments:{0}",checkinEvent.Comment);
                    Console.WriteLine("");
                    //TODO: persistir evento
                    break;
                case "BuildCompletionEvent":
                    var buildEvent = DeserializeXmlToType<BuildCompletionEvent>(eventXml);
                    Console.WriteLine("Build");
                    Console.WriteLine("Title:{0}",buildEvent.Title);
                    Console.WriteLine("Status:{0}",buildEvent.CompletionStatus);
                    Console.WriteLine("");
                    //TODO: persistir evento
                    break;
                default:
                    throw new NotSupportedException("Evento não suportado");
            }
        }

        private static T DeserializeXmlToType<T>(string eventXml) where T : class
        {
            var serializer = new XmlSerializer(typeof(T));

            using (var reader = new StringReader(eventXml))
            {
                return serializer.Deserialize(reader) as T;
            }
        }
    }
}

Agora já podemos executar o serviço, e configurar um alerta para o endereço que configuramos no nosso app.config, neste caso eu executei essa aplicação dentro do próprio servidor do Team Foundation, e configurei o alerta para o seguinte endereço: http://localhost:8731/TfsService, claro que o ideal é expor e hospedar esse serviço no IIS. image image Com alguns checkins de teste, já é possível ver os alertas sendo recebido por nossa aplicação e mostrado no console. image O código fonte do projeto está disponível no meu GitHub: https://github.com/rodolfofadino/TFSServiceAlerts Espero que este post seja útil, estou a disposição para duvidas, criticas e sugestões, abs Rodolfo