diff --git a/CHANGELOG.md b/CHANGELOG.md index fe6daaf0..cdb46c5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Change Log +## 26.2.0 + +* Added: `DocumentsDB` and `VectorsDB` services for document and vector database access +* Added: `userId`, `emailHash` and `name` parameters to `Avatars.getPhoto()` +* Added: `cloudflare` and `resend` values to `OAuthProvider` enum +* Added: `auto` value to `ImageGravity` enum +* Updated: `X-Appwrite-Response-Format` header to `2.0.0` + ## 26.1.0 * Fixed: realtime policy-violation errors are now delivered to active subscription streams instead of thrown from the message handler diff --git a/README.md b/README.md index 74165e5c..584d890c 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,12 @@ [![pub package](https://img.shields.io/pub/v/appwrite?style=flat-square)](https://pub.dartlang.org/packages/appwrite) ![License](https://img.shields.io/github/license/appwrite/sdk-for-flutter.svg?style=flat-square) -![Version](https://img.shields.io/badge/api%20version-1.9.x-blue.svg?style=flat-square) +![Version](https://img.shields.io/badge/api%20version-2.0.x-blue.svg?style=flat-square) [![Build Status](https://img.shields.io/travis/com/appwrite/sdk-generator?style=flat-square)](https://travis-ci.com/appwrite/sdk-generator) [![Twitter Account](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite) [![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord) -**This SDK is compatible with Appwrite server version 1.9.x. For older versions, please check [previous releases](https://github.com/appwrite/sdk-for-flutter/releases).** +**This SDK targets Appwrite server version 2.0.x as shipped on Appwrite Cloud.** Self-hosted releases can lag behind Cloud — if you run an older self-hosted build, use a matching older SDK from [previous releases](https://github.com/appwrite/sdk-for-flutter/releases) when APIs differ. Appwrite is an open-source backend as a service server that abstracts and simplifies complex and repetitive development tasks behind a very simple to use REST API. Appwrite aims to help you develop your apps faster and in a more secure way. Use the Flutter SDK to integrate your app with the Appwrite server to easily start interacting with all of Appwrite backend APIs and tools. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs) @@ -19,7 +19,7 @@ Add this to your package's `pubspec.yaml` file: ```yml dependencies: - appwrite: ^26.0.0 + appwrite: ^26.2.0 ``` You can install packages from the command line: diff --git a/docs/examples/avatars/get-initials.md b/docs/examples/avatars/get-initials.md index c832a1c8..89e20e7c 100644 --- a/docs/examples/avatars/get-initials.md +++ b/docs/examples/avatars/get-initials.md @@ -15,7 +15,7 @@ Uint8List bytes = await avatars.getInitials( name: '', // optional width: 0, // optional height: 0, // optional - background: '', // optional + background: 'FFFFFF', // optional ); final file = File('path_to_file/filename.ext'); @@ -27,7 +27,7 @@ FutureBuilder( name: '', // optional width: 0, // optional height: 0, // optional - background: '', // optional + background: 'FFFFFF', // optional ), // Works for both public file and private file, for private files you need to be logged in builder: (context, snapshot) { return snapshot.hasData && snapshot.data != null diff --git a/docs/examples/avatars/get-photo.md b/docs/examples/avatars/get-photo.md index d9baf564..371b19d9 100644 --- a/docs/examples/avatars/get-photo.md +++ b/docs/examples/avatars/get-photo.md @@ -17,6 +17,9 @@ Uint8List bytes = await avatars.getPhoto( quality: 0, // optional output: 'png', // optional rating: 'g', // optional + userId: 'current()', // optional + emailHash: '', // optional + name: '', // optional ); final file = File('path_to_file/filename.ext'); @@ -30,6 +33,9 @@ FutureBuilder( quality: 0, // optional output: 'png', // optional rating: 'g', // optional + userId: 'current()', // optional + emailHash: '', // optional + name: '', // optional ), // Works for both public file and private file, for private files you need to be logged in builder: (context, snapshot) { return snapshot.hasData && snapshot.data != null diff --git a/docs/examples/databases/decrement-document-attribute.md b/docs/examples/databases/decrement-document-attribute.md index 83eaf379..0d178cff 100644 --- a/docs/examples/databases/decrement-document-attribute.md +++ b/docs/examples/databases/decrement-document-attribute.md @@ -11,8 +11,8 @@ Document result = await databases.decrementDocumentAttribute( databaseId: '', collectionId: '', documentId: '', - attribute: '', - value: 0, // optional + attribute: '', + value: 1, // optional min: 0, // optional transactionId: '', // optional ); diff --git a/docs/examples/databases/increment-document-attribute.md b/docs/examples/databases/increment-document-attribute.md index 22ec3431..05b3690e 100644 --- a/docs/examples/databases/increment-document-attribute.md +++ b/docs/examples/databases/increment-document-attribute.md @@ -11,9 +11,9 @@ Document result = await databases.incrementDocumentAttribute( databaseId: '', collectionId: '', documentId: '', - attribute: '', - value: 0, // optional - max: 0, // optional + attribute: '', + value: 1, // optional + max: 100, // optional transactionId: '', // optional ); ``` diff --git a/docs/examples/documentsdb/create-document.md b/docs/examples/documentsdb/create-document.md new file mode 100644 index 00000000..d4b564e2 --- /dev/null +++ b/docs/examples/documentsdb/create-document.md @@ -0,0 +1,26 @@ +```dart +import 'package:appwrite/appwrite.dart'; +import 'package:appwrite/permission.dart'; +import 'package:appwrite/role.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +DocumentsDB documentsDB = DocumentsDB(client); + +Document result = await documentsDB.createDocument( + databaseId: '', + collectionId: '', + documentId: '', + data: { + "username": "walter.obrien", + "email": "walter.obrien@example.com", + "fullName": "Walter O'Brien", + "age": 30, + "isAdmin": false + }, + permissions: [Permission.read(Role.any())], // optional + transactionId: '', // optional +); +``` diff --git a/docs/examples/documentsdb/create-documents.md b/docs/examples/documentsdb/create-documents.md new file mode 100644 index 00000000..6a508373 --- /dev/null +++ b/docs/examples/documentsdb/create-documents.md @@ -0,0 +1,16 @@ +```dart +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +DocumentsDB documentsDB = DocumentsDB(client); + +DocumentList result = await documentsDB.createDocuments( + databaseId: '', + collectionId: '', + documents: [], + transactionId: '', // optional +); +``` diff --git a/docs/examples/documentsdb/create-operations.md b/docs/examples/documentsdb/create-operations.md new file mode 100644 index 00000000..a4d1ecdd --- /dev/null +++ b/docs/examples/documentsdb/create-operations.md @@ -0,0 +1,24 @@ +```dart +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +DocumentsDB documentsDB = DocumentsDB(client); + +Transaction result = await documentsDB.createOperations( + transactionId: '', + operations: [ + { + "action": "create", + "databaseId": "", + "collectionId": "", + "documentId": "", + "data": { + "name": "Walter O'Brien" + } + } + ], // optional +); +``` diff --git a/docs/examples/documentsdb/create-transaction.md b/docs/examples/documentsdb/create-transaction.md new file mode 100644 index 00000000..2d9cb9f7 --- /dev/null +++ b/docs/examples/documentsdb/create-transaction.md @@ -0,0 +1,13 @@ +```dart +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +DocumentsDB documentsDB = DocumentsDB(client); + +Transaction result = await documentsDB.createTransaction( + ttl: 60, // optional +); +``` diff --git a/docs/examples/documentsdb/decrement-document-attribute.md b/docs/examples/documentsdb/decrement-document-attribute.md new file mode 100644 index 00000000..dcdac3aa --- /dev/null +++ b/docs/examples/documentsdb/decrement-document-attribute.md @@ -0,0 +1,19 @@ +```dart +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +DocumentsDB documentsDB = DocumentsDB(client); + +Document result = await documentsDB.decrementDocumentAttribute( + databaseId: '', + collectionId: '', + documentId: '', + attribute: '', + value: 1, // optional + min: 0, // optional + transactionId: '', // optional +); +``` diff --git a/docs/examples/documentsdb/delete-document.md b/docs/examples/documentsdb/delete-document.md new file mode 100644 index 00000000..8d59422e --- /dev/null +++ b/docs/examples/documentsdb/delete-document.md @@ -0,0 +1,16 @@ +```dart +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +DocumentsDB documentsDB = DocumentsDB(client); + +await documentsDB.deleteDocument( + databaseId: '', + collectionId: '', + documentId: '', + transactionId: '', // optional +); +``` diff --git a/docs/examples/documentsdb/delete-transaction.md b/docs/examples/documentsdb/delete-transaction.md new file mode 100644 index 00000000..9b95d948 --- /dev/null +++ b/docs/examples/documentsdb/delete-transaction.md @@ -0,0 +1,13 @@ +```dart +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +DocumentsDB documentsDB = DocumentsDB(client); + +await documentsDB.deleteTransaction( + transactionId: '', +); +``` diff --git a/docs/examples/documentsdb/get-document.md b/docs/examples/documentsdb/get-document.md new file mode 100644 index 00000000..45db46e6 --- /dev/null +++ b/docs/examples/documentsdb/get-document.md @@ -0,0 +1,17 @@ +```dart +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +DocumentsDB documentsDB = DocumentsDB(client); + +Document result = await documentsDB.getDocument( + databaseId: '', + collectionId: '', + documentId: '', + queries: [], // optional + transactionId: '', // optional +); +``` diff --git a/docs/examples/documentsdb/get-transaction.md b/docs/examples/documentsdb/get-transaction.md new file mode 100644 index 00000000..4b1a5f1f --- /dev/null +++ b/docs/examples/documentsdb/get-transaction.md @@ -0,0 +1,13 @@ +```dart +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +DocumentsDB documentsDB = DocumentsDB(client); + +Transaction result = await documentsDB.getTransaction( + transactionId: '', +); +``` diff --git a/docs/examples/documentsdb/increment-document-attribute.md b/docs/examples/documentsdb/increment-document-attribute.md new file mode 100644 index 00000000..c2c6e95c --- /dev/null +++ b/docs/examples/documentsdb/increment-document-attribute.md @@ -0,0 +1,19 @@ +```dart +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +DocumentsDB documentsDB = DocumentsDB(client); + +Document result = await documentsDB.incrementDocumentAttribute( + databaseId: '', + collectionId: '', + documentId: '', + attribute: '', + value: 1, // optional + max: 100, // optional + transactionId: '', // optional +); +``` diff --git a/docs/examples/documentsdb/list-documents.md b/docs/examples/documentsdb/list-documents.md new file mode 100644 index 00000000..92d2651c --- /dev/null +++ b/docs/examples/documentsdb/list-documents.md @@ -0,0 +1,18 @@ +```dart +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +DocumentsDB documentsDB = DocumentsDB(client); + +DocumentList result = await documentsDB.listDocuments( + databaseId: '', + collectionId: '', + queries: [], // optional + transactionId: '', // optional + total: false, // optional + ttl: 0, // optional +); +``` diff --git a/docs/examples/documentsdb/list-transactions.md b/docs/examples/documentsdb/list-transactions.md new file mode 100644 index 00000000..d2c09525 --- /dev/null +++ b/docs/examples/documentsdb/list-transactions.md @@ -0,0 +1,13 @@ +```dart +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +DocumentsDB documentsDB = DocumentsDB(client); + +TransactionList result = await documentsDB.listTransactions( + queries: [], // optional +); +``` diff --git a/docs/examples/documentsdb/update-document.md b/docs/examples/documentsdb/update-document.md new file mode 100644 index 00000000..1b9fba9a --- /dev/null +++ b/docs/examples/documentsdb/update-document.md @@ -0,0 +1,20 @@ +```dart +import 'package:appwrite/appwrite.dart'; +import 'package:appwrite/permission.dart'; +import 'package:appwrite/role.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +DocumentsDB documentsDB = DocumentsDB(client); + +Document result = await documentsDB.updateDocument( + databaseId: '', + collectionId: '', + documentId: '', + data: {}, // optional + permissions: [Permission.read(Role.any())], // optional + transactionId: '', // optional +); +``` diff --git a/docs/examples/documentsdb/update-transaction.md b/docs/examples/documentsdb/update-transaction.md new file mode 100644 index 00000000..ebcf17be --- /dev/null +++ b/docs/examples/documentsdb/update-transaction.md @@ -0,0 +1,15 @@ +```dart +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +DocumentsDB documentsDB = DocumentsDB(client); + +Transaction result = await documentsDB.updateTransaction( + transactionId: '', + commit: false, // optional + rollback: false, // optional +); +``` diff --git a/docs/examples/documentsdb/upsert-document.md b/docs/examples/documentsdb/upsert-document.md new file mode 100644 index 00000000..bdf3844b --- /dev/null +++ b/docs/examples/documentsdb/upsert-document.md @@ -0,0 +1,20 @@ +```dart +import 'package:appwrite/appwrite.dart'; +import 'package:appwrite/permission.dart'; +import 'package:appwrite/role.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +DocumentsDB documentsDB = DocumentsDB(client); + +Document result = await documentsDB.upsertDocument( + databaseId: '', + collectionId: '', + documentId: '', + data: {}, // optional + permissions: [Permission.read(Role.any())], // optional + transactionId: '', // optional +); +``` diff --git a/docs/examples/storage/create-file.md b/docs/examples/storage/create-file.md index 007abee4..7d8fbd13 100644 --- a/docs/examples/storage/create-file.md +++ b/docs/examples/storage/create-file.md @@ -15,6 +15,6 @@ File result = await storage.createFile( fileId: '', file: InputFile(path: './path-to-files/image.jpg', filename: 'image.jpg'), permissions: [Permission.read(Role.any())], // optional - folder: '', // optional + folder: 'photos/2026', // optional ); ``` diff --git a/docs/examples/storage/get-file-preview.md b/docs/examples/storage/get-file-preview.md index bfb15b91..bc811343 100644 --- a/docs/examples/storage/get-file-preview.md +++ b/docs/examples/storage/get-file-preview.md @@ -17,14 +17,14 @@ Uint8List bytes = await storage.getFilePreview( fileId: '', width: 0, // optional height: 0, // optional - gravity: enums.ImageGravity.center, // optional + gravity: enums.ImageGravity.auto, // optional quality: -1, // optional borderWidth: 0, // optional - borderColor: '', // optional + borderColor: 'FFFFFF', // optional borderRadius: 0, // optional opacity: 0, // optional rotation: -360, // optional - background: '', // optional + background: 'FFFFFF', // optional output: enums.ImageFormat.jpg, // optional token: '', // optional ); @@ -39,14 +39,14 @@ FutureBuilder( fileId: '', width: 0, // optional height: 0, // optional - gravity: enums.ImageGravity.center, // optional + gravity: enums.ImageGravity.auto, // optional quality: -1, // optional borderWidth: 0, // optional - borderColor: '', // optional + borderColor: 'FFFFFF', // optional borderRadius: 0, // optional opacity: 0, // optional rotation: -360, // optional - background: '', // optional + background: 'FFFFFF', // optional output: enums.ImageFormat.jpg, // optional token: '', // optional ), // Works for both public file and private file, for private files you need to be logged in diff --git a/docs/examples/tablesdb/decrement-row-column.md b/docs/examples/tablesdb/decrement-row-column.md index 699badfc..bee4987f 100644 --- a/docs/examples/tablesdb/decrement-row-column.md +++ b/docs/examples/tablesdb/decrement-row-column.md @@ -11,8 +11,8 @@ Row result = await tablesDB.decrementRowColumn( databaseId: '', tableId: '', rowId: '', - column: '', - value: 0, // optional + column: '', + value: 1, // optional min: 0, // optional transactionId: '', // optional ); diff --git a/docs/examples/tablesdb/increment-row-column.md b/docs/examples/tablesdb/increment-row-column.md index 1837125e..9b0e4769 100644 --- a/docs/examples/tablesdb/increment-row-column.md +++ b/docs/examples/tablesdb/increment-row-column.md @@ -11,9 +11,9 @@ Row result = await tablesDB.incrementRowColumn( databaseId: '', tableId: '', rowId: '', - column: '', - value: 0, // optional - max: 0, // optional + column: '', + value: 1, // optional + max: 100, // optional transactionId: '', // optional ); ``` diff --git a/docs/examples/vectorsdb/create-document.md b/docs/examples/vectorsdb/create-document.md new file mode 100644 index 00000000..fc39a7c1 --- /dev/null +++ b/docs/examples/vectorsdb/create-document.md @@ -0,0 +1,30 @@ +```dart +import 'package:appwrite/appwrite.dart'; +import 'package:appwrite/permission.dart'; +import 'package:appwrite/role.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +VectorsDB vectorsDB = VectorsDB(client); + +Document result = await vectorsDB.createDocument( + databaseId: '', + collectionId: '', + documentId: '', + data: { + "embeddings": [ + 0.12, + -0.55, + 0.88, + 1.02 + ], + "metadata": { + "key": "value" + } + }, + permissions: [Permission.read(Role.any())], // optional + transactionId: '', // optional +); +``` diff --git a/docs/examples/vectorsdb/create-operations.md b/docs/examples/vectorsdb/create-operations.md new file mode 100644 index 00000000..a994693e --- /dev/null +++ b/docs/examples/vectorsdb/create-operations.md @@ -0,0 +1,24 @@ +```dart +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +VectorsDB vectorsDB = VectorsDB(client); + +Transaction result = await vectorsDB.createOperations( + transactionId: '', + operations: [ + { + "action": "create", + "databaseId": "", + "collectionId": "", + "documentId": "", + "data": { + "name": "Walter O'Brien" + } + } + ], // optional +); +``` diff --git a/docs/examples/vectorsdb/create-query.md b/docs/examples/vectorsdb/create-query.md new file mode 100644 index 00000000..fa41ee5c --- /dev/null +++ b/docs/examples/vectorsdb/create-query.md @@ -0,0 +1,18 @@ +```dart +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +VectorsDB vectorsDB = VectorsDB(client); + +DocumentList result = await vectorsDB.createQuery( + databaseId: '', + collectionId: '', + queries: [], // optional + transactionId: '', // optional + total: false, // optional + ttl: 0, // optional +); +``` diff --git a/docs/examples/vectorsdb/create-transaction.md b/docs/examples/vectorsdb/create-transaction.md new file mode 100644 index 00000000..9c8c96d3 --- /dev/null +++ b/docs/examples/vectorsdb/create-transaction.md @@ -0,0 +1,13 @@ +```dart +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +VectorsDB vectorsDB = VectorsDB(client); + +Transaction result = await vectorsDB.createTransaction( + ttl: 60, // optional +); +``` diff --git a/docs/examples/vectorsdb/delete-document.md b/docs/examples/vectorsdb/delete-document.md new file mode 100644 index 00000000..7827d9c3 --- /dev/null +++ b/docs/examples/vectorsdb/delete-document.md @@ -0,0 +1,16 @@ +```dart +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +VectorsDB vectorsDB = VectorsDB(client); + +await vectorsDB.deleteDocument( + databaseId: '', + collectionId: '', + documentId: '', + transactionId: '', // optional +); +``` diff --git a/docs/examples/vectorsdb/delete-transaction.md b/docs/examples/vectorsdb/delete-transaction.md new file mode 100644 index 00000000..e4b82888 --- /dev/null +++ b/docs/examples/vectorsdb/delete-transaction.md @@ -0,0 +1,13 @@ +```dart +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +VectorsDB vectorsDB = VectorsDB(client); + +await vectorsDB.deleteTransaction( + transactionId: '', +); +``` diff --git a/docs/examples/vectorsdb/get-document.md b/docs/examples/vectorsdb/get-document.md new file mode 100644 index 00000000..28e544b3 --- /dev/null +++ b/docs/examples/vectorsdb/get-document.md @@ -0,0 +1,17 @@ +```dart +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +VectorsDB vectorsDB = VectorsDB(client); + +Document result = await vectorsDB.getDocument( + databaseId: '', + collectionId: '', + documentId: '', + queries: [], // optional + transactionId: '', // optional +); +``` diff --git a/docs/examples/vectorsdb/get-transaction.md b/docs/examples/vectorsdb/get-transaction.md new file mode 100644 index 00000000..1d191d09 --- /dev/null +++ b/docs/examples/vectorsdb/get-transaction.md @@ -0,0 +1,13 @@ +```dart +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +VectorsDB vectorsDB = VectorsDB(client); + +Transaction result = await vectorsDB.getTransaction( + transactionId: '', +); +``` diff --git a/docs/examples/vectorsdb/list-documents.md b/docs/examples/vectorsdb/list-documents.md new file mode 100644 index 00000000..03b4931d --- /dev/null +++ b/docs/examples/vectorsdb/list-documents.md @@ -0,0 +1,18 @@ +```dart +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +VectorsDB vectorsDB = VectorsDB(client); + +DocumentList result = await vectorsDB.listDocuments( + databaseId: '', + collectionId: '', + queries: [], // optional + transactionId: '', // optional + total: false, // optional + ttl: 0, // optional +); +``` diff --git a/docs/examples/vectorsdb/list-transactions.md b/docs/examples/vectorsdb/list-transactions.md new file mode 100644 index 00000000..fb7f83db --- /dev/null +++ b/docs/examples/vectorsdb/list-transactions.md @@ -0,0 +1,13 @@ +```dart +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +VectorsDB vectorsDB = VectorsDB(client); + +TransactionList result = await vectorsDB.listTransactions( + queries: [], // optional +); +``` diff --git a/docs/examples/vectorsdb/update-document.md b/docs/examples/vectorsdb/update-document.md new file mode 100644 index 00000000..8c2a9091 --- /dev/null +++ b/docs/examples/vectorsdb/update-document.md @@ -0,0 +1,20 @@ +```dart +import 'package:appwrite/appwrite.dart'; +import 'package:appwrite/permission.dart'; +import 'package:appwrite/role.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +VectorsDB vectorsDB = VectorsDB(client); + +Document result = await vectorsDB.updateDocument( + databaseId: '', + collectionId: '', + documentId: '', + data: {}, // optional + permissions: [Permission.read(Role.any())], // optional + transactionId: '', // optional +); +``` diff --git a/docs/examples/vectorsdb/update-transaction.md b/docs/examples/vectorsdb/update-transaction.md new file mode 100644 index 00000000..1abd8b02 --- /dev/null +++ b/docs/examples/vectorsdb/update-transaction.md @@ -0,0 +1,15 @@ +```dart +import 'package:appwrite/appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +VectorsDB vectorsDB = VectorsDB(client); + +Transaction result = await vectorsDB.updateTransaction( + transactionId: '', + commit: false, // optional + rollback: false, // optional +); +``` diff --git a/docs/examples/vectorsdb/upsert-document.md b/docs/examples/vectorsdb/upsert-document.md new file mode 100644 index 00000000..1367bba4 --- /dev/null +++ b/docs/examples/vectorsdb/upsert-document.md @@ -0,0 +1,20 @@ +```dart +import 'package:appwrite/appwrite.dart'; +import 'package:appwrite/permission.dart'; +import 'package:appwrite/role.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject(''); // Your project ID + +VectorsDB vectorsDB = VectorsDB(client); + +Document result = await vectorsDB.upsertDocument( + databaseId: '', + collectionId: '', + documentId: '', + data: {}, // optional + permissions: [Permission.read(Role.any())], // optional + transactionId: '', // optional +); +``` diff --git a/lib/appwrite.dart b/lib/appwrite.dart index f55bd86a..e2826d9f 100644 --- a/lib/appwrite.dart +++ b/lib/appwrite.dart @@ -1,8 +1,10 @@ /// Appwrite Flutter SDK /// -/// This SDK is compatible with Appwrite server version 1.9.x. -/// For older versions, please check -/// [previous releases](https://github.com/appwrite/sdk-for-flutter/releases). +/// This SDK targets Appwrite server version 2.0.x as shipped on Appwrite Cloud. +/// Self-hosted releases can lag behind Cloud — if you run an older self-hosted +/// build, use a matching older SDK from +/// [previous releases](https://github.com/appwrite/sdk-for-flutter/releases) +/// when APIs differ. library appwrite; import 'dart:async'; @@ -35,6 +37,7 @@ part 'operator.dart'; part 'services/account.dart'; part 'services/avatars.dart'; part 'services/databases.dart'; +part 'services/documents_db.dart'; part 'services/functions.dart'; part 'services/graphql.dart'; part 'services/locale.dart'; @@ -44,3 +47,4 @@ part 'services/presences.dart'; part 'services/storage.dart'; part 'services/tables_db.dart'; part 'services/teams.dart'; +part 'services/vectors_db.dart'; diff --git a/lib/query.dart b/lib/query.dart index 2e809e22..1ad97bdc 100644 --- a/lib/query.dart +++ b/lib/query.dart @@ -159,15 +159,13 @@ class Query { static String updatedBetween(String start, String end) => between('\$updatedAt', start, end); - static String or(List queries) => - Query._( + static String or(List queries) => Query._( 'or', null, queries.map((query) => jsonDecode(query)).toList(), ).toString(); - static String and(List queries) => - Query._( + static String and(List queries) => Query._( 'and', null, queries.map((query) => jsonDecode(query)).toList(), @@ -177,8 +175,7 @@ class Query { /// /// [attribute] The attribute containing the array to filter on. /// [queries] The list of query strings to match against array elements. - static String elemMatch(String attribute, List queries) => - Query._( + static String elemMatch(String attribute, List queries) => Query._( 'elemMatch', attribute, queries.map((query) => jsonDecode(query)).toList(), diff --git a/lib/services/avatars.dart b/lib/services/avatars.dart index ba09d21e..c57fb66e 100644 --- a/lib/services/avatars.dart +++ b/lib/services/avatars.dart @@ -241,16 +241,30 @@ class Avatars extends Service { return res.data; } - /// Returns the best available profile photo for the currently authenticated - /// user. The endpoint tries each source in priority order and returns the - /// first successful result: Gravatar, Libavatar, Appwrite Initials, built-in - /// static fallback file. + /// Returns the best available profile photo for a user. The endpoint tries + /// each source in priority order and returns the first successful result: + /// OAuth2 identity photo, Gravatar, Libravatar, Appwrite Initials, built-in + /// static fallback. + /// + /// Passing `userId` — `current()` for the authenticated user — resolves + /// the photo from everything known about that user: identity photos, email, + /// and name. An explicit `emailHash` or `name` then overrides just that value, + /// and the user's remaining sources stay in the chain. Without `userId`, + /// passing `emailHash` and/or `name` resolves the avatar from those values + /// alone: the hash is looked up on Gravatar and Libravatar, the name is + /// rendered as initials, and the session user stays out of the chain so their + /// own photo never shadows the avatar being asked for. When nothing is passed, + /// the photo resolves for the currently authenticated user. Emails are only + /// ever accepted pre-hashed, so no address ends up in a URL. Future getPhoto({ int? width, int? height, int? quality, String? output, String? rating, + String? userId, + String? emailHash, + String? name, }) async { final String apiPath = '/avatars/photo'; @@ -260,6 +274,9 @@ class Avatars extends Service { if (quality != null) 'quality': quality, if (output != null) 'output': output, if (rating != null) 'rating': rating, + if (userId != null) 'userId': userId, + if (emailHash != null) 'emailHash': emailHash, + if (name != null) 'name': name, }; final Map apiHeaders = { diff --git a/lib/services/databases.dart b/lib/services/databases.dart index adbd28cc..aaa74ff5 100644 --- a/lib/services/databases.dart +++ b/lib/services/databases.dart @@ -164,11 +164,11 @@ class Databases extends Service { required String transactionId, List? operations, }) async { - final String apiPath = '/databases/transactions/{transactionId}/operations' - .replaceAll( - '{transactionId}', - transactionId, - ); + final String apiPath = + '/databases/transactions/{transactionId}/operations'.replaceAll( + '{transactionId}', + transactionId, + ); final Map apiParams = { if (operations != null) 'operations': operations, diff --git a/lib/services/documents_db.dart b/lib/services/documents_db.dart new file mode 100644 index 00000000..0756f7d1 --- /dev/null +++ b/lib/services/documents_db.dart @@ -0,0 +1,589 @@ +part of '../appwrite.dart'; + +class DocumentsDB extends Service { + /// Initializes a [DocumentsDB] service + DocumentsDB(super.client); + + /// List transactions across all databases. + Future listTransactions({ + List? queries, + }) async { + final String apiPath = '/documentsdb/transactions'; + + final Map apiParams = { + if (queries != null) 'queries': queries, + }; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'accept': 'application/json', + }; + + final res = await client.call( + HttpMethod.get, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return models.TransactionList.fromMap(res.data); + } + + /// Create a new transaction. + Future createTransaction({ + int? ttl, + }) async { + final String apiPath = '/documentsdb/transactions'; + + final Map apiParams = { + if (ttl != null) 'ttl': ttl, + }; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'content-type': 'application/json', + 'accept': 'application/json', + }; + + final res = await client.call( + HttpMethod.post, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return models.Transaction.fromMap(res.data); + } + + /// Get a transaction by its unique ID. + Future getTransaction({ + required String transactionId, + }) async { + final String apiPath = + '/documentsdb/transactions/{transactionId}'.replaceAll( + '{transactionId}', + transactionId, + ); + + final Map apiParams = {}; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'accept': 'application/json', + }; + + final res = await client.call( + HttpMethod.get, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return models.Transaction.fromMap(res.data); + } + + /// Update a transaction, to either commit or roll back its operations. + Future updateTransaction({ + required String transactionId, + bool? commit, + bool? rollback, + }) async { + final String apiPath = + '/documentsdb/transactions/{transactionId}'.replaceAll( + '{transactionId}', + transactionId, + ); + + final Map apiParams = { + if (commit != null) 'commit': commit, + if (rollback != null) 'rollback': rollback, + }; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'content-type': 'application/json', + 'accept': 'application/json', + }; + + final res = await client.call( + HttpMethod.patch, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return models.Transaction.fromMap(res.data); + } + + /// Delete a transaction by its unique ID. + Future deleteTransaction({ + required String transactionId, + }) async { + final String apiPath = + '/documentsdb/transactions/{transactionId}'.replaceAll( + '{transactionId}', + transactionId, + ); + + final Map apiParams = {}; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'content-type': 'application/json', + }; + + final res = await client.call( + HttpMethod.delete, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return res.data; + } + + /// Create multiple operations in a single transaction. + Future createOperations({ + required String transactionId, + List? operations, + }) async { + final String apiPath = + '/documentsdb/transactions/{transactionId}/operations'.replaceAll( + '{transactionId}', + transactionId, + ); + + final Map apiParams = { + if (operations != null) 'operations': operations, + }; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'content-type': 'application/json', + 'accept': 'application/json', + }; + + final res = await client.call( + HttpMethod.post, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return models.Transaction.fromMap(res.data); + } + + /// Get a list of all the user's documents in a given collection. You can use + /// the query params to filter your results. + Future listDocuments({ + required String databaseId, + required String collectionId, + List? queries, + String? transactionId, + bool? total, + int? ttl, + }) async { + final String apiPath = + '/documentsdb/{databaseId}/collections/{collectionId}/documents' + .replaceAll( + '{databaseId}', + databaseId, + ) + .replaceAll( + '{collectionId}', + collectionId, + ); + + final Map apiParams = { + if (queries != null) 'queries': queries, + if (transactionId != null) 'transactionId': transactionId, + if (total != null) 'total': total, + if (ttl != null) 'ttl': ttl, + }; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'accept': 'application/json', + }; + + final res = await client.call( + HttpMethod.get, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return models.DocumentList.fromMap(res.data); + } + + /// Create a new Document. Before using this route, you should create a new + /// collection resource using either a [server + /// integration](https://appwrite.io/docs/server/databases#documentsDBCreateCollection) + /// API or directly from your database console. + Future createDocument({ + required String databaseId, + required String collectionId, + required String documentId, + required Map data, + List? permissions, + String? transactionId, + }) async { + final String apiPath = + '/documentsdb/{databaseId}/collections/{collectionId}/documents' + .replaceAll( + '{databaseId}', + databaseId, + ) + .replaceAll( + '{collectionId}', + collectionId, + ); + + final Map apiParams = { + 'documentId': documentId, + 'data': data, + if (permissions != null) 'permissions': permissions, + if (transactionId != null) 'transactionId': transactionId, + }; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'content-type': 'application/json', + 'accept': 'application/json', + }; + + final res = await client.call( + HttpMethod.post, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return models.Document.fromMap(res.data); + } + + /// Create new Documents. Before using this route, you should create a new + /// collection resource using either a [server + /// integration](https://appwrite.io/docs/server/databases#documentsDBCreateCollection) + /// API or directly from your database console. + Future createDocuments({ + required String databaseId, + required String collectionId, + required List documents, + String? transactionId, + }) async { + final String apiPath = + '/documentsdb/{databaseId}/collections/{collectionId}/documents' + .replaceAll( + '{databaseId}', + databaseId, + ) + .replaceAll( + '{collectionId}', + collectionId, + ); + + final Map apiParams = { + 'documents': documents, + if (transactionId != null) 'transactionId': transactionId, + }; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'content-type': 'application/json', + 'accept': 'application/json', + }; + + final res = await client.call( + HttpMethod.post, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return models.DocumentList.fromMap(res.data); + } + + /// Get a document by its unique ID. This endpoint response returns a JSON + /// object with the document data. + Future getDocument({ + required String databaseId, + required String collectionId, + required String documentId, + List? queries, + String? transactionId, + }) async { + final String apiPath = + '/documentsdb/{databaseId}/collections/{collectionId}/documents/{documentId}' + .replaceAll( + '{databaseId}', + databaseId, + ) + .replaceAll( + '{collectionId}', + collectionId, + ) + .replaceAll( + '{documentId}', + documentId, + ); + + final Map apiParams = { + if (queries != null) 'queries': queries, + if (transactionId != null) 'transactionId': transactionId, + }; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'accept': 'application/json', + }; + + final res = await client.call( + HttpMethod.get, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return models.Document.fromMap(res.data); + } + + /// Create or update a Document. Before using this route, you should create a + /// new collection resource using either a [server + /// integration](https://appwrite.io/docs/server/databases#documentsDBCreateCollection) + /// API or directly from your database console. + Future upsertDocument({ + required String databaseId, + required String collectionId, + required String documentId, + Map? data, + List? permissions, + String? transactionId, + }) async { + final String apiPath = + '/documentsdb/{databaseId}/collections/{collectionId}/documents/{documentId}' + .replaceAll( + '{databaseId}', + databaseId, + ) + .replaceAll( + '{collectionId}', + collectionId, + ) + .replaceAll( + '{documentId}', + documentId, + ); + + final Map apiParams = { + if (data != null) 'data': data, + if (permissions != null) 'permissions': permissions, + if (transactionId != null) 'transactionId': transactionId, + }; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'content-type': 'application/json', + 'accept': 'application/json', + }; + + final res = await client.call( + HttpMethod.put, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return models.Document.fromMap(res.data); + } + + /// Update a document by its unique ID. Using the patch method you can pass + /// only specific fields that will get updated. + Future updateDocument({ + required String databaseId, + required String collectionId, + required String documentId, + Map? data, + List? permissions, + String? transactionId, + }) async { + final String apiPath = + '/documentsdb/{databaseId}/collections/{collectionId}/documents/{documentId}' + .replaceAll( + '{databaseId}', + databaseId, + ) + .replaceAll( + '{collectionId}', + collectionId, + ) + .replaceAll( + '{documentId}', + documentId, + ); + + final Map apiParams = { + if (data != null) 'data': data, + if (permissions != null) 'permissions': permissions, + if (transactionId != null) 'transactionId': transactionId, + }; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'content-type': 'application/json', + 'accept': 'application/json', + }; + + final res = await client.call( + HttpMethod.patch, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return models.Document.fromMap(res.data); + } + + /// Delete a document by its unique ID. + Future deleteDocument({ + required String databaseId, + required String collectionId, + required String documentId, + String? transactionId, + }) async { + final String apiPath = + '/documentsdb/{databaseId}/collections/{collectionId}/documents/{documentId}' + .replaceAll( + '{databaseId}', + databaseId, + ) + .replaceAll( + '{collectionId}', + collectionId, + ) + .replaceAll( + '{documentId}', + documentId, + ); + + final Map apiParams = { + if (transactionId != null) 'transactionId': transactionId, + }; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'content-type': 'application/json', + }; + + final res = await client.call( + HttpMethod.delete, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return res.data; + } + + /// Decrement a specific column of a row by a given value. + Future decrementDocumentAttribute({ + required String databaseId, + required String collectionId, + required String documentId, + required String attribute, + double? value, + double? min, + String? transactionId, + }) async { + final String apiPath = + '/documentsdb/{databaseId}/collections/{collectionId}/documents/{documentId}/{attribute}/decrement' + .replaceAll( + '{databaseId}', + databaseId, + ) + .replaceAll( + '{collectionId}', + collectionId, + ) + .replaceAll( + '{documentId}', + documentId, + ) + .replaceAll( + '{attribute}', + attribute, + ); + + final Map apiParams = { + if (value != null) 'value': value, + if (min != null) 'min': min, + if (transactionId != null) 'transactionId': transactionId, + }; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'content-type': 'application/json', + 'accept': 'application/json', + }; + + final res = await client.call( + HttpMethod.patch, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return models.Document.fromMap(res.data); + } + + /// Increment a specific column of a row by a given value. + Future incrementDocumentAttribute({ + required String databaseId, + required String collectionId, + required String documentId, + required String attribute, + double? value, + double? max, + String? transactionId, + }) async { + final String apiPath = + '/documentsdb/{databaseId}/collections/{collectionId}/documents/{documentId}/{attribute}/increment' + .replaceAll( + '{databaseId}', + databaseId, + ) + .replaceAll( + '{collectionId}', + collectionId, + ) + .replaceAll( + '{documentId}', + documentId, + ) + .replaceAll( + '{attribute}', + attribute, + ); + + final Map apiParams = { + if (value != null) 'value': value, + if (max != null) 'max': max, + if (transactionId != null) 'transactionId': transactionId, + }; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'content-type': 'application/json', + 'accept': 'application/json', + }; + + final res = await client.call( + HttpMethod.patch, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return models.Document.fromMap(res.data); + } +} diff --git a/lib/services/organization.dart b/lib/services/organization.dart index 70e4576a..1dd88dac 100644 --- a/lib/services/organization.dart +++ b/lib/services/organization.dart @@ -69,11 +69,11 @@ class Organization extends Service { Future getInstallation({ required String installationId, }) async { - final String apiPath = '/organization/installations/{installationId}' - .replaceAll( - '{installationId}', - installationId, - ); + final String apiPath = + '/organization/installations/{installationId}'.replaceAll( + '{installationId}', + installationId, + ); final Map apiParams = {}; @@ -100,11 +100,11 @@ class Organization extends Service { required String installationId, String? authorizationDetails, }) async { - final String apiPath = '/organization/installations/{installationId}' - .replaceAll( - '{installationId}', - installationId, - ); + final String apiPath = + '/organization/installations/{installationId}'.replaceAll( + '{installationId}', + installationId, + ); final Map apiParams = { if (authorizationDetails != null) @@ -133,11 +133,11 @@ class Organization extends Service { Future deleteInstallation({ required String installationId, }) async { - final String apiPath = '/organization/installations/{installationId}' - .replaceAll( - '{installationId}', - installationId, - ); + final String apiPath = + '/organization/installations/{installationId}'.replaceAll( + '{installationId}', + installationId, + ); final Map apiParams = {}; diff --git a/lib/services/tables_db.dart b/lib/services/tables_db.dart index 246ab939..d7f23f39 100644 --- a/lib/services/tables_db.dart +++ b/lib/services/tables_db.dart @@ -146,11 +146,11 @@ class TablesDB extends Service { required String transactionId, List? operations, }) async { - final String apiPath = '/tablesdb/transactions/{transactionId}/operations' - .replaceAll( - '{transactionId}', - transactionId, - ); + final String apiPath = + '/tablesdb/transactions/{transactionId}/operations'.replaceAll( + '{transactionId}', + transactionId, + ); final Map apiParams = { if (operations != null) 'operations': operations, diff --git a/lib/services/vectors_db.dart b/lib/services/vectors_db.dart new file mode 100644 index 00000000..08ba3e81 --- /dev/null +++ b/lib/services/vectors_db.dart @@ -0,0 +1,488 @@ +part of '../appwrite.dart'; + +class VectorsDB extends Service { + /// Initializes a [VectorsDB] service + VectorsDB(super.client); + + /// List transactions across all databases. + Future listTransactions({ + List? queries, + }) async { + final String apiPath = '/vectorsdb/transactions'; + + final Map apiParams = { + if (queries != null) 'queries': queries, + }; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'accept': 'application/json', + }; + + final res = await client.call( + HttpMethod.get, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return models.TransactionList.fromMap(res.data); + } + + /// Create a new transaction. + Future createTransaction({ + int? ttl, + }) async { + final String apiPath = '/vectorsdb/transactions'; + + final Map apiParams = { + if (ttl != null) 'ttl': ttl, + }; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'content-type': 'application/json', + 'accept': 'application/json', + }; + + final res = await client.call( + HttpMethod.post, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return models.Transaction.fromMap(res.data); + } + + /// Get a transaction by its unique ID. + Future getTransaction({ + required String transactionId, + }) async { + final String apiPath = '/vectorsdb/transactions/{transactionId}'.replaceAll( + '{transactionId}', + transactionId, + ); + + final Map apiParams = {}; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'accept': 'application/json', + }; + + final res = await client.call( + HttpMethod.get, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return models.Transaction.fromMap(res.data); + } + + /// Update a transaction, to either commit or roll back its operations. + Future updateTransaction({ + required String transactionId, + bool? commit, + bool? rollback, + }) async { + final String apiPath = '/vectorsdb/transactions/{transactionId}'.replaceAll( + '{transactionId}', + transactionId, + ); + + final Map apiParams = { + if (commit != null) 'commit': commit, + if (rollback != null) 'rollback': rollback, + }; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'content-type': 'application/json', + 'accept': 'application/json', + }; + + final res = await client.call( + HttpMethod.patch, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return models.Transaction.fromMap(res.data); + } + + /// Delete a transaction by its unique ID. + Future deleteTransaction({ + required String transactionId, + }) async { + final String apiPath = '/vectorsdb/transactions/{transactionId}'.replaceAll( + '{transactionId}', + transactionId, + ); + + final Map apiParams = {}; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'content-type': 'application/json', + }; + + final res = await client.call( + HttpMethod.delete, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return res.data; + } + + /// Create multiple operations in a single transaction. + Future createOperations({ + required String transactionId, + List? operations, + }) async { + final String apiPath = + '/vectorsdb/transactions/{transactionId}/operations'.replaceAll( + '{transactionId}', + transactionId, + ); + + final Map apiParams = { + if (operations != null) 'operations': operations, + }; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'content-type': 'application/json', + 'accept': 'application/json', + }; + + final res = await client.call( + HttpMethod.post, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return models.Transaction.fromMap(res.data); + } + + /// Get a list of all the user's documents in a given collection. You can use + /// the query params to filter your results. + Future listDocuments({ + required String databaseId, + required String collectionId, + List? queries, + String? transactionId, + bool? total, + int? ttl, + }) async { + final String apiPath = + '/vectorsdb/{databaseId}/collections/{collectionId}/documents' + .replaceAll( + '{databaseId}', + databaseId, + ) + .replaceAll( + '{collectionId}', + collectionId, + ); + + final Map apiParams = { + if (queries != null) 'queries': queries, + if (transactionId != null) 'transactionId': transactionId, + if (total != null) 'total': total, + if (ttl != null) 'ttl': ttl, + }; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'accept': 'application/json', + }; + + final res = await client.call( + HttpMethod.get, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return models.DocumentList.fromMap(res.data); + } + + /// Create a new Document. Before using this route, you should create a new + /// collection resource using either a [server + /// integration](https://appwrite.io/docs/server/databases#documentsDBCreateCollection) + /// API or directly from your database console. + Future createDocument({ + required String databaseId, + required String collectionId, + required String documentId, + required Map data, + List? permissions, + String? transactionId, + }) async { + final String apiPath = + '/vectorsdb/{databaseId}/collections/{collectionId}/documents' + .replaceAll( + '{databaseId}', + databaseId, + ) + .replaceAll( + '{collectionId}', + collectionId, + ); + + final Map apiParams = { + 'documentId': documentId, + 'data': data, + if (permissions != null) 'permissions': permissions, + if (transactionId != null) 'transactionId': transactionId, + }; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'content-type': 'application/json', + 'accept': 'application/json', + }; + + final res = await client.call( + HttpMethod.post, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return models.Document.fromMap(res.data); + } + + /// Get a list of all the user's documents in a given collection using a POST + /// request. This behaves identically to the list documents endpoint but + /// accepts the queries in the request body, allowing much larger `queries` + /// arrays than can fit in a URL query string. + Future createQuery({ + required String databaseId, + required String collectionId, + List? queries, + String? transactionId, + bool? total, + int? ttl, + }) async { + final String apiPath = + '/vectorsdb/{databaseId}/collections/{collectionId}/documents/query' + .replaceAll( + '{databaseId}', + databaseId, + ) + .replaceAll( + '{collectionId}', + collectionId, + ); + + final Map apiParams = { + if (queries != null) 'queries': queries, + if (transactionId != null) 'transactionId': transactionId, + if (total != null) 'total': total, + if (ttl != null) 'ttl': ttl, + }; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'content-type': 'application/json', + 'accept': 'application/json', + }; + + final res = await client.call( + HttpMethod.post, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return models.DocumentList.fromMap(res.data); + } + + /// Get a document by its unique ID. This endpoint response returns a JSON + /// object with the document data. + Future getDocument({ + required String databaseId, + required String collectionId, + required String documentId, + List? queries, + String? transactionId, + }) async { + final String apiPath = + '/vectorsdb/{databaseId}/collections/{collectionId}/documents/{documentId}' + .replaceAll( + '{databaseId}', + databaseId, + ) + .replaceAll( + '{collectionId}', + collectionId, + ) + .replaceAll( + '{documentId}', + documentId, + ); + + final Map apiParams = { + if (queries != null) 'queries': queries, + if (transactionId != null) 'transactionId': transactionId, + }; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'accept': 'application/json', + }; + + final res = await client.call( + HttpMethod.get, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return models.Document.fromMap(res.data); + } + + /// Create or update a Document. Before using this route, you should create a + /// new collection resource using either a [server + /// integration](https://appwrite.io/docs/server/databases#documentsDBCreateCollection) + /// API or directly from your database console. + Future upsertDocument({ + required String databaseId, + required String collectionId, + required String documentId, + Map? data, + List? permissions, + String? transactionId, + }) async { + final String apiPath = + '/vectorsdb/{databaseId}/collections/{collectionId}/documents/{documentId}' + .replaceAll( + '{databaseId}', + databaseId, + ) + .replaceAll( + '{collectionId}', + collectionId, + ) + .replaceAll( + '{documentId}', + documentId, + ); + + final Map apiParams = { + if (data != null) 'data': data, + if (permissions != null) 'permissions': permissions, + if (transactionId != null) 'transactionId': transactionId, + }; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'content-type': 'application/json', + 'accept': 'application/json', + }; + + final res = await client.call( + HttpMethod.put, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return models.Document.fromMap(res.data); + } + + /// Update a document by its unique ID. Using the patch method you can pass + /// only specific fields that will get updated. + Future updateDocument({ + required String databaseId, + required String collectionId, + required String documentId, + Map? data, + List? permissions, + String? transactionId, + }) async { + final String apiPath = + '/vectorsdb/{databaseId}/collections/{collectionId}/documents/{documentId}' + .replaceAll( + '{databaseId}', + databaseId, + ) + .replaceAll( + '{collectionId}', + collectionId, + ) + .replaceAll( + '{documentId}', + documentId, + ); + + final Map apiParams = { + if (data != null) 'data': data, + if (permissions != null) 'permissions': permissions, + if (transactionId != null) 'transactionId': transactionId, + }; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'content-type': 'application/json', + 'accept': 'application/json', + }; + + final res = await client.call( + HttpMethod.patch, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return models.Document.fromMap(res.data); + } + + /// Delete a document by its unique ID. + Future deleteDocument({ + required String databaseId, + required String collectionId, + required String documentId, + String? transactionId, + }) async { + final String apiPath = + '/vectorsdb/{databaseId}/collections/{collectionId}/documents/{documentId}' + .replaceAll( + '{databaseId}', + databaseId, + ) + .replaceAll( + '{collectionId}', + collectionId, + ) + .replaceAll( + '{documentId}', + documentId, + ); + + final Map apiParams = { + if (transactionId != null) 'transactionId': transactionId, + }; + + final Map apiHeaders = { + 'X-Appwrite-Project': client.config['project'] ?? '', + 'content-type': 'application/json', + }; + + final res = await client.call( + HttpMethod.delete, + path: apiPath, + params: apiParams, + headers: apiHeaders, + ); + + return res.data; + } +} diff --git a/lib/src/client.dart b/lib/src/client.dart index 6fe9425c..dbfe4bdf 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -27,7 +27,8 @@ abstract class Client { factory Client({ String endPoint = 'https://cloud.appwrite.io/v1', bool selfSigned = false, - }) => createClient(endPoint: endPoint, selfSigned: selfSigned); + }) => + createClient(endPoint: endPoint, selfSigned: selfSigned); /// Handle OAuth2 session creation. Future webAuth(Uri url, {String? callbackUrlScheme}); diff --git a/lib/src/client_browser.dart b/lib/src/client_browser.dart index 63a87c51..fdf011c8 100644 --- a/lib/src/client_browser.dart +++ b/lib/src/client_browser.dart @@ -40,8 +40,8 @@ class ClientBrowser extends ClientBase with ClientMixin { 'x-sdk-name': 'Flutter', 'x-sdk-platform': 'client', 'x-sdk-language': 'flutter', - 'x-sdk-version': '26.0.0', - 'X-Appwrite-Response-Format': '1.9.6', + 'x-sdk-version': '26.2.0', + 'X-Appwrite-Response-Format': '2.0.0', }; config = {}; diff --git a/lib/src/client_io.dart b/lib/src/client_io.dart index ee52e2ea..3ccefd87 100644 --- a/lib/src/client_io.dart +++ b/lib/src/client_io.dart @@ -47,10 +47,9 @@ class ClientIO extends ClientBase with ClientMixin { String endPoint = 'https://cloud.appwrite.io/v1', this.selfSigned = false, }) : _endPoint = endPoint { - _nativeClient = - HttpClient() - ..badCertificateCallback = - ((X509Certificate cert, String host, int port) => selfSigned); + _nativeClient = HttpClient() + ..badCertificateCallback = + ((X509Certificate cert, String host, int port) => selfSigned); _httpClient = IOClient(_nativeClient); _endPointRealtime = endPoint .replaceFirst('https://', 'wss://') @@ -60,8 +59,8 @@ class ClientIO extends ClientBase with ClientMixin { 'x-sdk-name': 'Flutter', 'x-sdk-platform': 'client', 'x-sdk-language': 'flutter', - 'x-sdk-version': '26.0.0', - 'X-Appwrite-Response-Format': '1.9.6', + 'x-sdk-version': '26.2.0', + 'X-Appwrite-Response-Format': '2.0.0', }; config = {}; @@ -358,13 +357,8 @@ class ClientIO extends ClientBase with ClientMixin { final totalChunks = (size / chunkSize).ceil(); - Future uploadChunk( - int index, - int start, - int end, - String? id, [ - RandomAccessFile? raf, - ]) async { + Future uploadChunk(int index, int start, int end, String? id, + [RandomAccessFile? raf]) async { List chunk = []; if (file.bytes != null) { chunk = file.bytes!.getRange(start, end).toList(); @@ -488,10 +482,9 @@ class ClientIO extends ClientBase with ClientMixin { Future webAuth(Uri url, {String? callbackUrlScheme}) { return FlutterWebAuth2.authenticate( url: url.toString(), - callbackUrlScheme: - callbackUrlScheme != null && _customSchemeAllowed - ? callbackUrlScheme - : "appwrite-callback-${config['project']!}", + callbackUrlScheme: callbackUrlScheme != null && _customSchemeAllowed + ? callbackUrlScheme + : "appwrite-callback-${config['project']!}", options: const FlutterWebAuth2Options( useWebview: false, ), @@ -506,10 +499,9 @@ class ClientIO extends ClientBase with ClientMixin { final parsed = jsonDecode(error); if (parsed is Map) { final codeValue = parsed['code']; - final int? code = - codeValue is int - ? codeValue - : int.tryParse('${codeValue ?? ''}'); + final int? code = codeValue is int + ? codeValue + : int.tryParse('${codeValue ?? ''}'); throw AppwriteException( parsed['message']?.toString() ?? error, code, diff --git a/lib/src/client_mixin.dart b/lib/src/client_mixin.dart index 3ca20708..952d7114 100644 --- a/lib/src/client_mixin.dart +++ b/lib/src/client_mixin.dart @@ -92,9 +92,8 @@ mixin ClientMixin { } if (res.statusCode >= 400) { - final isJson = (res.headers['content-type'] ?? '').contains( - 'application/json', - ); + final isJson = + (res.headers['content-type'] ?? '').contains('application/json'); // Empty bodies still arrive with application/json (e.g. truncated // proxy/edge errors). Avoid FormatException from json.decode(''). if (isJson && res.body.isNotEmpty) { @@ -142,12 +141,10 @@ mixin ClientMixin { return http.Response( '', streamedResponse.statusCode, - headers: streamedResponse.headers.map( - (k, v) => - k.toLowerCase() == 'content-type' - ? MapEntry(k, 'text/plain') - : MapEntry(k, v), - ), + headers: streamedResponse.headers.map((k, v) => + k.toLowerCase() == 'content-type' + ? MapEntry(k, 'text/plain') + : MapEntry(k, v)), request: streamedResponse.request, isRedirect: streamedResponse.isRedirect, persistentConnection: streamedResponse.persistentConnection, diff --git a/lib/src/cookie_manager.dart b/lib/src/cookie_manager.dart index 05253273..6b1e67cd 100644 --- a/lib/src/cookie_manager.dart +++ b/lib/src/cookie_manager.dart @@ -17,15 +17,14 @@ class CookieManager extends Interceptor { await cookieJar .loadForRequest(Uri(scheme: request.url.scheme, host: request.url.host)) .then((cookies) { - var cookie = getCookies(cookies); - if (cookie.isNotEmpty) { - request.headers.addAll({HttpHeaders.cookieHeader: cookie}); - } - return request; - }) - .catchError((e, stackTrace) { - return request; - }); + var cookie = getCookies(cookies); + if (cookie.isNotEmpty) { + request.headers.addAll({HttpHeaders.cookieHeader: cookie}); + } + return request; + }).catchError((e, stackTrace) { + return request; + }); return request; } @@ -44,9 +43,8 @@ class CookieManager extends Interceptor { var cookies = cookie.split(exp); await cookieJar.saveFromResponse( Uri( - scheme: response.request!.url.scheme, - host: response.request!.url.host, - ), + scheme: response.request!.url.scheme, + host: response.request!.url.host), cookies.map((str) => Cookie.fromSetCookieValue(str)).toList(), ); } diff --git a/lib/src/enums.dart b/lib/src/enums.dart index 0f250ea3..595afdc2 100644 --- a/lib/src/enums.dart +++ b/lib/src/enums.dart @@ -17,5 +17,5 @@ enum ResponseType { plain, /// Get original bytes, the type of response will be `List` - bytes, + bytes } diff --git a/lib/src/enums/image_gravity.dart b/lib/src/enums/image_gravity.dart index dbcef202..1a0de57b 100644 --- a/lib/src/enums/image_gravity.dart +++ b/lib/src/enums/image_gravity.dart @@ -1,6 +1,7 @@ part of '../../enums.dart'; enum ImageGravity { + auto(value: "auto"), center(value: "center"), topLeft(value: "top-left"), top(value: "top"), diff --git a/lib/src/enums/o_auth_provider.dart b/lib/src/enums/o_auth_provider.dart index 1bd23697..6626f3a0 100644 --- a/lib/src/enums/o_auth_provider.dart +++ b/lib/src/enums/o_auth_provider.dart @@ -10,6 +10,7 @@ enum OAuthProvider { bitbucket(value: "bitbucket"), bitly(value: "bitly"), box(value: "box"), + cloudflare(value: "cloudflare"), dailymotion(value: "dailymotion"), discord(value: "discord"), disqus(value: "disqus"), @@ -32,6 +33,7 @@ enum OAuthProvider { paypal(value: "paypal"), paypalSandbox(value: "paypalSandbox"), podio(value: "podio"), + resend(value: "resend"), salesforce(value: "salesforce"), slack(value: "slack"), spotify(value: "spotify"), diff --git a/lib/src/realtime_browser.dart b/lib/src/realtime_browser.dart index e56b7ddd..a0f4193a 100644 --- a/lib/src/realtime_browser.dart +++ b/lib/src/realtime_browser.dart @@ -25,12 +25,10 @@ class RealtimeBrowser extends RealtimeBase with RealtimeMixin { final jwt = client.config['jwt']; if (jwt != null && jwt.isNotEmpty) { - uri = uri.replace( - queryParameters: { - ...uri.queryParameters, - 'jwt': jwt, - }, - ); + uri = uri.replace(queryParameters: { + ...uri.queryParameters, + 'jwt': jwt, + }); } return HtmlWebSocketChannel.connect(uri); diff --git a/lib/src/realtime_io.dart b/lib/src/realtime_io.dart index ab1c3b42..0ed367a6 100644 --- a/lib/src/realtime_io.dart +++ b/lib/src/realtime_io.dart @@ -37,11 +37,9 @@ class RealtimeIO extends RealtimeBase with RealtimeMixin { headers['x-appwrite-jwt'] = jwt; } - final websok = IOWebSocketChannel( - (client as ClientIO).selfSigned - ? await _connectForSelfSignedCert(uri, headers) - : await WebSocket.connect(uri.toString(), headers: headers), - ); + final websok = IOWebSocketChannel((client as ClientIO).selfSigned + ? await _connectForSelfSignedCert(uri, headers) + : await WebSocket.connect(uri.toString(), headers: headers)); return websok; } @@ -75,18 +73,13 @@ class RealtimeIO extends RealtimeBase with RealtimeMixin { // https://github.com/jonataslaw/getsocket/blob/f25b3a264d8cc6f82458c949b86d286cd0343792/lib/src/io.dart#L104 // and from official dart sdk websocket_impl.dart connect method Future _connectForSelfSignedCert( - Uri uri, - Map headers, - ) async { + Uri uri, Map headers) async { try { var r = Random(); var key = base64.encode(List.generate(16, (_) => r.nextInt(255))); var client = HttpClient(context: SecurityContext()); - client.badCertificateCallback = ( - X509Certificate cert, - String host, - int port, - ) { + client.badCertificateCallback = + (X509Certificate cert, String host, int port) { return true; }; diff --git a/lib/src/realtime_mixin.dart b/lib/src/realtime_mixin.dart index 5ee9caa7..ee4c4c30 100644 --- a/lib/src/realtime_mixin.dart +++ b/lib/src/realtime_mixin.dart @@ -100,80 +100,74 @@ mixin RealtimeMixin { _websok = await getWebSocket(uri); } _retries = 0; - _websocketSubscription = _websok?.stream.listen( - (response) { - final data = RealtimeResponse.fromJson(response); - switch (data.type) { - case 'error': - handleError(data); - break; - case 'connected': - final message = RealtimeResponseConnected.fromMap(data.data); - - if (message.user.isEmpty) { - // send fallback cookie if exists - final cookie = getFallbackCookie?.call(); - if (cookie != null) { - _websok?.sink.add( - jsonEncode({ - "type": "authentication", - "data": { - "session": cookie, - }, - }), - ); - } - } - for (var entry in _subscriptions.entries) { - _pendingSubscribes[entry.key] = { - 'subscriptionId': entry.key, - 'channels': entry.value.channels, - 'queries': entry.value.queries, - }; + _websocketSubscription = _websok?.stream.listen((response) { + final data = RealtimeResponse.fromJson(response); + switch (data.type) { + case 'error': + handleError(data); + break; + case 'connected': + final message = RealtimeResponseConnected.fromMap(data.data); + + if (message.user.isEmpty) { + // send fallback cookie if exists + final cookie = getFallbackCookie?.call(); + if (cookie != null) { + _websok?.sink.add(jsonEncode({ + "type": "authentication", + "data": { + "session": cookie, + }, + })); } - _appConnected = true; - _sendPendingSubscribes(); - _flushPendingPresence(); - _startHeartbeat(); // Start heartbeat after successful connection - break; - case 'pong': + } + for (var entry in _subscriptions.entries) { + _pendingSubscribes[entry.key] = { + 'subscriptionId': entry.key, + 'channels': entry.value.channels, + 'queries': entry.value.queries, + }; + } + _appConnected = true; + _sendPendingSubscribes(); + _flushPendingPresence(); + _startHeartbeat(); // Start heartbeat after successful connection + break; + case 'pong': + break; + case 'event': + final messageData = data.data as Map; + final message = RealtimeMessage.fromMap(messageData); + final subscriptions = + (messageData['subscriptions'] as List?) + ?.map((x) => x.toString()) + .toList() ?? + []; + + if (subscriptions.isEmpty) { break; - case 'event': - final messageData = data.data as Map; - final message = RealtimeMessage.fromMap(messageData); - final subscriptions = - (messageData['subscriptions'] as List?) - ?.map((x) => x.toString()) - .toList() ?? - []; - - if (subscriptions.isEmpty) { - break; - } + } - for (var subscriptionId in subscriptions) { - final subscription = _subscriptions[subscriptionId]; - if (subscription != null) { - subscription.controller.add(message); - } + for (var subscriptionId in subscriptions) { + final subscription = _subscriptions[subscriptionId]; + if (subscription != null) { + subscription.controller.add(message); } - break; - } - }, - onDone: () { - _appConnected = false; - _stopHeartbeat(); - _retry(); - }, - onError: (err, stack) { - _appConnected = false; - _stopHeartbeat(); - for (var subscription in _subscriptions.values) { - subscription.controller.addError(err, stack); - } - _retry(); - }, - ); + } + break; + } + }, onDone: () { + _appConnected = false; + _stopHeartbeat(); + _retry(); + }, onError: (err, stack) { + _appConnected = false; + _stopHeartbeat(); + for (var subscription in _subscriptions.values) { + subscription.controller.addError(err, stack); + } + _retry(); + }); } catch (e) { if (e is AppwriteException) { rethrow; @@ -203,30 +197,27 @@ mixin RealtimeMixin { return _retries < 5 ? 1 : _retries < 15 - ? 5 - : _retries < 100 - ? 10 - : 60; + ? 5 + : _retries < 100 + ? 10 + : 60; } Uri _prepareUri() { if (client.endPointRealtime == null) { throw AppwriteException( - "Please set endPointRealtime to connect to realtime server", - ); + "Please set endPointRealtime to connect to realtime server"); } var uri = Uri.parse(client.endPointRealtime!); var queryParams = "project=${Uri.encodeComponent(client.config['project']!)}"; - final portPart = - (uri.hasPort && uri.port != 80 && uri.port != 443) - ? ':${uri.port}' - : ''; + final portPart = (uri.hasPort && uri.port != 80 && uri.port != 443) + ? ':${uri.port}' + : ''; return Uri.parse( - "${uri.scheme}://${uri.host}$portPart${uri.path}/realtime?$queryParams", - ); + "${uri.scheme}://${uri.host}$portPart${uri.path}/realtime?$queryParams"); } void _sendUnsubscribeMessage(List subscriptionIds) { @@ -236,12 +227,10 @@ mixin RealtimeMixin { if (_websok == null || _websok?.closeCode != null) { return; } - _websok!.sink.add( - jsonEncode({ - 'type': 'unsubscribe', - 'data': subscriptionIds.map((id) => {'subscriptionId': id}).toList(), - }), - ); + _websok!.sink.add(jsonEncode({ + 'type': 'unsubscribe', + 'data': subscriptionIds.map((id) => {'subscriptionId': id}).toList(), + })); } String _generateUniqueSubscriptionId() { @@ -303,12 +292,10 @@ mixin RealtimeMixin { final rows = _pendingSubscribes.values.toList(); _pendingSubscribes.clear(); - _websok!.sink.add( - jsonEncode({ - 'type': 'subscribe', - 'data': rows, - }), - ); + _websok!.sink.add(jsonEncode({ + 'type': 'subscribe', + 'data': rows, + })); } void _flushPendingPresence() { @@ -325,10 +312,8 @@ mixin RealtimeMixin { return channel is String ? channel : channel.toString(); } - RealtimeSubscription subscribeTo( - List channels, [ - List queries = const [], - ]) { + RealtimeSubscription subscribeTo(List channels, + [List queries = const []]) { StreamController controller = StreamController.broadcast(); final channelStrings = channels.map((ch) => _channelToString(ch)).toList().cast(); diff --git a/lib/src/realtime_stub.dart b/lib/src/realtime_stub.dart index 631afe31..3aab4e03 100644 --- a/lib/src/realtime_stub.dart +++ b/lib/src/realtime_stub.dart @@ -2,7 +2,6 @@ import 'realtime_base.dart'; import 'client.dart'; /// Implemented in `realtime_browser.dart` and `realtime_io.dart`. -RealtimeBase createRealtime(Client client) => - throw UnsupportedError( +RealtimeBase createRealtime(Client client) => throw UnsupportedError( 'Cannot create a client without dart:html or dart:io.', ); diff --git a/lib/src/realtime_subscription.dart b/lib/src/realtime_subscription.dart index 6461e344..e572b519 100644 --- a/lib/src/realtime_subscription.dart +++ b/lib/src/realtime_subscription.dart @@ -21,7 +21,7 @@ class RealtimeSubscription { /// Replace the channels and/or queries on this subscription without recreating it. final Future Function({List? channels, List? queries}) - update; + update; /// Alias of [unsubscribe] that also closes the socket when this was the last active /// subscription. Prefer [unsubscribe] plus [Realtime.disconnect] for explicit control. diff --git a/pubspec.yaml b/pubspec.yaml index 3eeb0c87..a08b4cdb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: appwrite -version: 26.1.0 +version: 26.2.0 description: Appwrite is an open-source self-hosted backend server that abstracts and simplifies complex and repetitive development tasks behind a very simple REST API homepage: https://appwrite.io repository: https://github.com/appwrite/sdk-for-flutter diff --git a/test/channel_test.dart b/test/channel_test.dart index 5ed52b81..9e0e11d3 100644 --- a/test/channel_test.dart +++ b/test/channel_test.dart @@ -8,28 +8,28 @@ void main() { }); test('returns database channel with specific IDs', () { - expect( - Channel.database('db1').collection('col1').document().toString(), - 'databases.db1.collections.col1.documents', - ); + expect(Channel.database('db1').collection('col1').document().toString(), + 'databases.db1.collections.col1.documents'); }); test('returns database channel with action', () { expect( - Channel.database( - 'db1', - ).collection('col1').document('doc1').create().toString(), - 'databases.db1.collections.col1.documents.doc1.create', - ); + Channel.database('db1') + .collection('col1') + .document('doc1') + .create() + .toString(), + 'databases.db1.collections.col1.documents.doc1.create'); }); test('returns database channel with upsert action', () { expect( - Channel.database( - 'db1', - ).collection('col1').document('doc1').upsert().toString(), - 'databases.db1.collections.col1.documents.doc1.upsert', - ); + Channel.database('db1') + .collection('col1') + .document('doc1') + .upsert() + .toString(), + 'databases.db1.collections.col1.documents.doc1.upsert'); }); }); @@ -39,17 +39,18 @@ void main() { }); test('returns tablesdb channel with specific IDs', () { - expect( - Channel.tablesdb('db1').table('table1').row().toString(), - 'tablesdb.db1.tables.table1.rows', - ); + expect(Channel.tablesdb('db1').table('table1').row().toString(), + 'tablesdb.db1.tables.table1.rows'); }); test('returns tablesdb channel with action', () { expect( - Channel.tablesdb('db1').table('table1').row('row1').update().toString(), - 'tablesdb.db1.tables.table1.rows.row1.update', - ); + Channel.tablesdb('db1') + .table('table1') + .row('row1') + .update() + .toString(), + 'tablesdb.db1.tables.table1.rows.row1.update'); }); }); @@ -66,16 +67,12 @@ void main() { test('returns buckets channel with specific IDs', () { expect( - Channel.bucket('bucket1').file().toString(), - 'buckets.bucket1.files', - ); + Channel.bucket('bucket1').file().toString(), 'buckets.bucket1.files'); }); test('returns buckets channel with action', () { - expect( - Channel.bucket('bucket1').file('file1').delete().toString(), - 'buckets.bucket1.files.file1.delete', - ); + expect(Channel.bucket('bucket1').file('file1').delete().toString(), + 'buckets.bucket1.files.file1.delete'); }); }); @@ -119,17 +116,13 @@ void main() { }); test('returns memberships channel with specific membership ID', () { - expect( - Channel.membership('membership1').toString(), - 'memberships.membership1', - ); + expect(Channel.membership('membership1').toString(), + 'memberships.membership1'); }); test('returns memberships channel with action', () { - expect( - Channel.membership('membership1').update().toString(), - 'memberships.membership1.update', - ); + expect(Channel.membership('membership1').update().toString(), + 'memberships.membership1.update'); }); }); @@ -147,31 +140,23 @@ void main() { }); test('returns presence channel with create action', () { - expect( - Channel.presence('presence1').create().toString(), - 'presences.presence1.create', - ); + expect(Channel.presence('presence1').create().toString(), + 'presences.presence1.create'); }); test('returns presence channel with upsert action', () { - expect( - Channel.presence('presence1').upsert().toString(), - 'presences.presence1.upsert', - ); + expect(Channel.presence('presence1').upsert().toString(), + 'presences.presence1.upsert'); }); test('returns presence channel with update action', () { - expect( - Channel.presence('presence1').update().toString(), - 'presences.presence1.update', - ); + expect(Channel.presence('presence1').update().toString(), + 'presences.presence1.update'); }); test('returns presence channel with delete action', () { - expect( - Channel.presence('presence1').delete().toString(), - 'presences.presence1.delete', - ); + expect(Channel.presence('presence1').delete().toString(), + 'presences.presence1.delete'); }); }); } diff --git a/test/services/databases_test.dart b/test/services/databases_test.dart index d55be0c4..d92a7381 100644 --- a/test/services/databases_test.dart +++ b/test/services/databases_test.dart @@ -312,7 +312,7 @@ void main() { databaseId: "", collectionId: "", documentId: "", - attribute: '', + attribute: "", ); expect(response, isA()); }); @@ -336,7 +336,7 @@ void main() { databaseId: "", collectionId: "", documentId: "", - attribute: '', + attribute: "", ); expect(response, isA()); }); diff --git a/test/services/documents_db_test.dart b/test/services/documents_db_test.dart new file mode 100644 index 00000000..6e7d3a62 --- /dev/null +++ b/test/services/documents_db_test.dart @@ -0,0 +1,362 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:appwrite/models.dart' as models; +import 'package:appwrite/enums.dart' as enums; +import 'package:appwrite/src/enums.dart'; +import 'package:appwrite/src/response.dart'; +import 'dart:typed_data'; +import 'package:appwrite/appwrite.dart'; + +class MockClient extends Mock implements Client { + Map config = {'project': 'testproject'}; + String endPoint = 'https://localhost/v1'; + + @override + Future call( + HttpMethod? method, { + String path = '', + Map headers = const {}, + Map params = const {}, + ResponseType? responseType, + }) async { + return super.noSuchMethod( + Invocation.method(#call, [method]), + returnValue: Response(), + ); + } + + @override + Future webAuth(Uri? url, {String? callbackUrlScheme}) async { + return super.noSuchMethod( + Invocation.method(#webAuth, [url]), + returnValue: 'done', + ); + } + + @override + Future chunkedUpload({ + String? path, + Map? params, + String? paramName, + String? idParamName, + Map? headers, + Function(UploadProgress)? onProgress, + }) async { + return super.noSuchMethod( + Invocation.method(#chunkedUpload, [ + path, + params, + paramName, + idParamName, + headers, + ]), + returnValue: Response(data: {}), + ); + } +} + +void main() { + group('DocumentsDB test', () { + late MockClient client; + late DocumentsDB documentsDB; + + setUp(() { + client = MockClient(); + documentsDB = DocumentsDB(client); + }); + + test('test method listTransactions()', () async { + final Map data = { + 'total': 5, + 'transactions': [], + }; + + when( + client.call(HttpMethod.get), + ).thenAnswer((_) async => Response(data: data)); + + final response = await documentsDB.listTransactions(); + expect(response, isA()); + }); + + test('test method createTransaction()', () async { + final Map data = { + '\$id': "259125845563242502", + '\$createdAt': "2020-10-15T06:38:00.000+00:00", + '\$updatedAt': "2020-10-15T06:38:00.000+00:00", + 'status': "pending", + 'operations': 5, + 'expiresAt': "2020-10-15T06:38:00.000+00:00", + }; + + when( + client.call(HttpMethod.post), + ).thenAnswer((_) async => Response(data: data)); + + final response = await documentsDB.createTransaction(); + expect(response, isA()); + }); + + test('test method getTransaction()', () async { + final Map data = { + '\$id': "259125845563242502", + '\$createdAt': "2020-10-15T06:38:00.000+00:00", + '\$updatedAt': "2020-10-15T06:38:00.000+00:00", + 'status': "pending", + 'operations': 5, + 'expiresAt': "2020-10-15T06:38:00.000+00:00", + }; + + when( + client.call(HttpMethod.get), + ).thenAnswer((_) async => Response(data: data)); + + final response = await documentsDB.getTransaction( + transactionId: "", + ); + expect(response, isA()); + }); + + test('test method updateTransaction()', () async { + final Map data = { + '\$id': "259125845563242502", + '\$createdAt': "2020-10-15T06:38:00.000+00:00", + '\$updatedAt': "2020-10-15T06:38:00.000+00:00", + 'status': "pending", + 'operations': 5, + 'expiresAt': "2020-10-15T06:38:00.000+00:00", + }; + + when( + client.call(HttpMethod.patch), + ).thenAnswer((_) async => Response(data: data)); + + final response = await documentsDB.updateTransaction( + transactionId: "", + ); + expect(response, isA()); + }); + + test('test method deleteTransaction()', () async { + final data = ''; + + when( + client.call(HttpMethod.delete), + ).thenAnswer((_) async => Response(data: data)); + + final response = await documentsDB.deleteTransaction( + transactionId: "", + ); + }); + + test('test method createOperations()', () async { + final Map data = { + '\$id': "259125845563242502", + '\$createdAt': "2020-10-15T06:38:00.000+00:00", + '\$updatedAt': "2020-10-15T06:38:00.000+00:00", + 'status': "pending", + 'operations': 5, + 'expiresAt': "2020-10-15T06:38:00.000+00:00", + }; + + when( + client.call(HttpMethod.post), + ).thenAnswer((_) async => Response(data: data)); + + final response = await documentsDB.createOperations( + transactionId: "", + ); + expect(response, isA()); + }); + + test('test method listDocuments()', () async { + final Map data = { + 'total': 5, + 'documents': [], + }; + + when( + client.call(HttpMethod.get), + ).thenAnswer((_) async => Response(data: data)); + + final response = await documentsDB.listDocuments( + databaseId: "", + collectionId: "", + ); + expect(response, isA()); + }); + + test('test method createDocument()', () async { + final Map data = { + '\$id': "5e5ea5c16897e", + '\$sequence': "1", + '\$collectionId': "5e5ea5c15117e", + '\$databaseId': "5e5ea5c15117e", + '\$createdAt': "2020-10-15T06:38:00.000+00:00", + '\$updatedAt': "2020-10-15T06:38:00.000+00:00", + '\$permissions': [], + }; + + when( + client.call(HttpMethod.post), + ).thenAnswer((_) async => Response(data: data)); + + final response = await documentsDB.createDocument( + databaseId: "", + collectionId: "", + documentId: "", + data: {}, + ); + expect(response, isA()); + }); + + test('test method createDocuments()', () async { + final Map data = { + 'total': 5, + 'documents': [], + }; + + when( + client.call(HttpMethod.post), + ).thenAnswer((_) async => Response(data: data)); + + final response = await documentsDB.createDocuments( + databaseId: "", + collectionId: "", + documents: [], + ); + expect(response, isA()); + }); + + test('test method getDocument()', () async { + final Map data = { + '\$id': "5e5ea5c16897e", + '\$sequence': "1", + '\$collectionId': "5e5ea5c15117e", + '\$databaseId': "5e5ea5c15117e", + '\$createdAt': "2020-10-15T06:38:00.000+00:00", + '\$updatedAt': "2020-10-15T06:38:00.000+00:00", + '\$permissions': [], + }; + + when( + client.call(HttpMethod.get), + ).thenAnswer((_) async => Response(data: data)); + + final response = await documentsDB.getDocument( + databaseId: "", + collectionId: "", + documentId: "", + ); + expect(response, isA()); + }); + + test('test method upsertDocument()', () async { + final Map data = { + '\$id': "5e5ea5c16897e", + '\$sequence': "1", + '\$collectionId': "5e5ea5c15117e", + '\$databaseId': "5e5ea5c15117e", + '\$createdAt': "2020-10-15T06:38:00.000+00:00", + '\$updatedAt': "2020-10-15T06:38:00.000+00:00", + '\$permissions': [], + }; + + when( + client.call(HttpMethod.put), + ).thenAnswer((_) async => Response(data: data)); + + final response = await documentsDB.upsertDocument( + databaseId: "", + collectionId: "", + documentId: "", + ); + expect(response, isA()); + }); + + test('test method updateDocument()', () async { + final Map data = { + '\$id': "5e5ea5c16897e", + '\$sequence': "1", + '\$collectionId': "5e5ea5c15117e", + '\$databaseId': "5e5ea5c15117e", + '\$createdAt': "2020-10-15T06:38:00.000+00:00", + '\$updatedAt': "2020-10-15T06:38:00.000+00:00", + '\$permissions': [], + }; + + when( + client.call(HttpMethod.patch), + ).thenAnswer((_) async => Response(data: data)); + + final response = await documentsDB.updateDocument( + databaseId: "", + collectionId: "", + documentId: "", + ); + expect(response, isA()); + }); + + test('test method deleteDocument()', () async { + final data = ''; + + when( + client.call(HttpMethod.delete), + ).thenAnswer((_) async => Response(data: data)); + + final response = await documentsDB.deleteDocument( + databaseId: "", + collectionId: "", + documentId: "", + ); + }); + + test('test method decrementDocumentAttribute()', () async { + final Map data = { + '\$id': "5e5ea5c16897e", + '\$sequence': "1", + '\$collectionId': "5e5ea5c15117e", + '\$databaseId': "5e5ea5c15117e", + '\$createdAt': "2020-10-15T06:38:00.000+00:00", + '\$updatedAt': "2020-10-15T06:38:00.000+00:00", + '\$permissions': [], + }; + + when( + client.call(HttpMethod.patch), + ).thenAnswer((_) async => Response(data: data)); + + final response = await documentsDB.decrementDocumentAttribute( + databaseId: "", + collectionId: "", + documentId: "", + attribute: "", + ); + expect(response, isA()); + }); + + test('test method incrementDocumentAttribute()', () async { + final Map data = { + '\$id': "5e5ea5c16897e", + '\$sequence': "1", + '\$collectionId': "5e5ea5c15117e", + '\$databaseId': "5e5ea5c15117e", + '\$createdAt': "2020-10-15T06:38:00.000+00:00", + '\$updatedAt': "2020-10-15T06:38:00.000+00:00", + '\$permissions': [], + }; + + when( + client.call(HttpMethod.patch), + ).thenAnswer((_) async => Response(data: data)); + + final response = await documentsDB.incrementDocumentAttribute( + databaseId: "", + collectionId: "", + documentId: "", + attribute: "", + ); + expect(response, isA()); + }); + }); +} diff --git a/test/services/tables_db_test.dart b/test/services/tables_db_test.dart index 9cf7d024..47f0eee8 100644 --- a/test/services/tables_db_test.dart +++ b/test/services/tables_db_test.dart @@ -312,7 +312,7 @@ void main() { databaseId: "", tableId: "", rowId: "", - column: '', + column: "", ); expect(response, isA()); }); @@ -336,7 +336,7 @@ void main() { databaseId: "", tableId: "", rowId: "", - column: '', + column: "", ); expect(response, isA()); }); diff --git a/test/services/vectors_db_test.dart b/test/services/vectors_db_test.dart new file mode 100644 index 00000000..14660ed0 --- /dev/null +++ b/test/services/vectors_db_test.dart @@ -0,0 +1,313 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:appwrite/models.dart' as models; +import 'package:appwrite/enums.dart' as enums; +import 'package:appwrite/src/enums.dart'; +import 'package:appwrite/src/response.dart'; +import 'dart:typed_data'; +import 'package:appwrite/appwrite.dart'; + +class MockClient extends Mock implements Client { + Map config = {'project': 'testproject'}; + String endPoint = 'https://localhost/v1'; + + @override + Future call( + HttpMethod? method, { + String path = '', + Map headers = const {}, + Map params = const {}, + ResponseType? responseType, + }) async { + return super.noSuchMethod( + Invocation.method(#call, [method]), + returnValue: Response(), + ); + } + + @override + Future webAuth(Uri? url, {String? callbackUrlScheme}) async { + return super.noSuchMethod( + Invocation.method(#webAuth, [url]), + returnValue: 'done', + ); + } + + @override + Future chunkedUpload({ + String? path, + Map? params, + String? paramName, + String? idParamName, + Map? headers, + Function(UploadProgress)? onProgress, + }) async { + return super.noSuchMethod( + Invocation.method(#chunkedUpload, [ + path, + params, + paramName, + idParamName, + headers, + ]), + returnValue: Response(data: {}), + ); + } +} + +void main() { + group('VectorsDB test', () { + late MockClient client; + late VectorsDB vectorsDB; + + setUp(() { + client = MockClient(); + vectorsDB = VectorsDB(client); + }); + + test('test method listTransactions()', () async { + final Map data = { + 'total': 5, + 'transactions': [], + }; + + when( + client.call(HttpMethod.get), + ).thenAnswer((_) async => Response(data: data)); + + final response = await vectorsDB.listTransactions(); + expect(response, isA()); + }); + + test('test method createTransaction()', () async { + final Map data = { + '\$id': "259125845563242502", + '\$createdAt': "2020-10-15T06:38:00.000+00:00", + '\$updatedAt': "2020-10-15T06:38:00.000+00:00", + 'status': "pending", + 'operations': 5, + 'expiresAt': "2020-10-15T06:38:00.000+00:00", + }; + + when( + client.call(HttpMethod.post), + ).thenAnswer((_) async => Response(data: data)); + + final response = await vectorsDB.createTransaction(); + expect(response, isA()); + }); + + test('test method getTransaction()', () async { + final Map data = { + '\$id': "259125845563242502", + '\$createdAt': "2020-10-15T06:38:00.000+00:00", + '\$updatedAt': "2020-10-15T06:38:00.000+00:00", + 'status': "pending", + 'operations': 5, + 'expiresAt': "2020-10-15T06:38:00.000+00:00", + }; + + when( + client.call(HttpMethod.get), + ).thenAnswer((_) async => Response(data: data)); + + final response = await vectorsDB.getTransaction( + transactionId: "", + ); + expect(response, isA()); + }); + + test('test method updateTransaction()', () async { + final Map data = { + '\$id': "259125845563242502", + '\$createdAt': "2020-10-15T06:38:00.000+00:00", + '\$updatedAt': "2020-10-15T06:38:00.000+00:00", + 'status': "pending", + 'operations': 5, + 'expiresAt': "2020-10-15T06:38:00.000+00:00", + }; + + when( + client.call(HttpMethod.patch), + ).thenAnswer((_) async => Response(data: data)); + + final response = await vectorsDB.updateTransaction( + transactionId: "", + ); + expect(response, isA()); + }); + + test('test method deleteTransaction()', () async { + final data = ''; + + when( + client.call(HttpMethod.delete), + ).thenAnswer((_) async => Response(data: data)); + + final response = await vectorsDB.deleteTransaction( + transactionId: "", + ); + }); + + test('test method createOperations()', () async { + final Map data = { + '\$id': "259125845563242502", + '\$createdAt': "2020-10-15T06:38:00.000+00:00", + '\$updatedAt': "2020-10-15T06:38:00.000+00:00", + 'status': "pending", + 'operations': 5, + 'expiresAt': "2020-10-15T06:38:00.000+00:00", + }; + + when( + client.call(HttpMethod.post), + ).thenAnswer((_) async => Response(data: data)); + + final response = await vectorsDB.createOperations( + transactionId: "", + ); + expect(response, isA()); + }); + + test('test method listDocuments()', () async { + final Map data = { + 'total': 5, + 'documents': [], + }; + + when( + client.call(HttpMethod.get), + ).thenAnswer((_) async => Response(data: data)); + + final response = await vectorsDB.listDocuments( + databaseId: "", + collectionId: "", + ); + expect(response, isA()); + }); + + test('test method createDocument()', () async { + final Map data = { + '\$id': "5e5ea5c16897e", + '\$sequence': "1", + '\$collectionId': "5e5ea5c15117e", + '\$databaseId': "5e5ea5c15117e", + '\$createdAt': "2020-10-15T06:38:00.000+00:00", + '\$updatedAt': "2020-10-15T06:38:00.000+00:00", + '\$permissions': [], + }; + + when( + client.call(HttpMethod.post), + ).thenAnswer((_) async => Response(data: data)); + + final response = await vectorsDB.createDocument( + databaseId: "", + collectionId: "", + documentId: "", + data: {}, + ); + expect(response, isA()); + }); + + test('test method createQuery()', () async { + final Map data = { + 'total': 5, + 'documents': [], + }; + + when( + client.call(HttpMethod.post), + ).thenAnswer((_) async => Response(data: data)); + + final response = await vectorsDB.createQuery( + databaseId: "", + collectionId: "", + ); + expect(response, isA()); + }); + + test('test method getDocument()', () async { + final Map data = { + '\$id': "5e5ea5c16897e", + '\$sequence': "1", + '\$collectionId': "5e5ea5c15117e", + '\$databaseId': "5e5ea5c15117e", + '\$createdAt': "2020-10-15T06:38:00.000+00:00", + '\$updatedAt': "2020-10-15T06:38:00.000+00:00", + '\$permissions': [], + }; + + when( + client.call(HttpMethod.get), + ).thenAnswer((_) async => Response(data: data)); + + final response = await vectorsDB.getDocument( + databaseId: "", + collectionId: "", + documentId: "", + ); + expect(response, isA()); + }); + + test('test method upsertDocument()', () async { + final Map data = { + '\$id': "5e5ea5c16897e", + '\$sequence': "1", + '\$collectionId': "5e5ea5c15117e", + '\$databaseId': "5e5ea5c15117e", + '\$createdAt': "2020-10-15T06:38:00.000+00:00", + '\$updatedAt': "2020-10-15T06:38:00.000+00:00", + '\$permissions': [], + }; + + when( + client.call(HttpMethod.put), + ).thenAnswer((_) async => Response(data: data)); + + final response = await vectorsDB.upsertDocument( + databaseId: "", + collectionId: "", + documentId: "", + ); + expect(response, isA()); + }); + + test('test method updateDocument()', () async { + final Map data = { + '\$id': "5e5ea5c16897e", + '\$sequence': "1", + '\$collectionId': "5e5ea5c15117e", + '\$databaseId': "5e5ea5c15117e", + '\$createdAt': "2020-10-15T06:38:00.000+00:00", + '\$updatedAt': "2020-10-15T06:38:00.000+00:00", + '\$permissions': [], + }; + + when( + client.call(HttpMethod.patch), + ).thenAnswer((_) async => Response(data: data)); + + final response = await vectorsDB.updateDocument( + databaseId: "", + collectionId: "", + documentId: "", + ); + expect(response, isA()); + }); + + test('test method deleteDocument()', () async { + final data = ''; + + when( + client.call(HttpMethod.delete), + ).thenAnswer((_) async => Response(data: data)); + + final response = await vectorsDB.deleteDocument( + databaseId: "", + collectionId: "", + documentId: "", + ); + }); + }); +} diff --git a/test/src/input_file_test.dart b/test/src/input_file_test.dart index 046fa07b..3d301a85 100644 --- a/test/src/input_file_test.dart +++ b/test/src/input_file_test.dart @@ -7,26 +7,22 @@ void main() { test('throws exception when neither path nor bytes are provided', () { expect( () => InputFile(), - throwsA( - isA().having( - (e) => e.message, - 'message', - 'One of `path` or `bytes` is required', - ), - ), + throwsA(isA().having( + (e) => e.message, + 'message', + 'One of `path` or `bytes` is required', + )), ); }); test('throws exception when path and bytes are both null', () { expect( () => InputFile(path: null, bytes: null), - throwsA( - isA().having( - (e) => e.message, - 'message', - 'One of `path` or `bytes` is required', - ), - ), + throwsA(isA().having( + (e) => e.message, + 'message', + 'One of `path` or `bytes` is required', + )), ); }); diff --git a/test/src/realtime_response_test.dart b/test/src/realtime_response_test.dart index 029c35bf..4435280a 100644 --- a/test/src/realtime_response_test.dart +++ b/test/src/realtime_response_test.dart @@ -48,9 +48,7 @@ void main() { final responseString = response1.toString(); expect( - responseString, - equals('RealtimeResponse(type: $type, data: $data)'), - ); + responseString, equals('RealtimeResponse(type: $type, data: $data)')); }); test('equality operator should compare two instances', () { diff --git a/test/src/realtime_subscription_test.dart b/test/src/realtime_subscription_test.dart index d19e6eac..1515f1a3 100644 --- a/test/src/realtime_subscription_test.dart +++ b/test/src/realtime_subscription_test.dart @@ -8,18 +8,15 @@ void main() { final mockStream = StreamController.broadcast(); final mockCloseFunction = () async {}; final mockUnsubscribeFunction = () async {}; - Future mockUpdateFunction({ - List? channels, - List? queries, - }) async {} + Future mockUpdateFunction( + {List? channels, List? queries}) async {} final subscription = RealtimeSubscription( - controller: mockStream, - close: mockCloseFunction, - unsubscribe: mockUnsubscribeFunction, - update: mockUpdateFunction, - channels: ['documents'], - queries: const [], - ); + controller: mockStream, + close: mockCloseFunction, + unsubscribe: mockUnsubscribeFunction, + update: mockUpdateFunction, + channels: ['documents'], + queries: const []); test('should have the correct stream and close function', () { expect(subscription.controller, equals(mockStream)); diff --git a/test/src/upload_progress_test.dart b/test/src/upload_progress_test.dart index 3dce66c9..d21607ec 100644 --- a/test/src/upload_progress_test.dart +++ b/test/src/upload_progress_test.dart @@ -14,7 +14,7 @@ void main() { "progress": progress, "sizeUploaded": sizeUploaded, "chunksTotal": chunksTotal, - "chunksUploaded": chunksUploaded, + "chunksUploaded": chunksUploaded }; final uploadProgress = UploadProgress( $id: id,