Converting Go []interface{} to protobuf Repeated Array - go

I have a service written in Go that get data from rest api and return that data as a grpc server. I got an array of object. And I want to return it through the grpc protobuf file. But it keeps failing because of type issue as the data from the api is []interface{} and I don't know how to return this through protobuf response.
Below is the go lang code
        return &waas.BankListResponse{
                Status:  result.Data["Status"].(bool),
                Message: result.Data["Message"].(string),
                Data: result.Data["banks"].([]*waas.Banks),
        }, nil
The proto file
message banks {
  string bankCode = 1;
  string bankName = 2;
}
  message BankListResponse {
    bool Status =1;
    string Message = 2;
    repeated banks data = 3;
  }
So the result.Data["bank"] is the array of banks and the data type is []interface{}
sample data
{
"banks": [
{
"bankCode":"",
"bankName":""
},
{
"bankCode":"",
"bankName":""
}
]
}
So can someone assist me point out how return such data on the proto file.

you need to write custom code to transfer data from the interface to your proto object. you will return this new proto object as the response.

Related

Define golang struct as type with arbitrary fields?

I'm very new to golang and the use of interfaces more generally. I've stubbed out some code here:
type Alerter interface {
Alert()
}
type AlertConfig struct{}
type Alert struct {
Config *AlertConfig
}
type AlertConfigurator interface {
UpdateConfig(key, value interface{}) (*AlertConfig, error)
}
type EmailAlertConfig AlertConfig {
Recipients []mail.Address,
}
type HTTPAlertConfig AlertConfig {
Method string
TargetURL url.URL
}
type HTTPAlert struct {
Config *HTTPAlertConfig
}
type EmailAlert struct {
Config *EmailAlertConfig
}
func (ea *EmailAlert) Alert() {
// this would actually send an email using data in its Config
return
}
func (ha *HTTPAlert) Alert() {
// this would actually hit an HTTP endpoint using data in its Config
return
}
I'm sure I have all kinds of assumptions & biases that are hobbling my ability to express what I want to accomplish with this:
I want to create different types of "Alert"s. Any alert I create should have an "Alert()"
method that triggers the Alert to actually do something (send an email, or POST to a URL,
for example.
The trouble comes in representing an Alert's "Config". Different Alerts have different fields
in their Configs. However, for each Alert type, specific fields are required to be there.
To accomplish that I wanted to create a base type "AlertConfig" as a struct with arbitrary fields,
then define, say, EmailAlertConfig as type 'AlertConfig', but having these specific fields, and then
type 'HTTPAlertConfig' as type 'AlertConfig' requiring different fields. This way, I can define
the 'Alert' type as having a field 'Config *AlertConfig'.
I can almost emulate what I want if AlertConfig is defined as map[string]interface{}, but
in this case I can't leverage golang's checking to validate that an EmailConfig has the required fields.
It seems pretty clear that I'm thinking about this the wrong way. I could use a hand in getting on the right track & appreciate your advice.
Declare a type with the common fields:
type AlertConfig struct {
ExampleCommonField string
}
Embed that type in actual configurations:
type HTTPAlertConfig struct {
AlertConfig
Method string
TargetURL url.URL
}
Based on the code in the question, the alert and config types can be combined.
func (ha *HTTPAlertConfig) Alert() {
// this will hit an HTTP endpoint using data in the receiver
return
}
One way to deal with this problem is to leave configs as purely implementation specific:
type HTTPAlert struct {
Config *HTTPAlertConfig
}
func (a *HTTPAlert) Alert() {...}
type EmailAlert struct {
Config *EmailAlertConfig
}
func (e *EmailAlert) Alert() {...}
With this implementation, the actual Alert implementation can use the type-specific alert configuration, leaving the problem of initializing the config.
When you create the instance of an alert, you can perform type-specific initialization. For instance:
var alerts = map[string]func(configFile string) Alert {
"htmlAlert": NewHTMLAlert,
"emailAlert" NewEmailAlert,
}
func NewHTMLAlert(configFile string) Alert {
var alert HTMLAlert
// Read file, initialize alert
return &alert
}
...

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.

How to create type for interface in gRPC Protobuf?

We have common Auth service for all microservices.
When we validate request in response, we send JSON that look like this.
{
Auth: bool,
Body: interface{}
}
While writing proto3 syntax, how can we write something like an interface type?
I have tried using Any type, but I am not able to use it as we need to pass message type in type url which I am not able to generate.
Proto message for response
message AuthResponse {
bool Auth = 1;
google.protobuf.Any Body = 2;
}
Can anyone help here?
You can set the body type to bytes.
If you have a pre-defined set of types that your body could be, then I suggest you change your Auth struct to something like this:
type AuthResponse struct {
Auth bool
Body Encodable
}
// Encodable defines types that can be encoded into a byte slice.
type Encodable interface {
Encode() []byte
}
Then, implement Encode() []byte on each of your types.
Alternatively, if your Body field can be anything, then perhaps a similar solution, except your Encode function would look like this:
func Encode(interface{}) []byte
In this scenario, though, you may have to use some reflection magic in order to encode it to some byte slice. You could even use JSON.
I hope this helps!

Returning a list of messages

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.

Dynamic Nested structure in GoLang

I am learning golang and want to write generic response from microservices response.
My General Response is like:
type GeneralResponse struct {
Success string
Message string
Data string
Error string
}
In the Data section I want to return any json, say list of Person, Instruments or any type of objects.
But it should be another json.
I tried assigning other json objects but it did not work.
It is fine if I dump json array as string into it but it should unmarshal from receiver end.
How should I go about it?
I am trying over here. https://play.golang.org/p/dc0uKtS76aA
You should use RawMessage in the type definition
type GeneralResponse struct {
Success string
Message string
Data json.RawMessage
Error string
}
and subsequently push a Marshalled json into that attribute.
You can do that by encoding other types in []bytes and setting them to the Data attribute.
Like in https://play.golang.org/p/CyoN5pe_aNV
If you put marshalled JSON into a string, it will be marshalled as a string (because it's a string) and the receiving end will have to unmarshal it twice (because it's been marshalled twice). What you want is probably more along the lines of:
type GeneralResponse struct {
Success string
Message string
Data interface{} // interface{} can be anything
Error string
}
This way you can put any data into Data and it will be marshalled directly into the response.
You can use json.RawMessage for that.
I have implemented the encoding part, you find more here to decode - https://golang.org/pkg/encoding/json/#RawMessage
json.RawMessage comes to rescue in case you wants to capture whole json without knowing its format.
type GeneralResponse struct {
Success string
Message string
Data json.RawMessage
Error string
}
Checkout this code. I have modified your code to dump data into response

Resources