Skip to main content

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

CampoValor
artifactIdstore-delivery-status-dynamics-create-db
groupIdcom.hawkersco
version1.0.25
Java25
Spring Boot4.0.6
Tipo de artefactojar (ejecutable, Spring Boot batch/CLI)
MódulosNo 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@Component activo, orquesta la lectura de GCS y la triple escritura en BD.
  • StoreDeliveryStatusDynamicsCreateDbLocalRunnerdeshabilitado (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.
  • .utilsStoreDeliveryStatusDynamicsCreateDbUtils (los 3 métodos save* invocados por fichero).
  • .configDynamicsDbConfig (@Primary, BD dynamics-pro), DynamicsDbGoldConfig (BD dynamics-gold), LogisticsDbConfig (BD logistics) — cada uno con su propio DataSource, EntityManagerFactory y TransactionManager.
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

DependenciaPropósito
spring-boot-starterNúcleo de Spring Boot (sin web, es un runner CLI)
spring-webRestClient/@HttpExchange usado por el cliente Slack
com.hawkersco:pi-function-commonsDirectoryUtils
com.hawkersco:dynamics-commonsStatusOrdersDynamics, DAOs/servicios OrderStatusPro/OrderStatusGold
com.hawkersco:logistics-commonsDAOs/servicios Order, Shipment, ShipmentStatusHistory
com.hawkersco:slack-clientNotificaciones de error
com.google.cloud:google-cloud-storage (transitiva, vía StorageOptions)Lectura/escritura de blobs GCS
com.google.code.gson:gsonDeserializació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

SistemaProtocoloDirecciónDetalle
Google Cloud Storage (bucket pi-logistics-segment)API de GCSEntrante/SalienteLectura de eventos pendientes y archivado tras procesar
PostgreSQL (dynamics-pro, dynamics-gold, logistics)JDBC (triple datasource)SalientePersistencia simultánea del estado de entrega en las 3 BD
SlackHTTP (SlackClient)SalienteNotificació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).

ClaveDescripció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.idConfiguració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:

  1. Rotar la contraseña de BD compartida entre las tres bases y el token de Slack.
  2. Sustituir los valores hardcodeados de application.properties por credenciales de un entorno de desarrollo aislado.
  3. 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): entidad OrderStatusPro.
  • dynamics-gold (DynamicsDbGoldConfig): entidad OrderStatusGold.
  • logistics (LogisticsDbConfig): entidades Order, 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 (base eclipse-temurin:25-jre, containerizingMode=packaged), publicada en europe-west3-docker.pkg.dev/pi-saldum/pi-repo/store-delivery-status-dynamics-create-db:<tag>.
  • Orquestación: Kubernetes CronJob en el clúster GKE pi-cluster-hw, namespace pi, contenedor no privilegiado, ejecutándose cada 30 minutos.
  • CI/CD (Jenkins): pipeline real de 3 etapas — CheckoutBuild & PushDeploy to GKE. El CLAUDE.md describe un pipeline con etapas KICS Scan, SonarQube Analysis y Test que no existen en el Jenkinsfile actual (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.md verificado 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 en StoreDeliveryStatusDynamicsCreateDbRunner y StoreDeliveryStatusDynamicsCreateDbUtils.
  • 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.