build(proto-loader): add full gts configuration and fix reported issues

This commit is contained in:
Natan Sągol 2019-11-08 08:09:40 +01:00
parent 9d36d8d5c7
commit 8cbda9b03c
No known key found for this signature in database
GPG Key ID: 891FD8B48DF1A5D4
6 changed files with 199 additions and 122 deletions

View File

@ -46,7 +46,7 @@
"@types/lodash.camelcase": "^4.3.4", "@types/lodash.camelcase": "^4.3.4",
"@types/node": "^10.12.5", "@types/node": "^10.12.5",
"clang-format": "^1.2.2", "clang-format": "^1.2.2",
"gts": "^0.5.3", "gts": "^1.1.0",
"typescript": "~3.3.3333" "typescript": "~3.3.3333"
}, },
"engines": { "engines": {

View File

@ -0,0 +1,5 @@
module.exports = {
proseWrap: 'always',
singleQuote: true,
trailingComma: 'es5',
};

View File

@ -24,25 +24,34 @@ import camelCase = require('lodash.camelcase');
declare module 'protobufjs' { declare module 'protobufjs' {
interface Type { interface Type {
toDescriptor(protoVersion: string): Protobuf toDescriptor(
.Message<descriptor.IDescriptorProto>&descriptor.IDescriptorProto; protoVersion: string
): Protobuf.Message<descriptor.IDescriptorProto> &
descriptor.IDescriptorProto;
} }
interface Root { interface Root {
toDescriptor(protoVersion: string): Protobuf toDescriptor(
.Message<descriptor.IFileDescriptorSet>&descriptor.IFileDescriptorSet; protoVersion: string
): Protobuf.Message<descriptor.IFileDescriptorSet> &
descriptor.IFileDescriptorSet;
} }
interface Enum { interface Enum {
toDescriptor(protoVersion: string): toDescriptor(
Protobuf.Message<descriptor.IEnumDescriptorProto>& protoVersion: string
): Protobuf.Message<descriptor.IEnumDescriptorProto> &
descriptor.IEnumDescriptorProto; descriptor.IEnumDescriptorProto;
} }
} }
export interface Serialize<T> { (value: T): Buffer; } export interface Serialize<T> {
(value: T): Buffer;
}
export interface Deserialize<T> { (bytes: Buffer): T; } export interface Deserialize<T> {
(bytes: Buffer): T;
}
export interface ProtobufTypeDefinition { export interface ProtobufTypeDefinition {
format: string; format: string;
@ -76,11 +85,16 @@ export interface ServiceDefinition {
} }
export type AnyDefinition = export type AnyDefinition =
ServiceDefinition|MessageTypeDefinition|EnumTypeDefinition; | ServiceDefinition
| MessageTypeDefinition
| EnumTypeDefinition;
export interface PackageDefinition { [index: string]: AnyDefinition; } export interface PackageDefinition {
[index: string]: AnyDefinition;
}
export type Options = Protobuf.IParseOptions&Protobuf.IConversionOptions&{ export type Options = Protobuf.IParseOptions &
Protobuf.IConversionOptions & {
includeDirs?: string[]; includeDirs?: string[];
}; };
@ -90,7 +104,7 @@ const descriptorOptions: Protobuf.IConversionOptions = {
bytes: String, bytes: String,
defaults: true, defaults: true,
oneofs: true, oneofs: true,
json: true json: true,
}; };
function joinName(baseName: string, name: string): string { function joinName(baseName: string, name: string): string {
@ -103,39 +117,48 @@ function joinName(baseName: string, name: string): string {
type HandledReflectionObject = Protobuf.Service | Protobuf.Type | Protobuf.Enum; type HandledReflectionObject = Protobuf.Service | Protobuf.Type | Protobuf.Enum;
function isHandledReflectionObject(obj: Protobuf.ReflectionObject): function isHandledReflectionObject(
obj is HandledReflectionObject { obj: Protobuf.ReflectionObject
return obj instanceof Protobuf.Service || obj instanceof Protobuf.Type || ): obj is HandledReflectionObject {
obj instanceof Protobuf.Enum; return (
obj instanceof Protobuf.Service ||
obj instanceof Protobuf.Type ||
obj instanceof Protobuf.Enum
);
} }
function isNamespaceBase(obj: Protobuf.ReflectionObject): function isNamespaceBase(
obj is Protobuf.NamespaceBase { obj: Protobuf.ReflectionObject
): obj is Protobuf.NamespaceBase {
return obj instanceof Protobuf.Namespace || obj instanceof Protobuf.Root; return obj instanceof Protobuf.Namespace || obj instanceof Protobuf.Root;
} }
function getAllHandledReflectionObjects( function getAllHandledReflectionObjects(
obj: Protobuf.ReflectionObject, obj: Protobuf.ReflectionObject,
parentName: string): Array<[string, HandledReflectionObject]> { parentName: string
): Array<[string, HandledReflectionObject]> {
const objName = joinName(parentName, obj.name); const objName = joinName(parentName, obj.name);
if (isHandledReflectionObject(obj)) { if (isHandledReflectionObject(obj)) {
return [[objName, obj]]; return [[objName, obj]];
} else { } else {
if (isNamespaceBase(obj) && typeof obj.nested !== 'undefined') { if (isNamespaceBase(obj) && typeof obj.nested !== 'undefined') {
return Object.keys(obj.nested!) return Object.keys(obj.nested!)
.map((name) => { .map(name => {
return getAllHandledReflectionObjects(obj.nested![name], objName); return getAllHandledReflectionObjects(obj.nested![name], objName);
}) })
.reduce( .reduce(
(accumulator, currentValue) => accumulator.concat(currentValue), (accumulator, currentValue) => accumulator.concat(currentValue),
[]); []
);
} }
} }
return []; return [];
} }
function createDeserializer( function createDeserializer(
cls: Protobuf.Type, options: Options): Deserialize<object> { cls: Protobuf.Type,
options: Options
): Deserialize<object> {
return function deserialize(argBuf: Buffer): object { return function deserialize(argBuf: Buffer): object {
return cls.toObject(cls.decode(argBuf), options); return cls.toObject(cls.decode(argBuf), options);
}; };
@ -149,8 +172,11 @@ function createSerializer(cls: Protobuf.Type): Serialize<object> {
} }
function createMethodDefinition( function createMethodDefinition(
method: Protobuf.Method, serviceName: string, options: Options, method: Protobuf.Method,
fileDescriptors: Buffer[]): MethodDefinition<object, object> { serviceName: string,
options: Options,
fileDescriptors: Buffer[]
): MethodDefinition<object, object> {
/* This is only ever called after the corresponding root.resolveAll(), so we /* This is only ever called after the corresponding root.resolveAll(), so we
* can assume that the resolved request and response types are non-null */ * can assume that the resolved request and response types are non-null */
const requestType: Protobuf.Type = method.resolvedRequestType!; const requestType: Protobuf.Type = method.resolvedRequestType!;
@ -166,41 +192,56 @@ function createMethodDefinition(
// TODO(murgatroid99): Find a better way to handle this // TODO(murgatroid99): Find a better way to handle this
originalName: camelCase(method.name), originalName: camelCase(method.name),
requestType: createMessageDefinition(requestType, fileDescriptors), requestType: createMessageDefinition(requestType, fileDescriptors),
responseType: createMessageDefinition(responseType, fileDescriptors) responseType: createMessageDefinition(responseType, fileDescriptors),
}; };
} }
function createServiceDefinition( function createServiceDefinition(
service: Protobuf.Service, name: string, options: Options, service: Protobuf.Service,
fileDescriptors: Buffer[]): ServiceDefinition { name: string,
options: Options,
fileDescriptors: Buffer[]
): ServiceDefinition {
const def: ServiceDefinition = {}; const def: ServiceDefinition = {};
for (const method of service.methodsArray) { for (const method of service.methodsArray) {
def[method.name] = def[method.name] = createMethodDefinition(
createMethodDefinition(method, name, options, fileDescriptors); method,
name,
options,
fileDescriptors
);
} }
return def; return def;
} }
function createMessageDefinition( function createMessageDefinition(
message: Protobuf.Type, fileDescriptors: Buffer[]): MessageTypeDefinition { message: Protobuf.Type,
const messageDescriptor: protobuf.Message<descriptor.IDescriptorProto> = fileDescriptors: Buffer[]
message.toDescriptor('proto3'); ): MessageTypeDefinition {
const messageDescriptor: protobuf.Message<
descriptor.IDescriptorProto
> = message.toDescriptor('proto3');
return { return {
format: 'Protocol Buffer 3 DescriptorProto', format: 'Protocol Buffer 3 DescriptorProto',
type: type: messageDescriptor.$type.toObject(
messageDescriptor.$type.toObject(messageDescriptor, descriptorOptions), messageDescriptor,
fileDescriptorProtos: fileDescriptors descriptorOptions
),
fileDescriptorProtos: fileDescriptors,
}; };
} }
function createEnumDefinition( function createEnumDefinition(
enumType: Protobuf.Enum, fileDescriptors: Buffer[]): EnumTypeDefinition { enumType: Protobuf.Enum,
const enumDescriptor: protobuf.Message<descriptor.IEnumDescriptorProto> = fileDescriptors: Buffer[]
enumType.toDescriptor('proto3'); ): EnumTypeDefinition {
const enumDescriptor: protobuf.Message<
descriptor.IEnumDescriptorProto
> = enumType.toDescriptor('proto3');
return { return {
format: 'Protocol Buffer 3 EnumDescriptorProto', format: 'Protocol Buffer 3 EnumDescriptorProto',
type: enumDescriptor.$type.toObject(enumDescriptor, descriptorOptions), type: enumDescriptor.$type.toObject(enumDescriptor, descriptorOptions),
fileDescriptorProtos: fileDescriptors fileDescriptorProtos: fileDescriptors,
}; };
} }
@ -212,8 +253,11 @@ function createEnumDefinition(
* EnumTypeDefinition; * EnumTypeDefinition;
*/ */
function createDefinition( function createDefinition(
obj: HandledReflectionObject, name: string, options: Options, obj: HandledReflectionObject,
fileDescriptors: Buffer[]): AnyDefinition { name: string,
options: Options,
fileDescriptors: Buffer[]
): AnyDefinition {
if (obj instanceof Protobuf.Service) { if (obj instanceof Protobuf.Service) {
return createServiceDefinition(obj, name, options, fileDescriptors); return createServiceDefinition(obj, name, options, fileDescriptors);
} else if (obj instanceof Protobuf.Type) { } else if (obj instanceof Protobuf.Type) {
@ -226,14 +270,17 @@ function createDefinition(
} }
function createPackageDefinition( function createPackageDefinition(
root: Protobuf.Root, options: Options): PackageDefinition { root: Protobuf.Root,
options: Options
): PackageDefinition {
const def: PackageDefinition = {}; const def: PackageDefinition = {};
root.resolveAll(); root.resolveAll();
const descriptorList: descriptor.IFileDescriptorProto[] = const descriptorList: descriptor.IFileDescriptorProto[] = root.toDescriptor(
root.toDescriptor('proto3').file; 'proto3'
const bufferList: Buffer[] = descriptorList.map( ).file;
value => const bufferList: Buffer[] = descriptorList.map(value =>
Buffer.from(descriptor.FileDescriptorProto.encode(value).finish())); Buffer.from(descriptor.FileDescriptorProto.encode(value).finish())
);
for (const [name, obj] of getAllHandledReflectionObjects(root, '')) { for (const [name, obj] of getAllHandledReflectionObjects(root, '')) {
def[name] = createDefinition(obj, name, options, bufferList); def[name] = createDefinition(obj, name, options, bufferList);
} }
@ -284,28 +331,33 @@ function addIncludePathResolver(root: Protobuf.Root, includePaths: string[]) {
* @param options.includeDirs Paths to search for imported `.proto` files. * @param options.includeDirs Paths to search for imported `.proto` files.
*/ */
export function load( export function load(
filename: string|string[], options?: Options): Promise<PackageDefinition> { filename: string | string[],
options?: Options
): Promise<PackageDefinition> {
const root: Protobuf.Root = new Protobuf.Root(); const root: Protobuf.Root = new Protobuf.Root();
options = options || {}; options = options || {};
if (!!options.includeDirs) { if (!!options.includeDirs) {
if (!(Array.isArray(options.includeDirs))) { if (!Array.isArray(options.includeDirs)) {
return Promise.reject( return Promise.reject(
new Error('The includeDirs option must be an array')); new Error('The includeDirs option must be an array')
);
} }
addIncludePathResolver(root, options.includeDirs as string[]); addIncludePathResolver(root, options.includeDirs as string[]);
} }
return root.load(filename, options).then((loadedRoot) => { return root.load(filename, options).then(loadedRoot => {
loadedRoot.resolveAll(); loadedRoot.resolveAll();
return createPackageDefinition(root, options!); return createPackageDefinition(root, options!);
}); });
} }
export function loadSync( export function loadSync(
filename: string|string[], options?: Options): PackageDefinition { filename: string | string[],
options?: Options
): PackageDefinition {
const root: Protobuf.Root = new Protobuf.Root(); const root: Protobuf.Root = new Protobuf.Root();
options = options || {}; options = options || {};
if (!!options.includeDirs) { if (!!options.includeDirs) {
if (!(Array.isArray(options.includeDirs))) { if (!Array.isArray(options.includeDirs)) {
throw new Error('The includeDirs option must be an array'); throw new Error('The includeDirs option must be an array');
} }
addIncludePathResolver(root, options.includeDirs as string[]); addIncludePathResolver(root, options.includeDirs as string[]);
@ -321,13 +373,18 @@ export function loadSync(
// and wrappers. compiler/plugin is excluded in Protobuf.js and here. // and wrappers. compiler/plugin is excluded in Protobuf.js and here.
const wellKnownProtos = ['api', 'descriptor', 'source_context', 'type']; const wellKnownProtos = ['api', 'descriptor', 'source_context', 'type'];
const sourceDir = path.join( const sourceDir = path.join(
path.dirname(require.resolve('protobufjs')), 'google', 'protobuf'); path.dirname(require.resolve('protobufjs')),
'google',
'protobuf'
);
for (const proto of wellKnownProtos) { for (const proto of wellKnownProtos) {
const file = path.join(sourceDir, `${proto}.proto`); const file = path.join(sourceDir, `${proto}.proto`);
const descriptor = Protobuf.loadSync(file).toJSON(); const descriptor = Protobuf.loadSync(file).toJSON();
// @ts-ignore Protobuf.common(
Protobuf.common(proto, descriptor.nested.google.nested); proto,
(descriptor.nested!.google as Protobuf.INamespace).nested!
);
} }
} }

View File

@ -23,17 +23,17 @@ import * as proto_loader from '../src/index';
const TEST_PROTO_DIR = `${__dirname}/../../test_protos/`; const TEST_PROTO_DIR = `${__dirname}/../../test_protos/`;
type TypeDefinition = type TypeDefinition =
proto_loader.EnumTypeDefinition|proto_loader.MessageTypeDefinition; | proto_loader.EnumTypeDefinition
| proto_loader.MessageTypeDefinition;
function isTypeObject(obj: proto_loader.AnyDefinition): obj is TypeDefinition { function isTypeObject(obj: proto_loader.AnyDefinition): obj is TypeDefinition {
return 'format' in obj; return 'format' in obj;
} }
describe('Descriptor types', () => { describe('Descriptor types', () => {
it('Should be output for each enum', (done) => { it('Should be output for each enum', done => {
proto_loader.load(`${TEST_PROTO_DIR}/enums.proto`) proto_loader.load(`${TEST_PROTO_DIR}/enums.proto`).then(
.then( packageDefinition => {
(packageDefinition) => {
assert('Enum1' in packageDefinition); assert('Enum1' in packageDefinition);
assert(isTypeObject(packageDefinition.Enum1)); assert(isTypeObject(packageDefinition.Enum1));
// Need additional check because compiler doesn't understand // Need additional check because compiler doesn't understand
@ -41,7 +41,9 @@ describe('Descriptor types', () => {
if (isTypeObject(packageDefinition.Enum1)) { if (isTypeObject(packageDefinition.Enum1)) {
const enum1Def: TypeDefinition = packageDefinition.Enum1; const enum1Def: TypeDefinition = packageDefinition.Enum1;
assert.strictEqual( assert.strictEqual(
enum1Def.format, 'Protocol Buffer 3 EnumDescriptorProto'); enum1Def.format,
'Protocol Buffer 3 EnumDescriptorProto'
);
} }
assert('Enum2' in packageDefinition); assert('Enum2' in packageDefinition);
@ -51,25 +53,28 @@ describe('Descriptor types', () => {
if (isTypeObject(packageDefinition.Enum2)) { if (isTypeObject(packageDefinition.Enum2)) {
const enum2Def: TypeDefinition = packageDefinition.Enum2; const enum2Def: TypeDefinition = packageDefinition.Enum2;
assert.strictEqual( assert.strictEqual(
enum2Def.format, 'Protocol Buffer 3 EnumDescriptorProto'); enum2Def.format,
'Protocol Buffer 3 EnumDescriptorProto'
);
} }
done(); done();
}, },
(error) => { error => {
done(error); done(error);
}
);
}); });
}); it('Should be output for each message', done => {
it('Should be output for each message', (done) => { proto_loader.load(`${TEST_PROTO_DIR}/messages.proto`).then(
proto_loader.load(`${TEST_PROTO_DIR}/messages.proto`) packageDefinition => {
.then(
(packageDefinition) => {
assert('LongValues' in packageDefinition); assert('LongValues' in packageDefinition);
assert(isTypeObject(packageDefinition.LongValues)); assert(isTypeObject(packageDefinition.LongValues));
if (isTypeObject(packageDefinition.LongValues)) { if (isTypeObject(packageDefinition.LongValues)) {
const longValuesDef: TypeDefinition = const longValuesDef: TypeDefinition = packageDefinition.LongValues;
packageDefinition.LongValues;
assert.strictEqual( assert.strictEqual(
longValuesDef.format, 'Protocol Buffer 3 DescriptorProto'); longValuesDef.format,
'Protocol Buffer 3 DescriptorProto'
);
} }
assert('SequenceValues' in packageDefinition); assert('SequenceValues' in packageDefinition);
@ -79,13 +84,15 @@ describe('Descriptor types', () => {
packageDefinition.SequenceValues; packageDefinition.SequenceValues;
assert.strictEqual( assert.strictEqual(
sequenceValuesDef.format, sequenceValuesDef.format,
'Protocol Buffer 3 DescriptorProto'); 'Protocol Buffer 3 DescriptorProto'
);
} }
done(); done();
}, },
(error) => { error => {
done(error); done(error);
}); }
);
}); });
it('Can use well known Google protos', () => { it('Can use well known Google protos', () => {

View File

@ -5,7 +5,7 @@
"outDir": "build" "outDir": "build"
}, },
"include": [ "include": [
"src/*.ts", "src/**/*.ts",
"test/*.ts" "test/**/*.ts"
] ]
} }

View File

@ -0,0 +1,8 @@
{
"extends": "gts/tslint.json",
"linterOptions": {
"exclude": [
"**/*.json"
]
}
}