Skip to content

chore: add setDevKey and upsertDocument methods #251

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 21, 2025
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

## 16.0.1

* Add `setDevKey` method to Client service
* Add `upsertDocument` method to Databases service

## 16.0.0

* Remove `Gif` from ImageFormat enum
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: ^16.0.0
appwrite: ^16.1.0
```

You can install packages from the command line:
Expand Down
15 changes: 15 additions & 0 deletions docs/examples/databases/upsert-document.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import 'package:appwrite/appwrite.dart';

Client client = Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID

Databases databases = Databases(client);

Document result = await databases.upsertDocument(
databaseId: '<DATABASE_ID>',
collectionId: '<COLLECTION_ID>',
documentId: '<DOCUMENT_ID>',
data: {},
permissions: ["read("any")"], // optional
);
24 changes: 11 additions & 13 deletions lib/query.dart
Original file line number Diff line number Diff line change
Expand Up @@ -82,19 +82,17 @@ class Query {
static String contains(String attribute, dynamic value) =>
Query._('contains', attribute, value).toString();

static String or(List<String> queries) =>
Query._(
'or',
null,
queries.map((query) => jsonDecode(query)).toList(),
).toString();

static String and(List<String> queries) =>
Query._(
'and',
null,
queries.map((query) => jsonDecode(query)).toList(),
).toString();
static String or(List<String> queries) => Query._(
'or',
null,
queries.map((query) => jsonDecode(query)).toList(),
).toString();

static String and(List<String> queries) => Query._(
'and',
null,
queries.map((query) => jsonDecode(query)).toList(),
).toString();

/// Specify which attributes should be returned by the API call.
static String select(List<String> attributes) =>
Expand Down
34 changes: 34 additions & 0 deletions lib/services/databases.dart
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,40 @@ class Databases extends Service {
return models.Document.fromMap(res.data);
}

/// Create or update a Document. Before using this route, you should create a
/// new collection resource using either a [server
/// integration](https://appwrite.io/docs/server/databases#databasesCreateCollection)
/// API or directly from your database console.
Future<models.Document> upsertDocument({
required String databaseId,
required String collectionId,
required String documentId,
required Map data,
List<String>? permissions,
}) async {
final String apiPath =
'/databases/{databaseId}/collections/{collectionId}/documents/{documentId}'
.replaceAll('{databaseId}', databaseId)
.replaceAll('{collectionId}', collectionId)
.replaceAll('{documentId}', documentId);

final Map<String, dynamic> apiParams = {
'data': data,
'permissions': permissions,
};

final Map<String, String> apiHeaders = {'content-type': 'application/json'};

final res = await client.call(
HttpMethod.put,
path: apiPath,
params: apiParams,
headers: apiHeaders,
);

return models.Document.fromMap(res.data);
}

/// Update a document by its unique ID. Using the patch method you can pass
/// only specific fields that will get updated.
Future<models.Document> updateDocument({
Expand Down
5 changes: 5 additions & 0 deletions lib/src/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ abstract class Client {
/// The user session to authenticate with.
Client setSession(value);

/// Set DevKey.
///
/// Your secret dev API key.
Client setDevKey(value);

/// Add headers that should be sent with all API calls.
Client addHeader(String key, String value);

Expand Down
4 changes: 4 additions & 0 deletions lib/src/client_base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ abstract class ClientBase implements Client {
@override
ClientBase setSession(value);

/// Your secret dev API key
@override
ClientBase setDevKey(value);

@override
ClientBase setSelfSigned({bool status = true});

Expand Down
10 changes: 9 additions & 1 deletion lib/src/client_browser.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class ClientBrowser extends ClientBase with ClientMixin {
'x-sdk-name': 'Flutter',
'x-sdk-platform': 'client',
'x-sdk-language': 'flutter',
'x-sdk-version': '16.0.0',
'x-sdk-version': '16.1.0',
'X-Appwrite-Response-Format': '1.7.0',
};

Expand Down Expand Up @@ -87,6 +87,14 @@ class ClientBrowser extends ClientBase with ClientMixin {
return this;
}

/// Your secret dev API key
@override
ClientBrowser setDevKey(value) {
config['devKey'] = value;
addHeader('X-Appwrite-Dev-Key', value);
return this;
}

@override
ClientBrowser setSelfSigned({bool status = true}) {
return this;
Expand Down
24 changes: 15 additions & 9 deletions lib/src/client_io.dart
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,9 @@ class ClientIO extends ClientBase with ClientMixin {
String endPoint = 'https://cloud.appwrite.io/v1',
this.selfSigned = false,
}) : _endPoint = endPoint {
_nativeClient =
HttpClient()
..badCertificateCallback =
((X509Certificate cert, String host, int port) => selfSigned);
_nativeClient = HttpClient()
..badCertificateCallback =
((X509Certificate cert, String host, int port) => selfSigned);
_httpClient = IOClient(_nativeClient);
_endPointRealtime = endPoint
.replaceFirst('https://', 'wss://')
Expand All @@ -59,7 +58,7 @@ class ClientIO extends ClientBase with ClientMixin {
'x-sdk-name': 'Flutter',
'x-sdk-platform': 'client',
'x-sdk-language': 'flutter',
'x-sdk-version': '16.0.0',
'x-sdk-version': '16.1.0',
'X-Appwrite-Response-Format': '1.7.0',
};

Expand Down Expand Up @@ -114,6 +113,14 @@ class ClientIO extends ClientBase with ClientMixin {
return this;
}

/// Your secret dev API key
@override
ClientIO setDevKey(value) {
config['devKey'] = value;
addHeader('X-Appwrite-Dev-Key', value);
return this;
}

@override
ClientIO setSelfSigned({bool status = true}) {
selfSigned = status;
Expand Down Expand Up @@ -349,10 +356,9 @@ class ClientIO extends ClientBase with ClientMixin {
Future webAuth(Uri url, {String? callbackUrlScheme}) {
return FlutterWebAuth2.authenticate(
url: url.toString(),
callbackUrlScheme:
callbackUrlScheme != null && _customSchemeAllowed
? callbackUrlScheme
: "appwrite-callback-" + config['project']!,
callbackUrlScheme: callbackUrlScheme != null && _customSchemeAllowed
? callbackUrlScheme
: "appwrite-callback-" + config['project']!,
options: const FlutterWebAuth2Options(intentFlags: ephemeralIntentFlags),
).then((value) async {
Uri url = Uri.parse(value);
Expand Down
7 changes: 3 additions & 4 deletions lib/src/client_mixin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,9 @@ mixin ClientMixin {
'',
streamedResponse.statusCode,
headers: streamedResponse.headers.map(
(k, v) =>
k.toLowerCase() == 'content-type'
? MapEntry(k, 'text/plain')
: MapEntry(k, v),
(k, v) => k.toLowerCase() == 'content-type'
? MapEntry(k, 'text/plain')
: MapEntry(k, v),
),
request: streamedResponse.request,
isRedirect: streamedResponse.isRedirect,
Expand Down
13 changes: 5 additions & 8 deletions lib/src/realtime_io.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,11 @@ class RealtimeIO extends RealtimeBase with RealtimeMixin {
var r = Random();
var key = base64.encode(List<int>.generate(16, (_) => r.nextInt(255)));
var client = HttpClient(context: SecurityContext());
client.badCertificateCallback = (
X509Certificate cert,
String host,
int port,
) {
debugPrint('AppwriteRealtime: Allow self-signed certificate');
return true;
};
client.badCertificateCallback =
(X509Certificate cert, String host, int port) {
debugPrint('AppwriteRealtime: Allow self-signed certificate');
return true;
};

uri = Uri(
scheme: uri.scheme == 'wss' ? 'https' : 'http',
Expand Down
7 changes: 3 additions & 4 deletions lib/src/realtime_stub.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import 'realtime_base.dart';
import 'client.dart';

/// Implemented in `realtime_browser.dart` and `realtime_io.dart`.
RealtimeBase createRealtime(Client client) =>
throw UnsupportedError(
'Cannot create a client without dart:html or dart:io.',
);
RealtimeBase createRealtime(Client client) => throw UnsupportedError(
'Cannot create a client without dart:html or dart:io.',
);
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: appwrite
version: 16.0.0
version: 16.1.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-flutter
Expand Down
25 changes: 25 additions & 0 deletions test/services/databases_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,31 @@ void main() {

});

test('test method upsertDocument()', () async {
final Map<String, dynamic> data = {
'\$id': '5e5ea5c16897e',
'\$collectionId': '5e5ea5c15117e',
'\$databaseId': '5e5ea5c15117e',
'\$createdAt': '2020-10-15T06:38:00.000+00:00',
'\$updatedAt': '2020-10-15T06:38:00.000+00:00',
'\$permissions': [],};


when(client.call(
HttpMethod.put,
)).thenAnswer((_) async => Response(data: data));


final response = await databases.upsertDocument(
databaseId: '<DATABASE_ID>',
collectionId: '<COLLECTION_ID>',
documentId: '<DOCUMENT_ID>',
data: {},
);
expect(response, isA<models.Document>());

});

test('test method updateDocument()', () async {
final Map<String, dynamic> data = {
'\$id': '5e5ea5c16897e',
Expand Down