Skip to content

Commit 073bb7f

Browse files
feat: deferred client initialization (#548)
This PR includes changes from googleapis/gapic-generator-typescript#317 that will move the asynchronous initialization and authentication from the client constructor to an `initialize()` method. This method will be automatically called when the first RPC call is performed. The client library usage has not changed, there is no need to update any code. If you want to make sure the client is authenticated _before_ the first RPC call, you can do ```js await client.initialize(); ``` manually before calling any client method.
1 parent 0578272 commit 073bb7f

File tree

6 files changed

+179
-54
lines changed

6 files changed

+179
-54
lines changed

packages/google-cloud-node/src/v1/speech_client.ts

+57-25
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,14 @@ export class SpeechClient {
4141
private _descriptors: Descriptors = {page: {}, stream: {}, longrunning: {}};
4242
private _innerApiCalls: {[name: string]: Function};
4343
private _terminated = false;
44+
private _opts: ClientOptions;
45+
private _gaxModule: typeof gax | typeof gax.fallback;
46+
private _gaxGrpc: gax.GrpcClient | gax.fallback.GrpcClient;
47+
private _protos: {};
48+
private _defaults: {[method: string]: gax.CallSettings};
4449
auth: gax.GoogleAuth;
4550
operationsClient: gax.OperationsClient;
46-
speechStub: Promise<{[name: string]: Function}>;
51+
speechStub?: Promise<{[name: string]: Function}>;
4752

4853
/**
4954
* Construct an instance of SpeechClient.
@@ -67,8 +72,6 @@ export class SpeechClient {
6772
* app is running in an environment which supports
6873
* {@link https://developers.google.com/identity/protocols/application-default-credentials Application Default Credentials},
6974
* your project ID will be detected automatically.
70-
* @param {function} [options.promise] - Custom promise module to use instead
71-
* of native Promises.
7275
* @param {string} [options.apiEndpoint] - The domain name of the
7376
* API remote host.
7477
*/
@@ -98,25 +101,28 @@ export class SpeechClient {
98101
// If we are in browser, we are already using fallback because of the
99102
// "browser" field in package.json.
100103
// But if we were explicitly requested to use fallback, let's do it now.
101-
const gaxModule = !isBrowser && opts.fallback ? gax.fallback : gax;
104+
this._gaxModule = !isBrowser && opts.fallback ? gax.fallback : gax;
102105

103106
// Create a `gaxGrpc` object, with any grpc-specific options
104107
// sent to the client.
105108
opts.scopes = (this.constructor as typeof SpeechClient).scopes;
106-
const gaxGrpc = new gaxModule.GrpcClient(opts);
109+
this._gaxGrpc = new this._gaxModule.GrpcClient(opts);
110+
111+
// Save options to use in initialize() method.
112+
this._opts = opts;
107113

108114
// Save the auth object to the client, for use by other methods.
109-
this.auth = gaxGrpc.auth as gax.GoogleAuth;
115+
this.auth = this._gaxGrpc.auth as gax.GoogleAuth;
110116

111117
// Determine the client header string.
112-
const clientHeader = [`gax/${gaxModule.version}`, `gapic/${version}`];
118+
const clientHeader = [`gax/${this._gaxModule.version}`, `gapic/${version}`];
113119
if (typeof process !== 'undefined' && 'versions' in process) {
114120
clientHeader.push(`gl-node/${process.versions.node}`);
115121
} else {
116-
clientHeader.push(`gl-web/${gaxModule.version}`);
122+
clientHeader.push(`gl-web/${this._gaxModule.version}`);
117123
}
118124
if (!opts.fallback) {
119-
clientHeader.push(`grpc/${gaxGrpc.grpcVersion}`);
125+
clientHeader.push(`grpc/${this._gaxGrpc.grpcVersion}`);
120126
}
121127
if (opts.libName && opts.libVersion) {
122128
clientHeader.push(`${opts.libName}/${opts.libVersion}`);
@@ -132,14 +138,14 @@ export class SpeechClient {
132138
'protos',
133139
'protos.json'
134140
);
135-
const protos = gaxGrpc.loadProto(
141+
this._protos = this._gaxGrpc.loadProto(
136142
opts.fallback ? require('../../protos/protos.json') : nodejsProtoPath
137143
);
138144

139145
// Some of the methods on this service provide streaming responses.
140146
// Provide descriptors for these.
141147
this._descriptors.stream = {
142-
streamingRecognize: new gaxModule.StreamDescriptor(
148+
streamingRecognize: new this._gaxModule.StreamDescriptor(
143149
gax.StreamType.BIDI_STREAMING
144150
),
145151
};
@@ -148,13 +154,15 @@ export class SpeechClient {
148154
// an Operation object that allows for tracking of the operation,
149155
// rather than holding a request open.
150156
const protoFilesRoot = opts.fallback
151-
? gaxModule.protobuf.Root.fromJSON(require('../../protos/protos.json'))
152-
: gaxModule.protobuf.loadSync(nodejsProtoPath);
157+
? this._gaxModule.protobuf.Root.fromJSON(
158+
require('../../protos/protos.json')
159+
)
160+
: this._gaxModule.protobuf.loadSync(nodejsProtoPath);
153161

154-
this.operationsClient = gaxModule
162+
this.operationsClient = this._gaxModule
155163
.lro({
156164
auth: this.auth,
157-
grpc: 'grpc' in gaxGrpc ? gaxGrpc.grpc : undefined,
165+
grpc: 'grpc' in this._gaxGrpc ? this._gaxGrpc.grpc : undefined,
158166
})
159167
.operationsClient(opts);
160168
const longRunningRecognizeResponse = protoFilesRoot.lookup(
@@ -165,15 +173,15 @@ export class SpeechClient {
165173
) as gax.protobuf.Type;
166174

167175
this._descriptors.longrunning = {
168-
longRunningRecognize: new gaxModule.LongrunningDescriptor(
176+
longRunningRecognize: new this._gaxModule.LongrunningDescriptor(
169177
this.operationsClient,
170178
longRunningRecognizeResponse.decode.bind(longRunningRecognizeResponse),
171179
longRunningRecognizeMetadata.decode.bind(longRunningRecognizeMetadata)
172180
),
173181
};
174182

175183
// Put together the default options sent with requests.
176-
const defaults = gaxGrpc.constructSettings(
184+
this._defaults = this._gaxGrpc.constructSettings(
177185
'google.cloud.speech.v1.Speech',
178186
gapicConfig as gax.ClientConfig,
179187
opts.clientConfig || {},
@@ -184,17 +192,35 @@ export class SpeechClient {
184192
// of calling the API is handled in `google-gax`, with this code
185193
// merely providing the destination and request information.
186194
this._innerApiCalls = {};
195+
}
196+
197+
/**
198+
* Initialize the client.
199+
* Performs asynchronous operations (such as authentication) and prepares the client.
200+
* This function will be called automatically when any class method is called for the
201+
* first time, but if you need to initialize it before calling an actual method,
202+
* feel free to call initialize() directly.
203+
*
204+
* You can await on this method if you want to make sure the client is initialized.
205+
*
206+
* @returns {Promise} A promise that resolves to an authenticated service stub.
207+
*/
208+
initialize() {
209+
// If the client stub promise is already initialized, return immediately.
210+
if (this.speechStub) {
211+
return this.speechStub;
212+
}
187213

188214
// Put together the "service stub" for
189215
// google.cloud.speech.v1.Speech.
190-
this.speechStub = gaxGrpc.createStub(
191-
opts.fallback
192-
? (protos as protobuf.Root).lookupService(
216+
this.speechStub = this._gaxGrpc.createStub(
217+
this._opts.fallback
218+
? (this._protos as protobuf.Root).lookupService(
193219
'google.cloud.speech.v1.Speech'
194220
)
195221
: // tslint:disable-next-line no-any
196-
(protos as any).google.cloud.speech.v1.Speech,
197-
opts
222+
(this._protos as any).google.cloud.speech.v1.Speech,
223+
this._opts
198224
) as Promise<{[method: string]: Function}>;
199225

200226
// Iterate over each of the methods that the service provides
@@ -218,9 +244,9 @@ export class SpeechClient {
218244
}
219245
);
220246

221-
const apiCall = gaxModule.createApiCall(
247+
const apiCall = this._gaxModule.createApiCall(
222248
innerCallPromise,
223-
defaults[methodName],
249+
this._defaults[methodName],
224250
this._descriptors.page[methodName] ||
225251
this._descriptors.stream[methodName] ||
226252
this._descriptors.longrunning[methodName]
@@ -234,6 +260,8 @@ export class SpeechClient {
234260
return apiCall(argument, callOptions, callback);
235261
};
236262
}
263+
264+
return this.speechStub;
237265
}
238266

239267
/**
@@ -352,6 +380,7 @@ export class SpeechClient {
352380
options = optionsOrCallback as gax.CallOptions;
353381
}
354382
options = options || {};
383+
this.initialize();
355384
return this._innerApiCalls.recognize(request, options, callback);
356385
}
357386

@@ -367,6 +396,7 @@ export class SpeechClient {
367396
* will emit objects representing [StreamingRecognizeResponse]{@link google.cloud.speech.v1.StreamingRecognizeResponse} on 'data' event asynchronously.
368397
*/
369398
_streamingRecognize(options?: gax.CallOptions): gax.CancellableStream {
399+
this.initialize();
370400
return this._innerApiCalls.streamingRecognize(options);
371401
}
372402

@@ -455,6 +485,7 @@ export class SpeechClient {
455485
options = optionsOrCallback as gax.CallOptions;
456486
}
457487
options = options || {};
488+
this.initialize();
458489
return this._innerApiCalls.longRunningRecognize(request, options, callback);
459490
}
460491

@@ -464,8 +495,9 @@ export class SpeechClient {
464495
* The client will no longer be usable and all future behavior is undefined.
465496
*/
466497
close(): Promise<void> {
498+
this.initialize();
467499
if (!this._terminated) {
468-
return this.speechStub.then(stub => {
500+
return this.speechStub!.then(stub => {
469501
this._terminated = true;
470502
stub.close();
471503
});

0 commit comments

Comments
 (0)