2

TFS 2010 – Capturer des évènements serveurs grâce à un plugin

Avec TFS 2010 il est facile de développer des plugins permettant d’effectuer des actions personnalisées lorsque certains évènement serveur se produisent dans TFS. Par exemple lors d’un Check-in, lorsqu’un WorkItem est modifié ou encore lorsqu’un build est terminé…

Pour cela, il suffit simplement d’écrire une assembly contenant notre code de capture d’événement serveur et de la déployer dans le répertoire Plugins de TFS 2010 situé ici : C:\Program Files\Microsoft Team Foundation Server 2010\Application Tier\Web Services\bin\Plugins

Prenons un exemple. Nous allons écrire un plugin qui au changement d’un WorkItem va déclencher l’envoi d’un mail d’alerte. Certains dirons qu’il existe déjà un système d’Alerts dans TFS, mais ici il s’agit juste d’illustrer la création d’un plugin. De plus le système de notification standard de TFS à quelques limitations.

Créer un nouveau projet de type Class Library puis renommer le fichier Class1.cs en WorkItemChangedEventHandler. Ajoutez à ce projet les références suivantes :

  • C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\ReferenceAssemblies\v2.0\Microsoft.TeamFoundation.dll
  • C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\ReferenceAssemblies\v2.0\Microsoft.TeamFoundation.Client.dll
  • C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\ReferenceAssemblies\v2.0\Microsoft.TeamFoundation.Common.dll
  • C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\ReferenceAssemblies\v2.0\Microsoft.TeamFoundation.WorkItemTracking.Client.dll
  • C:\Program Files\Microsoft Team Foundation Server 2010\Application Tier\Web Services\bin\Microsoft.TeamFoundation.Framework.Server.dll (sur le serveur TFS)
  • C:\Program Files\Microsoft Team Foundation Server 2010\Application Tier\Web Services\bin\Microsoft.TeamFoundation.Server.dll (sur le serveur TFS)
  • C:\Program Files\Microsoft Team Foundation Server 2010\Application Tier\Web Services\bin\Microsoft.TeamFoundation.VersionControl.Server.dll (sur le serveur TFS)
  • C:\Program Files\Microsoft Team Foundation Server 2010\Application Tier\Web Services\bin\Microsoft.TeamFoundation.WorkItemTracking.Server.DataAccessLayer.dll (sur le serveur TFS)

Ensuite, il faut que notre classe WorkItemChangedEventHandler implémente l’interface ISubscriber. Voici un exemple complet de code permettant l’envoi d’un mail lorsqu’un WorkItem est modifié :

using System;
using System.Text;
using System.Net.Mail;
using Microsoft.TeamFoundation.Framework.Server;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Server;
using Microsoft.TeamFoundation.Framework.Client;
using Microsoft.TeamFoundation.Framework.Common;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Server;
using System.IO;
using System.Diagnostics;
using Microsoft.TeamFoundation.Common;

namespace Itelios.TeamFoundation.EventHandler
{
    public class WorkItemChangedEventHandler : ISubscriber
    {
        public Type[] SubscribedTypes()
        {
            return new Type[1] { typeof(WorkItemChangedEvent) };
        }

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

            try
            {
                if (notificationType == NotificationType.Notification && notificationEventArgs is WorkItemChangedEvent)
                {
                    WorkItemChangedEvent ev = notificationEventArgs as WorkItemChangedEvent;
                    EventLog.WriteEntry("WorkItemChangedEventHandler", "WorkItem " + ev.WorkItemTitle + " was modified");

                    foreach (StringField field in ev.ChangedFields.StringFields)
                    {
                        if (field.Name == "Assigned To")
                        {
                            // Recover all information about project collection
                            string projectCollectionFolder = requestContext.ServiceHost.VirtualDirectory.ToString();
                            Uri projectCollectionUri = new Uri(@"http://win-gs9gmujits8:8080" + projectCollectionFolder);
                            TfsTeamProjectCollection projectCollection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(projectCollectionUri);
                            
                            // Recover all information about WorkItem 
                            WorkItemStore wiStore = projectCollection.GetService<WorkItemStore>();
                            WorkItem wItem = wiStore.GetWorkItem(ev.CoreFields.IntegerFields[0].NewValue);
                            IIdentityManagementService ims = projectCollection.GetService<IIdentityManagementService>();
                            IGroupSecurityService igs = projectCollection.GetService<IGroupSecurityService>();
                            
                            // Recover information about the assigned person
                            string assTo = wItem.Fields["Assigned To"].Value.ToString();

                            Microsoft.TeamFoundation.Framework.Client.TeamFoundationIdentity tfid =
                                ims.ReadIdentity(IdentitySearchFactor.DisplayName, assTo,
                                MembershipQuery.Direct, ReadIdentityOptions.None);

                            Identity ident = igs.ReadIdentity(SearchFactor.Sid, tfid.Descriptor.Identifier, QueryMembership.Direct);
                            assTo = ident.MailAddress;

                            // Send email
                            SmtpClient mailClient = new SmtpClient();
                            mailClient.Host = "SMTP";
                            mailClient.Port = 25;
                            mailClient.Credentials = new System.Net.NetworkCredential("LOGIN", "PASSWORD");

                            MailAddress from = new MailAddress("FROM");
                            MailAddress to = new MailAddress("TO");
                            MailMessage message = new MailMessage(from, to);
                            message.Body = "Le WorkItem " + wItem.Title + " vous a été assigné!";
                            message.BodyEncoding = Encoding.UTF8;
                            message.IsBodyHtml = true;
                            message.Subject = "Un work item vous a été assigné";
                            message.SubjectEncoding = Encoding.UTF8;

                            mailClient.Send(message);
                            break;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                EventLog.WriteEntry("WorkItemChangedEventHandler", ex.Message + " - Stack Trace: " + ex.StackTrace);
            }

            return EventNotificationStatus.ActionPermitted;
        }

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

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

Attention : Il vous faudra adapter certains éléments comme l’adresse du serveur SMTP, le Login/Password… pour rendre cet exemple fonctionnel.

Une fois compilé, il suffit de copier l’assembly dans le répertoire Plugins de TFS : C:\Program Files\Microsoft Team Foundation Server 2010\Application Tier\Web Services\bin\Plugins

Chaque mise à jour dans ce répertoire entraîne une brève interruption des services TFS, vous pouvez d’ailleurs le voir dans l’EventViewer du serveur :

clip_image001

Vous pouvez également copier vos assemblies dans un sous-répertoire pour éviter de les mélanger avec celles de TFS. Par exemple ci-dessous dans un répertoire TFSNotifier :

clip_image002

Vous pouvez, également externaliser des valeurs dans le fichier de configuration Web.config du Web service TFS ou encore dans un fichier de configuration satellite référencé dans le Web.config du Web service.

clip_image003

Voilà, vous avez écrit votre premier plugin TFS. Il existe plein d’évènements que vous pouvez capturer. Vous en trouverez la liste dans un post de Martin Hinshelwood ici :

http://blog.hinshelwood.com/tfs-event-handler-for-team-foundation-server-2010/ (en Anglais).

gbrout

Architecte, formateur MCT et expert sur la gamme Visual Studio ALM qu'il met en œuvre sur l'ensemble des projets. Il travaille pour la société Itelios, spécialisée dans les technologies Microsoft et le commerce connecté. Il accompagne quotidiennement de nombreuses équipes et projets dans différents domaines et technologies : Windows 8, Windows Phone, ASP.NET MVC, Dynamics CRM... Passionné par le développement, ses domaines de prédilections sont avant tout l'expertise technique, l'industrialisation des développements avec la gamme Visual Studio ALM, l'analyse des performances, les tests et tout ce qui a trait à la qualité. Son expertise sur les  technologies Microsoft sa passion pour les nouvelles technologies et les développements novateurs l'a conduit à l'écriture d'un livre accessible et opérationnel sur le développement pour Windows 8 à l'aide d'HTML5 et JavaScript. Il anime également des conférences et sessions techniques telles que des live meeting en ligne ou lors d’événements comme les Techdays.

2 Commentaires

  1. bjr

    excellent article !
    Une question relative à l’accès au journal d’évènements de Windows.
    Comment élever les privilèges d’utilisateur standard à administrateur dans le code de l’évènement?

    merci

  2. Bonjour,

    Le plus simple pour ne pas avoir besoin d’élever les privilèges pour accéder au journal d’événements Windows, c’est que le compte de service TFS possède les droits nécéssaire pour écrire dans le journal d’événements. Ce qui est normalement le cas.

    Dans le cas contraire et si vous obtenez une exception de type « Access Denied » ou autre, il vous faudra alors passer par de l’impersonnation, pour en effet élever les privilèges à administrateur dans le code de l’événement. Vous pouvez alors vous tournez vers WIF pour Windows Identity Foundation : http://msdn.microsoft.com/fr-fr/evalcenter/dd440951

    On utilise pour cela les mécanismes d’impersonnation. Plus d’information sur la MSDN ici : http://msdn.microsoft.com/fr-fr/library/e599ywa6.aspx.

    Il suffit de créer un objet WindowsIdentity et d’utiliser la méthode Impersonate.

    Vous avez aussi des Webcast pour comprendre ce mécanisme : http://www.microsoft.com/france/vision/mstechdays10/Webcast.aspx?EID=6eee2b91-b02a-49d6-8987-1bd5d91b8560

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *