From d26dfe88b4dccdd06839a04d64a4693d1f40e73f Mon Sep 17 00:00:00 2001 From: xiaozhenliugg Date: Tue, 19 Nov 2019 11:50:49 -0800 Subject: [PATCH 1/6] let user pass package name to generator --- templates/typescript_gapic/package.json.njk | 2 +- .../system-test/fixtures/sample/src/index.js.njk | 2 +- .../system-test/fixtures/sample/src/index.ts.njk | 2 +- typescript/src/generator.ts | 13 +++++++++++-- typescript/src/schema/api.ts | 11 ++++++++++- typescript/src/start_script.ts | 6 ++++++ .../testdata/texttospeech/package.json.baseline | 2 +- .../fixtures/sample/src/index.js.baseline | 2 +- .../fixtures/sample/src/index.ts.baseline | 2 +- .../unit/{grpc-client-config.ts => extra-option.ts} | 6 ++++-- 10 files changed, 37 insertions(+), 11 deletions(-) rename typescript/test/unit/{grpc-client-config.ts => extra-option.ts} (90%) diff --git a/templates/typescript_gapic/package.json.njk b/templates/typescript_gapic/package.json.njk index eaf401433..9c2130913 100644 --- a/templates/typescript_gapic/package.json.njk +++ b/templates/typescript_gapic/package.json.njk @@ -16,7 +16,7 @@ limitations under the License. -#} { - "name": "{{ api.naming.productName.toKebabCase() }}", + "name": "{{ api.publishName}}", "version": "0.1.0", "description": "{{ api.naming.productName }} client for Node.js", "repository": "googleapis/nodejs-{{ api.naming.productName.toKebabCase() }}", diff --git a/templates/typescript_gapic/system-test/fixtures/sample/src/index.js.njk b/templates/typescript_gapic/system-test/fixtures/sample/src/index.js.njk index a1998871d..4695b40d4 100644 --- a/templates/typescript_gapic/system-test/fixtures/sample/src/index.js.njk +++ b/templates/typescript_gapic/system-test/fixtures/sample/src/index.js.njk @@ -17,7 +17,7 @@ limitations under the License. -#} {% import "../../../../_license.njk" as license -%} {{license.license()}} -const {{ api.naming.productName.toKebabCase()}} = require('{{ api.naming.productName.toKebabCase() }}'); +const {{ api.naming.productName.toKebabCase()}} = require('{{ api.publishName }}'); function main() { {%- for service in api.services %} diff --git a/templates/typescript_gapic/system-test/fixtures/sample/src/index.ts.njk b/templates/typescript_gapic/system-test/fixtures/sample/src/index.ts.njk index 78ba439d9..9f617615f 100644 --- a/templates/typescript_gapic/system-test/fixtures/sample/src/index.ts.njk +++ b/templates/typescript_gapic/system-test/fixtures/sample/src/index.ts.njk @@ -23,7 +23,7 @@ import { {{- serviceJoiner() -}} {{- service.name.toPascalCase() + 'Client' -}} {%- endfor -%} -} from '{{ api.naming.productName.toKebabCase() }}'; +} from '{{ api.publishName }}'; function main() { {%- for service in api.services %} diff --git a/typescript/src/generator.ts b/typescript/src/generator.ts index 91479ea56..fcbc02005 100644 --- a/typescript/src/generator.ts +++ b/typescript/src/generator.ts @@ -39,6 +39,8 @@ export class Generator { request: plugin.google.protobuf.compiler.CodeGeneratorRequest; response: plugin.google.protobuf.compiler.CodeGeneratorResponse; grpcServiceConfig: plugin.grpc.service_config.ServiceConfig; + //This field is for users passing proper module name for system test. + publishName?: string; constructor() { this.request = plugin.google.protobuf.compiler.CodeGeneratorRequest.create(); @@ -66,7 +68,7 @@ export class Generator { } private async readGrpcServiceConfig(parameter: string) { - const match = parameter.match(/^["']?grpc-service-config=([^"]+)["']?$/); + const match = parameter.match(/^["']?grpc-service-config=([^"]+)["']?/); if (!match) { throw new Error(`Parameter ${parameter} was not recognized.`); } @@ -83,6 +85,11 @@ export class Generator { ); } + private async readPublishPackageName(parameter: string) { + const match = parameter.match(/["']?package-name=([^"]+)["']?$/); + if (match && match.length > 1) this.publishName = match[1]; + } + async initializeFromStdin() { const inputBuffer = await getStdin.buffer(); this.request = plugin.google.protobuf.compiler.CodeGeneratorRequest.decode( @@ -90,6 +97,7 @@ export class Generator { ); if (this.request.parameter) { await this.readGrpcServiceConfig(this.request.parameter); + await this.readPublishPackageName(this.request.parameter); } } @@ -125,7 +133,8 @@ export class Generator { const api = new API( this.request.protoFile, packageName, - this.grpcServiceConfig + this.grpcServiceConfig, + this.publishName ); return api; } diff --git a/typescript/src/schema/api.ts b/typescript/src/schema/api.ts index 3782cd3e0..d55baec84 100644 --- a/typescript/src/schema/api.ts +++ b/typescript/src/schema/api.ts @@ -32,19 +32,28 @@ export class API { hostName?: string; port?: string; mainServiceName?: string; + //This field is for users passing roper module name for system test. + publishName: string; // oauth_scopes: plugin.google.protobuf.IServiceOptions.prototype[".google.api.oauthScopes"]; // TODO: subpackages constructor( fileDescriptors: plugin.google.protobuf.IFileDescriptorProto[], packageName: string, - grpcServiceConfig: plugin.grpc.service_config.ServiceConfig + grpcServiceConfig: plugin.grpc.service_config.ServiceConfig, + publishName?: string ) { this.naming = new Naming( fileDescriptors.filter( fd => fd.package && fd.package.startsWith(packageName) ) ); + // users specify the actual package name, if not, set it to product name. + if (publishName) { + this.publishName = publishName; + } else { + this.publishName = this.naming.productName.toKebabCase(); + } // construct resource map const resourceMap = getResourceMap(fileDescriptors); // parse resource map to Proto constructor diff --git a/typescript/src/start_script.ts b/typescript/src/start_script.ts index 80c6dccab..786363313 100755 --- a/typescript/src/start_script.ts +++ b/typescript/src/start_script.ts @@ -34,6 +34,8 @@ const argv = yargs .describe('output_dir', 'Path to a directory for the generated code') .alias('grpc-service-config', 'grpc_service_config') .describe('grpc-service-config', 'Path to gRPC service config JSON') + .alias('package-name', 'package-name') + .describe('package-name', 'Publish package name') .alias('common-proto-path', 'common_protos_path') .describe( 'common_proto_path', @@ -43,6 +45,7 @@ const argv = yargs google/example/api/v1/api.proto`).argv; const outputDir = argv.outputDir as string; const grpcServiceConfig = argv.grpcServiceConfig as string | undefined; +const packageName = argv.packageName as string | undefined; const protoDirs: string[] = []; if (argv.I) { @@ -71,6 +74,9 @@ if (grpcServiceConfig) { `--typescript_gapic_opt="grpc-service-config=${grpcServiceConfig}"` ); } +if (packageName) { + protocCommand.push(`--typescript_gapic_opt="package-name=${packageName}"`); +} protocCommand.push(...protoDirsArg); protocCommand.push(...protoFiles); try { diff --git a/typescript/test/testdata/texttospeech/package.json.baseline b/typescript/test/testdata/texttospeech/package.json.baseline index 015bcea81..a55c5077d 100644 --- a/typescript/test/testdata/texttospeech/package.json.baseline +++ b/typescript/test/testdata/texttospeech/package.json.baseline @@ -1,5 +1,5 @@ { - "name": "texttospeech", + "name": "@google-cloud/text-to-speech", "version": "0.1.0", "description": "Texttospeech client for Node.js", "repository": "googleapis/nodejs-texttospeech", diff --git a/typescript/test/testdata/texttospeech/system-test/fixtures/sample/src/index.js.baseline b/typescript/test/testdata/texttospeech/system-test/fixtures/sample/src/index.js.baseline index 443142bb6..e1636daef 100644 --- a/typescript/test/testdata/texttospeech/system-test/fixtures/sample/src/index.js.baseline +++ b/typescript/test/testdata/texttospeech/system-test/fixtures/sample/src/index.js.baseline @@ -16,7 +16,7 @@ // ** https://github.com/googleapis/gapic-generator-typescript ** // ** All changes to this file may be overwritten. ** -const texttospeech = require('texttospeech'); +const texttospeech = require('@google-cloud/text-to-speech'); function main() { const textToSpeechClient = new texttospeech.TextToSpeechClient(); diff --git a/typescript/test/testdata/texttospeech/system-test/fixtures/sample/src/index.ts.baseline b/typescript/test/testdata/texttospeech/system-test/fixtures/sample/src/index.ts.baseline index 2e64b80ef..3d5e706fb 100644 --- a/typescript/test/testdata/texttospeech/system-test/fixtures/sample/src/index.ts.baseline +++ b/typescript/test/testdata/texttospeech/system-test/fixtures/sample/src/index.ts.baseline @@ -16,7 +16,7 @@ // ** https://github.com/googleapis/gapic-generator-typescript ** // ** All changes to this file may be overwritten. ** -import {TextToSpeechClient} from 'texttospeech'; +import {TextToSpeechClient} from '@google-cloud/text-to-speech'; function main() { const textToSpeechClient = new TextToSpeechClient(); diff --git a/typescript/test/unit/grpc-client-config.ts b/typescript/test/unit/extra-option.ts similarity index 90% rename from typescript/test/unit/grpc-client-config.ts rename to typescript/test/unit/extra-option.ts index 350a9662b..b2a031d9e 100644 --- a/typescript/test/unit/grpc-client-config.ts +++ b/typescript/test/unit/extra-option.ts @@ -58,12 +58,13 @@ const BASELINE_DIR = path.join( 'texttospeech' ); +const PACKAGE_NAME = '@google-cloud/text-to-speech'; const SRCDIR = path.join(cwd, 'build', 'src'); const CLI = path.join(SRCDIR, 'cli.js'); -describe('gRPC Client Config', () => { +describe('Package Name & grpc Config', () => { describe('Generate Text-to-Speech library', () => { - it('Generated proto list should have same output with baseline.', function() { + it('Generated library name & grpc Config should be same with baseline.', function() { this.timeout(10000); if (fs.existsSync(OUTPUT_DIR)) { rimraf.sync(OUTPUT_DIR); @@ -82,6 +83,7 @@ describe('gRPC Client Config', () => { `-I ${GOOGLE_GAX_PROTOS_DIR} ` + `-I ${PROTOS_DIR} ` + `--grpc-service-config=${GRPC_SERVICE_CONFIG} ` + + `--package-name=${PACKAGE_NAME} ` + TTS_PROTO_FILE ); assert(equalToBaseline(OUTPUT_DIR, BASELINE_DIR)); From a310b74ed66c500127f97f7a9a5e9807aa418f9d Mon Sep 17 00:00:00 2001 From: xiaozhenliugg Date: Tue, 19 Nov 2019 14:08:00 -0800 Subject: [PATCH 2/6] feedback --- templates/typescript_gapic/package.json.njk | 2 +- typescript/src/generator.ts | 2 +- typescript/src/schema/api.ts | 6 +----- typescript/src/start_script.ts | 2 +- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/templates/typescript_gapic/package.json.njk b/templates/typescript_gapic/package.json.njk index 9c2130913..9421ea11b 100644 --- a/templates/typescript_gapic/package.json.njk +++ b/templates/typescript_gapic/package.json.njk @@ -16,7 +16,7 @@ limitations under the License. -#} { - "name": "{{ api.publishName}}", + "name": "{{ api.publishName }}", "version": "0.1.0", "description": "{{ api.naming.productName }} client for Node.js", "repository": "googleapis/nodejs-{{ api.naming.productName.toKebabCase() }}", diff --git a/typescript/src/generator.ts b/typescript/src/generator.ts index fcbc02005..706a5c3a4 100644 --- a/typescript/src/generator.ts +++ b/typescript/src/generator.ts @@ -39,7 +39,7 @@ export class Generator { request: plugin.google.protobuf.compiler.CodeGeneratorRequest; response: plugin.google.protobuf.compiler.CodeGeneratorResponse; grpcServiceConfig: plugin.grpc.service_config.ServiceConfig; - //This field is for users passing proper module name for system test. + // This field is for users passing proper publish package name like @google-cloud/text-to-speech. publishName?: string; constructor() { diff --git a/typescript/src/schema/api.ts b/typescript/src/schema/api.ts index d55baec84..d2f42c15e 100644 --- a/typescript/src/schema/api.ts +++ b/typescript/src/schema/api.ts @@ -49,11 +49,7 @@ export class API { ) ); // users specify the actual package name, if not, set it to product name. - if (publishName) { - this.publishName = publishName; - } else { - this.publishName = this.naming.productName.toKebabCase(); - } + this.publishName = publishName || this.naming.productName.toKebabCase(); // construct resource map const resourceMap = getResourceMap(fileDescriptors); // parse resource map to Proto constructor diff --git a/typescript/src/start_script.ts b/typescript/src/start_script.ts index 786363313..e4af8ca17 100755 --- a/typescript/src/start_script.ts +++ b/typescript/src/start_script.ts @@ -34,7 +34,7 @@ const argv = yargs .describe('output_dir', 'Path to a directory for the generated code') .alias('grpc-service-config', 'grpc_service_config') .describe('grpc-service-config', 'Path to gRPC service config JSON') - .alias('package-name', 'package-name') + .alias('package-name', 'package_name') .describe('package-name', 'Publish package name') .alias('common-proto-path', 'common_protos_path') .describe( From b5102519b244368d5a7d826d31fc5e3e1ccb7358 Mon Sep 17 00:00:00 2001 From: xiaozhenliugg Date: Tue, 19 Nov 2019 15:24:26 -0800 Subject: [PATCH 3/6] use param map for options --- typescript/src/generator.ts | 51 +++++++++++++++++----------- typescript/test/unit/extra-option.ts | 14 ++++++++ 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/typescript/src/generator.ts b/typescript/src/generator.ts index 706a5c3a4..2f32b03d5 100644 --- a/typescript/src/generator.ts +++ b/typescript/src/generator.ts @@ -23,6 +23,9 @@ import { API } from './schema/api'; import { processTemplates } from './templater'; import { commonPrefix, duration } from './util'; +export interface Map { + [name: string]: string; +} const readFile = util.promisify(fs.readFile); const templateDirectory = path.join( @@ -32,6 +35,7 @@ const templateDirectory = path.join( 'templates', 'typescript_gapic' ); + // If needed, we can make it possible to load templates from different locations // to generate code for other languages. @@ -67,27 +71,35 @@ export class Generator { } } - private async readGrpcServiceConfig(parameter: string) { - const match = parameter.match(/^["']?grpc-service-config=([^"]+)["']?/); - if (!match) { - throw new Error(`Parameter ${parameter} was not recognized.`); - } - const filename = match[1]; - if (!fs.existsSync(filename)) { - throw new Error(`File ${filename} cannot be opened.`); + private getParamMap(parameter: string) { + // Example: "grpc-service-config=texamplejson","package-name=packageName" + const paramMap: Map = {}; + const parameters = parameter.split(','); + for (let param of parameters) { + // remove double quote + param = param.substring(1, param.length - 1); + const arr = param.split('='); + paramMap[arr[0].toKebabCase()] = arr[1]; } + return paramMap; + } - const content = await readFile(filename); - const json = JSON.parse(content.toString()); - Generator.updateDuration(json); - this.grpcServiceConfig = plugin.grpc.service_config.ServiceConfig.fromObject( - json - ); + private async readGrpcServiceConfig(map: Map) { + if (map && map['grpc-service-config']) { + const filename = map['grpc-service-config']; + const content = await readFile(filename); + const json = JSON.parse(content.toString()); + Generator.updateDuration(json); + this.grpcServiceConfig = plugin.grpc.service_config.ServiceConfig.fromObject( + json + ); + } } - private async readPublishPackageName(parameter: string) { - const match = parameter.match(/["']?package-name=([^"]+)["']?$/); - if (match && match.length > 1) this.publishName = match[1]; + private async readPublishPackageName(map: Map) { + if (map && map['package-name']) { + this.publishName = map['package-name']; + } } async initializeFromStdin() { @@ -96,8 +108,9 @@ export class Generator { inputBuffer ); if (this.request.parameter) { - await this.readGrpcServiceConfig(this.request.parameter); - await this.readPublishPackageName(this.request.parameter); + const map = this.getParamMap(this.request.parameter); + await this.readGrpcServiceConfig(map); + await this.readPublishPackageName(map); } } diff --git a/typescript/test/unit/extra-option.ts b/typescript/test/unit/extra-option.ts index b2a031d9e..435fcad98 100644 --- a/typescript/test/unit/extra-option.ts +++ b/typescript/test/unit/extra-option.ts @@ -88,5 +88,19 @@ describe('Package Name & grpc Config', () => { ); assert(equalToBaseline(OUTPUT_DIR, BASELINE_DIR)); }); + + it('Use alias name should also work.', function() { + this.timeout(10000); + execSync( + `node build/src/start_script.js ` + + `--output-dir=${OUTPUT_DIR} ` + + `-I ${GOOGLE_GAX_PROTOS_DIR} ` + + `-I ${PROTOS_DIR} ` + + `--grpc_service_config=${GRPC_SERVICE_CONFIG} ` + + `--package_name=${PACKAGE_NAME} ` + + TTS_PROTO_FILE + ); + assert(equalToBaseline(OUTPUT_DIR, BASELINE_DIR)); + }); }); }); From 8586850ce7ccd21d9d6141a24fded45c088c831c Mon Sep 17 00:00:00 2001 From: xiaozhenliugg Date: Tue, 19 Nov 2019 15:26:45 -0800 Subject: [PATCH 4/6] throw error if grpc file does not exist --- typescript/src/generator.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/typescript/src/generator.ts b/typescript/src/generator.ts index 2f32b03d5..9291383be 100644 --- a/typescript/src/generator.ts +++ b/typescript/src/generator.ts @@ -87,6 +87,9 @@ export class Generator { private async readGrpcServiceConfig(map: Map) { if (map && map['grpc-service-config']) { const filename = map['grpc-service-config']; + if (!fs.existsSync(filename)) { + throw new Error(`File ${filename} cannot be opened.`); + } const content = await readFile(filename); const json = JSON.parse(content.toString()); Generator.updateDuration(json); From 531325ccb54308225e1937928511eacda3e3299b Mon Sep 17 00:00:00 2001 From: xiaozhenliugg Date: Tue, 19 Nov 2019 15:45:15 -0800 Subject: [PATCH 5/6] make paramMap a member of generator class --- typescript/src/generator.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/typescript/src/generator.ts b/typescript/src/generator.ts index 9291383be..e24dc904b 100644 --- a/typescript/src/generator.ts +++ b/typescript/src/generator.ts @@ -23,7 +23,7 @@ import { API } from './schema/api'; import { processTemplates } from './templater'; import { commonPrefix, duration } from './util'; -export interface Map { +export interface OptionsMap { [name: string]: string; } const readFile = util.promisify(fs.readFile); @@ -43,6 +43,7 @@ export class Generator { request: plugin.google.protobuf.compiler.CodeGeneratorRequest; response: plugin.google.protobuf.compiler.CodeGeneratorResponse; grpcServiceConfig: plugin.grpc.service_config.ServiceConfig; + paramMap: OptionsMap; // This field is for users passing proper publish package name like @google-cloud/text-to-speech. publishName?: string; @@ -50,6 +51,7 @@ export class Generator { this.request = plugin.google.protobuf.compiler.CodeGeneratorRequest.create(); this.response = plugin.google.protobuf.compiler.CodeGeneratorResponse.create(); this.grpcServiceConfig = plugin.grpc.service_config.ServiceConfig.create(); + this.paramMap = {}; } // Fixes gRPC service config to replace string google.protobuf.Duration @@ -73,18 +75,16 @@ export class Generator { private getParamMap(parameter: string) { // Example: "grpc-service-config=texamplejson","package-name=packageName" - const paramMap: Map = {}; const parameters = parameter.split(','); for (let param of parameters) { // remove double quote param = param.substring(1, param.length - 1); const arr = param.split('='); - paramMap[arr[0].toKebabCase()] = arr[1]; + this.paramMap[arr[0].toKebabCase()] = arr[1]; } - return paramMap; } - private async readGrpcServiceConfig(map: Map) { + private async readGrpcServiceConfig(map: OptionsMap) { if (map && map['grpc-service-config']) { const filename = map['grpc-service-config']; if (!fs.existsSync(filename)) { @@ -99,7 +99,7 @@ export class Generator { } } - private async readPublishPackageName(map: Map) { + private async readPublishPackageName(map: OptionsMap) { if (map && map['package-name']) { this.publishName = map['package-name']; } @@ -111,9 +111,9 @@ export class Generator { inputBuffer ); if (this.request.parameter) { - const map = this.getParamMap(this.request.parameter); - await this.readGrpcServiceConfig(map); - await this.readPublishPackageName(map); + this.getParamMap(this.request.parameter); + await this.readGrpcServiceConfig(this.paramMap); + await this.readPublishPackageName(this.paramMap); } } From 7c4a70054cd3a851b4243ae93dbc5d356a71db9c Mon Sep 17 00:00:00 2001 From: xiaozhenliugg Date: Tue, 19 Nov 2019 16:00:26 -0800 Subject: [PATCH 6/6] comment --- typescript/src/schema/api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/typescript/src/schema/api.ts b/typescript/src/schema/api.ts index d2f42c15e..0b17c0d70 100644 --- a/typescript/src/schema/api.ts +++ b/typescript/src/schema/api.ts @@ -32,7 +32,7 @@ export class API { hostName?: string; port?: string; mainServiceName?: string; - //This field is for users passing roper module name for system test. + // This field is for users passing proper publish package name like @google-cloud/text-to-speech. publishName: string; // oauth_scopes: plugin.google.protobuf.IServiceOptions.prototype[".google.api.oauthScopes"]; // TODO: subpackages