Servicio REST desarrollado con Spring Boot para consultar el precio aplicable a un producto, una cadena y una fecha concreta.
Cuando existe más de una tarifa válida para la fecha indicada, se devuelve la de mayor prioridad.
- Java 17 o superior. Con varias versiones instaladas,
JAVA_HOMEdebe apuntar a la 17 o superior. - Maven 3.9 o compatible.
- Docker, de forma opcional. Con Docker no hace falta ni Maven ni JDK: la compilación ocurre dentro de la imagen.
Para compilar el proyecto y ejecutar los tests:
mvn clean verifyPara arrancar la aplicación:
mvn spring-boot:runLa API queda disponible en http://localhost:8080. La base de datos H2 se crea en memoria al iniciar la aplicación y se carga con los datos del enunciado.
GET /api/v1/pricesParámetros obligatorios:
applicationDateproductIdbrandId
Ejemplo:
curl "http://localhost:8080/api/v1/prices?applicationDate=2020-06-14T16:00:00&productId=35455&brandId=1"Respuesta:
{
"productId": 35455,
"brandId": 1,
"priceList": 2,
"startDate": "2020-06-14T15:00:00",
"endDate": "2020-06-14T18:30:00",
"price": 25.45,
"currency": "EUR"
}La fecha se envía en ISO-8601 local, sin zona horaria: 2020-06-14T16:00:00. Un valor con offset, como 2020-06-14T16:00:00Z, se rechaza con 400.
Si no se encuentra un precio aplicable, la API devuelve 404. Los parámetros ausentes o incorrectos devuelven 400, y cualquier método distinto de GET, 405. Todas las respuestas de error comparten el mismo cuerpo: timestamp, status, error, message y path.
El proyecto está separado siguiendo arquitectura hexagonal:
domaincontiene el modelo de precio y la excepción de precio no encontrado, sin dependencias de Spring ni JPA.applicationcontiene el caso de uso y los puertos de entrada y salida.infrastructurecontiene el controlador REST, la gestión de errores y la persistencia con JPA y H2.
El servicio de aplicación trabaja contra un puerto de salida y no conoce directamente el repositorio JPA. El adaptador de persistencia implementa ese puerto y transforma la entidad JPA al modelo de dominio.
La consulta filtra por cadena, producto y periodo de vigencia. Si coinciden varias tarifas, las ordena por prioridad descendente y devuelve la primera.
Se han incluido:
- Tests unitarios del caso de uso con JUnit y Mockito.
- Tests de persistencia con H2 sobre la consulta real.
- Tests end-to-end del endpoint con RestAssured, contra un servidor en puerto aleatorio.
Los tests E2E incluyen los cinco casos indicados en el enunciado:
| Fecha | Tarifa | Precio |
|---|---|---|
| 2020-06-14T10:00:00 | 1 | 35.50 EUR |
| 2020-06-14T16:00:00 | 2 | 25.45 EUR |
| 2020-06-14T21:00:00 | 1 | 35.50 EUR |
| 2020-06-15T10:00:00 | 3 | 30.50 EUR |
| 2020-06-16T21:00:00 | 4 | 38.95 EUR |
También se comprueban el precio no encontrado, los parámetros incorrectos o ausentes y el método no permitido.
Para ejecutar todos los tests:
mvn clean verifyEl contrato de la API está definido en src/main/resources/static/openapi.yaml y se publica en http://localhost:8080/openapi.yaml.
Swagger UI queda disponible en http://localhost:8080/swagger-ui.html.
El contrato se ha mantenido explícito en YAML y Swagger UI lo carga directamente, sin generar documentación a partir del controlador. Los parámetros incluyen ejemplos, de modo que el botón "Try it out" funciona sin escribir nada.
Consola disponible en http://localhost:8080/h2-console, con JDBC URL jdbc:h2:mem:prices, usuario sa y contraseña vacía.
Los precios se representan con BigDecimal para evitar la pérdida de precisión de los tipos en coma flotante, y las fechas se representan con LocalDateTime, ya que el enunciado trabaja con fecha y hora y no especifica zona horaria.
Los objetos de petición y respuesta son record, al tratarse de valores inmutables. La entidad JPA permanece en infraestructura para no acoplar el dominio a la persistencia.
Construir la imagen:
docker build -t price-api .Ejecutarla:
docker run --rm -p 8080:8080 price-apiLa primera fase del Dockerfile ejecuta mvn clean verify, de modo que la imagen no se construye si algún test falla. No requiere Maven ni JDK instalados en la máquina: la compilación ocurre dentro de la imagen.
El contenedor publica el 8080, por lo que se prueba igual que en local: sirven los curl anteriores, Swagger UI y la consola H2. Si el puerto está ocupado, basta mapear otro:
docker run --rm -p 9090:8080 price-api
curl "http://localhost:9090/api/v1/prices?applicationDate=2020-06-14T16:00:00&productId=35455&brandId=1"