Skip to main content

Cubbo Client

1. Descripción general

cubbo-client (description del pom.xml: cubbo-client test project) es una librería JAR compartida que encapsula la integración con la API logística de Cubbo (operador logístico, también conocido como 99Minutos). Envuelve los endpoints de autenticación, productos, inventario y pedidos usando los clientes HTTP declarativos nativos de Spring (@HttpExchange + HttpServiceProxyFactory, Spring Framework 7), sin depender de Spring Cloud OpenFeign.

El proyecto no es un microservicio desplegable de cara a negocio: no tiene capa web, base de datos ni endpoints REST propios. Su función real es la de auto-configuración de Spring Boot (@AutoConfiguration, registrada vía META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports) que se activa automáticamente en cualquier microservicio consumidor que declare la dependencia y las propiedades cubbo.client.*.

Resuelve el problema de que cada microservicio de Hawkers que necesita crear/consultar/cancelar pedidos y gestionar productos e inventario en Cubbo tenga que reimplementar el cliente HTTP, la autenticación OAuth2 (client_credentials) con caché de token, y los DTOs de request/response. Dentro del ecosistema de microservicios de Hawkers, actúa como capa de integración transversal con este operador logístico, de forma análoga a otros clientes similares (auro-client, coppel-client, etc.).

Nota: el description del pom.xml (cubbo-client test project) sugiere un origen o nombre provisional del proyecto; el <name> declarado es cubbo-client- (con guion final).


2. Información técnica

PropiedadValor
artifactIdcubbo-client
groupIdcom.hawkersco
version1.0.25-SNAPSHOT
Java25
Spring Boot4.0.6 (Spring Framework 7)
Tipo de artefactoJAR (librería con auto-configuración Spring Boot; sin capa web, sin @RestController)
MódulosProyecto mono-módulo
Repositorio MavenGoogle Artifact Registry — europe-west3-maven.pkg.dev/pi-saldum/pi-repo-maven

3. Arquitectura y diseño

Paquetes principales

com.hawkersco.cubboclient
├── client/ # Interfaces @HttpExchange — CubboClient (API negocio), CubboTokenClient (auth)
├── config/ # CubboClientAutoConfiguration, CacheStore (Guava), CubboClientConst
├── pojo/ # DTOs Lombok con anotaciones dobles Jackson/Gson
└── CubboClientApplication.java # Clase @SpringBootApplication de soporte

Componentes clave

  • client/CubboClient — interfaz @HttpExchange con las operaciones de negocio: creación de productos, creación de pedidos (estándar y marketplace), cancelación/borrado de pedidos, consulta de inventario y consulta de pedido por número.
  • client/CubboTokenClient — interfaz @HttpExchange para /v1/auth/token; de uso interno exclusivo de CubboClientAutoConfiguration.
  • config/CubboClientAutoConfiguration — clase @AutoConfiguration, condicionada a que existan las propiedades cubbo.client.{url,clientid.prod,clientsecret.prod} (@ConditionalOnProperty). Registra los beans CubboTokenClient y CubboClient, este último con un ClientHttpRequestInterceptor que resuelve el token Bearer en cada petición.
  • config/CacheStore<T> — envoltorio ligero sobre com.google.common.cache.Cache (Guava) con expiración configurable (expireAfterWrite); usado para cachear el token de acceso durante 45 minutos.
  • config/CubboClientConst — clase de constantes, actualmente solo TXT_TOKEN = "token" (clave de caché).
  • config/CubboClientConfig — clase marcada @Deprecated, vacía, reemplazada por CubboClientAutoConfiguration (ver sección 13).
  • pojo/ — DTOs Lombok (@Getter/@Setter/@Data según la clase) con anotaciones dobles @SerializedName (Gson) y @JsonProperty (Jackson).

Patrón de autenticación con caché

resolveToken(...) primero consulta la caché Guava (CacheStore); si no hay token vigente, lo solicita a /v1/auth/token vía CubboTokenClient y lo almacena con TTL de 45 minutos:

private String resolveToken(CubboTokenClient tokenClient) {
String token = cache.get(CubboClientConst.TXT_TOKEN);
if (token != null) {
return token;
}
CubboTokenRequest request = new CubboTokenRequest();
request.setClientID(clientId);
request.setClientSecret(clientSecret);
ResponseEntity<CubboTokenResponse> response = tokenClient.getToken(request);
if (response.getStatusCode().is2xxSuccessful() && response.getBody() != null) {
token = response.getBody().getToken();
cache.add(CubboClientConst.TXT_TOKEN, token);
}
return token != null ? token : "";
}

A diferencia de coppel-client (token estático) y de forma similar a auth0-client (aunque este último no cachea), cubbo-client sí implementa reutilización de token entre peticiones dentro de la ventana de 45 minutos, reduciendo llamadas a /v1/auth/token.

Flujo principal

sequenceDiagram
participant MS as Microservicio consumidor
participant CC as CubboClient (proxy HttpExchange)
participant Cache as CacheStore (Guava, TTL 45 min)
participant CTC as CubboTokenClient
participant Cubbo as API Cubbo

MS->>CC: createOrder(...) / getOrder(...) / ...
CC->>Cache: get("token")
alt Token en caché
Cache-->>CC: token vigente
else Sin token o expirado
CC->>CTC: getToken(clientId, clientSecret)
CTC->>Cubbo: POST /v1/auth/token
Cubbo-->>CTC: token + expires_in
CC->>Cache: add("token", token)
end
CC->>Cubbo: petición real (GET/POST /v1/...) + Authorization: Bearer <token>
Cubbo-->>CC: respuesta
CC-->>MS: ResponseEntity<...>

4. Dependencias principales

DependenciaPropósito
org.springframework.boot:spring-boot-starterNúcleo de Spring Boot (auto-configuración, contexto).
org.springframework:spring-webRestClient, HttpServiceProxyFactory y soporte @HttpExchange.
com.google.guava:guava (33.4.0-jre)Cache/CacheBuilder usado por CacheStore para el TTL del token.
org.json:json (20240303)Utilidades JSON adicionales — Pendiente de verificar su uso concreto (no localizado en las clases revisadas).
com.google.code.gson:gsonAnotaciones @SerializedName en los DTOs (uso dual junto a Jackson).
com.fasterxml.jackson.core:jackson-databindDeserialización de las respuestas de la API de Cubbo y anotaciones @JsonProperty.
org.projectlombok:lombok (1.18.42)Generación de getters/setters/constructores/toString/@Data en los DTOs.
org.springframework.boot:spring-boot-starter-test (test)Soporte de test de Spring Boot.

Extensión de build relevante: com.google.cloud.artifactregistry:artifactregistry-maven-wagon (2.2.1) — necesaria para publicar/resolver contra el Google Artifact Registry corporativo.


5. API / Endpoints

No expone API REST propia (no hay @RestController). En su lugar, define clientes HTTP declarativos que consumen la API de Cubbo:

CubboClient (operaciones de negocio)

Método HTTPRutaDescripciónRequestResponse
POST/v1/productsCrea un producto en el catálogo de Cubbo.Product (JSON)Product
POST/v1/ordersCrea un pedido estándar (createOrder).OrderCubboRequest (JSON)OrderCubboRequest
POST/v1/ordersCrea un pedido de canal marketplace (createOrderMp), incluye documentos de envío y datos de canal.OrderCubboMpRequest (JSON)OrderCubboRequest
POST/v1/orders/cancelCancela un pedido.Query store_id, orderNameString (respuesta cruda de Cubbo)
POST/v1/orders/deleteElimina un pedido.Query store_id, orderNameString (respuesta cruda de Cubbo)
GET/v1/inventoryConsulta stock por lista de SKUs.Query store_id, skus (lista)CubboProduct (paginado, con Meta)
GET/v1/ordersConsulta un pedido por número.Query store_id, order_numberOrderCubboResponse (paginado, con Meta)

CubboTokenClient (uso interno)

Método HTTPRutaDescripciónRequestResponse
POST/v1/auth/tokenObtiene el token de acceso OAuth2.CubboTokenRequest (client_id, client_secret)CubboTokenResponse (token, expires_in, type)

Ejemplo de payload OrderCubboRequest:

{
"store_id": "12345",
"order_number": "ORD-0001",
"shipping_line_code": "STANDARD",
"products": [
{ "sku": "SKU-001", "quantity": 2 }
],
"shipping": {
"first_name": "Nombre",
"last_name": "Apellido",
"phone": "600000000",
"email": "cliente@ejemplo.com",
"address1": "Calle Ejemplo 1",
"address2": "",
"country": "ES",
"city": "Madrid",
"province": "Madrid",
"zip_code": "28001"
}
}

OrderCubboResponse.Order es el DTO de mayor complejidad del proyecto: incluye estado del pedido, tracking (deliveryTracking), método de envío, líneas de pedido (orderLines) e integración de canal (channelIntegration), entre otros campos anidados.


6. Integraciones externas

SistemaProtocolo / MecanismoDirección del flujo
Cubbo / 99Minutos (operador logístico)HTTP/REST vía RestClient + @HttpExchange (application/json), autenticación OAuth2 client_credentials con token cacheado 45 minSaliente: el microservicio consumidor llama a Cubbo para crear/consultar/cancelar pedidos y gestionar productos/inventario.
Microservicios Hawkers consumidoresDependencia Maven (com.hawkersco:cubbo-client) + auto-configuración Spring BootEntrante como librería: se activa automáticamente al declarar la dependencia y configurar cubbo.client.*.

7. Configuración

El fichero src/main/resources/application.properties del proyecto está vacío. Los microservicios consumidores deben declarar las siguientes propiedades para que CubboClientAutoConfiguration se active (@ConditionalOnProperty exige las tres):

ClaveDescripciónEjemplo de valor
cubbo.client.urlURL base de la API de Cubbo.https://${CUBBO_API_HOST}
cubbo.client.clientid.prodClient ID OAuth2 para autenticación client_credentials.${CUBBO_CLIENT_ID}
cubbo.client.clientsecret.prodClient Secret OAuth2 para autenticación client_credentials.${CUBBO_CLIENT_SECRET}

Nunca deben commitearse valores reales de clientid.prod/clientsecret.prod; se recomienda inyectarlos vía variables de entorno o gestor de secretos del entorno de despliegue.


8. Persistencia

No aplica. La librería no accede a ninguna base de datos; todo el estado relevante (productos, inventario, pedidos) reside en Cubbo. La única forma de "estado" en memoria es la caché Guava del token de acceso (CacheStore), no persistente entre reinicios.


9. Procesos programados y mensajería

No aplica. No se han encontrado @Scheduled, @KafkaListener ni @RabbitListener en el proyecto.


10. Ejecución en local

Como librería auto-configurable, no se "ejecuta" de forma independiente en producción, aunque incluye una clase @SpringBootApplication (CubboClientApplication) para pruebas locales del contexto de auto-configuración.

Requisitos previos: JDK 25, Maven (o el wrapper ./mvnw incluido).

Build e instalación en repositorio Maven local (omitiendo tests):

./mvnw clean install -DskipTests

Build con tests:

./mvnw clean package

Ejecutar todos los tests:

./mvnw test

Ejecutar una clase de test concreta:

./mvnw test -Dtest=CubboClientApplicationTests

No aplica verificación vía Actuator/health al no ser un servicio desplegable de cara a producción. Los microservicios consumidores deben declarar com.hawkersco:cubbo-client:<version> en su pom.xml y configurar las propiedades cubbo.client.* descritas en la sección 7.


11. Despliegue

El despliegue consiste en la publicación del artefacto Maven al Google Artifact Registry corporativo (no hay despliegue de contenedor/servicio propio).

Pipeline Jenkins (Jenkinsfile, agente any, JDK25 + Maven3):

  1. Checkout del repositorio.
  2. Publish to Artifact Registry: mvn deploy -DskipTests, publicando en artifactregistry://europe-west3-maven.pkg.dev/pi-saldum/pi-repo-maven (definido en distributionManagement del pom.xml).

CLAUDE.md documenta un pipeline de tres stages (Maven build → KICS security scan → SonarQube), pero el Jenkinsfile presente en el repositorio solo define los stages de Checkout y publicación — Pendiente de verificar si los stages adicionales se gestionan en una shared library de Jenkins no visible en este repositorio.

Job de Jenkins:

https://jenkins-pi.hawkersco.net/job/cubbo-client/


12. Manejo de errores y logging

Los métodos de CubboClient y CubboTokenClient declaran explícitamente throws RestClientResponseException, dejando la gestión de errores HTTP de la API de Cubbo a cargo del microservicio consumidor; no se capturan ni envuelven internamente.

En resolveToken(...) (CubboClientAutoConfiguration), si la respuesta del token no es 2xx o el cuerpo es null, el método devuelve una cadena vacía ("") sin lanzar excepción — una petición posterior a CubboClient se realizaría entonces con Authorization: Bearer vacío, previsiblemente resultando en un error de autenticación de Cubbo en lugar de un fallo temprano explícito.

No se ha encontrado configuración de logging propia (logback.xml/log4j2.xml); el logging queda delegado a la configuración del microservicio que integra la librería.


13. Notas y consideraciones

  • Clase CubboClientConfig obsoleta y vacía: config/CubboClientConfig está marcada @Deprecated y no contiene ningún miembro (class CubboClientConfig {}), con el Javadoc @deprecated Replaced by {@link CubboClientAutoConfiguration}. Es candidata a eliminación si no hay código externo que dependa de su presencia (paquete-privada, por lo que no debería ser referenciada fuera de este módulo).
  • Fallo silencioso en la obtención de token: al igual que en auth0-client, si resolveToken(...) no logra obtener un token válido, se usa "" en vez de propagar la excepción, retrasando la detección del error hasta la respuesta de la API de Cubbo.
  • Dos métodos de creación de pedido con tipos distintos: createOrder (con OrderCubboRequest) y createOrderMp (con OrderCubboMpRequest, que añade shipping_method_id, channel_name, channel_order_id y shipping_documents) apuntan al mismo endpoint POST /v1/orders pero con payloads distintos — a diferencia de coppel-client, aquí sí hay diferencia de contrato real entre ambos métodos, correspondiente a pedidos de canal propio vs. marketplace.
  • Dependencia org.json:json sin uso localizado: declarada en el pom.xml pero no se ha encontrado ninguna clase del proyecto que la importe explícitamente entre los ficheros revisados — Pendiente de verificar si se usa en tests o si es una dependencia residual.
  • StatusOrderCubbo sin cliente HTTP asociado: este DTO (estado de pedido con tracking) no aparece referenciado por ningún método de CubboClient — su propósito parece ser modelar un payload de evento/webhook recibido desde fuera de esta librería (análogo a StatusOrdersDynamics en dynamics-commons), pero no hay un @HttpExchange ni listener que lo consuma dentro de este proyecto (Pendiente de verificar su punto de uso real, previsiblemente en un microservicio consumidor).
  • Campos Object en DTOs de respuesta: numerosos campos de OrderCubboResponse y CubboProduct (billingName, isDropshipping, freshness, price, weight, pricePerItem, name en OrderLine, etc.) están tipados como Object, obligando al consumidor a inspeccionar/castear manualmente su contenido real devuelto por la API de Cubbo.