Skip to main content

justeat-update-stock

1. Descripción general

Según el pom.xml, el proyecto se describe como "Update Stock JustEat". Es un microservicio batch (runner) que sincroniza la disponibilidad de stock desde Dynamics 365 (ERP) hacia la plataforma JustEat/Flyt, enviando eventos AVAILABLE/UNAVAILABLE por SKU y tienda para ~50 almacenes físicos habilitados como puntos de venta.

2. Información técnica

CampoValor
artifactIdjusteat-update-stock
groupIdcom.hawkersco
version1.0.25
Java25
Spring Boot4.0.6
Tipo de artefactojar (ejecutable, Spring Boot batch/CLI, spring.main.web-application-type=NONE)
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 (JustEatUpdateStockRunner) y un segundo runner deshabilitado.

RunnerEstadoPropósito
JustEatUpdateStockRunner (@Order(1))ActivoEnvía eventos AVAILABLE/UNAVAILABLE a JustEat por tienda y SKU
JusteatCreateMenuRunnerDeshabilitado (@Component comentado)Construcción de menú/carta para una tienda concreta (T348), sin persistir ni enviar nada realmente (ver hallazgo en la sección 13)
  • .configJusteatUpdateStockConfig (beans de servicios de dynamics-commons).
flowchart TD
A[JustEatUpdateStockRunner] -->|carga| B[justeat-total-sku-list.json]
A -->|findDistinctInventoryWarehouseIdByDataAreaId dataAreaId=60| C[(dynamics-pro · ProductDynamics)]
C --> D{almacén en<br/>allowlist de ~50 tiendas?}
D -->|sí| E[Productos con stock ≥ 4]
E -->|match directo + match lente| F[Lista SKUs disponibles]
F -->|AVAILABLE| G[JustEat Flyt API]
F -->|resto del catálogo| H[UNAVAILABLE → JustEat Flyt API]
A -.->|error| I[Slack]

Flujo: carga la lista completa de SKUs gestionados por JustEat desde un recurso JSON del classpath; consulta en Dynamics los almacenes distintos con DataAreaId=60; para cada almacén presente en una allowlist estática de ~50 códigos (formato 60STO###), consulta los productos con stock disponible ≥ 4 unidades, construye la lista de SKUs disponibles combinando coincidencia directa (ItemNumber) y coincidencia de lentes (clave compuesta ItemNumber+ProductStyleId, ≥10 caracteres), envía un evento AVAILABLE con esos SKUs y un evento UNAVAILABLE con el resto del catálogo JustEat no disponible en esa tienda. Cualquier error durante la ejecución completa se notifica a Slack.

4. Dependencias principales

DependenciaPropósito
spring-boot-starterNúcleo de Spring Boot (sin web, es un runner CLI)
org.json:jsonParseo de los ficheros JSON de recursos con las listas de SKU
commons-ioLectura de los recursos JSON del classpath
com.hawkersco:justeat-clientCliente @HttpExchange para la API de JustEat Flyt (itemAvailability)
com.hawkersco:dynamics-commonsProductDynamicsService, entidad ProductDynamics
com.hawkersco:slack-clientNotificaciones de error

5. API / Endpoints

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

6. Integraciones externas

SistemaProtocoloDirecciónDetalle
JustEat Flyt API (api.flytplatform.com)HTTP REST (JustEatClient)SalienteEventos de disponibilidad de stock por tienda/SKU (itemAvailability)
PostgreSQL (dynamics-pro)JDBCEntranteConsulta de almacenes y stock disponible por producto
SlackHTTP (SlackClient)SalienteNotificación de error de la ejecución completa

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
justeat.credentials.url / .keyCredenciales de la API de JustEat Flyt
slack.client.url / .auth.token / .channel.idConfiguración de Slack
spring.main.allow-circular-referencesFijado a true, requerido por la librería interna dynamics-commons

⚠️ 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 dynamics-pro (la misma ya señalada como expuesta en múltiples proyectos de este ecosistema), la clave de la API de JustEat Flyt, y el token de bot de Slack. Ninguno de estos valores se ha reproducido en este documento. Se recomienda:

  1. Rotar la contraseña de BD, la clave de JustEat 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

No hay base de datos propia gestionada por este proyecto: se consulta en modo solo lectura la BD dynamics-pro a través de dynamics-commons (entidad ProductDynamics), sin persistir ningún dato localmente. No hay Flyway/Liquibase.

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 hora (schedule: "0 * * * *", concurrencyPolicy: Forbid). Único runner activo, descrito en la sección 3.

10. Ejecución en local

Requisitos previos: JDK 25, Maven, acceso a la BD dynamics-pro y credenciales válidas de la API de JustEat.

# Compilar
mvn clean install

# Compilar sin tests
mvn -B -DskipTests clean install

# Ejecutar la aplicación localmente
mvn spring-boot:run

# Ejecutar el JAR
java -jar target/justeat-update-stock-1.0.25.jar

No existe directorio /src/test en este repositorio. Al ser un CommandLineRunner, no expone Actuator/health: la verificación se hace revisando el log de consola o el estado de disponibilidad reflejado en el panel de JustEat.

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/justeat-update-stock:<tag>. El CLAUDE.md menciona una imagen base eclipse-temurin:25-jdk-alpine, que no coincide con la configuración real vía Jib (mismo patrón detectado en varios proyectos hermanos).
  • Orquestación: Kubernetes CronJob en el clúster GKE pi-cluster-hw, namespace pi, con credenciales de cuenta de servicio de GCP montadas por volumen (GOOGLE_APPLICATION_CREDENTIALS), sin uso aparente de APIs de Google en el código (mismo patrón detectado en otros proyectos de este lote), ejecutándose cada hora.
  • CI/CD (Jenkins): pipeline real de 3 etapas — CheckoutBuild & PushDeploy to GKE. El CLAUDE.md describe un pipeline con KICS scan, SonarQube 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/justeat-update-stock/

12. Manejo de errores y logging

No hay una estrategia de excepciones centralizada (no hay @ControllerAdvice, es un runner). Todo el run() de JustEatUpdateStockRunner está envuelto en un único try/catch genérico que notifica a Slack y registra Level.SEVERE ante cualquier error, sin distinguir entre fallos de un almacén concreto o de toda la ejecución. Cada llamada individual a la API de JustEat captura RestClientResponseException y notifica a Slack sin interrumpir el resto de tiendas. Logging mediante java.util.logging.Logger estándar (consola).

13. Notas y consideraciones

  • justeat-sku-lens-list.json no se usa realmente para el mapeo de lentes: el CLAUDE.md afirma que las lentes se mapean "vía justeat-sku-lens-list.json", pero JustEatUpdateStockRunner.buildLensSkuList no lee ese fichero en ningún momento — construye la coincidencia de lentes filtrando directamente la lista justEatTotalSkuList (cargada de justeat-total-sku-list.json) por longitud y subcadenas. El fichero justeat-sku-lens-list.json existe físicamente en src/main/resources/ pero no tiene ninguna referencia en el código Java de este proyecto — mismo patrón de recurso huérfano detectado en update-stock (aliexpress-sku.json).
  • JusteatCreateMenuRunner deshabilitado y, además, no completo: aparte de estar desactivado (@Component comentado), su lógica interna nunca añade los Item construidos a ninguna colección del Menu/Category (item se crea y se rellena pero no se agrega a ninguna lista), por lo que aunque se reactivara tal cual, no enviaría ningún ítem de menú real. Su método transform() además lanza IllegalArgumentException ante un formato de almacén inválido, mientras que el método homónimo del runner activo devuelve una cadena vacía — comportamiento inconsistente entre ambas clases, aunque sin impacto actual al estar deshabilitado.
  • Pipeline de Jenkins e imagen base más simples de lo documentado: ver hallazgos en la sección 11.
  • El resto de la arquitectura descrita en CLAUDE.md (allowlist de ~50 tiendas, umbral de stock ≥4, flujo AVAILABLE/UNAVAILABLE) coincide con el código real, verificado directamente en JustEatUpdateStockRunner.
  • Ver alerta de seguridad en la sección 7 sobre credenciales reales expuestas en application.properties.