Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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