LogSolution Client
1. Descripción general
logsolution-client es una librería cliente reutilizable (JAR) que encapsula la comunicación con el servicio web SOAP de LogSolution, operador logístico de terceros (3PL) italiano 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 crear/consultar pedidos, artículos, tiendas y envíos en el sistema de LogSolution.
Expone siete operaciones SOAP: consulta de versión del servicio, creación y consulta de pedidos, consulta y creación de artículos, consulta de tiendas y consulta de envíos/tracking.
2. Información técnica
| Propiedad | Valor |
|---|---|
artifactId | logsolution-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.logsolutionclient
├── LogSolutionClientApplication.java # Clase @SpringBootApplication (residual, sin lógica)
├── client/
│ └── LogSolutionClient.java # Interfaz @HttpExchange con las 7 operaciones SOAP
├── config/
│ └── LogSolutionAutoConfiguration.java # @AutoConfiguration principal
└── pojo/
├── GetVersionRequest.java # Envelope SOAP para GetVersion
├── CreateOrderRequest.java # Envelope SOAP para CreateOrder (jerarquía completa: Delivery, Details, Events)
├── GetOrderRequest.java # Envelope SOAP para GetOrders (plural)
├── GetArticlesRequest.java / GetArticlesResponse.java # Envelope SOAP para GetArticles (request/response)
├── CreateArticleRequest.java # Envelope SOAP para CreateArticle
├── GetStoresRequest.java # Envelope SOAP para GetStores
├── GetShipmentRequest.java / GetShipmentRespons.java # Envelope SOAP para GetShipment (request/response, "Respons" sin la 'e' final)
├── ArticleModel.java # Modelo de artículo simplificado (no anotado con JAXB)
├── StatusOrderLogsolutions.java # Modelo de estado de envío deserializado con Gson (JSON, no XML)
├── SkuCsv.java # Bean OpenCSV para importación masiva de SKUs
└── OrderInternationalTest.java # Bean OpenCSV para datos de pedido internacional (a pesar del nombre, es un modelo de datos, no una clase de test)
Flujo principal
sequenceDiagram
participant Consumidor as Microservicio consumidor
participant Client as LogSolutionClient
participant API as Servicio SOAP LogSolution
Consumidor->>Client: createOrder(xmlBody) / getArticles(xmlBody) / ...
Client->>API: POST /LogisticService.svc?wsdl&Action=<Accion> + header SOAPAction fijo
API-->>Client: ResponseEntity<String> (XML crudo)
Client-->>Consumidor: ResponseEntity<String>
La autoconfiguración (LogSolutionAutoConfiguration) se activa condicionalmente con @ConditionalOnProperty(prefix = "logsolution.auth-logistic", name = "url"), registrando un único bean LogSolutionClient con un RestClient simple (sin interceptores) apuntando a logsolution.auth-logistic.client.url.
El registro de la autoconfiguración se realiza mediante el fichero estándar de Spring Boot:
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
Cada método de LogSolutionClient fija su propia URL relativa (incluyendo el parámetro Action en la query string) y su propia cabecera SOAPAction de forma estática en la anotación @PostExchange, en lugar de resolverse dinámicamente mediante un interceptor. El cuerpo XML completo (incluyendo el token de autenticación embebido en Authentication > Token) se construye por el consumidor a partir de los POJOs JAXB del paquete pojo/ y se pasa como String al método correspondiente.
Nota sobre la documentación previa del proyecto: el CLAUDE.md de este repositorio describe una arquitectura basada en OpenFeign con un RequestInterceptor (LogSolutionClientConfig) que resolvería dinámicamente la cabecera SOAPAction parseando la URL. Esa arquitectura no se corresponde con el código fuente actual: no existe ninguna dependencia de Feign en el pom.xml, la clase de configuración se llama LogSolutionAutoConfiguration (no LogSolutionClientConfig) y usa @HttpExchange/RestClient sin ningún interceptor, con las cabeceras SOAPAction fijadas de forma estática por método. Este documento describe el comportamiento observado en el código, no el descrito en CLAUDE.md.
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 |
jakarta.xml.bind:jakarta.xml.bind-api | 4.0.4 | Anotaciones JAXB para el mapeo XML/SOAP de los envelopes de request/response |
com.opencsv:opencsv | 5.12.0 | Lectura/escritura de ficheros CSV para importación masiva (SkuCsv, OrderInternationalTest) |
com.google.code.gson:gson | (gestionada SB4) | Deserialización JSON del estado de envío (StatusOrderLogsolutions) |
org.projectlombok:lombok | 1.18.46 | Generación de boilerplate en los POJOs (getters, setters, constructores) |
spring-boot-starter-test | (gestionada SB4) | Testing (scope test) |
5. API / Endpoints
No aplica a este proyecto. logsolution-client es una librería cliente JAR que no expone endpoints REST propios. Las operaciones SOAP que encapsula se detallan en la sección 6.
6. Integraciones externas
Servicio web SOAP de LogSolution
| Método cliente | Ruta remota (relativa) | SOAPAction | Dirección | Descripción |
|---|---|---|---|---|
getVersion | /PublicService.svc?wsdl&Action=GetVersion | http://tempuri.org/IPublicService/GetVersion | Saliente | Consulta la versión del servicio |
createOrder | /LogisticService.svc?wsdl&Action=CreateOrder | http://tempuri.org/ILogisticService/CreateOrder | Saliente | Crea un pedido en LogSolution |
getOrder | /LogisticService.svc?wsdl&Action=GetOrders | http://tempuri.org/ILogisticService/GetOrders | Saliente | Consulta pedidos (nombre del método en singular, acción remota GetOrders en plural) |
getArticles | /LogisticService.svc?wsdl&Action=GetArticles | http://tempuri.org/ILogisticService/GetArticles | Saliente | Consulta artículos por referencia de cliente/tienda |
createArticle | /LogisticService.svc?wsdl&Action=CreateArticle | http://tempuri.org/ILogisticService/CreateArticle | Saliente | Crea un artículo en el catálogo de LogSolution |
getStores | /LogisticService.svc?wsdl&Action=GetStores | http://tempuri.org/ILogisticService/GetStores | Saliente | Consulta las tiendas/almacenes configurados |
getShipment | /Service.svc?wsdl&Action=GetShipment | http://tempuri.org/IService/GetShipment | Saliente | Consulta el estado/tracking de un envío |
Todos los métodos reciben el cuerpo SOAP completo como String (@RequestBody String body) y devuelven ResponseEntity<String> con el XML crudo de respuesta; ninguno declara throws explícito.
Autenticación: no se usa ningún mecanismo HTTP (no hay cabecera Authorization ni API key); el token se embebe dentro del propio cuerpo SOAP, en el elemento Authentication > Token de cada request (ver POJOs *Request.Body.*.Request.Authentication).
Ejemplo de estructura del envelope SOAP para createOrder (simplificado, generado a partir de CreateOrderRequest):
<soapenv:Envelope xmlns:soapenv="..." xmlns:tem="..." xmlns:ns="...">
<soapenv:Header/>
<soapenv:Body>
<tem:CreateOrder>
<tem:request>
<ns:Authentication>
<ns:Token>********</ns:Token>
</ns:Authentication>
<ns:Order>
<ns:CarrierCode>DHL</ns:CarrierCode>
<ns:CustomerReference>ORD-000123</ns:CustomerReference>
<ns:StoreCode>HAWKERS_IT</ns:StoreCode>
<ns:Delivery>
<ns:Name>Cliente Final</ns:Name>
<ns:Location>
<ns:City>Roma</ns:City>
<ns:CountryIsoCode3>ITA</ns:CountryIsoCode3>
<ns:PostalCode>00100</ns:PostalCode>
</ns:Location>
</ns:Delivery>
<ns:Details>
<ns:OrderDetailModel>
<ns:ArticleStoreReference>SKU-001</ns:ArticleStoreReference>
<ns:Quantity>2</ns:Quantity>
<ns:UnitOfMeasure>PZ</ns:UnitOfMeasure>
</ns:OrderDetailModel>
</ns:Details>
</ns:Order>
</tem:request>
</tem:CreateOrder>
</soapenv:Body>
</soapenv:Envelope>
Ejemplo de respuesta de estado de envío (StatusOrderLogsolutions, deserializada con Gson, formato JSON derivado de la respuesta SOAP):
{
"InternalReference": "INT-000123",
"CarrierCode": "DHL",
"CarrierName": "DHL Express",
"TrackingUrl": "https://dhl.example/tracking",
"Events": [
{ "Country": "IT", "EventDate": "2026-07-01T10:00:00", "StatusCode": "DELIVERED" }
]
}
Protocolo: SOAP sobre HTTP (text/xml;charset=UTF-8). Autenticación: token embebido en el cuerpo SOAP (Authentication > Token), sin cabeceras HTTP de autenticación.
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
| Propiedad | Descripción | Ejemplo de valor |
|---|---|---|
logsolution.auth-logistic.client.url | URL base del servicio SOAP LogSolution (activa la autoconfiguración) | ${LOGSOLUTION_CLIENT_URL} |
El token de autenticación no se configura como propiedad Spring: se embebe manualmente por el consumidor en cada cuerpo SOAP (Authentication > Token) al construir el XML de request.
Variables de entorno recomendadas
| Variable | Propiedad mapeada |
|---|---|
LOGSOLUTION_CLIENT_URL | logsolution.auth-logistic.client.url |
Importante: Si
logsolution.auth-logistic.client.urlno está definida, el beanLogSolutionClientno se registra (condición@ConditionalOnProperty).
8. Persistencia
No aplica a este proyecto. La librería no accede a ninguna base de datos ni mantiene estado en memoria.
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
logsolution-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 Maven wrapper
./mvnw clean install
# Ejecutar tests (no existen actualmente)
./mvnw test
Uso como dependencia en un microservicio consumidor
<dependency>
<groupId>com.hawkersco</groupId>
<artifactId>logsolution-client</artifactId>
<version>1.0.25-SNAPSHOT</version>
</dependency>
La autoconfiguración se activa automáticamente al declarar logsolution.auth-logistic.client.url en la aplicación consumidora. El consumidor debe construir el XML SOAP completo (incluyendo el token de autenticación) usando los POJOs JAXB del paquete pojo/ antes de invocar cada método.
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.
| 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) |
CLAUDE.md menciona además análisis SonarQube (https://sonarqube.hawkersco.net) vía jenkins/scripts/mvn.sh, pero dicho script no está presente en el repositorio actual ni referenciado en el Jenkinsfile — pendiente de verificar si el análisis SonarQube se ejecuta desde una configuración externa al repositorio.
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/logsolution-client/
12. Manejo de errores y logging
La librería no implementa ninguna estrategia propia de manejo de excepciones ni logging estructurado. Ningún método de LogSolutionClient declara throws explícito; las excepciones de red o HTTP propagadas por RestClient (como RestClientResponseException) son responsabilidad del servicio consumidor. Al devolver siempre ResponseEntity<String> con el XML crudo, la detección de errores de negocio (p. ej. un SOAP Fault) requiere que el propio consumidor parsee la respuesta. No hay configuración de logback ni de niveles de log específicos en la librería.
13. Notas y consideraciones
-
Documentación previa desactualizada: El
CLAUDE.mddel proyecto describe una arquitectura basada en OpenFeign con unRequestInterceptordinámico, que no coincide con el código actual (@HttpExchange+RestClient, sin Feign, sin interceptor, cabecerasSOAPActionestáticas por método). Ver detalle en la sección 3. -
getOrder()mapea a la acción remotaGetOrders(plural): Confirmado en el código — el método del cliente se llamagetOrder(singular) pero la ruta y elSOAPActionremotos usanGetOrders(plural). Nomenclatura inconsistente que puede confundir al buscar la operación en la documentación de LogSolution. -
GetShipmentResponscon error tipográfico: La clase de respuesta degetShipmentse llamaGetShipmentRespons(falta la 'e' final de "Response"). Renombrarla requeriría actualizar todos los consumidores que la importen directamente. -
Construcción manual de XML SOAP con JAXB verboso: Los POJOs de request (
CreateOrderRequest,GetArticlesRequest, etc.) modelan el envelope SOAP completo con clases estáticas anidadas y setters marcados@XmlTransientque delegan en campos públicos accedidos directamente — patrón inusual que evita que JAXB serialice mediante los setters, y que resulta en una jerarquía profundamente anidada y repetitiva entre las distintas operaciones (cadaRequestreimplementa su propia claseAuthentication). -
Duplicación de la clase
Details/OrderDetailModelenCreateOrderRequest: Dentro deCreateOrderRequest.Body.CreateOrder.Request.Orderexisten dos definiciones de una claseDetailsconOrderDetailModelanidado: una dentro deOrder.Delivery(con dos campos:articleStoreReference,quantity) y otra directamente dentro deOrder(con tres campos, añadiendounitOfMeasure). Esto sugiere una refactorización incompleta o una clase obsoleta que no se ha eliminado — pendiente de verificar cuál de las dos se usa realmente al construir el XML. -
GetArticlesResponse.Body.getArticlescomo campostatic: El campogetArticlesenGetArticlesResponse.Bodyestá declaradostatic, lo cual es atípico para un campo mapeado por JAXB en una clase anidada no estática de instancia — con anotación@XmlAccessorType(XmlAccessType.PROPERTY), este campo probablemente no se serializa/deserializa correctamente al ser estático. Comportamiento no verificado en tiempo de ejecución. -
Modelos mixtos JSON/XML/CSV en el mismo paquete:
pojo/combina POJOs JAXB (SOAP/XML), un modelo Gson (StatusOrderLogsolutions, JSON) y dos beans OpenCSV (SkuCsv,OrderInternationalTest), reflejando que la integración completa con LogSolution abarca no solo el servicio SOAP sino también intercambio de ficheros CSV para altas masivas. -
OrderInternationalTestes un modelo de datos, no un test: Pese a su nombre y estar situado ensrc/main/, esta clase es un bean OpenCSV para importar datos de pedidos internacionales desde fichero — no debe confundirse con una clase de test JUnit. -
ArticleModelsin anotaciones de mapeo: A diferencia del resto de POJOs de request/response,ArticleModelno lleva anotaciones JAXB ni Jackson/Gson explícitas — no está claro cómo ni cuándo se serializa hacia/desde la API de LogSolution; podría tratarse de un modelo de conveniencia interno del consumidor. Pendiente de verificar su uso real. -
Sin tests implementados: No existe directorio
src/test/en el proyecto. Consistente con lo indicado enCLAUDE.md("Run tests (none currently exist)"). -
LogSolutionClientApplication.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.