diff --git a/templates/typescript_gapic/package.json.njk b/templates/typescript_gapic/package.json.njk index bb6f6480e..7d952b8b7 100644 --- a/templates/typescript_gapic/package.json.njk +++ b/templates/typescript_gapic/package.json.njk @@ -27,7 +27,7 @@ limitations under the License. "build/protos" ], "dependencies": { - "google-gax": "^1.9.0" + "google-gax": "^1.10.0" }, "devDependencies": { "@types/mocha": "^5.2.5", diff --git a/templates/typescript_gapic/src/$version/$service_client.ts.njk b/templates/typescript_gapic/src/$version/$service_client.ts.njk index 9baac44a6..3c0970883 100644 --- a/templates/typescript_gapic/src/$version/$service_client.ts.njk +++ b/templates/typescript_gapic/src/$version/$service_client.ts.njk @@ -21,9 +21,9 @@ limitations under the License. import * as gax from 'google-gax'; {% if service.longRunning.length > 0 -%} -import {Callback, LROperation, Descriptors, ClientOptions} from 'google-gax'; +import {APICallback, Callback, CallOptions, LROperation, Descriptors, ClientOptions} from 'google-gax'; {%- else -%} -import {Callback, Descriptors, ClientOptions} from 'google-gax'; +import {APICallback, Callback, CallOptions, Descriptors, ClientOptions} from 'google-gax'; {%- endif %} import * as path from 'path'; {% if (service.paging.length > 0) %} @@ -56,10 +56,12 @@ export interface PaginationResponse< */ export class {{ service.name }}Client { private _descriptors: Descriptors = {page: {}, stream: {}, longrunning: {}}; + private _{{ service.name.toCamelCase() }}Stub: Promise<{[name: string]: Function}>; private _innerApiCalls: {[name: string]: Function}; {%- if (service.pathTemplates.length > 0) %} private _pathTemplates: {[name: string]: gax.PathTemplate}; {%- endif %} + private _terminated = false; auth: gax.GoogleAuth; /** @@ -235,7 +237,7 @@ export class {{ service.name }}Client { // Put together the "service stub" for // {{api.naming.protoPackage}}.{{ service.name }}. - const {{ service.name.toCamelCase() }}Stub = gaxGrpc.createStub( + this._{{ service.name.toCamelCase() }}Stub = gaxGrpc.createStub( opts.fallback ? (protos as protobuf.Root).lookupService('{{api.naming.protoPackage}}.{{ service.name }}') : // tslint:disable-next-line no-any @@ -254,21 +256,32 @@ export class {{ service.name }}Client { ]; for (const methodName of {{ service.name.toCamelCase() }}StubMethods) { - const innerCallPromise = {{ service.name.toCamelCase() }}Stub.then( - (stub: {[method: string]: Function}) => (...args: Array<{}>) => { + const innerCallPromise = this._{{ service.name.toCamelCase() }}Stub.then( + stub => (...args: Array<{}>) => { return stub[methodName].apply(stub, args); }, (err: Error|null|undefined) => () => { throw err; }); - this._innerApiCalls[methodName] = gaxModule.createApiCall( + const apiCall = gaxModule.createApiCall( innerCallPromise, defaults[methodName], this._descriptors.page[methodName] || - this._descriptors.stream[methodName] || + this._descriptors.stream[methodName] || this._descriptors.longrunning[methodName] ); + + this._innerApiCalls[methodName] = ( + argument: {}, + callOptions?: CallOptions, + callback?: APICallback + ) => { + if (this._terminated) { + return Promise.reject('The client has already been closed.'); + } + return apiCall(argument, callOptions, callback); + }; } } @@ -589,4 +602,19 @@ export class {{ service.name }}Client { {%- endfor %} {%- endfor %} {%- endif %} + + /** + * Terminate the GRPC channel and close the client. + * + * The client will no longer be usable and all future behavior is undefined. + */ + close(): Promise { + if (!this._terminated) { + return this._{{ service.name.toCamelCase() }}Stub.then(stub => { + this._terminated = true; + stub.close(); + }); + } + return Promise.resolve(); + } } diff --git a/typescript/test/testdata/keymanager/package.json.baseline b/typescript/test/testdata/keymanager/package.json.baseline index 6efdd7e3f..b3f5d46c7 100644 --- a/typescript/test/testdata/keymanager/package.json.baseline +++ b/typescript/test/testdata/keymanager/package.json.baseline @@ -10,7 +10,7 @@ "build/protos" ], "dependencies": { - "google-gax": "^1.9.0" + "google-gax": "^1.10.0" }, "devDependencies": { "@types/mocha": "^5.2.5", diff --git a/typescript/test/testdata/keymanager/src/v1/key_management_service_client.ts.baseline b/typescript/test/testdata/keymanager/src/v1/key_management_service_client.ts.baseline index 539313daa..a46ba0816 100644 --- a/typescript/test/testdata/keymanager/src/v1/key_management_service_client.ts.baseline +++ b/typescript/test/testdata/keymanager/src/v1/key_management_service_client.ts.baseline @@ -17,7 +17,7 @@ // ** All changes to this file may be overwritten. ** import * as gax from 'google-gax'; -import {Callback, Descriptors, ClientOptions} from 'google-gax'; +import {APICallback, Callback, CallOptions, Descriptors, ClientOptions} from 'google-gax'; import * as path from 'path'; import { Transform } from 'stream'; @@ -58,7 +58,9 @@ export interface PaginationResponse< */ export class KeyManagementServiceClient { private _descriptors: Descriptors = {page: {}, stream: {}, longrunning: {}}; + private _keyManagementServiceStub: Promise<{[name: string]: Function}>; private _innerApiCalls: {[name: string]: Function}; + private _terminated = false; auth: gax.GoogleAuth; /** @@ -175,7 +177,7 @@ export class KeyManagementServiceClient { // Put together the "service stub" for // google.cloud.kms.v1.KeyManagementService. - const keyManagementServiceStub = gaxGrpc.createStub( + this._keyManagementServiceStub = gaxGrpc.createStub( opts.fallback ? (protos as protobuf.Root).lookupService('google.cloud.kms.v1.KeyManagementService') : // tslint:disable-next-line no-any @@ -188,21 +190,32 @@ export class KeyManagementServiceClient { ['listKeyRings', 'listCryptoKeys', 'listCryptoKeyVersions', 'listImportJobs', 'getKeyRing', 'getCryptoKey', 'getCryptoKeyVersion', 'getPublicKey', 'getImportJob', 'createKeyRing', 'createCryptoKey', 'createCryptoKeyVersion', 'importCryptoKeyVersion', 'createImportJob', 'updateCryptoKey', 'updateCryptoKeyVersion', 'encrypt', 'decrypt', 'asymmetricSign', 'asymmetricDecrypt', 'updateCryptoKeyPrimaryVersion', 'destroyCryptoKeyVersion', 'restoreCryptoKeyVersion']; for (const methodName of keyManagementServiceStubMethods) { - const innerCallPromise = keyManagementServiceStub.then( - (stub: {[method: string]: Function}) => (...args: Array<{}>) => { + const innerCallPromise = this._keyManagementServiceStub.then( + stub => (...args: Array<{}>) => { return stub[methodName].apply(stub, args); }, (err: Error|null|undefined) => () => { throw err; }); - this._innerApiCalls[methodName] = gaxModule.createApiCall( + const apiCall = gaxModule.createApiCall( innerCallPromise, defaults[methodName], this._descriptors.page[methodName] || - this._descriptors.stream[methodName] || + this._descriptors.stream[methodName] || this._descriptors.longrunning[methodName] ); + + this._innerApiCalls[methodName] = ( + argument: {}, + callOptions?: CallOptions, + callback?: APICallback + ) => { + if (this._terminated) { + return Promise.reject('The client has already been closed.'); + } + return apiCall(argument, callOptions, callback); + }; } } @@ -2068,4 +2081,19 @@ export class KeyManagementServiceClient { callSettings ); } + + /** + * Terminate the GRPC channel and close the client. + * + * The client will no longer be usable and all future behavior is undefined. + */ + close(): Promise { + if (!this._terminated) { + return this._keyManagementServiceStub.then(stub => { + this._terminated = true; + stub.close(); + }); + } + return Promise.resolve(); + } } diff --git a/typescript/test/testdata/showcase/package.json.baseline b/typescript/test/testdata/showcase/package.json.baseline index 5d690961e..dbacca51d 100644 --- a/typescript/test/testdata/showcase/package.json.baseline +++ b/typescript/test/testdata/showcase/package.json.baseline @@ -10,7 +10,7 @@ "build/protos" ], "dependencies": { - "google-gax": "^1.9.0" + "google-gax": "^1.10.0" }, "devDependencies": { "@types/mocha": "^5.2.5", diff --git a/typescript/test/testdata/showcase/src/v1beta1/echo_client.ts.baseline b/typescript/test/testdata/showcase/src/v1beta1/echo_client.ts.baseline index 433aa2c1b..ad2c52090 100644 --- a/typescript/test/testdata/showcase/src/v1beta1/echo_client.ts.baseline +++ b/typescript/test/testdata/showcase/src/v1beta1/echo_client.ts.baseline @@ -17,7 +17,7 @@ // ** All changes to this file may be overwritten. ** import * as gax from 'google-gax'; -import {Callback, LROperation, Descriptors, ClientOptions} from 'google-gax'; +import {APICallback, Callback, CallOptions, LROperation, Descriptors, ClientOptions} from 'google-gax'; import * as path from 'path'; import { Transform } from 'stream'; @@ -51,7 +51,9 @@ export interface PaginationResponse< */ export class EchoClient { private _descriptors: Descriptors = {page: {}, stream: {}, longrunning: {}}; + private _echoStub: Promise<{[name: string]: Function}>; private _innerApiCalls: {[name: string]: Function}; + private _terminated = false; auth: gax.GoogleAuth; /** @@ -193,7 +195,7 @@ export class EchoClient { // Put together the "service stub" for // google.showcase.v1beta1.Echo. - const echoStub = gaxGrpc.createStub( + this._echoStub = gaxGrpc.createStub( opts.fallback ? (protos as protobuf.Root).lookupService('google.showcase.v1beta1.Echo') : // tslint:disable-next-line no-any @@ -206,21 +208,32 @@ export class EchoClient { ['echo', 'expand', 'collect', 'chat', 'pagedExpand', 'wait']; for (const methodName of echoStubMethods) { - const innerCallPromise = echoStub.then( - (stub: {[method: string]: Function}) => (...args: Array<{}>) => { + const innerCallPromise = this._echoStub.then( + stub => (...args: Array<{}>) => { return stub[methodName].apply(stub, args); }, (err: Error|null|undefined) => () => { throw err; }); - this._innerApiCalls[methodName] = gaxModule.createApiCall( + const apiCall = gaxModule.createApiCall( innerCallPromise, defaults[methodName], this._descriptors.page[methodName] || - this._descriptors.stream[methodName] || + this._descriptors.stream[methodName] || this._descriptors.longrunning[methodName] ); + + this._innerApiCalls[methodName] = ( + argument: {}, + callOptions?: CallOptions, + callback?: APICallback + ) => { + if (this._terminated) { + return Promise.reject('The client has already been closed.'); + } + return apiCall(argument, callOptions, callback); + }; } } @@ -580,4 +593,19 @@ export class EchoClient { callSettings ); } + + /** + * Terminate the GRPC channel and close the client. + * + * The client will no longer be usable and all future behavior is undefined. + */ + close(): Promise { + if (!this._terminated) { + return this._echoStub.then(stub => { + this._terminated = true; + stub.close(); + }); + } + return Promise.resolve(); + } } diff --git a/typescript/test/testdata/texttospeech/package.json.baseline b/typescript/test/testdata/texttospeech/package.json.baseline index 84e222e36..9a39365f2 100644 --- a/typescript/test/testdata/texttospeech/package.json.baseline +++ b/typescript/test/testdata/texttospeech/package.json.baseline @@ -10,7 +10,7 @@ "build/protos" ], "dependencies": { - "google-gax": "^1.9.0" + "google-gax": "^1.10.0" }, "devDependencies": { "@types/mocha": "^5.2.5", diff --git a/typescript/test/testdata/texttospeech/src/v1/text_to_speech_client.ts.baseline b/typescript/test/testdata/texttospeech/src/v1/text_to_speech_client.ts.baseline index c6e40e355..79951755a 100644 --- a/typescript/test/testdata/texttospeech/src/v1/text_to_speech_client.ts.baseline +++ b/typescript/test/testdata/texttospeech/src/v1/text_to_speech_client.ts.baseline @@ -17,7 +17,7 @@ // ** All changes to this file may be overwritten. ** import * as gax from 'google-gax'; -import {Callback, Descriptors, ClientOptions} from 'google-gax'; +import {APICallback, Callback, CallOptions, Descriptors, ClientOptions} from 'google-gax'; import * as path from 'path'; import * as protosTypes from '../../protos/protos'; @@ -34,7 +34,9 @@ const version = require('../../../package.json').version; */ export class TextToSpeechClient { private _descriptors: Descriptors = {page: {}, stream: {}, longrunning: {}}; + private _textToSpeechStub: Promise<{[name: string]: Function}>; private _innerApiCalls: {[name: string]: Function}; + private _terminated = false; auth: gax.GoogleAuth; /** @@ -137,7 +139,7 @@ export class TextToSpeechClient { // Put together the "service stub" for // google.cloud.texttospeech.v1.TextToSpeech. - const textToSpeechStub = gaxGrpc.createStub( + this._textToSpeechStub = gaxGrpc.createStub( opts.fallback ? (protos as protobuf.Root).lookupService('google.cloud.texttospeech.v1.TextToSpeech') : // tslint:disable-next-line no-any @@ -150,21 +152,32 @@ export class TextToSpeechClient { ['listVoices', 'synthesizeSpeech']; for (const methodName of textToSpeechStubMethods) { - const innerCallPromise = textToSpeechStub.then( - (stub: {[method: string]: Function}) => (...args: Array<{}>) => { + const innerCallPromise = this._textToSpeechStub.then( + stub => (...args: Array<{}>) => { return stub[methodName].apply(stub, args); }, (err: Error|null|undefined) => () => { throw err; }); - this._innerApiCalls[methodName] = gaxModule.createApiCall( + const apiCall = gaxModule.createApiCall( innerCallPromise, defaults[methodName], this._descriptors.page[methodName] || - this._descriptors.stream[methodName] || + this._descriptors.stream[methodName] || this._descriptors.longrunning[methodName] ); + + this._innerApiCalls[methodName] = ( + argument: {}, + callOptions?: CallOptions, + callback?: APICallback + ) => { + if (this._terminated) { + return Promise.reject('The client has already been closed.'); + } + return apiCall(argument, callOptions, callback); + }; } } @@ -336,4 +349,19 @@ export class TextToSpeechClient { return this._innerApiCalls.synthesizeSpeech(request, options, callback); } + + /** + * Terminate the GRPC channel and close the client. + * + * The client will no longer be usable and all future behavior is undefined. + */ + close(): Promise { + if (!this._terminated) { + return this._textToSpeechStub.then(stub => { + this._terminated = true; + stub.close(); + }); + } + return Promise.resolve(); + } } diff --git a/typescript/test/testdata/translate/package.json.baseline b/typescript/test/testdata/translate/package.json.baseline index c34b51122..a94a226c7 100644 --- a/typescript/test/testdata/translate/package.json.baseline +++ b/typescript/test/testdata/translate/package.json.baseline @@ -10,7 +10,7 @@ "build/protos" ], "dependencies": { - "google-gax": "^1.9.0" + "google-gax": "^1.10.0" }, "devDependencies": { "@types/mocha": "^5.2.5", diff --git a/typescript/test/testdata/translate/src/v3beta1/translation_service_client.ts.baseline b/typescript/test/testdata/translate/src/v3beta1/translation_service_client.ts.baseline index 6d156b552..781e9a5d4 100644 --- a/typescript/test/testdata/translate/src/v3beta1/translation_service_client.ts.baseline +++ b/typescript/test/testdata/translate/src/v3beta1/translation_service_client.ts.baseline @@ -17,7 +17,7 @@ // ** All changes to this file may be overwritten. ** import * as gax from 'google-gax'; -import {Callback, LROperation, Descriptors, ClientOptions} from 'google-gax'; +import {APICallback, Callback, CallOptions, LROperation, Descriptors, ClientOptions} from 'google-gax'; import * as path from 'path'; import { Transform } from 'stream'; @@ -48,8 +48,10 @@ export interface PaginationResponse< */ export class TranslationServiceClient { private _descriptors: Descriptors = {page: {}, stream: {}, longrunning: {}}; + private _translationServiceStub: Promise<{[name: string]: Function}>; private _innerApiCalls: {[name: string]: Function}; private _pathTemplates: {[name: string]: gax.PathTemplate}; + private _terminated = false; auth: gax.GoogleAuth; /** @@ -211,7 +213,7 @@ export class TranslationServiceClient { // Put together the "service stub" for // google.cloud.translation.v3beta1.TranslationService. - const translationServiceStub = gaxGrpc.createStub( + this._translationServiceStub = gaxGrpc.createStub( opts.fallback ? (protos as protobuf.Root).lookupService('google.cloud.translation.v3beta1.TranslationService') : // tslint:disable-next-line no-any @@ -224,21 +226,32 @@ export class TranslationServiceClient { ['translateText', 'detectLanguage', 'getSupportedLanguages', 'batchTranslateText', 'createGlossary', 'listGlossaries', 'getGlossary', 'deleteGlossary']; for (const methodName of translationServiceStubMethods) { - const innerCallPromise = translationServiceStub.then( - (stub: {[method: string]: Function}) => (...args: Array<{}>) => { + const innerCallPromise = this._translationServiceStub.then( + stub => (...args: Array<{}>) => { return stub[methodName].apply(stub, args); }, (err: Error|null|undefined) => () => { throw err; }); - this._innerApiCalls[methodName] = gaxModule.createApiCall( + const apiCall = gaxModule.createApiCall( innerCallPromise, defaults[methodName], this._descriptors.page[methodName] || - this._descriptors.stream[methodName] || + this._descriptors.stream[methodName] || this._descriptors.longrunning[methodName] ); + + this._innerApiCalls[methodName] = ( + argument: {}, + callOptions?: CallOptions, + callback?: APICallback + ) => { + if (this._terminated) { + return Promise.reject('The client has already been closed.'); + } + return apiCall(argument, callOptions, callback); + }; } } @@ -1055,4 +1068,19 @@ export class TranslationServiceClient { matchGlossaryFromGlossaryName(glossaryName: string){ return this._pathTemplates.glossaryPathTemplate.match(glossaryName).glossary; } + + /** + * Terminate the GRPC channel and close the client. + * + * The client will no longer be usable and all future behavior is undefined. + */ + close(): Promise { + if (!this._terminated) { + return this._translationServiceStub.then(stub => { + this._terminated = true; + stub.close(); + }); + } + return Promise.resolve(); + } } diff --git a/typescript/test/util.ts b/typescript/test/util.ts index c111c3b0a..b7b85d204 100644 --- a/typescript/test/util.ts +++ b/typescript/test/util.ts @@ -82,6 +82,27 @@ function checkIdenticalFile( const readOutput = fs.readFileSync(outputFullPath).toString(); const baselineOutput = fs.readFileSync(baselineFullPath).toString(); if (readOutput === baselineOutput) return IDENTICAL_FILE; + + const readOutputLines = readOutput.split('\n'); + const baselineOutputLines = baselineOutput.split('\n'); + + if (readOutputLines.length !== baselineOutputLines.length) { + console.warn( + `Line count for ${outputFullPath} was ${readOutputLines.length}, ` + + `but expected ${baselineOutputLines.length}.` + ); + } else { + for (let i = 0; i < readOutputLines.length; ++i) { + if (readOutputLines[i] !== baselineOutputLines[i]) { + console.warn( + `Line ${i + 1} of ${outputFullPath} was \n\t"${ + readOutputLines[i] + }"\nbut expected\n\t"${baselineOutputLines[i]}"` + ); + } + } + } + return FILE_WITH_DIFF_CONTENT; }