Skip to main content

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

PropiedadValor
artifactIdlogsolution-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.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

DependenciaVersiónPropó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-api4.0.4Anotaciones JAXB para el mapeo XML/SOAP de los envelopes de request/response
com.opencsv:opencsv5.12.0Lectura/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:lombok1.18.46Generació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 clienteRuta remota (relativa)SOAPActionDirecciónDescripción
getVersion/PublicService.svc?wsdl&Action=GetVersionhttp://tempuri.org/IPublicService/GetVersionSalienteConsulta la versión del servicio
createOrder/LogisticService.svc?wsdl&Action=CreateOrderhttp://tempuri.org/ILogisticService/CreateOrderSalienteCrea un pedido en LogSolution
getOrder/LogisticService.svc?wsdl&Action=GetOrdershttp://tempuri.org/ILogisticService/GetOrdersSalienteConsulta pedidos (nombre del método en singular, acción remota GetOrders en plural)
getArticles/LogisticService.svc?wsdl&Action=GetArticleshttp://tempuri.org/ILogisticService/GetArticlesSalienteConsulta artículos por referencia de cliente/tienda
createArticle/LogisticService.svc?wsdl&Action=CreateArticlehttp://tempuri.org/ILogisticService/CreateArticleSalienteCrea un artículo en el catálogo de LogSolution
getStores/LogisticService.svc?wsdl&Action=GetStoreshttp://tempuri.org/ILogisticService/GetStoresSalienteConsulta las tiendas/almacenes configurados
getShipment/Service.svc?wsdl&Action=GetShipmenthttp://tempuri.org/IService/GetShipmentSalienteConsulta 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

PropiedadDescripciónEjemplo de valor
logsolution.auth-logistic.client.urlURL 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

VariablePropiedad mapeada
LOGSOLUTION_CLIENT_URLlogsolution.auth-logistic.client.url

Importante: Si logsolution.auth-logistic.client.url no está definida, el bean LogSolutionClient no 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:

  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.
ParámetroValor
JDKJDK25 (tool Jenkins)
MavenMaven3 (tool Jenkins)
Repositorioeurope-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 Jenkinsfilependiente 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.md del proyecto describe una arquitectura basada en OpenFeign con un RequestInterceptor dinámico, que no coincide con el código actual (@HttpExchange + RestClient, sin Feign, sin interceptor, cabeceras SOAPAction estáticas por método). Ver detalle en la sección 3.

  • getOrder() mapea a la acción remota GetOrders (plural): Confirmado en el código — el método del cliente se llama getOrder (singular) pero la ruta y el SOAPAction remotos usan GetOrders (plural). Nomenclatura inconsistente que puede confundir al buscar la operación en la documentación de LogSolution.

  • GetShipmentRespons con error tipográfico: La clase de respuesta de getShipment se llama GetShipmentRespons (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 @XmlTransient que 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 (cada Request reimplementa su propia clase Authentication).

  • Duplicación de la clase Details/OrderDetailModel en CreateOrderRequest: Dentro de CreateOrderRequest.Body.CreateOrder.Request.Order existen dos definiciones de una clase Details con OrderDetailModel anidado: una dentro de Order.Delivery (con dos campos: articleStoreReference, quantity) y otra directamente dentro de Order (con tres campos, añadiendo unitOfMeasure). 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.getArticles como campo static: El campo getArticles en GetArticlesResponse.Body está declarado static, 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.

  • OrderInternationalTest es un modelo de datos, no un test: Pese a su nombre y estar situado en src/main/, esta clase es un bean OpenCSV para importar datos de pedidos internacionales desde fichero — no debe confundirse con una clase de test JUnit.

  • ArticleModel sin anotaciones de mapeo: A diferencia del resto de POJOs de request/response, ArticleModel no 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 en CLAUDE.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.