Skip to main content

Slack Client

1. Descripción general

slack-client es una librería cliente reutilizable (JAR) que encapsula la comunicación con la API de Slack (api.slack.com/slack.com). El proyecto no expone ningún endpoint REST propio; se publica en el registro de artefactos Maven interno y es consumido por otros microservicios del ecosistema Hawkers que necesiten enviar mensajes a un canal de Slack o subir ficheros adjuntos siguiendo el flujo de subida externa de Slack.

2. Información técnica

PropiedadValor
artifactIdslack-client
groupIdcom.hawkersco
version1.0.25-SNAPSHOT
Java25
Spring Boot4.0.6
Tipo de artefactoJAR (librería, no ejecutable)
MódulosProyecto único (no multi-módulo)

3. Arquitectura y diseño

Estructura del proyecto:

com.hawkersco.slackclient
├── SlackClientApplication.java # Clase @SpringBootApplication (residual, sin lógica)
├── client/
│ └── SlackClient.java # Interfaz @HttpExchange: sendMessage, getUploadUrl, completeUpload
├── config/
│ └── SlackAutoConfiguration.java # @AutoConfiguration principal (Bearer token estático)
├── pojo/
│ ├── MessageSlack.java # Record: payload de chat.postMessage
│ ├── UploadUrlResponse.java # Record: respuesta de files.getUploadURLExternal
│ ├── CompleteUploadRequest.java # Record: payload de files.completeUploadExternal
│ └── MyMultipartFile.java # Implementación propia de MultipartFile (para subir ficheros desde disco)
└── utils/
└── SlackUtils.java # Conversión fichero→MultipartFile y subida de bytes crudos al pre-signed URL

Flujo principal — envío de mensaje

sequenceDiagram
participant Consumidor
participant Client as SlackClient
participant Slack as API Slack

Consumidor->>Client: sendMessage(MessageSlack)
Client->>Slack: POST /api/chat.postMessage + Authorization: Bearer <token>
Slack-->>Client: ResponseEntity<String>
Client-->>Consumidor: ResponseEntity<String>

Flujo principal — subida de fichero (3 pasos)

sequenceDiagram
participant Consumidor
participant Utils as SlackUtils
participant Client as SlackClient
participant Slack as API Slack

Consumidor->>Utils: convertFileToMultipartFile(dir, nombre)
Consumidor->>Client: getUploadUrl(length, filename)
Client->>Slack: POST /api/files.getUploadURLExternal
Slack-->>Client: UploadUrlResponse (uploadUrl + fileId)
Consumidor->>Utils: getCompleteUploadRequest(channelId, file, mensaje, uploadUrlResponse)
Utils->>Slack: POST <uploadUrl> (bytes crudos, application/octet-stream, RestClient ad-hoc sin autenticación)
Utils-->>Consumidor: CompleteUploadRequest listo
Consumidor->>Client: completeUpload(CompleteUploadRequest)
Client->>Slack: POST /api/files.completeUploadExternal + Authorization: Bearer <token>

La autoconfiguración (SlackAutoConfiguration) se activa condicionalmente con @ConditionalOnProperty(prefix = "slack", name = {"client.url", "auth.token"}), registrando el bean SlackClient con un RestClient que fija la cabecera Authorization: Bearer <token> como valor por defecto en todas las peticiones.

El registro de la autoconfiguración se realiza mediante el fichero estándar de Spring Boot:

META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

SlackUtils.uploadFileRaw realiza el paso intermedio de subida de bytes con una instancia ad-hoc de RestClient (RestClient.create()), no con el bean SlackClient — apropiado porque la URL pre-firmada de Slack no requiere ni acepta el token Bearer, a diferencia del resto de llamadas.

Nota sobre la documentación previa del proyecto: el CLAUDE.md de este repositorio describe una arquitectura basada en Spring Cloud OpenFeign con anotaciones Feign nativas (@RequestLine, @Headers, @Param) y un bean Contract personalizado en una clase SlackClientConfig. Ninguno de estos elementos existe en el código actual: no hay dependencia de Feign en el pom.xml, la clase de configuración se llama SlackAutoConfiguration (no SlackClientConfig) y usa @HttpExchange/RestClient sin Contract alguno. Este documento describe el comportamiento observado en el código, no el descrito en CLAUDE.md.

4. Dependencias principales

DependenciaVersiónPropósito
spring-boot-starter(gestionada SB4)Base de Spring Boot (contexto, autoconfiguración)
spring-web(gestionada SB4)RestClient + @HttpExchange / HttpServiceProxyFactory / soporte multipart
com.fasterxml.jackson.core:jackson-databind(gestionada SB4)Serialización/deserialización Jackson en los records (@JsonProperty)
com.google.code.gson:gson(gestionada SB4)Anotaciones @SerializedName en CompleteUploadRequest (soporte dual, no aplicado uniformemente — ver sección 13)
org.projectlombok:lombok1.18.42 (dependencia provided; annotationProcessorPath del compilador usa 1.18.46)Uso limitado, dado que los DTOs principales son records
spring-boot-starter-test(gestionada SB4)Testing (scope test)

5. API / Endpoints

No aplica a este proyecto. slack-client es una librería cliente JAR que no expone endpoints REST propios. Las operaciones que encapsula sobre la API de Slack se detallan en la sección 6.

6. Integraciones externas

API de Slack

Método clienteHTTPRuta remotaDescripción
sendMessagePOST/api/chat.postMessageEnvía un mensaje de texto a un canal de Slack
getUploadUrlPOST/api/files.getUploadURLExternalSolicita una URL pre-firmada y un ID de fichero para subir un adjunto
completeUploadPOST/api/files.completeUploadExternalFinaliza la subida, asociando el fichero a un canal (sin cuerpo de respuesta, ver sección 13)

Ejemplo de payload sendMessage (MessageSlack):

{
"channel": "C0123456789",
"text": "Pedido ORD-000123 procesado correctamente."
}

Ejemplo de respuesta getUploadUrl (UploadUrlResponse):

{
"ok": true,
"upload_url": "https://files.slack.com/upload/v1/...",
"file_id": "F0123456789"
}

Ejemplo de payload completeUpload (CompleteUploadRequest, construido por SlackUtils.getCompleteUploadRequest):

{
"files": [{ "id": "F0123456789", "title": "reporte-diario.csv" }],
"channel_id": "C0123456789"
}

Protocolo: HTTPS REST (JSON para las llamadas a api.slack.com; application/octet-stream para la subida de bytes al pre-signed URL). Autenticación: Bearer token estático (slack.auth.token, típicamente un token xoxb-... de bot de Slack), inyectado como cabecera por defecto; la subida de bytes al pre-signed URL no requiere autenticación adicional.

7. Configuración

El fichero src/main/resources/application.properties solo define spring.application.name=slack-client; el resto de propiedades deben ser inyectadas por la aplicación consumidora.

Propiedades requeridas (prefijo slack)

PropiedadDescripciónEjemplo de valor
slack.client.urlURL base de la API de Slack (activa la autoconfiguración)${SLACK_CLIENT_URL}
slack.auth.tokenToken Bearer del bot de Slack${SLACK_BOT_TOKEN}

Importante: Si falta slack.client.url o slack.auth.token, el bean SlackClient no se registra (condición @ConditionalOnProperty con ambas claves).

Variables de entorno recomendadas

VariablePropiedad mapeada
SLACK_CLIENT_URLslack.client.url
SLACK_BOT_TOKENslack.auth.token

8. Persistencia

No aplica a este proyecto. La librería no accede a ninguna base de datos ni mantiene estado en memoria.

9. Procesos programados y mensajería

No aplica a este proyecto. No existen jobs @Scheduled, listeners de colas/topics ni runners batch. El envío de mensajes/ficheros a Slack se realiza mediante llamadas HTTP síncronas bajo demanda del consumidor.

10. Ejecución en local

slack-client es una librería JAR, no una aplicación ejecutable. No tiene servidor embebido ni endpoint de health.

Requisitos previos

  • Java 25
  • Maven 3.x
  • Acceso al registro de artefactos Maven interno (europe-west3-maven.pkg.dev/pi-saldum/pi-repo-maven) para resolver/publicar dependencias.

Compilar e instalar en repositorio local

# Compilar
mvn clean install
mvn clean package

# Compilar sin tests (como en CI)
mvn -B -DskipTests clean install

# Ejecutar un test/método concreto
mvn test -Dtest=ClassName
mvn test -Dtest=ClassName#methodName

Uso como dependencia en un microservicio consumidor

<dependency>
<groupId>com.hawkersco</groupId>
<artifactId>slack-client</artifactId>
<version>1.0.25-SNAPSHOT</version>
</dependency>

La autoconfiguración se activa automáticamente al declarar slack.client.url y slack.auth.token en la aplicación consumidora.

11. Despliegue

El pipeline de Jenkins (Jenkinsfile) consta de dos etapas:

  1. Checkout — descarga el código del repositorio.
  2. Publish to Artifact Registry — ejecuta mvn deploy -DskipTests para publicar el JAR en Google Artifact Registry.

CLAUDE.md documenta un pipeline de cuatro etapas (Build, KICS, SonarQube, Clean) vía scripts (jenkins/scripts/mvn.sh/clean.sh), que no se corresponde con el Jenkinsfile actual del repositorio (dos etapas: Checkout y Publish to Artifact Registry). Se documenta el Jenkinsfile realmente presente.

ParámetroValor
JDKJDK25 (tool Jenkins)
MavenMaven3 (tool Jenkins)
Repositorioeurope-west3-maven.pkg.dev/pi-saldum/pi-repo-maven (Artifact Registry GCP)

No existe Dockerfile ni despliegue como servicio independiente; el artefacto es un JAR publicado en el registro Maven.

Job de Jenkins:

https://jenkins-pi.hawkersco.net/job/slack-client/

12. Manejo de errores y logging

La librería no implementa ninguna estrategia propia de manejo de excepciones ni logging estructurado. Ningún método de SlackClient declara throws explícito; las excepciones de red o HTTP propagadas por RestClient (como RestClientResponseException) son responsabilidad del servicio consumidor. Slack suele responder 200 OK con un campo ok: false incluso en caso de error de negocio (p. ej. canal inválido, token sin permisos): el consumidor debe inspeccionar el cuerpo de la respuesta manualmente, ya que el cliente no valida este campo. No hay configuración de logback ni de niveles de log específicos en la librería.

13. Notas y consideraciones

  • completeUpload sin tipo de retorno útil: El método está declarado como void en lugar de ResponseEntity<...>, a diferencia de sendMessage y getUploadUrl. El consumidor no tiene forma de inspeccionar el código de estado HTTP ni el cuerpo de la respuesta de files.completeUploadExternal a través de este método — si Slack devuelve un error, no hay manera directa de detectarlo salvo que se lance una excepción HTTP (4xx/5xx).

  • Documentación previa (CLAUDE.md) describe una arquitectura Feign inexistente: Ver detalle en la sección 3. El proyecto usa @HttpExchange/RestClient nativo de Spring, sin ninguna dependencia ni anotación de Feign.

  • Anotaciones Gson (@SerializedName) aplicadas de forma inconsistente: Solo CompleteUploadRequest lleva @SerializedName junto a @JsonProperty; MessageSlack y UploadUrlResponse solo usan @JsonProperty (Jackson). Si algún consumidor dependiera de Gson para estos dos últimos records, los campos con nombre distinto en JSON (upload_url, file_id) no se mapearían correctamente sin anotación Gson explícita.

  • MyMultipartFile: Implementación propia de la interfaz MultipartFile de Spring, necesaria porque SlackUtils.convertFileToMultipartFile construye el objeto a partir de bytes leídos de disco sin pasar por el ciclo de vida normal de una petición HTTP multipart (donde Spring proporciona su propia implementación). Patrón puntual, no reutilizado por otros clientes del ecosistema documentados hasta ahora.

  • Subida de bytes sin autenticación ni reintentos: uploadFileRaw usa una instancia efímera de RestClient sin ningún mecanismo de reintento; si la subida al pre-signed URL falla (p. ej. por expiración de la URL), la excepción se propaga directamente sin contexto adicional sobre en qué paso del flujo de tres pasos ocurrió el fallo.

  • Sin tests implementados: Confirmado explícitamente en CLAUDE.md ("No src/test directory exists").

  • SlackClientApplication.java: Clase principal de Spring Boot en el paquete raíz, sin funcionalidad operativa. Artefacto residual de la generación inicial del proyecto con Spring Initializr, mismo patrón observado en otros clientes del ecosistema.