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
| Propiedad | Valor |
|---|---|
artifactId | slack-client |
groupId | com.hawkersco |
version | 1.0.25-SNAPSHOT |
| Java | 25 |
| Spring Boot | 4.0.6 |
| Tipo de artefacto | JAR (librería, no ejecutable) |
| Módulos | Proyecto ú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
| Dependencia | Versión | Propó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:lombok | 1.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 cliente | HTTP | Ruta remota | Descripción |
|---|---|---|---|
sendMessage | POST | /api/chat.postMessage | Envía un mensaje de texto a un canal de Slack |
getUploadUrl | POST | /api/files.getUploadURLExternal | Solicita una URL pre-firmada y un ID de fichero para subir un adjunto |
completeUpload | POST | /api/files.completeUploadExternal | Finaliza 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)
| Propiedad | Descripción | Ejemplo de valor |
|---|---|---|
slack.client.url | URL base de la API de Slack (activa la autoconfiguración) | ${SLACK_CLIENT_URL} |
slack.auth.token | Token Bearer del bot de Slack | ${SLACK_BOT_TOKEN} |
Importante: Si falta
slack.client.urloslack.auth.token, el beanSlackClientno se registra (condición@ConditionalOnPropertycon ambas claves).
Variables de entorno recomendadas
| Variable | Propiedad mapeada |
|---|---|
SLACK_CLIENT_URL | slack.client.url |
SLACK_BOT_TOKEN | slack.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:
- Checkout — descarga el código del repositorio.
- Publish to Artifact Registry — ejecuta
mvn deploy -DskipTestspara 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ámetro | Valor |
|---|---|
| JDK | JDK25 (tool Jenkins) |
| Maven | Maven3 (tool Jenkins) |
| Repositorio | europe-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
-
completeUploadsin tipo de retorno útil: El método está declarado comovoiden lugar deResponseEntity<...>, a diferencia desendMessageygetUploadUrl. El consumidor no tiene forma de inspeccionar el código de estado HTTP ni el cuerpo de la respuesta defiles.completeUploadExternala 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/RestClientnativo de Spring, sin ninguna dependencia ni anotación de Feign. -
Anotaciones Gson (
@SerializedName) aplicadas de forma inconsistente: SoloCompleteUploadRequestlleva@SerializedNamejunto a@JsonProperty;MessageSlackyUploadUrlResponsesolo 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 interfazMultipartFilede Spring, necesaria porqueSlackUtils.convertFileToMultipartFileconstruye 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:
uploadFileRawusa una instancia efímera deRestClientsin 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("Nosrc/testdirectory 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.