Skip to content

Commit 604dd0f

Browse files
authored
Merge pull request #1635 from tatemz/feature/load-file-descriptor-sets
Add functions for loading and parsing binary-encoded or plain object file descriptor sets
2 parents 1e37fbd + d86994d commit 604dd0f

File tree

6 files changed

+191
-0
lines changed

6 files changed

+191
-0
lines changed

packages/proto-loader/src/index.ts

+55
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,17 @@ declare module 'protobufjs' {
3030
descriptor.IDescriptorProto;
3131
}
3232

33+
interface RootConstructor {
34+
new (options?: Options): Root;
35+
fromDescriptor(
36+
descriptorSet:
37+
| descriptor.IFileDescriptorSet
38+
| Protobuf.Reader
39+
| Uint8Array
40+
): Root;
41+
fromJSON(json: Protobuf.INamespace, root?: Root): Root;
42+
}
43+
3344
interface Root {
3445
toDescriptor(
3546
protoVersion: string
@@ -98,6 +109,9 @@ export type Options = Protobuf.IParseOptions &
98109
includeDirs?: string[];
99110
};
100111

112+
type DecodedDescriptorSet = Protobuf.Message<descriptor.IFileDescriptorSet> &
113+
descriptor.IFileDescriptorSet;
114+
101115
const descriptorOptions: Protobuf.IConversionOptions = {
102116
longs: String,
103117
enums: String,
@@ -307,6 +321,19 @@ function addIncludePathResolver(root: Protobuf.Root, includePaths: string[]) {
307321
};
308322
}
309323

324+
function createPackageDefinitionFromDescriptorSet(
325+
decodedDescriptorSet: DecodedDescriptorSet,
326+
options?: Options
327+
) {
328+
options = options || {};
329+
330+
const root = (Protobuf.Root as Protobuf.RootConstructor).fromDescriptor(
331+
decodedDescriptorSet
332+
);
333+
root.resolveAll();
334+
return createPackageDefinition(root, options);
335+
}
336+
310337
/**
311338
* Load a .proto file with the specified options.
312339
* @param filename One or multiple file paths to load. Can be an absolute path
@@ -368,6 +395,34 @@ export function loadSync(
368395
return createPackageDefinition(root, options!);
369396
}
370397

398+
export function loadFileDescriptorSetFromBuffer(
399+
descriptorSet: Buffer,
400+
options?: Options
401+
): PackageDefinition {
402+
const decodedDescriptorSet = descriptor.FileDescriptorSet.decode(
403+
descriptorSet
404+
) as DecodedDescriptorSet;
405+
406+
return createPackageDefinitionFromDescriptorSet(
407+
decodedDescriptorSet,
408+
options
409+
);
410+
}
411+
412+
export function loadFileDescriptorSetFromObject(
413+
descriptorSet: Parameters<typeof descriptor.FileDescriptorSet.fromObject>[0],
414+
options?: Options
415+
): PackageDefinition {
416+
const decodedDescriptorSet = descriptor.FileDescriptorSet.fromObject(
417+
descriptorSet
418+
) as DecodedDescriptorSet;
419+
420+
return createPackageDefinitionFromDescriptorSet(
421+
decodedDescriptorSet,
422+
options
423+
);
424+
}
425+
371426
// Load Google's well-known proto files that aren't exposed by Protobuf.js.
372427

373428
// Protobuf.js exposes: any, duration, empty, field_mask, struct, timestamp,

packages/proto-loader/test/descriptor_type_test.ts

+20
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
*/
1717

1818
import * as assert from 'assert';
19+
import { rpcFileDescriptorSet } from '../test_protos/rpc.desc';
20+
import { readFileSync } from 'fs';
1921

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

@@ -99,4 +101,22 @@ describe('Descriptor types', () => {
99101
// This will throw if the well known protos are not available.
100102
proto_loader.loadSync(`${TEST_PROTO_DIR}/well_known.proto`);
101103
});
104+
105+
it('Can load binary-encoded proto file descriptor sets', () => {
106+
const buffer = readFileSync(`${TEST_PROTO_DIR}/rpc.desc.bin`);
107+
// This will throw if the rpc descriptor cannot be decoded
108+
proto_loader.loadFileDescriptorSetFromBuffer(buffer);
109+
});
110+
111+
it('Can load json file descriptor sets', () => {
112+
const buffer = readFileSync(`${TEST_PROTO_DIR}/rpc.desc.json`);
113+
const json = JSON.parse(buffer.toString());
114+
// This will throw if the rpc descriptor JSON cannot be decoded
115+
proto_loader.loadFileDescriptorSetFromObject(json);
116+
});
117+
118+
it('Can parse plain file descriptor set objects', () => {
119+
// This will throw if the file descriptor object cannot be parsed
120+
proto_loader.loadFileDescriptorSetFromObject(rpcFileDescriptorSet);
121+
});
102122
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
2+
�
3+
test_protos/rpc.proto"
4+
MyRequest
5+
path ( Rpath"$
6+
7+
MyResponse
8+
status (Rstatus20
9+
MyService#
10+
MyMethod
11+
.MyRequest .MyResponsebproto3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"file": [
3+
{
4+
"name": "test_protos/rpc.proto",
5+
"messageType": [
6+
{
7+
"name": "MyRequest",
8+
"field": [
9+
{
10+
"name": "path",
11+
"number": 1,
12+
"label": "LABEL_OPTIONAL",
13+
"type": "TYPE_STRING",
14+
"jsonName": "path"
15+
}
16+
]
17+
},
18+
{
19+
"name": "MyResponse",
20+
"field": [
21+
{
22+
"name": "status",
23+
"number": 2,
24+
"label": "LABEL_OPTIONAL",
25+
"type": "TYPE_INT32",
26+
"jsonName": "status"
27+
}
28+
]
29+
}
30+
],
31+
"service": [
32+
{
33+
"name": "MyService",
34+
"method": [
35+
{
36+
"name": "MyMethod",
37+
"inputType": ".MyRequest",
38+
"outputType": ".MyResponse"
39+
}
40+
]
41+
}
42+
],
43+
"syntax": "proto3"
44+
}
45+
]
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
export const rpcFileDescriptorSet = {
2+
"file": [
3+
{
4+
"name": "test_protos/rpc.proto",
5+
"messageType": [
6+
{
7+
"name": "MyRequest",
8+
"field": [
9+
{
10+
"name": "path",
11+
"number": 1,
12+
"label": "LABEL_OPTIONAL",
13+
"type": "TYPE_STRING",
14+
"jsonName": "path"
15+
}
16+
]
17+
},
18+
{
19+
"name": "MyResponse",
20+
"field": [
21+
{
22+
"name": "status",
23+
"number": 2,
24+
"label": "LABEL_OPTIONAL",
25+
"type": "TYPE_INT32",
26+
"jsonName": "status"
27+
}
28+
]
29+
}
30+
],
31+
"service": [
32+
{
33+
"name": "MyService",
34+
"method": [
35+
{
36+
"name": "MyMethod",
37+
"inputType": ".MyRequest",
38+
"outputType": ".MyResponse"
39+
}
40+
]
41+
}
42+
],
43+
"syntax": "proto3"
44+
}
45+
]
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
syntax = "proto3";
2+
3+
service MyService {
4+
rpc MyMethod (MyRequest) returns (MyResponse);
5+
}
6+
7+
message MyRequest {
8+
string path = 1;
9+
}
10+
11+
message MyResponse {
12+
int32 status = 2;
13+
}

0 commit comments

Comments
 (0)