store-delivery-status-dynamics-create-db
1. Descripción general
Según el pom.xml, el proyecto se describe como "Store delivery status dynamics create to DB". Es un microservicio batch (runner) que lee ficheros JSON de eventos de estado de entrega (generados por Dynamics 365) desde Google Cloud Storage, y persiste cada evento simultáneamente en tres bases de datos PostgreSQL distintas: dynamics-pro, dynamics-gold y logistics.
2. Información técnica
| Campo | Valor |
|---|---|
artifactId | store-delivery-status-dynamics-create-db |
groupId | com.hawkersco |
version | 1.0.25 |
| Java | 25 |
| Spring Boot | 4.0.6 |
| Tipo de artefacto | jar (ejecutable, Spring Boot batch/CLI) |
| Módulos | No aplica (proyecto de módulo único) |
3. Arquitectura y diseño
No es una API REST: es una aplicación Spring Boot CLI con un único CommandLineRunner activo, más un runner de pruebas locales deshabilitado.
StoreDeliveryStatusDynamicsCreateDbRunner—@Componentactivo, orquesta la lectura de GCS y la triple escritura en BD.StoreDeliveryStatusDynamicsCreateDbLocalRunner— deshabilitado (sin anotación@Component), usado para pruebas locales con un payload JSON hardcodeado; según comentarios del código, se activa comentando el runner principal y descomentando este..utils—StoreDeliveryStatusDynamicsCreateDbUtils(los 3 métodossave*invocados por fichero)..config—DynamicsDbConfig(@Primary, BDdynamics-pro),DynamicsDbGoldConfig(BDdynamics-gold),LogisticsDbConfig(BDlogistics) — cada uno con su propioDataSource,EntityManagerFactoryyTransactionManager.
flowchart TD
A[StoreDeliveryStatusDynamicsCreateDbRunner] -->|list prefix pending| B[(GCS pi-logistics-segment)]
A -->|deserializa Gson| C[StatusOrdersDynamics]
C --> D[(dynamics-pro · OrderStatusPro)]
C --> E[(dynamics-gold · OrderStatusGold)]
C --> F[(logistics · Shipment / ShipmentStatusHistory)]
A -->|copyTo + delete| G[GCS processed_dynamics/{yyyy}/{MM}/{dd}/]
A -.->|error por fichero| H[Slack]
Flujo: pagina los blobs de GCS bajo el prefijo order-dynamics-create-gs/store_delivery_status_pending_dynamics/, filtrando solo los que tienen la profundidad de ruta esperada (3 segmentos); por cada blob, lo descarga, lo deserializa a StatusOrdersDynamics (Gson), y llama secuencialmente a saveStatusDynamicsDbErpGold, saveStatusDynamicsDbErp y saveStatusDynamicsDbLogistic — las dos primeras insertan el estado si no existe ya (countByDsOrderAndDsStatus), la tercera busca el pedido y el envío en logistics, actualiza el estado del envío y registra el historial si es nuevo. Tras procesar con éxito, el blob se copia al prefijo de procesados (particionado por fecha) y se borra del prefijo de pendientes. Cualquier error durante el procesamiento de un fichero se notifica a Slack y no interrumpe el resto del lote.
4. Dependencias principales
| Dependencia | Propósito |
|---|---|
spring-boot-starter | Núcleo de Spring Boot (sin web, es un runner CLI) |
spring-web | RestClient/@HttpExchange usado por el cliente Slack |
com.hawkersco:pi-function-commons | DirectoryUtils |
com.hawkersco:dynamics-commons | StatusOrdersDynamics, DAOs/servicios OrderStatusPro/OrderStatusGold |
com.hawkersco:logistics-commons | DAOs/servicios Order, Shipment, ShipmentStatusHistory |
com.hawkersco:slack-client | Notificaciones de error |
com.google.cloud:google-cloud-storage (transitiva, vía StorageOptions) | Lectura/escritura de blobs GCS |
com.google.code.gson:gson | Deserialización del JSON del evento |
| Lombok (annotation processor) | Generación de código boilerplate |
5. API / Endpoints
No aplica a este proyecto. Es un batch/runner sin capa REST.
6. Integraciones externas
| Sistema | Protocolo | Dirección | Detalle |
|---|---|---|---|
Google Cloud Storage (bucket pi-logistics-segment) | API de GCS | Entrante/Saliente | Lectura de eventos pendientes y archivado tras procesar |
PostgreSQL (dynamics-pro, dynamics-gold, logistics) | JDBC (triple datasource) | Saliente | Persistencia simultánea del estado de entrega en las 3 BD |
| Slack | HTTP (SlackClient) | Saliente | Notificación de errores por fichero |
7. Configuración
En producción (application-pro.properties) las credenciales llegan por variables de entorno inyectadas como Secret de Kubernetes; en local (application.properties) el repositorio contiene actualmente valores reales hardcodeados (ver alerta de seguridad).
| Clave | Descripción |
|---|---|
spring.datasource.* | Credenciales de la BD dynamics-pro (datasource primario) |
dynamicsgold.datasource.* | Credenciales de la BD dynamics-gold |
logistics.datasource.* | Credenciales de la BD logistics |
slack.client.url / .auth.token / .channel.id | Configuración de Slack |
⚠️ Alerta de seguridad
El fichero src/main/resources/application.properties (perfil local) contiene actualmente credenciales reales en texto plano: la misma contraseña de PostgreSQL reutilizada para las tres bases de datos (dynamics-pro, dynamics-gold, logistics — ya señalada como expuesta en múltiples proyectos de este ecosistema), y el token de bot de Slack (el mismo ya señalado en otros proyectos showroom-*). Ninguno de estos valores se ha reproducido en este documento. Se recomienda:
- Rotar la contraseña de BD compartida entre las tres bases y el token de Slack.
- Sustituir los valores hardcodeados de
application.propertiespor credenciales de un entorno de desarrollo aislado. - Revisar el historial de control de versiones, ya que estas credenciales pueden seguir expuestas en commits anteriores.
8. Persistencia
Tres bases de datos PostgreSQL independientes, cada una con su propio EntityManagerFactory/TransactionManager (ddl-auto=none en las tres):
dynamics-pro(DynamicsDbConfig,@Primary): entidadOrderStatusPro.dynamics-gold(DynamicsDbGoldConfig): entidadOrderStatusGold.logistics(LogisticsDbConfig): entidadesOrder,Shipment,ShipmentStatusHistory.
No hay Flyway/Liquibase en este repositorio.
9. Procesos programados y mensajería
No hay @Scheduled ni listeners de colas: la periodicidad la impone el CronJob de Kubernetes (k8s/cronjob.yaml), que ejecuta el contenedor cada 30 minutos (schedule: "*/30 * * * *", concurrencyPolicy: Forbid, activeDeadlineSeconds: 7200). Único runner activo, descrito en la sección 3.
10. Ejecución en local
Requisitos previos: JDK 25, Maven, acceso a las tres BD y credenciales de aplicación por defecto de Google (GCS).
# Compilar sin tests
./mvnw -B -DskipTests clean install
# Ejecutar tests
./mvnw test
# Ejecutar un test concreto
./mvnw test -Dtest=ClassName#methodName
# Ejecutar la aplicación localmente
./mvnw spring-boot:run
Para probar sin GCS: descomentar @Component en StoreDeliveryStatusDynamicsCreateDbLocalRunner y comentarlo en StoreDeliveryStatusDynamicsCreateDbRunner; el runner local usa un payload JSON hardcodeado y solo invoca saveStatusDynamicsDbErp y saveStatusDynamicsDbLogistic (no Gold). Al ser un CommandLineRunner, no expone Actuator/health: la verificación se hace revisando el log de consola o el estado reflejado en las 3 BD.
11. Despliegue
- Imagen: construida con
jib-maven-plugin(baseeclipse-temurin:25-jre,containerizingMode=packaged), publicada eneurope-west3-docker.pkg.dev/pi-saldum/pi-repo/store-delivery-status-dynamics-create-db:<tag>. - Orquestación: Kubernetes
CronJoben el clúster GKEpi-cluster-hw, namespacepi, contenedor no privilegiado, ejecutándose cada 30 minutos. - CI/CD (Jenkins): pipeline real de 3 etapas —
Checkout→Build & Push→Deploy to GKE. ElCLAUDE.mddescribe un pipeline con etapasKICS Scan,SonarQube AnalysisyTestque no existen en elJenkinsfileactual (mismo patrón detectado en varios proyectos hermanos de este lote).
Job de Jenkins: https://jenkins-pi.hawkersco.net/job/store-delivery-status-dynamics-create-db/
12. Manejo de errores y logging
No hay una estrategia de excepciones centralizada (no hay @ControllerAdvice, es un runner). processBlob envuelve la descarga, deserialización, triple persistencia y archivado de cada fichero en un único try/catch genérico: ante cualquier error, registra Level.SEVERE, notifica a Slack y continúa con el siguiente fichero, sin interrumpir el lote completo. Logging mediante java.util.logging.Logger estándar (consola).
13. Notas y consideraciones
CLAUDE.mdverificado y consistente con el código: el patrón de triple datasource (DynamicsDbConfig/DynamicsDbGoldConfig/LogisticsDbConfig), el flujo GCS → deserialización → 3 escrituras → archivado, y el mecanismo de activación del runner local coinciden con el código real, verificado directamente enStoreDeliveryStatusDynamicsCreateDbRunneryStoreDeliveryStatusDynamicsCreateDbUtils.- Pipeline de Jenkins más simple de lo documentado: ver hallazgo en la sección 11.
- Contraseña de BD compartida entre los tres datasources: en el perfil local, las tres cadenas de conexión (
dynamics-pro,dynamics-gold,logistics) usan el mismo usuario y contraseña de PostgreSQL — coherente con que las tres BD residen en la misma instancia (noctua-instance.hawkersco.net), pero implica que una única credencial comprometida da acceso a las tres bases de datos. - Ver alerta de seguridad en la sección 7 sobre credenciales reales expuestas en
application.properties.