Callables / API
Toda mutación pasa por una Cloud Function (Principio: cero writes directos del
cliente). El frontend las invoca con el SDK de Firebase; el organizationId viaja
en el payload. Hay 25 functions desplegadas.
Errores tipados
Las callables usan códigos de HttpsError que el front mapea:
| Código | Significado |
|---|---|
unauthenticated | sin sesión |
invalid-argument | el payload no pasó la validación Zod (VALIDATION) |
permission-denied | el rol no alcanza, o no sos miembro de la org |
not-found | el recurso no existe |
failed-precondition | conflicto de version (CONFLICT) o integridad (INTEGRITY); details.reason lo discrimina |
Las mutaciones con locking reciben expectedVersion: si no coincide con la del
doc, devuelven failed-precondition (CONFLICT) en vez de pisar.
Ontología (Fase 2)
| Callable | Payload | Devuelve |
|---|---|---|
createOntologyType | { organizationId, type } | { id, version } |
updateOntologyType | { organizationId, expectedVersion, type } | { version } |
softDeleteOntologyType | { organizationId, id, expectedVersion } | ok |
restoreOntologyType | { organizationId, id, expectedVersion } | ok |
Objetos (Fase 3)
| Callable | Payload | Devuelve |
|---|---|---|
createObject | { organizationId, typeId, properties, links } | { id, version } |
updateObject | { organizationId, id, expectedVersion, properties, links } | { version } |
softDeleteObject / restoreObject | { organizationId, id, expectedVersion } | ok |
Gate: admin / editor / operator. Validación dirigida por el schema del
OntologyType (tipos, required, enum, integridad de references/links).
Actions (Fase 4)
| Callable | Payload | Devuelve |
|---|---|---|
createActionType | { organizationId, actionType } | { id, version } |
updateActionType | { organizationId, expectedVersion, actionType } | { version } |
softDeleteActionType / restoreActionType | { organizationId, id, expectedVersion } | ok |
executeAction | { organizationId, actionTypeId, parameters } | { executionId, status, affectedObjects } |
executeAction es el motor: gate por rol → valida params → tx atómica (stateCheck +
rules + version++) → side effects best-effort → ActionExecution inmutable.
DataSources (Fase 5)
| Callable | Payload | Devuelve |
|---|---|---|
createDataSource | { organizationId, dataSource, secret? } | { id, version, webhookToken? } |
updateDataSource | { organizationId, expectedVersion, dataSource, secret?, rotateWebhookToken? } | { version, webhookToken? } |
softDeleteDataSource / restoreDataSource | { organizationId, id, expectedVersion } | ok |
ingestDataSource | { organizationId, dataSourceId, csvStoragePath? } | { ingestionRunId, status, counts } |
El webhookToken plano se devuelve una sola vez (al crear o rotar); después solo
vive su hash. Las webhook no se disparan por ingestDataSource (son push externo).
Vistas (Fase 6)
| Callable | Payload | Devuelve |
|---|---|---|
saveView | { organizationId, view } | { id } |
deleteView | { organizationId, id } | ok |
Cualquier miembro guarda sus vistas (incl. viewer). Sin AuditEvent (es config
de UI).
HTTP públicas (onRequest)
| Endpoint | Qué hace |
|---|---|
submitAccessRequest | recibe una solicitud de acceso (Zod + rate-limit por IP) → /accessRequests |
ingestWebhook | recibe registros de un sistema externo (?org=&source= + header x-webhook-token) → ingesta |
Triggers y scheduler
| Función | Disparador |
|---|---|
onUserCreate | alta en Firebase Auth → crea /users/{uid} (invited) |
onAccessRequestCreated | nueva /accessRequests/{id} → email al admin (Resend) |
scheduledIngestion | Cloud Scheduler (cada 60 min) → pull de las fuentes rest_api con schedule |