Skip to main content

Privalia Client

1. Descripción general

privalia-client es una librería cliente reutilizable (JAR) que encapsula la comunicación con la API logística de Privalia, marketplace/proveedor logístico integrado en el ecosistema de microservicios de Hawkers. El proyecto no expone ningún endpoint REST propio; se publica en el registro de artefactos Maven interno y es consumido por otros microservicios que necesiten consultar operaciones, lotes (batches), órdenes de entrega y paquetes, así como generar etiquetas de envío (PDF y Zebra/ZPL) en el sistema de Privalia.

La librería gestiona de forma transparente la autenticación mediante token Bearer (con caché de 30 minutos), de modo que los servicios consumidores no necesitan implementar ninguna lógica de autenticación.

2. Información técnica

PropiedadValor
artifactIdprivalia-client
groupIdcom.hawkersco
version1.0.25-SNAPSHOT
Java25
Spring Boot4.0.6
Tipo de artefactoJAR (librería, no ejecutable)
MódulosProyecto único (no multi-módulo)

3. Arquitectura y diseño

Estructura del proyecto:

com.hawkersco.privaliaclient
├── PrivaliaClientApplication.java # Clase @SpringBootApplication (residual, sin lógica)
├── client/
│ ├── PrivaliaClient.java # Interfaz @HttpExchange con las 11 operaciones de negocio
│ └── PrivaliaTokenClient.java # Interfaz @HttpExchange para /auth/login
├── config/
│ ├── PrivaliaClientConfig.java # @AutoConfiguration principal (interceptor Bearer + caché 30 min)
│ ├── PrivaliaTokenClientConfig.java # @AutoConfiguration del cliente de token
│ ├── PrivaliaCredentialsProperties.java # @ConfigurationProperties record (username/password)
│ ├── PrivaliaClientConst.java # Constantes de cabeceras y content-types
│ └── CacheStore.java # Cache genérica en memoria (Guava)
└── models/
├── PrivaliaTokenRequest.java / PrivaliaTokenResponse.java
├── PrivaliaOperationsResponse.java, PrivaliaBatchesResponse.java
├── PrivaliaDeliveryOrdersResponse.java, PrivaliaParcelResponse.java
└── PrivaliaLabelResponse.java

Flujo principal de autenticación y llamada

sequenceDiagram
participant Consumidor as Microservicio consumidor
participant Client as PrivaliaClient
participant Cache as CacheStore
participant TokenClient as PrivaliaTokenClient
participant API as API Privalia

Consumidor->>Client: getOperations / getBatches / generateLabel / ...
Client->>Cache: get("token")
alt Token en caché (< 30 min)
Cache-->>Client: token válido
else Token ausente o expirado
Client->>TokenClient: getToken(userName, password)
TokenClient->>API: POST /auth/login
API-->>TokenClient: { "access_token": "...", "expires_in": ... }
TokenClient-->>Client: token
Client->>Cache: add("token", token)
end
Client->>API: request + Authorization: Bearer <token> + Content-Type: application/json-patch+json
API-->>Client: respuesta JSON
Client-->>Consumidor: ResponseEntity<T>

PrivaliaTokenClientConfig se activa condicionalmente con @ConditionalOnProperty(prefix = "privalia.credentials", name = "url"), registrando PrivaliaTokenClient con un RestClient sin interceptor de auth (solo cabecera Content-Type: application/json por defecto).

PrivaliaClientConfig (@AutoConfiguration(after = PrivaliaTokenClientConfig.class), misma condición sobre privalia.credentials.url) registra PrivaliaClient con un RestClient cuyo requestInterceptor resuelve el token (desde caché de 30 minutos o pidiendo uno nuevo) e inyecta las cabeceras Authorization: Bearer <token> y Content-Type: application/json-patch+json en cada llamada. Usa @EnableConfigurationProperties(PrivaliaCredentialsProperties.class) para vincular privalia.credentials.token.username/password mediante un record de configuración (PrivaliaCredentialsProperties), en lugar de @Value individuales como en otros clientes del ecosistema.

El registro de ambas autoconfiguraciones se realiza mediante el fichero estándar de Spring Boot:

META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

4. Dependencias principales

DependenciaVersiónPropósito
spring-boot-starter(gestionada SB4)Base de Spring Boot (contexto, autoconfiguración, @ConfigurationProperties)
spring-web(gestionada SB4)RestClient + @HttpExchange / HttpServiceProxyFactory
com.google.guava:guava33.4.0-jreImplementación de caché en CacheStore (CacheBuilder)
com.google.code.gson:gson(gestionada SB4)Anotaciones @SerializedName en modelos de token (soporte dual con Jackson)
com.fasterxml.jackson.core:jackson-annotations(gestionada SB4)Anotaciones @JsonProperty en modelos de token
org.projectlombok:lombok1.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. privalia-client es una librería cliente JAR que no expone endpoints REST propios. Las operaciones que encapsula sobre la API de Privalia se detallan en la sección 6.

6. Integraciones externas

API logística de Privalia

Método clienteHTTPRuta remotaDescripción
getOperationsGET/operationsLista todas las operaciones logísticas disponibles
getParcelsGET/parcels/operation/{operationId}Consulta los paquetes de una operación (respuesta cruda String)
createParcelPOST/parcels/operation/{operationId}/batch/{batchId}Crea un paquete para un lote de una operación
getParcelGET/parcels/operation/{operationId}/batch/{batchId}/deliveryOrder/{deliveryOrder}Consulta los paquetes de una orden de entrega concreta
getBatchesGET/operations/{operationId}/batches?deliveryOrderStatus=AvailableLista los lotes disponibles de una operación
getBatchGET/operations/{operationId}/batches/{batchId}Consulta un lote (respuesta cruda String)
getDeliveryOrdersGET/operations/{operationId}/batches/{batchId}Consulta las órdenes de entrega de un lote (misma ruta que getBatch, tipado)
getDeliveryOrderGET/operations/{operationId}/batches/{batchId}/deliveryOrder/{deliveryOrder}Consulta una orden de entrega concreta
generateLabelPOST/parcels/.../deliveryOrder/{deliveryOrder}/label?labelType=Pdf&labelDpi=203Genera la etiqueta PDF de una orden de entrega
reGenerateLabelPOST/parcels/.../parcel/{parcelId}/label?labelType=Pdf&labelDpi=203Regenera la etiqueta PDF de un paquete concreto
generateLabelZplPOST/parcels/.../deliveryOrder/{deliveryOrder}/label?labelType=ZebraGenera la etiqueta en formato Zebra/ZPL de una orden de entrega
reGenerateLabelZplPOST/parcels/.../parcel/{parcelId}/label?labelType=ZebraRegenera la etiqueta Zebra/ZPL de un paquete concreto

El endpoint de autenticación es POST /auth/login (vía PrivaliaTokenClient), con userName/password en el body JSON.

Ejemplo de respuesta getOperations (PrivaliaOperationsResponse[]):

[
{
"code": "OP-2026-07",
"beginDate": "2026-07-01T00:00:00Z",
"endDate": "2026-07-31T23:59:59Z",
"status": "Active",
"modes": ["Standard"],
"warehouseID": "WH01"
}
]

Ejemplo de respuesta generateLabel/generateLabelZpl (PrivaliaLabelResponse):

{
"id": 123456,
"fileContents": "<contenido en base64>",
"contentType": "application/pdf",
"fileName": "label-123456.pdf"
}

Protocolo: HTTPS REST (JSON, Content-Type: application/json-patch+json en las llamadas de negocio). Autenticación: Bearer token (obtenido dinámicamente vía /auth/login, cacheado 30 minutos).

7. Configuración

No se incluye ningún application.properties/application.yml en la librería. Las propiedades deben ser inyectadas por la aplicación consumidora.

Propiedades requeridas

PropiedadDescripciónEjemplo de valor
privalia.credentials.urlURL base de la API de Privalia (activa ambas autoconfiguraciones)${PRIVALIA_URL}
privalia.credentials.token.usernameUsuario para autenticación en Privalia${PRIVALIA_USERNAME}
privalia.credentials.token.passwordContraseña para autenticación en Privalia${PRIVALIA_PASSWORD}

privalia.credentials.token.username/password se vinculan mediante el record @ConfigurationProperties PrivaliaCredentialsProperties (prefijo privalia.credentials.token).

Importante: Si privalia.credentials.url no está definida, ni PrivaliaClient ni PrivaliaTokenClient se registran (condición @ConditionalOnProperty en ambas autoconfiguraciones).

Variables de entorno recomendadas

VariablePropiedad mapeada
PRIVALIA_URLprivalia.credentials.url
PRIVALIA_USERNAMEprivalia.credentials.token.username
PRIVALIA_PASSWORDprivalia.credentials.token.password

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 Bearer (CacheStore, TTL 30 minutos, backend Guava).

9. Procesos programados y mensajería

No aplica a este proyecto. No existen jobs @Scheduled, listeners de colas/topics ni runners batch.

10. Ejecución en local

privalia-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 clean install -DskipTests

# Compilar con tests
mvn clean install

# Ejecutar un test concreto
mvn test -Dtest=MyTestClass
mvn test -Dtest=MyTestClass#myMethod

Uso como dependencia en un microservicio consumidor

<dependency>
<groupId>com.hawkersco</groupId>
<artifactId>privalia-client</artifactId>
<version>1.0.25-SNAPSHOT</version>
</dependency>

La autoconfiguración se activa automáticamente al declarar privalia.credentials.url (y las credenciales de token) en la aplicación consumidora.

11. Despliegue

El pipeline de Jenkins (Jenkinsfile) consta de dos etapas:

  1. Checkout — descarga el código del repositorio.
  2. Publish to Artifact Registry — ejecuta mvn deploy -DskipTests para publicar el JAR en Google Artifact Registry.

CLAUDE.md documenta un pipeline de tres etapas (Build, SonarQube, Clean), que no se corresponde con el Jenkinsfile actual del repositorio (dos etapas: Checkout y Publish to Artifact Registry). Se documenta el Jenkinsfile realmente presente.

ParámetroValor
JDKJDK25 (tool Jenkins)
MavenMaven3 (tool Jenkins)
Repositorioeurope-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/privalia-client/

12. Manejo de errores y logging

La librería no implementa ninguna estrategia propia de manejo de excepciones ni logging estructurado. Todos los métodos de PrivaliaClient y PrivaliaTokenClient declaran throws RestClientResponseException, propagando al consumidor los errores HTTP devueltos por la API de Privalia.

En resolveToken, si la respuesta de /auth/login no es 2xx o no tiene body, 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 Privalia en vez de fallar explícitamente en el cliente. Mismo patrón observado en hk-timeslogistics-client y meli-client.

No hay configuración de logback ni de niveles de log específicos en la librería.

13. Notas y consideraciones

  • getBatch y getDeliveryOrders comparten exactamente la misma ruta: Ambos métodos apuntan a GET /operations/{operationId}/batches/{batchId}, difiriendo únicamente en el tipo de retorno (ResponseEntity<String> crudo vs. ResponseEntity<PrivaliaDeliveryOrdersResponse> tipado). El consumidor debe elegir el método según si necesita el JSON crudo o el modelo deserializado, pese a ser semánticamente la misma llamada HTTP.

  • Fallo silencioso en resolveToken: Igual que en hk-timeslogistics-client y meli-client, si la autenticación falla, resolveToken devuelve "" en lugar de propagar una excepción, dificultando el diagnóstico de errores de autenticación.

  • PrivaliaCredentialsProperties como record @ConfigurationProperties: A diferencia del resto de clientes del ecosistema (que usan @Value individuales para cada propiedad de credenciales), este proyecto vincula username/password mediante un record anotado con @ConfigurationProperties, habilitado explícitamente con @EnableConfigurationProperties. Patrón más idiomático de Spring Boot pero inconsistente con el resto de clientes documentados.

  • Endpoints de generación/regeneración de etiqueta con nombres asimétricos: generateLabel/generateLabelZpl operan sobre una deliveryOrder, mientras que reGenerateLabel/reGenerateLabelZpl operan sobre un parcelId — la distinción conceptual entre "generar por orden de entrega" y "regenerar por paquete" no está documentada explícitamente en el código; pendiente de verificar contra la documentación oficial de la API de Privalia.

  • labelDpi=203 hardcodeado en generateLabel/reGenerateLabel: El parámetro de resolución de impresión está fijado en la anotación @PostExchange y no es configurable desde el método; cualquier necesidad de otra resolución requeriría modificar el código de la librería.

  • Uso de java.util.Date en modelos: PrivaliaOperationsResponse y PrivaliaBatchesResponse usan java.util.Date en lugar de java.time.LocalDate/LocalDateTime, inconsistente con el resto de clientes del ecosistema que tienden a usar String o tipos de java.time para fechas.

  • Sin tests implementados: No existe directorio src/test/ en el proyecto.

  • PrivaliaClientApplication.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.