Modeling schema metadata without serializing into the protobuf message - protocol-buffers

Does protobuf support embedding non functional metadata into the protobuf schema without affecting the message serialization/de-serialization? I am attempting to embed business information (ownership, contact info) into a large shared protobuf schema but do NOT want to impact functionality at all.
A structured comment or custom_option that does not get serialized would work. I would also like to parse the information from the .proto file for auditing purposes.
TIA
message Bar {
optional int32 a = 1 [(silent_options).owner = "team1", (silent_options).email = "team1#company.com"];
optional int32 b = 2;
}

To do what you want, you will need to use the extend keyword to create a custom FieldOption and then create a protoc plugin. I recommend using Go or C++ for custom plugins, their API is easier.
First we create a custom field option with extend. You can do so like this:
syntax = "proto3";
import "google/protobuf/descriptor.proto";
option go_package = "test.com/proto";
message SilentOptions {
string owner = 1; // maybe an enum if you know all teams
string email = 2;
}
extend google.protobuf.FieldOptions {
SilentOptions silent_options = 1000;
}
There are few things to note. The first one is that we are extending google.protobuf.FieldOptions which is available because we import descriptor.proto. Then, in my case my go module is called "test.com" and this file is in a folder called proto. So the go_package is "test.com/proto".
Once we have this, we need to create a protoc plugin. There are few tutorials on the web but here is a quick solution which is only working for your example:
package main
import (
"log"
"google.golang.org/protobuf/compiler/protogen"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/descriptorpb"
"google.golang.org/protobuf/types/pluginpb"
silentOption "test.com/proto"
)
func main() {
protogen.Options{}.Run(func(gen *protogen.Plugin) error {
gen.SupportedFeatures = uint64(pluginpb.CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL)
for _, sourceFile := range gen.Files {
if !sourceFile.Generate {
continue
}
for _, msg := range sourceFile.Messages {
for _, field := range msg.Fields {
opt := field.Desc.Options().(*descriptorpb.FieldOptions)
if opt == nil { // no FieldOption
continue
}
ext1 := proto.GetExtension(opt, silentOption.E_SilentOptions)
if ext1 != nil { // no SilentOptions
log.Println(ext1)
}
}
}
}
return nil
})
}
For this to run you will need to go get -u google.golang.org/protobuf and run protoc on the silent_options.proto: protoc --go_out=. --go_opt=module=test.com proto/*.proto. For the protoc command to run you need to run the following commands: go get google.golang.org/protobuf/cmd/protoc-gen-go. This is getting the official plugin to generate go code from protobuf.
Finally, build the main.go with a command similar to go build -o protoc-gen-test (note that the name of the binary should start with protoc-gen and then is followed by you plugin name). Then, copy the generated binary to a place in your PATH. And with that you should be able to run: protoc --test_out=. test.proto where test.proto is:
syntax = "proto3";
import "proto/silent_options.proto"; // import the proto file for options
option go_package = "another_test.com"; // can be in a different package
message Bar {
optional int32 a = 1 [
(silent_options) = {
owner: "team1",
email: "team1#company.com"
}
];
optional int32 b = 2;
}
It should print something like:
owner:"team1" email:"team1#company.com"
Obviously this is quite an involved process but I would be happy to update this answer if anything is unclear or if you need more details on the code.
Edit
I forgot to mention that I chose the tag 1000 but there might already have other plugins have the same tag. If you plan to use other plugins later, you should be careful with that and check the tag already in use (protobuf documentation had a page on this, can't find it yet) otherwise you might have conflicts.
Edit 2
Here is the list of the extension tags that are already taken.

Related

Use Array of struct in proto

I am not able to call a gRPC function due to type mismatch
my proto file :
message Analytics {
fields ...
}
message AnalyticsSet {
repeated Analytics analytics = 1;
}
service StatService {
rpc MyMethod(AnalyticsSet) returns (<something>) {}
}
Now, I need to call "MyMethod"
My current code :
type Analytics struct {
same fields as in proto : Analytics
}
analytics := make([]Analytics, 4)
// .. some modifications in analytics ...
_, err := c.MyMethod(context.Background(), analytics)
if err != nil {
log.Fatalf("error: %s", err)
}
in Proto file "AnalyticsSet" is the array of "Analytics"
and in Go code "analytics" is an array of type "Analytics"
but this is not enough to call "MyMethod", and I am facing type mismatch..
How should I modify the go code ?
You must use the Analytics struct generated from the proto file -- you cannot use your own type.
You can generate the required Go code using protoc with your .proto file. Here is an example with gRPC generation options set as well:
.
$ protoc --go_out=. --go-grpc_out=. --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative analytics.proto
Your proto file should have the go_package option set to describe the Go import path for that your generated proto code belongs to. You will also need to install the go / go-grpc generator utilities required by protoc:
$ go install google.golang.org/protobuf/cmd/protoc-gen-go#latest
$ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc#latest
More details can be found in:
https://developers.google.com/protocol-buffers/docs/gotutorial
https://developers.google.com/protocol-buffers/docs/reference/go-generated
https://grpc.io/docs/languages/go/quickstart/

cannot import proto file

I have spent hours trying to read blogs on how to use simple proto file in Golang. I generated the .pb.go files. All internet examples are littered with doing import from some random "github... urls for proto import. I am not able to find any example on how to import a simple proto file that exists in same dir as my .go file or diff directory. How do I use proto files from local file systems.
go build hello.go
hello.go:5:2: cannot find package "customer" in any of:
/usr/local/go/src/customer (from $GOROOT)
/Users/myhomedir/go/src/customer (from $GOPATH)
Contentes of hello.goin $SOME_DIR/customer
package main
import (
"fmt"
pb "customer"
)
func main() {
fmt.Println("hello test message\n")
}
Contents of customer.proto
syntax = "proto3";
package customer;
// The Customer service definition.
service Customer {
// Get all Customers with filter - A server-to-client streaming RPC.
rpc GetCustomers(CustomerFilter) returns (stream CustomerRequest) {}
// Create a new Customer - A simple RPC
rpc CreateCustomer (CustomerRequest) returns (CustomerResponse) {}
}
// Request message for creating a new customer
message CustomerRequest {
int32 id = 1; // Unique ID number for a Customer.
string name = 2;
string email = 3;
string phone= 4;
message Address {
string street = 1;
string city = 2;
string state = 3;
string zip = 4;
bool isShippingAddress = 5;
}
repeated Address addresses = 5;
}
message CustomerResponse {
int32 id = 1;
bool success = 2;
}
message CustomerFilter {
string keyword = 1;
}
Just put the Customer code in a directory and import it like you would a package
package main
import "$SOME_DIR/customer"
As some of the comments already mentioned, you have to import your customer file like any other go import - with its full url-like path. That is true even if your main.go is in the same directory as your dependency.
There is a specific way in which everyone should manage go code dependencies and you can read about it here:
How to write go code
It does feel weird to follow a common standard of how to organize your workspace (at least to me it did when I first started with go) - but since you run into errors like yours if you don't follow them; just try to do it.
A pretty good place to start (after you've read 'how to write go code') is actually the error message you got. It will change if you change your import statement/directory structure and it will help you to specify the full path to your dependency in your import ... statement.

golang protobuf remove omitempty tag from generated json tags

I am using google grpc with a json proxy. for some reason i need to remove the omitempty tags from the struct generated in the *.pb.go files.
if i have a proto message like this
message Status {
int32 code = 1;
string message = 2;
}
The generated struct looks like this
type Status struct {
Code int32 `protobuf:"varint,1,opt,name=code" json:"code,omitempty"`
Message string `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"`
}
But My need is to remove the omitempty tag from the generated structs. How can i do this?
If you are using grpc-gateway and you need the default values to be present during json marshaling, you may consider to add the following option when creating your servemux
gwmux := runtime.NewServeMux(runtime.WithMarshalerOption(runtime.MIMEWildcard, &runtime.JSONPb{OrigName: true, EmitDefaults: true}))
Outside of grpc-gateway, if you want to marshal your protocol buffer message, use google.golang.org/protobuf/encoding/protojson (*) package instead of encoding/json
func sendProtoMessage(resp proto.Message, w http.ResponseWriter) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
m := protojson.Marshaler{EmitDefaults: true}
m.Marshal(w, resp) // You should check for errors here
}
(*) google.golang.org/protobuf replaces the now deprecated github.com/golang/protobuf and its jsonpb package.
A [more] portable solution:
Use sed to strip the tags after generating via protoc.
Example of what I actually use in my go:generate script after having generated the *.pb.go files:
ls *.pb.go | xargs -n1 -IX bash -c 'sed s/,omitempty// X > X.tmp && mv X{.tmp,}'
Note: sed -i (inline-replacement) is not used here because that flag isn't portable between standard OS-X and Linux.
You can try using gogo proto (https://github.com/gogo/protobuf)
With the jsontag extension, your proto message would look like
message Status {
int32 code = 1 [(gogoproto.jsontag) = "code"];
string message = 2 [(gogoproto.jsontag) = "message"];
}
You can also add more tags, if you like.
I found that the omitempty json tag is hard-coded into the protoc-gen-go source around line 1778:
tag := fmt.Sprintf("protobuf:%s json:%q",
g.goTag(message, field, wiretype), jsonName+",omitempty")
it will be easy changing the source and make a new protoc-gen-go binary yourself.
It's worth noting that this is likely inadvisable and not recommended for several reasons, particularly because you will then be responsible for ensuring that the hacked up binary always gets used if the protobufs need to be regenerated.
I am posting an update to DeeSilence's answer that works with the latest protobuf version (at the moment of writing).
import "google.golang.org/protobuf/encoding/protojson"
m := protojson.MarshalOptions{EmitUnpopulated: true}
resp, err := m.Marshal(w)
The Marshaler under the jsonpb package has a EmitDefaults field. Setting this to true, will just ignore the omitempty tag in struct.
https://godoc.org/github.com/golang/protobuf/jsonpb#JSONPBMarshaler
You could use "sed" command to remove this text from files like following
sed -i "" -e "s/,omitempty//g" ./api/proto/*.go
where args:
-i "" is meaning that keep same name of files
-e "s/,omitempty//g" = format to replace like "s/SEARCH/INSERT/g"
you can copy the encoding/json package to your own folder for example my_json, and modify omitEmpty field to false, and use my_json.Marshal() to encode the struct to json string.
To expand on #colm.anseo's edit saying that the google.golang.org/protobuf/encoding/protojson is now deprecated and now google.golang.org/protobuf should be used, here is an example how to do it without modifying the proto.
import (
"google.golang.org/protobuf/encoding/protojson"
)
func main() {
someProto := SomeProto{}
marshaler := protojson.MarshalOptions{EmitUnpopulated: true}
output, _ := marshaler.Marshal(someProto)
...
}
This is the only way to do it if you don't want to modify the generated struct / are importing the struct via buf and the bsr.

How to evaluate external go source code "dynamically"?

I need to parse a Go source code file, find a specific type (by name) and use it in my program.
I already managed to find the type I need using the go/ast package but I don't know how to "load" it into my program so that I can use it.
Question: What's the best way to extract and use a type from an external source code file and use it on runtime?
I can't think of anything except an ugly method to basically copy the file, modify it by injecting a "main" function with my encoding stuff which sends the result to stdOut, execute the it, collect the encoded data from stdout, delete the modified file.
Use case: Analyse go source code and encode the types in a specific format (e.g. json schema)
Edit:
Here is some code. The question is how to encode type allTypes (zero value) and then print it.
package main
import (
"fmt"
"go/ast"
"go/parser"
"go/token"
"encoding/json"
)
var src string = `
package mypack
type allTypes struct{
Brands Brands
Colours Colours
}
type Brands struct{
Sony string
Apple string
}
type Colours struct{
Red string
Green string
}
`
type sometype struct{
Nothing int
}
func main() {
// src is the input for which we want to inspect the AST.
// Create the AST by parsing src.
fset := token.NewFileSet() // positions are relative to fset
f, err := parser.ParseFile(fset, "src.go", src, 0)
if err != nil {
panic(err)
}
// Inspect the AST and find our function
var tp ast.TypeSpec
ast.Inspect(f, func(n ast.Node) bool {
switch x := n.(type) {
case *ast.TypeSpec:
if x.Name.Name == "allTypes"{
tp = *x
}
}
return true
})
fmt.Printf("We found the type: it is %v", tp)
// Encode the zero value of sometype
x := sometype{}
b, _ := json.Marshal(&x)
fmt.Printf("\n Zero value of someType (json) %s", b)
//Next/Question: How to encode the zero value of "allTypes" ???
}
Also on playground
If I understand you are asking for dynamic type loading ala Java's Class.forName(String className). The short answer is Go doesn't support this.
The correct way, as Nick Johnson pointed out, is to parse the tree using ast, and then "generate" the JSON yourself. You will not be able to "load" the type and use JSON.Marshal. It is also worth noting that any type which supports the json.Marshaler interface may generate custom JSON. You also need to ignore, but mark optional "omitempty" behavior. This really prevents you from using the compile it and hack through "stdout" behavior as well.
If you need to extract type information at runtime, you need to use the reflect package. This is the way Go's encoding/json and other similar packages work.
If you want to operate on the types defined in a Go source file, you can use the go.parser package to read and parse a source file into an AST, then traverse the AST for the elements you want to examine.
It seems there is no way to load the type information extracted from the source code(please correct me if I'm wrong). The only solution is to create/generate a package (main), inject it with all the types extracted from the target source code file, build/compile it, execute it and collect the encoded data from stdout.

How to handle configuration in Go [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
The community reviewed whether to reopen this question 12 months ago and left it closed:
Opinion-based Update the question so it can be answered with facts and citations by editing this post.
Improve this question
What is the preferred way to handle configuration parameters for a Go program (the kind of stuff one might use properties files or ini files for, in other contexts)?
The JSON format worked for me quite well. The
standard library offers methods to write the data structure indented, so it is quite
readable.
See also this golang-nuts thread.
The benefits of JSON are that it is fairly simple to parse and human readable/editable
while offering semantics for lists and mappings (which can become quite handy), which
is not the case with many ini-type config parsers.
Example usage:
conf.json:
{
"Users": ["UserA","UserB"],
"Groups": ["GroupA"]
}
Program to read the configuration
import (
"encoding/json"
"os"
"fmt"
)
type Configuration struct {
Users []string
Groups []string
}
file, _ := os.Open("conf.json")
defer file.Close()
decoder := json.NewDecoder(file)
configuration := Configuration{}
err := decoder.Decode(&configuration)
if err != nil {
fmt.Println("error:", err)
}
fmt.Println(configuration.Users) // output: [UserA, UserB]
Another option is to use TOML, which is an INI-like format created by Tom Preston-Werner. I built a Go parser for it that is extensively tested. You can use it like other options proposed here. For example, if you have this TOML data in something.toml
Age = 198
Cats = [ "Cauchy", "Plato" ]
Pi = 3.14
Perfection = [ 6, 28, 496, 8128 ]
DOB = 1987-07-05T05:45:00Z
Then you can load it into your Go program with something like
type Config struct {
Age int
Cats []string
Pi float64
Perfection []int
DOB time.Time
}
var conf Config
if _, err := toml.DecodeFile("something.toml", &conf); err != nil {
// handle error
}
Viper is a golang configuration management system that works with JSON, YAML, and TOML. It looks pretty interesting.
I usually use JSON for more complicated data structures. The downside is that you easily end up with a bunch of code to tell the user where the error was, various edge cases and what not.
For base configuration (api keys, port numbers, ...) I've had very good luck with the gcfg package. It is based on the git config format.
From the documentation:
Sample config:
; Comment line
[section]
name = value # Another comment
flag # implicit value for bool is true
Go struct:
type Config struct {
Section struct {
Name string
Flag bool
}
}
And the code needed to read it:
var cfg Config
err := gcfg.ReadFileInto(&cfg, "myconfig.gcfg")
It also supports slice values, so you can allow specifying a key multiple times and other nice features like that.
Just use standard go flags with iniflags.
go flags
import "flag"
var nFlag = flag.Int("n", 1234, "help message for flag n")
iniflags
package main
import (
"flag"
...
"github.com/vharitonsky/iniflags"
...
)
var (
flag1 = flag.String("flag1", "default1", "Description1")
...
flagN = flag.Int("flagN", 123, "DescriptionN")
)
func main() {
iniflags.Parse() // use instead of flag.Parse()
}
Standard go flags have the following benefits:
Idiomatic.
Easy to use. Flags can be easily added and scattered across arbitrary packages your project uses.
Flags have out-of-the-box support for default values and description.
Flags provide standard 'help' output with default values and description.
The only drawback standard go flags have - is management problems when the number of flags used in your app becomes too large.
Iniflags elegantly solves this problem: just modify two lines in your main package and it magically gains support for reading flag values from ini file. Flags from ini files can be overriden by passing new values in command-line.
See also https://groups.google.com/forum/#!topic/golang-nuts/TByzyPgoAQE for details.
I have started using Gcfg which uses Ini-like files. It's simple - if you want something simple, this is a good choice.
Here's the loading code I am using currently, which has default settings and allows command line flags (not shown) that override some of my config:
package util
import (
"code.google.com/p/gcfg"
)
type Config struct {
Port int
Verbose bool
AccessLog string
ErrorLog string
DbDriver string
DbConnection string
DbTblPrefix string
}
type configFile struct {
Server Config
}
const defaultConfig = `
[server]
port = 8000
verbose = false
accessLog = -
errorLog = -
dbDriver = mysql
dbConnection = testuser:TestPasswd9#/test
dbTblPrefix =
`
func LoadConfiguration(cfgFile string, port int, verbose bool) Config {
var err error
var cfg configFile
if cfgFile != "" {
err = gcfg.ReadFileInto(&cfg, cfgFile)
} else {
err = gcfg.ReadStringInto(&cfg, defaultConfig)
}
PanicOnError(err)
if port != 0 {
cfg.Server.Port = port
}
if verbose {
cfg.Server.Verbose = true
}
return cfg.Server
}
have a look at gonfig
// load
config, _ := gonfig.FromJson(myJsonFile)
// read with defaults
host, _ := config.GetString("service/host", "localhost")
port, _ := config.GetInt("service/port", 80)
test, _ := config.GetBool("service/testing", false)
rate, _ := config.GetFloat("service/rate", 0.0)
// parse section into target structure
config.GetAs("service/template", &template)
https://github.com/spf13/viper and https://github.com/zpatrick/go-config are a pretty good libraries for configuration files.
Use toml like this article Reading config files the Go way
I wrote a simple ini config library in golang.
https://github.com/c4pt0r/cfg
goroutine-safe, easy to use
package cfg
import (
"testing"
)
func TestCfg(t *testing.T) {
c := NewCfg("test.ini")
if err := c.Load() ; err != nil {
t.Error(err)
}
c.WriteInt("hello", 42)
c.WriteString("hello1", "World")
v, err := c.ReadInt("hello", 0)
if err != nil || v != 42 {
t.Error(err)
}
v1, err := c.ReadString("hello1", "")
if err != nil || v1 != "World" {
t.Error(err)
}
if err := c.Save(); err != nil {
t.Error(err)
}
}
===================Update=======================
Recently I need an INI parser with section support, and I write a simple package:
github.com/c4pt0r/cfg
u can parse INI like using "flag" package:
package main
import (
"log"
"github.com/c4pt0r/ini"
)
var conf = ini.NewConf("test.ini")
var (
v1 = conf.String("section1", "field1", "v1")
v2 = conf.Int("section1", "field2", 0)
)
func main() {
conf.Parse()
log.Println(*v1, *v2)
}
You might also be interested in go-libucl, a set of Go bindings for UCL, the Universal Configuration Language. UCL is a bit like JSON, but with better support for humans: it supports comments and human-readable constructs like SI multipliers (10k, 40M, etc.) and has a little bit less boilerplate (e.g., quotes around keys). It's actually pretty close to the nginx configuration file format, if you're already familiar with that.
I agree with nemo and I wrote a little tool to make it all real easy.
bitbucket.org/gotamer/cfg is a json configuration package
You define your config items in your application as a struct.
A json config file template from your struct is saved on the first run
You can save runtime modifications to the config
See doc.go for an example
I tried JSON. It worked. But I hate having to create the struct of the exact fields and types I might be setting. To me that was a pain. I noticed it was the method used by all the configuration options I could find. Maybe my background in dynamic languages makes me blind to the benefits of such verboseness. I made a new simple config file format, and a more dynamic-ish lib for reading it out.
https://github.com/chrisftw/ezconf
I am pretty new to the Go world, so it might not be the Go way. But it works, it is pretty quick, and super simple to use.
Pros
Super simple
Less code
Cons
No Arrays or Map types
Very flat file format
Non-standard conf files
Does have a little convention built-in, which I now if frowned upon in general in Go community. (Looks for config file in the config directory)

Resources