5.3 KiB
Creación y consulta de solicitudes — T08
Implementados POST /api/v1/requests y GET /api/v1/requests/{id} según el contrato
OpenAPI. No se implementan todavía eventos, worker, historial ni tareas del front.
Habilitación
REQUEST_API_ENABLED=true habilita ambas rutas y exige un runtime PostgreSQL
restringido con credenciales de migración separadas (ver SECURITY.md). Es
independiente de FLOW_ADMIN_ENABLED: se puede atender solicitudes sin exponer
administración ni cargar un catálogo de publicación local. Ambos interruptores
son false por defecto para conservar el despliegue previo con pgadmin hasta que
se configure la cuenta de ejecución correcta. No se modificaron cuentas remotas.
El JWT debe contener sub, audiencia, firma y ámbito válidos. Cualquier actor así autenticado puede crear una solicitud de un tipo habilitado; propietario y ámbito se obtienen del token. Los roles de transición serán exigidos al aceptar eventos en T09. No se introduce un rol creador nuevo ni una ACL de tipos inexistente.
Contrato
POST recibe exclusivamente typeKey y data, con Idempotency-Key obligatorio
(1–200 caracteres ASCII visibles, sin espacios; una sola cabecera). Retorna 201,
Location y ETag fuerte. UUID, estado inicial, flujo, propietario, ámbito y revisión
son asignados por el servidor. Los datos deben satisfacer initialDataSchema y
dataSchema de la versión publicada; si falta initialDataSchema se usa dataSchema.
No se aplican defaults ni conversiones implícitas. Los schemas cerrados rechazados
en publicación protegen campos provenientes de proveedores.
Límite de datos: 256 KiB de JSON canónico UTF-8; cuerpo: 256 KiB + 4096 bytes, leído de forma acotada también sin Content-Length. Profundidad JSON máxima 64. Se rechazan claves duplicadas, JSON adicional, Unicode incompatible con JSONB, campos adicionales de comando y números fuera de los límites del codec T06. Tamaño excesivo retorna 413; sintaxis/comando inválido 400; datos fuera del schema 422. Tipo inexistente: 404. Sin flujo publicado activo, deshabilitado o cambiado concurrentemente durante la asignación: 409 INVALID_FLOW_STATE. Un comando fallido no reserva su clave y puede reintentarse tras corregir la causa.
GET retorna versión fijada, estado, datos, revisión, fechas y activeExecutionId si existe ejecución PENDING/RUNNING/RETRY_WAIT/RECONCILIATION_REQUIRED. La lectura SQL obtiene instancia/flujo/ejecución activa en un solo snapshot. Exige propietario y ámbito, incluso para FLOW_ADMIN; inexistente y ajeno retornan 404. El DTO omite owner/scope y los campos opcionales ausentes. La creación no copia la versión de flujo a request_instance: la consulta la obtiene de flow_id.
Idempotencia y transacción
Identidad de la clave: actor + ámbito + Idempotency-Key. Hash SHA-256 del comando canónico typeKey/data usando el codec T06: orden de propiedades y escala decimal equivalente no cambian el hash. Otra identidad tiene un espacio de claves distinto.
El repositorio usa READ COMMITTED y la misma DataSource para RequestStore y FlowStore. Antes de consultar idempotencia toma pg_advisory_xact_lock con 64 bits de SHA-256 sobre schema/actor/ámbito/clave encuadrados por longitud. Una colisión solo serializa claves distintas; la búsqueda y restricción única SQL siguen usando la identidad completa. No hay locks de proceso ni caché de respuestas en memoria.
Si existe registro y coincide el hash, se verifica acceso actual a la solicitud y se devuelve el snapshot original de creación (201, Location y ETag originales), aunque el flujo se haya retirado, el tipo esté deshabilitado o la solicitud haya avanzado. GET obtiene el estado actual. Un hash distinto retorna 409 IDEMPOTENCY_CONFLICT antes de resolver de nuevo el flujo activo.
Sin registro previo, la puerta T06 bloquea flujo y tipo en ese orden; luego valida datos, inserta solicitud con revisión 0, REQUEST_CREATED e idempotencia con respuesta original. Todo confirma o revierte junto. La auditoría incluye correlationId y referencias, sin duplicar datos personales. No se invocan servicios externos. Un cambio de activo detectado al tomar los bloqueos produce conflicto reintentable; no se asigna silenciosamente el flujo leído antes de la carrera. Un retiro espera una creación que ya tiene sus locks; una vez confirmado impide nuevas asignaciones.
Verificación
mvnw.cmd clean verify -Pmigration-tests-external, con MIGRATION_TEST_DB_* solo en
entorno, ejecuta la suite contra orquestador_test. RequestCreationIT reutiliza el
fixture aislado T07 (schema t07_UUID, LOGIN temporal y JWT RSA/JWKS local) con
administración apagada y API de solicitudes encendida. No se prueba una emisión
real de Keycloak ni se despliega el servicio en server02:8787.
Casos: contrato RequestView/Problem, replay y conflicto, versiones fijadas, retiro, separación de claves por actor/ámbito, acceso ajeno, validación/tamaño, concurrencia misma clave y comandos distintos, activación y retiro con barreras, ejecución activa, y rollback SQL real en auditoría/registro idempotente. RequestJsonTest verifica parseo estricto, canonicalización decimal y límites UTF-8.
Desde T09 se implementa también POST de eventos (EVENTS.md). La referencia anterior al carácter pendiente de eventos describe el alcance de T08; worker, tareas e historial siguen pendientes. REQUEST_API_ENABLED habilita ambos módulos.