Skip to main content

Decathlon Client

1. Descripción general

decathlon-client (description del pom.xml: Decathlon Client) es una librería JAR compartida que encapsula la integración con las APIs del marketplace de terceros de Decathlon (programa "third-party seller"). Envuelve los endpoints de pedidos, tracking, ofertas, documentos y facturación 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 expone endpoints propios ni base de datos (DataSourceAutoConfiguration queda excluida, según CLAUDE.md). Su función real es la de auto-configuración de Spring Boot (dos clases @AutoConfiguration, registradas vía META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports) que se activan automáticamente en cualquier microservicio consumidor que declare la dependencia y las propiedades de credenciales correspondientes.

Una particularidad de este cliente frente a otros del ecosistema (coppel-client, cubbo-client) es que modela dos regiones/tenants de Decathlon como clientes independientes: la API estándar (DecathlonClient) y la variante de Australia (DecathlonAuClient), ambas implementando el mismo contrato (DecathlonApiClient) pero apuntando a URLs y credenciales distintas. Además, incluye un tercer componente (DecathlonInvoicesClient) para la subida de documentos de facturación mediante multipart/form-data, implementado directamente con RestClient en lugar de @HttpExchange.

Dentro del ecosistema de microservicios de Hawkers, resuelve el problema de que cada microservicio que sincroniza pedidos, tracking, ofertas y facturación con Decathlon (en ambas regiones) tenga que reimplementar el cliente HTTP y los DTOs de request/response.


2. Información técnica

PropiedadValor
artifactIddecathlon-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; incluye clase @SpringBootApplication de soporte, sin despliegue independiente)
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.decathlonclient
├── client/ # DecathlonApiClient (contrato base), DecathlonClient, DecathlonAuClient,
│ # DecathlonInvoicesClient (RestClient directo, sin @HttpExchange)
├── config/ # DecathlonClientConfig, DecathlonAuClientConfig — @AutoConfiguration
├── dao/ # TrackingDecathlon
├── request/ # DocumentDecathlonRequest
├── response/ # DTOs de respuesta (OrdersDecathlonResponse, OffersDecathlonResponse, ...)
└── DecathlonClientApplication.java # Clase @SpringBootApplication de soporte

Componentes clave

  • client/DecathlonApiClient — interfaz @HttpExchange base con todos los endpoints de negocio (pedidos, tracking, envío, ofertas, documentos, stock). Ambos clientes regionales heredan de esta interfaz sin añadir ni redefinir métodos.
  • client/DecathlonClient / client/DecathlonAuClient — interfaces "marcador" (extends DecathlonApiClient, sin cuerpo) usadas únicamente para diferenciar el bean de Spring inyectado según la región.
  • client/DecathlonInvoicesClient@Component (no @HttpExchange) que construye su propio RestClient en @PostConstruct y realiza la subida multipart de documentos de facturación a /api/document-request/documents/upload, deserializando la respuesta con Gson.
  • config/DecathlonClientConfig@AutoConfiguration, condicionada a decathlon.credentials.{url,key} (@ConditionalOnProperty). Registra el bean DecathlonClient (con HttpComponentsClientHttpRequestFactory, es decir, Apache HttpClient 5 como transporte) y el bean DecathlonInvoicesClient.
  • config/DecathlonAuClientConfig@AutoConfiguration análoga, condicionada a decathlon-au.credentials.{url,key}, registra el bean DecathlonAuClient.

Autenticación

A diferencia de auth0-client/cubbo-client/dafiti-client (token OAuth2 dinámico), aquí el token se inyecta como header estático Authorization con el valor configurado directamente en *.credentials.key, de forma similar a coppel-client:

var restClient = RestClient.builder()
.requestFactory(new HttpComponentsClientHttpRequestFactory())
.baseUrl(baseUrl)
.defaultHeader("Authorization", apiKey)
.defaultHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
.build();

Flujo principal

sequenceDiagram
participant MS as Microservicio consumidor
participant DC as DecathlonClient / DecathlonAuClient (proxy HttpExchange)
participant Decathlon as API Decathlon (estándar / AU)
participant DIC as DecathlonInvoicesClient

MS->>DC: getOrderList(state, startDate, max, offset)
DC->>Decathlon: GET /api/orders + Authorization estático
Decathlon-->>MS: OrdersDecathlonResponse

MS->>DC: updateTracking(orderId, TrackingDecathlon)
DC->>Decathlon: PUT /api/orders/{orderId}/tracking
Decathlon-->>MS: 200 OK

MS->>DIC: uploadDocumentRequest(documentsInput, file)
DIC->>Decathlon: POST /api/document-request/documents/upload (multipart)
Decathlon-->>DIC: JSON con requests/errors
DIC-->>MS: InvoiceDecathlonResponse

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.
org.apache.httpcomponents.client5:httpclient5Transporte HTTP subyacente (HttpComponentsClientHttpRequestFactory) usado por ambos RestClient regionales, en lugar del cliente JDK por defecto.
com.fasterxml.jackson.core:jackson-annotationsAnotaciones @JsonProperty en los DTOs (usadas por RestClient/Jackson en tiempo de ejecución).
com.google.code.gson:gson (2.11.0)Anotaciones @SerializedName en los DTOs y deserialización manual en DecathlonInvoicesClient (new Gson().fromJson(...)).
org.projectlombok:lombok (1.18.38)Generación de getters/setters/constructores/@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 Decathlon (idénticos para DecathlonClient y DecathlonAuClient, al compartir el contrato DecathlonApiClient):

Método HTTPRutaDescripciónRequestResponse
GET/api/ordersLista paginada de pedidos filtrada por estado y fecha de inicio.Query order_state_codes, start_date, max, offsetOrdersDecathlonResponse
GET/api/orders/documents/downloadDescarga un ZIP de documentos para una lista de pedidos.Query order_ids (IDs separados por coma)byte[]
PUT/api/orders/{orderId}/trackingActualiza la información de tracking de un pedido.Path orderId + TrackingDecathlonString (respuesta cruda de Decathlon)
PUT/api/orders/{orderId}/shipMarca un pedido como enviado.Path orderId + Map<String,Object> (body libre)String (respuesta cruda de Decathlon)
GET/api/offersLista paginada de ofertas de producto.Query max, offsetOffersDecathlonResponse
GET/api/offers/{offerId}Detalle de una oferta concreta.Path offerIdString (respuesta cruda de Decathlon)
POST/api/offersCrea/reemplaza ofertas mediante JSON crudo.String (JSON serializado manualmente)String (respuesta cruda de Decathlon)
POST/api/orders/{orderId}/documentsSube un documento adjunto a un pedido (multipart).Path orderId + MultipartFile (files) + String (order_documents)String (respuesta cruda de Decathlon)
GET/api/document-request/requestsSolicitudes de documento filtradas por tipo y estado.Query type, stateDocumentDecathlonResponse
GET/api/document-request/requestsSiguiente página de solicitudes de documento vía token.Query page_tokenDocumentDecathlonResponse
POST/api/offers/stock/importsImporta niveles de stock desde fichero (multipart).MultipartFile (file)String (respuesta cruda de Decathlon)

DecathlonInvoicesClient (fuera del contrato @HttpExchange)

Método HTTPRutaDescripciónRequestResponse
POST/api/document-request/documents/uploadSube un fichero de factura junto a su metadata JSON (multipart).documents_input (JSON string) + files (java.io.File)InvoiceDecathlonResponse

Ejemplo de payload TrackingDecathlon:

{
"carrier_code": "DHL",
"carrier_name": "DHL Express",
"carrier_url": "https://www.dhl.com/tracking",
"tracking_number": "1234567890"
}

Ejemplo de estructura esperada por documents_input en uploadDocumentRequest (según DocumentDecathlonRequest, aunque el método de DecathlonApiClient.getDocumentRequest no lo usa directamente — ver sección 13):

{
"requests": [
{
"request_id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"document_number": "INV-0001",
"issue_date": "2026-07-01",
"due_date": "2026-07-15",
"total_amount_excluding_taxes": 100.00,
"total_tax_amount": 21.00,
"files": [{ "name": "invoice.pdf", "format": "pdf" }]
}
]
}

6. Integraciones externas

SistemaProtocolo / MecanismoDirección del flujo
Decathlon (marketplace, API estándar)HTTP/REST vía RestClient (Apache HttpClient 5) + @HttpExchange, autenticación por header Authorization estáticoSaliente: el microservicio consumidor llama a Decathlon para leer/gestionar pedidos, ofertas, documentos y stock.
Decathlon (marketplace, API Australia)Idéntico al anterior, base URL y credenciales independientes (decathlon-au.credentials.*)Saliente.
Microservicios Hawkers consumidoresDependencia Maven (com.hawkersco:decathlon-client) + auto-configuración Spring BootEntrante como librería: se activa automáticamente al declarar la dependencia y configurar las propiedades de credenciales.

7. Configuración

El proyecto no incluye application.properties/application.yml propio de servicio (es una librería auto-configurable). Los microservicios consumidores deben declarar las siguientes propiedades:

ClaveDescripciónEjemplo de valor
decathlon.credentials.urlURL base de la API estándar de Decathlon (activa DecathlonClientConfig).https://${DECATHLON_API_HOST}
decathlon.credentials.keyHeader Authorization completo para la API estándar.${DECATHLON_API_KEY}
decathlon-au.credentials.urlURL base de la API de Decathlon Australia (activa DecathlonAuClientConfig).https://${DECATHLON_AU_API_HOST}
decathlon-au.credentials.keyHeader Authorization completo para la API de Australia.${DECATHLON_AU_API_KEY}

Ambos pares de propiedades son independientes: un consumidor puede activar solo una región, ambas, o ninguna (en cuyo caso ningún bean de cliente se registra, gracias a @ConditionalOnProperty).

Nunca deben commitearse valores reales de *.credentials.key; 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 (DataSourceAutoConfiguration excluida según CLAUDE.md); todo el estado relevante (pedidos, ofertas, documentos) reside en Decathlon.


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 (DecathlonClientApplication) 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, como en CI):

./mvnw -B -DskipTests clean install

Build con tests:

./mvnw clean install

Ejecutar solo los tests:

./mvnw test

Solo compilar:

./mvnw compile

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:decathlon-client:<version> en su pom.xml y configurar las propiedades descritas en la sección 7 (una o ambas regiones, según necesidad).


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 dos stages (Build vía jenkins/scripts/mvn.sh + Clean vía jenkins/scripts/clean.sh), pero el Jenkinsfile presente en el repositorio ejecuta mvn deploy -DskipTests directamente sin invocar dichos scripts — Pendiente de verificar si los scripts existen en una ubicación no incluida en este repositorio o si la documentación está desactualizada.

Job de Jenkins:

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


12. Manejo de errores y logging

No se ha encontrado estrategia de excepciones propia (no hay @ControllerAdvice, excepciones custom ni códigos de error definidos en el proyecto). Las respuestas de error de la API de Decathlon se propagan como RestClientResponseException estándar de Spring en las llamadas a DecathlonClient/DecathlonAuClient; el consumidor es responsable de capturarlas.

En DecathlonInvoicesClient.uploadDocumentRequest(...), la respuesta cruda se parsea con new Gson().fromJson(response, InvoiceDecathlonResponse.class) sin manejo explícito de JsonSyntaxException: una respuesta no-JSON o con formato inesperado de Decathlon propagaría esta excepción sin envolver hacia el consumidor.

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

  • DecathlonClient y DecathlonAuClient son interfaces vacías: ambas solo existen para que Spring registre proxies HttpServiceProxyFactory distintos (uno por región) a partir del mismo contrato DecathlonApiClient. Al añadir un nuevo endpoint, basta con añadirlo una vez en DecathlonApiClient para que quede disponible en ambas regiones.
  • Transporte HTTP explícito (Apache HttpClient 5): a diferencia de otros clientes del ecosistema que dejan el RestClient con el transporte por defecto de la JDK, aquí ambas configuraciones fijan HttpComponentsClientHttpRequestFactory explícitamente — Pendiente de verificar el motivo (p. ej. necesidad de proxy, timeouts o pool de conexiones específico no configurado en el código revisado).
  • updateOffers y getOffer devuelven String/reciben String crudo: pese a existir OffersDecathlonRequest y OffersDecathlonResponse como DTOs tipados, updateOffers(String) no los usa (recibe un JSON ya serializado) y getOffer devuelve String en lugar de un DTO — inconsistente con getOrderList/getOffers, que sí devuelven tipos concretos.
  • DocumentDecathlonRequest sin cliente HTTP que lo use directamente: este DTO modela el payload esperado por DecathlonInvoicesClient.uploadDocumentRequest(String, File), pero dicho método recibe el JSON ya serializado como String en vez de aceptar el DTO tipado — el consumidor debe serializar DocumentDecathlonRequest manualmente antes de invocar el método.
  • updateShip acepta un Map<String, Object> como cuerpo libre: a diferencia del resto de operaciones de escritura, no hay un DTO tipado para el body de "marcar como enviado", lo que deja la validación del contrato completamente en manos del consumidor.
  • Uso dual de Jackson y Gson en el mismo componente: DecathlonInvoicesClient usa RestClient (Jackson internamente para las partes JSON) pero deserializa la respuesta final con new Gson() de forma manual, en lugar de aprovechar la deserialización automática de RestClient/Jackson — inconsistencia de estilo dentro del propio proyecto.
  • Sin tests: no se ha localizado src/test/java en el proyecto pese a que CLAUDE.md documenta comandos de test (./mvnw test) — Pendiente de verificar si los tests existen en una rama distinta o si la documentación está desactualizada.