Skip to content

Commit 403a111

Browse files
committed
Support bufbuild-protobuf
Signed-off-by: Sri Krishna <[email protected]>
1 parent a7dfb68 commit 403a111

File tree

8 files changed

+309
-9
lines changed

8 files changed

+309
-9
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
This is the static code generation variant of the Hello World. Code in these examples is pre-generated using protoc and the Node gRPC protoc plugin, and the generated code can be found in various `*_pb.js` files. The command line sequence for generating those files is as follows (assuming that `protoc` and `grpc_node_plugin` are present, and starting in the directory which contains this README.md file):
2+
3+
```sh
4+
cd ../protos
5+
npm install -g grpc-tools @bufbuild/protoc-gen-es
6+
grpc_tools_node_protoc --es_out=target=js,js_import_style=legacy_commonjs:../helloworld/static_codegen_es/ --grpc_out=grpc_js,runtime=es:../helloworld/static_codegen_es/ helloworld.proto
7+
```
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
*
3+
* Copyright 2015 gRPC authors.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
var parseArgs = require('minimist');
20+
var messages = require('./helloworld_pb');
21+
var services = require('./helloworld_grpc_pb');
22+
23+
var grpc = require('@grpc/grpc-js');
24+
25+
function main() {
26+
var argv = parseArgs(process.argv.slice(2), {
27+
string: 'target'
28+
});
29+
var target;
30+
if (argv.target) {
31+
target = argv.target;
32+
} else {
33+
target = 'localhost:50051';
34+
}
35+
var client = new services.GreeterClient(target,
36+
grpc.credentials.createInsecure());
37+
var request = new messages.HelloRequest();
38+
var user;
39+
if (argv._.length > 0) {
40+
user = argv._[0];
41+
} else {
42+
user = 'world';
43+
}
44+
request.setName(user);
45+
client.sayHello(request, function(err, response) {
46+
console.log('Greeting:', response.getMessage());
47+
});
48+
}
49+
50+
main();
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
*
3+
* Copyright 2015 gRPC authors.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
var messages = require('./helloworld_pb');
20+
var services = require('./helloworld_grpc_pb');
21+
22+
var grpc = require('@grpc/grpc-js');
23+
24+
/**
25+
* Implements the SayHello RPC method.
26+
*/
27+
function sayHello(call, callback) {
28+
var reply = new messages.HelloReply();
29+
reply.setMessage('Hello ' + call.request.getName());
30+
callback(null, reply);
31+
}
32+
33+
/**
34+
* Starts an RPC server that receives requests for the Greeter service at the
35+
* sample server port
36+
*/
37+
function main() {
38+
var server = new grpc.Server();
39+
server.addService(services.GreeterService, {sayHello: sayHello});
40+
server.bindAsync('0.0.0.0:50051', grpc.ServerCredentials.createInsecure(), (err, port) => {
41+
if (err != null) {
42+
return console.error(err);
43+
}
44+
console.log(`gRPC listening on ${port}`)
45+
});
46+
}
47+
48+
main();
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// GENERATED CODE -- DO NOT EDIT!
2+
3+
// Original file comments:
4+
// Copyright 2015 gRPC authors.
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
'use strict';
19+
var grpc = require('@grpc/grpc-js');
20+
var proto = require('@bufbuild/protobuf');
21+
var helloworld_pb = require('./helloworld_pb.js');
22+
23+
function serialize_helloworld_HelloReply(arg) {
24+
if (!proto.isMessage(arg, helloworld_pb.HelloReplySchema)) {
25+
throw new Error('Expected argument of type helloworld.HelloReply');
26+
}
27+
return Buffer.from(proto.toBinary(helloworld_pb.HelloReplySchema, arg));
28+
}
29+
30+
function deserialize_helloworld_HelloReply(buffer_arg) {
31+
return proto.fromBinary(helloworld_pb.HelloReplySchema, new Uint8Array(buffer_arg));
32+
}
33+
34+
function serialize_helloworld_HelloRequest(arg) {
35+
if (!proto.isMessage(arg, helloworld_pb.HelloRequestSchema)) {
36+
throw new Error('Expected argument of type helloworld.HelloRequest');
37+
}
38+
return Buffer.from(proto.toBinary(helloworld_pb.HelloRequestSchema, arg));
39+
}
40+
41+
function deserialize_helloworld_HelloRequest(buffer_arg) {
42+
return proto.fromBinary(helloworld_pb.HelloRequestSchema, new Uint8Array(buffer_arg));
43+
}
44+
45+
46+
// The greeting service definition.
47+
var GreeterService = exports.GreeterService = {
48+
// Sends a greeting
49+
sayHello: {
50+
path: '/helloworld.Greeter/SayHello',
51+
requestStream: false,
52+
responseStream: false,
53+
requestType: helloworld_pb.HelloRequestSchema,
54+
responseType: helloworld_pb.HelloReplySchema,
55+
requestSerialize: serialize_helloworld_HelloRequest,
56+
requestDeserialize: deserialize_helloworld_HelloRequest,
57+
responseSerialize: serialize_helloworld_HelloReply,
58+
responseDeserialize: deserialize_helloworld_HelloReply,
59+
},
60+
sayHelloStreamReply: {
61+
path: '/helloworld.Greeter/SayHelloStreamReply',
62+
requestStream: false,
63+
responseStream: true,
64+
requestType: helloworld_pb.HelloRequestSchema,
65+
responseType: helloworld_pb.HelloReplySchema,
66+
requestSerialize: serialize_helloworld_HelloRequest,
67+
requestDeserialize: deserialize_helloworld_HelloRequest,
68+
responseSerialize: serialize_helloworld_HelloReply,
69+
responseDeserialize: deserialize_helloworld_HelloReply,
70+
},
71+
};
72+
73+
exports.GreeterClient = grpc.makeGenericClientConstructor(GreeterService, 'Greeter');
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright 2015 gRPC authors.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// @generated by protoc-gen-es v2.7.0 with parameter "target=js,js_import_style=legacy_commonjs"
16+
// @generated from file helloworld.proto (package helloworld, syntax proto3)
17+
/* eslint-disable */
18+
19+
"use strict";
20+
Object.defineProperty(exports, "__esModule", { value: true });
21+
22+
const { fileDesc, messageDesc, serviceDesc } = require("@bufbuild/protobuf/codegenv2");
23+
24+
/**
25+
* Describes the file helloworld.proto.
26+
*/
27+
const file_helloworld = /*@__PURE__*/
28+
fileDesc("ChBoZWxsb3dvcmxkLnByb3RvEgpoZWxsb3dvcmxkIhwKDEhlbGxvUmVxdWVzdBIMCgRuYW1lGAEgASgJIh0KCkhlbGxvUmVwbHkSDwoHbWVzc2FnZRgBIAEoCTKWAQoHR3JlZXRlchI+CghTYXlIZWxsbxIYLmhlbGxvd29ybGQuSGVsbG9SZXF1ZXN0GhYuaGVsbG93b3JsZC5IZWxsb1JlcGx5IgASSwoTU2F5SGVsbG9TdHJlYW1SZXBseRIYLmhlbGxvd29ybGQuSGVsbG9SZXF1ZXN0GhYuaGVsbG93b3JsZC5IZWxsb1JlcGx5IgAwAUI2Chtpby5ncnBjLmV4YW1wbGVzLmhlbGxvd29ybGRCD0hlbGxvV29ybGRQcm90b1ABogIDSExXYgZwcm90bzM");
29+
30+
/**
31+
* Describes the message helloworld.HelloRequest.
32+
* Use `create(HelloRequestSchema)` to create a new message.
33+
*/
34+
const HelloRequestSchema = /*@__PURE__*/
35+
messageDesc(file_helloworld, 0);
36+
37+
/**
38+
* Describes the message helloworld.HelloReply.
39+
* Use `create(HelloReplySchema)` to create a new message.
40+
*/
41+
const HelloReplySchema = /*@__PURE__*/
42+
messageDesc(file_helloworld, 1);
43+
44+
/**
45+
* The greeting service definition.
46+
*
47+
* @generated from service helloworld.Greeter
48+
*/
49+
const Greeter = /*@__PURE__*/
50+
serviceDesc(file_helloworld, 0);
51+
52+
53+
exports.file_helloworld = file_helloworld;
54+
exports.HelloRequestSchema = HelloRequestSchema;
55+
exports.HelloReplySchema = HelloReplySchema;
56+
exports.Greeter = Greeter;

packages/grpc-tools/src/node_generator.cc

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -112,21 +112,27 @@ grpc::string MessageIdentifierName(const grpc::string& name) {
112112
return grpc_generator::StringReplace(name, ".", "_");
113113
}
114114

115-
grpc::string NodeObjectPath(const Descriptor* descriptor) {
115+
grpc::string NodeObjectPath(const Descriptor* descriptor, const grpc::string& runtime) {
116116
grpc::string module_alias = ModuleAlias(descriptor->file()->name());
117+
if (runtime == "es" && descriptor->file()->name().find("google/protobuf") == 0) {
118+
module_alias = "wkt";
119+
}
117120
grpc::string name = descriptor->full_name();
118121
grpc_generator::StripPrefix(&name, descriptor->file()->package() + ".");
122+
if (runtime == "es") {
123+
name += "Schema";
124+
}
119125
return module_alias + "." + name;
120126
}
121127

122-
// Prints out the message serializer and deserializer functions
123-
void PrintMessageTransformer(const Descriptor* descriptor, Printer* out,
128+
// Prints out the message serializer and deserializer functions for google-protobuf.
129+
void PrintGoogleProtobufMessageTransformer(const Descriptor* descriptor, Printer* out,
124130
const Parameters& params) {
125131
map<grpc::string, grpc::string> template_vars;
126132
grpc::string full_name = descriptor->full_name();
127133
template_vars["identifier_name"] = MessageIdentifierName(full_name);
128134
template_vars["name"] = full_name;
129-
template_vars["node_name"] = NodeObjectPath(descriptor);
135+
template_vars["node_name"] = NodeObjectPath(descriptor, params.runtime);
130136
// Print the serializer
131137
out->Print(template_vars, "function serialize_$identifier_name$(arg) {\n");
132138
out->Indent();
@@ -153,15 +159,59 @@ void PrintMessageTransformer(const Descriptor* descriptor, Printer* out,
153159
out->Print("}\n\n");
154160
}
155161

156-
void PrintMethod(const MethodDescriptor* method, Printer* out) {
162+
// Prints out the message serializer and deserializer functions for bufbuild-protobuf.
163+
void PrintBufbuildProtobufMessageTransformer(const Descriptor* descriptor, Printer* out,
164+
const Parameters& params) {
165+
map<grpc::string, grpc::string> template_vars;
166+
grpc::string full_name = descriptor->full_name();
167+
template_vars["identifier_name"] = MessageIdentifierName(full_name);
168+
template_vars["name"] = full_name;
169+
template_vars["node_name"] = NodeObjectPath(descriptor, params.runtime);
170+
// Print the serializer
171+
out->Print(template_vars, "function serialize_$identifier_name$(arg) {\n");
172+
out->Indent();
173+
if (!params.omit_serialize_instanceof) {
174+
out->Print(template_vars, "if (!proto.isMessage(arg, $node_name$)) {\n");
175+
out->Indent();
176+
out->Print(template_vars,
177+
"throw new Error('Expected argument of type $name$');\n");
178+
out->Outdent();
179+
out->Print("}\n");
180+
}
181+
out->Print(template_vars, "return Buffer.from(proto.toBinary($node_name$, arg));\n");
182+
out->Outdent();
183+
out->Print("}\n\n");
184+
185+
// Print the deserializer
186+
out->Print(template_vars,
187+
"function deserialize_$identifier_name$(buffer_arg) {\n");
188+
out->Indent();
189+
out->Print(
190+
template_vars,
191+
"return proto.fromBinary($node_name$, new Uint8Array(buffer_arg));\n");
192+
out->Outdent();
193+
out->Print("}\n\n");
194+
}
195+
196+
// Prints out the message serializer and deserializer functions
197+
void PrintMessageTransformer(const Descriptor* descriptor, Printer* out,
198+
const Parameters& params) {
199+
if (params.runtime == "es") {
200+
PrintBufbuildProtobufMessageTransformer(descriptor, out, params);
201+
} else {
202+
PrintGoogleProtobufMessageTransformer(descriptor, out, params);
203+
}
204+
}
205+
206+
void PrintMethod(const MethodDescriptor* method, Printer* out, const Parameters& params) {
157207
const Descriptor* input_type = method->input_type();
158208
const Descriptor* output_type = method->output_type();
159209
map<grpc::string, grpc::string> vars;
160210
vars["service_name"] = method->service()->full_name();
161211
vars["name"] = method->name();
162-
vars["input_type"] = NodeObjectPath(input_type);
212+
vars["input_type"] = NodeObjectPath(input_type, params.runtime);
163213
vars["input_type_id"] = MessageIdentifierName(input_type->full_name());
164-
vars["output_type"] = NodeObjectPath(output_type);
214+
vars["output_type"] = NodeObjectPath(output_type, params.runtime);
165215
vars["output_type_id"] = MessageIdentifierName(output_type->full_name());
166216
vars["client_stream"] = method->client_streaming() ? "true" : "false";
167217
vars["server_stream"] = method->server_streaming() ? "true" : "false";
@@ -198,7 +248,7 @@ void PrintService(const ServiceDescriptor* service, Printer* out,
198248
grpc_generator::LowercaseFirstLetter(service->method(i)->name());
199249
out->PrintRaw(GetNodeComments(service->method(i), true).c_str());
200250
out->Print("$method_name$: ", "method_name", method_name);
201-
PrintMethod(service->method(i), out);
251+
PrintMethod(service->method(i), out, params);
202252
out->Print(",\n");
203253
out->PrintRaw(GetNodeComments(service->method(i), false).c_str());
204254
}
@@ -218,14 +268,25 @@ void PrintImports(const FileDescriptor* file, Printer* out,
218268
grpc::string package = params.grpc_js ? "@grpc/grpc-js" : "grpc";
219269
out->Print("var grpc = require('$package$');\n", "package", package);
220270
}
271+
if (params.runtime == "es") {
272+
out->Print("var proto = require('@bufbuild/protobuf');\n");
273+
}
221274
if (file->message_type_count() > 0) {
222275
grpc::string file_path =
223276
GetRelativePath(file->name(), GetJSMessageFilename(file->name()));
224277
out->Print("var $module_alias$ = require('$file_path$');\n", "module_alias",
225278
ModuleAlias(file->name()), "file_path", file_path);
226279
}
227-
280+
bool imports_wkt = false;
228281
for (int i = 0; i < file->dependency_count(); i++) {
282+
if (params.runtime == "es" && file->dependency(i)->name().find("google/protobuf") == 0) {
283+
// WKTs are provided by the runtime from a single location.
284+
if (!imports_wkt) {
285+
out->Print("var wkt = require('@bufbuild/protobuf/wkt');");
286+
imports_wkt = true;
287+
}
288+
continue;
289+
}
229290
grpc::string file_path = GetRelativePath(
230291
file->name(), GetJSMessageFilename(file->dependency(i)->name()));
231292
out->Print("var $module_alias$ = require('$file_path$');\n", "module_alias",

packages/grpc-tools/src/node_generator.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ struct Parameters {
3030
bool grpc_js;
3131
// Omit instanceof check for messages in serialize methods
3232
bool omit_serialize_instanceof;
33+
// Runtime to use for protobuf serialization (default: "google-protobuf", "es" for @bufbuild/protobuf)
34+
grpc::string runtime;
3335
};
3436

3537
grpc::string GenerateFile(const grpc::protobuf::FileDescriptor* file,

packages/grpc-tools/src/node_plugin.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class NodeGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
4040
generator_parameters.generate_package_definition = false;
4141
generator_parameters.grpc_js = false;
4242
generator_parameters.omit_serialize_instanceof = false;
43+
generator_parameters.runtime = "google-protobuf";
4344
if (!parameter.empty()) {
4445
std::vector<grpc::string> parameters_list =
4546
grpc_generator::tokenize(parameter, ",");
@@ -51,6 +52,8 @@ class NodeGrpcGenerator : public grpc::protobuf::compiler::CodeGenerator {
5152
generator_parameters.grpc_js = true;
5253
} else if (*parameter_string == "omit_serialize_instanceof") {
5354
generator_parameters.omit_serialize_instanceof = true;
55+
} else if (parameter_string->find("runtime=") == 0) {
56+
generator_parameters.runtime = parameter_string->substr(8);
5457
}
5558
}
5659
}

0 commit comments

Comments
 (0)