roble 1.3.0
roble: ^1.3.0 copied to clipboard
Cliente Flutter para la plataforma ROBLE de Uninorte OpenLab. Autenticación y CRUD sobre bases de datos PostgreSQL.
Changelog #
1.3.0 #
Añadido #
createMany(..., strict: true)lanzaRoblePartialInsertExceptionsi el servidor rechaza alguna fila, en vez de confiar en que quien llama reviseskipped. La excepción conserva el resultado completo, así que se sabe qué sí llegó a escribirse.RobleApiConfig.fromContractvalida sus argumentos y lanzaArgumentErrorsibaseUrlno es una URL o si elcontractIdestá vacío o sigue siendo un valor de ejemplo. Antes eso se manifestaba como un500incomprensible en la primera petición.- Pista en el
500de autenticación: es lo que devuelve Roble cuando el contrato no existe, así que ahora el mensaje lo sugiere en lugar de dejar soloError inesperado al autenticar. register(autoLogin: true)inicia sesión al terminar el registro y devuelve el perfil, igual quelogin. Por defecto esfalsey se sigue devolviendo el mensaje del servidor.registerWithVerificationno lo admite: hasta validar el código del correo la cuenta no puede entrar.login(persistSession: false)mantiene la sesión solo en memoria: sirve para todo mientras la app esté abierta, pero no sobrevive al reinicio. Es el "recordarme" de siempre. Ponerfalseborra además la sesión que hubiera guardada, para no dejar una sesión anterior recuperable en el dispositivo. El valor se respeta también en los refrescos automáticos posteriores.
Cambios incompatibles #
-
El servicio Realtime sale de la API pública.
db.realtimey los tiposRobleRealtime*se retiran mientras se estabiliza, junto con la dependenciasocket_io_client. El código sigue en el historial (v1.2.0) para reincorporarlo más adelante. -
Se recorta la superficie de datos a lo esencial. Desaparecen
createTable()ygetTableData()—usaban endpoints que ROBLE no documenta—,createTableFromTemplate()(las tablas se crean en la consola) y los envoltoriosgetAll()ygetWhere(), que eranread()con otro nombre. Se mantienegetById(), que sí aporta: devuelve una fila onull.Antes Ahora getAll(tabla)read(tabla)getWhere(tabla, col, valor)read(tabla, filters: {col: valor}) -
La sesión se persiste sola. El paquete usa
flutter_secure_storage(Keychain / Keystore / almacenamiento cifrado) por defecto, así que ya no hay que implementar ni pasar unRobleTokenStorage. El parámetrostoragesigue existiendo para sustituirlo en pruebas porRobleMemoryStorage.Esto añade la dependencia
flutter_secure_storagey sube el SDK mínimo a Dart 3.3 / Flutter 3.19. -
RobleApiConfigsolo se crea confromContract(). El constructor conauthUrl/dataUrl/realtimeUrlsueltas pasa a ser privado, y desaparecenfromStrings(),copyWith()yvalidate(). Las URLs se componen siempre a partir del host y del identificador del contrato. -
La sesión deja de ser manipulable desde fuera. Se eliminan de la API pública
accessToken,refreshToken,setTokens(),clearTokens()yonTokenUpdate, y elhttp.Clientpasa a ser privado. El paquete guarda los tokens, los adjunta a cada petición, los renueva ante un401y los borra al cerrar sesión; nada de eso necesita intervención de la app.En su lugar hay un único miembro de consulta:
bool get isLoggedInEquivalencias:
db.accessToken != null→db.isLoggedIn; guardar tokens a mano → pasar unstorageal constructor; restaurar sesión →restoreSession(); borrarla →logout(). Elhttp.Clientse sigue pudiendo inyectar por el constructor para pruebas, pero ya no se expone.
Cambiado #
-
restoreSession()ahora comprueba que la sesión siga viva. Además de cargar los tokens guardados, renueva el access token contra el servidor, así que untruesignifica que la sesión sirve de verdad y no solo que había tokens en el almacenamiento. Si el refresh token caducó o fue revocado, limpia la sesión y devuelvefalse.Los fallos de red no borran la sesión: se propagan
RobleApiNetworkExceptionyRobleApiTimeoutExceptionpara poder distinguir "sesión caducada" de "sin conexión".Con
restoreSession(verify: false)se mantiene el comportamiento anterior de solo leer el almacenamiento.
1.2.0 #
Cambios incompatibles #
-
currentUser()ahora devuelve el perfil del usuario, no los datos del token. Pasa deGET /verify-tokenaGET /me, que es lo que realmente interesa a una app:userId,email,name, elextradel registro y las fechas de creación y actualización. Antes devolvía los claims del JWT (sub,role,sessionId), que son detalle interno de la autenticación.Antes ( /verify-token)Ahora ( /me)subuserIdemailemaildbName,role,sessionId— — id,name,extra,createdAt,updatedAtSi leías
user['sub'], usauser['userId']. La librería ya no llama a/verify-token: la validez del token la gestiona ella sola con el refresco automático. -
login()devuelve el perfil del usuario, no los tokens. Tras autenticar pide/mey devuelve el mismo mapa que [currentUser]. Los tokens se guardan internamente y siguen disponibles enaccessTokenyrefreshToken.Si la llamada a
/mefalla, la sesión sigue activa: el error se propaga peroaccessTokenya tiene valor, así que se puede distinguir un fallo de credenciales de uno de perfil y reintentar concurrentUser().
1.1.0 #
Añadido #
- Persistencia de sesión opcional: la interfaz
RobleTokenStorage, el parámetrostoragedel constructor yrestoreSession(). El cliente guarda la sesión en cada login y refresco y la borra al cerrar sesión, así que sobrevive a un reinicio de la app. IncluyeRobleMemoryStoragepara pruebas. Sinstorage, los tokens siguen viviendo solo en memoria, como hasta ahora.
Corregido #
- Si el servidor rotara el refresh token al refrescar, ahora se conserva en
lugar de descartarse. Hoy
/refresh-tokensolo devuelveaccessToken, así que es prevención.
1.0.0 #
Primera versión publicada bajo el nombre roble. Sustituye al paquete
roble_api_database, cuya API se mantiene salvo por los cambios listados abajo.
Añadido #
RobleApiConfig.fromContract({baseUrl, contractId}): compone las rutas/auth/...y/database/...a partir del identificador del contrato.- Getters públicos
accessTokenyrefreshToken. - Callback
onTokenUpdate, invocado en cada cambio del access token. RobleApiConfig.timeoutconfigurable (30 s por defecto).- Documentación de todos los métodos públicos en el README.
- Cobertura completa de la API documentada de ROBLE (19 endpoints):
registerWithVerification(),verifyEmail(),resendCode(),currentUser()(/verify-token, el único endpoint que devuelve la identidad del usuario),forgotPassword(),resetPassword(),deleteAccount(),createMany(),executeQuery(),createTableFromTemplate()ypublicRead(). - Modelos
RobleInsertResult,RobleSkippedRecordyRobleQueryResult. - Servicio Realtime (
db.realtime): árbol JSON por proyecto con API al estilo Firebase —ref(),child(),parent,key,get(shallow:),set(),update(),push(),remove(), máscollections()yhealth(). - Suscripciones en tiempo real:
ref.onValueyref.onEventsobre WebSocket, conRobleRealtimeEvent,status,onStatusChangeyclose(). Un solo socket compartido, resuscripción automática al reconectar y cancelación por colección cuando no quedan escuchas. Añade la dependenciasocket_io_client. register()yregisterWithVerification()aceptan unextraopcional (Map<String, dynamic>) con campos adicionales que el backend guarda junto al usuario. Se envía en el campoextradel cuerpo, y se omite si es nulo.
Eliminado #
authHeadersydataHeadersdeRobleApiConfig, junto conwithBearerToken()y el getter muertodefaultHeaders. La API solo necesitaContent-TypeyAuthorization, y ambos los pone el cliente. Si necesitas cabeceras propias, inyecta unhttp.Clientque las añada.
Corregido #
PATCHse enviaba comoPUT. Elswitchde_makeRequestagrupaba ambos métodos enclient.put(), así querealtime.ref().update()sobrescribía el nodo en lugar de fusionar los campos. Ahora usaclient.patch().create()podía informar éxito sobre una fila rechazada. Enviaba el registro a/insert, que responde200con{inserted: [], skipped: [...]}cuando el servidor lo rechaza; al no haber nada eninserted, el método devolvía ese objeto como si fuera la fila creada, sin_idy sin error. Ahora usa/insert-one, que devuelve la fila directamente y falla con un error HTTP si la rechaza. Para varios registros,createMany()exponeskippeden lugar de descartarlo.
Cambiado #
- Las excepciones que lanza el cliente ahora son las subclases exportadas del
paquete:
RobleApiNetworkException,RobleApiTimeoutException,RobleApiFormatException,RobleApiHttpException(constatusCode) yRobleApiAuthException. Antes se lanzaba una clase interna homónima que nunca coincidía con la exportada, por lo queon RobleApiException catchjamás capturaba nada. logout()ya no recibeaccessToken: usa el token almacenado y limpia la sesión al terminar.- El punto de entrada de la librería pasa a ser
package:roble/roble.dart. - Un error HTTP ya no se envuelve como
Error inesperado: ...; se propaga comoRobleApiHttpExceptioncon el mensaje del servidor. - Tras un
401, solo el fallo del refresco produceRobleApiAuthException; si el reintento falla, se reporta el error real de esa petición.
Eliminado #
refreshAccessToken()yrefreshToken({refreshToken})públicos. El refresco del token es interno y automático ante un401.simulateGet(), que no hacía nada.RobleApiDataBase.timeoutDuration, reemplazado porRobleApiConfig.timeout.