Skip to main content

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

CampoValor
artifactIdfalabella-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 (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.
  • .configFalabellaCreateDbConfig (declaración manual de servicios de logistics-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

DependenciaPropósito
spring-boot-starterNúcleo de Spring Boot (sin web, es un runner CLI)
com.hawkersco:falabella-client:1.0.25-SNAPSHOTCliente HTTP (FalabellaClient) para la API de Falabella Seller Center
com.hawkersco:logistics-commons:1.0.25-SNAPSHOTEntidades JPA y servicios de logística compartidos
com.hawkersco:pi-function-commons:1.0.25-SNAPSHOTUtilidades comunes (DateUtils)
com.hawkersco:slack-client:1.0.25-SNAPSHOTDeclarada en el pom.xml; sin uso detectado en el código actual (ver sección 13)
com.hawkersco:labelary-client:1.0.25-SNAPSHOTDeclarada 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

SistemaProtocoloDirecciónDetalle
Falabella Seller Center API (sellercenter-api.falabella.com)HTTP (FalabellaClient, respuesta XML/JSON parseada manualmente con Gson)EntranteGetOrders (paginado, filtro status=delivered) y GetOrderItems por pedido
PostgreSQL (logistics)JDBCEntrante/SalientePersistencia 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).

ClaveDescripciónEjemplo (producción)
falabella.urlURL base de la API de Falabella Seller Center${falabellaUrl}
falabella.userUsuario de autenticación con Falabella${falabellaUser}
falabella.keyAPI key de autenticación con Falabella${falabellaKey}
slack.client.urlURL API Slack (configurada pero sin cliente activo en el código, ver sección 13)${slackClientUrl}
slack.auth.tokenToken bot de Slack (idem)${slackAuthToken}
slack.channel.idCanal de notificaciones (idem)${slackChannelId}
spring.datasource.urlURL JDBC de la BD logistics${dbLogisitcsUrl}
spring.datasource.usernameUsuario de BD${dbLogisitcsUsername}
spring.datasource.passwordContraseñ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:

  1. Rotar la API key de Falabella, la contraseña de BD 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

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:

  1. Pagina falabellaClient.getOrdersString("GetOrders", "delivered", limit=100, offset, "2.0") (deserializado manualmente con Gson y adaptadores tolerantes para Long/BigDecimal), únicamente para pedidos ya en estado delivered (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).
  2. Para cada pedido, si no existe ya (orderService.findBySourceAndCdOrderExternal(77L, externalOrderId)), se ejecuta: saveCustomertransformToOrderLogisticorderService.savesaveInitialShipmentStatusprocessOrderItems (llamada a GetOrderItems) → saveShipment.
  3. 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 (base eclipse-temurin:25-jre, containerizingMode=packaged), publicada en europe-west3-docker.pkg.dev/pi-saldum/pi-repo/falabella-create-db:<tag>.
  • Orquestación: Kubernetes CronJob (k8s/cronjob.yaml) en el clúster GKE pi-cluster-hw (zona europe-west3-a, proyecto pi-saldum), namespace pi, ejecutándose cada 15 minutos.
  • CI/CD (Jenkins): pipeline con 3 etapas — CheckoutBuild & Push (sustituye application-pro.properties por application.properties antes de mvn clean package jib:build) → Deploy to GKE.
  • Las variables sensibles se inyectan en el pod mediante un Secret de 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 NullPointerException en saveCustomer: si falabellaOrder.getAddressShipping() == null, la variable email se queda en cadena vacía (rama if no se ejecuta), pero justo después, dentro del bloque if (customer == null), el código accede incondicionalmente a falabellaOrder.getAddressShipping().getPhone() sin volver a comprobar si addressShipping es null. Si Falabella devuelve un pedido sin dirección de envío, esta línea lanza una NullPointerException que, al no estar capturada localmente, es atrapada por el try/catch global de run() 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 del CronJob, 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 en orderService.findBySourceAndCdOrderExternal. En la siguiente ejecución, el runner lo detectará como "ya existente" y lo omitirá (else log "ya existe en la base de datos"), por lo que ese pedido nunca llegará a tener OrderLine/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: saveOrderLine contiene orderLine.setNmSkuQty(1); //TODO:revisar — la cantidad real del OrderItem de 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 usarse getShopSku() en su lugar; conviene confirmar cuál es el campo correcto según el contrato de Falabella.
  • Dependencias declaradas sin uso: slack-client y labelary-client están en el pom.xml (y slack.* está configurado en ambos perfiles de application.properties) pero no se ha encontrado ningún import ni uso de SlackClient/LabelaryClient en 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()) y order.setCdBillingCountryIso2(billing.getCountry()) asignan directamente el valor devuelto por Falabella sin normalizarlo ni validarlo contra el catálogo interno de países. Solo se usa countryService para fijar un país por defecto ("CO") en la creación del Customer.
  • 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 de decathlon-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.