diff --git a/CHANGELOG.md b/CHANGELOG.md index b2099bf..50e6f4e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Change Log +## 22.0.0 + +* [BREAKING] Changed `$sequence` type from `int` to `String` for `Row` and `Document` models +* [BREAKING] Renamed `IndexType` enum: split into `DatabasesIndexType` (for Databases) and `TablesDBIndexType` (for TablesDB) +* [BREAKING] Replaced `specification` parameter with `buildSpecification` and `runtimeSpecification` in `Functions.create()`, `Functions.update()`, `Sites.create()`, `Sites.update()` +* Added new `Project` service with full CRUD for project-level environment variables +* Added new `Webhooks` service with full CRUD for project webhooks (including `updateSignature`) +* Added `Webhook` and `WebhookList` models +* Added `Users.updateImpersonator()` method for enabling/disabling user impersonation +* Added impersonation support: `setImpersonateUserId()`, `setImpersonateUserEmail()`, `setImpersonateUserPhone()` on `Client` +* Added `impersonator` and `impersonatorUserId` optional fields to `User` model +* Added `deploymentRetention` parameter to Functions and Sites create/update +* Added `startCommand` parameter to Sites create/update +* Added `Documentsdb`, `Vectorsdb` values to `BackupServices` and `DatabaseType` enums +* Added `WebhooksRead`, `WebhooksWrite`, `ProjectRead`, `ProjectWrite` scopes +* Removed `getQueueBillingProjectAggregation`, `getQueueBillingTeamAggregation`, `getQueuePriorityBuilds`, `getQueueRegionManager`, `getQueueThreats` from `Health` service +* Updated `Log` model field descriptions to clarify impersonation behavior +* Updated `X-Appwrite-Response-Format` header to `1.9.0` + ## 21.3.0 * Added `ttl` parameter to listDocuments diff --git a/README.md b/README.md index 54c35e4..a4c6514 100644 --- a/README.md +++ b/README.md @@ -2,12 +2,12 @@ [![pub package](https://img.shields.io/pub/v/dart_appwrite.svg?style=flat-square)](https://pub.dartlang.org/packages/dart_appwrite) ![License](https://img.shields.io/github/license/appwrite/sdk-for-dart.svg?style=flat-square) -![Version](https://img.shields.io/badge/api%20version-1.8.x-blue.svg?style=flat-square) +![Version](https://img.shields.io/badge/api%20version-1.9.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.8.x. For older versions, please check [previous releases](https://github.com/appwrite/sdk-for-dart/releases).** +**This SDK is compatible with Appwrite server version 1.9.x. For older versions, please check [previous releases](https://github.com/appwrite/sdk-for-dart/releases).** > This is the Dart SDK for integrating with Appwrite from your Dart server-side code. If you're looking for the Flutter SDK you should check [appwrite/sdk-for-flutter](https://github.com/appwrite/sdk-for-flutter) @@ -21,7 +21,7 @@ Add this to your package's `pubspec.yaml` file: ```yml dependencies: - dart_appwrite: ^21.3.0 + dart_appwrite: ^22.0.0 ``` You can install packages from the command line: diff --git a/docs/examples/databases/create-index.md b/docs/examples/databases/create-index.md index 32a2b19..336678f 100644 --- a/docs/examples/databases/create-index.md +++ b/docs/examples/databases/create-index.md @@ -13,7 +13,7 @@ Index result = await databases.createIndex( databaseId: '', collectionId: '', key: '', - type: enums.IndexType.key, + type: enums.DatabasesIndexType.key, attributes: [], orders: [enums.OrderBy.asc], // (optional) lengths: [], // (optional) diff --git a/docs/examples/functions/create.md b/docs/examples/functions/create.md index 7efe07f..c960e9b 100644 --- a/docs/examples/functions/create.md +++ b/docs/examples/functions/create.md @@ -27,6 +27,8 @@ Func result = await functions.create( providerBranch: '', // (optional) providerSilentMode: false, // (optional) providerRootDirectory: '', // (optional) - specification: '', // (optional) + buildSpecification: '', // (optional) + runtimeSpecification: '', // (optional) + deploymentRetention: 0, // (optional) ); ``` diff --git a/docs/examples/functions/update.md b/docs/examples/functions/update.md index 3a23cd6..bd99c93 100644 --- a/docs/examples/functions/update.md +++ b/docs/examples/functions/update.md @@ -27,6 +27,8 @@ Func result = await functions.update( providerBranch: '', // (optional) providerSilentMode: false, // (optional) providerRootDirectory: '', // (optional) - specification: '', // (optional) + buildSpecification: '', // (optional) + runtimeSpecification: '', // (optional) + deploymentRetention: 0, // (optional) ); ``` diff --git a/docs/examples/project/create-variable.md b/docs/examples/project/create-variable.md new file mode 100644 index 0000000..838fce0 --- /dev/null +++ b/docs/examples/project/create-variable.md @@ -0,0 +1,17 @@ +```dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +Project project = Project(client); + +Variable result = await project.createVariable( + variableId: '', + key: '', + value: '', + secret: false, // (optional) +); +``` diff --git a/docs/examples/project/delete-variable.md b/docs/examples/project/delete-variable.md new file mode 100644 index 0000000..9a177d5 --- /dev/null +++ b/docs/examples/project/delete-variable.md @@ -0,0 +1,14 @@ +```dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +Project project = Project(client); + +await project.deleteVariable( + variableId: '', +); +``` diff --git a/docs/examples/project/get-variable.md b/docs/examples/project/get-variable.md new file mode 100644 index 0000000..2b39e10 --- /dev/null +++ b/docs/examples/project/get-variable.md @@ -0,0 +1,14 @@ +```dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +Project project = Project(client); + +Variable result = await project.getVariable( + variableId: '', +); +``` diff --git a/docs/examples/project/list-variables.md b/docs/examples/project/list-variables.md new file mode 100644 index 0000000..ae5b527 --- /dev/null +++ b/docs/examples/project/list-variables.md @@ -0,0 +1,15 @@ +```dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +Project project = Project(client); + +VariableList result = await project.listVariables( + queries: [], // (optional) + total: false, // (optional) +); +``` diff --git a/docs/examples/project/update-variable.md b/docs/examples/project/update-variable.md new file mode 100644 index 0000000..eef236d --- /dev/null +++ b/docs/examples/project/update-variable.md @@ -0,0 +1,17 @@ +```dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +Project project = Project(client); + +Variable result = await project.updateVariable( + variableId: '', + key: '', // (optional) + value: '', // (optional) + secret: false, // (optional) +); +``` diff --git a/docs/examples/sites/create.md b/docs/examples/sites/create.md index 692255b..39cd360 100644 --- a/docs/examples/sites/create.md +++ b/docs/examples/sites/create.md @@ -19,6 +19,7 @@ Site result = await sites.create( timeout: 1, // (optional) installCommand: '', // (optional) buildCommand: '', // (optional) + startCommand: '', // (optional) outputDirectory: '', // (optional) adapter: enums.Adapter.static, // (optional) installationId: '', // (optional) @@ -27,6 +28,8 @@ Site result = await sites.create( providerBranch: '', // (optional) providerSilentMode: false, // (optional) providerRootDirectory: '', // (optional) - specification: '', // (optional) + buildSpecification: '', // (optional) + runtimeSpecification: '', // (optional) + deploymentRetention: 0, // (optional) ); ``` diff --git a/docs/examples/sites/update.md b/docs/examples/sites/update.md index 8846b51..ba6036c 100644 --- a/docs/examples/sites/update.md +++ b/docs/examples/sites/update.md @@ -18,6 +18,7 @@ Site result = await sites.update( timeout: 1, // (optional) installCommand: '', // (optional) buildCommand: '', // (optional) + startCommand: '', // (optional) outputDirectory: '', // (optional) buildRuntime: enums.BuildRuntime.node145, // (optional) adapter: enums.Adapter.static, // (optional) @@ -27,6 +28,8 @@ Site result = await sites.update( providerBranch: '', // (optional) providerSilentMode: false, // (optional) providerRootDirectory: '', // (optional) - specification: '', // (optional) + buildSpecification: '', // (optional) + runtimeSpecification: '', // (optional) + deploymentRetention: 0, // (optional) ); ``` diff --git a/docs/examples/tablesdb/create-index.md b/docs/examples/tablesdb/create-index.md index 1dfebbb..2d8ab9e 100644 --- a/docs/examples/tablesdb/create-index.md +++ b/docs/examples/tablesdb/create-index.md @@ -13,7 +13,7 @@ ColumnIndex result = await tablesDB.createIndex( databaseId: '', tableId: '', key: '', - type: enums.IndexType.key, + type: enums.TablesDBIndexType.key, columns: [], orders: [enums.OrderBy.asc], // (optional) lengths: [], // (optional) diff --git a/docs/examples/users/update-impersonator.md b/docs/examples/users/update-impersonator.md new file mode 100644 index 0000000..6d1d964 --- /dev/null +++ b/docs/examples/users/update-impersonator.md @@ -0,0 +1,15 @@ +```dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +Users users = Users(client); + +User result = await users.updateImpersonator( + userId: '', + impersonator: false, +); +``` diff --git a/docs/examples/webhooks/create.md b/docs/examples/webhooks/create.md new file mode 100644 index 0000000..6e0ba71 --- /dev/null +++ b/docs/examples/webhooks/create.md @@ -0,0 +1,21 @@ +```dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +Webhooks webhooks = Webhooks(client); + +Webhook result = await webhooks.create( + webhookId: '', + url: '', + name: '', + events: [], + enabled: false, // (optional) + security: false, // (optional) + httpUser: '', // (optional) + httpPass: '', // (optional) +); +``` diff --git a/docs/examples/webhooks/delete.md b/docs/examples/webhooks/delete.md new file mode 100644 index 0000000..5df49aa --- /dev/null +++ b/docs/examples/webhooks/delete.md @@ -0,0 +1,14 @@ +```dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +Webhooks webhooks = Webhooks(client); + +await webhooks.delete( + webhookId: '', +); +``` diff --git a/docs/examples/webhooks/get.md b/docs/examples/webhooks/get.md new file mode 100644 index 0000000..60defa8 --- /dev/null +++ b/docs/examples/webhooks/get.md @@ -0,0 +1,14 @@ +```dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +Webhooks webhooks = Webhooks(client); + +Webhook result = await webhooks.get( + webhookId: '', +); +``` diff --git a/docs/examples/webhooks/list.md b/docs/examples/webhooks/list.md new file mode 100644 index 0000000..ea2d91e --- /dev/null +++ b/docs/examples/webhooks/list.md @@ -0,0 +1,15 @@ +```dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +Webhooks webhooks = Webhooks(client); + +WebhookList result = await webhooks.list( + queries: [], // (optional) + total: false, // (optional) +); +``` diff --git a/docs/examples/webhooks/update-signature.md b/docs/examples/webhooks/update-signature.md new file mode 100644 index 0000000..a73a164 --- /dev/null +++ b/docs/examples/webhooks/update-signature.md @@ -0,0 +1,14 @@ +```dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +Webhooks webhooks = Webhooks(client); + +Webhook result = await webhooks.updateSignature( + webhookId: '', +); +``` diff --git a/docs/examples/webhooks/update.md b/docs/examples/webhooks/update.md new file mode 100644 index 0000000..2bd2310 --- /dev/null +++ b/docs/examples/webhooks/update.md @@ -0,0 +1,21 @@ +```dart +import 'package:dart_appwrite/dart_appwrite.dart'; + +Client client = Client() + .setEndpoint('https://.cloud.appwrite.io/v1') // Your API Endpoint + .setProject('') // Your project ID + .setKey(''); // Your secret API key + +Webhooks webhooks = Webhooks(client); + +Webhook result = await webhooks.update( + webhookId: '', + name: '', + url: '', + events: [], + enabled: false, // (optional) + security: false, // (optional) + httpUser: '', // (optional) + httpPass: '', // (optional) +); +``` diff --git a/lib/dart_appwrite.dart b/lib/dart_appwrite.dart index 4722ebf..371d4ca 100644 --- a/lib/dart_appwrite.dart +++ b/lib/dart_appwrite.dart @@ -1,6 +1,6 @@ /// Appwrite Dart SDK /// -/// This SDK is compatible with Appwrite server version 1.8.x. +/// This SDK is compatible with Appwrite server version 1.9.x. /// For older versions, please check /// [previous releases](https://github.com/appwrite/sdk-for-dart/releases). library dart_appwrite; @@ -38,9 +38,11 @@ part 'services/graphql.dart'; part 'services/health.dart'; part 'services/locale.dart'; part 'services/messaging.dart'; +part 'services/project.dart'; part 'services/sites.dart'; part 'services/storage.dart'; part 'services/tables_db.dart'; part 'services/teams.dart'; part 'services/tokens.dart'; part 'services/users.dart'; +part 'services/webhooks.dart'; diff --git a/lib/enums.dart b/lib/enums.dart index 7dc7da3..8c7bbec 100644 --- a/lib/enums.dart +++ b/lib/enums.dart @@ -14,7 +14,7 @@ part 'src/enums/image_format.dart'; part 'src/enums/backup_services.dart'; part 'src/enums/relationship_type.dart'; part 'src/enums/relation_mutate.dart'; -part 'src/enums/index_type.dart'; +part 'src/enums/databases_index_type.dart'; part 'src/enums/order_by.dart'; part 'src/enums/runtime.dart'; part 'src/enums/scopes.dart'; @@ -30,6 +30,7 @@ part 'src/enums/build_runtime.dart'; part 'src/enums/adapter.dart'; part 'src/enums/compression.dart'; part 'src/enums/image_gravity.dart'; +part 'src/enums/tables_db_index_type.dart'; part 'src/enums/password_hash.dart'; part 'src/enums/messaging_provider_type.dart'; part 'src/enums/database_type.dart'; diff --git a/lib/models.dart b/lib/models.dart index 7d9cc0b..d95ab22 100644 --- a/lib/models.dart +++ b/lib/models.dart @@ -26,6 +26,7 @@ part 'src/models/framework_list.dart'; part 'src/models/runtime_list.dart'; part 'src/models/deployment_list.dart'; part 'src/models/execution_list.dart'; +part 'src/models/webhook_list.dart'; part 'src/models/country_list.dart'; part 'src/models/continent_list.dart'; part 'src/models/language_list.dart'; @@ -112,6 +113,7 @@ part 'src/models/framework.dart'; part 'src/models/framework_adapter.dart'; part 'src/models/deployment.dart'; part 'src/models/execution.dart'; +part 'src/models/webhook.dart'; part 'src/models/variable.dart'; part 'src/models/country.dart'; part 'src/models/continent.dart'; diff --git a/lib/services/databases.dart b/lib/services/databases.dart index e927e11..e0b8f28 100644 --- a/lib/services/databases.dart +++ b/lib/services/databases.dart @@ -2002,7 +2002,7 @@ class Databases extends Service { {required String databaseId, required String collectionId, required String key, - required enums.IndexType type, + required enums.DatabasesIndexType type, required List attributes, List? orders, List? lengths}) async { diff --git a/lib/services/functions.dart b/lib/services/functions.dart index f40a82f..e17e0f6 100644 --- a/lib/services/functions.dart +++ b/lib/services/functions.dart @@ -47,7 +47,9 @@ class Functions extends Service { String? providerBranch, bool? providerSilentMode, String? providerRootDirectory, - String? specification}) async { + String? buildSpecification, + String? runtimeSpecification, + int? deploymentRetention}) async { final String apiPath = '/functions'; final Map apiParams = { @@ -70,7 +72,11 @@ class Functions extends Service { if (providerSilentMode != null) 'providerSilentMode': providerSilentMode, if (providerRootDirectory != null) 'providerRootDirectory': providerRootDirectory, - if (specification != null) 'specification': specification, + if (buildSpecification != null) 'buildSpecification': buildSpecification, + if (runtimeSpecification != null) + 'runtimeSpecification': runtimeSpecification, + if (deploymentRetention != null) + 'deploymentRetention': deploymentRetention, }; final Map apiHeaders = { @@ -145,7 +151,9 @@ class Functions extends Service { String? providerBranch, bool? providerSilentMode, String? providerRootDirectory, - String? specification}) async { + String? buildSpecification, + String? runtimeSpecification, + int? deploymentRetention}) async { final String apiPath = '/functions/{functionId}'.replaceAll('{functionId}', functionId); @@ -167,7 +175,11 @@ class Functions extends Service { if (providerSilentMode != null) 'providerSilentMode': providerSilentMode, if (providerRootDirectory != null) 'providerRootDirectory': providerRootDirectory, - if (specification != null) 'specification': specification, + if (buildSpecification != null) 'buildSpecification': buildSpecification, + if (runtimeSpecification != null) + 'runtimeSpecification': runtimeSpecification, + if (deploymentRetention != null) + 'deploymentRetention': deploymentRetention, }; final Map apiHeaders = { diff --git a/lib/services/health.dart b/lib/services/health.dart index 81ff102..097a7c3 100644 --- a/lib/services/health.dart +++ b/lib/services/health.dart @@ -129,40 +129,6 @@ class Health extends Service { return models.HealthQueue.fromMap(res.data); } - /// Get billing project aggregation queue. - Future getQueueBillingProjectAggregation( - {int? threshold}) async { - final String apiPath = '/health/queue/billing-project-aggregation'; - - final Map apiParams = { - if (threshold != null) 'threshold': threshold, - }; - - final Map apiHeaders = {}; - - final res = await client.call(HttpMethod.get, - path: apiPath, params: apiParams, headers: apiHeaders); - - return models.HealthQueue.fromMap(res.data); - } - - /// Get billing team aggregation queue. - Future getQueueBillingTeamAggregation( - {int? threshold}) async { - final String apiPath = '/health/queue/billing-team-aggregation'; - - final Map apiParams = { - if (threshold != null) 'threshold': threshold, - }; - - final Map apiHeaders = {}; - - final res = await client.call(HttpMethod.get, - path: apiPath, params: apiParams, headers: apiHeaders); - - return models.HealthQueue.fromMap(res.data); - } - /// Get the number of builds that are waiting to be processed in the Appwrite /// internal queue server. Future getQueueBuilds({int? threshold}) async { @@ -180,22 +146,6 @@ class Health extends Service { return models.HealthQueue.fromMap(res.data); } - /// Get the priority builds queue size. - Future getQueuePriorityBuilds({int? threshold}) async { - final String apiPath = '/health/queue/builds-priority'; - - final Map apiParams = { - if (threshold != null) 'threshold': threshold, - }; - - final Map apiHeaders = {}; - - final res = await client.call(HttpMethod.get, - path: apiPath, params: apiParams, headers: apiHeaders); - - return models.HealthQueue.fromMap(res.data); - } - /// Get the number of certificates that are waiting to be issued against /// [Letsencrypt](https://letsencrypt.org/) in the Appwrite internal queue /// server. @@ -354,22 +304,6 @@ class Health extends Service { return models.HealthQueue.fromMap(res.data); } - /// Get region manager queue. - Future getQueueRegionManager({int? threshold}) async { - final String apiPath = '/health/queue/region-manager'; - - final Map apiParams = { - if (threshold != null) 'threshold': threshold, - }; - - final Map apiHeaders = {}; - - final res = await client.call(HttpMethod.get, - path: apiPath, params: apiParams, headers: apiHeaders); - - return models.HealthQueue.fromMap(res.data); - } - /// Get the number of metrics that are waiting to be processed in the Appwrite /// stats resources queue. Future getQueueStatsResources({int? threshold}) async { @@ -404,22 +338,6 @@ class Health extends Service { return models.HealthQueue.fromMap(res.data); } - /// Get threats queue. - Future getQueueThreats({int? threshold}) async { - final String apiPath = '/health/queue/threats'; - - final Map apiParams = { - if (threshold != null) 'threshold': threshold, - }; - - final Map apiHeaders = {}; - - final res = await client.call(HttpMethod.get, - path: apiPath, params: apiParams, headers: apiHeaders); - - return models.HealthQueue.fromMap(res.data); - } - /// Get the number of webhooks that are waiting to be processed in the Appwrite /// internal queue server. Future getQueueWebhooks({int? threshold}) async { diff --git a/lib/services/project.dart b/lib/services/project.dart new file mode 100644 index 0000000..c491266 --- /dev/null +++ b/lib/services/project.dart @@ -0,0 +1,108 @@ +part of '../dart_appwrite.dart'; + +/// The Project service allows you to manage all the projects in your Appwrite +/// server. +class Project extends Service { + Project(super.client); + + /// Get a list of all project environment variables. + Future listVariables( + {List? queries, bool? total}) async { + final String apiPath = '/project/variables'; + + final Map apiParams = { + if (queries != null) 'queries': queries, + if (total != null) 'total': total, + }; + + final Map apiHeaders = {}; + + final res = await client.call(HttpMethod.get, + path: apiPath, params: apiParams, headers: apiHeaders); + + return models.VariableList.fromMap(res.data); + } + + /// Create a new project environment variable. These variables can be accessed + /// by all functions and sites in the project. + Future createVariable( + {required String variableId, + required String key, + required String value, + bool? secret}) async { + final String apiPath = '/project/variables'; + + final Map apiParams = { + 'variableId': variableId, + 'key': key, + 'value': value, + if (secret != null) 'secret': secret, + }; + + final Map apiHeaders = { + 'content-type': 'application/json', + }; + + final res = await client.call(HttpMethod.post, + path: apiPath, params: apiParams, headers: apiHeaders); + + return models.Variable.fromMap(res.data); + } + + /// Get a variable by its unique ID. + Future getVariable({required String variableId}) async { + final String apiPath = '/project/variables/{variableId}' + .replaceAll('{variableId}', variableId); + + final Map apiParams = {}; + + final Map apiHeaders = {}; + + final res = await client.call(HttpMethod.get, + path: apiPath, params: apiParams, headers: apiHeaders); + + return models.Variable.fromMap(res.data); + } + + /// Update variable by its unique ID. + Future updateVariable( + {required String variableId, + String? key, + String? value, + bool? secret}) async { + final String apiPath = '/project/variables/{variableId}' + .replaceAll('{variableId}', variableId); + + final Map apiParams = { + 'key': key, + 'value': value, + 'secret': secret, + }; + + final Map apiHeaders = { + 'content-type': 'application/json', + }; + + final res = await client.call(HttpMethod.put, + path: apiPath, params: apiParams, headers: apiHeaders); + + return models.Variable.fromMap(res.data); + } + + /// Delete a variable by its unique ID. + Future deleteVariable({required String variableId}) async { + final String apiPath = '/project/variables/{variableId}' + .replaceAll('{variableId}', variableId); + + final Map apiParams = {}; + + final Map apiHeaders = { + 'content-type': 'application/json', + }; + + final res = await client.call(HttpMethod.delete, + path: apiPath, params: apiParams, headers: apiHeaders); + + return res.data; + } +} diff --git a/lib/services/sites.dart b/lib/services/sites.dart index 94696a1..80bd96f 100644 --- a/lib/services/sites.dart +++ b/lib/services/sites.dart @@ -35,6 +35,7 @@ class Sites extends Service { int? timeout, String? installCommand, String? buildCommand, + String? startCommand, String? outputDirectory, enums.Adapter? adapter, String? installationId, @@ -43,7 +44,9 @@ class Sites extends Service { String? providerBranch, bool? providerSilentMode, String? providerRootDirectory, - String? specification}) async { + String? buildSpecification, + String? runtimeSpecification, + int? deploymentRetention}) async { final String apiPath = '/sites'; final Map apiParams = { @@ -55,6 +58,7 @@ class Sites extends Service { if (timeout != null) 'timeout': timeout, if (installCommand != null) 'installCommand': installCommand, if (buildCommand != null) 'buildCommand': buildCommand, + if (startCommand != null) 'startCommand': startCommand, if (outputDirectory != null) 'outputDirectory': outputDirectory, 'buildRuntime': buildRuntime.value, if (adapter != null) 'adapter': adapter.value, @@ -66,7 +70,11 @@ class Sites extends Service { if (providerSilentMode != null) 'providerSilentMode': providerSilentMode, if (providerRootDirectory != null) 'providerRootDirectory': providerRootDirectory, - if (specification != null) 'specification': specification, + if (buildSpecification != null) 'buildSpecification': buildSpecification, + if (runtimeSpecification != null) + 'runtimeSpecification': runtimeSpecification, + if (deploymentRetention != null) + 'deploymentRetention': deploymentRetention, }; final Map apiHeaders = { @@ -132,6 +140,7 @@ class Sites extends Service { int? timeout, String? installCommand, String? buildCommand, + String? startCommand, String? outputDirectory, enums.BuildRuntime? buildRuntime, enums.Adapter? adapter, @@ -141,7 +150,9 @@ class Sites extends Service { String? providerBranch, bool? providerSilentMode, String? providerRootDirectory, - String? specification}) async { + String? buildSpecification, + String? runtimeSpecification, + int? deploymentRetention}) async { final String apiPath = '/sites/{siteId}'.replaceAll('{siteId}', siteId); final Map apiParams = { @@ -152,6 +163,7 @@ class Sites extends Service { if (timeout != null) 'timeout': timeout, if (installCommand != null) 'installCommand': installCommand, if (buildCommand != null) 'buildCommand': buildCommand, + if (startCommand != null) 'startCommand': startCommand, if (outputDirectory != null) 'outputDirectory': outputDirectory, if (buildRuntime != null) 'buildRuntime': buildRuntime.value, if (adapter != null) 'adapter': adapter.value, @@ -163,7 +175,11 @@ class Sites extends Service { if (providerSilentMode != null) 'providerSilentMode': providerSilentMode, if (providerRootDirectory != null) 'providerRootDirectory': providerRootDirectory, - if (specification != null) 'specification': specification, + if (buildSpecification != null) 'buildSpecification': buildSpecification, + if (runtimeSpecification != null) + 'runtimeSpecification': runtimeSpecification, + if (deploymentRetention != null) + 'deploymentRetention': deploymentRetention, }; final Map apiHeaders = { diff --git a/lib/services/tables_db.dart b/lib/services/tables_db.dart index f435822..9c6ebdd 100644 --- a/lib/services/tables_db.dart +++ b/lib/services/tables_db.dart @@ -1534,7 +1534,7 @@ class TablesDB extends Service { {required String databaseId, required String tableId, required String key, - required enums.IndexType type, + required enums.TablesDBIndexType type, required List columns, List? orders, List? lengths}) async { diff --git a/lib/services/users.dart b/lib/services/users.dart index 67de24b..6804e33 100644 --- a/lib/services/users.dart +++ b/lib/services/users.dart @@ -357,6 +357,31 @@ class Users extends Service { return models.User.fromMap(res.data); } + /// Enable or disable whether a user can impersonate other users. When + /// impersonation headers are used, the request runs as the target user for API + /// behavior, while internal audit logs still attribute the action to the + /// original impersonator and store the impersonated target details only in + /// internal audit payload data. + /// + Future updateImpersonator( + {required String userId, required bool impersonator}) async { + final String apiPath = + '/users/{userId}/impersonator'.replaceAll('{userId}', userId); + + final Map apiParams = { + 'impersonator': impersonator, + }; + + final Map apiHeaders = { + 'content-type': 'application/json', + }; + + final res = await client.call(HttpMethod.patch, + path: apiPath, params: apiParams, headers: apiHeaders); + + return models.User.fromMap(res.data); + } + /// Use this endpoint to create a JSON Web Token for user by its unique ID. You /// can use the resulting JWT to authenticate on behalf of the user. The JWT /// secret will become invalid if the session it uses gets deleted. diff --git a/lib/services/webhooks.dart b/lib/services/webhooks.dart new file mode 100644 index 0000000..a5ea340 --- /dev/null +++ b/lib/services/webhooks.dart @@ -0,0 +1,144 @@ +part of '../dart_appwrite.dart'; + +class Webhooks extends Service { + Webhooks(super.client); + + /// Get a list of all webhooks belonging to the project. You can use the query + /// params to filter your results. + Future list({List? queries, bool? total}) async { + final String apiPath = '/webhooks'; + + final Map apiParams = { + if (queries != null) 'queries': queries, + if (total != null) 'total': total, + }; + + final Map apiHeaders = {}; + + final res = await client.call(HttpMethod.get, + path: apiPath, params: apiParams, headers: apiHeaders); + + return models.WebhookList.fromMap(res.data); + } + + /// Create a new webhook. Use this endpoint to configure a URL that will + /// receive events from Appwrite when specific events occur. + Future create( + {required String webhookId, + required String url, + required String name, + required List events, + bool? enabled, + bool? security, + String? httpUser, + String? httpPass}) async { + final String apiPath = '/webhooks'; + + final Map apiParams = { + 'webhookId': webhookId, + 'url': url, + 'name': name, + 'events': events, + if (enabled != null) 'enabled': enabled, + if (security != null) 'security': security, + if (httpUser != null) 'httpUser': httpUser, + if (httpPass != null) 'httpPass': httpPass, + }; + + final Map apiHeaders = { + 'content-type': 'application/json', + }; + + final res = await client.call(HttpMethod.post, + path: apiPath, params: apiParams, headers: apiHeaders); + + return models.Webhook.fromMap(res.data); + } + + /// Get a webhook by its unique ID. This endpoint returns details about a + /// specific webhook configured for a project. + Future get({required String webhookId}) async { + final String apiPath = + '/webhooks/{webhookId}'.replaceAll('{webhookId}', webhookId); + + final Map apiParams = {}; + + final Map apiHeaders = {}; + + final res = await client.call(HttpMethod.get, + path: apiPath, params: apiParams, headers: apiHeaders); + + return models.Webhook.fromMap(res.data); + } + + /// Update a webhook by its unique ID. Use this endpoint to update the URL, + /// events, or status of an existing webhook. + Future update( + {required String webhookId, + required String name, + required String url, + required List events, + bool? enabled, + bool? security, + String? httpUser, + String? httpPass}) async { + final String apiPath = + '/webhooks/{webhookId}'.replaceAll('{webhookId}', webhookId); + + final Map apiParams = { + 'name': name, + 'url': url, + 'events': events, + if (enabled != null) 'enabled': enabled, + if (security != null) 'security': security, + if (httpUser != null) 'httpUser': httpUser, + if (httpPass != null) 'httpPass': httpPass, + }; + + final Map apiHeaders = { + 'content-type': 'application/json', + }; + + final res = await client.call(HttpMethod.put, + path: apiPath, params: apiParams, headers: apiHeaders); + + return models.Webhook.fromMap(res.data); + } + + /// Delete a webhook by its unique ID. Once deleted, the webhook will no longer + /// receive project events. + Future delete({required String webhookId}) async { + final String apiPath = + '/webhooks/{webhookId}'.replaceAll('{webhookId}', webhookId); + + final Map apiParams = {}; + + final Map apiHeaders = { + 'content-type': 'application/json', + }; + + final res = await client.call(HttpMethod.delete, + path: apiPath, params: apiParams, headers: apiHeaders); + + return res.data; + } + + /// Update the webhook signature key. This endpoint can be used to regenerate + /// the signature key used to sign and validate payload deliveries for a + /// specific webhook. + Future updateSignature({required String webhookId}) async { + final String apiPath = + '/webhooks/{webhookId}/signature'.replaceAll('{webhookId}', webhookId); + + final Map apiParams = {}; + + final Map apiHeaders = { + 'content-type': 'application/json', + }; + + final res = await client.call(HttpMethod.patch, + path: apiPath, params: apiParams, headers: apiHeaders); + + return models.Webhook.fromMap(res.data); + } +} diff --git a/lib/src/client.dart b/lib/src/client.dart index 9f903e9..ea877a5 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -65,6 +65,21 @@ abstract class Client { /// The user agent string of the client that made the request Client setForwardedUserAgent(String value); + /// Set ImpersonateUserId + /// + /// Impersonate a user by ID on an already user-authenticated request. Requires the current request to be authenticated as a user with impersonator capability; X-Appwrite-Key alone is not sufficient. Impersonator users are intentionally granted users.read so they can discover a target before impersonation begins. Internal audit logs still attribute actions to the original impersonator and record the impersonated target only in internal audit payload data. + Client setImpersonateUserId(String value); + + /// Set ImpersonateUserEmail + /// + /// Impersonate a user by email on an already user-authenticated request. Requires the current request to be authenticated as a user with impersonator capability; X-Appwrite-Key alone is not sufficient. Impersonator users are intentionally granted users.read so they can discover a target before impersonation begins. Internal audit logs still attribute actions to the original impersonator and record the impersonated target only in internal audit payload data. + Client setImpersonateUserEmail(String value); + + /// Set ImpersonateUserPhone + /// + /// Impersonate a user by phone on an already user-authenticated request. Requires the current request to be authenticated as a user with impersonator capability; X-Appwrite-Key alone is not sufficient. Impersonator users are intentionally granted users.read so they can discover a target before impersonation begins. Internal audit logs still attribute actions to the original impersonator and record the impersonated target only in internal audit payload data. + Client setImpersonateUserPhone(String value); + /// Add headers that should be sent with all API calls. Client addHeader(String key, String value); diff --git a/lib/src/client_base.dart b/lib/src/client_base.dart index 08d93b8..337a893 100644 --- a/lib/src/client_base.dart +++ b/lib/src/client_base.dart @@ -25,6 +25,18 @@ abstract class ClientBase implements Client { @override ClientBase setForwardedUserAgent(value); + /// Impersonate a user by ID on an already user-authenticated request. Requires the current request to be authenticated as a user with impersonator capability; X-Appwrite-Key alone is not sufficient. Impersonator users are intentionally granted users.read so they can discover a target before impersonation begins. Internal audit logs still attribute actions to the original impersonator and record the impersonated target only in internal audit payload data. + @override + ClientBase setImpersonateUserId(value); + + /// Impersonate a user by email on an already user-authenticated request. Requires the current request to be authenticated as a user with impersonator capability; X-Appwrite-Key alone is not sufficient. Impersonator users are intentionally granted users.read so they can discover a target before impersonation begins. Internal audit logs still attribute actions to the original impersonator and record the impersonated target only in internal audit payload data. + @override + ClientBase setImpersonateUserEmail(value); + + /// Impersonate a user by phone on an already user-authenticated request. Requires the current request to be authenticated as a user with impersonator capability; X-Appwrite-Key alone is not sufficient. Impersonator users are intentionally granted users.read so they can discover a target before impersonation begins. Internal audit logs still attribute actions to the original impersonator and record the impersonated target only in internal audit payload data. + @override + ClientBase setImpersonateUserPhone(value); + @override ClientBase setSelfSigned({bool status = true}); diff --git a/lib/src/client_browser.dart b/lib/src/client_browser.dart index f67c538..d289c7c 100644 --- a/lib/src/client_browser.dart +++ b/lib/src/client_browser.dart @@ -33,8 +33,8 @@ class ClientBrowser extends ClientBase with ClientMixin { 'x-sdk-name': 'Dart', 'x-sdk-platform': 'server', 'x-sdk-language': 'dart', - 'x-sdk-version': '21.3.0', - 'X-Appwrite-Response-Format': '1.8.0', + 'x-sdk-version': '22.0.0', + 'X-Appwrite-Response-Format': '1.9.0', }; config = {}; @@ -93,6 +93,30 @@ class ClientBrowser extends ClientBase with ClientMixin { return this; } + /// Impersonate a user by ID on an already user-authenticated request. Requires the current request to be authenticated as a user with impersonator capability; X-Appwrite-Key alone is not sufficient. Impersonator users are intentionally granted users.read so they can discover a target before impersonation begins. Internal audit logs still attribute actions to the original impersonator and record the impersonated target only in internal audit payload data. + @override + ClientBrowser setImpersonateUserId(value) { + config['impersonateUserId'] = value; + addHeader('X-Appwrite-Impersonate-User-Id', value); + return this; + } + + /// Impersonate a user by email on an already user-authenticated request. Requires the current request to be authenticated as a user with impersonator capability; X-Appwrite-Key alone is not sufficient. Impersonator users are intentionally granted users.read so they can discover a target before impersonation begins. Internal audit logs still attribute actions to the original impersonator and record the impersonated target only in internal audit payload data. + @override + ClientBrowser setImpersonateUserEmail(value) { + config['impersonateUserEmail'] = value; + addHeader('X-Appwrite-Impersonate-User-Email', value); + return this; + } + + /// Impersonate a user by phone on an already user-authenticated request. Requires the current request to be authenticated as a user with impersonator capability; X-Appwrite-Key alone is not sufficient. Impersonator users are intentionally granted users.read so they can discover a target before impersonation begins. Internal audit logs still attribute actions to the original impersonator and record the impersonated target only in internal audit payload data. + @override + ClientBrowser setImpersonateUserPhone(value) { + config['impersonateUserPhone'] = value; + addHeader('X-Appwrite-Impersonate-User-Phone', value); + return this; + } + @override ClientBrowser setSelfSigned({bool status = true}) { return this; diff --git a/lib/src/client_io.dart b/lib/src/client_io.dart index f5a5a90..d1de987 100644 --- a/lib/src/client_io.dart +++ b/lib/src/client_io.dart @@ -42,10 +42,10 @@ class ClientIO extends ClientBase with ClientMixin { 'x-sdk-name': 'Dart', 'x-sdk-platform': 'server', 'x-sdk-language': 'dart', - 'x-sdk-version': '21.3.0', + 'x-sdk-version': '22.0.0', 'user-agent': - 'AppwriteDartSDK/21.3.0 (${Platform.operatingSystem}; ${Platform.operatingSystemVersion})', - 'X-Appwrite-Response-Format': '1.8.0', + 'AppwriteDartSDK/22.0.0 (${Platform.operatingSystem}; ${Platform.operatingSystemVersion})', + 'X-Appwrite-Response-Format': '1.9.0', }; config = {}; @@ -104,6 +104,30 @@ class ClientIO extends ClientBase with ClientMixin { return this; } + /// Impersonate a user by ID on an already user-authenticated request. Requires the current request to be authenticated as a user with impersonator capability; X-Appwrite-Key alone is not sufficient. Impersonator users are intentionally granted users.read so they can discover a target before impersonation begins. Internal audit logs still attribute actions to the original impersonator and record the impersonated target only in internal audit payload data. + @override + ClientIO setImpersonateUserId(value) { + config['impersonateUserId'] = value; + addHeader('X-Appwrite-Impersonate-User-Id', value); + return this; + } + + /// Impersonate a user by email on an already user-authenticated request. Requires the current request to be authenticated as a user with impersonator capability; X-Appwrite-Key alone is not sufficient. Impersonator users are intentionally granted users.read so they can discover a target before impersonation begins. Internal audit logs still attribute actions to the original impersonator and record the impersonated target only in internal audit payload data. + @override + ClientIO setImpersonateUserEmail(value) { + config['impersonateUserEmail'] = value; + addHeader('X-Appwrite-Impersonate-User-Email', value); + return this; + } + + /// Impersonate a user by phone on an already user-authenticated request. Requires the current request to be authenticated as a user with impersonator capability; X-Appwrite-Key alone is not sufficient. Impersonator users are intentionally granted users.read so they can discover a target before impersonation begins. Internal audit logs still attribute actions to the original impersonator and record the impersonated target only in internal audit payload data. + @override + ClientIO setImpersonateUserPhone(value) { + config['impersonateUserPhone'] = value; + addHeader('X-Appwrite-Impersonate-User-Phone', value); + return this; + } + @override ClientIO setSelfSigned({bool status = true}) { _nativeClient.badCertificateCallback = diff --git a/lib/src/enums/backup_services.dart b/lib/src/enums/backup_services.dart index c4917b6..8bdf087 100644 --- a/lib/src/enums/backup_services.dart +++ b/lib/src/enums/backup_services.dart @@ -2,6 +2,9 @@ part of '../../enums.dart'; enum BackupServices { databases(value: 'databases'), + tablesdb(value: 'tablesdb'), + documentsdb(value: 'documentsdb'), + vectorsdb(value: 'vectorsdb'), functions(value: 'functions'), storage(value: 'storage'); diff --git a/lib/src/enums/database_type.dart b/lib/src/enums/database_type.dart index ce969d6..c6887ff 100644 --- a/lib/src/enums/database_type.dart +++ b/lib/src/enums/database_type.dart @@ -2,7 +2,9 @@ part of '../../enums.dart'; enum DatabaseType { legacy(value: 'legacy'), - tablesdb(value: 'tablesdb'); + tablesdb(value: 'tablesdb'), + documentsdb(value: 'documentsdb'), + vectorsdb(value: 'vectorsdb'); const DatabaseType({required this.value}); diff --git a/lib/src/enums/databases_index_type.dart b/lib/src/enums/databases_index_type.dart new file mode 100644 index 0000000..1ae29cf --- /dev/null +++ b/lib/src/enums/databases_index_type.dart @@ -0,0 +1,14 @@ +part of '../../enums.dart'; + +enum DatabasesIndexType { + key(value: 'key'), + fulltext(value: 'fulltext'), + unique(value: 'unique'), + spatial(value: 'spatial'); + + const DatabasesIndexType({required this.value}); + + final String value; + + String toJson() => value; +} diff --git a/lib/src/enums/scopes.dart b/lib/src/enums/scopes.dart index 6e6679f..adf5695 100644 --- a/lib/src/enums/scopes.dart +++ b/lib/src/enums/scopes.dart @@ -58,6 +58,10 @@ enum Scopes { assistantRead(value: 'assistant.read'), tokensRead(value: 'tokens.read'), tokensWrite(value: 'tokens.write'), + webhooksRead(value: 'webhooks.read'), + webhooksWrite(value: 'webhooks.write'), + projectRead(value: 'project.read'), + projectWrite(value: 'project.write'), policiesWrite(value: 'policies.write'), policiesRead(value: 'policies.read'), archivesRead(value: 'archives.read'), diff --git a/lib/src/enums/index_type.dart b/lib/src/enums/tables_db_index_type.dart similarity index 71% rename from lib/src/enums/index_type.dart rename to lib/src/enums/tables_db_index_type.dart index e5633bc..77bec7a 100644 --- a/lib/src/enums/index_type.dart +++ b/lib/src/enums/tables_db_index_type.dart @@ -1,12 +1,12 @@ part of '../../enums.dart'; -enum IndexType { +enum TablesDBIndexType { key(value: 'key'), fulltext(value: 'fulltext'), unique(value: 'unique'), spatial(value: 'spatial'); - const IndexType({required this.value}); + const TablesDBIndexType({required this.value}); final String value; diff --git a/lib/src/enums/template_reference_type.dart b/lib/src/enums/template_reference_type.dart index 48ee1fa..e688d65 100644 --- a/lib/src/enums/template_reference_type.dart +++ b/lib/src/enums/template_reference_type.dart @@ -1,8 +1,8 @@ part of '../../enums.dart'; enum TemplateReferenceType { - branch(value: 'branch'), commit(value: 'commit'), + branch(value: 'branch'), tag(value: 'tag'); const TemplateReferenceType({required this.value}); diff --git a/lib/src/models/document.dart b/lib/src/models/document.dart index e3b9f3c..be08d59 100644 --- a/lib/src/models/document.dart +++ b/lib/src/models/document.dart @@ -6,7 +6,7 @@ class Document implements Model { final String $id; /// Document sequence ID. - final int $sequence; + final String $sequence; /// Collection ID. final String $collectionId; @@ -39,7 +39,7 @@ class Document implements Model { factory Document.fromMap(Map map) { return Document( $id: map['\$id'].toString(), - $sequence: map['\$sequence'], + $sequence: map['\$sequence'].toString(), $collectionId: map['\$collectionId'].toString(), $databaseId: map['\$databaseId'].toString(), $createdAt: map['\$createdAt'].toString(), diff --git a/lib/src/models/function.dart b/lib/src/models/function.dart index fd4807a..ddf8acc 100644 --- a/lib/src/models/function.dart +++ b/lib/src/models/function.dart @@ -29,6 +29,9 @@ class Func implements Model { /// Function execution and build runtime. final String runtime; + /// How many days to keep the non-active deployments before they will be automatically deleted. + final int deploymentRetention; + /// Function's active deployment ID. final String deploymentId; @@ -83,8 +86,11 @@ class Func implements Model { /// Is VCS (Version Control System) connection is in silent mode? When in silence mode, no comments will be posted on the repository pull or merge requests final bool providerSilentMode; - /// Machine specification for builds and executions. - final String specification; + /// Machine specification for deployment builds. + final String buildSpecification; + + /// Machine specification for executions. + final String runtimeSpecification; Func({ required this.$id, @@ -96,6 +102,7 @@ class Func implements Model { required this.live, required this.logging, required this.runtime, + required this.deploymentRetention, required this.deploymentId, required this.deploymentCreatedAt, required this.latestDeploymentId, @@ -114,7 +121,8 @@ class Func implements Model { required this.providerBranch, required this.providerRootDirectory, required this.providerSilentMode, - required this.specification, + required this.buildSpecification, + required this.runtimeSpecification, }); factory Func.fromMap(Map map) { @@ -128,6 +136,7 @@ class Func implements Model { live: map['live'], logging: map['logging'], runtime: map['runtime'].toString(), + deploymentRetention: map['deploymentRetention'], deploymentId: map['deploymentId'].toString(), deploymentCreatedAt: map['deploymentCreatedAt'].toString(), latestDeploymentId: map['latestDeploymentId'].toString(), @@ -146,7 +155,8 @@ class Func implements Model { providerBranch: map['providerBranch'].toString(), providerRootDirectory: map['providerRootDirectory'].toString(), providerSilentMode: map['providerSilentMode'], - specification: map['specification'].toString(), + buildSpecification: map['buildSpecification'].toString(), + runtimeSpecification: map['runtimeSpecification'].toString(), ); } @@ -162,6 +172,7 @@ class Func implements Model { "live": live, "logging": logging, "runtime": runtime, + "deploymentRetention": deploymentRetention, "deploymentId": deploymentId, "deploymentCreatedAt": deploymentCreatedAt, "latestDeploymentId": latestDeploymentId, @@ -180,7 +191,8 @@ class Func implements Model { "providerBranch": providerBranch, "providerRootDirectory": providerRootDirectory, "providerSilentMode": providerSilentMode, - "specification": specification, + "buildSpecification": buildSpecification, + "runtimeSpecification": runtimeSpecification, }; } } diff --git a/lib/src/models/log.dart b/lib/src/models/log.dart index de0d83b..ff67b75 100644 --- a/lib/src/models/log.dart +++ b/lib/src/models/log.dart @@ -5,13 +5,13 @@ class Log implements Model { /// Event name. final String event; - /// User ID. + /// User ID of the actor recorded for this log. During impersonation, this is the original impersonator, not the impersonated target user. final String userId; - /// User Email. + /// User email of the actor recorded for this log. During impersonation, this is the original impersonator. final String userEmail; - /// User Name. + /// User name of the actor recorded for this log. During impersonation, this is the original impersonator. final String userName; /// API mode when event triggered. diff --git a/lib/src/models/row.dart b/lib/src/models/row.dart index 32d8a38..a825eab 100644 --- a/lib/src/models/row.dart +++ b/lib/src/models/row.dart @@ -6,7 +6,7 @@ class Row implements Model { final String $id; /// Row sequence ID. - final int $sequence; + final String $sequence; /// Table ID. final String $tableId; @@ -39,7 +39,7 @@ class Row implements Model { factory Row.fromMap(Map map) { return Row( $id: map['\$id'].toString(), - $sequence: map['\$sequence'], + $sequence: map['\$sequence'].toString(), $tableId: map['\$tableId'].toString(), $databaseId: map['\$databaseId'].toString(), $createdAt: map['\$createdAt'].toString(), diff --git a/lib/src/models/site.dart b/lib/src/models/site.dart index 1b61e0e..d2594ea 100644 --- a/lib/src/models/site.dart +++ b/lib/src/models/site.dart @@ -26,6 +26,9 @@ class Site implements Model { /// Site framework. final String framework; + /// How many days to keep the non-active deployments before they will be automatically deleted. + final int deploymentRetention; + /// Site's active deployment ID. final String deploymentId; @@ -59,6 +62,9 @@ class Site implements Model { /// The build command used to build the site. final String buildCommand; + /// Custom command to use when starting site runtime. + final String startCommand; + /// The directory where the site build output is located. final String outputDirectory; @@ -77,8 +83,11 @@ class Site implements Model { /// Is VCS (Version Control System) connection is in silent mode? When in silence mode, no comments will be posted on the repository pull or merge requests final bool providerSilentMode; - /// Machine specification for builds and executions. - final String specification; + /// Machine specification for deployment builds. + final String buildSpecification; + + /// Machine specification for SSR executions. + final String runtimeSpecification; /// Site build runtime. final String buildRuntime; @@ -98,6 +107,7 @@ class Site implements Model { required this.live, required this.logging, required this.framework, + required this.deploymentRetention, required this.deploymentId, required this.deploymentCreatedAt, required this.deploymentScreenshotLight, @@ -109,13 +119,15 @@ class Site implements Model { required this.timeout, required this.installCommand, required this.buildCommand, + required this.startCommand, required this.outputDirectory, required this.installationId, required this.providerRepositoryId, required this.providerBranch, required this.providerRootDirectory, required this.providerSilentMode, - required this.specification, + required this.buildSpecification, + required this.runtimeSpecification, required this.buildRuntime, required this.adapter, required this.fallbackFile, @@ -131,6 +143,7 @@ class Site implements Model { live: map['live'], logging: map['logging'], framework: map['framework'].toString(), + deploymentRetention: map['deploymentRetention'], deploymentId: map['deploymentId'].toString(), deploymentCreatedAt: map['deploymentCreatedAt'].toString(), deploymentScreenshotLight: map['deploymentScreenshotLight'].toString(), @@ -142,13 +155,15 @@ class Site implements Model { timeout: map['timeout'], installCommand: map['installCommand'].toString(), buildCommand: map['buildCommand'].toString(), + startCommand: map['startCommand'].toString(), outputDirectory: map['outputDirectory'].toString(), installationId: map['installationId'].toString(), providerRepositoryId: map['providerRepositoryId'].toString(), providerBranch: map['providerBranch'].toString(), providerRootDirectory: map['providerRootDirectory'].toString(), providerSilentMode: map['providerSilentMode'], - specification: map['specification'].toString(), + buildSpecification: map['buildSpecification'].toString(), + runtimeSpecification: map['runtimeSpecification'].toString(), buildRuntime: map['buildRuntime'].toString(), adapter: map['adapter'].toString(), fallbackFile: map['fallbackFile'].toString(), @@ -166,6 +181,7 @@ class Site implements Model { "live": live, "logging": logging, "framework": framework, + "deploymentRetention": deploymentRetention, "deploymentId": deploymentId, "deploymentCreatedAt": deploymentCreatedAt, "deploymentScreenshotLight": deploymentScreenshotLight, @@ -177,13 +193,15 @@ class Site implements Model { "timeout": timeout, "installCommand": installCommand, "buildCommand": buildCommand, + "startCommand": startCommand, "outputDirectory": outputDirectory, "installationId": installationId, "providerRepositoryId": providerRepositoryId, "providerBranch": providerBranch, "providerRootDirectory": providerRootDirectory, "providerSilentMode": providerSilentMode, - "specification": specification, + "buildSpecification": buildSpecification, + "runtimeSpecification": runtimeSpecification, "buildRuntime": buildRuntime, "adapter": adapter, "fallbackFile": fallbackFile, diff --git a/lib/src/models/user.dart b/lib/src/models/user.dart index 1c444e7..67b7f44 100644 --- a/lib/src/models/user.dart +++ b/lib/src/models/user.dart @@ -59,6 +59,12 @@ class User implements Model { /// Most recent access date in ISO 8601 format. This attribute is only updated again after 24 hours. final String accessedAt; + /// Whether the user can impersonate other users. + final bool? impersonator; + + /// ID of the original actor performing the impersonation. Present only when the current request is impersonating another user. Internal audit logs attribute the action to this user, while the impersonated target is recorded only in internal audit payload data. + final String? impersonatorUserId; + User({ required this.$id, required this.$createdAt, @@ -79,6 +85,8 @@ class User implements Model { required this.prefs, required this.targets, required this.accessedAt, + this.impersonator, + this.impersonatorUserId, }); factory User.fromMap(Map map) { @@ -102,6 +110,8 @@ class User implements Model { prefs: Preferences.fromMap(map['prefs']), targets: List.from(map['targets'].map((p) => Target.fromMap(p))), accessedAt: map['accessedAt'].toString(), + impersonator: map['impersonator'], + impersonatorUserId: map['impersonatorUserId']?.toString(), ); } @@ -127,6 +137,8 @@ class User implements Model { "prefs": prefs.toMap(), "targets": targets.map((p) => p.toMap()).toList(), "accessedAt": accessedAt, + "impersonator": impersonator, + "impersonatorUserId": impersonatorUserId, }; } } diff --git a/lib/src/models/webhook.dart b/lib/src/models/webhook.dart new file mode 100644 index 0000000..13e7c2b --- /dev/null +++ b/lib/src/models/webhook.dart @@ -0,0 +1,96 @@ +part of '../../models.dart'; + +/// Webhook +class Webhook implements Model { + /// Webhook ID. + final String $id; + + /// Webhook creation date in ISO 8601 format. + final String $createdAt; + + /// Webhook update date in ISO 8601 format. + final String $updatedAt; + + /// Webhook name. + final String name; + + /// Webhook URL endpoint. + final String url; + + /// Webhook trigger events. + final List events; + + /// Indicated if SSL / TLS Certificate verification is enabled. + final bool security; + + /// HTTP basic authentication username. + final String httpUser; + + /// HTTP basic authentication password. + final String httpPass; + + /// Signature key which can be used to validated incoming + final String signatureKey; + + /// Indicates if this webhook is enabled. + final bool enabled; + + /// Webhook error logs from the most recent failure. + final String logs; + + /// Number of consecutive failed webhook attempts. + final int attempts; + + Webhook({ + required this.$id, + required this.$createdAt, + required this.$updatedAt, + required this.name, + required this.url, + required this.events, + required this.security, + required this.httpUser, + required this.httpPass, + required this.signatureKey, + required this.enabled, + required this.logs, + required this.attempts, + }); + + factory Webhook.fromMap(Map map) { + return Webhook( + $id: map['\$id'].toString(), + $createdAt: map['\$createdAt'].toString(), + $updatedAt: map['\$updatedAt'].toString(), + name: map['name'].toString(), + url: map['url'].toString(), + events: List.from(map['events'] ?? []), + security: map['security'], + httpUser: map['httpUser'].toString(), + httpPass: map['httpPass'].toString(), + signatureKey: map['signatureKey'].toString(), + enabled: map['enabled'], + logs: map['logs'].toString(), + attempts: map['attempts'], + ); + } + + @override + Map toMap() { + return { + "\$id": $id, + "\$createdAt": $createdAt, + "\$updatedAt": $updatedAt, + "name": name, + "url": url, + "events": events, + "security": security, + "httpUser": httpUser, + "httpPass": httpPass, + "signatureKey": signatureKey, + "enabled": enabled, + "logs": logs, + "attempts": attempts, + }; + } +} diff --git a/lib/src/models/webhook_list.dart b/lib/src/models/webhook_list.dart new file mode 100644 index 0000000..5f14cbe --- /dev/null +++ b/lib/src/models/webhook_list.dart @@ -0,0 +1,31 @@ +part of '../../models.dart'; + +/// Webhooks List +class WebhookList implements Model { + /// Total number of webhooks that matched your query. + final int total; + + /// List of webhooks. + final List webhooks; + + WebhookList({ + required this.total, + required this.webhooks, + }); + + factory WebhookList.fromMap(Map map) { + return WebhookList( + total: map['total'], + webhooks: + List.from(map['webhooks'].map((p) => Webhook.fromMap(p))), + ); + } + + @override + Map toMap() { + return { + "total": total, + "webhooks": webhooks.map((p) => p.toMap()).toList(), + }; + } +} diff --git a/pubspec.yaml b/pubspec.yaml index e5c9f12..9887edc 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: dart_appwrite -version: 21.3.0 +version: 22.0.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-dart diff --git a/test/services/databases_test.dart b/test/services/databases_test.dart index 383fcd2..f85e4c1 100644 --- a/test/services/databases_test.dart +++ b/test/services/databases_test.dart @@ -1296,7 +1296,7 @@ void main() { test('test method createDocument()', () async { final Map data = { '\$id': '5e5ea5c16897e', - '\$sequence': 1, + '\$sequence': '1', '\$collectionId': '5e5ea5c15117e', '\$databaseId': '5e5ea5c15117e', '\$createdAt': '2020-10-15T06:38:00.000+00:00', @@ -1390,7 +1390,7 @@ void main() { test('test method getDocument()', () async { final Map data = { '\$id': '5e5ea5c16897e', - '\$sequence': 1, + '\$sequence': '1', '\$collectionId': '5e5ea5c15117e', '\$databaseId': '5e5ea5c15117e', '\$createdAt': '2020-10-15T06:38:00.000+00:00', @@ -1413,7 +1413,7 @@ void main() { test('test method upsertDocument()', () async { final Map data = { '\$id': '5e5ea5c16897e', - '\$sequence': 1, + '\$sequence': '1', '\$collectionId': '5e5ea5c15117e', '\$databaseId': '5e5ea5c15117e', '\$createdAt': '2020-10-15T06:38:00.000+00:00', @@ -1436,7 +1436,7 @@ void main() { test('test method updateDocument()', () async { final Map data = { '\$id': '5e5ea5c16897e', - '\$sequence': 1, + '\$sequence': '1', '\$collectionId': '5e5ea5c15117e', '\$databaseId': '5e5ea5c15117e', '\$createdAt': '2020-10-15T06:38:00.000+00:00', @@ -1473,7 +1473,7 @@ void main() { test('test method decrementDocumentAttribute()', () async { final Map data = { '\$id': '5e5ea5c16897e', - '\$sequence': 1, + '\$sequence': '1', '\$collectionId': '5e5ea5c15117e', '\$databaseId': '5e5ea5c15117e', '\$createdAt': '2020-10-15T06:38:00.000+00:00', @@ -1497,7 +1497,7 @@ void main() { test('test method incrementDocumentAttribute()', () async { final Map data = { '\$id': '5e5ea5c16897e', - '\$sequence': 1, + '\$sequence': '1', '\$collectionId': '5e5ea5c15117e', '\$databaseId': '5e5ea5c15117e', '\$createdAt': '2020-10-15T06:38:00.000+00:00', @@ -1556,7 +1556,7 @@ void main() { databaseId: '', collectionId: '', key: '', - type: enums.IndexType.key, + type: enums.DatabasesIndexType.key, attributes: [], ); expect(response, isA()); diff --git a/test/services/functions_test.dart b/test/services/functions_test.dart index 0696169..917bc94 100644 --- a/test/services/functions_test.dart +++ b/test/services/functions_test.dart @@ -79,6 +79,7 @@ void main() { 'live': true, 'logging': true, 'runtime': 'python-3.8', + 'deploymentRetention': 7, 'deploymentId': '5e5ea5c16897e', 'deploymentCreatedAt': '2020-10-15T06:38:00.000+00:00', 'latestDeploymentId': '5e5ea5c16897e', @@ -97,7 +98,8 @@ void main() { 'providerBranch': 'main', 'providerRootDirectory': 'functions/helloWorld', 'providerSilentMode': true, - 'specification': 's-1vcpu-512mb', + 'buildSpecification': 's-1vcpu-512mb', + 'runtimeSpecification': 's-1vcpu-512mb', }; when(client.call( @@ -151,6 +153,7 @@ void main() { 'live': true, 'logging': true, 'runtime': 'python-3.8', + 'deploymentRetention': 7, 'deploymentId': '5e5ea5c16897e', 'deploymentCreatedAt': '2020-10-15T06:38:00.000+00:00', 'latestDeploymentId': '5e5ea5c16897e', @@ -169,7 +172,8 @@ void main() { 'providerBranch': 'main', 'providerRootDirectory': 'functions/helloWorld', 'providerSilentMode': true, - 'specification': 's-1vcpu-512mb', + 'buildSpecification': 's-1vcpu-512mb', + 'runtimeSpecification': 's-1vcpu-512mb', }; when(client.call( @@ -193,6 +197,7 @@ void main() { 'live': true, 'logging': true, 'runtime': 'python-3.8', + 'deploymentRetention': 7, 'deploymentId': '5e5ea5c16897e', 'deploymentCreatedAt': '2020-10-15T06:38:00.000+00:00', 'latestDeploymentId': '5e5ea5c16897e', @@ -211,7 +216,8 @@ void main() { 'providerBranch': 'main', 'providerRootDirectory': 'functions/helloWorld', 'providerSilentMode': true, - 'specification': 's-1vcpu-512mb', + 'buildSpecification': 's-1vcpu-512mb', + 'runtimeSpecification': 's-1vcpu-512mb', }; when(client.call( @@ -248,6 +254,7 @@ void main() { 'live': true, 'logging': true, 'runtime': 'python-3.8', + 'deploymentRetention': 7, 'deploymentId': '5e5ea5c16897e', 'deploymentCreatedAt': '2020-10-15T06:38:00.000+00:00', 'latestDeploymentId': '5e5ea5c16897e', @@ -266,7 +273,8 @@ void main() { 'providerBranch': 'main', 'providerRootDirectory': 'functions/helloWorld', 'providerSilentMode': true, - 'specification': 's-1vcpu-512mb', + 'buildSpecification': 's-1vcpu-512mb', + 'runtimeSpecification': 's-1vcpu-512mb', }; when(client.call( diff --git a/test/services/health_test.dart b/test/services/health_test.dart index 3935e4b..87f8740 100644 --- a/test/services/health_test.dart +++ b/test/services/health_test.dart @@ -171,32 +171,6 @@ void main() { expect(response, isA()); }); - test('test method getQueueBillingProjectAggregation()', () async { - final Map data = { - 'size': 8, - }; - - when(client.call( - HttpMethod.get, - )).thenAnswer((_) async => Response(data: data)); - - final response = await health.getQueueBillingProjectAggregation(); - expect(response, isA()); - }); - - test('test method getQueueBillingTeamAggregation()', () async { - final Map data = { - 'size': 8, - }; - - when(client.call( - HttpMethod.get, - )).thenAnswer((_) async => Response(data: data)); - - final response = await health.getQueueBillingTeamAggregation(); - expect(response, isA()); - }); - test('test method getQueueBuilds()', () async { final Map data = { 'size': 8, @@ -210,19 +184,6 @@ void main() { expect(response, isA()); }); - test('test method getQueuePriorityBuilds()', () async { - final Map data = { - 'size': 8, - }; - - when(client.call( - HttpMethod.get, - )).thenAnswer((_) async => Response(data: data)); - - final response = await health.getQueuePriorityBuilds(); - expect(response, isA()); - }); - test('test method getQueueCertificates()', () async { final Map data = { 'size': 8, @@ -342,19 +303,6 @@ void main() { expect(response, isA()); }); - test('test method getQueueRegionManager()', () async { - final Map data = { - 'size': 8, - }; - - when(client.call( - HttpMethod.get, - )).thenAnswer((_) async => Response(data: data)); - - final response = await health.getQueueRegionManager(); - expect(response, isA()); - }); - test('test method getQueueStatsResources()', () async { final Map data = { 'size': 8, @@ -381,19 +329,6 @@ void main() { expect(response, isA()); }); - test('test method getQueueThreats()', () async { - final Map data = { - 'size': 8, - }; - - when(client.call( - HttpMethod.get, - )).thenAnswer((_) async => Response(data: data)); - - final response = await health.getQueueThreats(); - expect(response, isA()); - }); - test('test method getQueueWebhooks()', () async { final Map data = { 'size': 8, diff --git a/test/services/project_test.dart b/test/services/project_test.dart new file mode 100644 index 0000000..3083b50 --- /dev/null +++ b/test/services/project_test.dart @@ -0,0 +1,151 @@ +import 'package:test/test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:dart_appwrite/models.dart' as models; +import 'package:dart_appwrite/enums.dart' as enums; +import 'package:dart_appwrite/src/enums.dart'; +import 'package:dart_appwrite/src/response.dart'; +import 'dart:typed_data'; +import 'package:dart_appwrite/dart_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) 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('Project test', () { + late MockClient client; + late Project project; + + setUp(() { + client = MockClient(); + project = Project(client); + }); + + test('test method listVariables()', () async { + final Map data = { + 'total': 5, + 'variables': [], + }; + + when(client.call( + HttpMethod.get, + )).thenAnswer((_) async => Response(data: data)); + + final response = await project.listVariables(); + expect(response, isA()); + }); + + test('test method createVariable()', () async { + final Map data = { + '\$id': '5e5ea5c16897e', + '\$createdAt': '2020-10-15T06:38:00.000+00:00', + '\$updatedAt': '2020-10-15T06:38:00.000+00:00', + 'key': 'API_KEY', + 'value': 'myPa\$\$word1', + 'secret': true, + 'resourceType': 'function', + 'resourceId': 'myAwesomeFunction', + }; + + when(client.call( + HttpMethod.post, + )).thenAnswer((_) async => Response(data: data)); + + final response = await project.createVariable( + variableId: '', + key: '', + value: '', + ); + expect(response, isA()); + }); + + test('test method getVariable()', () async { + final Map data = { + '\$id': '5e5ea5c16897e', + '\$createdAt': '2020-10-15T06:38:00.000+00:00', + '\$updatedAt': '2020-10-15T06:38:00.000+00:00', + 'key': 'API_KEY', + 'value': 'myPa\$\$word1', + 'secret': true, + 'resourceType': 'function', + 'resourceId': 'myAwesomeFunction', + }; + + when(client.call( + HttpMethod.get, + )).thenAnswer((_) async => Response(data: data)); + + final response = await project.getVariable( + variableId: '', + ); + expect(response, isA()); + }); + + test('test method updateVariable()', () async { + final Map data = { + '\$id': '5e5ea5c16897e', + '\$createdAt': '2020-10-15T06:38:00.000+00:00', + '\$updatedAt': '2020-10-15T06:38:00.000+00:00', + 'key': 'API_KEY', + 'value': 'myPa\$\$word1', + 'secret': true, + 'resourceType': 'function', + 'resourceId': 'myAwesomeFunction', + }; + + when(client.call( + HttpMethod.put, + )).thenAnswer((_) async => Response(data: data)); + + final response = await project.updateVariable( + variableId: '', + ); + expect(response, isA()); + }); + + test('test method deleteVariable()', () async { + final data = ''; + + when(client.call( + HttpMethod.delete, + )).thenAnswer((_) async => Response(data: data)); + + final response = await project.deleteVariable( + variableId: '', + ); + }); + }); +} diff --git a/test/services/sites_test.dart b/test/services/sites_test.dart index 8d0c05b..208a16b 100644 --- a/test/services/sites_test.dart +++ b/test/services/sites_test.dart @@ -78,6 +78,7 @@ void main() { 'live': true, 'logging': true, 'framework': 'react', + 'deploymentRetention': 7, 'deploymentId': '5e5ea5c16897e', 'deploymentCreatedAt': '2020-10-15T06:38:00.000+00:00', 'deploymentScreenshotLight': '5e5ea5c16897e', @@ -89,13 +90,15 @@ void main() { 'timeout': 300, 'installCommand': 'npm install', 'buildCommand': 'npm run build', + 'startCommand': 'node custom-server.mjs', 'outputDirectory': 'build', 'installationId': '6m40at4ejk5h2u9s1hboo', 'providerRepositoryId': 'appwrite', 'providerBranch': 'main', 'providerRootDirectory': 'sites/helloWorld', 'providerSilentMode': true, - 'specification': 's-1vcpu-512mb', + 'buildSpecification': 's-1vcpu-512mb', + 'runtimeSpecification': 's-1vcpu-512mb', 'buildRuntime': 'node-22', 'adapter': 'static', 'fallbackFile': 'index.html', @@ -152,6 +155,7 @@ void main() { 'live': true, 'logging': true, 'framework': 'react', + 'deploymentRetention': 7, 'deploymentId': '5e5ea5c16897e', 'deploymentCreatedAt': '2020-10-15T06:38:00.000+00:00', 'deploymentScreenshotLight': '5e5ea5c16897e', @@ -163,13 +167,15 @@ void main() { 'timeout': 300, 'installCommand': 'npm install', 'buildCommand': 'npm run build', + 'startCommand': 'node custom-server.mjs', 'outputDirectory': 'build', 'installationId': '6m40at4ejk5h2u9s1hboo', 'providerRepositoryId': 'appwrite', 'providerBranch': 'main', 'providerRootDirectory': 'sites/helloWorld', 'providerSilentMode': true, - 'specification': 's-1vcpu-512mb', + 'buildSpecification': 's-1vcpu-512mb', + 'runtimeSpecification': 's-1vcpu-512mb', 'buildRuntime': 'node-22', 'adapter': 'static', 'fallbackFile': 'index.html', @@ -195,6 +201,7 @@ void main() { 'live': true, 'logging': true, 'framework': 'react', + 'deploymentRetention': 7, 'deploymentId': '5e5ea5c16897e', 'deploymentCreatedAt': '2020-10-15T06:38:00.000+00:00', 'deploymentScreenshotLight': '5e5ea5c16897e', @@ -206,13 +213,15 @@ void main() { 'timeout': 300, 'installCommand': 'npm install', 'buildCommand': 'npm run build', + 'startCommand': 'node custom-server.mjs', 'outputDirectory': 'build', 'installationId': '6m40at4ejk5h2u9s1hboo', 'providerRepositoryId': 'appwrite', 'providerBranch': 'main', 'providerRootDirectory': 'sites/helloWorld', 'providerSilentMode': true, - 'specification': 's-1vcpu-512mb', + 'buildSpecification': 's-1vcpu-512mb', + 'runtimeSpecification': 's-1vcpu-512mb', 'buildRuntime': 'node-22', 'adapter': 'static', 'fallbackFile': 'index.html', @@ -252,6 +261,7 @@ void main() { 'live': true, 'logging': true, 'framework': 'react', + 'deploymentRetention': 7, 'deploymentId': '5e5ea5c16897e', 'deploymentCreatedAt': '2020-10-15T06:38:00.000+00:00', 'deploymentScreenshotLight': '5e5ea5c16897e', @@ -263,13 +273,15 @@ void main() { 'timeout': 300, 'installCommand': 'npm install', 'buildCommand': 'npm run build', + 'startCommand': 'node custom-server.mjs', 'outputDirectory': 'build', 'installationId': '6m40at4ejk5h2u9s1hboo', 'providerRepositoryId': 'appwrite', 'providerBranch': 'main', 'providerRootDirectory': 'sites/helloWorld', 'providerSilentMode': true, - 'specification': 's-1vcpu-512mb', + 'buildSpecification': 's-1vcpu-512mb', + 'runtimeSpecification': 's-1vcpu-512mb', 'buildRuntime': 'node-22', 'adapter': 'static', 'fallbackFile': 'index.html', diff --git a/test/services/tables_db_test.dart b/test/services/tables_db_test.dart index 549b28d..2711abd 100644 --- a/test/services/tables_db_test.dart +++ b/test/services/tables_db_test.dart @@ -1314,7 +1314,7 @@ void main() { databaseId: '', tableId: '', key: '', - type: enums.IndexType.key, + type: enums.TablesDBIndexType.key, columns: [], ); expect(response, isA()); @@ -1379,7 +1379,7 @@ void main() { test('test method createRow()', () async { final Map data = { '\$id': '5e5ea5c16897e', - '\$sequence': 1, + '\$sequence': '1', '\$tableId': '5e5ea5c15117e', '\$databaseId': '5e5ea5c15117e', '\$createdAt': '2020-10-15T06:38:00.000+00:00', @@ -1473,7 +1473,7 @@ void main() { test('test method getRow()', () async { final Map data = { '\$id': '5e5ea5c16897e', - '\$sequence': 1, + '\$sequence': '1', '\$tableId': '5e5ea5c15117e', '\$databaseId': '5e5ea5c15117e', '\$createdAt': '2020-10-15T06:38:00.000+00:00', @@ -1496,7 +1496,7 @@ void main() { test('test method upsertRow()', () async { final Map data = { '\$id': '5e5ea5c16897e', - '\$sequence': 1, + '\$sequence': '1', '\$tableId': '5e5ea5c15117e', '\$databaseId': '5e5ea5c15117e', '\$createdAt': '2020-10-15T06:38:00.000+00:00', @@ -1519,7 +1519,7 @@ void main() { test('test method updateRow()', () async { final Map data = { '\$id': '5e5ea5c16897e', - '\$sequence': 1, + '\$sequence': '1', '\$tableId': '5e5ea5c15117e', '\$databaseId': '5e5ea5c15117e', '\$createdAt': '2020-10-15T06:38:00.000+00:00', @@ -1556,7 +1556,7 @@ void main() { test('test method decrementRowColumn()', () async { final Map data = { '\$id': '5e5ea5c16897e', - '\$sequence': 1, + '\$sequence': '1', '\$tableId': '5e5ea5c15117e', '\$databaseId': '5e5ea5c15117e', '\$createdAt': '2020-10-15T06:38:00.000+00:00', @@ -1580,7 +1580,7 @@ void main() { test('test method incrementRowColumn()', () async { final Map data = { '\$id': '5e5ea5c16897e', - '\$sequence': 1, + '\$sequence': '1', '\$tableId': '5e5ea5c15117e', '\$databaseId': '5e5ea5c15117e', '\$createdAt': '2020-10-15T06:38:00.000+00:00', diff --git a/test/services/users_test.dart b/test/services/users_test.dart index 9f448c2..ee8e4e5 100644 --- a/test/services/users_test.dart +++ b/test/services/users_test.dart @@ -429,6 +429,37 @@ void main() { expect(response, isA()); }); + test('test method updateImpersonator()', () async { + final Map data = { + '\$id': '5e5ea5c16897e', + '\$createdAt': '2020-10-15T06:38:00.000+00:00', + '\$updatedAt': '2020-10-15T06:38:00.000+00:00', + 'name': 'John Doe', + 'registration': '2020-10-15T06:38:00.000+00:00', + 'status': true, + 'labels': [], + 'passwordUpdate': '2020-10-15T06:38:00.000+00:00', + 'email': 'john@appwrite.io', + 'phone': '+4930901820', + 'emailVerification': true, + 'phoneVerification': true, + 'mfa': true, + 'prefs': {}, + 'targets': [], + 'accessedAt': '2020-10-15T06:38:00.000+00:00', + }; + + when(client.call( + HttpMethod.patch, + )).thenAnswer((_) async => Response(data: data)); + + final response = await users.updateImpersonator( + userId: '', + impersonator: true, + ); + expect(response, isA()); + }); + test('test method createJWT()', () async { final Map data = { 'jwt': diff --git a/test/services/webhooks_test.dart b/test/services/webhooks_test.dart new file mode 100644 index 0000000..e2018b9 --- /dev/null +++ b/test/services/webhooks_test.dart @@ -0,0 +1,197 @@ +import 'package:test/test.dart'; +import 'package:mockito/mockito.dart'; +import 'package:dart_appwrite/models.dart' as models; +import 'package:dart_appwrite/enums.dart' as enums; +import 'package:dart_appwrite/src/enums.dart'; +import 'package:dart_appwrite/src/response.dart'; +import 'dart:typed_data'; +import 'package:dart_appwrite/dart_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) 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('Webhooks test', () { + late MockClient client; + late Webhooks webhooks; + + setUp(() { + client = MockClient(); + webhooks = Webhooks(client); + }); + + test('test method list()', () async { + final Map data = { + 'total': 5, + 'webhooks': [], + }; + + when(client.call( + HttpMethod.get, + )).thenAnswer((_) async => Response(data: data)); + + final response = await webhooks.list(); + expect(response, isA()); + }); + + test('test method create()', () async { + final Map data = { + '\$id': '5e5ea5c16897e', + '\$createdAt': '2020-10-15T06:38:00.000+00:00', + '\$updatedAt': '2020-10-15T06:38:00.000+00:00', + 'name': 'My Webhook', + 'url': 'https://example.com/webhook', + 'events': [], + 'security': true, + 'httpUser': 'username', + 'httpPass': 'password', + 'signatureKey': 'ad3d581ca230e2b7059c545e5a', + 'enabled': true, + 'logs': 'Failed to connect to remote server.', + 'attempts': 10, + }; + + when(client.call( + HttpMethod.post, + )).thenAnswer((_) async => Response(data: data)); + + final response = await webhooks.create( + webhookId: '', + url: '', + name: '', + events: [], + ); + expect(response, isA()); + }); + + test('test method get()', () async { + final Map data = { + '\$id': '5e5ea5c16897e', + '\$createdAt': '2020-10-15T06:38:00.000+00:00', + '\$updatedAt': '2020-10-15T06:38:00.000+00:00', + 'name': 'My Webhook', + 'url': 'https://example.com/webhook', + 'events': [], + 'security': true, + 'httpUser': 'username', + 'httpPass': 'password', + 'signatureKey': 'ad3d581ca230e2b7059c545e5a', + 'enabled': true, + 'logs': 'Failed to connect to remote server.', + 'attempts': 10, + }; + + when(client.call( + HttpMethod.get, + )).thenAnswer((_) async => Response(data: data)); + + final response = await webhooks.get( + webhookId: '', + ); + expect(response, isA()); + }); + + test('test method update()', () async { + final Map data = { + '\$id': '5e5ea5c16897e', + '\$createdAt': '2020-10-15T06:38:00.000+00:00', + '\$updatedAt': '2020-10-15T06:38:00.000+00:00', + 'name': 'My Webhook', + 'url': 'https://example.com/webhook', + 'events': [], + 'security': true, + 'httpUser': 'username', + 'httpPass': 'password', + 'signatureKey': 'ad3d581ca230e2b7059c545e5a', + 'enabled': true, + 'logs': 'Failed to connect to remote server.', + 'attempts': 10, + }; + + when(client.call( + HttpMethod.put, + )).thenAnswer((_) async => Response(data: data)); + + final response = await webhooks.update( + webhookId: '', + name: '', + url: '', + events: [], + ); + expect(response, isA()); + }); + + test('test method delete()', () async { + final data = ''; + + when(client.call( + HttpMethod.delete, + )).thenAnswer((_) async => Response(data: data)); + + final response = await webhooks.delete( + webhookId: '', + ); + }); + + test('test method updateSignature()', () async { + final Map data = { + '\$id': '5e5ea5c16897e', + '\$createdAt': '2020-10-15T06:38:00.000+00:00', + '\$updatedAt': '2020-10-15T06:38:00.000+00:00', + 'name': 'My Webhook', + 'url': 'https://example.com/webhook', + 'events': [], + 'security': true, + 'httpUser': 'username', + 'httpPass': 'password', + 'signatureKey': 'ad3d581ca230e2b7059c545e5a', + 'enabled': true, + 'logs': 'Failed to connect to remote server.', + 'attempts': 10, + }; + + when(client.call( + HttpMethod.patch, + )).thenAnswer((_) async => Response(data: data)); + + final response = await webhooks.updateSignature( + webhookId: '', + ); + expect(response, isA()); + }); + }); +} diff --git a/test/src/models/document_test.dart b/test/src/models/document_test.dart index 971b58d..5fbdc12 100644 --- a/test/src/models/document_test.dart +++ b/test/src/models/document_test.dart @@ -6,7 +6,7 @@ void main() { test('model', () { final model = Document( $id: '5e5ea5c16897e', - $sequence: 1, + $sequence: '1', $collectionId: '5e5ea5c15117e', $databaseId: '5e5ea5c15117e', $createdAt: '2020-10-15T06:38:00.000+00:00', @@ -19,7 +19,7 @@ void main() { final result = Document.fromMap(map); expect(result.$id, '5e5ea5c16897e'); - expect(result.$sequence, 1); + expect(result.$sequence, '1'); expect(result.$collectionId, '5e5ea5c15117e'); expect(result.$databaseId, '5e5ea5c15117e'); expect(result.$createdAt, '2020-10-15T06:38:00.000+00:00'); diff --git a/test/src/models/function_test.dart b/test/src/models/function_test.dart index 204e18f..8a0dfb7 100644 --- a/test/src/models/function_test.dart +++ b/test/src/models/function_test.dart @@ -14,6 +14,7 @@ void main() { live: true, logging: true, runtime: 'python-3.8', + deploymentRetention: 7, deploymentId: '5e5ea5c16897e', deploymentCreatedAt: '2020-10-15T06:38:00.000+00:00', latestDeploymentId: '5e5ea5c16897e', @@ -32,7 +33,8 @@ void main() { providerBranch: 'main', providerRootDirectory: 'functions/helloWorld', providerSilentMode: true, - specification: 's-1vcpu-512mb', + buildSpecification: 's-1vcpu-512mb', + runtimeSpecification: 's-1vcpu-512mb', ); final map = model.toMap(); @@ -47,6 +49,7 @@ void main() { expect(result.live, true); expect(result.logging, true); expect(result.runtime, 'python-3.8'); + expect(result.deploymentRetention, 7); expect(result.deploymentId, '5e5ea5c16897e'); expect(result.deploymentCreatedAt, '2020-10-15T06:38:00.000+00:00'); expect(result.latestDeploymentId, '5e5ea5c16897e'); @@ -65,7 +68,8 @@ void main() { expect(result.providerBranch, 'main'); expect(result.providerRootDirectory, 'functions/helloWorld'); expect(result.providerSilentMode, true); - expect(result.specification, 's-1vcpu-512mb'); + expect(result.buildSpecification, 's-1vcpu-512mb'); + expect(result.runtimeSpecification, 's-1vcpu-512mb'); }); }); } diff --git a/test/src/models/row_test.dart b/test/src/models/row_test.dart index 9770d87..5d81c34 100644 --- a/test/src/models/row_test.dart +++ b/test/src/models/row_test.dart @@ -6,7 +6,7 @@ void main() { test('model', () { final model = Row( $id: '5e5ea5c16897e', - $sequence: 1, + $sequence: '1', $tableId: '5e5ea5c15117e', $databaseId: '5e5ea5c15117e', $createdAt: '2020-10-15T06:38:00.000+00:00', @@ -19,7 +19,7 @@ void main() { final result = Row.fromMap(map); expect(result.$id, '5e5ea5c16897e'); - expect(result.$sequence, 1); + expect(result.$sequence, '1'); expect(result.$tableId, '5e5ea5c15117e'); expect(result.$databaseId, '5e5ea5c15117e'); expect(result.$createdAt, '2020-10-15T06:38:00.000+00:00'); diff --git a/test/src/models/site_test.dart b/test/src/models/site_test.dart index d73d100..6d97b6f 100644 --- a/test/src/models/site_test.dart +++ b/test/src/models/site_test.dart @@ -13,6 +13,7 @@ void main() { live: true, logging: true, framework: 'react', + deploymentRetention: 7, deploymentId: '5e5ea5c16897e', deploymentCreatedAt: '2020-10-15T06:38:00.000+00:00', deploymentScreenshotLight: '5e5ea5c16897e', @@ -24,13 +25,15 @@ void main() { timeout: 300, installCommand: 'npm install', buildCommand: 'npm run build', + startCommand: 'node custom-server.mjs', outputDirectory: 'build', installationId: '6m40at4ejk5h2u9s1hboo', providerRepositoryId: 'appwrite', providerBranch: 'main', providerRootDirectory: 'sites/helloWorld', providerSilentMode: true, - specification: 's-1vcpu-512mb', + buildSpecification: 's-1vcpu-512mb', + runtimeSpecification: 's-1vcpu-512mb', buildRuntime: 'node-22', adapter: 'static', fallbackFile: 'index.html', @@ -47,6 +50,7 @@ void main() { expect(result.live, true); expect(result.logging, true); expect(result.framework, 'react'); + expect(result.deploymentRetention, 7); expect(result.deploymentId, '5e5ea5c16897e'); expect(result.deploymentCreatedAt, '2020-10-15T06:38:00.000+00:00'); expect(result.deploymentScreenshotLight, '5e5ea5c16897e'); @@ -58,13 +62,15 @@ void main() { expect(result.timeout, 300); expect(result.installCommand, 'npm install'); expect(result.buildCommand, 'npm run build'); + expect(result.startCommand, 'node custom-server.mjs'); expect(result.outputDirectory, 'build'); expect(result.installationId, '6m40at4ejk5h2u9s1hboo'); expect(result.providerRepositoryId, 'appwrite'); expect(result.providerBranch, 'main'); expect(result.providerRootDirectory, 'sites/helloWorld'); expect(result.providerSilentMode, true); - expect(result.specification, 's-1vcpu-512mb'); + expect(result.buildSpecification, 's-1vcpu-512mb'); + expect(result.runtimeSpecification, 's-1vcpu-512mb'); expect(result.buildRuntime, 'node-22'); expect(result.adapter, 'static'); expect(result.fallbackFile, 'index.html'); diff --git a/test/src/models/webhook_list_test.dart b/test/src/models/webhook_list_test.dart new file mode 100644 index 0000000..c17b914 --- /dev/null +++ b/test/src/models/webhook_list_test.dart @@ -0,0 +1,19 @@ +import 'package:dart_appwrite/models.dart'; +import 'package:test/test.dart'; + +void main() { + group('WebhookList', () { + test('model', () { + final model = WebhookList( + total: 5, + webhooks: [], + ); + + final map = model.toMap(); + final result = WebhookList.fromMap(map); + + expect(result.total, 5); + expect(result.webhooks, []); + }); + }); +} diff --git a/test/src/models/webhook_test.dart b/test/src/models/webhook_test.dart new file mode 100644 index 0000000..639e9d3 --- /dev/null +++ b/test/src/models/webhook_test.dart @@ -0,0 +1,41 @@ +import 'package:dart_appwrite/models.dart'; +import 'package:test/test.dart'; + +void main() { + group('Webhook', () { + test('model', () { + final model = Webhook( + $id: '5e5ea5c16897e', + $createdAt: '2020-10-15T06:38:00.000+00:00', + $updatedAt: '2020-10-15T06:38:00.000+00:00', + name: 'My Webhook', + url: 'https://example.com/webhook', + events: [], + security: true, + httpUser: 'username', + httpPass: 'password', + signatureKey: 'ad3d581ca230e2b7059c545e5a', + enabled: true, + logs: 'Failed to connect to remote server.', + attempts: 10, + ); + + final map = model.toMap(); + final result = Webhook.fromMap(map); + + expect(result.$id, '5e5ea5c16897e'); + expect(result.$createdAt, '2020-10-15T06:38:00.000+00:00'); + expect(result.$updatedAt, '2020-10-15T06:38:00.000+00:00'); + expect(result.name, 'My Webhook'); + expect(result.url, 'https://example.com/webhook'); + expect(result.events, []); + expect(result.security, true); + expect(result.httpUser, 'username'); + expect(result.httpPass, 'password'); + expect(result.signatureKey, 'ad3d581ca230e2b7059c545e5a'); + expect(result.enabled, true); + expect(result.logs, 'Failed to connect to remote server.'); + expect(result.attempts, 10); + }); + }); +}