Returning a list of messages - go

Given that i have multiple models, each needed to have their own create/get/get list API.
Do i need to add two different types of messages (single and list) for every model?
For example :
If i have a student type -
message Student{
string name = 1;
}
and a rpc:
rpc CreateStudent(Student) returns (google.protobuf.Empty){
..............
}
If i'd like to add a rpc to create a list of students, or get a list of students
rpc CreateStudends(??????) returns (google.protobuf.Empty){
..............
}
rpc GetAllStudents() returns (??????){
..............
}
Do i need to also define
message StudentList{
repeated Student students = 1;
}
Or is there a way to use a list type directly in the message input/output?

Yes, basically - you would want a different message type per element type, or maybe a single root type with a oneof style content. Raw protobuf does not include a concept of generics or templates.
Some libraries do, but: that's outside of the specification.

You can simply add the stream keyword to your RPCs. No need to define a message field as repeated, stream will send or receive multiple independent messages.
message Student {
string name = 1;
}
with RPCs:
rpc CreateStudent(Student) returns (google.protobuf.Empty) {
..............
}
rpc CreateStudents(stream Student) returns (google.protobuf.Empty) {
..............
}
rpc GetAllStudents() returns (stream Student) {
..............
}
It's good practice to send/stream a response object rather than empty. Otherwise, you only have the gRPC response code to indicate a problem and will need to reference the logs to debug.

Related

Is there known way to add new syntax features to Protobuf?

Protobuf provides service keyword that defines rpc-interface of one application.
I also want to use concept of entity which means that is part of service (one service contains multiple entities). Each entity type has own unique identifier that gives possibility to address different entities in service.
I would like to use proto like this
message UserReq {
string username = 1;
string password = 2;
}
message RegReq {
uint8 result_code = 1;
}
message RemoteEntityInterface
{
MyEntity entity = 1;
}
message GiveItemResult
{
uint8 result_code = 1;
}
service MyService {
rpc RegisterUser (UserReq) returns (RegReq) {}
rpc Login(UserReq) returns (RemoteEntityInterface) {}
}
entity MyEntity
{
rpc GiveItem (GiveItemReq) returns (GiveItemResult) {}
}
As you can see in example, I used unknown for protobuf keyword entity, this keyword means that MyService can return the interface to some remote object (MyEntity) by using Login remote method.
What are the ways to do this? (maybe write plugin or known way to modify source code of protobuf). Or maybe there are more flexible solutions than protobuf?
I also would like to use multiple parameters per one rpc; adding java-like attributes to rpc; service and entity; and data-model for entity (variables/fields) to add real-time replication support from entity to another service.
I think it is very flexible for services in game-development.
The only official way to extend .proto syntax is to define custom options.
For example, you could have something like:
extend google.protobuf.ServiceOptions {
optional bool is_entity = 123456;
}
service MyEntity
{
option (is_entity) = true;
rpc GiveItem (GiveItemReq) returns (GiveItemResult) {}
}
The default code generator will not do anything special with this option, but you can access it from your own code and from a protoc plugin if you write one.

Can gRPC method return a message with a field that could be string or null?

I'm designing a gRPC service written in Go.
In front of the gRPC service is Envoy which converts incoming HTTP requests to gRPC and converts the gRPC responses to JSON.
The requirement of this application is to have an endpoint that returns the following JSON object:
{
my_id: "AAA"
}
I can model this response pretty simply in Go like this:
// A MyResponse object.
message MyResponse {
// contents is a list of contents.
string my_id = 1;
}
But the requirement that I have is that sometimes my_id might be null. In that case, I want to get the following JSON back:
{
my_id: null
}
it
Is it possible to modify MyResponse such that my_id can be a string or a null in the JSON object that is returned? If so, how? If not, isn't this a pretty big gap in the design of gRPC?
I suggest you to use the StringValue field of the Package google.protobuf:
StringValue Wrapper message for string.
The JSON representation for StringValue is JSON string.
So in your proto files, you should import:
import "google/protobuf/wrappers.proto";
then use as example:
google.protobuf.StringValue name = 2;
For handle the values you can check the wrappers.StringValue
type of the github.com/golang/protobuf/ptypes/wrappers package and the helpers of the google.golang.org/protobuf/types/known/wrapperspb repo.

GRPC: Client streaming with configuration message

Here's a proto definition for a service that consumes a stream of events
from a client
message Event {
// ...
}
service EventService {
rpc Publisher(stream Event) returns (google.protobuf.Empty);
}
The problem is that the server needs to be told what to do with this stream.
Ideally, it would first recieve an Options message:
message Event {
// ...
}
message Options {
// ...
}
service EventService {
rpc Publisher(Options, stream Event) returns (google.protobuf.Empty);
}
However, grpc only supports one parameter for rpc methods.
One solution is to introduce an additional PublishMessage message which
can contain either an Options or Event message.
message PublishMessage {
oneof content {
Options options = 1;
Event event = 2;
}
}
The service would then expect the first PublishMessage to contain an Options message, with all subsequent ones containing Event messages. This introduces additional overhead from the wrapping message and makes the api a little clunky.
Is there a cleaner way to achieve the same result?
Using oneof is the suggested approach when many fields or messages are in play. The overhead is minimal, so wouldn't generally be a concern. There is the clunkiness though.
If there's only a few fields, you may want to combine the fields from Options and Event into a single message. Or similarly add Options to Event as a field. You'd expect the Options fields to be present on the first request and missing from subsequent. This works better when there's fewer configuration fields, like just a "name."

Can I define a grpc call with a null request or response?

Does the rpc syntax in proto3 allow null requests or responses?
e.g. I want the equivalent of the following:
rpc Logout;
rpc Status returns (Status);
rpc Log (LogData);
Or should I just create a null type?
message Null {};
rpc Logout (Null) returns (Null);
rpc Status (Null) returns (Status);
rpc Log (LogData) returns (Null);
Kenton's comment below is sound advice:
... we as developers are really bad at guessing what we might want in the future. So I recommend being safe by always defining custom params and results types for every method, even if they are empty.
Answering my own question:
Looking through the default proto files, I came across Empty that is exactly like the Null type I suggested above :)
excerpt from that file:
// A generic empty message that you can re-use to avoid defining duplicated
// empty messages in your APIs. A typical example is to use it as the request
// or the response type of an API method. For instance:
//
// service Foo {
// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty);
// }
//
message Empty {
}
You also can use predefined:
import "google/protobuf/empty.proto";
package MyPackage;
service MyService {
rpc Check(google.protobuf.Empty) returns (google.protobuf.Empty) {}
}
you can also use another bool property inside the Reply structure. like this
message Reply {
string result = 1;
bool found = 2;
}
so if you dont find the result or some error happened you can return from the service class this
return new Reply()
{
Found = false
};

Can protobuf service method return primitive type?

I'm trying to use Google protobuf and i 'm having the next descriptions:
message.proto file:
message Request {
required int32 id = 1;
optional string value = 2;
}
service.proto file:
import "message.proto";
service Service {
rpc request (Request) returns (bool);
}
I'm trying to generate c++ sources and getting an error:
$ protoc service.proto --cpp_out=/tmp/proto/build
service.proto:4:40: Expected message type.
Do i have to return user-defined types only? Are primitive (like bool or string) supported? Can i use primitive types as service method argument (instead of Request in my example)?
No, you cannot use a primitive type as either the request or response. You must use a message type.
This is important because a message type can be extended later, in case you decide you want to add a new parameter or return some additional data.
If you want to return a primitive type, wrap it in a message and return it:
message Name {
string name = 1;
}
In case you don't want to return anything, void I mean, you can just create an empty message:
message Void {}
message Name {
string name = 1;
}
..
service MyService{
rpc MyFunc(Name) returns (Void);
}
You can return scalar datatypes like bool, int, etc by making use of wrappers.proto
service.proto file:
import "message.proto";
import "google/protobuf/wrappers.proto";
service Service {
rpc request (Request) returns (.google.protobuf.BoolValue);
}

Resources