@@ -206,6 +206,28 @@ class Firestore extends commonGrpc.Service {
206
206
207
207
super ( config , options ) ;
208
208
209
+ /**
210
+ * @private
211
+ * @type {object|null }
212
+ * @property {FirestoreClient } Firestore The Firestore GAPIC client.
213
+ */
214
+ this . _firestoreClient = null ;
215
+
216
+ /**
217
+ * The configuration options for the GAPIC client.
218
+ * @private
219
+ * @type {Object }
220
+ */
221
+ this . _initalizationOptions = options ;
222
+
223
+ /**
224
+ * A Promise that resolves when client initialization completes. Can be
225
+ * 'null' if initialization hasn't started yet.
226
+ * @private
227
+ * @type {Promise|null }
228
+ */
229
+ this . _clientInitialized = null ;
230
+
209
231
// GCF currently tears down idle connections after two minutes. Requests
210
232
// that are issued after this period may fail. On GCF, we therefore issue
211
233
// these requests as part of a transaction so that we can safely retry until
@@ -220,22 +242,13 @@ class Firestore extends commonGrpc.Service {
220
242
Firestore . log ( 'Firestore' , 'Detected GCF environment' ) ;
221
243
}
222
244
223
- /**
224
- * @private
225
- * @type {object }
226
- * @property {FirestoreClient } Firestore The Firestore GAPIC client.
227
- */
228
- this . api = {
229
- Firestore : v1beta1 ( options ) . firestoreClient ( options ) ,
230
- } ;
231
-
232
- this . _referencePath = new ResourcePath ( '{{projectId}}' , '(default)' ) ;
233
-
234
- if ( options ) {
235
- if ( options . projectId ) {
236
- validate . isString ( 'options.projectId' , options . projectId ) ;
237
- this . _referencePath = new ResourcePath ( options . projectId , '(default)' ) ;
238
- }
245
+ if ( options && options . projectId ) {
246
+ validate . isString ( 'options.projectId' , options . projectId ) ;
247
+ this . _referencePath = new ResourcePath ( options . projectId , '(default)' ) ;
248
+ } else {
249
+ // Initialize a temporary reference path that will be overwritten during
250
+ // project ID detection.
251
+ this . _referencePath = new ResourcePath ( '{{projectId}}' , '(default)' ) ;
239
252
}
240
253
241
254
Firestore . log ( 'Firestore' , 'Initialized Firestore' ) ;
@@ -589,11 +602,7 @@ class Firestore extends commonGrpc.Service {
589
602
let self = this ;
590
603
591
604
return self
592
- . readStream (
593
- this . api . Firestore . batchGetDocuments . bind ( this . api . Firestore ) ,
594
- request ,
595
- /* allowRetries= */ true
596
- )
605
+ . readStream ( 'batchGetDocuments' , request , /* allowRetries= */ true )
597
606
. then ( stream => {
598
607
return new Promise ( ( resolve , reject ) => {
599
608
stream
@@ -684,60 +693,70 @@ class Firestore extends commonGrpc.Service {
684
693
}
685
694
686
695
/**
687
- * Decorate all request options before being sent with to an API request. This
688
- * is used to replace any `{{projectId}}` placeholders with the value detected
689
- * from the user's environment, if one wasn't provided manually .
696
+ * Initializes the client and detects the Firestore Project ID. Returns a
697
+ * Promise on completion. If the client is already initialized, the returned
698
+ * Promise resolves immediately .
690
699
*
691
700
* @private
692
701
*/
693
- _decorateRequest ( request ) {
694
- let self = this ;
695
-
696
- function decorate ( ) {
697
- return new Promise ( resolve => {
698
- let decoratedRequest = extend ( true , { } , request ) ;
699
- decoratedRequest = common . util . replaceProjectIdToken (
700
- decoratedRequest ,
701
- self . _referencePath . projectId
702
- ) ;
703
-
704
- let decoratedGax = { otherArgs : { headers : { } } } ;
705
- decoratedGax . otherArgs . headers [ CLOUD_RESOURCE_HEADER ] =
706
- self . formattedName ;
707
-
708
- resolve ( { request : decoratedRequest , gax : decoratedGax } ) ;
702
+ _ensureClient ( ) {
703
+ if ( ! this . _clientInitialized ) {
704
+ this . _clientInitialized = new Promise ( ( resolve , reject ) => {
705
+ this . _firestoreClient = v1beta1 (
706
+ this . _initalizationOptions
707
+ ) . firestoreClient ( this . _initalizationOptions ) ;
708
+
709
+ Firestore . log ( 'Firestore' , 'Initialized Firestore GAPIC Client' ) ;
710
+
711
+ // We schedule Project ID detection using `setImmediate` to allow the
712
+ // testing framework to provide its own implementation of
713
+ // `getProjectId`.
714
+ setImmediate ( ( ) => {
715
+ this . _firestoreClient . getProjectId ( ( err , projectId ) => {
716
+ if ( err ) {
717
+ Firestore . log (
718
+ 'Firestore._ensureClient' ,
719
+ 'Failed to detect project ID: %s' ,
720
+ err
721
+ ) ;
722
+ reject ( err ) ;
723
+ } else {
724
+ Firestore . log (
725
+ 'Firestore._ensureClient' ,
726
+ 'Detected project ID: %s' ,
727
+ projectId
728
+ ) ;
729
+ this . _referencePath = new ResourcePath (
730
+ projectId ,
731
+ this . _referencePath . databaseId
732
+ ) ;
733
+ resolve ( ) ;
734
+ }
735
+ } ) ;
736
+ } ) ;
709
737
} ) ;
710
738
}
739
+ return this . _clientInitialized ;
740
+ }
711
741
712
- if ( this . _referencePath . projectId !== '{{projectId}}' ) {
713
- return decorate ( ) ;
714
- }
742
+ /**
743
+ * Decorate the request options of an API request. This is used to replace
744
+ * any `{{projectId}}` placeholders with the value detected from the user's
745
+ * environment, if one wasn't provided manually.
746
+ *
747
+ * @private
748
+ */
749
+ _decorateRequest ( request ) {
750
+ let decoratedRequest = extend ( true , { } , request ) ;
751
+ decoratedRequest = common . util . replaceProjectIdToken (
752
+ decoratedRequest ,
753
+ this . _referencePath . projectId
754
+ ) ;
715
755
716
- return new Promise ( ( resolve , reject ) => {
717
- this . api . Firestore . getProjectId ( ( err , projectId ) => {
718
- if ( err ) {
719
- Firestore . log (
720
- 'Firestore._decorateRequest' ,
721
- 'Failed to detect project ID: %s' ,
722
- err
723
- ) ;
724
- reject ( err ) ;
725
- } else {
726
- Firestore . log (
727
- 'Firestore._decorateRequest' ,
728
- 'Detected project ID: %s' ,
729
- projectId
730
- ) ;
731
- self . _referencePath = new ResourcePath (
732
- projectId ,
733
- self . _referencePath . databaseId
734
- ) ;
735
- decorate ( )
736
- . then ( resolve )
737
- . catch ( reject ) ;
738
- }
739
- } ) ;
740
- } ) ;
756
+ let decoratedGax = { otherArgs : { headers : { } } } ;
757
+ decoratedGax . otherArgs . headers [ CLOUD_RESOURCE_HEADER ] = this . formattedName ;
758
+
759
+ return { request : decoratedRequest , gax : decoratedGax } ;
741
760
}
742
761
743
762
/**
@@ -922,37 +941,42 @@ class Firestore extends commonGrpc.Service {
922
941
* necessary within the request options.
923
942
*
924
943
* @private
925
- * @param {function } method - Veneer API endpoint that takes a request and
926
- * GAX options.
944
+ * @param {function } methodName - Name of the veneer API endpoint that takes a
945
+ * request and GAX options.
927
946
* @param {Object } request - The Protobuf request to send.
928
947
* @param {boolean } allowRetries - Whether this is an idempotent request that
929
948
* can be retried.
930
949
* @returns {Promise.<Object> } A Promise with the request result.
931
950
*/
932
- request ( method , request , allowRetries ) {
951
+ request ( methodName , request , allowRetries ) {
933
952
let attempts = allowRetries ? MAX_REQUEST_RETRIES : 1 ;
934
953
935
- return this . _decorateRequest ( request ) . then ( decorated => {
954
+ return this . _ensureClient ( ) . then ( ( ) => {
955
+ const decorated = this . _decorateRequest ( request ) ;
936
956
return this . _retry ( attempts , ( ) => {
937
957
return new Promise ( ( resolve , reject ) => {
938
958
Firestore . log (
939
959
'Firestore.request' ,
940
960
'Sending request: %j' ,
941
961
decorated . request
942
962
) ;
943
- method ( decorated . request , decorated . gax , ( err , result ) => {
944
- if ( err ) {
945
- Firestore . log ( 'Firestore.request' , 'Received error:' , err ) ;
946
- reject ( err ) ;
947
- } else {
948
- Firestore . log (
949
- 'Firestore.request' ,
950
- 'Received response: %j' ,
951
- result
952
- ) ;
953
- resolve ( result ) ;
963
+ this . _firestoreClient [ methodName ] (
964
+ decorated . request ,
965
+ decorated . gax ,
966
+ ( err , result ) => {
967
+ if ( err ) {
968
+ Firestore . log ( 'Firestore.request' , 'Received error:' , err ) ;
969
+ reject ( err ) ;
970
+ } else {
971
+ Firestore . log (
972
+ 'Firestore.request' ,
973
+ 'Received response: %j' ,
974
+ result
975
+ ) ;
976
+ resolve ( result ) ;
977
+ }
954
978
}
955
- } ) ;
979
+ ) ;
956
980
} ) ;
957
981
} ) ;
958
982
} ) ;
@@ -966,17 +990,18 @@ class Firestore extends commonGrpc.Service {
966
990
* listeners are attached.
967
991
*
968
992
* @private
969
- * @param {function } method - Streaming Veneer API endpoint that takes a
970
- * request and GAX options.
993
+ * @param {string } methodName - Name of the streaming Veneer API endpoint that
994
+ * takes a request and GAX options.
971
995
* @param {Object } request - The Protobuf request to send.
972
996
* @param {boolean } allowRetries - Whether this is an idempotent request that
973
997
* can be retried.
974
998
* @returns {Promise.<Stream> } A Promise with the resulting read-only stream.
975
999
*/
976
- readStream ( method , request , allowRetries ) {
1000
+ readStream ( methodName , request , allowRetries ) {
977
1001
let attempts = allowRetries ? MAX_REQUEST_RETRIES : 1 ;
978
1002
979
- return this . _decorateRequest ( request ) . then ( decorated => {
1003
+ return this . _ensureClient ( ) . then ( ( ) => {
1004
+ const decorated = this . _decorateRequest ( request ) ;
980
1005
return this . _retry ( attempts , ( ) => {
981
1006
return new Promise ( ( resolve , reject ) => {
982
1007
try {
@@ -985,7 +1010,10 @@ class Firestore extends commonGrpc.Service {
985
1010
'Sending request: %j' ,
986
1011
decorated . request
987
1012
) ;
988
- let stream = method ( decorated . request , decorated . gax ) ;
1013
+ let stream = this . _firestoreClient [ methodName ] (
1014
+ decorated . request ,
1015
+ decorated . gax
1016
+ ) ;
989
1017
let logger = through . obj ( function ( chunk , enc , callback ) {
990
1018
Firestore . log (
991
1019
'Firestore.readStream' ,
@@ -1013,25 +1041,29 @@ class Firestore extends commonGrpc.Service {
1013
1041
* listeners are attached.
1014
1042
*
1015
1043
* @private
1016
- * @param {function } method - Streaming Veneer API endpoint that takes GAX
1017
- * options.
1044
+ * @param {string } methodName - Name of the streaming Veneer API endpoint that
1045
+ * takes GAX options.
1018
1046
* @param {Object } request - The Protobuf request to send as the first stream
1019
1047
* message.
1020
1048
* @param {boolean } allowRetries - Whether this is an idempotent request that
1021
1049
* can be retried.
1022
1050
* @returns {Promise.<Stream> } A Promise with the resulting read/write stream.
1023
1051
*/
1024
- readWriteStream ( method , request , allowRetries ) {
1052
+ readWriteStream ( methodName , request , allowRetries ) {
1025
1053
let self = this ;
1026
1054
let attempts = allowRetries ? MAX_REQUEST_RETRIES : 1 ;
1027
1055
1028
- return this . _decorateRequest ( { } ) . then ( decorated => {
1056
+ return this . _ensureClient ( ) . then ( ( ) => {
1057
+ const decorated = this . _decorateRequest ( request ) ;
1029
1058
return this . _retry ( attempts , ( ) => {
1030
1059
return Promise . resolve ( ) . then ( ( ) => {
1031
1060
Firestore . log ( 'Firestore.readWriteStream' , 'Opening stream' ) ;
1032
1061
// The generated bi-directional streaming API takes the list of GAX
1033
1062
// headers as its second argument.
1034
- let requestStream = method ( { } , decorated . gax ) ;
1063
+ let requestStream = this . _firestoreClient [ methodName ] (
1064
+ { } ,
1065
+ decorated . gax
1066
+ ) ;
1035
1067
1036
1068
// The transform stream to assign the project ID.
1037
1069
let transform = through . obj ( function ( chunk , encoding , callback ) {
0 commit comments