ofbiz_jira

JIRA Plugins: Modelo de datos

En artículos anteriores hemos detallado cómo crear plugins para Atlassian JIRA extendiendo la funcionalidad de búsqueda mediante funciones JQL. En esta nueva entrada mostramos como utilizar OfBiz para acceder al modelo de datos de JIRA. Para ello crearemos una nueva función JQL que muestre cómo extender las búsquedas para buscar en cualquier tabla del modelo de datos.

¿Qué es Ofbiz?

Apache Ofbiz (The Apache Open For Business Project) es un proyecto de Apache definido como de automatización corporativa. En la práctica es una aplicación que contiene la mayoría de herramientas corporativas: ERP, CRM, Punto de venta, etc. Os preguntareis que tiene que ver JIRA con todo esto. Pues que JIRA ha utilizado históricamente OfBiz como capa de abstracción sobre los diferentes servidores de BBDD que retornan filas de una tabla o vista como un mapa de campos (en forma de pares etiqueta-valor) de la clase GenericValue. De esta forma el esquema de BBDD puede cambiar en el tiempo sin afectar a la compatibilidad de las queries sobre ofbiz.

La correspondencia entre el modelo lógico y el esquema de la BBDD se realiza mediante la definición de Entidades (tablas) que contienen un conjunto de Campos (columnas) con un tipo de datos conocido. Las queries sobre el modelo de datos se basan en entidades y campos y Ofbiz se encarga de convertir la consulta en una query contra el motor de BBDD correspondiente. Las definiciones de entidades se encuentran en un subdirectorio llamado entitydefs. En concreto el fichero entitymodel.xml contiene todas las entidades accesibles desde Ofbiz. A continuación se muestra un extracto de este archivo:

    <entity entity-name="Issue" table-name="jiraissue" package-name="">
        <field name="id" type="numeric"/>

        <field name="key" col-name="pkey" type="long-varchar"/>

        <field name="project" type="numeric"/>

        <field name="reporter" type="long-varchar"/>
        <field name="assignee" type="long-varchar"/>

        <field name="type" col-name="issuetype" type="long-varchar"/>
        <field name="summary" type="long-varchar"/>
        <field name="description" type="extremely-long"/>
        <field name="environment" type="extremely-long"/>
        <field name="priority" type="long-varchar"/>
        <field name="resolution" type="long-varchar"/>
        <field name="status" col-name="issuestatus" type="long-varchar"/>
        <field name="created" type="date-time"/>
        <field name="updated" type="date-time"/>
        <field name="duedate" type="date-time"/>
        <field name="resolutiondate" type="date-time"/>
        ...
    </entity>

El API de Ofbiz es realmente complejo y no es objetivo de este artículo por lo que nos centraremos en la interfaz que utiliza JIRA para facilitar las consultas: Delegator. Este interfaz contiene todos los métodos necesarios para realizar búsquedas más o menos complejas sobre el  modelo de datos.

Acceso a OfBiz desde JIRA

El API de JIRA exporta interfaces que nos permiten acceder a la funcionalidad de OfBiz. La interfaz com.atlassian.jira.ofbiz.OfBizDelegator proporciona un recubrimiento sobre la interfaz nativa Delegator. Para obtener una instancia de la interfaz OfBizDelegator es necesario emplear el siguiente código:

OfBizDelegator delegator = new DefaultOfBizDelegator(CoreFactory.getGenericDelegator());

Sobre el objeto delegator podemos ejecutar consultas en base a las Entidades y Campos que proporciona JIRA. Es importante tener en cuenta que los valores de los campos en muchos de los casos son claves que hacen referencia al valor real del campo en otra tabla. Por ejemplo, podríamos recuperar todos los issues de tipo Bug sabiendo que el valor numérico de Bug es 1:

final List<GenericValue> bugs = delegator.findByField("Issue", "type", (Integer)1);

Sobre el objeto GenericValue retornado podemos obtener el valor de los campos mediante la siguiente sentencia:

String key = bugs.get(0).getString("key");

La interfaz de OfBizDelegator expone métodos que permiten realizar búsquedas sobre diferente tipo y número de campos:

  • findByLike
  • findByPrimaryKey
  • findByCondition
  • findByAnd
  • findByOr

Consulta el API de JIRA para conocer más en detalle la interfaz OfBizDelegator.

Sentencias Condicionales

En el ejemplo anterior hemos visto cómo usar la interfaz OfBizDelegator de JIRA para recuperar valores del modelo de datos. Sin embargo, la búsqueda mediante el valor de 1 campo no permite realizar búsquedas basadas en condiciones complejas en las que intervienen varios campos. A continuación enumeramos algunas clases que nos ayudan a elaborar condiciones lógicas:

  • org.ofbiz.core.entity.EntityCondition: Representa una condición consistente en un campo, un operador (de tipo EntityOperator) y un valor.
  • org.ofbiz.core.entity.EntityExpr: Representa una expresión lógica entre operandos o condiciones (OR, AND, etc)
  • org.ofbiz.core.util.UtilMisc: Permite construir una lista de condiciones concatenando expresiones y/o condiciones.

Una vez que tenemos definida la sentencia en base a condiciones empleamos el método findByCondition de OfBizDelegator para obtener la lista de GenericValues.

java.sql.Timestamp fromTime = new Timestamp(new java.util.Date().getTime() - 365*24*3600);
List conditions = UtilMisc.toList(
                                  new EntityExpr("created", EntityOperator.GREATER_THAN, fromTime),
                                  new EntityExpr("field", EntityOperator.LIKE, "status"),
                                  new EntityExpr("fieldtype", EntityOperator.LIKE, "jira"),
                                  new EntityExpr("newvalue", EntityOperator.LIKE, (Integer)5) // "Resolved"
                                 );
final EntityCondition condition = new EntityConditionList(conditions, EntityOperator.AND);

OfBizDelegator delegator = new DefaultOfBizDelegator(CoreFactory.getGenericDelegator());
final List<GenericValue> resolved = delegator.findByCondition("ChangeGroupChangeItemView", condition, EasyList.build("issue"));

 El operador JQL WAS

Desde la versión 4.3 de JIRA se ha introducido un operador nuevo en la sintaxis JQL: el operador WAS. Como su propio nombre indica, este operador es idéntico al operador IS pero incluye en la búsqueda el historial de cada issue. Por ejemplo, la siguiente sentencia JQL retornaría las issues que han sido asignadas al usuario actual desde su creación:

assignee was currentUser()

A continuación vamos a emular esta funcionalidad mediante una función JQL basada en OfBiz.

¿Es el responsable actual?

El operador IS está contenido en el operador WAS pero desde un punto de vista del modelo de datos, son consultas diferentes ya que afectan a tablas distintas. El responsable de una issue en JIRA se encuentra en la entidad Issue que veíamos más arriba.

final List<QueryLiteral> literals = new LinkedList<QueryLiteral>();
OfBizDelegator delegator = new DefaultOfBizDelegator(CoreFactory.getGenericDelegator());
final List<GenericValue> assignedToGVs = delegator.findByField("Issue", "assignee", userId);

for (GenericValue changeItem : assignedToGVs) {
    try {
        literals.add(new QueryLiteral(operand, changeItem.getLong("id")));
    }
    catch (NumberFormatException e) {
    }
}

Entonces, busca en el pasado

Sin embargo para conocer si el usuario estuvo asignado en el pasado debemos recurrir a la entidad ChangeGroupChangeItemView (ver el código fuente). En este caso recurriremos a la clase helper EasyMap que permite construir un mapa mediante los parámetros que se suministran en el constructor.

final List<QueryLiteral> literals = new LinkedList<QueryLiteral>();
Map<String, Object> paramsItem = EasyMap.build("field", field, "fieldtype", "jira", "newvalue", newValue);
final List<GenericValue> changeItemsForFieldGVs = delegator.findByLike("ChangeGroupChangeItemView", paramsItem);
for (GenericValue changeItem : changeItemsForFieldGVs) {
    try {
         literals.add(new QueryLiteral(operand, changeItem.getLong("issue")));
    }
    catch (NumberFormatException e) {
    }
}

La prueba final

En anteriores entregas hemos mostrado cómo construir una función JQL, empaquetarla y desplegarla en JIRA por lo que nos saltaremos esos pasos. A continuación mostramos el descriptor que hemos usado en el plugin:

<jql-function key="issue-assigned-to-jql-function" name="Issue Ever Assigned To Function"
 class="com.novagenia.jira.plugins.jql.function.EverAssignedToJQLFunction" system="true">
    <fname>everAssignedTo</fname>
    <list>true</list>
</jql-function>

Desplegamos y probamos nuestra nueva función JQL everAssignedTo.

Probamos con el operador WAS para validar que los resultados son idénticos.

Referencias

Además de los enlaces que hemos incluido en esta entrada, existe documentación exhaustiva en Atlassian sobre el modelo de datos en la siguiente página.

Como en el resto de entradas, es posible descargarse el código completo del plugin en el repositorio ElManifiesto de bitbucket.org: https://bitbucket.org/novagenia/elmanifiesto.git.

Déjanos tu opinión.

Author: Eduardo Mayor

Eduardo Mayor is a software engineer with 20+ years of experience. He is also the founder of Novagenia Information Technologies, a company focused on introducing agile methods and tools to companies worldwide.

Visit us in: http://www.novagenia.com
Share this...

3 comentarios en “JIRA Plugins: Modelo de datos

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *

Puedes usar las siguientes etiquetas y atributos HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>