Makale Özeti

Bu makalemde sizlerle Team Foundation Server olay bildirim API'sini kullanarak nasıl kolaylıkla geçersiz olay bildirim kayıtlarını silebileceğinizi paylaşıyorum.

Makale

   Üyesi olduğum ve kaynak kodlarını Team Foundation Server üzerinde tuttuğumuz projelerde yapılan check-in’leri takip ederek ekibi olası yanlışlıklara karşı uyarmak sevdiğim bir huyumdur. Benim gibi diğer ekip üyelerinin de bu şekilde hareket etmesi bir iç kontrol mekanizması oluşturarak daha kaliteli kod çıkmasına da yardımcı olacaktır.

   Bunun belkide tek dez avantajı, ekip üyelerinden birisi ayrıldığında ortaya çıkacaktır. Bu durumda ayrılan ekip üyesi olay bildirimlerini temizlememiş ise, sistemde gereksiz yere atılan bir dolu mail olacak ve yüksek sirkalasyona sahip kurumlarda bu durumda büyük problem oluşturacaktır.

   Bu konuda sıkıntı yaşayanlar için aşağıda ekran görüntüsünü gördüğünüz küçük bir program yazdım, TFS Olay Bildirim Temizleme.

TFS Olay Bildirim Temizleme

   Uygulama, temelde olay bildirim üyeleri ile bu üyelerin izledikleri olayları bularak kullanıcıya göstermekte. Kullanıcı isterse olay bildirim üyeliklerini seçerek “Seçilenler için bildirimleri kaldır” butonu  yardımıyla bildirimlerine son verebilmekte.

   Bonus olarak; bildirim üyelerinin geçerli kullanıcılar olup olmadığını (domain içerisinde kayıtlı olup olmadığını) kontrol ederek, geçersiz kullanıcıların başına !- ekleyerek farkedilmelerini sağlıyorum.

   İsterseniz bu uygulamayı birlikte adım adım inceleyelim;

   İşe TFS sunucunuzu adresini öğrenerek aşağıdaki ile bağlanarak başlamalıyız;

var uri = new Uri("http://TFS_SUNUCU_ADRESI");
sunucu = new TeamFoundationServer(uri.AbsoluteUri, new UICredentialsProvider());
sunucu.EnsureAuthenticated();

   İlk satır ile tfs sunucumuzun adresini belirtip ardındanda bir sunucu nesnesi oluşturuyoruz. Sunucuyu oluştururken verdiğimiz ikinci parametre (UICredentialsProvider) bir domain’e dahil olmayan sistemlerde kullanıcı bilgilerinin arayüz üzerinden sorulması amacıyla verilmiştir. son satırda çağrılar EnsureAuthenticated fonksiyonu ise bilgilerin doğrulanması/ hatalı bilgi verilmesi durumunda ise yeniden sorulmasını sağlayacaktır. Burada düşmem gereken bir not; sunucu nesnesi sınıf içerisinde tanımlandığı için yukarıdaki satırlarda bir tanımlama yer almamaktadır, bu kodu doğrudan kullanırsanız öncelikle bu tanımlamayı yapmalısınız. Aksi durumda hata mesajı alacaksınızdır.

   Alternatif olarak aşağıdaki şekillerde de TFS sunucuna bağlantı kurabilirsiniz;

  • Bu örnekte sisteme giriş yaptığınız kullanıcı bilgileri kullanılarak TFS yetkilendirilmesi yapılmakta;
    var uri = new Uri("http://TFS_SUNUCU_ADRESI");
    sunucu = TeamFoundationServerFactory.GetServer(uri.AbsoluteUri);
    sunucu.EnsureAuthenticated();
    
  • Bu örnekte ise, TFS yetkilendirmesinde kullanılacak olan kullanıcı bilgileri programsal olarak verilmekte;
    var networkCredential = new NetworkCredential("fatih", "ŞİFRENİZ", "enterprisecoding.com");
    
    var uri = new Uri("http://TFS_SUNUCU_ADRESI");
    sunucu = new TeamFoundationServer(uri.AbsoluteUri, networkCredential);
    sunucu.EnsureAuthenticated();
    

  TFS bağlantısını başarılı şekilde sağladıktan sonra sıra da kayıtlı olay bildirimlerini listelemede. Formun açılışında kullanıcıyı bekletmemek adına olay bildirim listesinin çekilmesi için forma bir buton ekleyerek tıklanması durumunda listemeyi uygun gördüm. TFS api’siyle gelen Olay Hizmeti (IEventService, Microsoft.TeamFoundation.Client.dll) kayıtlı bildirimleri listemek amacıyla bizlere EventSubscriptions fonksiyonunu sunmaktadır. Parametre olarak sorgulanmak istenen kullanıcının id’sini (SID) isteyen fonksiyon, “*” parametresi kullanılırsa bizlere tüm girdileri sunacaktır. TFS olay bildirim hizmetine ulaşmak ve EventSubscriptions fonksiyonunu çağırmak için gerekli kod parçacığını aşağıda bulabilirsiniz;

var olayHizmeti = sunucu.GetService<IEventService>();
var uyelikler = olayHizmeti.EventSubscriptions("*");

   Kod parçacığıyla kayıtlı tüm bildirim üyelikleri hizmet üzerinden çekilerek uyelikler değişkeninde saklanmaktadır. Subscription (Microsoft.TeamFoundation.Framework.Client) listesi olan bu değişkenin döngü içerisinde dönülmesiyle kayıt her bir olay bildirim üyeliğine ulaşılabilir. Subscription sınıfı içerisinde olay türü, bildirim üyelik id’si, koşul ve bildirimin iletilme şekli gibi bilgiler yanında, kaydeden kullanıcının SID’sinin tutulduğu Subscriber özelliğine de sahiptir. Aşağıdaki kod parçacığıyla kayıtlı olay bildirimleri tek tek dönülerek kaydeden kullanıcının SID’si alınmaktadır;

foreach (var uyelik in uyelikler) {
    var kullaniciSID = uyelik.Subscriber;
}

   Peki geldiğimiz noktada elimizde bulunan SID bilgisi ne işimize yarayacak? Elimizdeki SID’yi kullanıcıya göstermek tek başına bir şey ifade etmeyecektir. Uygulamayı kullanacak olan kullanıcılar SID’ler yerine kullanıcı isimlerini görmeyi isteyeceklerdir. Şanslıyız ki TFS api’siyle gelen Grup Güvenlik Hizmetiyle (IGroupSecurityService, Microsoft.TeamFoundation.dll) SID’den kullanıcı kimlik bilgilerine ulaşmamızı sağlayacak olan ReadIdentity fonksiyonunu sunmakta. Aşağıdaki kod parçacığı ile bu servise ulaşarak SID’si üzerinden sorgulama yapılabilir;

var grupGuvenlikHizmeti = sunucu.GetService<IGroupSecurityService>();

var kimlik = grupGuvenlikHizmeti.ReadIdentity(SearchFactor.Sid, kullaniciSID, QueryMembership.None);

   Bu noktada düşülmesi gereken önemli bir not var; Olay Hizmetince iletilen üyelikler her zaman için kullanıcı hesapları olmayabilir, hatta kimi zaman bu SID’ler için sisteminizde bir kayıt dahi bulamayailirsiniz (kullanıcı hesabının sistemden silinmesi durumunda). Bu sebeple aşağıdaki kod parçacığında da görebileceğiniz şekilde öncelikle gelen kimlik bilgisinin boş olmadığını ve bir Windows kullanıcısı olduğunu kontrol etmelisiniz.

if (kimlik != null && kimlik.Type == IdentityType.WindowsUser) {
  //kullanıcıyı arayüzde göstermeye yönelik kodlar buraya eklenecek
}

   Yönetiminin daha kolay olması için tasarımımda aşağıdaki şekilde basit bir Kullanıcı sınıfı oluşturarak kullanıcının tüm kayıtlı olay bildirimlerini bu nesne örneği altında topladım;

internal sealed class Kullanici {
    private List<int> olayIdListesi;

    public string Adi { get; private set; }
    public int[] OlayIdListesi { get{ return olayIdListesi.ToArray();} }

    public Kullanici(string adi) { 
        Adi = adi;
        olayIdListesi = new List<int>();
    }

    public void OlayEkle(int id) {
        olayIdListesi.Add(id);
    }

    public override string ToString() {
        return Adi;
    }
}

   Aşağıdaki kod parçacığında Kullanıcı sınıfı ve bir sözlük kullanılarak olay bildirimlerinin bir araya nasıl toplandığını görebilirsiniz;

if (!olayKullanicilari.ContainsKey(kimlik.AccountName)) {
    var kullanici = new Kullanici(kimlik.AccountName);
    olayKullanicilari.Add(kimlik.AccountName, kullanici);

    bildirimUyeListesi.Items.Add(kullanici);
}

olayKullanicilari[kimlik.AccountName].OlayEkle(uyelik.ID);

   Verilen SID için bir kimlik bilgisi bulunamaması durumunda da kullanıcılar bilgilendirilerek, gerekiyorsa olay bildiriminin kaldırılması sağlanabilir. Aşağıdaki kod bu amaçla kullanılmaktadır;

var anahtar = "!-" + uyelik.DeliveryPreference.Address;

if (!olayKullanicilari.ContainsKey(anahtar)) {
    var kullanici = new Kullanici(anahtar);
    olayKullanicilari.Add(anahtar, kullanici);

    bildirimUyeListesi.Items.Add(kullanici);
}

olayKullanicilari[anahtar].OlayEkle(uyelik.ID);

   Arayüz üzerinden bildirimleri kaldırılacak olan kullanıcıların seçilmesi sonrası “Seçilenler için bildirimleri kaldır” butonuna basılmasıyla Olay Hizmeti içerisinde yer alan UnsubscribeEvent fonksiyonu yardımıyla seçili kullanıcıya ait tüm bildirim kayıtları sırayla silinebilir. Bu işleme ait kod parçacığı ise şu şekilde olacaktır;

var seciliKullanicilar = bildirimUyeListesi.CheckedItems;
var eventService = sunucu.GetService<IEventService>();

foreach (Kullanici seciliKullanici in seciliKullanicilar) {
    foreach (var olayId in seciliKullanici.OlayIdListesi) {
        eventService.UnsubscribeEvent(olayId);
    }
}

   Bu adımlar sonrası kayıt olay bildirimlerini listeyebilir, seçili kullanıcılar için olay bildirimlerini silebilirsiniz. Aşağıda şimdiye kadar sizlerle paylaşmış olduğum kod parçacıklarını bütün olarak bulabilirsiniz;

public partial class AnaEkran : Form {
    private TeamFoundationServer sunucu;

    public AnaEkran() {
        InitializeComponent();

        var uri = new Uri("http://TFS_SUNUCU_ADRESI");

        sunucu = new TeamFoundationServer(uri.AbsoluteUri, new UICredentialsProvider());

        sunucu.EnsureAuthenticated();
    }

    private void listele_Click(object sender, EventArgs e) {
        bildirimUyeListesi.Items.Clear();

        var olayHizmeti = sunucu.GetService<IEventService>();
        var grupGuvenlikHizmeti = sunucu.GetService<IGroupSecurityService>();

        var uyelikler = olayHizmeti.EventSubscriptions("*");
        var olayKullanicilari = new Dictionary<string, Kullanici>();

        foreach (var uyelik in uyelikler) {
            var kullaniciSID = uyelik.Subscriber;

            var kimlik = grupGuvenlikHizmeti.ReadIdentity(SearchFactor.Sid, kullaniciSID, QueryMembership.None);

            if (kimlik != null && kimlik.Type == IdentityType.WindowsUser) {
                if (!olayKullanicilari.ContainsKey(kimlik.AccountName)) {
                    var kullanici = new Kullanici(kimlik.AccountName);
                    olayKullanicilari.Add(kimlik.AccountName, kullanici);

                    bildirimUyeListesi.Items.Add(kullanici);
                }

                olayKullanicilari[kimlik.AccountName].OlayEkle(uyelik.ID);
            }
            else if (kimlik == null) {
                var anahtar = "!-" + uyelik.DeliveryPreference.Address;

                if (!olayKullanicilari.ContainsKey(anahtar)) {
                    var kullanici = new Kullanici(anahtar);
                    olayKullanicilari.Add(anahtar, kullanici);

                    bildirimUyeListesi.Items.Add(kullanici);
                }

                olayKullanicilari[anahtar].OlayEkle(uyelik.ID);
            }
        }
    }

    private void secilileriKaldir_Click(object sender, EventArgs e) {
        var seciliKullanicilar = bildirimUyeListesi.CheckedItems;
        var eventService = sunucu.GetService<IEventService>();

        foreach (Kullanici seciliKullanici in seciliKullanicilar) {
            foreach (var olayId in seciliKullanici.OlayIdListesi) {
                eventService.UnsubscribeEvent(olayId);
            }
        }
    }
}

internal sealed class Kullanici {
    private List<int> olayIdListesi;

    public string Adi { get; private set; }
    public int[] OlayIdListesi { get { return olayIdListesi.ToArray(); } }

    public Kullanici(string adi) {
        Adi = adi;
        olayIdListesi = new List<int>();
    }

    public void OlayEkle(int id) {
        olayIdListesi.Add(id);
    }

    public override string ToString() {
        return Adi;
    }
}

Fatih Boy

http://www.enterprisecoding.com
http://twitter.com/fatihboy