From fbc146c4051f1db0ca78d507b3cbdac543789398 Mon Sep 17 00:00:00 2001 From: Alexander Fenster Date: Fri, 17 Jun 2022 22:40:25 +0000 Subject: [PATCH] fix: camel case for multiple capital letters --- .../naming/v1beta1/naming.proto.baseline | 8 +++ ...ing.create_a_b_c_d_e_something.js.baseline | 53 ++++++++++++++ ...tadata.google.naming.v1beta1.json.baseline | 35 +++++++++ .../src/v1beta1/gapic_metadata.json.baseline | 10 +++ .../src/v1beta1/naming_client.ts.baseline | 65 ++++++++++++++++- .../naming_client_config.json.baseline | 4 ++ .../test/gapic_naming_v1beta1.ts.baseline | 72 +++++++++++++++++++ index.d.ts | 10 +-- .../src/$version/$service_client.ts.njk | 20 +++--- .../protos/google/naming/v1beta1/naming.proto | 8 +++ typescript/src/util.ts | 50 ++++++++++--- typescript/test/unit/util.ts | 32 +++++++++ 12 files changed, 341 insertions(+), 26 deletions(-) create mode 100644 baselines/naming/samples/generated/v1beta1/naming.create_a_b_c_d_e_something.js.baseline diff --git a/baselines/naming/protos/google/naming/v1beta1/naming.proto.baseline b/baselines/naming/protos/google/naming/v1beta1/naming.proto.baseline index 5996647ba..55de1d722 100644 --- a/baselines/naming/protos/google/naming/v1beta1/naming.proto.baseline +++ b/baselines/naming/protos/google/naming/v1beta1/naming.proto.baseline @@ -120,6 +120,14 @@ service Naming { body: "*" }; } + + rpc createABCDESomething(google.protobuf.Empty) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/v1beta1/naming:createABCDESomething" + body: "*" + }; + } + } message PaginatedMethodRequest { diff --git a/baselines/naming/samples/generated/v1beta1/naming.create_a_b_c_d_e_something.js.baseline b/baselines/naming/samples/generated/v1beta1/naming.create_a_b_c_d_e_something.js.baseline new file mode 100644 index 000000000..d09c6f557 --- /dev/null +++ b/baselines/naming/samples/generated/v1beta1/naming.create_a_b_c_d_e_something.js.baseline @@ -0,0 +1,53 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// ** This file is automatically generated by gapic-generator-typescript. ** +// ** https://github.com/googleapis/gapic-generator-typescript ** +// ** All changes to this file may be overwritten. ** + + + +'use strict'; + +function main() { + // [START localhost_v1beta1_generated_Naming_createABCDESomething_async] + /** + * TODO(developer): Uncomment these variables before running the sample. + */ + + // Imports the Naming library + const {NamingClient} = require('naming').v1beta1; + + // Instantiates a client + const namingClient = new NamingClient(); + + async function callCreateABCDESomething() { + // Construct request + const request = { + }; + + // Run request + const response = await namingClient.createABCDESomething(request); + console.log(response); + } + + callCreateABCDESomething(); + // [END localhost_v1beta1_generated_Naming_createABCDESomething_async] +} + +process.on('unhandledRejection', err => { + console.error(err.message); + process.exitCode = 1; +}); +main(...process.argv.slice(2)); diff --git a/baselines/naming/samples/generated/v1beta1/snippet_metadata.google.naming.v1beta1.json.baseline b/baselines/naming/samples/generated/v1beta1/snippet_metadata.google.naming.v1beta1.json.baseline index b8a6b6414..c8501bd00 100644 --- a/baselines/naming/samples/generated/v1beta1/snippet_metadata.google.naming.v1beta1.json.baseline +++ b/baselines/naming/samples/generated/v1beta1/snippet_metadata.google.naming.v1beta1.json.baseline @@ -448,6 +448,41 @@ } } } + }, + { + "regionTag": "localhost_v1beta1_generated_Naming_createABCDESomething_async", + "title": "Naming createABCDESomething Sample", + "origin": "API_DEFINITION", + "description": "", + "canonical": true, + "file": "naming.create_a_b_c_d_e_something.js", + "language": "JAVASCRIPT", + "segments": [ + { + "start": 25, + "end": 45, + "type": "FULL" + } + ], + "clientMethod": { + "shortName": "createABCDESomething", + "fullName": "google.naming.v1beta1.Naming.createABCDESomething", + "async": true, + "parameters": [], + "resultType": ".google.protobuf.Empty", + "client": { + "shortName": "NamingClient", + "fullName": "google.naming.v1beta1.NamingClient" + }, + "method": { + "shortName": "CreateABCDESomething", + "fullName": "google.naming.v1beta1.Naming.CreateABCDESomething", + "service": { + "shortName": "Naming", + "fullName": "google.naming.v1beta1.Naming" + } + } + } } ] } diff --git a/baselines/naming/src/v1beta1/gapic_metadata.json.baseline b/baselines/naming/src/v1beta1/gapic_metadata.json.baseline index 8a526637d..43ee96b6f 100644 --- a/baselines/naming/src/v1beta1/gapic_metadata.json.baseline +++ b/baselines/naming/src/v1beta1/gapic_metadata.json.baseline @@ -60,6 +60,11 @@ "getReservedWord" ] }, + "createABCDESomething": { + "methods": [ + "createABCDESomething" + ] + }, "LongRunning": { "methods": [ "longRunning" @@ -127,6 +132,11 @@ "getReservedWord" ] }, + "createABCDESomething": { + "methods": [ + "createABCDESomething" + ] + }, "LongRunning": { "methods": [ "longRunning" diff --git a/baselines/naming/src/v1beta1/naming_client.ts.baseline b/baselines/naming/src/v1beta1/naming_client.ts.baseline index fac6ed55e..762a536f9 100644 --- a/baselines/naming/src/v1beta1/naming_client.ts.baseline +++ b/baselines/naming/src/v1beta1/naming_client.ts.baseline @@ -240,7 +240,7 @@ export class NamingClient { // Iterate over each of the methods that the service provides // and create an API call method for each. const namingStubMethods = - ['paginatedMethod', 'paginatedMethodStream', 'paginatedMethodAsync', 'longRunning', 'checkLongRunningProgress', 'initialize', 'servicePath', 'apiEndpoint', 'port', 'scopes', 'getProjectId', 'getReservedWord']; + ['paginatedMethod', 'paginatedMethodStream', 'paginatedMethodAsync', 'longRunning', 'checkLongRunningProgress', 'initialize', 'servicePath', 'apiEndpoint', 'port', 'scopes', 'getProjectId', 'getReservedWord', 'createAbcdeSomething']; for (const methodName of namingStubMethods) { const callPromise = this.namingStub.then( stub => (...args: Array<{}>) => { @@ -954,6 +954,69 @@ export class NamingClient { this.initialize1(); return this.innerApiCalls.getReservedWord(request, options, callback); } +/** + * + * @param {Object} request + * The request object that will be sent. + * @param {object} [options] + * Call options. See {@link https://googleapis.dev/nodejs/google-gax/latest/interfaces/CallOptions.html|CallOptions} for more details. + * @returns {Promise} - The promise which resolves to an array. + * The first element of the array is an object representing [Empty]{@link google.protobuf.Empty}. + * Please see the + * [documentation](https://github.com/googleapis/gax-nodejs/blob/master/client-libraries.md#regular-methods) + * for more details and examples. + * @example include:samples/generated/v1beta1/naming.create_a_b_c_d_e_something.js + * region_tag:localhost_v1beta1_generated_Naming_createABCDESomething_async + */ + createABCDESomething( + request?: protos.google.protobuf.IEmpty, + options?: CallOptions): + Promise<[ + protos.google.protobuf.IEmpty, + protos.google.protobuf.IEmpty|undefined, {}|undefined + ]>; + createABCDESomething( + request: protos.google.protobuf.IEmpty, + options: CallOptions, + callback: Callback< + protos.google.protobuf.IEmpty, + protos.google.protobuf.IEmpty|null|undefined, + {}|null|undefined>): void; + createABCDESomething( + request: protos.google.protobuf.IEmpty, + callback: Callback< + protos.google.protobuf.IEmpty, + protos.google.protobuf.IEmpty|null|undefined, + {}|null|undefined>): void; + createABCDESomething( + request?: protos.google.protobuf.IEmpty, + optionsOrCallback?: CallOptions|Callback< + protos.google.protobuf.IEmpty, + protos.google.protobuf.IEmpty|null|undefined, + {}|null|undefined>, + callback?: Callback< + protos.google.protobuf.IEmpty, + protos.google.protobuf.IEmpty|null|undefined, + {}|null|undefined>): + Promise<[ + protos.google.protobuf.IEmpty, + protos.google.protobuf.IEmpty|undefined, {}|undefined + ]>|void { + request = request || {}; + let options: CallOptions; + if (typeof optionsOrCallback === 'function' && callback === undefined) { + callback = optionsOrCallback; + options = {}; + } + else { + options = optionsOrCallback as CallOptions; + } + options = options || {}; + options.otherArgs = options.otherArgs || {}; + options.otherArgs.headers = options.otherArgs.headers || {}; + this.initialize1(); + return this.innerApiCalls.createAbcdeSomething(request, options, callback); + } /** * Problem #2: long running method generates extra method check*Progress() diff --git a/baselines/naming/src/v1beta1/naming_client_config.json.baseline b/baselines/naming/src/v1beta1/naming_client_config.json.baseline index 27b3fe548..2b2302872 100644 --- a/baselines/naming/src/v1beta1/naming_client_config.json.baseline +++ b/baselines/naming/src/v1beta1/naming_client_config.json.baseline @@ -67,6 +67,10 @@ "getReservedWord": { "retry_codes_name": "non_idempotent", "retry_params_name": "default" + }, + "createABCDESomething": { + "retry_codes_name": "non_idempotent", + "retry_params_name": "default" } } } diff --git a/baselines/naming/test/gapic_naming_v1beta1.ts.baseline b/baselines/naming/test/gapic_naming_v1beta1.ts.baseline index 6278d9b16..0bf66be7c 100644 --- a/baselines/naming/test/gapic_naming_v1beta1.ts.baseline +++ b/baselines/naming/test/gapic_naming_v1beta1.ts.baseline @@ -916,6 +916,78 @@ describe('v1beta1.NamingClient', () => { }); }); + describe('createABCDESomething', () => { + it('invokes createABCDESomething without error', async () => { + const client = new namingModule.v1beta1.NamingClient({ + credentials: {client_email: 'bogus', private_key: 'bogus'}, + projectId: 'bogus', + }); + client.initialize1(); + const request = generateSampleMessage(new protos.google.protobuf.Empty()); + const expectedOptions = {otherArgs: {headers: {}}};; + const expectedResponse = generateSampleMessage(new protos.google.protobuf.Empty()); + client.innerApiCalls.createABCDESomething = stubSimpleCall(expectedResponse); + const [response] = await client.createABCDESomething(request); + assert.deepStrictEqual(response, expectedResponse); + assert((client.innerApiCalls.createABCDESomething as SinonStub) + .getCall(0).calledWith(request, expectedOptions, undefined)); + }); + + it('invokes createABCDESomething without error using callback', async () => { + const client = new namingModule.v1beta1.NamingClient({ + credentials: {client_email: 'bogus', private_key: 'bogus'}, + projectId: 'bogus', + }); + client.initialize1(); + const request = generateSampleMessage(new protos.google.protobuf.Empty()); + const expectedOptions = {otherArgs: {headers: {}}};; + const expectedResponse = generateSampleMessage(new protos.google.protobuf.Empty()); + client.innerApiCalls.createABCDESomething = stubSimpleCallWithCallback(expectedResponse); + const promise = new Promise((resolve, reject) => { + client.createABCDESomething( + request, + (err?: Error|null, result?: protos.google.protobuf.IEmpty|null) => { + if (err) { + reject(err); + } else { + resolve(result); + } + }); + }); + const response = await promise; + assert.deepStrictEqual(response, expectedResponse); + assert((client.innerApiCalls.createABCDESomething as SinonStub) + .getCall(0).calledWith(request, expectedOptions /*, callback defined above */)); + }); + + it('invokes createABCDESomething with error', async () => { + const client = new namingModule.v1beta1.NamingClient({ + credentials: {client_email: 'bogus', private_key: 'bogus'}, + projectId: 'bogus', + }); + client.initialize1(); + const request = generateSampleMessage(new protos.google.protobuf.Empty()); + const expectedOptions = {otherArgs: {headers: {}}};; + const expectedError = new Error('expected'); + client.innerApiCalls.createABCDESomething = stubSimpleCall(undefined, expectedError); + await assert.rejects(client.createABCDESomething(request), expectedError); + assert((client.innerApiCalls.createABCDESomething as SinonStub) + .getCall(0).calledWith(request, expectedOptions, undefined)); + }); + + it('invokes createABCDESomething with closed client', async () => { + const client = new namingModule.v1beta1.NamingClient({ + credentials: {client_email: 'bogus', private_key: 'bogus'}, + projectId: 'bogus', + }); + client.initialize1(); + const request = generateSampleMessage(new protos.google.protobuf.Empty()); + const expectedError = new Error('The client has already been closed.'); + client.close(); + await assert.rejects(client.createABCDESomething(request), expectedError); + }); + }); + describe('longRunning', () => { it('invokes longRunning without error', async () => { const client = new namingModule.v1beta1.NamingClient({ diff --git a/index.d.ts b/index.d.ts index a84774831..48431a754 100644 --- a/index.d.ts +++ b/index.d.ts @@ -14,11 +14,11 @@ interface String { capitalize(): string; - words(): string[]; - toCamelCase(): string; - toPascalCase(): string; - toKebabCase(): string; - toSnakeCase(): string; + words(protobufJsStyle?: boolean): string[]; + toCamelCase(protobufJsStyle?: boolean): string; + toPascalCase(protobufJsStyle?: boolean): string; + toKebabCase(protobufJsStyle?: boolean): string; + toSnakeCase(protobufJsStyle?: boolean): string; replaceAll(this: string, search: string, replacement: string): string; } diff --git a/templates/typescript_gapic/src/$version/$service_client.ts.njk b/templates/typescript_gapic/src/$version/$service_client.ts.njk index bf9e8637a..332de7a5a 100644 --- a/templates/typescript_gapic/src/$version/$service_client.ts.njk +++ b/templates/typescript_gapic/src/$version/$service_client.ts.njk @@ -398,7 +398,7 @@ export class {{ service.name }}Client { {%- for method in service.method -%} {%- if not method.ignoreMapPagingMethod %} {{- stubMethodsJoiner() -}} - '{{ method.name.toCamelCase() }}' + '{{ method.name.toCamelCase(true) }}' {%- endif %} {%- endfor -%} ]; @@ -589,7 +589,7 @@ export class {{ service.name }}Client { this.warn('DEP${{service.name}}-${{method.name}}','{{method.name}} is deprecated and may be removed in a future version.', 'DeprecationWarning'); {%- endif %} {%- if method.isDiregapicLRO %} - return this.innerApiCalls.{{ method.name.toCamelCase() }}(request, options, callback) + return this.innerApiCalls.{{ method.name.toCamelCase(true) }}(request, options, callback) .then(([response, operation, rawResponse]: [protos.google.cloud.compute.v1.IOperation, protos.google.cloud.compute.v1.IOperation, protos.google.cloud.compute.v1.IOperation]) => { return [ { latestResponse: response, done: false, name: response.id, metadata: null, result: {}}, @@ -598,7 +598,7 @@ export class {{ service.name }}Client { ]; }); {%- else %} - return this.innerApiCalls.{{ method.name.toCamelCase() }}(request, options, callback); + return this.innerApiCalls.{{ method.name.toCamelCase(true) }}(request, options, callback); {%- endif %} } {%- endfor %} @@ -621,7 +621,7 @@ export class {{ service.name }}Client { {%- if method.options and method.options.deprecated %} this.warn('DEP${{service.name}}-${{method.name}}','{{method.name}} is deprecated and may be removed in a future version.', 'DeprecationWarning'); {%- endif %} - return this.innerApiCalls.{{ method.name.toCamelCase() }}(null, options); + return this.innerApiCalls.{{ method.name.toCamelCase(true) }}(null, options); } {%- elif method.serverStreaming %} /** @@ -644,7 +644,7 @@ export class {{ service.name }}Client { {%- if method.options and method.options.deprecated %} this.warn('DEP${{service.name}}-${{method.name}}','{{method.name}} is deprecated and may be removed in a future version.', 'DeprecationWarning'); {%- endif %} - return this.innerApiCalls.{{ method.name.toCamelCase() }}(request, options); + return this.innerApiCalls.{{ method.name.toCamelCase(true) }}(request, options); } {%- elif method.clientStreaming %} /** @@ -689,7 +689,7 @@ export class {{ service.name }}Client { {%- if method.options and method.options.deprecated %} this.warn('DEP${{service.name}}-${{method.name}}','{{method.name}} is deprecated and may be removed in a future version.', 'DeprecationWarning'); {%- endif %} - return this.innerApiCalls.{{ method.name.toCamelCase() }}(null, options, callback); + return this.innerApiCalls.{{ method.name.toCamelCase(true) }}(null, options, callback); } {%- endif %} {% endfor %} @@ -752,7 +752,7 @@ export class {{ service.name }}Client { {%- if method.options and method.options.deprecated %} this.warn('DEP${{service.name}}-${{method.name}}','{{method.name}} is deprecated and may be removed in a future version.', 'DeprecationWarning'); {%- endif %} - return this.innerApiCalls.{{ method.name.toCamelCase() }}(request, options, callback); + return this.innerApiCalls.{{ method.name.toCamelCase(true) }}(request, options, callback); } /** {%- set decodeMethodName = id.get("check" + method.name.toPascalCase() + "Progress") -%} @@ -834,7 +834,7 @@ export class {{ service.name }}Client { {%- if method.options and method.options.deprecated %} this.warn('DEP${{service.name}}-${{method.name}}','{{method.name}} is deprecated and may be removed in a future version.', 'DeprecationWarning'); {%- endif %} - return this.innerApiCalls.{{ method.name.toCamelCase() }}(request, options, callback); + return this.innerApiCalls.{{ method.name.toCamelCase(true) }}(request, options, callback); } /** @@ -856,7 +856,7 @@ export class {{ service.name }}Client { this.warn('DEP${{service.name}}-${{method.name}}','{{method.name}} is deprecated and may be removed in a future version.', 'DeprecationWarning'); {%- endif %} return this.descriptors.page.{{ method.name.toCamelCase() }}.createStream( - this.innerApiCalls.{{ method.name.toCamelCase() }} as gax.GaxCall, + this.innerApiCalls.{{ method.name.toCamelCase(true) }} as gax.GaxCall, request, callSettings ); @@ -890,7 +890,7 @@ export class {{ service.name }}Client { this.warn('DEP${{service.name}}-${{method.name}}','{{method.name}} is deprecated and may be removed in a future version.', 'DeprecationWarning'); {%- endif %} return this.descriptors.page.{{ method.name.toCamelCase() }}.asyncIterate( - this.innerApiCalls['{{ method.name.toCamelCase() }}'] as GaxCall, + this.innerApiCalls['{{ method.name.toCamelCase(true) }}'] as GaxCall, request as unknown as RequestType, callSettings {%- if method.pagingMapResponseType %} diff --git a/test-fixtures/protos/google/naming/v1beta1/naming.proto b/test-fixtures/protos/google/naming/v1beta1/naming.proto index 5996647ba..55de1d722 100644 --- a/test-fixtures/protos/google/naming/v1beta1/naming.proto +++ b/test-fixtures/protos/google/naming/v1beta1/naming.proto @@ -120,6 +120,14 @@ service Naming { body: "*" }; } + + rpc createABCDESomething(google.protobuf.Empty) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/v1beta1/naming:createABCDESomething" + body: "*" + }; + } + } message PaginatedMethodRequest { diff --git a/typescript/src/util.ts b/typescript/src/util.ts index 4c0198039..b3a02d164 100644 --- a/typescript/src/util.ts +++ b/typescript/src/util.ts @@ -82,15 +82,36 @@ String.prototype.capitalize = function (this: string): string { return this[0].toUpperCase() + this.slice(1); }; -String.prototype.words = function (this: string): string[] { +String.prototype.words = function ( + this: string, + protobufJsStyle = false +): string[] { + // eslint-disable-next-line @typescript-eslint/no-this-alias + let arg = this; + if (protobufJsStyle) { + // treat multiple capital letters as one word + // e.g. CreateOSPolicy => create, os, policy + // (the default would've been create, o, s, policy) + arg = this.replace(/([A-Z])([A-Z]+)([A-Z])/g, (str: string) => { + return ( + str[0] + + str.slice(1, str.length - 1).toLowerCase() + + str[str.length - 1] + ); + }); + } // split on spaces, non-alphanumeric, or capital letters - return this.split(/(?=[A-Z])|[\s\W_]+/) + return arg + .split(/(?=[A-Z])|[\s\W_]+/) .filter(w => w.length > 0) .map(w => w.toLowerCase()); }; -String.prototype.toCamelCase = function (this: string): string { - const words = this.words(); +String.prototype.toCamelCase = function ( + this: string, + protobufJsStyle = false +): string { + const words = this.words(protobufJsStyle); if (words.length === 0) { return this; } @@ -106,8 +127,11 @@ String.prototype.toCamelCase = function (this: string): string { return result.join(''); }; -String.prototype.toPascalCase = function (this: string): string { - const words = this.words(); +String.prototype.toPascalCase = function ( + this: string, + protobufJsStyle = false +): string { + const words = this.words(protobufJsStyle); if (words.length === 0) { return this; } @@ -115,16 +139,22 @@ String.prototype.toPascalCase = function (this: string): string { return result.join(''); }; -String.prototype.toKebabCase = function (this: string): string { - const words = this.words(); +String.prototype.toKebabCase = function ( + this: string, + protobufJsStyle = false +): string { + const words = this.words(protobufJsStyle); if (words.length === 0) { return this; } return words.join('-'); }; -String.prototype.toSnakeCase = function (this: string): string { - const words = this.words(); +String.prototype.toSnakeCase = function ( + this: string, + protobufJsStyle = false +): string { + const words = this.words(protobufJsStyle); if (words.length === 0) { return this; } diff --git a/typescript/test/unit/util.ts b/typescript/test/unit/util.ts index 37be4c643..901404834 100644 --- a/typescript/test/unit/util.ts +++ b/typescript/test/unit/util.ts @@ -140,11 +140,35 @@ describe('src/util.ts', () => { 'case', 'string', ]); + assert.deepStrictEqual('camelCaseABCString'.words(), [ + 'camel', + 'case', + 'a', + 'b', + 'c', + 'string', + ]); + assert.deepStrictEqual( + 'camelCaseABCString'.words(/*protobufJsStyle:*/ true), + ['camel', 'case', 'abc', 'string'] + ); assert.deepStrictEqual('PascalCaseString'.words(), [ 'pascal', 'case', 'string', ]); + assert.deepStrictEqual('PascalCaseABCString'.words(), [ + 'pascal', + 'case', + 'a', + 'b', + 'c', + 'string', + ]); + assert.deepStrictEqual( + 'PascalCaseABCString'.words(/*protobufJsStyle:*/ true), + ['pascal', 'case', 'abc', 'string'] + ); assert.deepStrictEqual('snake_case_string'.words(), [ 'snake', 'case', @@ -209,6 +233,14 @@ describe('src/util.ts', () => { 'display_video_360_advertiser_link'.toCamelCase(), 'displayVideo_360AdvertiserLink' ); + assert.deepStrictEqual( + 'CreateOSSomething'.toCamelCase(), + 'createOSSomething' + ); + assert.deepStrictEqual( + 'CreateOSSomething'.toCamelCase(/*protobufJsStyle:*/ true), + 'createOsSomething' + ); }); it('should convert to PascalCase', () => {