Leer elementos de Team Foundation Server desde Microsoft Dynamics AX 2012 (ALM-V)

Vuelvo brevemente sobre una de mis series de post mas largas hasta la fecha, lo referente a la integración de AX 2012 con Team Foundation Server, no sólo para la gestión del código fuente, sino también como herramienta de gestión del trabajo del equipo. En capítulos anteriores ya vimos cómo se instala y configura TFS (esto cambia entre versiones, también vimos que no necesitamos instalar nada si utilizamos la versión en la nube) y cómo podíamos crear elementos de trabajo en TFS (WorkItems) para gestionar el trabajo desde esta herramienta.

Al hacer un check-in de código desde Microsoft Dynamics AX 2012 se nos plantea la posibilidad de asociar ese check-in a algunos elementos de trabajo de TFS. Esto es casi obligatorio para poder llevar un buen control de para qué se realiza cada cambio, pero la herramienta que nos ofrece AX es bastante limitada. Sin embargo, gracias al API de TFS, podemos ampliarla e incluso desarrollar nuestras propias herramientas e informes en AX 2012 obteniendo datos del servidor de TFS (sí, también desde Visual Studio Online).

Una funcionalidad muy interesante para ahorrarnos tiempo de desarrollo es poder ejecutar desde AX 2012 consultas que están guardadas y utilizamos en TFS. Esto nos permite tener las mismas vistas de datos en los dos entornos, lo que facilita el trabajo. Profundizaré sobre esto en los siguientes post sobre este tema, pero ahí va un código de ejemplo para realizar esta tarea.

NOTA: Para que estos ejemplos funcionen, es necesario tener instalado el SDK de Visual Studio de la versión de TFS que estemos utilizando. En mi caso es el SDK de la versión 2013 porque estoy utilizando Visual Studio Online, pero esto puede variar en cada instalación. Se puede descargar del siguiente enlace:

Después de instalar el SDK, hay que añadir las referencias necesarias al AOT para que AX 2012 pueda utilizarlas:

Add references to AOT

Ahora si, el código:

static void JAEE_TFSPROXY_EjecutarQueryTFS(Args _args)
{
    System.Uri tfsUri = new System.Uri("https:///DefaultCollection");
    str tfsProject = "Libro AX2012";
    str tfsFolder = "My Queries";
    str tfsQuery = "Todas Features";

    Microsoft.TeamFoundation.Client.TfsTeamProjectCollection teamProjectCollection;
    Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore workItemStore;
    Microsoft.TeamFoundation.WorkItemTracking.Client.ProjectCollection projectCollection;
    Microsoft.TeamFoundation.WorkItemTracking.Client.Project project;
    Microsoft.TeamFoundation.WorkItemTracking.Client.QueryHierarchy projectQueryHierarchy;
    Microsoft.TeamFoundation.WorkItemTracking.Client.QueryFolder queryFolder;
    Microsoft.TeamFoundation.WorkItemTracking.Client.QueryDefinition queryDefinition;
    Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemCollection workItemCollection;
    Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItem workItem;
    Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemType workItemType;

    System.Exception ex;
    System.Type workItemStoreType;
    ClrObject parameters;
    ClrObject enumerator;
    str title, state, type;

    setPrefix(strFmt("%1 > %2 > %3", tfsProject, tfsFolder, tfsQuery));

    try
    {
        // Collection
        teamProjectCollection = new Microsoft.TeamFoundation.Client.TfsTeamProjectCollection(tfsUri);
        workItemStoreType = CLRInterop::getType('Microsoft.TeamFoundation.WorkItemTracking.Client.WorkItemStore');
        workItemStore = teamProjectCollection.GetService(workItemStoreType);
        projectCollection = workItemStore.get_Projects();

        // Project
        project = projectCollection.get_Item(tfsProject);
        projectQueryHierarchy = project.get_QueryHierarchy();

        // Query
        queryFolder = projectQueryHierarchy.get_Item(tfsFolder);
        queryDefinition = queryFolder.get_Item(tfsQuery);

        // Query @Parameters
        parameters = new ClrObject("System.Collections.Generic.Dictionary`2[System.String,System.String]");
        parameters.Add('project', tfsProject);
       
        // Run Query
        workItemCollection = workItemStore.Query(queryDefinition.get_QueryText(), parameters);

        enumerator = workItemCollection.GetEnumerator();
        while (enumerator.MoveNext())
        {
            workItem = enumerator.get_Current();
            workItemType = workItem.get_Type();
            type = workItemType.get_Name();
            title = workItem.get_Title();
            state = workItem.get_State();

            info(strFmt("[%1 > %2] %3", type, state, title));
        }
    }
    catch (Exception::CLRError)
    {
        ex = ClrInterop::getLastException();
        if (ex != null)
        {
            ex = ex.get_InnerException();
            if (ex != null)
                error(ex.ToString());
        }
    }
}

El resultado es el siguiente:

Execute TFS stored query from AX

En los siguientes capítulos seguiremos hablando sobre las ventajas de utilizar TFS, cómo utilizarlo desde AX 2012, y cómo seguir mejorando nuestra herramienta de integración desde X++. Este código esta simplificado al máximo para que sea sencillo, pero de este ejemplo podemos sacar mucha información para realizar otras muchas tareas 🙂