products-feeds-generate
1. Descripción general
Según el pom.xml, el proyecto se describe como "A service to generate products feeds from the operational products model". Es un servicio híbrido (API REST + tarea programada) que genera y sirve los feeds de Google Shopping en formato XML/PHP para los sitios Hawkers y Northweek. Combina el feed "maestro" (generado por otro proceso y almacenado en GCS) con overrides de campo por SKU y reglas de "columna protegida" almacenadas en base de datos, produciendo una variante -php del feed que se sirve públicamente.
2. Información técnica
| Campo | Valor |
|---|---|
artifactId | products-feeds-generate |
groupId | com.hawkersco |
version | 1.0.25 |
| Java | 25 (maven.compiler.release=25) |
| Spring Boot | 4.0.6 |
| Tipo de artefacto | jar (ejecutable, API web de larga duración) |
| Módulos | No aplica (proyecto de módulo único) |
3. Arquitectura y diseño
API REST con una tarea programada de sincronización horaria.
com.hawkersco.feedsgenerator— clase principal (FeedsGeneratorApplication,@EnableScheduling,@EnableJpaRepositories("com.hawkersco.feedscommons.repository"))..controller—FeedsGeneratorController(lectura de feeds),FeedsGeneratorUpdateController(carga/publicación de feeds),GlobalExceptionHandler..job—FeedsGeneratorCronJob(sincronización horaria XML→PHP)..util—FeedsGeneratorUtil(asignación de valores de campo),GoogleStorage(wrapper de lectura/escritura en GCS)..model—GoogleFeedRss(modelo JAXB del RSS de Google Shopping)..config—FeedsGeneratorConfig.
flowchart TD
A["@Scheduled cada hora<br/>FeedsGeneratorCronJob"] -->|lee XML maestro| B[(GCS feeds/{site}-google-generic/)]
A -->|aplica overrides + columnas protegidas| C[(feeds · FieldFeed / ProtectedColumn)]
A -->|escribe PHP fusionado| D[(GCS feeds/{site}-google-generic-php/)]
E["GET /load-feed/{site}/{country}"] --> D
F["GET /load-feeds/{site}"] --> D
G["POST /publish-feed"] --> D
H["GET /{feed_type}/{locale}"] -->|sirve| D
I["GET /get-feed/{site}/{country}"] -->|lee| D
4. Dependencias principales
| Dependencia | Propósito |
|---|---|
spring-boot-starter-web | Exposición de los endpoints REST |
com.google.cloud:google-cloud-storage:2.68.0 | Lectura/escritura de feeds en GCS |
org.glassfish.jaxb:jaxb-runtime + jakarta.xml.bind-api | (Des)serialización del RSS de Google Shopping (GoogleFeedRss) |
com.hawkersco:feeds-commons:1.0.25-SNAPSHOT | Entidades JPA (FieldFeed, ProtectedColumn) y servicios de la BD feeds |
lombok | Generación de código boilerplate |
spring-boot-devtools (runtime, opcional) | Recarga en caliente en desarrollo |
spring-boot-starter-test (test) | JUnit 5 + Spring Test |
No hay dependencia spring-boot-starter-security en este proyecto (ver alerta de seguridad).
5. API / Endpoints
| Método | Ruta | Descripción |
|---|---|---|
GET | /check-domain | Health check |
GET | /{feed_type}/{locale} | Sirve el feed resolviendo automáticamente las variantes PHP de Hawkers/Northweek |
GET | /{feed_type}/{locale}.xml | Sirve el feed XML por tipo y locale |
GET | /{feed_type}/php/{filename}.php | Sirve el feed PHP por tipo y nombre de fichero |
GET | /load-feed/{site}/{country_iso} | Recarga y fusiona el feed de un país concreto (overrides + columnas protegidas) y lo sube a GCS |
GET | /load-feeds/{site} | Recarga y fusiona todos los feeds de país de un sitio |
POST | /publish-feed | Publica un feed aplicando todos los overrides activos de la BD, excluyendo productos de marca "Hawkers Apparel" o tipo "Eyeglasses" |
GET | /get-all-sku-feed/{site}/{country_iso} | Devuelve la lista de SKUs presentes en el feed PHP |
GET | /get-feed/{site}/{country_iso} | Devuelve el feed PHP completo parseado como JSON |
Ninguno de estos endpoints requiere autenticación (ver alerta de seguridad).
6. Integraciones externas
| Sistema | Protocolo | Dirección | Detalle |
|---|---|---|---|
Google Cloud Storage (bucket pi-logistics-segment) | API de GCS | Entrante/Saliente | Lectura del feed XML maestro y escritura del feed PHP fusionado, por sitio (hawkers, northweek) y país |
PostgreSQL (feeds) | JDBC | Entrante | Lectura de overrides de campo (FieldFeed) y columnas protegidas (ProtectedColumn) |
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) |
|---|---|---|
gcs.bucket.name | Bucket de GCS con los feeds | pi-logistics-segment |
spring.datasource.url / .username / .password | Credenciales de la BD feeds | ${dbFeedsUrl}, etc. |
management.endpoints.web.exposure.include | Endpoints de Actuator expuestos | * (todos, ver alerta de seguridad) |
server.port | Puerto HTTP (8080 en dev, 80 en pro según CLAUDE.md) | — |
⚠️ Alerta de seguridad (severidad alta)
Se han encontrado dos problemas de seguridad relevantes en este proyecto:
- Ningún endpoint requiere autenticación: no hay dependencia
spring-boot-starter-securityen elpom.xml, ni ninguna configuración de seguridad en el código. Los endpointsPOST /publish-feed,GET /load-feed/*yGET /load-feeds/*modifican los feeds de Google Shopping en producción (los mismos que ve Google y, potencialmente, los clientes finales) sin ningún control de acceso — cualquiera con conectividad de red al servicio puede reescribirlos. - Todos los endpoints de Spring Boot Actuator expuestos sin restricción:
management.endpoints.web.exposure.include=*expone todos los endpoints de Actuator (incluyendo, según la versión y configuración del classpath, endpoints potencialmente sensibles como/actuator/envo/actuator/heapdump) sin ninguna capa de seguridad delante. - Adicionalmente,
application.properties(perfil local) contiene la contraseña real de la base de datos PostgreSQLfeedsen texto plano, no reproducida en este documento.
Se recomienda con prioridad alta:
- Añadir autenticación (al menos HTTP Basic, como en otros servicios del ecosistema) a los endpoints de escritura/publicación de feeds.
- Restringir
management.endpoints.web.exposure.includea los endpoints estrictamente necesarios (p. ej.health) y protegerlos si se exponen fuera de la red interna. - Rotar la contraseña de la base de datos
feedsexpuesta enapplication.properties.
8. Persistencia
Base de datos PostgreSQL (feeds), acceso vía JPA a través de la librería feeds-commons. spring.jpa.hibernate.ddl-auto=none. Tablas relevantes: field_feed (overrides de campo por SKU/sitio/país) y protected_column (campos bloqueados frente a overrides, por sitio/país). No hay Flyway/Liquibase en este repositorio.
9. Procesos programados y mensajería
FeedsGeneratorCronJob.getOriginalFeedInfo — @Scheduled(cron = "0 0 * * * ?") (cada hora en punto). Para cada sitio (hawkers, northweek): recorre los feeds XML maestros en GCS, los fusiona con la versión PHP existente (añadiendo ítems nuevos, eliminando los que ya no están en el maestro, excluyendo productos de marca "Hawkers Apparel" o tipo "Eyeglasses"), aplica los overrides de FieldFeed respetando las columnas protegidas de ProtectedColumn, y sube el resultado de vuelta a GCS como fichero -php.
10. Ejecución en local
Requisitos previos: JDK 25, Maven, acceso a la BD feeds y credenciales de aplicación por defecto de Google con acceso al bucket pi-logistics-segment.
# Compilar sin tests
mvn -B -DskipTests clean install
# Ejecutar la aplicación localmente (perfil dev, puerto 8080)
mvn spring-boot:run
# Ejecutar el JAR con perfil de producción
java -Xmx2g -jar target/products-feeds-generate.jar --spring.profiles.active=pro
# Build Docker
docker build -t products-feeds-generate:latest .
Verificación de que el servicio está operativo: GET /check-domain.
11. Despliegue
- Imagen: construida con
jib-maven-plugin(baseeclipse-temurin:25-jre,containerizingMode=packaged), publicada eneurope-west3-docker.pkg.dev/pi-saldum/pi-repo/products-feeds-generate:<tag>. - Orquestación: Kubernetes
Deployment(noCronJob— la periodicidad la gestiona el propio@Scheduledinterno), según elCLAUDE.mdcon 2 réplicas y volumen de cuenta de servicio de GCP montado en/etc/gcp/sa_credentials.json. - CI/CD (Jenkins): pipeline
Maven build → KICS security scan → SonarQube → Docker push → Kubernetes deploy, según el propioCLAUDE.md.
Job de Jenkins: https://jenkins-pi.hawkersco.net/job/products-feeds-generate/
12. Manejo de errores y logging
GlobalExceptionHandler gestiona errores a nivel de controlador (no revisado exhaustivamente en este documento). Tanto los controladores de actualización como el job programado envuelven su lógica principal en un try/catch genérico que registra con Level.SEVERE y devuelve 200 OK con un mensaje de texto plano incluso en caso de error (p. ej. publishFeed siempre responde "Feed published", haya tenido éxito o no internamente) — esto dificulta detectar fallos desde el lado del llamador, ya que la respuesta HTTP no refleja el resultado real de la operación. Logging mediante java.util.logging.Logger estándar (consola).
13. Notas y consideraciones
- Respuestas HTTP que no reflejan el resultado real: varios endpoints (
loadFeed,loadFeeds,publishFeed) devuelven200 OKcon un mensaje fijo de éxito incluso cuando la operación falla internamente (elcatchsolo registra el error en el log). Un llamador automatizado no puede distinguir un éxito real de un fallo silencioso a partir de la respuesta HTTP. - Duplicación de lógica entre
FeedsGeneratorCronJobyFeedsGeneratorUpdateController: la lógica de fusión de campos (recorrido deCOLUMN_LIST, aplicación de overrides respetando columnas protegidas) está prácticamente duplicada entre el job programado y los endpointsloadFeed/loadFeeds, incluyendo constantes repetidas (FEEDS_PREFIX,COLUMN_LIST, etc.) en ambas clases. Podría extraerse a un servicio compartido para reducir el riesgo de que ambas rutas diverjan con el tiempo. - Ver alerta de seguridad en la sección 7: endpoints de escritura sin autenticación, Actuator completamente expuesto, y credencial real de BD en
application.properties.