gio-api-notifications
1. Descripción general
Según el pom.xml, el proyecto se describe como "Project for gio api notifications". Es una API REST cuya única responsabilidad es recibir payloads JSON de actualización/creación de cliente enviados por los sistemas GIO y POS, validarlos como JSON bien formado, y persistirlos como ficheros .json con marca de tiempo en un bucket de Google Cloud Storage, para que procesos posteriores los recojan de forma asíncrona. No realiza ninguna validación de negocio sobre el contenido del payload más allá de comprobar que es JSON válido.
2. Información técnica
| Campo | Valor |
|---|---|
artifactId | gio-api-notifications |
groupId | com.hawkersco |
version | 1.0.25 |
| Java | 25 (maven.compiler.release=25) |
| Spring Boot | 4.0.6 |
| Tipo de artefacto | jar (ejecutable, API web de larga duración) |
| Módulos | No aplica (proyecto de módulo único) |
3. Arquitectura y diseño
Es una API REST clásica con seguridad basada en Basic Auth y roles. Paquetes principales:
com.hawkersco.gioapinotifications— clase principal (GioApiNotificationsApplication, con@EnableScheduling, ver hallazgo en sección 13)..controller—GioApiNotificationsController, único controlador de la aplicación..config—GioApiNotificationsConfig(@Order(1), usuarios en memoria) yApp2ConfigurationAdapter(@Order(2),SecurityFilterChain, CORS, exclusión de Swagger)..exception—GcsUploadException(excepción de dominio) yGlobalExceptionHandler(@RestControllerAdvice).utils.GioApiNotificationsConst— fuera del paquete raízcom.hawkersco(paqueteutilsa secas); inconsistencia señalada explícitamente como intencional en elCLAUDE.mddel proyecto.
flowchart TD
A[GIO] -->|POST /api/gio-notification/update-client<br/>rol GIOXHAWKERS| C[GioApiNotificationsController]
B[POS] -->|POST /api/pos-notification/update-client<br/>rol ADMIN| C
C -->|valida JSON| C
C -->|sube fichero| D[(GCS bucket hawkers-gio)]
C -.->|fallo de subida| E[GcsUploadException]
E --> F[GlobalExceptionHandler]
F -->|notifica| G[Slack]
Dos @Configuration de seguridad ordenadas explícitamente (@Order(1)/@Order(2)): GioApiNotificationsConfig define los usuarios en memoria (admin con rol ADMIN, gioxhawkers con rol GIOXHAWKERS, ambos con la misma contraseña, proveniente de gioapinotifications.encodepass), y App2ConfigurationAdapter define el SecurityFilterChain, la política CORS y qué rutas requieren qué rol.
4. Dependencias principales
| Dependencia | Propósito |
|---|---|
spring-boot-starter-web | Exposición de los endpoints REST |
spring-boot-starter-security | Autenticación HTTP Basic + autorización por roles |
org.json:json:20240303 | Validación de que el payload recibido es JSON bien formado |
lombok | Generación de código boilerplate |
com.hawkersco:pi-function-commons:1.0.25-SNAPSHOT | DateUtils (timestamp del nombre de fichero), StorageUtils (subida a GCS) |
com.hawkersco:slack-client:1.0.25-SNAPSHOT | SlackClient/MessageSlack para notificar fallos de subida |
spring-boot-starter-test (test) | JUnit 5 + Spring Test |
5. API / Endpoints
| Método | Ruta | Descripción | Autenticación / Rol |
|---|---|---|---|
GET | /api/check | Health check público | Ninguna (permitAll) |
POST | /api/gio-notification/update-client | Recibe una actualización/creación de cliente desde GIO y la guarda en GCS | HTTP Basic, rol GIOXHAWKERS |
POST | /api/pos-notification/update-client | Recibe una actualización/creación de cliente desde POS y la guarda en GCS | HTTP Basic, rol ADMIN |
Ambos endpoints de actualización devuelven siempre HTTP 200, incluso ante fallos: si el cuerpo no es JSON válido, responden "true"; si la subida a GCS falla, el GlobalExceptionHandler también responde "true" tras notificar el error a Slack (diseño deliberado para no propagar errores al llamador, según el javadoc del propio controlador).
Ejemplo de respuesta exitosa:
{"Successfully saved": "update_client_gio_20260710153000123.json"}
6. Integraciones externas
| Sistema | Protocolo | Dirección | Detalle |
|---|---|---|---|
Google Cloud Storage (bucket hawkers-gio) | API de GCS (StorageUtils) | Saliente | Persistencia de los payloads recibidos como ficheros .json, bajo <appName>/update-client-gio-pending/ o <appName>/update-client-pos-pending/ |
| Slack | HTTP (SlackClient) | Saliente | Notificación cuando falla la subida a GCS |
7. Configuración
Este proyecto es una excepción positiva dentro del ecosistema: no existe un fichero application.properties local con valores hardcodeados; solo hay application-pro.properties, y todos sus valores sensibles ya usan ${VARIABLE}. No se ha encontrado ninguna credencial real en el repositorio.
| Clave | Descripción | Ejemplo |
|---|---|---|
server.port | Puerto HTTP del servicio | 80 |
server.session.tracking-modes | Modo de seguimiento de sesión | cookie |
gioapinotifications.encodepass | Contraseña (en claro antes de cifrar con BCrypt) compartida por los usuarios admin y gioxhawkers | ${encodePass} |
gcs.bucket.name | Bucket de GCS destino | hawkers-gio |
slack.client.url | URL API Slack | ${slackClientUrl} |
slack.auth.token | Token bot de Slack | ${slackAuthToken} |
slack.channel.id | Canal de notificaciones | ${slackChannelId} |
Las credenciales de GCS se resuelven vía Application Default Credentials (GOOGLE_APPLICATION_CREDENTIALS), montadas en el Deployment desde el Secret pi-saldum-gcp-credentials en /etc/gcp/sa_credentials.json.
No se emite alerta de seguridad por credenciales hardcodeadas en este proyecto, ya que no existen en el repositorio; ver en la sección 13 una observación sobre el diseño de la autenticación (contraseña compartida entre roles) y la política CORS.
8. Persistencia
No aplica a este proyecto. No usa base de datos: el estado se materializa como ficheros JSON en el bucket de GCS.
9. Procesos programados y mensajería
No hay ningún job real programado ni listener de colas en este servicio: es puramente reactivo a las peticiones HTTP entrantes. Ver hallazgo en la sección 13 sobre @EnableScheduling declarada sin ningún @Scheduled asociado.
10. Ejecución en local
Requisitos previos: JDK 25, Maven, credenciales de aplicación por defecto de Google con permiso de escritura sobre el bucket hawkers-gio, y variables de entorno resueltas para encodePass, slackClientUrl, slackAuthToken, slackChannelId (no hay perfil local con valores por defecto).
# Compilar sin tests
./mvnw -B -DskipTests clean install
# Compilar con tests
./mvnw clean install
# Ejecutar tests
./mvnw test
# Ejecutar un test concreto
./mvnw test -Dtest=GioApiNotificationsApplicationTests
# Ejecutar la aplicación localmente
./mvnw spring-boot:run
Verificación de que el servicio está operativo: GET /api/check (público, sin autenticación) devuelve true con 200 OK. No hay Actuator configurado.
11. Despliegue
- Imagen: construida con
jib-maven-plugin(baseeclipse-temurin:25-jre,containerizingMode=packaged), publicada eneurope-west3-docker.pkg.dev/pi-saldum/pi-repo/gio-api-notifications:<tag>. - Orquestación: Kubernetes
Deployment(k8s/deployment.yaml, noCronJob) en el clúster GKEpi-cluster-hw(zonaeurope-west3-a, proyectopi-saldum), namespacepi,replicas: 1,restartPolicy: Always. SinlivenessProbe/readinessProbeconfigurados, pese a existir el endpoint/api/checkque podría usarse para ello. - CI/CD (Jenkins): pipeline con 2 etapas —
Checkout→Build & Push(renombraapplication-pro.propertiesaapplication.propertiesantes demvn clean package jib:build) →Deploy to GKE(aplica el manifiesto templado víasedy espera elrollout statusdelDeployment). - Las variables sensibles se inyectan en el pod mediante un
Secretde Kubernetes llamado igual que la app (gio-api-notifications).
Job de Jenkins: https://jenkins-pi.hawkersco.net/job/gio-api-notifications/
12. Manejo de errores y logging
GlobalExceptionHandler (@RestControllerAdvice) intercepta GcsUploadException (lanzada por el controlador cuando falla la subida a GCS), registra el error con Logger.severe, envía una notificación a Slack con el mensaje preformado según el origen (GIO o POS), y devuelve 200 OK con "true" para no propagar el fallo al llamador. Los payloads que no son JSON válido también devuelven 200 OK con "true" sin lanzar excepción ni registrar el error como fallo (solo se registra el payload recibido a nivel INFO). No hay otro manejo de errores adicional (p. ej. para fallos de autenticación, gestionados por defecto por Spring Security con 401).
13. Notas y consideraciones
@EnableSchedulingsin ningún@Scheduled: la clase principal habilita la programación de tareas de Spring, pero no existe ningún método@Scheduleden todo el código fuente del proyecto. Es probable que sea un vestigio de una versión anterior o una plantilla compartida; no tiene efecto funcional pero añade una dependencia/inicialización innecesaria.- Contraseña compartida entre roles distintos:
GioApiNotificationsConfigcrea los usuariosadmin(rolADMIN) ygioxhawkers(rolGIOXHAWKERS) con la misma contraseña (gioapinotifications.encodepass). Cualquier consumidor con esa contraseña puede autenticarse indistintamente como cualquiera de los dos usuarios y, dado que solo se autoriza por rol y no por usuario, cualquiera con la contraseña podría, en principio, invocar ambos endpoints si conociera también el nombre de usuario del otro rol. Sería más seguro usar contraseñas independientes por integración. - Política CORS permisiva a nivel global:
App2ConfigurationAdapter.corsConfigurationSource()registraapplyPermitDefaultValues()para el patrón/**, lo que habilita CORS con orígenes por defecto (*) para todas las rutas de la aplicación, incluidos los endpoints autenticados. Combinado con HTTP Basic Auth, esto no expone credenciales por sí solo, pero amplía la superficie de peticiones cross-origin más de lo que estrictamente necesitan los dos endpoints de notificación. SlackClientConfigmencionada enCLAUDE.mdno existe en este repositorio: elCLAUDE.mddescribe la configuración del cliente Slack como gestionada por una claseSlackClientConfig, pero no hay ninguna clase con ese nombre en el código fuente actual; el beanSlackClientse resuelve mediante la autoconfiguración interna de la libreríaslack-client(patrón@AutoConfiguration+spring.factories), no por una clase local. Puede ser una referencia heredada de otro proyecto de la familia.- Inconsistencia de paquete confirmada:
GioApiNotificationsConstvive en el paqueteutils(sin prefijocom.hawkersco), tal como advierte el propioCLAUDE.md, que la documenta explícitamente como una inconsistencia conocida y aceptada, no un error a corregir. - Sin credenciales hardcodeadas: a diferencia de la mayoría de proyectos de este ecosistema, no existe un
application.propertieslocal con secretos reales — no se emite alerta de seguridad para este proyecto.