diff --git a/LICENSE b/LICENSE index 5479bb8e..c1602fcd 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2024 Appwrite (https://appwrite.io) and individual contributors. +Copyright (c) 2025 Appwrite (https://appwrite.io) and individual contributors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/README.md b/README.md index 117a0265..c1ab7d9f 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Add this to your package's `pubspec.yaml` file: ```yml dependencies: - dart_appwrite: ^12.2.0 + dart_appwrite: ^13.0.0 ``` You can install packages from the command line: diff --git a/docs/examples/account/update-mfa-challenge.md b/docs/examples/account/update-mfa-challenge.md index fd64c61c..2843d2f1 100644 --- a/docs/examples/account/update-mfa-challenge.md +++ b/docs/examples/account/update-mfa-challenge.md @@ -7,7 +7,7 @@ Client client = Client() Account account = Account(client); - result = await account.updateMfaChallenge( +Session result = await account.updateMfaChallenge( challengeId: '<CHALLENGE_ID>', otp: '<OTP>', ); diff --git a/lib/services/account.dart b/lib/services/account.dart index 00b7785c..7c477724 100644 --- a/lib/services/account.dart +++ b/lib/services/account.dart @@ -282,7 +282,7 @@ class Account extends Service { /// the flow, use /// [createMfaChallenge](/docs/references/cloud/client-web/account#createMfaChallenge) /// method. - Future updateMfaChallenge( + Future<models.Session> updateMfaChallenge( {required String challengeId, required String otp}) async { final String apiPath = '/account/mfa/challenge'; @@ -298,7 +298,7 @@ class Account extends Service { final res = await client.call(HttpMethod.put, path: apiPath, params: apiParams, headers: apiHeaders); - return res.data; + return models.Session.fromMap(res.data); } /// List factors diff --git a/lib/services/functions.dart b/lib/services/functions.dart index 7a59547e..1916994c 100644 --- a/lib/services/functions.dart +++ b/lib/services/functions.dart @@ -364,6 +364,11 @@ class Functions extends Service { /// Rebuild deployment /// + /// Create a new build for an existing function deployment. This endpoint + /// allows you to rebuild a deployment with the updated function configuration, + /// including its entrypoint and build commands if they have been modified The + /// build process will be queued and executed asynchronously. The original + /// deployment's code will be preserved and used for the new build. Future createBuild( {required String functionId, required String deploymentId, @@ -389,6 +394,11 @@ class Functions extends Service { /// Cancel deployment /// + /// Cancel an ongoing function deployment build. If the build is already in + /// progress, it will be stopped and marked as canceled. If the build hasn't + /// started yet, it will be marked as canceled without executing. You cannot + /// cancel builds that have already completed (status 'ready') or failed. The + /// response includes the final build status and details. Future<models.Build> updateDeploymentBuild( {required String functionId, required String deploymentId}) async { final String apiPath = diff --git a/lib/services/messaging.dart b/lib/services/messaging.dart index 5fc759fd..977c3a4c 100644 --- a/lib/services/messaging.dart +++ b/lib/services/messaging.dart @@ -72,7 +72,9 @@ class Messaging extends Service { /// Update email /// - /// Update an email message by its unique ID. + /// Update an email message by its unique ID. This endpoint only works on + /// messages that are in draft status. Messages that are already processing, + /// sent, or failed cannot be updated. /// Future<models.Message> updateEmail( {required String messageId, @@ -173,7 +175,9 @@ class Messaging extends Service { /// Update push notification /// - /// Update a push notification by its unique ID. + /// Update a push notification by its unique ID. This endpoint only works on + /// messages that are in draft status. Messages that are already processing, + /// sent, or failed cannot be updated. /// Future<models.Message> updatePush( {required String messageId, @@ -264,7 +268,9 @@ class Messaging extends Service { /// Update SMS /// - /// Update an email message by its unique ID. + /// Update an SMS message by its unique ID. This endpoint only works on + /// messages that are in draft status. Messages that are already processing, + /// sent, or failed cannot be updated. /// Future<models.Message> updateSms( {required String messageId, diff --git a/lib/services/users.dart b/lib/services/users.dart index 28673732..dcb9e352 100644 --- a/lib/services/users.dart +++ b/lib/services/users.dart @@ -506,7 +506,7 @@ class Users extends Service { /// Delete authenticator /// /// Delete an authenticator app. - Future<models.User> deleteMfaAuthenticator( + Future deleteMfaAuthenticator( {required String userId, required enums.AuthenticatorType type}) async { final String apiPath = '/users/{userId}/mfa/authenticators/{type}' .replaceAll('{userId}', userId) @@ -521,7 +521,7 @@ class Users extends Service { final res = await client.call(HttpMethod.delete, path: apiPath, params: apiParams, headers: apiHeaders); - return models.User.fromMap(res.data); + return res.data; } /// List factors diff --git a/lib/src/client.dart b/lib/src/client.dart index bac07cfb..07da93fd 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -67,6 +67,9 @@ abstract class Client { /// Add headers that should be sent with all API calls. Client addHeader(String key, String value); + /// Sends a "ping" request to Appwrite to verify connectivity. + Future<String> ping(); + /// Upload a file in chunks. Future<Response> chunkedUpload({ required String path, diff --git a/lib/src/client_base.dart b/lib/src/client_base.dart index 95476465..a392cc05 100644 --- a/lib/src/client_base.dart +++ b/lib/src/client_base.dart @@ -34,6 +34,9 @@ abstract class ClientBase implements Client { @override ClientBase addHeader(String key, String value); + @override + Future<String> ping(); + @override Future<Response> call( HttpMethod method, { diff --git a/lib/src/client_browser.dart b/lib/src/client_browser.dart index 428877a5..66292582 100644 --- a/lib/src/client_browser.dart +++ b/lib/src/client_browser.dart @@ -33,7 +33,7 @@ class ClientBrowser extends ClientBase with ClientMixin { 'x-sdk-name': 'Dart', 'x-sdk-platform': 'server', 'x-sdk-language': 'dart', - 'x-sdk-version': '12.2.0', + 'x-sdk-version': '13.0.0', 'X-Appwrite-Response-Format': '1.6.0', }; @@ -110,6 +110,15 @@ class ClientBrowser extends ClientBase with ClientMixin { return this; } + @override + Future<String> ping() async { + final String apiPath = '/ping'; + final response = await call(HttpMethod.get, + path: apiPath, responseType: ResponseType.plain); + + return response.data; + } + @override Future<String?> webAuth(Uri url) async { final request = http.Request('GET', url); @@ -147,7 +156,7 @@ class ClientBrowser extends ClientBase with ClientMixin { } var offset = 0; - if (idParamName.isNotEmpty && params[idParamName] != 'unique()') { + if (idParamName.isNotEmpty) { //make a request to check if a file already exists try { res = await call( diff --git a/lib/src/client_io.dart b/lib/src/client_io.dart index f0b5c028..c1250ad3 100644 --- a/lib/src/client_io.dart +++ b/lib/src/client_io.dart @@ -42,9 +42,9 @@ class ClientIO extends ClientBase with ClientMixin { 'x-sdk-name': 'Dart', 'x-sdk-platform': 'server', 'x-sdk-language': 'dart', - 'x-sdk-version': '12.2.0', + 'x-sdk-version': '13.0.0', 'user-agent': - 'AppwriteDartSDK/12.2.0 (${Platform.operatingSystem}; ${Platform.operatingSystemVersion})', + 'AppwriteDartSDK/13.0.0 (${Platform.operatingSystem}; ${Platform.operatingSystemVersion})', 'X-Appwrite-Response-Format': '1.6.0', }; @@ -123,6 +123,15 @@ class ClientIO extends ClientBase with ClientMixin { return this; } + @override + Future<String> ping() async { + final String apiPath = '/ping'; + final response = await call(HttpMethod.get, + path: apiPath, responseType: ResponseType.plain); + + return response.data; + } + @override Future<Response> chunkedUpload({ required String path, @@ -168,7 +177,7 @@ class ClientIO extends ClientBase with ClientMixin { } var offset = 0; - if (idParamName.isNotEmpty && params[idParamName] != 'unique()') { + if (idParamName.isNotEmpty) { //make a request to check if a file already exists try { res = await call( diff --git a/lib/src/enums/image_format.dart b/lib/src/enums/image_format.dart index acc07f27..4d07af18 100644 --- a/lib/src/enums/image_format.dart +++ b/lib/src/enums/image_format.dart @@ -6,6 +6,7 @@ enum ImageFormat { gif(value: 'gif'), png(value: 'png'), webp(value: 'webp'), + heic(value: 'heic'), avif(value: 'avif'); const ImageFormat({required this.value}); diff --git a/lib/src/models/attribute_enum.dart b/lib/src/models/attribute_enum.dart index 746f9864..298c181a 100644 --- a/lib/src/models/attribute_enum.dart +++ b/lib/src/models/attribute_enum.dart @@ -59,7 +59,7 @@ class AttributeEnum implements Model { array: map['array'], $createdAt: map['\$createdAt'].toString(), $updatedAt: map['\$updatedAt'].toString(), - elements: map['elements'] ?? [], + elements: List.from(map['elements'] ?? []), format: map['format'].toString(), xdefault: map['default']?.toString(), ); diff --git a/lib/src/models/attribute_list.dart b/lib/src/models/attribute_list.dart index 4d9846c1..9ac338eb 100644 --- a/lib/src/models/attribute_list.dart +++ b/lib/src/models/attribute_list.dart @@ -16,7 +16,7 @@ class AttributeList implements Model { factory AttributeList.fromMap(Map<String, dynamic> map) { return AttributeList( total: map['total'], - attributes: map['attributes'] ?? [], + attributes: List.from(map['attributes'] ?? []), ); } diff --git a/lib/src/models/bucket.dart b/lib/src/models/bucket.dart index e2aa51fe..b8fb5e48 100644 --- a/lib/src/models/bucket.dart +++ b/lib/src/models/bucket.dart @@ -58,12 +58,12 @@ class Bucket implements Model { $id: map['\$id'].toString(), $createdAt: map['\$createdAt'].toString(), $updatedAt: map['\$updatedAt'].toString(), - $permissions: map['\$permissions'] ?? [], + $permissions: List.from(map['\$permissions'] ?? []), fileSecurity: map['fileSecurity'], name: map['name'].toString(), enabled: map['enabled'], maximumFileSize: map['maximumFileSize'], - allowedFileExtensions: map['allowedFileExtensions'] ?? [], + allowedFileExtensions: List.from(map['allowedFileExtensions'] ?? []), compression: map['compression'].toString(), encryption: map['encryption'], antivirus: map['antivirus'], diff --git a/lib/src/models/collection.dart b/lib/src/models/collection.dart index ef572743..72de429d 100644 --- a/lib/src/models/collection.dart +++ b/lib/src/models/collection.dart @@ -50,12 +50,12 @@ class Collection implements Model { $id: map['\$id'].toString(), $createdAt: map['\$createdAt'].toString(), $updatedAt: map['\$updatedAt'].toString(), - $permissions: map['\$permissions'] ?? [], + $permissions: List.from(map['\$permissions'] ?? []), databaseId: map['databaseId'].toString(), name: map['name'].toString(), enabled: map['enabled'], documentSecurity: map['documentSecurity'], - attributes: map['attributes'] ?? [], + attributes: List.from(map['attributes'] ?? []), indexes: List<Index>.from(map['indexes'].map((p) => Index.fromMap(p))), ); } diff --git a/lib/src/models/document.dart b/lib/src/models/document.dart index f8164b61..37fb2b7b 100644 --- a/lib/src/models/document.dart +++ b/lib/src/models/document.dart @@ -19,6 +19,7 @@ class Document implements Model { /// Document permissions. [Learn more about permissions](https://appwrite.io/docs/permissions). final List<String> $permissions; + final Map<String, dynamic> data; Document({ @@ -38,7 +39,7 @@ class Document implements Model { $databaseId: map['\$databaseId'].toString(), $createdAt: map['\$createdAt'].toString(), $updatedAt: map['\$updatedAt'].toString(), - $permissions: map['\$permissions'] ?? [], + $permissions: List.from(map['\$permissions'] ?? []), data: map, ); } diff --git a/lib/src/models/execution.dart b/lib/src/models/execution.dart index 9f9b8917..4dee011d 100644 --- a/lib/src/models/execution.dart +++ b/lib/src/models/execution.dart @@ -78,7 +78,7 @@ class Execution implements Model { $id: map['\$id'].toString(), $createdAt: map['\$createdAt'].toString(), $updatedAt: map['\$updatedAt'].toString(), - $permissions: map['\$permissions'] ?? [], + $permissions: List.from(map['\$permissions'] ?? []), functionId: map['functionId'].toString(), trigger: map['trigger'].toString(), status: map['status'].toString(), diff --git a/lib/src/models/file.dart b/lib/src/models/file.dart index 4bca1fe3..a6a9fa46 100644 --- a/lib/src/models/file.dart +++ b/lib/src/models/file.dart @@ -55,7 +55,7 @@ class File implements Model { bucketId: map['bucketId'].toString(), $createdAt: map['\$createdAt'].toString(), $updatedAt: map['\$updatedAt'].toString(), - $permissions: map['\$permissions'] ?? [], + $permissions: List.from(map['\$permissions'] ?? []), name: map['name'].toString(), signature: map['signature'].toString(), mimeType: map['mimeType'].toString(), diff --git a/lib/src/models/function.dart b/lib/src/models/function.dart index 83b5a60d..fccea87e 100644 --- a/lib/src/models/function.dart +++ b/lib/src/models/function.dart @@ -106,16 +106,16 @@ class Func implements Model { $id: map['\$id'].toString(), $createdAt: map['\$createdAt'].toString(), $updatedAt: map['\$updatedAt'].toString(), - execute: map['execute'] ?? [], + execute: List.from(map['execute'] ?? []), name: map['name'].toString(), enabled: map['enabled'], live: map['live'], logging: map['logging'], runtime: map['runtime'].toString(), deployment: map['deployment'].toString(), - scopes: map['scopes'] ?? [], + scopes: List.from(map['scopes'] ?? []), vars: List<Variable>.from(map['vars'].map((p) => Variable.fromMap(p))), - events: map['events'] ?? [], + events: List.from(map['events'] ?? []), schedule: map['schedule'].toString(), timeout: map['timeout'], entrypoint: map['entrypoint'].toString(), diff --git a/lib/src/models/index.dart b/lib/src/models/index.dart index 322f1228..b24bf242 100644 --- a/lib/src/models/index.dart +++ b/lib/src/models/index.dart @@ -43,8 +43,8 @@ class Index implements Model { type: map['type'].toString(), status: map['status'].toString(), error: map['error'].toString(), - attributes: map['attributes'] ?? [], - orders: map['orders'], + attributes: List.from(map['attributes'] ?? []), + orders: List.from(map['orders'] ?? []), $createdAt: map['\$createdAt'].toString(), $updatedAt: map['\$updatedAt'].toString(), ); diff --git a/lib/src/models/membership.dart b/lib/src/models/membership.dart index 1dd1186d..8ee142ad 100644 --- a/lib/src/models/membership.dart +++ b/lib/src/models/membership.dart @@ -71,7 +71,7 @@ class Membership implements Model { joined: map['joined'].toString(), confirm: map['confirm'], mfa: map['mfa'], - roles: map['roles'] ?? [], + roles: List.from(map['roles'] ?? []), ); } diff --git a/lib/src/models/message.dart b/lib/src/models/message.dart index 9664cebb..9b93b00d 100644 --- a/lib/src/models/message.dart +++ b/lib/src/models/message.dart @@ -63,12 +63,12 @@ class Message implements Model { $createdAt: map['\$createdAt'].toString(), $updatedAt: map['\$updatedAt'].toString(), providerType: map['providerType'].toString(), - topics: map['topics'] ?? [], - users: map['users'] ?? [], - targets: map['targets'] ?? [], + topics: List.from(map['topics'] ?? []), + users: List.from(map['users'] ?? []), + targets: List.from(map['targets'] ?? []), scheduledAt: map['scheduledAt']?.toString(), deliveredAt: map['deliveredAt']?.toString(), - deliveryErrors: map['deliveryErrors'], + deliveryErrors: List.from(map['deliveryErrors'] ?? []), deliveredTotal: map['deliveredTotal'], data: map['data'], status: map['status'].toString(), diff --git a/lib/src/models/mfa_recovery_codes.dart b/lib/src/models/mfa_recovery_codes.dart index c304de00..425539e8 100644 --- a/lib/src/models/mfa_recovery_codes.dart +++ b/lib/src/models/mfa_recovery_codes.dart @@ -11,7 +11,7 @@ class MfaRecoveryCodes implements Model { factory MfaRecoveryCodes.fromMap(Map<String, dynamic> map) { return MfaRecoveryCodes( - recoveryCodes: map['recoveryCodes'] ?? [], + recoveryCodes: List.from(map['recoveryCodes'] ?? []), ); } diff --git a/lib/src/models/runtime.dart b/lib/src/models/runtime.dart index 249690f8..7c9677d8 100644 --- a/lib/src/models/runtime.dart +++ b/lib/src/models/runtime.dart @@ -46,7 +46,7 @@ class Runtime implements Model { base: map['base'].toString(), image: map['image'].toString(), logo: map['logo'].toString(), - supports: map['supports'] ?? [], + supports: List.from(map['supports'] ?? []), ); } diff --git a/lib/src/models/session.dart b/lib/src/models/session.dart index 11732bd1..d2fe4f64 100644 --- a/lib/src/models/session.dart +++ b/lib/src/models/session.dart @@ -149,7 +149,7 @@ class Session implements Model { countryCode: map['countryCode'].toString(), countryName: map['countryName'].toString(), current: map['current'], - factors: map['factors'] ?? [], + factors: List.from(map['factors'] ?? []), secret: map['secret'].toString(), mfaUpdatedAt: map['mfaUpdatedAt'].toString(), ); diff --git a/lib/src/models/topic.dart b/lib/src/models/topic.dart index 9714abba..430d44ac 100644 --- a/lib/src/models/topic.dart +++ b/lib/src/models/topic.dart @@ -46,7 +46,7 @@ class Topic implements Model { emailTotal: map['emailTotal'], smsTotal: map['smsTotal'], pushTotal: map['pushTotal'], - subscribe: map['subscribe'] ?? [], + subscribe: List.from(map['subscribe'] ?? []), ); } diff --git a/lib/src/models/user.dart b/lib/src/models/user.dart index e5942793..50bfb3ce 100644 --- a/lib/src/models/user.dart +++ b/lib/src/models/user.dart @@ -92,7 +92,7 @@ class User implements Model { hashOptions: map['hashOptions'], registration: map['registration'].toString(), status: map['status'], - labels: map['labels'] ?? [], + labels: List.from(map['labels'] ?? []), passwordUpdate: map['passwordUpdate'].toString(), email: map['email'].toString(), phone: map['phone'].toString(), diff --git a/pubspec.yaml b/pubspec.yaml index bffe869e..a540fe9f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: dart_appwrite -version: 12.2.0 +version: 13.0.0 description: Appwrite is an open-source self-hosted backend server that abstract and simplify 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/account_test.dart b/test/services/account_test.dart index 67144409..55679f3f 100644 --- a/test/services/account_test.dart +++ b/test/services/account_test.dart @@ -332,7 +332,37 @@ void main() { }); test('test method updateMfaChallenge()', () async { - final data = ''; + final Map<String, dynamic> data = { + '\$id': '5e5ea5c16897e', + '\$createdAt': '2020-10-15T06:38:00.000+00:00', + '\$updatedAt': '2020-10-15T06:38:00.000+00:00', + 'userId': '5e5bb8c16897e', + 'expire': '2020-10-15T06:38:00.000+00:00', + 'provider': 'email', + 'providerUid': 'user@example.com', + 'providerAccessToken': 'MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3', + 'providerAccessTokenExpiry': '2020-10-15T06:38:00.000+00:00', + 'providerRefreshToken': 'MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3', + 'ip': '127.0.0.1', + 'osCode': 'Mac', + 'osName': 'Mac', + 'osVersion': 'Mac', + 'clientType': 'browser', + 'clientCode': 'CM', + 'clientName': 'Chrome Mobile iOS', + 'clientVersion': '84.0', + 'clientEngine': 'WebKit', + 'clientEngineVersion': '605.1.15', + 'deviceName': 'smartphone', + 'deviceBrand': 'Google', + 'deviceModel': 'Nexus 5', + 'countryCode': 'US', + 'countryName': 'United States', + 'current': true, + 'factors': [], + 'secret': '5e5bb8c16897e', + 'mfaUpdatedAt': '2020-10-15T06:38:00.000+00:00',}; + when(client.call( HttpMethod.put, @@ -343,6 +373,8 @@ void main() { challengeId: '<CHALLENGE_ID>', otp: '<OTP>', ); + expect(response, isA<models.Session>()); + }); test('test method listMfaFactors()', () async { diff --git a/test/services/users_test.dart b/test/services/users_test.dart index d5ac88da..9f05493d 100644 --- a/test/services/users_test.dart +++ b/test/services/users_test.dart @@ -577,24 +577,7 @@ void main() { }); test('test method deleteMfaAuthenticator()', () async { - final Map<String, dynamic> 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': <String, dynamic>{}, - 'targets': [], - 'accessedAt': '2020-10-15T06:38:00.000+00:00',}; - + final data = ''; when(client.call( HttpMethod.delete, @@ -605,8 +588,6 @@ void main() { userId: '<USER_ID>', type: 'totp', ); - expect(response, isA<models.User>()); - }); test('test method listMfaFactors()', () async {