Skip to main content

pickup-point-create-db

1. Descripción general

Según el pom.xml, el proyecto se describe como "Pickup Points create db". Es un microservicio batch (runner) que sincroniza el catálogo de puntos de recogida de dos transportistas — CTT (vía fichero CSV público) y GLS (vía API XML, para 15 países europeos) — con la tabla pickup_point de la base de datos de logística, añadiendo los puntos nuevos y eliminando los que ya no existen en la fuente.

2. Información técnica

CampoValor
artifactIdpickup-point-create-db
groupIdcom.hawkersco
version1.0.25
Java25 (maven.compiler.release=25)
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 dos CommandLineRunner, ambos activos.

  • com.hawkersco.pickuppointcreatedb — clase principal (PickupPointCreateDbApplication) y los dos runners.
  • .configPickupPointCreateDBConfig (bean PickupPointService, PersistenceManagedTypes, cliente GLS).
  • .pojoCTTPoint (POJO mapeado desde columnas del CSV de CTT).
  • .utilsPickupPointCreateDbUtils (marshalling/unmarshalling JAXB de las respuestas XML de GLS).
flowchart TD
A["1. PickupPointCTTCreateDbRunner"] -->|descarga CSV| B[transfer.cttexpress.com]
A -->|diff códigos DB vs CSV| C[(logistics · pickup_point)]

D["2. PickupPointGLSCreateDbRunner"] -->|por cada país + zip existente| E[GLS API XML]
D -->|diff códigos DB vs API| C
D -->|System.exit al terminar| F[Fin del proceso]

Ambos runners siguen el mismo patrón de sincronización: construyen el conjunto de códigos ya presentes en BD, el conjunto de códigos de la fuente externa, calculan la diferencia (Sets.difference de Guava) para saber qué añadir y qué borrar, y aplican ambos cambios.

4. Dependencias principales

DependenciaPropósito
spring-boot-starterNúcleo de Spring Boot (sin web, es un runner CLI)
com.opencsv:opencsv:5.12.0Parseo del CSV de puntos CTT
jakarta.xml.bind:jakarta.xml.bind-api:4.0.1JAXB para (des)serializar el XML de GLS
com.hawkersco:gls-pickup-client:1.0.25-SNAPSHOTCliente @HttpExchange (GlsPickupClient) para la API de puntos de GLS
com.hawkersco:logistics-commons:1.0.25-SNAPSHOTPickupPoint (entidad), PickupPointService
com.hawkersco:pi-function-commons:1.0.25-SNAPSHOTDateUtils, DirectoryUtils
com.hawkersco:slack-client:1.0.25-SNAPSHOTDeclarada en el pom.xml; sin uso detectado en el código actual (ver sección 13)
lombokGeneración de código boilerplate (usada en CTTPoint)
spring-boot-starter-test (test)JUnit 5 + Spring Test

5. API / Endpoints

No aplica a este proyecto. Es un batch/runner sin capa REST.

6. Integraciones externas

SistemaProtocoloDirecciónDetalle
CTT Express (transfer.cttexpress.com/IT/export/integra/PUNTOSCTT.csv)HTTP (descarga directa de CSV)EntranteFichero público con todos los puntos de recogida de CTT
GLS (wsclientes.asmred.com/infoasm.asmx)HTTP (@HttpExchange vía GlsPickupClient, XML)EntranteBúsqueda de puntos de recogida cercanos a un código postal, para 15 países EU
PostgreSQL (logistics)JDBCEntrante/SalienteLectura/escritura de la tabla pickup_point

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ónEjemplo (producción)
spring.datasource.url / .username / .passwordCredenciales de la BD logistics${dbLogisitcsUrl}, etc.
gls.client.urlURL base del servicio web de GLShttps://wsclientes.asmred.com/infoasm.asmx
slack.client.url / .auth.token / .channel.idConfiguración del cliente Slack (declarada, sin uso real, ver sección 13)${slackClientUrl}, etc.

⚠️ Alerta de seguridad

El fichero src/main/resources/application.properties (perfil local) contiene actualmente credenciales reales en texto plano: contraseña de la base de datos PostgreSQL y token de bot de Slack (xoxb-...). Ninguno de estos valores se ha reproducido en este documento. Se recomienda:

  1. Rotar la contraseña de BD y el token de Slack expuestos.
  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

Base de datos PostgreSQL (logistics), acceso vía JPA a través de la librería logistics-commons. spring.jpa.hibernate.ddl-auto=none. Entidad relevante: PickupPoint (misma entidad consultada por pickup-point-api). 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 dos veces al día, a las 08:00 y a las 20:00 (schedule: "0 8,20 * * *"). Se ejecutan en orden los 2 runners:

  1. PickupPointCTTCreateDbRunner (order=1): descarga el CSV público de CTT, lo parsea con OpenCSV, compara los códigos de punto con los ya existentes en BD para el carrier CTT, añade los nuevos y elimina los que ya no aparecen en el CSV.
  2. PickupPointGLSCreateDbRunner (order=2): para cada uno de los 15 países soportados (AT, BE, HR, CZ, DK, FI, FR, DE, HU, IT, LU, NL, PL, SK, SI), obtiene primero los códigos postales ya distintos existentes en BD para ese país (findDistinctZipCodesByCountry) y, si no hay ninguno, omite el país por completo; para cada código postal existente, consulta la API de GLS (radio -1, sin límite) y agrega los puntos devueltos, deduplicando por código. Al terminar todos los países, cierra la JVM.

10. Ejecución en local

Requisitos previos: JDK 25, Maven, acceso a la BD logistics y conectividad hacia CTT/GLS.

# Compilar sin tests
./mvnw clean install -DskipTests

# Compilar con tests
./mvnw clean install

# Ejecutar todos los tests
./mvnw test

# Ejecutar un test concreto
./mvnw test -Dtest=PickupPointCreateDbApplicationTests
./mvnw test -Dtest=PickupPointCreateDbApplicationTests#contextLoads

Al ser un CommandLineRunner, no expone Actuator/health: la forma de verificar la ejecución es revisar el log de consola o consultar el número de registros en pickup_point por carrier tras la ejecución.

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/pickup-point-create-db:<tag>.
  • Orquestación: Kubernetes CronJob (k8s/cronjob.yaml) en el clúster GKE pi-cluster-hw, namespace pi, ejecutándose dos veces al día.
  • CI/CD (Jenkins): pipeline con Build & Push (sustituye application-pro.properties por application.properties) → Deploy to GKE. El CLAUDE.md describe un pipeline más extenso (Build → KICS scan → SonarQube → Test → Docker push → Deploy), no verificado exhaustivamente contra el Jenkinsfile real en este documento.
  • Las variables sensibles se inyectan en el pod mediante un Secret de Kubernetes llamado igual que la app (pickup-point-create-db).

Job de Jenkins: https://jenkins-pi.hawkersco.net/job/pickup-point-create-db/

12. Manejo de errores y logging

No hay una estrategia de excepciones centralizada (no hay @ControllerAdvice, es un runner). PickupPointCTTCreateDbRunner.downloadCsvFile captura IOException y devuelve false si la descarga falla, evitando la sincronización en ese ciclo. PickupPointGLSCreateDbRunner.run() envuelve todo el bucle de países en un único try/catch que registra el error con Level.INFO (no SEVERE/WARNING) y continúa hasta cerrar la JVM. Ningún runner notifica errores por Slack pese a tener la dependencia declarada. Logging mediante java.util.logging.Logger estándar (consola).

13. Notas y consideraciones

  • slack-client declarado pero sin uso: la dependencia está en el pom.xml y el CLAUDE.md la describe como responsable de "notifications", pero no se ha encontrado ninguna llamada a SlackClient en ninguno de los dos runners; todos los errores quedan solo en el log del pod.
  • GLS solo se sincroniza para códigos postales que ya existen en BD: PickupPointGLSCreateDbRunner obtiene primero los códigos postales ya presentes en la tabla pickup_point para cada país (findDistinctZipCodesByCountry) y usa esa lista para consultar la API de GLS; si un país no tiene ningún punto GLS previamente registrado, se omite por completo (if (zipCodes.isEmpty()) return;). Esto significa que añadir GLS como carrier en un país completamente nuevo requeriría una carga inicial manual de al menos un código postal antes de que este runner pueda empezar a descubrir puntos automáticamente — un efecto "huevo y gallina" a tener en cuenta si se amplía la cobertura de países.
  • Nivel de log inconsistente en errores: PickupPointGLSCreateDbRunner registra su error de nivel más alto con Level.INFO en lugar de WARNING/SEVERE, lo que podría hacer que fallos reales de sincronización pasen desapercibidos en sistemas de monitorización que filtran por nivel de log.
  • Ver alerta de seguridad en la sección 7 sobre credenciales reales expuestas en application.properties.