hefestoapicontainer

Directivas

Las directivas son pequeños fragmentos de funcionalidad que se invocan desde el flujo del api.yaml.

Catálogo de directivas reutilizables

Todas estas directivas están disponibles en hefesto-client/Directives/. Se añaden automáticamente al desplegar cualquier API.

🔀 Flujo y control

CacheDisabled
  Elimina las cabeceras de caché de la respuesta.
  Se usa normalmente en "before:" para garantizar que
  ningún endpoint haga cachear sus respuestas.

PropagateCorrelationId
  Lee la cabecera X-Correlation-Id de la request,
  y la propaga automáticamente en cualquier conexión HTTP que hagas después.

ExecuteIf
  Ejecuta otra directiva solo si se cumple una condición.
  Parámetros: execute (directive), if (condición)

ForTheWorker
  Encola las directivas posteriores para que las ejecute
  un worker en segundo plano (ver jobs).
  Parámetros: identifier (bool), delay (segundos)

CheckStatus
  Verifica que el status HTTP actual sea < 400.
  Si falla, lanza excepción con status 502 (o el indicado).
  Parámetros: status (int opcional), message (string opcional)

EnableDebug
  Activa el modo debug detallado de directivas.

🔒 Seguridad

CheckKey
  Compara una clave esperada con la enviada por el cliente.
  Si no coinciden, lanza excepción 401.
  Parámetros: expected, current

CheckLocalCall
  Lanza excepción 401 si la petición NO es una llamada
  local desde otra API en el mismo virtual-host.

RateLimit
  Limita el número de peticiones por día/minuto, tanto
  totales como por IP. Usa Redis como backend.
  Parámetros: path (identificador), dayTotal, minuteTotal,
  dayIp, minuteIp

🌐 Conexiones HTTP

Http
  Envía la request actual (con los modificadores aplicados)
  a otro host. Actúa como proxy.
  Parámetros: host (obligatorio), timeout, connectTimeout

Pull
  Realiza una petición GET (o POST si tiene body), valida
  la respuesta contra un JSON Schema y la guarda en memory.
  Opcionalmente cachea la respuesta en Redis.
  Parámetros: host, path, target, body, headers, queryParams,
  cache, verify, verifyStatus, serverSideError, timeout,
  connectTimeout

Push
  Realiza una petición POST/PUT para escritura, sin
  validación de esquema. Por defecto POST, o PUT si
  se usa "id".
  Parámetros: host, path, verb, id, body, headers,
  queryParams, verify, timeout, connectTimeout

✉️ Message y respuesta

ModifyMessage
  Modifica el mensaje: path, verb, headers, body,
  queryParams, status. Es la directiva más versátil
  para preparar requests y responses.

Ping
  Devuelve {"message":"pong"} con status 200.
  Útil para health checks.

IdResponse
  Devuelve {"id": "valor"} con status 201.
  Útil para respuestas de creación.
  Parámetros: id

ResultResponse
  Devuelve {"result": "valor"} con status 200.

✅ Validación

LoadAndValidateModel
  Carga un objeto (normalmente el body), lo valida
  contra un JSON Schema en Maps/, y si es correcto
  lo guarda en memory.
  Parámetros: source, target, server-side-error

LoadModel
  Carga un objeto directamente en memory sin validar.
  Parámetros: source, target

ValidateModel
  Valida un objeto ya cargado en memory contra un
  JSON Schema.
  Parámetros: name, server-side-error

🗄️ Base de datos

DatabaseConnect
  Conecta a PostgreSQL y guarda la conexión PDO
  en memory con la clave "db-conn".

DatabaseCreate
  Crea la base de datos de la API (aislada).
  Debe ejecutarse antes de DatabaseConnect.

DatabaseDrop
  Elimina la base de datos de la API.

DatabaseMigrate
  Ejecuta migraciones SQL desde Assets/sql/.
  Solo ejecuta las no ejecutadas previamente.
  Parámetros: files (ruta a los SQL)

DatabaseBeginTransaction
  Inicia una transacción (BEGIN).

DatabaseCommitTransaction
  Confirma una transacción (COMMIT).

📡 Redis

RedisSet
  Guarda un array en Redis con expiración.
  Parámetros: key, value (array), expire (segundos),
  global (bool, accesible desde otras APIs)

RedisGet
  Lee un array de Redis.
  Parámetros: key, target (clave en memory),
  global (bool)

⚡ Caché

CacheUrl
  Hace que nginx cachee la URL actual.
  Parámetros: expirationMinutes

CacheStatic
  Cachea automáticamente estáticos según su Content-Type
  (CSS, JS, imágenes, fuentes).
  Parámetros: expirationMinutes

💾 Archivos

SaveFile
  Guarda contenido en un fichero en el espacio reservado
  de la API.
  Parámetros: name, content

DeleteFile
  Elimina un fichero del espacio reservado.
  Parámetros: name, verify, status, message

ServeStatic
  Sirve un fichero estático (imagen, JSON, etc.).
  Parámetros: file, type, path, storage (bool),
  attachment (string)

ServeCompiledStatic
  Sirve CSS/JS compilados combinando múltiples ficheros
  en una sola respuesta comprimida.
  Parámetros: extension, type

🎨 Vistas

View
  Renderiza una plantilla Blade con datos.
  Parámetros: name, staticBasePath, css, js, data,
  fragments

🔧 Utilidades

Concat
  Concatena strings y guarda el resultado en memory.
  Parámetros: element1, element2, ..., target

StringToFriendlyUrl
  Convierte un string en una URL amigable (slug).
  Parámetros: string, target

CalculateIp
  Calcula la IP real del cliente (REMOTE_ADDR).
  La guarda en memory con clave "ip".

XmlToModel
  Convierte XML a array y lo guarda en memory.
  Parámetros: source, target

WriteToJob
  Escribe un valor en el estado del job actual.
  Parámetros: value

Event
  Dispara un evento Push a otra API local.
  Parámetros: target

⚠️ Errores

OnError
  Captura excepciones y devuelve un JSON con
  el error y el status almacenados en memory.
  Se usa en ERROR_FLOW.

ThrowError
  Lanza una excepción con status y mensaje.
  Parámetros: status, message

Uso de directivas existentes

Para utilizar una directiva existente, por ejemplo CheckKey, editamos el api.yaml de esta manera:

key: poems
endpoints:
  get /ping:
    CheckKeyPatata:
      directive: CheckKey
      expected: patata
      current: $.message.header.api-key
    Ping:
      directive: Ping

En el endpoint, creamos un elemento al que podemos dar cualquier nombre (en este caso CheckKeyPatata). Dentro usamos directive para referirnos a la directiva concreta que queremos ejecutar (CheckKey). Luego cada directiva puede admitir o no parámetros de configuración.

Crear directivas personalizadas

Si quisiéramos crear nuestra propia directiva, primero crearemos el fichero dentro de la carpeta Directives de la API:

mi-api/
├── api.yaml
├── Directives/
│   └── MyDirective.php
├── Maps/
└── Assets/

Las directivas personalizadas deben empezar con el encabezado exacto: <?php /*dlv-code-engine***/. Ejemplo básico:

<?php /*dlv-code-engine***/

$output = 'Hola hombre desconocido';

$name = $state->message()->getQueryParam('myname');

if ($name === 'isidoro') {
    $output = 'Hola creador';
}

$state->message()->setHeader('Content-Type','application/json');
$state->message()->setBodyAsArray([
    'text' => $output
]);

Luego se invoca desde el api.yaml:

key: poems
endpoints:
  get /ping:
    MyCustomDirective:
      directive: MyDirective

Paso de parámetros y ejecución anidada

Puedes parametrizar tu directiva pasando parámetros desde el api.yaml:

get /ping:
  MyCustomDirective:
    directive: MyDirective
    nameQueryParam: myname

Y recoger ese parámetro en la directiva usando el array $config:

<?php /*dlv-code-engine***/

$output = 'Hola hombre desconocido';

$name = $state->message()->getQueryParam($config['nameQueryParam']);

if ($name === 'isidoro') {
    $output = 'Hola creador';
}

$state->message()->setHeader('Content-Type','application/json');
$state->message()->setBodyAsArray([
    'text' => $output
]);

Una directiva puede llamar a otra directiva desde su código usando el método estático ::run():

<?php /*dlv-code-engine***/

CheckKey::run($state, [
    'expected' => 'patata',
    'current'  => $state->message()->getHeader('api-key')
]);

MyDirective::run($state, [
    'nameQueryParam' => 'myname'
]);

No existen limitaciones: puedes ejecutar cualquier lógica, acceder al estado, combinarlo con llamadas a otras directivas dentro de un bucle, etc. Sin embargo, es recomendable que desde el api.yaml se vean rápidamente las directivas que se ejecutan para lograr una mayor comprensión del flujo, y evitar delegar toda la funcionalidad a una super-directiva indescifrable.

Ejemplo avanzado: directiva de validación con base de datos

Este ejemplo es una directiva real (CheckRouteNotExist) que verifica si una ruta ya existe antes de crearla:

<?php /*dlv-code-engine***/

$db = $state->memory()->get('db-conn');

$sth = $db->prepare(
    'SELECT id FROM vias_bike_routes WHERE id = ?'
);
$sth->execute([$config['routeId']]);
$data = $sth->fetchObject();

if ($data) {
    $state->memory()->set('error.status', 409);
    $state->memory()->set('error.message', 'Route already exists');
    throw new \Exception();
}

Observa el patrón: se obtiene la conexión a BD de memory (previamente establecida con DatabaseConnect), se ejecuta una consulta y, si la ruta ya existe, se lanza una excepción con status 409 (Conflict) que será capturada por OnError en el ERROR_FLOW.