Skip to content

Available Connections Catalog

Cambio de diseño (2026-04-23, connections-redesign): el catálogo ahora es gestionable. La tabla antes modelaba 5 filas seed inmutables; ahora modela una entidad CRUD-capaz con slug canónico, displayName editable y authType discriminador. Las conexiones connections.connections referencian el catálogo vía FK available_connection_id.

El módulo connections expone un catálogo de tipos de conexión disponibles. El panel consulta este catálogo para construir el flujo de "elegir integración" sin hardcodear la lista en el frontend.

Nueva forma de la fila

connections.available_connections:

ColumnaTipoRestricciónNotas
iduuidPK, DEFAULT uuidv7()
created_attimestamptzDEFAULT now()
updated_attimestamptzDEFAULT now()
slugvarchar(64)NOT NULL, UNIQUEIdentificador canónico (smtp, telegram, gmail, sheets, calendar). Esta es la llave contractual con el código en runtime.
display_namevarchar(120)NOT NULLNombre human-readable para UI (SMTP, Telegram, Gmail, Google Sheets, Google Calendar).
auth_typevarchar(32)NOT NULL'credentials' o 'oauth'. Discrimina qué ruta seguir al crear una conexión: formulario de credenciales vs flujo OAuth.
icontextnullable
covertextnullable
descriptiontextnullable

La columna name del diseño original fue reemplazada por slug (mismo conjunto de valores). Las nuevas columnas display_name, auth_type se poblan desde el registry in-code al hacer POST /available/seed.

Registry in-code (source of truth)

apps/api/src/modules/connections/domain/registry/available-connections.registry.ts es el contrato runtime entre el código (factories, scopes OAuth, AI tools) y las filas del catálogo. Ver Registry Contract para el detalle.

Los 5 slugs actuales y su authType:

SlugdisplayNameauthTypeOAuth scopes
smtpSMTPcredentials
telegramTelegramcredentials
gmailGmailoauthgmail.send, gmail.modify
sheetsGoogle Sheetsoauthspreadsheets, drive.file, drive.readonly
calendarGoogle Calendaroauthcalendar, calendar.events

Endpoints

Todas las rutas están montadas bajo /connections/available/* (el sub-controlador AvailableConnectionsController usa @Controller('available') y el módulo lo monta dentro del prefijo /connections).

GET /connections/available (list)

Devuelve el catálogo completo. Respuesta 200 OK con un array de IAvailableConnectionResponse:

json
[
  {
    "id": "01958653-0001-7045-8e6e-...",
    "slug": "calendar",
    "displayName": "Google Calendar",
    "authType": "oauth",
    "cover": null,
    "icon": null,
    "description": "Gestión de eventos de Google Calendar vía OAuth.",
    "createdAt": "2026-01-01T00:00:00.000Z",
    "updatedAt": "2026-01-01T00:00:00.000Z"
  }
]
  • Autenticación: @Auth() → JWT requerido.
  • Policy: read sobre connections.connection (cualquier miembro de la org puede leer el catálogo).
  • Orden: alfabético por slug.

GET /connections/available/:id (by id)

  • Ruta @Get(':id'). Valida el path con ParseUUIDPipe({ version: '7' }).
  • Error: 404 CONNECTION.CATALOG_ENTRY_NOT_FOUND si no existe.

GET /connections/available/:id/form-schema (discovery)

Devuelve el descriptor del formulario que la UI debe renderizar para crear una conexión de este tipo. Para entradas authType = 'credentials' regresa los campos del registry; para authType = 'oauth' regresa { fields: [] }.

json
// Para smtp (credentials)
{
  "fields": [
    { "name": "host",     "type": "string",   "required": true },
    { "name": "port",     "type": "number",   "required": true },
    { "name": "user",     "type": "string",   "required": true },
    { "name": "password", "type": "password", "required": true },
    { "name": "secure",   "type": "boolean",  "required": true }
  ]
}
json
// Para gmail (oauth)
{ "fields": [] }

El consumidor detecta el tipo de flujo leyendo authType del GET /available/:id — ver Auth Discovery Flow.

POST /connections/available/seed (platform admin)

Idempotente. Recorre el registry in-code y hace un upsertBySlug por entrada:

  • Si el slug no existe en la DB, inserta la fila con display_name, auth_type, icon, cover, description tomados del registry.
  • Si existe, refresca los campos "display" (display_name, icon, cover, description). No sobrescribe customizaciones que el admin haya hecho sobre icon/cover si el registry expone null — la regla es "el registry es la fuente; valores no-null ganan".

Policy: manage sobre connections.available-connection (reservado a admins de plataforma).

Se espera ejecutar este endpoint después de la migración 1 (ver Connections Redesign Migration Runbook) para poblar los campos derivados del registry.

PUT /connections/available/:id

Permite editar solo campos display (displayName, icon, cover, description). slug y authType son inmutables — la combinación define el contrato de código.

DELETE /connections/available/:id

  • 204 No Content si la entrada no está referenciada por ninguna connections.connections.
  • 409 CONNECTION.CATALOG_ENTRY_IN_USE con metadata: { id, count } si hay conexiones existentes usando esta entrada.

La restricción también se refuerza a nivel DB con un FK ON DELETE RESTRICT — el 409 se devuelve antes de tocar la DB para dar mejor UX.

Policies nuevas (Phase 4 CASL)

Subject connections.available-connection:

ActionScopeQuién
readorgmiembros autenticados (se reusa el subject connections.connection para reads)
manageplatformplatform admin
updateplatformplatform admin
deleteplatformplatform admin

Relación con connections.connections

Cada fila en connections.connections tiene available_connection_id (NOT NULL, FK). En la respuesta IConnectionResponse, el campo slug se hidrata del catálogo unido — el cliente NUNCA recibe el id del catálogo crudo junto a un slug obsoleto.

Nota histórica: antes del connections-redesign change, la tabla connections llevaba provider (smtp | telegram | google_gmail | google_workspace) y type. Después del Phase 4 esas columnas se eliminan — ver runbooks/connections-redesign-migration.md para el paso-a-paso de migración.

Páginas relacionadas