Sarmed Client
1. Descripción general
sarmed-client es una librería cliente reutilizable (JAR) que encapsula la comunicación con el backend ecommerce/WMS de Sarmed. 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 autenticarse, enviar pedidos y dar de alta productos en el sistema de Sarmed.
2. Información técnica
| Propiedad | Valor |
|---|---|
artifactId | sarmed-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.sarmedclient
├── SarmedClientApplication.java # Clase @SpringBootApplication (residual, sin lógica)
├── client/
│ └── SarmedClient.java # Interfaz @HttpExchange: getSession, placeOrder, product
├── config/
│ └── SarmedClientAutoConfiguration.java # @AutoConfiguration principal (soporte opcional de SSL "trust-all")
└── model/
├── CredentialSarmedRequest.java # Record de credenciales; no cableado a ningún método del cliente (ver sección 13)
├── SessionSarmedRequest.java / SessionSarmedResponse.java
├── OrderSarmedRequest.java / OrderSarmedResponse.java
└── ProductSarmedRequest.java / ProductSarmedResponse.java
Flujo principal
sequenceDiagram
participant Consumidor as Microservicio consumidor
participant Client as SarmedClient
participant API as Backend Sarmed
Consumidor->>Client: getSession(username, password)
Client->>API: POST /ecommerce/api/login
API-->>Client: { "success": true, "session": "<uuid>" }
Consumidor->>Client: placeOrder(session, ...) / product(session, ...)
Client->>API: POST /ecommerce/api/placeOrder | POST /ecommerce/api/product
API-->>Client: ResponseEntity<OrderSarmedResponse | ProductSarmedResponse>
Client-->>Consumidor: ResponseEntity<T>
La autoconfiguración (SarmedClientAutoConfiguration) se activa condicionalmente con @ConditionalOnProperty(prefix = "sarmed.api", name = "url"), registrando el bean SarmedClient con un RestClient construido a partir de sarmed.api.url. Además, admite una propiedad opcional sarmed.api.trust-all-ssl (por defecto false): si se activa, configura un SSLContext que acepta cualquier certificado (implementación de X509TrustManager sin validación) y lo aplica a un HttpClient del JDK usado como JdkClientHttpRequestFactory del RestClient — ver consideración de seguridad en la sección 13.
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
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 en los modelos de clase (soporte dual con Jackson) |
com.fasterxml.jackson.core:jackson-databind | (gestionada SB4) | Serialización/deserialización Jackson en los modelos |
org.projectlombok:lombok | 1.18.46 | Generación de boilerplate en OrderSarmedRequest/ProductSarmedRequest (los DTOs simples son records) |
spring-boot-starter-test | (gestionada SB4) | Testing (scope test) |
5. API / Endpoints
No aplica a este proyecto. sarmed-client es una librería cliente JAR que no expone endpoints REST propios. Las operaciones que encapsula sobre el backend de Sarmed se detallan en la sección 6.
6. Integraciones externas
Backend ecommerce Sarmed
| Método cliente | HTTP | Ruta remota | Descripción |
|---|---|---|---|
getSession | POST | /ecommerce/api/login | Autentica con usuario/contraseña y devuelve una sesión (UUID) |
placeOrder | POST | /ecommerce/api/placeOrder | Envía un pedido con líneas de producto al sistema Sarmed |
product | POST | /ecommerce/api/product | Da de alta o actualiza un producto (con códigos de barras, unidades, dimensiones) |
Ejemplo de payload getSession (SessionSarmedRequest, record):
{
"username": "usuario_hawkers",
"password": "********"
}
Ejemplo de respuesta getSession (SessionSarmedResponse, record):
{
"success": true,
"errorCode": 0,
"errorDescription": "",
"session": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
Ejemplo de payload placeOrder (OrderSarmedRequest, resumido):
{
"Session": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"OrderType": 1,
"OrderCode": "ORD-000123",
"DepositorCode": "HAWKERS",
"FinalCustomerName": "Cliente Final",
"FinalCustomerCountryCode": "ES",
"OrderItem": [
{ "LineID": 1, "ProductCode": "SKU-001", "ShipQuantity": "2", "ShipProductUnit": "UN" }
]
}
Ejemplo de payload product (ProductSarmedRequest, resumido):
{
"Session": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"ActionCode": 1,
"ProductInfo": {
"ProductCode": "SKU-001",
"SkuID": "SKU-001",
"Descr": "Camiseta básica",
"DepositorCode": "HAWKERS",
"UnitCode1": "UN",
"Barcodes": [ { "Barcode": "8412345678901" } ]
}
}
Protocolo: HTTPS/HTTP REST (JSON, contentType = application/json). Autenticación: sesión obtenida mediante getSession (usuario/contraseña) y reenviada como campo Session en el cuerpo de las llamadas posteriores; no se usa ninguna cabecera 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 y opcionales (prefijo sarmed.api)
| Propiedad | Descripción | Ejemplo de valor |
|---|---|---|
sarmed.api.url | URL base del backend Sarmed (activa la autoconfiguración) | ${SARMED_API_URL} |
sarmed.api.trust-all-ssl | (Opcional, por defecto false) Si es true, desactiva la validación de certificados TLS | false |
Importante: Si
sarmed.api.urlno está definida, el beanSarmedClientno se registra (condición@ConditionalOnProperty). Las credenciales (username/password) no se configuran como propiedades Spring: se pasan explícitamente en el body degetSession.
Variables de entorno recomendadas
| Variable | Propiedad mapeada |
|---|---|
SARMED_API_URL | sarmed.api.url |
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
sarmed-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
# Ejecutar un test concreto
mvn -Dtest=ClassName test
mvn -Dtest=ClassName#methodName test
Uso como dependencia en un microservicio consumidor
<dependency>
<groupId>com.hawkersco</groupId>
<artifactId>sarmed-client</artifactId>
<version>1.0.25-SNAPSHOT</version>
</dependency>
La autoconfiguración se activa automáticamente al declarar sarmed.api.url 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 menciona que el build se ejecuta vía jenkins/scripts/mvn.sh, script no presente en este repositorio ni referenciado directamente en el Jenkinsfile actual (que invoca mvn deploy -DskipTests directamente). Se documenta el Jenkinsfile realmente presente.
| 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/sarmed-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 SarmedClient declara throws explícito; las excepciones de red o HTTP propagadas por RestClient (como RestClientResponseException) son responsabilidad del servicio consumidor. Los modelos de respuesta (SessionSarmedResponse, OrderSarmedResponse, ProductSarmedResponse) incluyen campos success, errorCode y errorDescription propios del contrato de Sarmed, que el consumidor debe inspeccionar manualmente para detectar errores de negocio (la API puede responder 200 OK con success: false). No hay configuración de logback ni de niveles de log específicos en la librería.
13. Notas y consideraciones
-
Bypass de validación TLS configurable (
sarmed.api.trust-all-ssl): Cuando esta propiedad se activa,SarmedClientAutoConfigurationinstala unX509TrustManagerque acepta cualquier certificado sin validación (checkClientTrusted/checkServerTrustedvacíos), desactivando efectivamente la verificación TLS delRestClient. Esto expone al consumidor a ataques de intermediario (man-in-the-middle) si se habilita en un entorno que no sea estrictamente de pruebas/desarrollo controlado. Debe evitarse en producción; su presencia sugiere que el endpoint de Sarmed en algún entorno (p. ej. staging) tiene un certificado autofirmado o mal configurado. -
CredentialSarmedRequestno utilizado: Existe un recordCredentialSarmedRequest(username, password)con los mismos campos queSessionSarmedRequest, peroSarmedClient.getSessionrecibeSessionSarmedRequest, noCredentialSarmedRequest— el primero es un modelo huérfano/duplicado, posiblemente un remanente de una refactorización o de una versión anterior de la API. -
Inconsistencia de tipo en el campo
session:SessionSarmedResponse.sessiones de tipoUUID,OrderSarmedRequest.sessiontambién esUUID, peroProductSarmedRequest.sessionesString. El consumidor debe convertir manualmente el UUID de sesión a texto al construir el request de producto. -
Mezcla de records y clases Lombok: Los DTOs simples de sesión y respuesta (
SessionSarmedRequest,SessionSarmedResponse,OrderSarmedResponse,ProductSarmedResponse,CredentialSarmedRequest) están modelados como records de Java, mientras que los DTOs anidados más complejos (OrderSarmedRequest,ProductSarmedRequest) usan clases con Lombok y anotaciones Jackson/Gson — refleja una mezcla de estilos dentro del mismo proyecto, a diferencia de otros clientes del ecosistema que son consistentemente uno u otro. -
Sin renovación ni caché de sesión: El cliente no gestiona el ciclo de vida de la sesión obtenida en
getSession; corresponde al consumidor almacenarla, reutilizarla y volver a autenticar cuando expire (el contrato no documenta explícitamente un TTL de sesión en los modelos disponibles). -
Sin tests implementados: No se ha encontrado directorio
src/test/en el proyecto. -
SarmedClientApplication.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.