Meli Client
1. Descripción general
meli-client es una librería cliente reutilizable (JAR) que actúa como fachada sobre la API REST de Mercado Libre. El proyecto no expone ningún endpoint REST propio; se publica en el registro de artefactos Maven interno y es consumido por otros microservicios del ecosistema Hawkers que necesiten consultar pedidos, ítems, paquetes y envíos del marketplace Mercado Libre.
Gestiona de forma transparente la autenticación OAuth (obtención y refresco de token, con caché) para el flujo general, y ofrece además un cliente específico para Mercado Libre Colombia (MeliCoClient) que recibe el token explícitamente en cada llamada en lugar de mediante interceptor automático.
2. Información técnica
| Propiedad | Valor |
|---|---|
artifactId | meli-client |
groupId | com.hawkersco |
version | 1.0.25-SNAPSHOT |
| Java | 25 |
| Spring Boot | 4.0.6 |
| Tipo de artefacto | JAR (librería, no ejecutable) |
| Módulos | Proyecto único (no multi-módulo) |
3. Arquitectura y diseño
Estructura del proyecto:
com.hawkersco.meliclient
├── MeliClientApplication.java # Clase @SpringBootApplication (residual, sin lógica)
├── client/
│ ├── MeliClient.java # Interfaz @HttpExchange principal (token vía interceptor)
│ ├── MeliCoClient.java # Interfaz @HttpExchange para Mercado Libre Colombia (token explícito por método)
│ └── MeliTokenClient.java # Interfaz @HttpExchange para obtención/refresco de token OAuth
├── config/
│ ├── MeliClientAutoConfiguration.java # @AutoConfiguration de MeliClient (interceptor + caché de token local)
│ ├── MeliCoClientAutoConfiguration.java # @AutoConfiguration de MeliCoClient (sin interceptor)
│ ├── MeliTokenClientAutoConfiguration.java # @AutoConfiguration de MeliTokenClient
│ ├── CacheStore.java # Caché genérica en memoria (Guava)
│ └── CacheStoreBeans.java # Bean de caché adicional con TTL de 5h (no referenciado por MeliClientAutoConfiguration)
├── model/ # POJOs de request/response (Lombok + Jackson + Gson)
│ ├── MeliTokenResquest.java / MeliTokenResponse.java
│ ├── MeliRefreshTokenResquest.java / MeliRefreshTokenResponse.java
│ ├── MeliOrdersResponse.java, MeliItemResponse.java, MeliPackResponse.java
│ ├── MeliShipmentRequest.java / MeliShipmentResponse.java
│ ├── MeliCoOrderResponse.java # Modelo de respuesta de pedido específico de Mercado Libre Colombia
│ └── MeliNotificationResponse.java # Modelo de notificación webhook de Mercado Libre
└── util/
└── LenientLongTypeAdapter.java # TypeAdapter Gson que tolera valores long con decimales
Flujo principal (MeliClient con token cacheado)
sequenceDiagram
participant Consumidor as Microservicio consumidor
participant MeliClient
participant CacheStore
participant MeliTokenClient
participant API as API Mercado Libre
Consumidor->>MeliClient: getOrders / getItemById / getShipmentsByOrder / ...
MeliClient->>CacheStore: get("token")
alt Token en caché (< 5h)
CacheStore-->>MeliClient: token válido
else Token ausente o expirado
MeliClient->>MeliTokenClient: getToken(grantType, clientId, clientSecret)
MeliTokenClient->>API: POST /oauth/token
API-->>MeliTokenClient: { "access_token": "...", "expires_in": ... }
MeliTokenClient-->>MeliClient: token
MeliClient->>CacheStore: add("token", token)
end
MeliClient->>API: request + header Authorization: Bearer <token>
API-->>MeliClient: respuesta JSON
MeliClient-->>Consumidor: ResponseEntity<T>
MeliClientAutoConfiguration se activa condicionalmente con @ConditionalOnProperty(prefix = "meli.credentials", name = {"url", "granttype", "clientid", "clientsecret"}) y se declara @AutoConfiguration(after = MeliTokenClientAutoConfiguration.class) para garantizar que el bean MeliTokenClient ya exista al construir MeliClient. Crea internamente su propia instancia local de CacheStore<String> (TTL 5 horas) para el token — no reutiliza el bean CacheStore<String> expuesto por CacheStoreBeans.
MeliCoClientAutoConfiguration y MeliTokenClientAutoConfiguration se activan con la condición más laxa @ConditionalOnProperty(prefix = "meli.credentials", name = "url") (solo requieren la URL), y registran sus respectivos RestClient sin ningún interceptor de autenticación: MeliCoClient exige el Authorization como parámetro @RequestHeader explícito en cada método.
Las tres autoconfiguraciones se registran mediante el fichero estándar de Spring Boot:
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
4. Dependencias principales
| Dependencia | Versión | Propósito |
|---|---|---|
spring-boot-starter | (gestionada SB4) | Base de Spring Boot (contexto, autoconfiguración) |
spring-web | (gestionada SB4) | RestClient + @HttpExchange / HttpServiceProxyFactory |
com.google.code.gson:gson | (gestionada SB4) | Anotaciones @SerializedName y TypeAdapter (LenientLongTypeAdapter) |
com.fasterxml.jackson.core:jackson-databind | (gestionada SB4) | Serialización/deserialización Jackson en los modelos |
com.google.guava:guava | 33.4.0-jre | Implementación de caché en CacheStore (CacheBuilder) |
org.projectlombok:lombok | 1.18.42 (dependencia; annotationProcessorPath del compilador usa 1.18.46) | Generación de boilerplate en los modelos |
spring-boot-starter-test | (gestionada SB4) | Testing (scope test) |
5. API / Endpoints
No aplica a este proyecto. meli-client es una librería cliente JAR que no expone endpoints REST propios. Las operaciones que encapsula sobre la API de Mercado Libre se detallan en la sección 6.
6. Integraciones externas
API de Mercado Libre — MeliClient (autenticación automática vía interceptor)
| Método | HTTP | Ruta remota | Descripción |
|---|---|---|---|
getOrders | GET | /orders/search | Busca pedidos de un vendedor filtrados por estado y fecha de creación |
getOrdersWithoutStatus | GET | /orders/search | Busca pedidos de un vendedor filtrados solo por fecha de creación |
getPackById | GET | /packs/{idPack} | Consulta un pack (agrupación de pedidos con envío conjunto) |
getShipmentsByOrder | GET | /orders/{idOrder}/shipments | Consulta el envío asociado a un pedido |
getOrderById | GET | /orders/{idOrder} | Consulta un pedido por su identificador |
getItemById | GET | /items/{idItem} | Consulta un ítem (publicación) por su identificador |
getShipmentsByResource | GET | {resource} | Consulta un envío usando la ruta de recurso recibida en una notificación webhook |
getLabelByShipment | GET | /shipment_labels | Descarga la etiqueta de envío (PDF/ZPL) como recurso binario |
API de Mercado Libre — MeliCoClient (Colombia, token explícito)
| Método | HTTP | Ruta remota | Descripción |
|---|---|---|---|
getOrders | GET | /orders/search | Busca pedidos de un vendedor filtrados por estado y fecha |
getOrderById | GET | /orders/{idOrder} | Consulta un pedido por su identificador |
getItemById | GET | /items/{idItem} | Consulta un ítem por su identificador |
getShipmentsByOrder | GET | /orders/{idOrder}/shipments | Consulta el envío asociado a un pedido |
getLabelByShipment | GET | /shipment_labels | Descarga la etiqueta de envío |
getPackById | GET | /packs/{idPack} | Consulta un pack |
Todos los métodos de MeliCoClient reciben @RequestHeader("Authorization") String token como primer parámetro (valor completo, p. ej. "Bearer <token>") — es responsabilidad del consumidor obtener y renovar ese token.
API de Mercado Libre — MeliTokenClient (OAuth)
| Método | HTTP | Ruta remota | Descripción |
|---|---|---|---|
getToken (client credentials) | POST | /oauth/token | Obtiene un nuevo access token con grant_type, client_id, client_secret |
getToken (refresh) | POST | /oauth/token | Renueva el access token usando un refresh_token |
Ejemplo de respuesta de token (MeliTokenResponse):
{
"access_token": "********",
"token_type": "bearer",
"expires_in": 21600,
"scope": "offline_access read write",
"user_id": 123456789
}
Ejemplo de payload MeliShipmentRequest (modelo de actualización de envío; no está referenciado por ningún método declarado en MeliClient ni MeliCoClient — ver sección 13):
{
"status": "shipped",
"substatus": "in_hub",
"tracking_number": "ABC123456",
"tracking_url": "https://tracking.meli.example/ABC123456",
"payload": { "service_id": 12345, "comment": "En tránsito", "date": "2026-07-10" }
}
Protocolo: HTTPS REST (JSON). Autenticación: OAuth 2.0 (client credentials / refresh token). MeliClient inyecta el Bearer token automáticamente mediante un interceptor con caché de 5 horas; MeliCoClient requiere que el consumidor gestione y pase el token manualmente en cada llamada.
7. Configuración
El fichero src/main/resources/application.properties existe pero está intencionalmente vacío. Las propiedades deben ser inyectadas por la aplicación consumidora.
Propiedades requeridas (prefijo meli.credentials)
| Propiedad | Descripción | Ejemplo de valor |
|---|---|---|
meli.credentials.url | URL base de la API de Mercado Libre (requerida por las tres autoconfiguraciones) | ${MELI_URL} |
meli.credentials.granttype | Grant type OAuth (requerida solo por MeliClientAutoConfiguration) | ${MELI_GRANT_TYPE} |
meli.credentials.clientid | Client ID OAuth (requerida solo por MeliClientAutoConfiguration) | ${MELI_CLIENT_ID} |
meli.credentials.clientsecret | Client secret OAuth (requerida solo por MeliClientAutoConfiguration) | ${MELI_CLIENT_SECRET} |
Importante:
MeliTokenClientyMeliCoClientsolo requierenmeli.credentials.urlpara activarse;MeliClientrequiere ademásgranttype,clientidyclientsecret. Es posible tenerMeliCoClient/MeliTokenClientactivos sin queMeliClientlo esté.
Variables de entorno recomendadas
| Variable | Propiedad mapeada |
|---|---|
MELI_URL | meli.credentials.url |
MELI_GRANT_TYPE | meli.credentials.granttype |
MELI_CLIENT_ID | meli.credentials.clientid |
MELI_CLIENT_SECRET | meli.credentials.clientsecret |
8. Persistencia
No aplica a este proyecto. La librería no accede a ninguna base de datos. El único estado que persiste en memoria es la caché del token OAuth (CacheStore, TTL 5 horas, backend Guava).
9. Procesos programados y mensajería
No aplica a este proyecto. No existen jobs @Scheduled, listeners de colas/topics ni runners batch. MeliNotificationResponse modela el payload de una notificación webhook entrante de Mercado Libre, pero no hay ningún listener/controlador en este proyecto que la reciba — el modelo está pensado para ser usado por el consumidor al parsear notificaciones recibidas en su propio endpoint.
10. Ejecución en local
meli-client es una librería JAR, no una aplicación ejecutable. No tiene servidor embebido ni endpoint de health.
Requisitos previos
- Java 25
- Maven 3.x
- Acceso al registro de artefactos Maven interno (
europe-west3-maven.pkg.dev/pi-saldum/pi-repo-maven) para resolver/publicar dependencias.
Compilar e instalar en repositorio local
# Compilar sin tests
mvn -B -DskipTests clean install
# Compilar con tests
mvn clean install
# Usando el wrapper
./mvnw clean install
Uso como dependencia en un microservicio consumidor
<dependency>
<groupId>com.hawkersco</groupId>
<artifactId>meli-client</artifactId>
<version>1.0.25-SNAPSHOT</version>
</dependency>
La autoconfiguración de cada cliente se activa automáticamente según las propiedades meli.credentials.* declaradas en la aplicación consumidora.
11. Despliegue
El pipeline de Jenkins (Jenkinsfile) consta de dos etapas:
- Checkout — descarga el código del repositorio.
- Publish to Artifact Registry — ejecuta
mvn deploy -DskipTestspara publicar el JAR en Google Artifact Registry.
CLAUDE.md documenta un pipeline con etapas Build y Clean vía scripts (jenkins/scripts/mvn.sh / clean.sh), que no se corresponde con el Jenkinsfile actual del repositorio (dos etapas: Checkout y Publish to Artifact Registry, sin scripts externos). Se documenta el Jenkinsfile realmente presente en el repositorio.
| Parámetro | Valor |
|---|---|
| JDK | JDK25 (tool Jenkins) |
| Maven | Maven3 (tool Jenkins) |
| Repositorio | europe-west3-maven.pkg.dev/pi-saldum/pi-repo-maven (Artifact Registry GCP) |
No existe Dockerfile ni despliegue como servicio independiente; el artefacto es un JAR publicado en el registro Maven.
Job de Jenkins:
https://jenkins-pi.hawkersco.net/job/meli-client/
12. Manejo de errores y logging
La librería no implementa ninguna estrategia propia de manejo de excepciones ni logging estructurado. Las excepciones de red o HTTP propagadas por RestClient (como RestClientResponseException) son responsabilidad del servicio consumidor.
En MeliClientAutoConfiguration.resolveToken, si la respuesta de /oauth/token no es 2xx, no tiene body, o el access_token viene vacío, el método devuelve una cadena vacía ("") en lugar de lanzar una excepción — las llamadas posteriores se autenticarían con Authorization: Bearer (token vacío), fallando con un error HTTP del lado de Mercado Libre en vez de fallar explícitamente en el cliente.
No hay configuración de logback ni de niveles de log específicos en la librería.
13. Notas y consideraciones
-
CacheStoreBeans.token()no utilizado porMeliClientAutoConfiguration:CacheStoreBeansexpone un beanCacheStore<String>con TTL de 5 horas destinado (según su Javadoc) a ser "compartido" para el token OAuth, peroMeliClientAutoConfigurationcrea su propia instancia local deCacheStore<String>en el métodomeliClient(...)en lugar de inyectar el bean deCacheStoreBeans. El bean expuesto queda disponible en el contexto Spring pero no es consumido por ningún componente de esta librería — mismo patrón de caché "huérfana" observado enauro-client. -
Comentario de
CacheStoreBeansdesactualizado: El Javadoc deCacheStoreBeans.token()menciona "Feign interceptors", pero el proyecto no usa Feign en ningún punto (usa@HttpExchange+RestClient), consistente con la discrepancia general entre partes de la documentación heredada y el código actual. -
MeliShipmentRequestsin método de cliente asociado: El modeloMeliShipmentRequest(payload para actualizar estado/tracking de un envío) no es referenciado por ningún método deMeliClientniMeliCoClient— ninguna de las dos interfaces declara unPostExchange/PutExchangede envíos. Pendiente de verificar si esta operación se implementa en otro punto no incluido en este repositorio o si el modelo es un remanente. -
Fallo silencioso en
resolveToken: Igual que en otros clientes del ecosistema (hk-timeslogistics-client), si la autenticación falla,MeliClientAutoConfiguration.resolveTokendevuelve""en lugar de propagar una excepción, dificultando el diagnóstico de errores de autenticación. -
Dos estrategias de autenticación incompatibles conviviendo en el mismo JAR:
MeliClientresuelve el token automáticamente vía interceptor y caché;MeliCoClientdelega esa responsabilidad completamente en el consumidor (token como parámetro explícito). Un desarrollador que use ambos clientes debe tener presente que su comportamiento respecto a la autenticación es distinto, pese a exponer operaciones equivalentes (ambos tienengetOrders,getOrderById,getItemById, etc. con firmas casi idénticas salvo el parámetrotoken). -
LenientLongTypeAdapter: Utilidad Gson que trunca valores decimales (p. ej.40166.67) alongal deserializar. Su Javadoc indica que debe registrarse manualmente en unGsonBuilderpor el consumidor antes de parsearMeliCoOrderResponse; no se aplica automáticamente en ningún punto de esta librería. Existe presumiblemente porque la API de Mercado Libre Colombia devuelve, en algunos campos numéricos declarados comolongen los POJOs, valores con parte decimal. -
Versión de Lombok inconsistente: El
pom.xmlfijaorg.projectlombok:lomboken1.18.42, mientras que elannotationProcessorPathdel compilador usa1.18.46— misma inconsistencia observada enlogisfashion-client. -
Duplicación de modelos entre
MeliCoOrderResponseyMeliOrdersResponse/MeliShipmentResponse:MeliCoOrderResponsereimplementa una jerarquía de clases (Shipment,Order,Payment,Item, direcciones, etc.) con una estructura muy similar a la de los modelos genéricos (MeliOrdersResponse,MeliShipmentResponse), en lugar de reutilizarlos. Refleja que la respuesta de Mercado Libre Colombia difiere lo suficiente del formato genérico como para justificar un modelo propio, pero incrementa la superficie de mantenimiento. -
Sin tests implementados: No existe directorio
src/test/en el proyecto, consistente con lo indicado enCLAUDE.md. -
MeliClientApplication.java: Clase principal de Spring Boot en el paquete raíz, sin funcionalidad operativa. Artefacto residual de la generación inicial del proyecto con Spring Initializr, mismo patrón observado en otros clientes del ecosistema.