Skip to content

Commit

Permalink
Merge pull request #1635 from tatemz/feature/load-file-descriptor-sets
Browse files Browse the repository at this point in the history
Add functions for loading and parsing binary-encoded or plain object file descriptor sets
  • Loading branch information
murgatroid99 committed Dec 1, 2020
2 parents 1e37fbd + d86994d commit 604dd0f
Show file tree
Hide file tree
Showing 6 changed files with 191 additions and 0 deletions.
55 changes: 55 additions & 0 deletions packages/proto-loader/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ declare module 'protobufjs' {
descriptor.IDescriptorProto;
}

interface RootConstructor {
new (options?: Options): Root;
fromDescriptor(
descriptorSet:
| descriptor.IFileDescriptorSet
| Protobuf.Reader
| Uint8Array
): Root;
fromJSON(json: Protobuf.INamespace, root?: Root): Root;
}

interface Root {
toDescriptor(
protoVersion: string
Expand Down Expand Up @@ -98,6 +109,9 @@ export type Options = Protobuf.IParseOptions &
includeDirs?: string[];
};

type DecodedDescriptorSet = Protobuf.Message<descriptor.IFileDescriptorSet> &
descriptor.IFileDescriptorSet;

const descriptorOptions: Protobuf.IConversionOptions = {
longs: String,
enums: String,
Expand Down Expand Up @@ -307,6 +321,19 @@ function addIncludePathResolver(root: Protobuf.Root, includePaths: string[]) {
};
}

function createPackageDefinitionFromDescriptorSet(
decodedDescriptorSet: DecodedDescriptorSet,
options?: Options
) {
options = options || {};

const root = (Protobuf.Root as Protobuf.RootConstructor).fromDescriptor(
decodedDescriptorSet
);
root.resolveAll();
return createPackageDefinition(root, options);
}

/**
* Load a .proto file with the specified options.
* @param filename One or multiple file paths to load. Can be an absolute path
Expand Down Expand Up @@ -368,6 +395,34 @@ export function loadSync(
return createPackageDefinition(root, options!);
}

export function loadFileDescriptorSetFromBuffer(
descriptorSet: Buffer,
options?: Options
): PackageDefinition {
const decodedDescriptorSet = descriptor.FileDescriptorSet.decode(
descriptorSet
) as DecodedDescriptorSet;

return createPackageDefinitionFromDescriptorSet(
decodedDescriptorSet,
options
);
}

export function loadFileDescriptorSetFromObject(
descriptorSet: Parameters<typeof descriptor.FileDescriptorSet.fromObject>[0],
options?: Options
): PackageDefinition {
const decodedDescriptorSet = descriptor.FileDescriptorSet.fromObject(
descriptorSet
) as DecodedDescriptorSet;

return createPackageDefinitionFromDescriptorSet(
decodedDescriptorSet,
options
);
}

// Load Google's well-known proto files that aren't exposed by Protobuf.js.

// Protobuf.js exposes: any, duration, empty, field_mask, struct, timestamp,
Expand Down
20 changes: 20 additions & 0 deletions packages/proto-loader/test/descriptor_type_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
*/

import * as assert from 'assert';
import { rpcFileDescriptorSet } from '../test_protos/rpc.desc';
import { readFileSync } from 'fs';

import * as proto_loader from '../src/index';

Expand Down Expand Up @@ -99,4 +101,22 @@ describe('Descriptor types', () => {
// This will throw if the well known protos are not available.
proto_loader.loadSync(`${TEST_PROTO_DIR}/well_known.proto`);
});

it('Can load binary-encoded proto file descriptor sets', () => {
const buffer = readFileSync(`${TEST_PROTO_DIR}/rpc.desc.bin`);
// This will throw if the rpc descriptor cannot be decoded
proto_loader.loadFileDescriptorSetFromBuffer(buffer);
});

it('Can load json file descriptor sets', () => {
const buffer = readFileSync(`${TEST_PROTO_DIR}/rpc.desc.json`);
const json = JSON.parse(buffer.toString());
// This will throw if the rpc descriptor JSON cannot be decoded
proto_loader.loadFileDescriptorSetFromObject(json);
});

it('Can parse plain file descriptor set objects', () => {
// This will throw if the file descriptor object cannot be parsed
proto_loader.loadFileDescriptorSetFromObject(rpcFileDescriptorSet);
});
});
11 changes: 11 additions & 0 deletions packages/proto-loader/test_protos/rpc.desc.bin
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

�
test_protos/rpc.proto"
MyRequest
path ( Rpath"$

MyResponse
status (Rstatus20
MyService#
MyMethod
.MyRequest .MyResponsebproto3
46 changes: 46 additions & 0 deletions packages/proto-loader/test_protos/rpc.desc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{
"file": [
{
"name": "test_protos/rpc.proto",
"messageType": [
{
"name": "MyRequest",
"field": [
{
"name": "path",
"number": 1,
"label": "LABEL_OPTIONAL",
"type": "TYPE_STRING",
"jsonName": "path"
}
]
},
{
"name": "MyResponse",
"field": [
{
"name": "status",
"number": 2,
"label": "LABEL_OPTIONAL",
"type": "TYPE_INT32",
"jsonName": "status"
}
]
}
],
"service": [
{
"name": "MyService",
"method": [
{
"name": "MyMethod",
"inputType": ".MyRequest",
"outputType": ".MyResponse"
}
]
}
],
"syntax": "proto3"
}
]
}
46 changes: 46 additions & 0 deletions packages/proto-loader/test_protos/rpc.desc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
export const rpcFileDescriptorSet = {
"file": [
{
"name": "test_protos/rpc.proto",
"messageType": [
{
"name": "MyRequest",
"field": [
{
"name": "path",
"number": 1,
"label": "LABEL_OPTIONAL",
"type": "TYPE_STRING",
"jsonName": "path"
}
]
},
{
"name": "MyResponse",
"field": [
{
"name": "status",
"number": 2,
"label": "LABEL_OPTIONAL",
"type": "TYPE_INT32",
"jsonName": "status"
}
]
}
],
"service": [
{
"name": "MyService",
"method": [
{
"name": "MyMethod",
"inputType": ".MyRequest",
"outputType": ".MyResponse"
}
]
}
],
"syntax": "proto3"
}
]
}
13 changes: 13 additions & 0 deletions packages/proto-loader/test_protos/rpc.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
syntax = "proto3";

service MyService {
rpc MyMethod (MyRequest) returns (MyResponse);
}

message MyRequest {
string path = 1;
}

message MyResponse {
int32 status = 2;
}

0 comments on commit 604dd0f

Please sign in to comment.