Add json option and google.protobuf.Any wrapper type

This commit is contained in:
Michael Lumish 2020-06-18 15:52:37 -07:00
parent c6e17f2758
commit c7bbf045b6
4 changed files with 79 additions and 5 deletions

View File

@ -38,6 +38,7 @@ The options parameter is an object that can have the following optional properti
| `arrays` | `true` or `false` | Set empty arrays for missing array values even if `defaults` is `false` Defaults to `false`.
| `objects` | `true` or `false` | Set empty objects for missing object values even if `defaults` is `false` Defaults to `false`.
| `oneofs` | `true` or `false` | Set virtual oneof properties to the present field's name. Defaults to `false`.
| `json` | `true` or `false` | Represent `Infinity` and `NaN` as strings in `float` fields, and automatically decode `google.protobuf.Any` values. Defaults to `false`
| `includeDirs` | An array of strings | A list of search paths for imported `.proto` files.
The following options object closely approximates the existing behavior of `grpc.load`:

View File

@ -96,6 +96,15 @@ function getImportLine(dependency: Protobuf.Type | Protobuf.Enum, from?: Protobu
}
function generatePermissiveMessageInterface(formatter: TextFormatter, messageType: Protobuf.Type) {
if (messageType.fullName === '.google.protobuf.Any') {
/* This describes the behavior of the Protobuf.js Any wrapper fromObject
* replacement function */
formatter.writeLine('export type Any__Output = AnyExtension | {');
formatter.writeLine(' type_url: string;');
formatter.writeLine(' value: Buffer | Uint8Array | string;');
formatter.writeLine('}');
return;
}
formatter.writeLine(`export interface ${messageType.name} {`);
formatter.indent();
for (const field of messageType.fieldsArray) {
@ -104,6 +113,8 @@ function generatePermissiveMessageInterface(formatter: TextFormatter, messageTyp
switch (field.type) {
case 'double':
case 'float':
type = 'number | string';
break;
case 'int32':
case 'uint32':
case 'sint32':
@ -149,6 +160,23 @@ function generatePermissiveMessageInterface(formatter: TextFormatter, messageTyp
}
function generateRestrictedMessageInterface(formatter: TextFormatter, messageType: Protobuf.Type, options: Protobuf.IConversionOptions) {
if (messageType.fullName === '.google.protobuf.Any' && options.json) {
/* This describes the behavior of the Protobuf.js Any wrapper toObject
* replacement function */
formatter.writeLine('export type Any__Output = AnyExtension | {');
formatter.writeLine(' type_url: string;');
let type: string;
if (options.bytes === Array) {
type = 'Uint8Array';
} else if (options.bytes === String) {
type = 'string';
} else {
type = 'Buffer';
}
formatter.writeLine(` value: ${type};`);
formatter.writeLine('}');
return;
}
formatter.writeLine(`export interface ${messageType.name}__Output {`);
formatter.indent();
for (const field of messageType.fieldsArray) {
@ -158,6 +186,12 @@ function generateRestrictedMessageInterface(formatter: TextFormatter, messageTyp
switch (field.type) {
case 'double':
case 'float':
if (options.json) {
type = 'number | string';
} else {
type = 'number';
}
break;
case 'int32':
case 'uint32':
case 'sint32':
@ -244,6 +278,9 @@ function generateMessageInterfaces(formatter: TextFormatter, messageType: Protob
if (usesLong) {
formatter.writeLine("import { Long } from '@grpc/proto-loader';");
}
if (messageType.fullName === '.google.protobuf.Any') {
formatter.writeLine("import { AnyExtension } from '@grpc/proto-loader';")
}
formatter.writeLine('');
generatePermissiveMessageInterface(formatter, messageType);
@ -524,7 +561,7 @@ function runScript() {
.string(['includeDirs', 'grpcLib'])
.normalize(['includeDirs', 'outDir'])
.array('includeDirs')
.boolean(['keepCase', 'defaults', 'arrays', 'objects', 'oneofs'])
.boolean(['keepCase', 'defaults', 'arrays', 'objects', 'oneofs', 'json'])
// .choices('longs', ['String', 'Number'])
// .choices('enums', ['String'])
// .choices('bytes', ['Array', 'String'])
@ -559,13 +596,14 @@ function runScript() {
outDir: 'O'
}).describe({
keepCase: 'Preserve the case of field names',
longs: 'The type that should be used to output 64 bit integer values',
enums: 'The type that should be used to output enum fields',
bytes: 'The type that should be used to output bytes fields',
longs: 'The type that should be used to output 64 bit integer values. Can be String, Number',
enums: 'The type that should be used to output enum fields. Can be String',
bytes: 'The type that should be used to output bytes fields. Can be String, Array',
defaults: 'Output default values for omitted fields',
arrays: 'Output default values for omitted repeated fields even if --defaults is not set',
objects: 'Output default values for omitted message fields even if --defaults is not set',
oneofs: 'Output virtual oneof fields set to the present field\'s name',
json: 'Represent Infinity and NaN as strings in float fields. Also decode google.protobuf.Any automatically',
includeDirs: 'Directories to search for included files',
outDir: 'Directory in which to output files',
grpcLib: 'The gRPC implementation library that these types will be used with'

View File

@ -1,6 +1,6 @@
{
"name": "@grpc/proto-loader",
"version": "0.6.0-pre2",
"version": "0.6.0-pre3",
"author": "Google Inc.",
"contributors": [
{

View File

@ -22,6 +22,39 @@ import * as descriptor from 'protobufjs/ext/descriptor';
export { Long } from 'long';
/**
* This type exists for use with code generated by the proto-loader-gen-types
* tool. This type should be used with another interface, e.g.
* MessageType & AnyExtension for an object that is converted to or from a
* google.protobuf.Any message.
* For example, when processing an Any message:
*
* ```ts
* if (isAnyExtension(message)) {
* switch (message['@type']) {
* case TYPE1_URL:
* handleType1(message as AnyExtension & Type1);
* break;
* case TYPE2_URL:
* handleType2(message as AnyExtension & Type2);
* break;
* // ...
* }
* }
* ```
*/
export interface AnyExtension {
/**
* The fully qualified name of the message type that this object represents,
* possibly including a URL prefix.
*/
'@type': string;
}
export function isAnyExtension(obj: object): obj is AnyExtension {
return ('@type' in obj) && (typeof (obj as AnyExtension)['@type'] === 'string');
}
import camelCase = require('lodash.camelcase');
declare module 'protobufjs' {
@ -331,6 +364,8 @@ function addIncludePathResolver(root: Protobuf.Root, includePaths: string[]) {
* `defaults` is `false`. Defaults to `false`.
* @param options.oneofs Set virtual oneof properties to the present field's
* name
* @param options.json Represent Infinity and NaN as strings in float fields,
* and automatically decode google.protobuf.Any values.
* @param options.includeDirs Paths to search for imported `.proto` files.
*/
export function load(