Elastest TestLink Plugin REST API (1)

En esta nueva serie de blogs, iré contando como estoy diseñando e implementando la API REST con SpringBoot mediante la cuál se realizaran la diferentes conexiones con las instancias de TestLink.

Figura 1: Logos de Spring y TestLink.

Introducción

Antes de empezar con la implementación de la API REST, debemos analizar y diseñar nuestro sistema, y con ello tomar algunas decisiones de diseño para algunos problemas que surgirán o que podrían surgir (rediseño) respetando el principio YAGNI (You aren’t gonna need it), los principios SOLID y utilizando, en tanto en cuanto sea necesario, los patrones de diseño que nos ofrece Gamma.

Spring Beans

Para este último punto Spring cuenta con un sistema de Beans que facilita enormemente, hasta el punto de hacerlo invisible a nosotros, un grupo de patrones concretos (principalmente creacionales, pero también orientados a comportamiento) y que mencionaré brevemente.

Para conocer los diferentes tipos de Scope beans primero hay que saber qué son.

Bean: Objeto que forma la columna vertebral (backbone) de su aplicación Spring y que es instanciado, ensamblado y gestionado por el contenedor Spring IoC.
Scope Bean: Define el ciclo de vida y la visibilidad del bean en el contexto (scope) en el que es usado.

Tipos de beans:

Creacionales:

  • Singletone: Garantiza que solo exista un objeto de la clase y cuyo acceso sea global.
  • Protoype: Su objetivo es crear objetos a partir de una instancia prototípica de la clase.

Ámbito:

Estas requieren un ScopedProxyMode (basado en otro de los patrones de Gamma - Proxy), necesario porque en el momento de la instanciación del contexto de la aplicación no hay una solicitud activa. Spring creará un proxy para ser inyectado como una dependencia e instancirá el bean cuando sea necesario en una solicitud/session/globals session(portlet).

  • Request: Garantiza que el objeto persista durante el ámbito de cada petición.
  • Session: Garantiza que el objeto persista durante el ámbito de cada sesión.
  • GlobalSession: Similar a session pero para aplicaciones que corren sobre un portlet container, estos tienen su propia sesión cada uno.

Para más profundidad ….

Problemas de diseño.

Teniendo estos detalles en mente se procede a analizar y diseñar la API, sobre todo este proceso voy a resaltar algunas decisiones relevantes a problemas concretos.

  • Adecuación a versiones de TestLink y diferentes métodos de comunicación entre la API y TestLink.

Para evitar un acoplamiento excesivo a la API RPC, de la que ya he hablado previamente en otros blogs, se tratará de desacoplar los servicios y controladores REST de la implementación del propio plugin basado en RPC ( acoplado a Testlink Java API puesto que requiere sus servicios para un correcto funcionamiento).

Es por ello que se definirá una interfaz llamada Plugin.java que implementarán todas las clases que contengan diferentes implementaciones del Plugin, por ejemplo conexión RPC o REST. Y cada una de estas implementaciones ofrecerá una fachada con los métodos que ofrece.

Figura 2: Diagrama de paquetes para la resolución de Adecuación a versiones de TestLink y diferentes métodos de comunicación entre la API y TestLink.
  • Mantener el Objeto Plugin durante todo el ámbito de la petición

Para conseguir esto usaré los beans de los que he hablado previamente, pero hay que recordar que si el plugin es un @component con un scope de petición solo podrá ser implementado por una de las clases del modelo, para ello se puede definir un archivo de configuración de los beans en función de la funcionalidad que necesitemos en cada despliegue.

Para conseguir mantener el estado durante el ámbito de la petición configuraremos las clases de la siguiente manera :

Figura 3: Interfaz Plugin.java.
Figura 3: Clase RPCPlugin.java que implementa la interfaz Plugin.java y configura un bean de Spring.
  • Envío de dirección del servidor de TestLink y de su correspondiente clave de API en cada petición.

Para este último punto las opciones eran varias, enviar un parámetro en cada petición era la opción más rápida (un parche bastante sucio) por el cuál todas las peticiones serían algo parecido a lo siguiente:

{GET/POST/…} http://<server>:[port]/testlink-plugin-api/info?server=http://urltoserver/api/xmlrpc.php&apikey=ee4bd5651d858501ea47056785625d2c4d745bf

Y en todos los controladores rest tendriamos el código que recoge esa información y se la lanza al RPCPlugin.

Figura 5: Ejemplo de controlador REST y un método acoplado a los parámetros url y key.

Obligando así a todos lo métodos a incluir ese código repitiendo código hasta el infinito (y más allá ….) o bien obligándonos a crear una clase padre de todos los controladores que se encargará de extraer la información y procesarla, pero todas las llamadas a la API seguirían siendo muy ‘sucias’ (sin pensar en los posibles parámetros que podremos necesitar en el futuro que engordarían aún más las URL’s).

Por esto decidí implementar algo que es muy corriente en las empresas de telecomunicaciones, y más en concreto de los departamentos de inspección de paquetes (DPI), y es lo que se concoce como HTTP Enrichment por el cuál se introduce un sistema de cabeceras HTTP (headers) que serán filtradas, en este caso por un javax.servlet.filter.

[ — Parece que la beca en Ericsson, en el departamento de DPI Operations, que estoy cursando y el máster de Software Craftmanship de la UPM empiezan a dar sus frutos … :) ]

El siguiente diagrama de clases muestra el modelo utilizado para el enriquecimiento de cabeceras (incluye la interacción son un servicio cualquiera):

Figura 6: Diagrama de clases propuesto para la solución del problema Envío de dirección del servidor de TestLink y de su correspondiente clave de API en cada petición.

Y el diagrama de secuencia del enriquecimiento de las cabeceras, incluyendo una pequeña variación de funcionamiento en función del tipo de petición (get/post), seria el siguiente:

Figura 7: Diagrama de secuencia propuesto para la solución del problema Envío de dirección del servidor de TestLink y de su correspondiente clave de API en cada petición.

Analizando la calidad del diseño

Tras añadir unos cuantos servicios y controladores para poder hacer prueba con este primer prototipo, observamos el diagrama de clases que resulta y las clases en un pequeño code review en busca de smell codes, antipatrones , ciclos, …

Figura 8: Modelo de clases del primer prototipo de la API RESTpuesto a revisión.

A simple vista no se aprecian ciclos ni acoplamientos entre servicios y controladores REST con las clases que implementarán la conexión a las instancias de TestLink.

Observando las clases podemos ver que no hay smell codes ni antipatrones a excepción de un pequeño detalle que ya decidiré como solucionar más adelante. Y es que la implementación del RPCPlugin conlleva una serie de llamadas a procedimientos remotos con muchos parámetros que necesitaremos recoger de alguna forma y que nos llevarían a métodos con más de 6 ó 7 parámetros .

Aún con todo esto y usando el plugin de eclipse llamado SonarLint Report que nos informa de los smell codes que podamos ir introduciendo mientras programamos mediante un sistema de análisis integrada en eclipse. He configurado un entorno de integración continua con :

SonarCloud + Travis + GitHub + Jacoco

Para llevar una correcta trazabilidad del proyecto estoy usando GitHub.

Si a esto le añadimos la combinación que nos ofrece Travis + SonarCloud podemos tener un análisis mas exhaustivo de la aplicación, para ello:

  1. Configuramos nuestro pom.xml de la siguiente manera:
Figura 9 : Pom.xl configurado para integración continua y creación de builds.

[ — Cabe destacar que ya está preparado con jacoco para reportar la cobertura de test a sonarcloud].

2. Accedemos a SonarCloud enlazando con la cuenta de GitHub y genereamos un token de conexión en la pestaña de security.

3. Activamos nuestro repositorio en Travis.org (puesto que se trata de un repositorio público), y añadimos las variables de entorno que necesitamos, para mayor seguridad, con nuestras claves de GitHub o Sonar.

(Existe la posibilidad de encriptarlas con Travis pero me parecía más cómodo usar variables de entorno).

Figura 10 : Configuración de repositorio en trvis.org.

4. Añadimos un archivo .travis.yml en el root de nuestro repositorio y lo configuramos para que genere un nuevo jar, un nuevo análisis de sonar y en el futuro corran los tests, con cada rama que se suba a master.

Figura 11 : archivo .travis.yml

Por último al subir los cambios a GitHub comprobamos el estado de la build en Travis y las estadísticas que arroja Sonar con aprox. 500 lineas de código.

Figura 12: Estadísticas al subir cambios al repositorio vistas en travis y sonar (izuierda a derecha).

Con los resultados obtenidos podemos seguir implementando funcionalidades sabemos que todas las clases cumplen las normas de calidad y establecen unos altos niveles de mantenibilidad, portabilidad y eficiencia, y bajos de deuda técnica, vulnerabilidades, bugs o smell codes. Todo ello hace de la aplicación un sistema robusto, mantenible y adaptable ante nuevos requisitos (como pretendíamos). Esta tabla muestra diferentes datos sobre cada paquete de clases:

Figura 13: Estadisticas de clase por paquete, SonarCloud.io.

Una de nuestras futuras tareas será cubrir los casos de test, con SpringTest, Karata, Junit, … para la api y Karma, Protactor , Mocha…. para la web.

De momento podemos usar los badges directamente en nuestro repositorio par mostrar el estado del mismo.

Figura 14: Badges en repositorio con estado passing de build y calidad.

A continuación y para concluir mostraré una pequeña demo de peticiones a la API con el filtro de enriquecimiento y las excepciones que lanza en caso de no existir o ser inapropiadas:

Figura 15: Demo Elastest TestLink plugin .

Cuándo termine la implementación de las funcionalidades necesarias y estén incluidas en la aplicación web subiré un nuevo blog analizando las nuevas estadísticas y resultados finales,

…. y posiblemente integremos nuevas funcionalidades como JIRA bug report.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.