@@ -220,15 +220,15 @@ const GRPC_UNAVAILABLE = 14;
220
220
*/
221
221
class Firestore {
222
222
/**
223
- * @param {Object= } options - [Configuration object](#/docs).
224
- * @param {string= } options .projectId The Firestore Project ID. Can be
223
+ * @param {Object= } settings - [Configuration object](#/docs).
224
+ * @param {string= } settings .projectId The Firestore Project ID. Can be
225
225
* omitted in environments that support `Application Default Credentials`
226
226
* {@see https://cloud.google.com/docs/authentication}
227
- * @param {string= } options .keyFilename Local file containing the Service
227
+ * @param {string= } settings .keyFilename Local file containing the Service
228
228
* Account credentials. Can be omitted in environments that support
229
229
* `Application Default Credentials`
230
230
* {@see https://cloud.google.com/docs/authentication}
231
- * @param {boolean= } options .timestampsInSnapshots Enables the use of
231
+ * @param {boolean= } settings .timestampsInSnapshots Enables the use of
232
232
* `Timestamp`s for timestamp fields in `DocumentSnapshots`.<br/>
233
233
* Currently, Firestore returns timestamp fields as `Date` but `Date` only
234
234
* supports millisecond precision, which leads to truncation and causes
@@ -242,8 +242,8 @@ class Firestore {
242
242
* default and this option will be removed so you should change your code to
243
243
* use `Timestamp` now and opt-in to this new behavior as soon as you can.
244
244
*/
245
- constructor ( options ) {
246
- options = extend ( { } , options , {
245
+ constructor ( settings ) {
246
+ settings = extend ( { } , settings , {
247
247
libName : 'gccl' ,
248
248
libVersion : libVersion ,
249
249
} ) ;
@@ -257,11 +257,12 @@ class Firestore {
257
257
this . _clientPool = null ;
258
258
259
259
/**
260
- * The configuration options for the GAPIC client.
260
+ * Whether the initialization settings can still be changed by invoking
261
+ * `settings()`.
261
262
* @private
262
- * @type {Object }
263
+ * @type {boolean }
263
264
*/
264
- this . _initalizationOptions = options ;
265
+ this . _settingsFrozen = false ;
265
266
266
267
/**
267
268
* A Promise that resolves when client initialization completes. Can be
@@ -271,6 +272,15 @@ class Firestore {
271
272
*/
272
273
this . _clientInitialized = null ;
273
274
275
+ /**
276
+ * The configuration options for the GAPIC client.
277
+ * @private
278
+ * @type {Object }
279
+ */
280
+ this . _initalizationSettings = null ;
281
+
282
+ this . validateAndApplySettings ( settings ) ;
283
+
274
284
// GCF currently tears down idle connections after two minutes. Requests
275
285
// that are issued after this period may fail. On GCF, we therefore issue
276
286
// these requests as part of a transaction so that we can safely retry until
@@ -285,43 +295,59 @@ class Firestore {
285
295
Firestore . log ( 'Firestore' , null , 'Detected GCF environment' ) ;
286
296
}
287
297
288
- this . _timestampsInSnapshotsEnabled = ! ! options . timestampsInSnapshots ;
298
+ Firestore . log ( 'Firestore' , null , 'Initialized Firestore' ) ;
299
+ }
289
300
290
- if ( ! this . _timestampsInSnapshotsEnabled ) {
291
- console . error ( `
292
- The behavior for Date objects stored in Firestore is going to change
293
- AND YOUR APP MAY BREAK.
294
- To hide this warning and ensure your app does not break, you need to add the
295
- following code to your app before calling any other Cloud Firestore methods:
301
+ /**
302
+ * Specifies custom settings to be used to configure the `Firestore`
303
+ * instance. Can only be invoked once and before any other Firestore method.
304
+ *
305
+ * If settings are provided via both `settings()` and the `Firestore`
306
+ * constructor, both settings objects are merged and any settings provided via
307
+ * `settings()` take precedence.
308
+ *
309
+ * @param {object } settings The settings to use for all Firestore operations.
310
+ */
311
+ settings ( settings ) {
312
+ validate . isObject ( 'settings' , settings ) ;
313
+ validate . isOptionalString ( 'settings.projectId' , settings . projectId ) ;
314
+ validate . isOptionalBoolean (
315
+ 'settings.timestampsInSnapshots' , settings . timestampsInSnapshots ) ;
296
316
297
- const settings = {/* your settings... */ timestampsInSnapshots: true};
298
- const firestore = new Firestore(settings);
317
+ if ( this . _clientInitialized ) {
318
+ throw new Error (
319
+ 'Firestore has already been started and its settings can no longer ' +
320
+ 'be changed. You can only call settings() before calling any other ' +
321
+ 'methods on a Firestore object.' ) ;
322
+ }
299
323
300
- With this change, timestamps stored in Cloud Firestore will be read back as
301
- Firebase Timestamp objects instead of as system Date objects. So you will also
302
- need to update code expecting a Date to instead expect a Timestamp. For example:
324
+ if ( this . _settingsFrozen ) {
325
+ throw new Error (
326
+ 'Firestore.settings() has already be called. You can only call ' +
327
+ 'settings() once, and only before calling any other methods on a ' +
328
+ 'Firestore object.' ) ;
329
+ }
303
330
304
- // Old:
305
- const date = snapshot.get('created_at');
306
- // New:
307
- const timestamp = snapshot.get('created_at');
308
- const date = timestamp.toDate();
331
+ const mergedSettings = extend ( { } , this . _initalizationSettings , settings ) ;
332
+ this . validateAndApplySettings ( mergedSettings ) ;
333
+ this . _settingsFrozen = true ;
334
+ }
309
335
310
- Please audit all existing usages of Date when you enable the new behavior. In a
311
- future release, the behavior will change to the new behavior, so if you do not
312
- follow these steps, YOUR APP MAY BREAK.` ) ;
313
- }
336
+ validateAndApplySettings ( settings ) {
337
+ validate . isOptionalBoolean (
338
+ 'settings.timestampsInSnapshots' , settings . timestampsInSnapshots ) ;
339
+ this . _timestampsInSnapshotsEnabled = ! ! settings . timestampsInSnapshots ;
314
340
315
- if ( options && options . projectId ) {
316
- validate . isString ( 'options .projectId' , options . projectId ) ;
317
- this . _referencePath = new ResourcePath ( options . projectId , '(default)' ) ;
341
+ if ( settings && settings . projectId ) {
342
+ validate . isString ( 'settings .projectId' , settings . projectId ) ;
343
+ this . _referencePath = new ResourcePath ( settings . projectId , '(default)' ) ;
318
344
} else {
319
345
// Initialize a temporary reference path that will be overwritten during
320
346
// project ID detection.
321
347
this . _referencePath = new ResourcePath ( '{{projectId}}' , '(default)' ) ;
322
348
}
323
349
324
- Firestore . log ( 'Firestore' , null , 'Initialized Firestore' ) ;
350
+ this . _initalizationSettings = settings ;
325
351
}
326
352
327
353
/**
@@ -429,6 +455,12 @@ follow these steps, YOUR APP MAY BREAK.`);
429
455
* for existing documents, otherwise a DocumentSnapshot.
430
456
*/
431
457
snapshot_ ( documentOrName , readTime , encoding ) {
458
+ if ( ! this . _initalizationSettings . projectId ) {
459
+ throw new Error (
460
+ 'Cannot use `snapshot_()` without a Project ID. Please provide a ' +
461
+ 'Project ID via `Firestore.settings()`.' ) ;
462
+ }
463
+
432
464
let convertTimestamp ;
433
465
let convertDocument ;
434
466
@@ -750,9 +782,36 @@ follow these steps, YOUR APP MAY BREAK.`);
750
782
// Initialize the client pool if this is the first request.
751
783
if ( ! this . _clientInitialized ) {
752
784
common = require ( '@google-cloud/common' ) ;
785
+
786
+ if ( ! this . _timestampsInSnapshotsEnabled ) {
787
+ console . error ( `
788
+ The behavior for Date objects stored in Firestore is going to change
789
+ AND YOUR APP MAY BREAK.
790
+ To hide this warning and ensure your app does not break, you need to add the
791
+ following code to your app before calling any other Cloud Firestore methods:
792
+
793
+ const firestore = new Firestore();
794
+ const settings = {/* your settings... */ timestampsInSnapshots: true};
795
+ firestore.settings(settings);
796
+
797
+ With this change, timestamps stored in Cloud Firestore will be read back as
798
+ Firebase Timestamp objects instead of as system Date objects. So you will also
799
+ need to update code expecting a Date to instead expect a Timestamp. For example:
800
+
801
+ // Old:
802
+ const date = snapshot.get('created_at');
803
+ // New:
804
+ const timestamp = snapshot.get('created_at');
805
+ const date = timestamp.toDate();
806
+
807
+ Please audit all existing usages of Date when you enable the new behavior. In a
808
+ future release, the behavior will change to the new behavior, so if you do not
809
+ follow these steps, YOUR APP MAY BREAK.` ) ;
810
+ }
811
+
753
812
this . _clientInitialized = this . _initClientPool ( ) . then ( clientPool => {
754
813
this . _clientPool = clientPool ;
755
- } )
814
+ } ) ;
756
815
}
757
816
758
817
return this . _clientInitialized . then ( ( ) => this . _clientPool . run ( op ) ) ;
0 commit comments