Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 2b89c20

Browse files
Support for update and merge writes (#298)
1 parent dd45c33 commit 2b89c20

File tree

8 files changed

+142
-9
lines changed

8 files changed

+142
-9
lines changed

packages/cloud_firestore/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 0.1.2
2+
3+
* Support for `DocumentReference` update and merge writes
4+
* Suppress unchecked warnings and package name warnings on Android
5+
16
## 0.1.1
27

38
* Added FLT prefix to iOS types.

packages/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/CloudFirestorePlugin.java renamed to packages/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/cloudfirestore/CloudFirestorePlugin.java

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5-
package io.flutter.plugins.firebase.cloud_firestore;
5+
package io.flutter.plugins.firebase.cloudfirestore;
66

7+
import android.support.annotation.NonNull;
78
import android.util.SparseArray;
9+
import com.google.android.gms.tasks.OnFailureListener;
10+
import com.google.android.gms.tasks.OnSuccessListener;
11+
import com.google.android.gms.tasks.Task;
812
import com.google.firebase.firestore.CollectionReference;
913
import com.google.firebase.firestore.DocumentChange;
1014
import com.google.firebase.firestore.DocumentReference;
@@ -15,6 +19,7 @@
1519
import com.google.firebase.firestore.ListenerRegistration;
1620
import com.google.firebase.firestore.Query;
1721
import com.google.firebase.firestore.QuerySnapshot;
22+
import com.google.firebase.firestore.SetOptions;
1823
import io.flutter.plugin.common.MethodCall;
1924
import io.flutter.plugin.common.MethodChannel;
2025
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
@@ -27,7 +32,6 @@
2732

2833
public class CloudFirestorePlugin implements MethodCallHandler {
2934

30-
public static final String TAG = "FirestorePlugin";
3135
private final MethodChannel channel;
3236

3337
// Handles are ints used as indexes into the sparse array of active observers
@@ -61,6 +65,7 @@ private Query getQuery(Map<String, Object> arguments) {
6165
@SuppressWarnings("unchecked")
6266
Map<String, Object> parameters = (Map<String, Object>) arguments.get("parameters");
6367
if (parameters == null) return query;
68+
@SuppressWarnings("unchecked")
6469
List<List<Object>> whereConditions = (List<List<Object>>) parameters.get("where");
6570
for (List<Object> condition : whereConditions) {
6671
String fieldName = (String) condition.get(0);
@@ -80,6 +85,7 @@ private Query getQuery(Map<String, Object> arguments) {
8085
// Invalid operator.
8186
}
8287
}
88+
@SuppressWarnings("unchecked")
8389
List<Object> orderBy = (List<Object>) parameters.get("orderBy");
8490
if (orderBy == null) return query;
8591
String orderByFieldName = (String) orderBy.get(0);
@@ -163,6 +169,23 @@ public void onEvent(QuerySnapshot querySnapshot, FirebaseFirestoreException e) {
163169
}
164170
}
165171

172+
private void addDefaultListeners(final String description, Task<Void> task, final Result result) {
173+
task.addOnSuccessListener(
174+
new OnSuccessListener<Void>() {
175+
@Override
176+
public void onSuccess(Void ignored) {
177+
result.success(null);
178+
}
179+
});
180+
task.addOnFailureListener(
181+
new OnFailureListener() {
182+
@Override
183+
public void onFailure(@NonNull Exception e) {
184+
result.error("Error performing " + description, e.getMessage(), null);
185+
}
186+
});
187+
}
188+
166189
@Override
167190
public void onMethodCall(MethodCall call, final Result result) {
168191
switch (call.method) {
@@ -201,16 +224,33 @@ public void onMethodCall(MethodCall call, final Result result) {
201224
{
202225
Map<String, Object> arguments = call.arguments();
203226
DocumentReference documentReference = getDocumentReference(arguments);
204-
documentReference.set(arguments.get("data"));
205-
result.success(null);
227+
@SuppressWarnings("unchecked")
228+
Map<String, Object> options = (Map<String, Object>) arguments.get("options");
229+
Task<Void> task;
230+
if (options != null && (Boolean) options.get("merge")) {
231+
task = documentReference.set(arguments.get("data"), SetOptions.merge());
232+
} else {
233+
task = documentReference.set(arguments.get("data"));
234+
}
235+
addDefaultListeners("setData", task, result);
236+
break;
237+
}
238+
case "DocumentReference#updateData":
239+
{
240+
Map<String, Object> arguments = call.arguments();
241+
DocumentReference documentReference = getDocumentReference(arguments);
242+
@SuppressWarnings("unchecked")
243+
Map<String, Object> data = (Map<String, Object>) arguments.get("data");
244+
Task<Void> task = documentReference.update(data);
245+
addDefaultListeners("updateData", task, result);
206246
break;
207247
}
208248
case "DocumentReference#delete":
209249
{
210250
Map<String, Object> arguments = call.arguments();
211251
DocumentReference documentReference = getDocumentReference(arguments);
212-
documentReference.delete();
213-
result.success(null);
252+
Task<Void> task = documentReference.delete();
253+
addDefaultListeners("delete", task, result);
214254
break;
215255
}
216256
default:

packages/cloud_firestore/ios/Classes/CloudFirestorePlugin.m

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,20 @@ - (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result
8787
};
8888
if ([@"DocumentReference#setData" isEqualToString:call.method]) {
8989
NSString *path = call.arguments[@"path"];
90+
NSDictionary *options = call.arguments[@"options"];
9091
FIRDocumentReference *reference = [[FIRFirestore firestore] documentWithPath:path];
91-
[reference setData:call.arguments[@"data"] completion:defaultCompletionBlock];
92+
if (![options isEqual:[NSNull null]] &&
93+
[options[@"merge"] isEqual:[NSNumber numberWithBool:YES]]) {
94+
[reference setData:call.arguments[@"data"]
95+
options:[FIRSetOptions merge]
96+
completion:defaultCompletionBlock];
97+
} else {
98+
[reference setData:call.arguments[@"data"] completion:defaultCompletionBlock];
99+
}
100+
} else if ([@"DocumentReference#updateData" isEqualToString:call.method]) {
101+
NSString *path = call.arguments[@"path"];
102+
FIRDocumentReference *reference = [[FIRFirestore firestore] documentWithPath:path];
103+
[reference updateData:call.arguments[@"data"] completion:defaultCompletionBlock];
92104
} else if ([@"DocumentReference#delete" isEqualToString:call.method]) {
93105
NSString *path = call.arguments[@"path"];
94106
FIRDocumentReference *reference = [[FIRFirestore firestore] documentWithPath:path];

packages/cloud_firestore/lib/cloud_firestore.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ part 'src/document_reference.dart';
1919
part 'src/firestore.dart';
2020
part 'src/query.dart';
2121
part 'src/query_snapshot.dart';
22+
part 'src/set_options.dart';

packages/cloud_firestore/lib/src/document_reference.dart

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,27 @@ class DocumentReference {
2222
/// Slash-delimited path representing the database location of this query.
2323
String get path => _pathComponents.join('/');
2424

25-
Future<Null> setData(Map<String, dynamic> data) {
25+
/// Writes to the document referred to by this [DocumentReference]. If the
26+
/// document does not yet exist, it will be created. If you pass [SetOptions],
27+
/// the provided data will be merged into an existing document.
28+
Future<Null> setData(Map<String, dynamic> data, [SetOptions options]) {
2629
return Firestore.channel.invokeMethod(
2730
'DocumentReference#setData',
31+
<String, dynamic>{'path': path, 'data': data, 'options': options?._data},
32+
);
33+
}
34+
35+
/// Updates fields in the document referred to by this [DocumentReference].
36+
///
37+
/// If no document exists yet, the update will fail.
38+
Future<Null> updateData(Map<String, dynamic> data) {
39+
return Firestore.channel.invokeMethod(
40+
'DocumentReference#updateData',
2841
<String, dynamic>{'path': path, 'data': data},
2942
);
3043
}
3144

45+
/// Deletes the document referred to by this [DocumentReference].
3246
Future<Null> delete() {
3347
return Firestore.channel.invokeMethod(
3448
'DocumentReference#delete',
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2017, the Chromium project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
part of cloud_firestore;
6+
7+
/// An options object that configures the behavior of [setData()] calls.
8+
///
9+
/// By providing the [SetOptions] objects returned by [merge], the [setData()]
10+
/// calls on [DocumentReference] can be configured to perform granular merges
11+
/// instead of overwriting the target documents in their entirety.
12+
class SetOptions {
13+
const SetOptions._(this._data);
14+
final Map<String, dynamic> _data;
15+
16+
/// Changes the behavior of set() calls to only replace the values specified
17+
/// in its data argument.
18+
static const SetOptions merge = const SetOptions._(
19+
const <String, dynamic>{'merge': true},
20+
);
21+
22+
// TODO(jackson): The Android Firestore SDK supports `mergeFieldPaths` and
23+
// `mergeFields`, but these options don't seem to be available yet on iOS.
24+
}

packages/cloud_firestore/pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ version: 0.1.1
77

88
flutter:
99
plugin:
10-
androidPackage: io.flutter.plugins.firebase.cloud_firestore
10+
androidPackage: io.flutter.plugins.firebase.cloudfirestore
1111
iosPrefix: FLT
1212
pluginClass: CloudFirestorePlugin
1313

packages/cloud_firestore/test/cloud_firestore_test.dart

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,43 @@ void main() {
219219
<Matcher>[
220220
isMethodCall(
221221
'DocumentReference#setData',
222+
arguments: <String, dynamic>{
223+
'path': 'foo/bar',
224+
'data': <String, String>{'bazKey': 'quxValue'},
225+
'options': null,
226+
},
227+
),
228+
],
229+
);
230+
});
231+
test('merge set', () async {
232+
await collectionReference
233+
.document('bar')
234+
.setData(<String, String>{'bazKey': 'quxValue'}, SetOptions.merge);
235+
expect(SetOptions.merge, isNotNull);
236+
expect(
237+
log,
238+
<Matcher>[
239+
isMethodCall(
240+
'DocumentReference#setData',
241+
arguments: <String, dynamic>{
242+
'path': 'foo/bar',
243+
'data': <String, String>{'bazKey': 'quxValue'},
244+
'options': <String, bool>{'merge': true},
245+
},
246+
),
247+
],
248+
);
249+
});
250+
test('update', () async {
251+
await collectionReference
252+
.document('bar')
253+
.updateData(<String, String>{'bazKey': 'quxValue'});
254+
expect(
255+
log,
256+
<Matcher>[
257+
isMethodCall(
258+
'DocumentReference#updateData',
222259
arguments: <String, dynamic>{
223260
'path': 'foo/bar',
224261
'data': <String, String>{'bazKey': 'quxValue'},

0 commit comments

Comments
 (0)