falabella-create-db
1. Descripción general
Según el pom.xml, el proyecto se describe como "Falabella Marketplace". Es un microservicio batch (runner) que importa los pedidos del marketplace Falabella (Colombia) que ya están en estado delivered y los persiste como entidades internas de logística (Order, OrderLine, Customer, Shipment, OrderShipmentStatus). Forma parte de la familia de runners de creación de pedidos de marketplace del ecosistema Hawkers.
Este repositorio no contiene un fichero CLAUDE.md, a diferencia de la mayoría de proyectos hermanos.
2. Información técnica
| Campo | Valor |
|---|---|
artifactId | falabella-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 (FalabellaCreateDbRunner, Ordered orden 1) que ejecuta todo el flujo y cierra la JVM al finalizar.
Paquetes principales:
com.hawkersco.falabellacreatedb— clase principal (FalabellaCreateDbApplication) y el runner..config—FalabellaCreateDbConfig(declaración manual de servicios delogistics-commons).
flowchart TD
A[FalabellaCreateDbRunner] -->|GetOrders status=delivered, paginado| B[Falabella Seller Center API]
A -->|findBySourceAndCdOrderExternal| C[(logistics · Order)]
A -->|saveCustomer| D[(logistics · Customer)]
A -->|GetOrderItems| B
A -->|saveOrderLine / saveInitialShipmentStatus / saveShipment| C
FalabellaCreateDbConfig declara manualmente los servicios de logistics-commons usados (OrderService, CustomerService, OrderLineService, ShipmentService, OrderShipmentStatusService, CountryService, CustomerSourceService) como @Bean, ya que no son @Component en la librería externa, junto con el bean PersistenceManagedTypes para el escaneo de com.hawkersco.logisticscommons.dao.
4. Dependencias principales
| Dependencia | Propósito |
|---|---|
spring-boot-starter | Núcleo de Spring Boot (sin web, es un runner CLI) |
com.hawkersco:falabella-client:1.0.25-SNAPSHOT | Cliente HTTP (FalabellaClient) para la API de Falabella Seller Center |
com.hawkersco:logistics-commons:1.0.25-SNAPSHOT | Entidades JPA y servicios de logística compartidos |
com.hawkersco:pi-function-commons:1.0.25-SNAPSHOT | Utilidades comunes (DateUtils) |
com.hawkersco:slack-client:1.0.25-SNAPSHOT | Declarada en el pom.xml; sin uso detectado en el código actual (ver sección 13) |
com.hawkersco:labelary-client:1.0.25-SNAPSHOT | Declarada en el pom.xml; sin uso detectado en el código actual (ver sección 13) |
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
| Sistema | Protocolo | Dirección | Detalle |
|---|---|---|---|
Falabella Seller Center API (sellercenter-api.falabella.com) | HTTP (FalabellaClient, respuesta XML/JSON parseada manualmente con Gson) | Entrante | GetOrders (paginado, filtro status=delivered) y GetOrderItems por pedido |
PostgreSQL (logistics) | JDBC | Entrante/Saliente | Persistencia de pedidos vía logistics-commons |
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 | Ejemplo (producción) |
|---|---|---|
falabella.url | URL base de la API de Falabella Seller Center | ${falabellaUrl} |
falabella.user | Usuario de autenticación con Falabella | ${falabellaUser} |
falabella.key | API key de autenticación con Falabella | ${falabellaKey} |
slack.client.url | URL API Slack (configurada pero sin cliente activo en el código, ver sección 13) | ${slackClientUrl} |
slack.auth.token | Token bot de Slack (idem) | ${slackAuthToken} |
slack.channel.id | Canal de notificaciones (idem) | ${slackChannelId} |
spring.datasource.url | URL JDBC de la BD logistics | ${dbLogisitcsUrl} |
spring.datasource.username | Usuario de BD | ${dbLogisitcsUsername} |
spring.datasource.password | Contraseña de BD | ${dbLogisitcsPassword} |
⚠️ Alerta de seguridad
El fichero src/main/resources/application.properties (perfil local) contiene actualmente credenciales reales en texto plano: usuario y API key de Falabella, 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:
- Rotar la API key de Falabella, la contraseña de BD 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
Base de datos PostgreSQL (logistics), acceso vía JPA a través de la librería logistics-commons (@EnableJpaRepositories("com.hawkersco.logisticscommons.repository")). spring.jpa.hibernate.ddl-auto=none. Entidades relevantes: Order, OrderLine, Customer, CustomerSource, Shipment, OrderShipmentStatus. 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 15 minutos (schedule: "*/15 * * * *", zona horaria Europe/Madrid, concurrencyPolicy: Forbid). Flujo único de FalabellaCreateDbRunner:
- Pagina
falabellaClient.getOrdersString("GetOrders", "delivered", limit=100, offset, "2.0")(deserializado manualmente con Gson y adaptadores tolerantes paraLong/BigDecimal), únicamente para pedidos ya en estadodelivered(el comentario en el propio código enumera el resto de estados posibles:pending,canceled,ready_to_ship,returned,shipped,failed, que este runner no procesa). - Para cada pedido, si no existe ya (
orderService.findBySourceAndCdOrderExternal(77L, externalOrderId)), se ejecuta:saveCustomer→transformToOrderLogistic→orderService.save→saveInitialShipmentStatus→processOrderItems(llamada aGetOrderItems) →saveShipment. - Al terminar toda la paginación, cierra la JVM (
System.exit(SpringApplication.exit(context))).
10. Ejecución en local
Requisitos previos: JDK 25, Maven, acceso a la BD logistics y credenciales válidas de Falabella Seller Center en un application.properties local.
# Compilar sin tests (patrón habitual del ecosistema)
./mvnw -B -DskipTests clean install
# Ejecutar tests
./mvnw test
# Ejecutar la aplicación localmente
./mvnw spring-boot:run
Al ser un CommandLineRunner, no expone Actuator/health: la forma de verificar la ejecución es revisar el log de consola o consultar la tabla orders (fuente idSource=77) tras la ejecución.
11. Despliegue
- Imagen: construida con
jib-maven-plugin(baseeclipse-temurin:25-jre,containerizingMode=packaged), publicada eneurope-west3-docker.pkg.dev/pi-saldum/pi-repo/falabella-create-db:<tag>. - Orquestación: Kubernetes
CronJob(k8s/cronjob.yaml) en el clúster GKEpi-cluster-hw(zonaeurope-west3-a, proyectopi-saldum), namespacepi, ejecutándose cada 15 minutos. - CI/CD (Jenkins): pipeline con 3 etapas —
Checkout→Build & Push(sustituyeapplication-pro.propertiesporapplication.propertiesantes demvn clean package jib:build) →Deploy to GKE. - Las variables sensibles se inyectan en el pod mediante un
Secretde Kubernetes llamado igual que la app (falabella-create-db), incluyendo variables de Slack pese a que el cliente Slack no se usa en el código (ver sección 13).
Job de Jenkins: https://jenkins-pi.hawkersco.net/job/falabella-create-db/
12. Manejo de errores y logging
No hay una estrategia de excepciones centralizada (no hay @ControllerAdvice, es un runner). Todo el bucle de paginación está envuelto en un único try/catch genérico a nivel de run(), que registra la excepción con Level.SEVERE y continúa hasta el final del método (cerrando igualmente la JVM). processOrderItems tiene su propio try/catch interno, por lo que un fallo al recuperar las líneas de un pedido no interrumpe la paginación. Sin embargo, transformToOrderLogistic, orderService.save y saveShipment no están protegidos individualmente: una excepción ahí (p. ej. la NPE descrita en la sección 13) es capturada por el try/catch global, lo que aborta el resto de la paginación de esa ejecución. Logging mediante java.util.logging.Logger estándar (consola), sin notificación a Slack a pesar de que la dependencia y la configuración están presentes.
13. Notas y consideraciones
- Riesgo real de
NullPointerExceptionensaveCustomer: sifalabellaOrder.getAddressShipping() == null, la variableemailse queda en cadena vacía (ramaifno se ejecuta), pero justo después, dentro del bloqueif (customer == null), el código accede incondicionalmente afalabellaOrder.getAddressShipping().getPhone()sin volver a comprobar siaddressShippingesnull. Si Falabella devuelve un pedido sin dirección de envío, esta línea lanza unaNullPointerExceptionque, al no estar capturada localmente, es atrapada por eltry/catchglobal derun()y aborta el resto de la paginación de esa ejecución, dejando sin procesar todos los pedidos restantes de esa pasada (se reintentarán en la siguiente invocación delCronJob, 15 minutos después). - Riesgo de pedidos "huérfanos" sin líneas ni envío: si
orderService.save(order)tiene éxito pero una llamada posterior (p. ej.saveShipment) lanza una excepción no capturada localmente, el pedido ya queda registrado enorderService.findBySourceAndCdOrderExternal. En la siguiente ejecución, el runner lo detectará como "ya existente" y lo omitirá (elselog "ya existe en la base de datos"), por lo que ese pedido nunca llegará a tenerOrderLine/Shipment, sin ninguna alerta que lo señale. - Cantidad de línea de pedido fijada a 1 con TODO explícito en el código:
saveOrderLinecontieneorderLine.setNmSkuQty(1); //TODO:revisar— la cantidad real delOrderItemde Falabella no se usa; todas las líneas se registran con cantidad 1 independientemente de lo que indique el pedido. Es una limitación conocida y marcada como pendiente por el propio equipo de desarrollo. - Ambigüedad de SKU señalada en el propio código:
orderLine.setCdSku(item.getSku()); // O item.getShopSku()— el comentario indica duda sobre si debería usarsegetShopSku()en su lugar; conviene confirmar cuál es el campo correcto según el contrato de Falabella. - Dependencias declaradas sin uso:
slack-clientylabelary-clientestán en elpom.xml(yslack.*está configurado en ambos perfiles deapplication.properties) pero no se ha encontrado ningúnimportni uso deSlackClient/LabelaryClienten el código fuente actual. A diferencia de otros runners de marketplace (decathlon-create-db,eci-create-db), este proyecto no envía alertas a Slack ni genera etiquetas de envío, pese a tener ambas capacidades declaradas como dependencias. - Sin validación de país en las direcciones: a diferencia de otros runners de la familia (que resuelven el país vía
countryService.findByCdCountryIso2(...).get()para billing/shipping), aquíorder.setCdShippingCountryIso2(addr.getCountry())yorder.setCdBillingCountryIso2(billing.getCountry())asignan directamente el valor devuelto por Falabella sin normalizarlo ni validarlo contra el catálogo interno de países. Solo se usacountryServicepara fijar un país por defecto ("CO") en la creación delCustomer. - Estrategia de nombrado de pedido distinta al resto de la familia:
order.setDsOrder("MKFA" + falabellaOrder.getOrderNumber())usa directamente el número de pedido externo de Falabella, sin secuencia interna ni relleno con ceros — a diferencia dedecathlon-create-db/eci-create-db, que generan un nombre interno secuencial independiente del ID externo. - Ver alerta de seguridad en la sección 7 sobre credenciales reales expuestas en
application.properties.