-
Notifications
You must be signed in to change notification settings - Fork 3.9k
/
firestore.dart
306 lines (270 loc) 路 11.4 KB
/
firestore.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
// Copyright 2020, the Chromium project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
part of cloud_firestore;
/// The entry point for accessing a [FirebaseFirestore].
///
/// You can get an instance by calling [FirebaseFirestore.instance]. The instance
/// can also be created with a secondary [Firebase] app by calling
/// [FirebaseFirestore.instanceFor], for example:
///
/// ```dart
/// FirebaseApp secondaryApp = Firebase.app('SecondaryApp');
///
/// FirebaseFirestore firestore = FirebaseFirestore.instanceFor(app: secondaryApp);
/// ```
class FirebaseFirestore extends FirebasePluginPlatform {
FirebaseFirestore._({required this.app})
: super(app.name, 'plugins.flutter.io/firebase_firestore');
static final Map<String, FirebaseFirestore> _cachedInstances = {};
/// Returns an instance using the default [FirebaseApp].
static FirebaseFirestore get instance {
return FirebaseFirestore.instanceFor(
app: Firebase.app(),
);
}
/// Returns an instance using a specified [FirebaseApp].
static FirebaseFirestore instanceFor({required FirebaseApp app}) {
if (_cachedInstances.containsKey(app.name)) {
return _cachedInstances[app.name]!;
}
FirebaseFirestore newInstance = FirebaseFirestore._(app: app);
FirebasePluginPlatform.verify(newInstance);
_cachedInstances[app.name] = newInstance;
return newInstance;
}
// Cached and lazily loaded instance of [FirestorePlatform] to avoid
// creating a [MethodChannelFirestore] when not needed or creating an
// instance with the default app before a user specifies an app.
FirebaseFirestorePlatform? _delegatePackingProperty;
FirebaseFirestorePlatform get _delegate {
return _delegatePackingProperty ??=
FirebaseFirestorePlatform.instanceFor(app: app);
}
/// The [FirebaseApp] for this current [FirebaseFirestore] instance.
FirebaseApp app;
/// Gets a [CollectionReference] for the specified Firestore path.
CollectionReference<Map<String, dynamic>> collection(String collectionPath) {
assert(
collectionPath.isNotEmpty,
'a collectionPath path must be a non-empty string',
);
assert(
!collectionPath.contains('//'),
'a collection path must not contain "//"',
);
assert(
isValidCollectionPath(collectionPath),
'a collection path must point to a valid collection.',
);
return _JsonCollectionReference(this, _delegate.collection(collectionPath));
}
/// Returns a [WriteBatch], used for performing multiple writes as a single
/// atomic operation.
///
/// Unlike [Transaction]s, [WriteBatch]es are persisted offline and therefore are
/// preferable when you don鈥檛 need to condition your writes on read data.
WriteBatch batch() {
return WriteBatch._(this, _delegate.batch());
}
/// Clears any persisted data for the current instance.
Future<void> clearPersistence() {
return _delegate.clearPersistence();
}
/// Enable persistence of Firestore data.
///
/// This is a web-only method. Use [Settings.persistenceEnabled] for non-web platforms.
Future<void> enablePersistence([
PersistenceSettings? persistenceSettings,
]) async {
return _delegate.enablePersistence(persistenceSettings);
}
LoadBundleTask loadBundle(Uint8List bundle) {
return LoadBundleTask._(_delegate.loadBundle(bundle));
}
/// Changes this instance to point to a FirebaseFirestore emulator running locally.
///
/// Set the [host] of the local emulator, such as "localhost"
/// Set the [port] of the local emulator, such as "8080" (port 8080 is default)
///
/// Note: Must be called immediately, prior to accessing FirebaseFirestore methods.
/// Do not use with production credentials as emulator traffic is not encrypted.
void useFirestoreEmulator(String host, int port, {bool sslEnabled = false}) {
if (kIsWeb) {
// use useEmulator() API for web as settings are set immediately unlike native platforms
try {
_delegate.useEmulator(host, port);
} catch (e) {
final String code = (e as dynamic).code;
// this catches FirebaseError from web that occurs after hot reloading & hot restarting
if (code != 'failed-precondition') {
rethrow;
}
}
} else {
String mappedHost = host;
// Android considers localhost as 10.0.2.2 - automatically handle this for users.
if (!kIsWeb && defaultTargetPlatform == TargetPlatform.android) {
if (mappedHost == 'localhost' || mappedHost == '127.0.0.1') {
// ignore: avoid_print
print('Mapping Firestore Emulator host "$mappedHost" to "10.0.2.2".');
mappedHost = '10.0.2.2';
}
}
_delegate.settings = _delegate.settings.copyWith(
// "sslEnabled" has to be set to false for android to work
sslEnabled: sslEnabled,
host: '$mappedHost:$port',
);
}
}
/// Reads a [QuerySnapshot] if a namedQuery has been retrieved and passed as a [Buffer] to [loadBundle()]. To read from cache, pass [GetOptions.source] value as [Source.cache].
/// To read from the Firestore backend, use [GetOptions.source] as [Source.server].
Future<QuerySnapshot<Map<String, dynamic>>> namedQueryGet(
String name, {
GetOptions options = const GetOptions(),
}) async {
QuerySnapshotPlatform snapshotDelegate =
await _delegate.namedQueryGet(name, options: options);
return _JsonQuerySnapshot(FirebaseFirestore.instance, snapshotDelegate);
}
/// Gets a [Query] for the specified collection group.
Query<Map<String, dynamic>> collectionGroup(String collectionPath) {
assert(
collectionPath.isNotEmpty,
'a collection path must be a non-empty string',
);
assert(
!collectionPath.contains('/'),
'a collection path passed to collectionGroup() cannot contain "/"',
);
return _JsonQuery(this, _delegate.collectionGroup(collectionPath));
}
/// Instructs [FirebaseFirestore] to disable the network for the instance.
///
/// Once disabled, any writes will only resolve once connection has been
/// restored. However, the local database will still be updated and any
/// listeners will still trigger.
Future<void> disableNetwork() {
return _delegate.disableNetwork();
}
/// Gets a [DocumentReference] for the specified Firestore path.
DocumentReference<Map<String, dynamic>> doc(String documentPath) {
assert(
documentPath.isNotEmpty,
'a document path must be a non-empty string',
);
assert(
!documentPath.contains('//'),
'a collection path must not contain "//"',
);
assert(
isValidDocumentPath(documentPath),
'a document path must point to a valid document.',
);
return _JsonDocumentReference(this, _delegate.doc(documentPath));
}
/// Enables the network for this instance. Any pending local-only writes
/// will be written to the remote servers.
Future<void> enableNetwork() {
return _delegate.enableNetwork();
}
/// Returns a [Stream] which is called each time all of the active listeners
/// have been synchronised.
Stream<void> snapshotsInSync() {
return _delegate.snapshotsInSync();
}
/// Executes the given [TransactionHandler] and then attempts to commit the
/// changes applied within an atomic transaction.
///
/// In the [TransactionHandler], a set of reads and writes can be performed
/// atomically using the [Transaction] object passed to the [TransactionHandler].
/// After the [TransactionHandler] is run, [FirebaseFirestore] will attempt to apply the
/// changes to the server. If any of the data read has been modified outside
/// of this [Transaction] since being read, then the transaction will be
/// retried by executing the provided [TransactionHandler] again. If the transaction still
/// fails after 5 retries, then the transaction will fail.s
///
/// The [TransactionHandler] may be executed multiple times, it should be able
/// to handle multiple executions.
///
/// Data accessed with the transaction will not reflect local changes that
/// have not been committed. For this reason, it is required that all
/// reads are performed before any writes. Transactions must be performed
/// while online. Otherwise, reads will fail, and the final commit will fail.
///
/// By default transactions are limited to 30 seconds of execution time. This
/// timeout can be adjusted by setting the timeout parameter.
///
/// By default transactions will retry 5 times. You can change the number of attemps
/// with [maxAttempts]. Attempts should be at least 1.
Future<T> runTransaction<T>(
TransactionHandler<T> transactionHandler, {
Duration timeout = const Duration(seconds: 30),
int maxAttempts = 5,
}) async {
late T output;
await _delegate.runTransaction(
(transaction) async {
output = await transactionHandler(Transaction._(this, transaction));
},
timeout: timeout,
maxAttempts: maxAttempts,
);
return output;
}
/// Specifies custom settings to be used to configure this [FirebaseFirestore] instance.
///
/// You must set these before invoking any other methods on this [FirebaseFirestore] instance.
set settings(Settings settings) {
_delegate.settings = _delegate.settings.copyWith(
sslEnabled: settings.sslEnabled,
persistenceEnabled: settings.persistenceEnabled,
host: settings.host,
cacheSizeBytes: settings.cacheSizeBytes,
);
}
/// The current [Settings] for this [FirebaseFirestore] instance.
Settings get settings {
return _delegate.settings;
}
/// Terminates this [FirebaseFirestore] instance.
///
/// After calling [terminate()] only the [clearPersistence()] method may be used.
/// Any other method will throw a [FirebaseException].
///
/// Termination does not cancel any pending writes, and any promises that are
/// awaiting a response from the server will not be resolved. If you have
/// persistence enabled, the next time you start this instance, it will resume
/// sending these writes to the server.
///
/// Note: Under normal circumstances, calling [terminate()] is not required.
/// This method is useful only when you want to force this instance to release
/// all of its resources or in combination with [clearPersistence()] to ensure
/// that all local state is destroyed between test runs.
Future<void> terminate() {
return _delegate.terminate();
}
/// Waits until all currently pending writes for the active user have been
/// acknowledged by the backend.
///
/// The returned Future resolves immediately if there are no outstanding writes.
/// Otherwise, the Promise waits for all previously issued writes (including
/// those written in a previous app session), but it does not wait for writes
/// that were added after the method is called. If you want to wait for
/// additional writes, call [waitForPendingWrites] again.
///
/// Any outstanding [waitForPendingWrites] calls are rejected during user changes.
Future<void> waitForPendingWrites() {
return _delegate.waitForPendingWrites();
}
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
bool operator ==(Object other) =>
other is FirebaseFirestore && other.app.name == app.name;
@override
// ignore: avoid_equals_and_hash_code_on_mutable_classes
int get hashCode => Object.hash(app.name, app.options);
@override
String toString() => '$FirebaseFirestore(app: ${app.name})';
}