Saltar a contenido

Novedades

Lo que cambia al actualizar tai-api, empezando por lo que rompe y qué hacer con ello. Después de actualizar, siempre: tai-api generate (la API y el cliente TypeScript), tai-api rbac push si cambian permisos y tai-api rules install para las reglas del asistente.

Sin publicar

Lo que se arregla

El aviso de root_path al arrancar la API. Desde FastAPI 0.146, FastAPI(root_path=...) está desaprobado («Configure the server's root path instead»), y la API generada lo usaba: avisaba al arrancar, y una versión de FastAPI que quite el parámetro la dejaría sin arrancar. Ahora la app se construye sin él, y un middleware ASGI de la propia app (RootPath, en app/__init__.py) pone el ROOT_PATH de config.py en cada petición, HTTP y WebSocket, como hacía FastAPI. No depende de cómo se arranque (tai-api dev, uvicorn en el contenedor, __main__.py), y ROOT_PATH sigue en un solo sitio: de él cuelgan la ruta de la cookie de sesión y los enlaces de la documentación.

Nada rompe. Tras actualizar, tai-api generate. Lo único distinto: app.root_path ya no lleva el ROOT_PATH (el de cada petición está en request.scope["root_path"], como antes).

POST /<tabla>/bulk lento en una tabla con owner(). Después de insertar, el endpoint comprobaba que cada fila creada fuera del usuario con una consulta por fila, y lo creado anidado con otras dos por fila: mil registros eran mil consultas, o miles con hijos anidados, cada una un viaje a la base de datos. Ahora es una consulta por tabla y nivel para todo el lote. En local, 10.000 registros pasan de 12-18 s a unos 2 s, y mil publicaciones con dos etiquetas cada una, de 4.000 consultas a 3. Con una base de datos remota la diferencia es mucho mayor: lo que se ahorra son los viajes.

Nada rompe. Lo único nuevo es que el 403 OWNER de /bulk dice qué registro fue: su field es la posición (3), o la posición y la relación si es algo creado anidado (3.etiquetas), que antes ya llevaba. Basta con tai-api generate.

Lo que se crea vuelve sin sus columnas vectoriales, como lo que se lee. POST /<tabla>, /bulk y el create de _tx devolvían el vector entero de cada fila creada, aunque ninguna lectura lo trae: mil filas con un vector de 3.072 dimensiones eran decenas de MB de respuesta. Ahora sale a null, y se sigue guardando. Lo hace el DAO de tai-sql (create/create_many con include_vectors=False por defecto, en la versión siguiente a la 0.8.0); tai-api no lo pide, así que al regenerar con ella basta. Lo único que rompe es leer el vector de la respuesta de un alta, que era el único sitio de la API donde salía.

0.7.2

Lo nuevo

Lo propio en el cliente TypeScript, como routers/custom/ en la API. Los endpoints de routers/custom/ se declaran en <output_dir>/custom/, que es del proyecto: tai-api generate no lo escribe, no lo borra y no lo retira. custom/index.ts exporta la lista de dominios con defineCustomResources, y createTaiClient la registra junto a los schemas, con sus tipos en TaiClient (y en useTai()):

// custom/index.ts
export const customResources = defineCustomResources({
  informes: informesResources,          // routers/custom/informes → api.informes
  facturas: facturasResources(REALM),   // un dominio que necesita algo más: lo recibe y devuelve el dominio
})

Ver Lo propio. generate avisa de un custom/index.ts que no exporta customResources —no lo registra— y de un import de custom/ que llega al index.ts del cliente, que sería un ciclo.

Lo que rompe

  • Un schema no puede llamarse custom: es una carpeta reservada del cliente, como core o auth. generate falla con la solución antes de escribir nada.
  • Un dominio propio no puede llamarse como algo del cliente: http, un schema y, con autenticación, session o rbac. No compila (satisfies), en vez de pisarlo en silencio. Si tenías la gestión de /rbac escrita a mano en custom/, quítala: el cliente la genera desde la 0.7.1 (api.rbac(realm)).
  • Un custom/index.ts que creaba el cliente (createTaiClient() dentro de custom/, el apaño de antes) no se registra: pásalo a la lista de defineCustomResources y crea la instancia fuera.

Tras actualizar, tai-api generate y tai-api rules install.

0.7.1

Lo nuevo

tai-api dev se nota en el frontend. Con la autorización apagada (tai-api dev, sin --auth), toda respuesta de la API lleva X-Tai-Auth: off, y el cliente TypeScript lo expone en session.state.authDisabled —en useSession() y en getSession()— y avisa una vez por consola (createTaiClient({ onAuthDisabled: false }) lo calla). Hasta ahora nada lo decía: el frontend exigía login y aplicaba los permisos del usuario mientras la API los ignoraba, sin un 403.

Con el frontend, tai-api dev sirve para desarrollar pantallas con un usuario de verdad: hace falta el schema auth con usuarios (tai-sql push -s auth --with-feed y tai-api rbac push), y la interfaz usa los permisos reales aunque la API no los compruebe. Para probar permisos y RLS, tai-api dev --auth. Ver Contra tai-api dev.

Nada rompe: la cabecera es nueva y solo aparece con la autorización apagada, y SessionState gana un campo. Tras actualizar, tai-api generate (la API y el cliente) y tai-api rules install.

0.7.0

El cliente TypeScript, rehecho a partir del primer proyecto que lo adoptó entero, y lo que ese proyecto le pidió a la API: transacciones de varias tablas, el registro en la respuesta de un PATCH, un modo estricto y la gestión de usuarios en el cliente.

Requiere tai-sql 0.8.0. Es la que trae buena parte de lo de abajo: las escrituras anidadas de un update en orden delete → update → create (quitar y volver a poner un hijo de clave natural en el mismo PATCH ya no es un 409), acotadas a la fila que se actualiza y con el RLS del usuario; un update que solo trae hijos se aplica (antes era un 404); in_<pk>; un in_ vacío que no casa con ninguna fila; bytes en base64; y las agregaciones imposibles como error.

Lo que rompe en la API

Afecta a cualquier frontend, use o no el cliente TypeScript.

Qué Hasta la 0.6.2 Ahora Qué hacer
Escrituras anidadas (los hijos dentro del POST o del PATCH del padre, también en /bulk) Bastaba el permiso del padre, y un update/delete anidado no miraba el RLS ni el owner() de la hija Piden el permiso de la tabla hija y le aplican su RLS y su owner(): 403 con details.required_permission y field en el anidado Dar al rol <hija>-create/-update/-delete (o <hija>-admin) en rbac/<realm>.py y tai-api rbac push. Ver Escrituras anidadas
Una hija anidada que no existe, no se alcanza o es de otro padre Se ignoraba, era un 500 o se aplicaba a la fila de otro padre 404 con field en el anidado, sin escribir nada Corregir la clave que se envía
Un update anidado que cambia la relación con el padre (mueve la hija a otro) Se aplicaba 422 con field en la columna Moverla por el PATCH de su tabla
Una agregación imposible (mean de un texto, max de un enumerado, agrupar por un decimal) 200, y la clave faltaba en data 422 con el motivo, la petición entera; max/min de un texto, que antes se descartaban, ahora valen Quitar la operación, o contar con group_by
Columnas bytes Se guardaba el texto que llegaba y se devolvía tal cual; un binario de verdad era un 500 Base64 estándar al leer (lo ya guardado como texto base64 sale igual); al escribir, base64 sin el prefijo data:…;base64, (con él, o lo que no es base64, 422) Quitar el prefijo data: al enviar (Los tipos)
PATCH /{tabla} masivo con relaciones en values Se perdían en silencio, o se aplicaban una vez por fila según la tabla 422 sobre values. El cuerpo es <Clase>UpdateMany, sin relaciones Quitarlas: los hijos, por el PATCH de cada padre o por su tabla
Un in_ vacío en un filtro de cuerpo (PATCH masivo, agg, los masivos de _tx) Contaba como «sin filtro»: el PATCH actualizaba todas las filas del usuario sin confirm_all, y agg agregaba la tabla entera No casa con ninguna fila: 0, o la agregación de cero filas Nada, salvo que se buscara eso: entonces, sin filtro y con confirm_all
El OpenAPI y la marca del contrato — El componente del cuerpo del masivo pasa de <Clase>Update a <Clase>UpdateMany; X-Tai-Contract cambia en todos los schemas y gana auth= Regenerar los clientes hechos desde el OpenAPI

Nada más cambia sin pedirlo: lo nuevo de la API va con una cabecera (Prefer, X-Tai-Strict) o en un endpoint nuevo (_tx, el DELETE de rol por ruta).

Lo que rompe en el cliente TypeScript

El cliente no es compatible con el de la 0.6: las mutaciones de TanStack son factorías, las keys cambian de forma, <Tabla>Read deja de tener relaciones (el tipo sale de los includes), el realm es un tipo y el modo estricto va por defecto. Paso a paso, en Migrar desde el cliente 0.6.

Y tai-api generate falla (A012) si el bloque frontend activa un adaptador o zod que el package.json del frontend no tiene: antes generaba un cliente que no compilaba.

Lo nuevo

La API

  • POST /{schema}/_tx: hasta 100 escrituras de varias tablas en una transacción, con $ref a lo creado por una anterior y el permiso de cada operación (Escritura).
  • Prefer: return=representation en PATCH /{tabla}/{clave}: el registro actualizado, con sus includes, en la misma petición.
  • X-Tai-Strict: 1: lo que la API ignoraría —un filtro, un order_by o un includes que no existen— es un 422 (El modo estricto).
  • DELETE /rbac/{realm}/user/{username}/role/{role_name}, sin cuerpo (la del cuerpo se conserva).
  • in_<pk>: la clave autoincremental se filtra por lista (?in_id=3&in_id=7), también en los masivos y en agg; no tiene igualdad ni rango (Filtros).

El cliente TypeScript

  • Compila con el tsconfig y pasa el eslint de las plantillas de Vite.
  • El registro con los includes que se pidieron, sin ?; una ruta que no existe o pasa de max_depth no compila. agg tipado por la petición.
  • api.<schema>.transaction tipada por operación; update(…, { returning: true, includes }).
  • api.rbac(realm) para la gestión de usuarios, session.updateProfile(), session.me(), canAdminRealm() y el tipo Realm.
  • TanStack: factorías que suman los callbacks a la invalidación, invalidación por tablas, publicQuery/publicMutation para lo propio, infinite, agg, connectQueryClient.
  • Un in_ vacío no llama a la API; fechas en hora de pared y bytes en base64 (core/dates, core/bytes); isTaiApiError(error, código), error.constraint, error.requiredPermission.
  • Segmentos opcionales en las rutas (/ordenes/:id/ciclos/:numero?), como react-router 7.

tai-api generate avisa de un ROOT_PATH distinto del base_path y de ALLOWED_ORIGINS = ["*"]: las dos cosas que pierden la sesión con cookie en producción.

Cómo actualizar

  1. pip install -U tai-api, que trae tai-sql 0.8.0 o posterior.
  2. tai-api generate.
  3. Los permisos de las tablas hijas en los roles que guardan hijos anidados, y tai-api rbac push. endpoints-<schema>.md (de tai-api rules install) lista las hijas de cada endpoint.
  4. En el frontend, npm run build y lo que diga el compilador: Migrar desde el cliente 0.6.
  5. tai-api rules install (y tai-sql rules install).