diff --git a/gulpfile.ts b/gulpfile.ts index 1c116d9b..969fb894 100644 --- a/gulpfile.ts +++ b/gulpfile.ts @@ -54,21 +54,22 @@ function loadGulpTasksWithPrefix(path: string, prefix: string) { ['./packages/grpc-native/gulpfile', 'native'], ['./packages/grpc-native-core/gulpfile', 'native.core'], ['./packages/grpc-surface/gulpfile', 'surface'], - ['./test/gulpfile', 'internal.test'] + ['./packages/grpc-protobufjs/gulpfile', 'protobuf'], + ['./test/gulpfile', 'internal.test'], ].forEach((args) => loadGulpTasksWithPrefix(args[0], args[1])); const root = __dirname; gulp.task('install.all', 'Install dependencies for all subdirectory packages', - ['js.install', 'js.core.install', 'native.core.install', 'surface.install', 'health-check.install', 'internal.test.install']); + ['js.install', 'js.core.install', 'native.core.install', 'surface.install', 'health-check.install', 'protobuf.install', 'internal.test.install']); gulp.task('install.all.windows', 'Install dependencies for all subdirectory packages for MS Windows', - ['js.core.install', 'native.core.install.windows', 'surface.install', 'health-check.install', 'internal.test.install']); + ['js.core.install', 'native.core.install.windows', 'surface.install', 'health-check.install', 'protobuf.install', 'internal.test.install']); gulp.task('lint', 'Emit linting errors in source and test files', ['js.core.lint', 'native.core.lint']); -gulp.task('build', 'Build packages', ['js.compile', 'js.core.compile', 'native.core.build']); +gulp.task('build', 'Build packages', ['js.compile', 'js.core.compile', 'native.core.build', 'protobuf.compile']); gulp.task('link.core', 'Add links to core packages without rebuilding', ['js.link.add', 'native.link.add']); @@ -91,11 +92,11 @@ gulp.task('setup.windows', 'One-time setup for a clean repository for MS Windows runSequence('install.all.windows', 'link', callback); }); -gulp.task('clean', 'Delete generated files', ['js.core.clean', 'native.core.clean']); +gulp.task('clean', 'Delete generated files', ['js.core.clean', 'native.core.clean', 'protobuf.clean']); gulp.task('clean.all', 'Delete all files created by tasks', ['js.core.clean.all', 'native.core.clean.all', 'health-check.clean.all', - 'internal.test.clean.all', 'js.clean.all', 'native.clean.all']); + 'internal.test.clean.all', 'js.clean.all', 'native.clean.all', 'protobuf.clean.all']); gulp.task('native.test.only', 'Run tests of native code without rebuilding anything', ['native.core.test', 'internal.test.test', 'health-check.test']); diff --git a/packages/grpc-protobufjs/gulpfile.ts b/packages/grpc-protobufjs/gulpfile.ts new file mode 100644 index 00000000..550ce364 --- /dev/null +++ b/packages/grpc-protobufjs/gulpfile.ts @@ -0,0 +1,60 @@ +/* + * Copyright 2017 gRPC authors. + * + * 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 + * + * http://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. + * + */ + +import * as _gulp from 'gulp'; +import * as help from 'gulp-help'; + +import * as fs from 'fs'; +import * as mocha from 'gulp-mocha'; +import * as path from 'path'; +import * as execa from 'execa'; + +// gulp-help monkeypatches tasks to have an additional description parameter +const gulp = help(_gulp); + +Error.stackTraceLimit = Infinity; + +const protojsDir = __dirname; +const tslintPath = path.resolve(protojsDir, 'node_modules/google-ts-style/tslint.json'); +const tsconfigPath = path.resolve(protojsDir, 'tsconfig.json'); +const outDir = path.resolve(protojsDir, 'build'); +const srcDir = path.resolve(protojsDir, 'src'); +const testDir = path.resolve(protojsDir, 'test'); + +const execNpmVerb = (verb: string, ...args: string[]) => + execa('npm', [verb, ...args], {cwd: protojsDir, stdio: 'inherit'}); +const execNpmCommand = execNpmVerb.bind(null, 'run'); + +gulp.task('install', 'Install native core dependencies', () => + execNpmVerb('install', '--unsafe-perm')); + +/** + * Runs tslint on files in src/, with linting rules defined in tslint.json. + */ +gulp.task('lint', 'Emits linting errors found in src/ and test/.', () => + execNpmCommand('check')); + +gulp.task('clean', 'Deletes transpiled code.', ['install'], + () => execNpmCommand('clean')); + +gulp.task('clean.all', 'Deletes all files added by targets', ['clean']); + +/** + * Transpiles TypeScript files in src/ to JavaScript according to the settings + * found in tsconfig.json. + */ +gulp.task('compile', 'Transpiles src/.', () => execNpmCommand('compile')); diff --git a/packages/grpc-protobufjs/index.js b/packages/grpc-protobufjs/index.js deleted file mode 100644 index 5e7ede99..00000000 --- a/packages/grpc-protobufjs/index.js +++ /dev/null @@ -1,139 +0,0 @@ -/** - * @license - * Copyright 2017 gRPC authors. - * - * 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 - * - * http://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. - * - */ - -'use strict'; - -var path = require('path'); - -var _ = require('lodash'); -var ProtoBuf = require('protobufjs'); - -module.exports = function(grpc) { - - let exports = {}; - - const protobuf_js_5_common = require('protobuf_js_5_common')(grpc); - const protobuf_js_6_common = require('protobuf_js_6_common')(grpc); - - /** - * Default options for loading proto files into gRPC - * @alias grpc~defaultLoadOptions - */ - const defaultGrpcOptions = { - convertFieldsToCamelCase: false, - binaryAsBase64: false, - longsAsStrings: true, - enumsAsStrings: true - }; - - /** - * Load a ProtoBuf.js object as a gRPC object. The options object can provide - * the following options: - * - binaryAsBase64: deserialize bytes values as base64 strings instead of - * Buffers. Defaults to false - * - longsAsStrings: deserialize long values as strings instead of objects. - * Defaults to true - * - enumsAsStrings: deserialize enum values as strings instead of numbers. - * Defaults to true - * - protobufjsVersion: Available values are 5, 6, and 'detect'. 5 and 6 - * respectively indicate that an object from the corresponding version of - * ProtoBuf.js is provided in the value argument. If the option is 'detect', - * gRPC will guess what the version is based on the structure of the value. - * Defaults to 'detect'. - * @param {Object} value The ProtoBuf.js reflection object to load - * @param {Object=} options Options to apply to the loaded file - * @return {Object} The resulting gRPC object - */ - exports.loadObject = function loadObject(value, options) { - options = _.defaults(options, defaultGrpcOptions); - options = _.defaults(options, {'protobufjsVersion': 'detect'}); - var protobufjsVersion; - if (options.protobufjsVersion === 'detect') { - if (protobuf_js_6_common.isProbablyProtobufJs6(value)) { - protobufjsVersion = 6; - } else if (protobuf_js_5_common.isProbablyProtobufJs5(value)) { - protobufjsVersion = 5; - } else { - var error_message = 'Could not detect ProtoBuf.js version. Please ' + - 'specify the version number with the "protobufjs_version" option'; - throw new Error(error_message); - } - } else { - protobufjsVersion = options.protobufjsVersion; - } - switch (protobufjsVersion) { - case 6: return protobuf_js_6_common.loadObject(value, options); - case 5: return protobuf_js_5_common.loadObject(value, options); - default: - throw new Error('Unrecognized protobufjsVersion', protobufjsVersion); - } - }; - - var loadObject = exports.loadObject; - - function applyProtoRoot(filename, root) { - if (_.isString(filename)) { - return filename; - } - filename.root = path.resolve(filename.root) + '/'; - root.resolvePath = function(originPath, importPath, alreadyNormalized) { - return ProtoBuf.util.path.resolve(filename.root, - importPath, - alreadyNormalized); - }; - return filename.file; - } - - /** - * Load a gRPC object from a .proto file. The options object can provide the - * following options: - * - convertFieldsToCamelCase: Load this file with field names in camel case - * instead of their original case - * - binaryAsBase64: deserialize bytes values as base64 strings instead of - * Buffers. Defaults to false - * - longsAsStrings: deserialize long values as strings instead of objects. - * Defaults to true - * - enumsAsStrings: deserialize enum values as strings instead of numbers. - * Defaults to true - * - deprecatedArgumentOrder: Use the beta method argument order for client - * methods, with optional arguments after the callback. Defaults to false. - * This option is only a temporary stopgap measure to smooth an API breakage. - * It is deprecated, and new code should not use it. - * @param {string|{root: string, file: string}} filename The file to load - * @param {string=} format The file format to expect. Must be either 'proto' or - * 'json'. Defaults to 'proto' - * @param {Object=} options Options to apply to the loaded file - * @return {Object} The resulting gRPC object - */ - exports.load = function load(filename, format, options) { - /* Note: format is currently unused, because the API for loading a proto - file or a JSON file is identical in Protobuf.js 6. In the future, there is - still the possibility of adding other formats that would be loaded - differently */ - options = _.defaults(options, defaultGrpcOptions); - options.protobufjs_version = 6; - var root = new ProtoBuf.Root(); - var parse_options = {keepCase: !options.convertFieldsToCamelCase}; - return loadObject(root.loadSync(applyProtoRoot(filename, root), - parse_options), - options); - }; - - return exports; - -}; diff --git a/packages/grpc-protobufjs/package.json b/packages/grpc-protobufjs/package.json index 60b3dddd..13eb7c01 100644 --- a/packages/grpc-protobufjs/package.json +++ b/packages/grpc-protobufjs/package.json @@ -2,9 +2,19 @@ "name": "@grpc/protobufjs", "version": "1.0.0", "description": "", - "main": "index.js", + "main": "build/src/index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "build": "npm run compile", + "clean": "gts clean", + "compile": "tsc -p .", + "format": "clang-format -i -style=\"{Language: JavaScript, BasedOnStyle: Google, ColumnLimit: 80}\" src/*.ts test/*.ts", + "lint": "tslint -c node_modules/google-ts-style/tslint.json -p . -t codeFrame --type-check", + "prepare": "npm run compile", + "test": "gulp test", + "check": "gts check", + "fix": "gts fix", + "pretest": "npm run compile", + "posttest": "npm run check" }, "repository": { "type": "git", @@ -15,8 +25,15 @@ "bugs": { "url": "https://github.com/grpc/grpc-node/issues" }, + "files": [ + "build/src/*.js" + ], "dependencies": { + "@types/node": "^9.4.6", + "clang-format": "^1.2.2", + "gts": "^0.5.3", "lodash": "^4.17.4", - "protobufjs": "^6.8.0" + "protobufjs": "^6.8.5", + "typescript": "~2.7.2" } } diff --git a/packages/grpc-protobufjs/protobuf_js_5_common.js b/packages/grpc-protobufjs/protobuf_js_5_common.js deleted file mode 100644 index 141852f6..00000000 --- a/packages/grpc-protobufjs/protobuf_js_5_common.js +++ /dev/null @@ -1,177 +0,0 @@ -/** - * @license - * Copyright 2017 gRPC authors. - * - * 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 - * - * http://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. - * - */ - -/** - * @module - * @private - */ - -'use strict'; - -var _ = require('lodash'); - -module.exports = function(grpc) { - - let exports = {}; - - /** - * Get a function that deserializes a specific type of protobuf. - * @param {function()} cls The constructor of the message type to deserialize - * @param {bool=} binaryAsBase64 Deserialize bytes fields as base64 strings - * instead of Buffers. Defaults to false - * @param {bool=} longsAsStrings Deserialize long values as strings instead of - * objects. Defaults to true - * @return {function(Buffer):cls} The deserialization function - */ - exports.deserializeCls = function deserializeCls(cls, options) { - /** - * Deserialize a buffer to a message object - * @param {Buffer} arg_buf The buffer to deserialize - * @return {cls} The resulting object - */ - return function deserialize(arg_buf) { - // Convert to a native object with binary fields as Buffers (first argument) - // and longs as strings (second argument) - return cls.decode(arg_buf).toRaw(options.binaryAsBase64, - options.longsAsStrings); - }; - }; - - var deserializeCls = exports.deserializeCls; - - /** - * Get a function that serializes objects to a buffer by protobuf class. - * @param {function()} Cls The constructor of the message type to serialize - * @return {function(Cls):Buffer} The serialization function - */ - exports.serializeCls = function serializeCls(Cls) { - /** - * Serialize an object to a Buffer - * @param {Object} arg The object to serialize - * @return {Buffer} The serialized object - */ - return function serialize(arg) { - return new Buffer(new Cls(arg).encode().toBuffer()); - }; - }; - - var serializeCls = exports.serializeCls; - - /** - * Get the fully qualified (dotted) name of a ProtoBuf.Reflect value. - * @param {ProtoBuf.Reflect.Namespace} value The value to get the name of - * @return {string} The fully qualified name of the value - */ - exports.fullyQualifiedName = function fullyQualifiedName(value) { - if (value === null || value === undefined) { - return ''; - } - var name = value.name; - var parent_name = fullyQualifiedName(value.parent); - if (parent_name !== '') { - name = parent_name + '.' + name; - } - return name; - }; - - var fullyQualifiedName = exports.fullyQualifiedName; - - /** - * Return a map from method names to method attributes for the service. - * @param {ProtoBuf.Reflect.Service} service The service to get attributes for - * @param {Object=} options Options to apply to these attributes - * @return {Object} The attributes map - */ - exports.getProtobufServiceAttrs = function getProtobufServiceAttrs(service, - options) { - var prefix = '/' + fullyQualifiedName(service) + '/'; - var binaryAsBase64, longsAsStrings; - if (options) { - binaryAsBase64 = options.binaryAsBase64; - longsAsStrings = options.longsAsStrings; - } - /* This slightly awkward construction is used to make sure we only use - lodash@3.10.1-compatible functions. A previous version used - _.fromPairs, which would be cleaner, but was introduced in lodash - version 4 */ - return _.zipObject(_.map(service.children, function(method) { - return _.camelCase(method.name); - }), _.map(service.children, function(method) { - return { - originalName: method.name, - path: prefix + method.name, - requestStream: method.requestStream, - responseStream: method.responseStream, - requestType: method.resolvedRequestType, - responseType: method.resolvedResponseType, - requestSerialize: serializeCls(method.resolvedRequestType.build()), - requestDeserialize: deserializeCls(method.resolvedRequestType.build(), - options), - responseSerialize: serializeCls(method.resolvedResponseType.build()), - responseDeserialize: deserializeCls(method.resolvedResponseType.build(), - options) - }; - })); - }; - - var getProtobufServiceAttrs = exports.getProtobufServiceAttrs; - - /** - * Load a gRPC object from an existing ProtoBuf.Reflect object. - * @param {ProtoBuf.Reflect.Namespace} value The ProtoBuf object to load. - * @param {Object=} options Options to apply to the loaded object - * @return {Object} The resulting gRPC object - */ - exports.loadObject = function loadObject(value, options) { - var result = {}; - if (!value) { - return value; - } - if (value.hasOwnProperty('ns')) { - return loadObject(value.ns, options); - } - if (value.className === 'Namespace') { - _.each(value.children, function(child) { - result[child.name] = loadObject(child, options); - }); - return result; - } else if (value.className === 'Service') { - return grpc.makeGenericClientConstructor(getProtobufServiceAttrs(value, options), - options); - } else if (value.className === 'Message' || value.className === 'Enum') { - return value.build(); - } else { - return value; - } - }; - - /** - * The primary purpose of this method is to distinguish between reflection - * objects from different versions of ProtoBuf.js. This is just a heuristic, - * checking for properties that are (currently) specific to this version of - * ProtoBuf.js - * @param {Object} obj The object to check - * @return {boolean} Whether the object appears to be a Protobuf.js 5 - * ReflectionObject - */ - exports.isProbablyProtobufJs5 = function isProbablyProtobufJs5(obj) { - return _.isArray(obj.children) && (typeof obj.build === 'function'); - }; - - return exports; -}; diff --git a/packages/grpc-protobufjs/protobuf_js_6_common.js b/packages/grpc-protobufjs/protobuf_js_6_common.js deleted file mode 100644 index 76241994..00000000 --- a/packages/grpc-protobufjs/protobuf_js_6_common.js +++ /dev/null @@ -1,166 +0,0 @@ -/** - * @license - * Copyright 2017 gRPC authors. - * - * 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 - * - * http://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. - * - */ - -/** - * @module - * @private - */ - -'use strict'; - -var _ = require('lodash'); - -module.exports = function(grpc) { - - let exports = {}; - - /** - * Get a function that deserializes a specific type of protobuf. - * @param {function()} cls The constructor of the message type to deserialize - * @param {bool=} binaryAsBase64 Deserialize bytes fields as base64 strings - * instead of Buffers. Defaults to false - * @param {bool=} longsAsStrings Deserialize long values as strings instead of - * objects. Defaults to true - * @return {function(Buffer):cls} The deserialization function - */ - exports.deserializeCls = function deserializeCls(cls, options) { - var conversion_options = { - defaults: true, - bytes: options.binaryAsBase64 ? String : Buffer, - longs: options.longsAsStrings ? String : null, - enums: options.enumsAsStrings ? String : null, - oneofs: true - }; - /** - * Deserialize a buffer to a message object - * @param {Buffer} arg_buf The buffer to deserialize - * @return {cls} The resulting object - */ - return function deserialize(arg_buf) { - return cls.toObject(cls.decode(arg_buf), conversion_options); - }; - }; - - var deserializeCls = exports.deserializeCls; - - /** - * Get a function that serializes objects to a buffer by protobuf class. - * @param {function()} Cls The constructor of the message type to serialize - * @return {function(Cls):Buffer} The serialization function - */ - exports.serializeCls = function serializeCls(cls) { - /** - * Serialize an object to a Buffer - * @param {Object} arg The object to serialize - * @return {Buffer} The serialized object - */ - return function serialize(arg) { - var message = cls.fromObject(arg); - return cls.encode(message).finish(); - }; - }; - - var serializeCls = exports.serializeCls; - - /** - * Get the fully qualified (dotted) name of a ProtoBuf.Reflect value. - * @param {ProtoBuf.ReflectionObject} value The value to get the name of - * @return {string} The fully qualified name of the value - */ - exports.fullyQualifiedName = function fullyQualifiedName(value) { - if (value === null || value === undefined) { - return ''; - } - var name = value.name; - var parent_fqn = fullyQualifiedName(value.parent); - if (parent_fqn !== '') { - name = parent_fqn + '.' + name; - } - return name; - }; - - var fullyQualifiedName = exports.fullyQualifiedName; - - /** - * Return a map from method names to method attributes for the service. - * @param {ProtoBuf.Service} service The service to get attributes for - * @param {Object=} options Options to apply to these attributes - * @return {Object} The attributes map - */ - exports.getProtobufServiceAttrs = function getProtobufServiceAttrs(service, - options) { - var prefix = '/' + fullyQualifiedName(service) + '/'; - service.resolveAll(); - return _.zipObject(_.map(service.methods, function(method) { - return _.camelCase(method.name); - }), _.map(service.methods, function(method) { - return { - originalName: method.name, - path: prefix + method.name, - requestStream: !!method.requestStream, - responseStream: !!method.responseStream, - requestType: method.resolvedRequestType, - responseType: method.resolvedResponseType, - requestSerialize: serializeCls(method.resolvedRequestType), - requestDeserialize: deserializeCls(method.resolvedRequestType, options), - responseSerialize: serializeCls(method.resolvedResponseType), - responseDeserialize: deserializeCls(method.resolvedResponseType, options) - }; - })); - }; - - var getProtobufServiceAttrs = exports.getProtobufServiceAttrs; - - exports.loadObject = function loadObject(value, options) { - var result = {}; - if (!value) { - return value; - } - if (value.hasOwnProperty('methods')) { - // It's a service object - var service_attrs = getProtobufServiceAttrs(value, options); - return grpc..makeGenericClientConstructor(service_attrs); - } - - if (value.hasOwnProperty('nested')) { - // It's a namespace or root object - _.each(value.nested, function(nested, name) { - result[name] = loadObject(nested, options); - }); - return result; - } - - // Otherwise, it's not something we need to change - return value; - }; - - /** - * The primary purpose of this method is to distinguish between reflection - * objects from different versions of ProtoBuf.js. This is just a heuristic, - * checking for properties that are (currently) specific to this version of - * ProtoBuf.js - * @param {Object} obj The object to check - * @return {boolean} Whether the object appears to be a Protobuf.js 6 - * ReflectionObject - */ - exports.isProbablyProtobufJs6 = function isProbablyProtobufJs6(obj) { - return (typeof obj.root === 'object') && (typeof obj.resolve === 'function'); - }; - - return exports; -}; diff --git a/packages/grpc-protobufjs/src/index.ts b/packages/grpc-protobufjs/src/index.ts new file mode 100644 index 00000000..22083f98 --- /dev/null +++ b/packages/grpc-protobufjs/src/index.ts @@ -0,0 +1,163 @@ +/** + * @license + * Copyright 2018 gRPC authors. + * + * 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 + * + * http://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. + * + */ +import * as Protobuf from 'protobufjs'; +import * as fs from 'fs'; +import * as path from 'path'; + +export interface Serialize { + (value: T): Buffer; +} + +export interface Deserialize { + (bytes: Buffer): T; +} + +export interface MethodDefinition { + path: string; + requestStream: boolean; + responseStream: boolean; + requestSerialize: Serialize; + responseSerialize: Serialize; + requestDeserialize: Deserialize; + responseDeserialize: Deserialize; +} + +export interface ServiceDefinition { + [index: string]: MethodDefinition; +} + +export interface PackageDefinition { + [index: string]: ServiceDefinition; +} + +export type Options = Protobuf.IParseOptions & Protobuf.IConversionOptions & { + include?: string[]; +}; + +function joinName(baseName: string, name: string): string { + if (baseName === '') { + return name; + } else { + return baseName + '.' + name; + } +} + +function getAllServices(obj: Protobuf.NamespaceBase, parentName: string): Array<[string, Protobuf.Service]> { + const objName = joinName(parentName, obj.name); + if (obj.hasOwnProperty('methods')) { + return [[objName, obj as Protobuf.Service]]; + } else { + return obj.nestedArray.map((child) => { + if (child.hasOwnProperty('nested')) { + return getAllServices(child as Protobuf.NamespaceBase, objName); + } else { + return []; + } + }).reduce((accumulator, currentValue) => accumulator.concat(currentValue), []); + } +} + +function createDeserializer(cls: Protobuf.Type, options: Options): Deserialize { + return function deserialize(argBuf: Buffer): object { + return cls.toObject(cls.decode(argBuf), options); + }; +} + +function createSerializer(cls: Protobuf.Type): Serialize { + return function serialize(arg: object): Buffer { + const message = cls.fromObject(arg); + return cls.encode(message).finish() as Buffer; + }; +} + +function createMethodDefinition(method: Protobuf.Method, serviceName: string, options: Options): MethodDefinition { + return { + path: '/' + serviceName + '/' + method.name, + requestStream: !!method.requestStream, + responseStream: !!method.responseStream, + requestSerialize: createSerializer(method.resolvedRequestType as Protobuf.Type), + requestDeserialize: createDeserializer(method.resolvedRequestType as Protobuf.Type, options), + responseSerialize: createSerializer(method.resolvedResponseType as Protobuf.Type), + responseDeserialize: createDeserializer(method.resolvedResponseType as Protobuf.Type, options) + }; +} + +function createServiceDefinition(service: Protobuf.Service, name: string, options: Options): ServiceDefinition { + const def: ServiceDefinition = {}; + for (const method of service.methodsArray) { + def[method.name] = createMethodDefinition(method, name, options); + } + return def; +} + +function createPackageDefinition(root: Protobuf.Root, options: Options): PackageDefinition { + const def: PackageDefinition = {}; + for (const [name, service] of getAllServices(root, '')) { + def[name] = createServiceDefinition(service, name, options); + } + return def; +} + +/** + * Load a .proto file with the specified options. + * @param filename The file path to load. Can be an absolute path or relative to + * an include path. + * @param options.keepCase Preserve field names. The default is to change them + * to camel case. + * @param options.longs The type that should be used to represent `long` values. + * Valid options are `Number` and `String`. Defaults to a `Long` object type + * from a library. + * @param options.enums The type that should be used to represent `enum` values. + * The only valid option is `String`. Defaults to the numeric value. + * @param options.bytes The type that should be used to represent `bytes` + * values. Valid options are `Array` and `String`. The default is to use + * `Buffer`. + * @param options.defaults Set default values on output objects. Defaults to + * `false`. + * @param options.arrays Set empty arrays for missing array values even if + * `defaults` is `false`. Defaults to `false`. + * @param options.objects Set empty objects for missing object values even if + * `defaults` is `false`. Defaults to `false`. + * @param options.oneofs Set virtual oneof properties to the present field's + * name + * @param options.include Paths to search for imported `.proto` files. + */ +export function load(filename: string, options: Options): Promise { + const root: Protobuf.Root = new Protobuf.Root(); + if (!!options.include) { + if (!(options.include instanceof Array)) { + return Promise.reject(new Error('The include option must be an array')); + } + root.resolvePath = (origin: string, target: string) => { + for (const directory of options.include as string[]) { + const fullPath: string = path.join(directory, target); + try { + fs.accessSync(fullPath, fs.constants.R_OK); + return fullPath; + } catch (err) { + continue; + } + } + return null; + }; + } + return root.load(filename, options).then((loadedRoot) => { + loadedRoot.resolveAll(); + return createPackageDefinition(root, options); + }); +} diff --git a/packages/grpc-protobufjs/tsconfig.json b/packages/grpc-protobufjs/tsconfig.json new file mode 100644 index 00000000..9218530c --- /dev/null +++ b/packages/grpc-protobufjs/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "./node_modules/gts/tsconfig-google.json", + "compilerOptions": { + "rootDir": ".", + "outDir": "build" + }, + "include": [ + "src/*.ts", + "src/**/*.ts" + ], + "exclude": [ + "node_modules" + ] +}