Slack Bot para Spring Info Actuator
Tags: node, javascript, slack, netlify
Published: 2021-05-30 20:17:40

---
title: Slack Bot para Spring Info Actuator
published: true
date: 2020-11-13 00:00:00 UTC
tags: node,javascript,slack,netlify
canonical_url: https://jorge.aguilera.soy/blog/2020/slack-bot-info-actuator.html
---

<dl>
<dt>WARNING</dt>
<dd>
<p>Este post se puede considerar una continuación a <a href="mi-primer-slack-bot.html">mi-primer-slack-bot.html</a>, de hecho
sólo voy a contar la parte de código de interés porque todo lo demás ya se explica en ese post</p>
</dd>
</dl>

Si tu aplicación usa el framework Spring o Grails entonces dispones de la funcionalidad Spring Actuator, que te permite monitorizar y obtener información sobre tu aplicación en tiempo real a través de unos endpoints, como el típico `/health` , `/env` o `/info`

En concreto `/info` ofrece información como el nombre de la aplicacion, version de la misma, o del java, etc. Además si encuentra un fichero `git.properties` es capaz de adjuntar la información del mismo en la response

Poder acceder a esta información de forma fácil puede ser una gran utilidad. Por ejemplo poder consultar qué versión de API está corriendo en el entorno de desarrollo puede servirle al desarrollador de front para comprobar si es la correcta contra la que quiere probar sus desarrollos.

## Aplicación

En el caso en concreto que vamos a ver en este post, se trata de una aplicación Grails la cual incluye por defecto tanto la dependencia a spring actuator como una configuración mínima para exportar estos endpoints (más info en[http://docs.grails.org/3.3.4/guide/spring.html#actuators](http://docs.grails.org/3.3.4/guide/spring.html#actuators) )

Así mismo, como norma básica, el acceso a dicho endpoint estará protegido con Spring Security por lo que primero habrá que hacer un login para obtener tokens de acceso para poder acceder al endpoint indicado.

Por último, para poder ofrecer la máxima cantidad de información al usuario (por ejemplo el mensaje del último commit), especificaremos que el endpoint use el formato `full`([https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready-application-info-git](https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready-application-info-git))

## Conectividad

Dependiendo de dónde se encuentre alojada la aplicación, así como el bot, tendrás que lidiar con diferentes aspectos de conectividad.

En este caso en concreto el bot se va a alojar en Netlify, el cual lo expone mediante una url pública en Internet. Así mismo la aplicación es abierta (pero protegida) al público, por lo que también cuenta con una url pública. Por último, para este ejemplo vamos a contar con que los 3 entornos, dev, staging y prod, están accesibles desde Internet

Si no fuera así, por ejemplo tu api de prod está abierta al público, pero los entornos de desarrollo están en una red interna, tendrías que buscar una forma de unir los 3 "mundos": slack → bot → aplicación

 ![](https://jorge.aguilera.soy/images/1605439173760.svg)

## Creación del proyecto

Para la creación del proyecto y su publicación te remito al post [mi-primer-slack-bot.html](mi-primer-slack-bot.html)donde se explica de forma detallada. De hecho si has conseguido seguirlo y publicar tu primer bot puedes reutilizarlo pues lo que vamos a hacer es añadir una función nueva (y algunas variables de entorno)

### Nueva function

Siguiendo los pasos del post anterior crearemos una nueva `function` git-status:

`netlify function:create`

(O simplemente ya que tenemos todo montado crearemos un nuevo subdirectorio dentro de `functions` y añadiremos el código siguiente)

git-status.js

```
const querystring = require("querystring");
const axios = require("axios");

const { SLACK_BOT_TOKEN } = process.env;
const { REPO_GIT } = process.env;

const handler = async (event) => {
  try {

    if (event.httpMethod !== "POST") {
      return { statusCode: 405, body: "Method Not Allowed" };
    }

    const params = querystring.parse(event.body);

    const environment = params.command.toUpperCase().substring(1)

    const REMOTE_API = process.env[`${environment}_API`];
    const REMOTE_USERNAME = process.env[`${environment}_USERNAME`];
    const REMOTE_PASSWORD = process.env[`${environment}_PASSWORD`];

    let login = await axios.post(`${REMOTE_API}/api/login`,{
      username:REMOTE_USERNAME,
      password:REMOTE_PASSWORD,
    })
    let access_token = login.data.access_token

    let version = await axios.get(`${REMOTE_API}/info`,{
      headers:{
        'Authorization':`Bearer ${access_token}`
      }
    })

    const data = {
      text: `
*${environment}* *${version.data.git.commit.message.short}
Version *${version.data.app.version}*
Cooked at *${version.data.git.commit.time}*
last commit: *${version.data.git.commit.id}*
${REPO_GIT}/${version.data.git.commit.id}
`,
      channel: params.channel_id,
      as_user: true
    };

    const result = await axios.post('https://slack.com/api/chat.postMessage', data, {
      headers:{
        'Authorization': `Bearer ${SLACK_BOT_TOKEN}`,
        'X-Slack-User': params.user_id
      }
    })

    return {
      statusCode: 200
    }

  } catch (error) {
    console.log(error)
    return { statusCode: 500, body: error.toString() }
  }
}

module.exports = { handler }
```

Como puedes ver el código es muy similar al del comando anterior. Simplemente usaremos el comando que nos envía el usuario para detectar que entorno es el que nos está pidiendo. Así por ejemplo si el usuario ejecuta el comando /prod pasaremos a mayusculas esta cadena y quitaremos el primer caracter

`const environment = params.command.toUpperCase().substring(1)`

y usaremos la constante `environment` como prefijo para buscar las credenciales y url a invocar

```
const REMOTE_API = process.env[`${environment}_API`];
const REMOTE_USERNAME = process.env[`${environment}_USERNAME`];
const REMOTE_PASSWORD = process.env[`${environment}_PASSWORD`];
```

Básicamente realizaremos un login y obtendremos el access\_token para poder invocar al endpoint de Spring Actuator.

Por último renderizamos toda la información como una cadena Markdown:

```
const data = {
  text: `
*${environment}* *${version.data.git.commit.message.short}
Version *${version.data.app.version}*
Cooked at *${version.data.git.commit.time}*
last commit: *${version.data.git.commit.id}*
${REPO_GIT}/${version.data.git.commit.id}
`,
```

## Netlify

Por la parte del bot simplemente resta configurar todas las variables de entorno en Netlify:

`PROD_API=http://xxxxx.com/`

`STAGING_API=http://yyyyy.com/`

`DEV_API=http://zzzzz.com/`

etc

## Slack

Por último habilitamos nuevos comandos desde la consola de nuestra App de Slack apuntando a la nueva`function` creada (los 3 comandos apuntan a la misma url pues en base a el comando la `function` ya sabe qué ejecutar)