From 62401d37913a0e42340b4337015b21eb6116f404 Mon Sep 17 00:00:00 2001 From: jesquivels Date: Mon, 26 Oct 2026 13:32:22 -0600 Subject: [PATCH] first commit --- .env.example | 9 +-- README.md | 4 +- compose.yaml | 1 + pom.xml | 14 ++++ specs/DECISIONS.md | 12 ++++ specs/IMPLEMENTATION_PLAN.md | 2 + specs/PROGRESS.md | 13 +++- specs/RUNBOOK.md | 17 ++++- .../resources/application-postgres96.yaml | 5 ++ .../Postgres96CompatibilityProbe.java | 66 +++++++++++++++++++ .../com/organizacion/orchestrator/README.md | 2 + 11 files changed, 138 insertions(+), 7 deletions(-) create mode 100644 src/main/resources/application-postgres96.yaml create mode 100644 src/test/java/com/organizacion/compatibility/Postgres96CompatibilityProbe.java diff --git a/.env.example b/.env.example index 06a10a0..bc13a16 100644 --- a/.env.example +++ b/.env.example @@ -1,11 +1,12 @@ # Copiar a .env y completar antes de ejecutar docker compose up --build -d. # PostgreSQL externo: sustituir por su DNS/IP. No usar localhost dentro del contenedor. # Para una base en el equipo anfitrión: jdbc:postgresql://host.docker.internal:5432/orquestador -DB_URL=jdbc:postgresql://postgres.example.com:5432/orquestador -DB_USERNAME=orchestrator +DB_URL=jdbc:postgresql://server02:30432/orquestador +DB_USERNAME=pgadmin DB_PASSWORD= -JWT_ISSUER_URI= -JWT_JWK_SET_URI= +SPRING_PROFILES_ACTIVE=postgres96 +JWT_ISSUER_URI=http://keycloakic.ddns.net/auth/realms/controlms +JWT_JWK_SET_URI=http://keycloakic.ddns.net/auth/realms/controlms/protocol/openid-connect/certs JWT_AUDIENCE=orquestador-nmp APP_BIND_ADDRESS=127.0.0.1 APP_PORT=8080 diff --git a/README.md b/README.md index 6121692..c80852c 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Incluye la estructura Maven, configuración JWT/PostgreSQL, Docker y salud verif ## Requisitos y ejecución - Docker con contenedores Linux y Compose v2. La imagen compila con JDK 21 y Maven Wrapper; no requiere Java/Maven en el anfitrión. -- PostgreSQL 17 externo con base y usuario existentes, accesible desde Docker. +- PostgreSQL externo con base y usuario existentes, accesible desde Docker. El entorno actual usa PostgreSQL 9.6.2 en server02:30432 mediante el perfil postgres96; ver ADR-006. - Un issuer JWT con JWKS y audiencia; se conserva la autenticación. ```powershell @@ -23,6 +23,8 @@ docker compose logs -f orquestador Compose levanta únicamente `orquestador`. Para PostgreSQL en otra máquina usar `jdbc:postgresql://HOST:5432/BASE`; si está en el anfitrión de Docker, usar `host.docker.internal` en lugar de `localhost`. Ver [RUNBOOK](specs/RUNBOOK.md). +Compose selecciona `postgres96` por defecto para el servidor disponible. `.env.example` contiene su URL/usuario y los endpoints Keycloak; completar DB_PASSWORD externamente y confirmar JWT_AUDIENCE con los tokens. Para una base moderna se puede seleccionar otro perfil, como `SPRING_PROFILES_ACTIVE=local`, conservando su DB_URL explícita. No se desactivan Flyway ni la validación del esquema en el servicio. + Salud: `http://localhost:8080/actuator/health/readiness`. El puerto publicado se configura con APP_PORT. El healthcheck incluye PostgreSQL; liveness no depende de proveedores. Las demás rutas requieren JWT. No hay endpoints de negocio implementados todavía. ## Organización y seguimiento diff --git a/compose.yaml b/compose.yaml index a1e742a..1f7fc09 100644 --- a/compose.yaml +++ b/compose.yaml @@ -5,6 +5,7 @@ services: dockerfile: Dockerfile image: orquestador-nmp:local environment: + SPRING_PROFILES_ACTIVE: ${SPRING_PROFILES_ACTIVE:-postgres96} DB_URL: ${DB_URL:?Configura DB_URL en .env} DB_USERNAME: ${DB_USERNAME:?Configura DB_USERNAME en .env} DB_PASSWORD: ${DB_PASSWORD:?Configura DB_PASSWORD en .env} diff --git a/pom.xml b/pom.xml index aba2f26..7f9da03 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,7 @@ org.springframework.bootspring-boot-starter-webmvc org.springframework.bootspring-boot-starter-validation org.springframework.bootspring-boot-starter-data-jpa + org.hibernate.ormhibernate-community-dialects org.springframework.bootspring-boot-starter-jdbc org.springframework.bootspring-boot-starter-security-oauth2-resource-server org.springframework.bootspring-boot-starter-actuator @@ -54,6 +55,19 @@ + + postgres96-check + + org.apache.maven.pluginsmaven-failsafe-plugin + + + **/*IT.java**/IT*.java**/*ITCase.java + **/Postgres96CompatibilityProbe.java + + true + + + deployment-smoke diff --git a/specs/DECISIONS.md b/specs/DECISIONS.md index 205c4b8..42065a7 100644 --- a/specs/DECISIONS.md +++ b/specs/DECISIONS.md @@ -70,3 +70,15 @@ Herramientas fijadas, únicamente scope test: networknt json-schema-validator 1. Se conserva una copia del schema oficial OpenAPI 3.1 (2022-10-07), SHA256 DA01BA28852CAC0DE53893797CB8D1942BC3B05084F526DCC216717DEC314ED0, para validar sin red. Swagger Parser con resolve=true intentó expandir indefinidamente las referencias recursivas del AST: se usa resolve=false y comprobación explícita de todos los $ref locales. Networknt valida los schemas y ejemplos reales. No se relajan límites ni se ignoran referencias rotas para resolver esa incompatibilidad. Referencias: [OpenAPI 3.1](https://spec.openapis.org/oas/v3.1.0), [schema oficial](https://spec.openapis.org/oas/3.1/schema/2022-10-07), [networknt](https://github.com/networknt/json-schema-validator), [Swagger Parser y soporte 3.1](https://github.com/swagger-api/swagger-parser). Detalles de uso en contracts/README.md y schemas/README.md. + +## ADR-006 — Compatibilidad con PostgreSQL disponible (2026-10-26) + +El usuario solicita adaptar la conexión al PostgreSQL existente, identificado por JDBC como 9.6.2 en server02:30432/orquestador. Esta instrucción sustituye el requisito de PostgreSQL 17 para el entorno actual. La especificación original permanece como antecedente; las migraciones de T03 deberán ser compatibles con 9.6 (incluidas las formas de DDL de triggers), sin cambiar semántica de locking, idempotencia o seguridad. + +Se añade hibernate-community-dialects 7.4.13.Final, alineado al BOM/core, y application-postgres96.yaml con PostgreSQLLegacyDialect. Compose activa postgres96 por defecto y permite elegir otro perfil explícito. Spring Boot/Java/JDBC/Flyway no se degradan. Se preservan ddl-auto=validate, Flyway habilitado y clean-disabled en runtime; no se modifica JWT. .env.example contiene parámetros públicos de conexión, nunca la contraseña. + +Flyway 12.4.0 inspeccionado admite desde PostgreSQL 9.0 y la validación real sobre 9.6.2 pasó; registra que 9.6 queda fuera del soporte comunitario Redgate. El dialecto legacy pertenece al módulo comunitario de Hibernate, no al conjunto de dialectos actuales soportados. Esta adaptación no promete soporte oficial para la combinación ni prueba todavía consultas de entidades/migraciones del motor. + +Verificación: clean verify con Java 21 (92 tests) y postgres96-check (1 prueba de integración compuesta). La prueba inicia el servicio local con pool read-only, Flyway automático desactivado, SQL init desactivado y validación Hibernate; después invoca exclusivamente Flyway.validate() con createSchemas=false. No ejecuta migrate/create/clean ni modifica la instancia externa. La primera propuesta de arranque fue rechazada por la revisión automática por riesgo de migraciones; se usó esta alternativa explícitamente de sólo lectura, aprobada y ejecutada. + +Fuentes: [dialectos y módulo comunitario Hibernate](https://docs.hibernate.org/stable/core/dialect/), [versiones de bases de datos Flyway](https://documentation.red-gate.com/fd/supported-databases-and-versions-143754067.html). La compatibilidad se comprobó con las dependencias reales del proyecto, no sólo con la documentación. diff --git a/specs/IMPLEMENTATION_PLAN.md b/specs/IMPLEMENTATION_PLAN.md index aa821b6..76e9814 100644 --- a/specs/IMPLEMENTATION_PLAN.md +++ b/specs/IMPLEMENTATION_PLAN.md @@ -55,6 +55,8 @@ Cerrado el 2026-10-26: clean verify con Java 21, 92 comprobaciones, 0 fallos/err Dependencias: T02. Entregables: db/migration/, pruebas *IT. +Entorno objetivo actualizado por el usuario: PostgreSQL 9.6.2 disponible en server02; ver ADR-006. Usar DDL compatible con 9.6 y un entorno aislado para pruebas de escritura. La comprobación de conexión/arranque E11 no cierra esta tarea. + - [ ] Crear tablas, FKs e índices. - [ ] Añadir idempotencia de creación/admin_audit/reconciliation_resolution. - [ ] Proteger inmutabilidad e historial con roles y triggers. diff --git a/specs/PROGRESS.md b/specs/PROGRESS.md index 9eb0d60..a5bbe6b 100644 --- a/specs/PROGRESS.md +++ b/specs/PROGRESS.md @@ -8,7 +8,7 @@ Estados permitidos: PENDING, IN_PROGRESS, BLOCKED, DONE. No más de una tarea pr |---|---|---|---|---|---|---|---| | T01 | DONE | — | pom.xml, mvnw*, .mvn, configuración, DeploymentSmokeCheck.java, ADR-001/004 | E09: clean verify con JDK 21 y deployment-smoke | BUILD SUCCESS; 5 pruebas, 0 fallos, 0 errores, 0 omitidas | Ninguno para T01 | Iniciar T02: OpenAPI y JSON Schema | | T02 | DONE | T01 DONE | contracts/openapi.yaml, schemas/*.schema.json, examples/contracts, ContractValidationTest.java, ADR-005 | E10: clean verify con Java 21 | BUILD SUCCESS; 92 comprobaciones, 0 fallos/errores/omisiones | Ninguno para T02 | Iniciar T03: migraciones PostgreSQL y Testcontainers | -| T03 | PENDING | T02 DONE | db/migration/, pruebas *IT | Pendiente: Testcontainers PostgreSQL, rollback y migraciones | No ejecutado | Docker requerido para pruebas; dependencia T02 satisfecha | Crear tablas, FKs e índices y preparar entorno Testcontainers | +| T03 | PENDING | T02 DONE | db/migration/, pruebas *IT | E11 verifica conexión/arranque 9.6; migraciones pendientes | Compatibilidad inicial aprobada; modelo no implementado | Falta entorno aislado para pruebas de migraciones | Crear DDL compatible con PostgreSQL 9.6 según ADR-006 | | T04 | PENDING | T02,T03 | flow/, request/, execution/, decision/, action/ | Pendiente: Pruebas de dominio y dependencias entre capas | No ejecutado | Dependencias sin completar | Definir records y errores estables | | T05 | PENDING | T04 | decision/, pruebas de AST | Pendiente: JUnit parametrizado: 71/70/61/60, tipos y solapamientos | No ejecutado | Dependencias sin completar | Implementar operadores/JSON Pointer/BigDecimal | | T06 | PENDING | T03,T04,T05 | flow/, admin_audit, pruebas *IT | Pendiente: Pruebas PostgreSQL de carreras publicación/activación/retiro | No ejecutado | Dependencias sin completar | Validar grafo, mappings, roles y capacidades | @@ -24,6 +24,17 @@ Estados permitidos: PENDING, IN_PROGRESS, BLOCKED, DONE. No más de una tarea pr | T16 | PENDING | T15 | src/test/, CI, matriz A01–A20 | Pendiente: ./mvnw clean verify con Docker, sin skips de integración | No ejecutado | Dependencias sin completar | Automatizar A01–A20 | | T17 | PENDING | T16 | Dockerfile, compose.yaml, README, RUNBOOK, examples/http, CI | Pendiente: Build de imagen, compose, verify y demo completa de sección 16 | No ejecutado | Dependencias sin completar | Construir imagen y Compose completos | +## E11 — Adaptación PostgreSQL 9.6.2 — 2026-10-26 13:23 (America/Mexico_City) + +- Solicitud del usuario: utilizar el PostgreSQL disponible. ADR-006 documenta la modificación del objetivo PostgreSQL 17 al entorno 9.6.2 actual. +- Configuración: perfil Spring postgres96, Hibernate PostgreSQLLegacyDialect (community-dialects 7.4.13.Final), Compose selecciona ese perfil por defecto. .env.example apunta a server02:30432/orquestador y no incluye contraseña. +- `.\mvnw.cmd --batch-mode --no-transfer-progress clean verify` con Java 21: BUILD SUCCESS, 92 tests aprobados (16.074 s). +- `.\mvnw.cmd --batch-mode --no-transfer-progress failsafe:integration-test failsafe:verify -Ppostgres96-check`, con DB/JWT en variables de proceso: BUILD SUCCESS, 1 prueba compuesta aprobada (12.696 s). Reporte: target/failsafe-reports/com.organizacion.compatibility.Postgres96CompatibilityProbe.txt. +- Evidencia: conexión real 9.6.2, dialecto PostgreSQLLegacyDialect, default_transaction_read_only=on, validación Flyway 12.4.0 de 0 migraciones, health/readiness/liveness HTTP 200 UP y ruta protegida HTTP 401. Servicio local detenido al terminar. +- Sin cambios a datos, schemas o historial de migraciones. El inicializador automático Flyway y SQL init se desactivaron únicamente en la prueba; la configuración normal conserva Flyway. No se desplegó el JAR en server02. +- La revisión automática rechazó el primer diseño por posible migración; la alternativa explícitamente read-only fue aprobada y ejecutada. No queda bloqueo de aprobación para esta adaptación. +- T03 sigue PENDING: faltan tablas, migraciones y pruebas de restricciones/concurrencia. La conexión disponible permite diseñar para 9.6, pero no sustituye un entorno aislado de pruebas ni prueba soporte completo del motor. + ## E10 — Cierre T02 — 2026-10-26 11:58 (America/Mexico_City) - Estado: DONE. 14 operaciones REST OpenAPI 3.1.0, DTOs, schema DSL 2020-12, ejemplos de resultados normalizados y fixtures contractuales completos para esta fase. diff --git a/specs/RUNBOOK.md b/specs/RUNBOOK.md index 0e222a9..dc83342 100644 --- a/specs/RUNBOOK.md +++ b/specs/RUNBOOK.md @@ -4,13 +4,14 @@ Requiere Docker con contenedores Linux y Compose v2. La imagen compila con JDK 21 y ejecuta con JRE 21; no requiere Java/Maven en el anfitrión. PostgreSQL y el proveedor JWT son externos. -1. Crear base y usuario en PostgreSQL 17. Permitir conexión desde Docker según firewall, listen_addresses y pg_hba.conf del servidor. +1. Usar la base externa existente. Actualmente server02:30432/orquestador ejecuta PostgreSQL 9.6.2 y requiere el perfil postgres96; se conserva PostgreSQL 17 como alternativa futura. Permitir conexión desde Docker según firewall, listen_addresses y pg_hba.conf del servidor. 2. Copiar `.env.example` a `.env`, sin sobrescribir uno ya configurado. 3. Completar DB_URL, DB_USERNAME, DB_PASSWORD, JWT_ISSUER_URI, JWT_JWK_SET_URI y JWT_AUDIENCE. | Variable | Uso | |---|---| | DB_URL | JDBC de la base externa: jdbc:postgresql://db.interna:5432/orquestador | +| SPRING_PROFILES_ACTIVE | postgres96 para la instancia disponible; selecciona PostgreSQLLegacyDialect de Hibernate | | DB_USERNAME / DB_PASSWORD | Credenciales del usuario de la base existente | | JWT_ISSUER_URI | Issuer exacto de los tokens | | JWT_JWK_SET_URI | Endpoint JWKS correspondiente, accesible desde Docker | @@ -54,6 +55,8 @@ Estos comandos no administran la base externa. Flyway permanece habilitado y apl ## Ejecución desde el IDE +Para el servidor actual configurar `DB_URL=jdbc:postgresql://server02:30432/orquestador`, `DB_USERNAME=pgadmin` y `SPRING_PROFILES_ACTIVE=postgres96`; la contraseña sólo en el entorno local. El argumento de perfil local del ejemplo siguiente debe reemplazarse por postgres96 cuando se use PostgreSQL 9.6. + Configurar JDK 21 y las seis variables DB/JWT en la sesión o Run Configuration. Spring Boot no importa `.env` automáticamente fuera de Compose. ```powershell @@ -63,6 +66,18 @@ Configurar JDK 21 y las seis variables DB/JWT en la sesión o Run Configuration. ## Diagnóstico +### Prueba de compatibilidad con PostgreSQL 9.6 + +Con JDK 21 y las seis variables DB/JWT exportadas, ejecutar: + +```powershell +.\mvnw.cmd clean verify -Ppostgres96-check +``` + +La prueba arranca el checkout local en 127.0.0.1 con puerto aleatorio y cierra el contexto al terminar. Fuerza PostgreSQL default_transaction_read_only=on, desactiva el inicializador automático Flyway y SQL init sólo en la prueba, conserva ddl-auto=validate y ejecuta Flyway.validate() sin crear schemas ni migrar. Comprueba versión 9.6, dialecto legacy, health/readiness/liveness UP y 401 sin token. No corre en el build normal ni depende de Docker. No sustituye pruebas de migraciones, DML/concurrencia ni Testcontainers de T03. + +Hibernate community-dialects se alinea al BOM (7.4.13.Final). Flyway 12.4.0 detecta 9.6 y valida; informa que esa versión queda fuera de soporte comunitario de Redgate. La compatibilidad verificada se limita al arranque/conexión actual, todavía sin entidades ni migraciones de negocio. + Para automatizar la comprobación del despliegue con JDK 21: ```powershell diff --git a/src/main/resources/application-postgres96.yaml b/src/main/resources/application-postgres96.yaml new file mode 100644 index 0000000..5bee93d --- /dev/null +++ b/src/main/resources/application-postgres96.yaml @@ -0,0 +1,5 @@ +# Compatibilidad explícita para la instancia PostgreSQL 9.6 del entorno actual. +# Flyway y ddl-auto=validate conservan su configuración normal. +spring: + jpa: + database-platform: org.hibernate.community.dialect.PostgreSQLLegacyDialect diff --git a/src/test/java/com/organizacion/compatibility/Postgres96CompatibilityProbe.java b/src/test/java/com/organizacion/compatibility/Postgres96CompatibilityProbe.java new file mode 100644 index 0000000..bd529ff --- /dev/null +++ b/src/test/java/com/organizacion/compatibility/Postgres96CompatibilityProbe.java @@ -0,0 +1,66 @@ +package com.organizacion.compatibility; + +import com.organizacion.orchestrator.OrchestratorApplication; +import jakarta.persistence.EntityManagerFactory; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.junit.jupiter.api.Test; +import org.springframework.boot.SpringApplication; +import org.flywaydb.core.Flyway; +import javax.sql.DataSource; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.time.Duration; +import tools.jackson.databind.json.JsonMapper; +import static org.junit.jupiter.api.Assertions.*; + +/** Explicit integration probe; starts this checkout against an external DB without writes. */ +class Postgres96CompatibilityProbe { + @Test + void applicationStartsAndServesHealthAgainstPostgres96WithoutWrites() throws Exception { + for (String name : new String[]{"DB_URL", "DB_USERNAME", "DB_PASSWORD", "JWT_ISSUER_URI", "JWT_JWK_SET_URI", "JWT_AUDIENCE"}) { + assertNotNull(System.getenv(name), "Missing environment variable: " + name); + assertFalse(System.getenv(name).isBlank(), "Empty environment variable: " + name); + } + var app = new SpringApplication(OrchestratorApplication.class); + try (var context = app.run( + "--spring.profiles.active=postgres96", + "--spring.flyway.enabled=false", + "--spring.jpa.hibernate.ddl-auto=validate", + "--spring.sql.init.mode=never", + "--server.address=127.0.0.1", "--server.port=0", + "--spring.datasource.hikari.data-source-properties.options=-c default_transaction_read_only=on", + "--spring.datasource.hikari.maximum-pool-size=2", + "--spring.datasource.hikari.connection-timeout=10000", + "--spring.datasource.hikari.data-source-properties.connectTimeout=10", + "--spring.datasource.hikari.data-source-properties.socketTimeout=10")) { + try (var connection = context.getBean(DataSource.class).getConnection(); + var statement = connection.createStatement(); + var result = statement.executeQuery("select current_setting('server_version_num')::int, current_setting('default_transaction_read_only')")) { + assertTrue(result.next()); + assertTrue(result.getInt(1) >= 90600 && result.getInt(1) < 100000, "This probe targets PostgreSQL 9.6"); + assertEquals("on", result.getString(2)); + } + // No migration initializer exists in this probe. Only validate() is invoked, + // using the same read-only pool; schema creation is explicitly forbidden. + Flyway.configure().dataSource(context.getBean(DataSource.class)) + .locations("classpath:db/migration").createSchemas(false).load().validate(); + var sessionFactory = context.getBean(EntityManagerFactory.class).unwrap(SessionFactoryImplementor.class); + assertEquals("org.hibernate.community.dialect.PostgreSQLLegacyDialect", + sessionFactory.getJdbcServices().getDialect().getClass().getName()); + int port = Integer.parseInt(context.getEnvironment().getRequiredProperty("local.server.port")); + var client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build(); + for (String endpoint : new String[]{"health", "health/readiness", "health/liveness"}) { + var request = HttpRequest.newBuilder(URI.create("http://127.0.0.1:" + port + "/actuator/" + endpoint)) + .timeout(Duration.ofSeconds(10)).GET().build(); + var response = client.send(request, HttpResponse.BodyHandlers.ofString()); + assertEquals(200, response.statusCode(), endpoint); + assertEquals("\"UP\"", JsonMapper.builder().build().readTree(response.body()).path("status").toString()); + } + var protectedRequest = HttpRequest.newBuilder(URI.create("http://127.0.0.1:" + port + "/api/v1/requests")) + .timeout(Duration.ofSeconds(10)).GET().build(); + assertEquals(401, client.send(protectedRequest, HttpResponse.BodyHandlers.discarding()).statusCode()); + } + } +} diff --git a/src/test/java/com/organizacion/orchestrator/README.md b/src/test/java/com/organizacion/orchestrator/README.md index 42078e0..b28ac37 100644 --- a/src/test/java/com/organizacion/orchestrator/README.md +++ b/src/test/java/com/organizacion/orchestrator/README.md @@ -9,3 +9,5 @@ Sólo efectúa GET sin credenciales y no modifica datos. No demuestra JWT válid El build normal/CI no contacta servidores privados: este smoke requiere un despliegue ya iniciado. T03 incorpora Testcontainers PostgreSQL; nunca sustituir concurrencia o JSONB por H2. No omitir integraciones silenciosamente cuando Docker no esté disponible. + +Postgres96CompatibilityProbe (paquete com.organizacion.compatibility) se ejecuta únicamente con -Ppostgres96-check y variables DB/JWT externas. Arranca el checkout con PostgreSQL 9.6 en modo sólo lectura, valida dialecto/Flyway y salud. No cambia el comportamiento de migración del servicio normal ni sustituye T03.