From 90177c69cb7ab0c399c6f12fb6a2d63319a9c474 Mon Sep 17 00:00:00 2001 From: Xiaozhen Liu Date: Fri, 31 Jan 2020 10:52:54 -0800 Subject: [PATCH] feat: support proto packages in different namespace (#219) * logic for parsing proto package * add unit test * lint fix * lint * feedback * no else if --- typescript/src/schema/api.ts | 12 +++++------ typescript/src/schema/naming.ts | 35 ++++++++++++++++++++++++++++++--- typescript/test/unit/naming.ts | 26 ++++++++++++++++++++++-- 3 files changed, 61 insertions(+), 12 deletions(-) diff --git a/typescript/src/schema/api.ts b/typescript/src/schema/api.ts index 154b6c7db..73c722226 100644 --- a/typescript/src/schema/api.ts +++ b/typescript/src/schema/api.ts @@ -16,9 +16,10 @@ import * as plugin from '../../../pbjs-genfiles/plugin'; import * as fs from 'fs'; import * as path from 'path'; -import { Naming } from './naming'; +import { Naming, Options as namingOptions } from './naming'; import { Proto, MessagesMap } from './proto'; import { ResourceDatabase, ResourceDescriptor } from './resourceDatabase'; +import { Options } from 'yargs-parser'; const googleGaxLocation = path.dirname(require.resolve('google-gax')); const gaxProtosLocation = path.join(googleGaxLocation, '..', '..', 'protos'); @@ -41,16 +42,13 @@ export class API { constructor( fileDescriptors: plugin.google.protobuf.IFileDescriptorProto[], packageName: string, - options: { - grpcServiceConfig: plugin.grpc.service_config.ServiceConfig; - publishName?: string; - mainServiceName?: string; - } + options: namingOptions ) { this.naming = new Naming( fileDescriptors.filter( fd => fd.package && fd.package.startsWith(packageName) - ) + ), + options ); // users specify the actual package name, if not, set it to product name. this.publishName = diff --git a/typescript/src/schema/naming.ts b/typescript/src/schema/naming.ts index ec67ed532..fcbf1b834 100644 --- a/typescript/src/schema/naming.ts +++ b/typescript/src/schema/naming.ts @@ -15,6 +15,11 @@ import * as plugin from '../../../pbjs-genfiles/plugin'; import { commonPrefix } from '../util'; +export interface Options { + grpcServiceConfig: plugin.grpc.service_config.ServiceConfig; + publishName?: string; + mainServiceName?: string; +} export class Naming { name: string; namespace: string[]; @@ -22,7 +27,12 @@ export class Naming { productName: string; protoPackage: string; - constructor(fileDescriptors: plugin.google.protobuf.IFileDescriptorProto[]) { + constructor( + fileDescriptors: plugin.google.protobuf.IFileDescriptorProto[], + options?: Options + ) { + let rootPackage = ''; + const mainServiceName = options ? options.mainServiceName : ''; const protoPackages = fileDescriptors .filter(fd => fd.service && fd.service.length > 0) // LRO is an exception: it's a service but we don't generate any code for it @@ -31,10 +41,17 @@ export class Naming { const prefix = commonPrefix(protoPackages); // common prefix must either end with `.`, or be equal to at least one of // the packages' prefix - if (!prefix.endsWith('.') && !protoPackages.some(pkg => pkg === prefix)) { + const invalidPrefix = + !prefix.endsWith('.') && !protoPackages.some(pkg => pkg === prefix); + if (invalidPrefix && mainServiceName) { + rootPackage = this.checkServiceInPackage(protoPackages, mainServiceName); + } + if (invalidPrefix && !mainServiceName) { throw new Error('Protos provided have different proto packages.'); } - const rootPackage = prefix.replace(/\.$/, ''); + if (!invalidPrefix) { + rootPackage = prefix.replace(/\.$/, ''); + } const segments = rootPackage.split('.'); if (!segments || segments.length < 2) { throw new Error(`Cannot parse package name ${rootPackage}.`); @@ -61,4 +78,16 @@ export class Naming { ); } } + + private checkServiceInPackage( + protoPackages: string[], + mainServiceName: string + ) { + for (const packageName of protoPackages) { + if (packageName.indexOf(mainServiceName.toLowerCase()) !== -1) { + return packageName; + } + } + return ''; + } } diff --git a/typescript/test/unit/naming.ts b/typescript/test/unit/naming.ts index 769cdd094..e38f6a6c6 100644 --- a/typescript/test/unit/naming.ts +++ b/typescript/test/unit/naming.ts @@ -15,7 +15,7 @@ import * as assert from 'assert'; import { describe, it } from 'mocha'; import * as plugin from '../../../pbjs-genfiles/plugin'; -import { Naming } from '../../src/schema/naming'; +import { Naming, Options } from '../../src/schema/naming'; describe('schema/naming.ts', () => { it('parses name correctly', () => { @@ -113,7 +113,7 @@ describe('schema/naming.ts', () => { }); }); - it('fails if no common package', () => { + it('fails if no common package, no service-name', () => { const descriptor1 = new plugin.google.protobuf.FileDescriptorProto(); const descriptor2 = new plugin.google.protobuf.FileDescriptorProto(); descriptor1.package = 'namespace1.service.v1beta1'; @@ -125,6 +125,28 @@ describe('schema/naming.ts', () => { }); }); + it('parse name correctly if no common package, but service-name specified', () => { + const descriptor1 = new plugin.google.protobuf.FileDescriptorProto(); + const descriptor2 = new plugin.google.protobuf.FileDescriptorProto(); + descriptor1.package = 'namespace1.service1.v1beta1'; + descriptor1.service = [new plugin.google.protobuf.ServiceDescriptorProto()]; + descriptor2.package = 'namespace2.service2.v1beta1'; + descriptor2.service = [new plugin.google.protobuf.ServiceDescriptorProto()]; + const serviceConfig = new plugin.grpc.service_config.ServiceConfig(); + const options: Options = { + grpcServiceConfig: serviceConfig, + mainServiceName: 'service1', + }; + assert.throws(() => { + const naming = new Naming([descriptor1, descriptor2], options); + assert.strictEqual(naming.name, 'service1'); + assert.strictEqual(naming.productName, 'service1'); + assert.strictEqual(naming.version, 'v1beta1'); + assert.strictEqual(naming.namespace, 'namespace1'); + assert.strictEqual(naming.protoPackage, 'namespace1.service1.v1beta1'); + }); + }); + it('fails if different versions', () => { const descriptor1 = new plugin.google.protobuf.FileDescriptorProto(); const descriptor2 = new plugin.google.protobuf.FileDescriptorProto();