Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Change Log

## 20.3.0

* Add `total` parameter to list queries allowing skipping counting rows in a table for improved performance
* Add `Operator` class for atomic modification of rows via update, bulk update, upsert, and bulk upsert operations

## 20.2.2

* Widen `device_info_plus` and `package_info_plus` dependencies to allow for newer versions for Android 15+ support
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Add this to your package's `pubspec.yaml` file:

```yml
dependencies:
appwrite: ^20.2.2
appwrite: ^20.3.0
```

You can install packages from the command line:
Expand Down
1 change: 1 addition & 0 deletions docs/examples/account/list-identities.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ Account account = Account(client);

IdentityList result = await account.listIdentities(
queries: [], // optional
total: false, // optional
);
1 change: 1 addition & 0 deletions docs/examples/account/list-logs.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ Account account = Account(client);

LogList result = await account.listLogs(
queries: [], // optional
total: false, // optional
);
1 change: 1 addition & 0 deletions docs/examples/databases/list-documents.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ DocumentList result = await databases.listDocuments(
collectionId: '<COLLECTION_ID>',
queries: [], // optional
transactionId: '<TRANSACTION_ID>', // optional
total: false, // optional
);
1 change: 1 addition & 0 deletions docs/examples/functions/list-executions.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ Functions functions = Functions(client);
ExecutionList result = await functions.listExecutions(
functionId: '<FUNCTION_ID>',
queries: [], // optional
total: false, // optional
);
1 change: 1 addition & 0 deletions docs/examples/storage/list-files.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ FileList result = await storage.listFiles(
bucketId: '<BUCKET_ID>',
queries: [], // optional
search: '<SEARCH>', // optional
total: false, // optional
);
1 change: 1 addition & 0 deletions docs/examples/tablesdb/list-rows.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ RowList result = await tablesDB.listRows(
tableId: '<TABLE_ID>',
queries: [], // optional
transactionId: '<TRANSACTION_ID>', // optional
total: false, // optional
);
1 change: 1 addition & 0 deletions docs/examples/teams/list-memberships.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ MembershipList result = await teams.listMemberships(
teamId: '<TEAM_ID>',
queries: [], // optional
search: '<SEARCH>', // optional
total: false, // optional
);
1 change: 1 addition & 0 deletions docs/examples/teams/list.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ Teams teams = Teams(client);
TeamList result = await teams.list(
queries: [], // optional
search: '<SEARCH>', // optional
total: false, // optional
);
1 change: 1 addition & 0 deletions lib/appwrite.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ part 'query.dart';
part 'permission.dart';
part 'role.dart';
part 'id.dart';
part 'operator.dart';
part 'services/account.dart';
part 'services/avatars.dart';
part 'services/databases.dart';
Expand Down
187 changes: 187 additions & 0 deletions lib/operator.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
part of 'appwrite.dart';

/// Filter condition for array operations
enum Condition {
equal('equal'),
notEqual('notEqual'),
greaterThan('greaterThan'),
greaterThanEqual('greaterThanEqual'),
lessThan('lessThan'),
lessThanEqual('lessThanEqual'),
contains('contains'),
isNull('isNull'),
isNotNull('isNotNull');

final String value;
const Condition(this.value);

@override
String toString() => value;
}

/// Helper class to generate operator strings for atomic operations.
class Operator {
final String method;
final dynamic values;

Operator._(this.method, [this.values = null]);

Map<String, dynamic> toJson() {
final result = <String, dynamic>{};

result['method'] = method;

if (values != null) {
result['values'] = values is List ? values : [values];
}

return result;
}

@override
String toString() => jsonEncode(toJson());

/// Increment a numeric attribute by a specified value.
static String increment([num value = 1, num? max]) {
if (value.toDouble().isNaN || value.toDouble().isInfinite) {
throw ArgumentError('Value cannot be NaN or Infinity');
}
if (max != null && (max.toDouble().isNaN || max.toDouble().isInfinite)) {
throw ArgumentError('Max cannot be NaN or Infinity');
}
final values = <dynamic>[value];
if (max != null) {
values.add(max);
}
return Operator._('increment', values).toString();
}

/// Decrement a numeric attribute by a specified value.
static String decrement([num value = 1, num? min]) {
if (value.toDouble().isNaN || value.toDouble().isInfinite) {
throw ArgumentError('Value cannot be NaN or Infinity');
}
if (min != null && (min.toDouble().isNaN || min.toDouble().isInfinite)) {
throw ArgumentError('Min cannot be NaN or Infinity');
}
final values = <dynamic>[value];
if (min != null) {
values.add(min);
}
return Operator._('decrement', values).toString();
}

/// Multiply a numeric attribute by a specified factor.
static String multiply(num factor, [num? max]) {
if (factor.toDouble().isNaN || factor.toDouble().isInfinite) {
throw ArgumentError('Factor cannot be NaN or Infinity');
}
if (max != null && (max.toDouble().isNaN || max.toDouble().isInfinite)) {
throw ArgumentError('Max cannot be NaN or Infinity');
}
final values = <dynamic>[factor];
if (max != null) {
values.add(max);
}
return Operator._('multiply', values).toString();
}

/// Divide a numeric attribute by a specified divisor.
static String divide(num divisor, [num? min]) {
if (divisor.toDouble().isNaN || divisor.toDouble().isInfinite) {
throw ArgumentError('Divisor cannot be NaN or Infinity');
}
if (min != null && (min.toDouble().isNaN || min.toDouble().isInfinite)) {
throw ArgumentError('Min cannot be NaN or Infinity');
}
if (divisor == 0) {
throw ArgumentError('Divisor cannot be zero');
}
final values = <dynamic>[divisor];
if (min != null) {
values.add(min);
}
return Operator._('divide', values).toString();
}

/// Apply modulo operation on a numeric attribute.
static String modulo(num divisor) {
if (divisor.toDouble().isNaN || divisor.toDouble().isInfinite) {
throw ArgumentError('Divisor cannot be NaN or Infinity');
}
if (divisor == 0) {
throw ArgumentError('Divisor cannot be zero');
}
return Operator._('modulo', [divisor]).toString();
}

/// Raise a numeric attribute to a specified power.
static String power(num exponent, [num? max]) {
if (exponent.toDouble().isNaN || exponent.toDouble().isInfinite) {
throw ArgumentError('Exponent cannot be NaN or Infinity');
}
if (max != null && (max.toDouble().isNaN || max.toDouble().isInfinite)) {
throw ArgumentError('Max cannot be NaN or Infinity');
}
final values = <dynamic>[exponent];
if (max != null) {
values.add(max);
}
return Operator._('power', values).toString();
}

/// Append values to an array attribute.
static String arrayAppend(List<dynamic> values) =>
Operator._('arrayAppend', values).toString();

/// Prepend values to an array attribute.
static String arrayPrepend(List<dynamic> values) =>
Operator._('arrayPrepend', values).toString();

/// Insert a value at a specific index in an array attribute.
static String arrayInsert(int index, dynamic value) =>
Operator._('arrayInsert', [index, value]).toString();

/// Remove a value from an array attribute.
static String arrayRemove(dynamic value) =>
Operator._('arrayRemove', [value]).toString();

/// Remove duplicate values from an array attribute.
static String arrayUnique() => Operator._('arrayUnique', []).toString();

/// Keep only values that exist in both the current array and the provided array.
static String arrayIntersect(List<dynamic> values) =>
Operator._('arrayIntersect', values).toString();

/// Remove values from the array that exist in the provided array.
static String arrayDiff(List<dynamic> values) =>
Operator._('arrayDiff', values).toString();

/// Filter array values based on a condition.
static String arrayFilter(Condition condition, [dynamic value]) {
final values = <dynamic>[condition.value, value];
return Operator._('arrayFilter', values).toString();
}

/// Concatenate a value to a string or array attribute.
static String stringConcat(dynamic value) =>
Operator._('stringConcat', [value]).toString();

/// Replace occurrences of a search string with a replacement string.
static String stringReplace(String search, String replace) =>
Operator._('stringReplace', [search, replace]).toString();

/// Toggle a boolean attribute.
static String toggle() => Operator._('toggle', []).toString();

/// Add days to a date attribute.
static String dateAddDays(int days) =>
Operator._('dateAddDays', [days]).toString();

/// Subtract days from a date attribute.
static String dateSubDays(int days) =>
Operator._('dateSubDays', [days]).toString();

/// Set a date attribute to the current date and time.
static String dateSetNow() => Operator._('dateSetNow', []).toString();
}
16 changes: 6 additions & 10 deletions lib/query.dart
Original file line number Diff line number Diff line change
Expand Up @@ -106,28 +106,24 @@ class Query {
Query._('notEndsWith', attribute, value).toString();

/// Filter resources where document was created before [value].
static String createdBefore(String value) =>
Query._('createdBefore', null, value).toString();
static String createdBefore(String value) => lessThan('\$createdAt', value);

/// Filter resources where document was created after [value].
static String createdAfter(String value) =>
Query._('createdAfter', null, value).toString();
static String createdAfter(String value) => greaterThan('\$createdAt', value);

/// Filter resources where document was created between [start] and [end] (inclusive).
static String createdBetween(String start, String end) =>
Query._('createdBetween', null, [start, end]).toString();
between('\$createdAt', start, end);

/// Filter resources where document was updated before [value].
static String updatedBefore(String value) =>
Query._('updatedBefore', null, value).toString();
static String updatedBefore(String value) => lessThan('\$updatedAt', value);

/// Filter resources where document was updated after [value].
static String updatedAfter(String value) =>
Query._('updatedAfter', null, value).toString();
static String updatedAfter(String value) => greaterThan('\$updatedAt', value);

/// Filter resources where document was updated between [start] and [end] (inclusive).
static String updatedBetween(String start, String end) =>
Query._('updatedBetween', null, [start, end]).toString();
between('\$updatedAt', start, end);

static String or(List<String> queries) => Query._(
'or',
Expand Down
7 changes: 5 additions & 2 deletions lib/services/account.dart
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,13 @@ class Account extends Service {
}

/// Get the list of identities for the currently logged in user.
Future<models.IdentityList> listIdentities({List<String>? queries}) async {
Future<models.IdentityList> listIdentities(
{List<String>? queries, bool? total}) async {
const String apiPath = '/account/identities';

final Map<String, dynamic> apiParams = {
'queries': queries,
'total': total,
};

final Map<String, String> apiHeaders = {};
Expand Down Expand Up @@ -132,11 +134,12 @@ class Account extends Service {

/// Get the list of latest security activity logs for the currently logged in
/// user. Each log returns user IP address, location and date and time of log.
Future<models.LogList> listLogs({List<String>? queries}) async {
Future<models.LogList> listLogs({List<String>? queries, bool? total}) async {
const String apiPath = '/account/logs';

final Map<String, dynamic> apiParams = {
'queries': queries,
'total': total,
};

final Map<String, String> apiHeaders = {};
Expand Down
4 changes: 3 additions & 1 deletion lib/services/databases.dart
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ class Databases extends Service {
{required String databaseId,
required String collectionId,
List<String>? queries,
String? transactionId}) async {
String? transactionId,
bool? total}) async {
final String apiPath =
'/databases/{databaseId}/collections/{collectionId}/documents'
.replaceAll('{databaseId}', databaseId)
Expand All @@ -132,6 +133,7 @@ class Databases extends Service {
final Map<String, dynamic> apiParams = {
'queries': queries,
'transactionId': transactionId,
'total': total,
};

final Map<String, String> apiHeaders = {};
Expand Down
3 changes: 2 additions & 1 deletion lib/services/functions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ class Functions extends Service {
/// Get a list of all the current user function execution logs. You can use the
/// query params to filter your results.
Future<models.ExecutionList> listExecutions(
{required String functionId, List<String>? queries}) async {
{required String functionId, List<String>? queries, bool? total}) async {
final String apiPath = '/functions/{functionId}/executions'
.replaceAll('{functionId}', functionId);

final Map<String, dynamic> apiParams = {
'queries': queries,
'total': total,
};

final Map<String, String> apiHeaders = {};
Expand Down
6 changes: 5 additions & 1 deletion lib/services/storage.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@ class Storage extends Service {
/// Get a list of all the user files. You can use the query params to filter
/// your results.
Future<models.FileList> listFiles(
{required String bucketId, List<String>? queries, String? search}) async {
{required String bucketId,
List<String>? queries,
String? search,
bool? total}) async {
final String apiPath =
'/storage/buckets/{bucketId}/files'.replaceAll('{bucketId}', bucketId);

final Map<String, dynamic> apiParams = {
'queries': queries,
'search': search,
'total': total,
};

final Map<String, String> apiHeaders = {};
Expand Down
4 changes: 3 additions & 1 deletion lib/services/tables_db.dart
Original file line number Diff line number Diff line change
Expand Up @@ -119,14 +119,16 @@ class TablesDB extends Service {
{required String databaseId,
required String tableId,
List<String>? queries,
String? transactionId}) async {
String? transactionId,
bool? total}) async {
final String apiPath = '/tablesdb/{databaseId}/tables/{tableId}/rows'
.replaceAll('{databaseId}', databaseId)
.replaceAll('{tableId}', tableId);

final Map<String, dynamic> apiParams = {
'queries': queries,
'transactionId': transactionId,
'total': total,
};

final Map<String, String> apiHeaders = {};
Expand Down
Loading