Skip to content

Commit

Permalink
Add build json template command (#15)
Browse files Browse the repository at this point in the history
  • Loading branch information
axl1232 authored Jul 4, 2024
1 parent 27a08dc commit efce9c9
Show file tree
Hide file tree
Showing 6 changed files with 308 additions and 2 deletions.
40 changes: 39 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ $ protokaf produce -h
### Examples
This proto file will be used in the examples below.

`api/example.ptoto`
`api/example.proto`
```protobuf
syntax = "proto3";
Expand Down Expand Up @@ -127,6 +127,44 @@ $ protokaf produce HelloRequest -t test -d '{"name": "Alice", "age": 11}'
$ protokaf produce --template-functions-print
```

## Build json template by proto file
This can be useful for creating body for produce command
```sh
$ protokaf build HelloRequest --proto internal/proto/testdata/example.proto
```
For proto file
```protobuf
syntax = "proto3";
package example;
message HelloRequest {
enum Status {
PENDING = 0;
COMPLETED = 1;
}
string name = 1;
int32 age = 2;
optional float amount = 4;
Status status = 5;
repeated string keys = 6;
}
```
command will print
```json
{
"name": "",
"age": 0,
"amount": 0,
"status": "PENDING",
"keys": [
""
]
}

```

## Consume
### Help
```sh
Expand Down
77 changes: 77 additions & 0 deletions cmd/cmd_build_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package cmd

import (
"testing"

"github.com/stretchr/testify/require"
)

func Test_NewBuildCmd(t *testing.T) {
cmd := NewBuildCmd()
NewFlags(cmd).Init()
cmd.SetArgs([]string{"ExampleMessage", "--proto", "../internal/proto/testdata/types.proto"})

expected := `{
"int32Field": 0,
"int64Field": "0",
"uint32Field": 0,
"uint64Field": "0",
"sint32Field": 0,
"sint64Field": "0",
"fixed32Field": 0,
"fixed64Field": "0",
"sfixed32Field": 0,
"sfixed64Field": "0",
"floatField": 0,
"doubleField": 0,
"boolField": true,
"stringField": "",
"bytesField": "",
"enumField": "UNKNOWN",
"messageField": {
"nestedInt32": 0,
"nestedString": ""
},
"repeatedInt32Field": [
0
],
"repeatedStringField": [
""
],
"mapStringInt32Field": {
"": 0
},
"mapInt32MessageField": {
"0": {
"nestedInt32": 0,
"nestedString": ""
}
},
"option1": 0,
"anyField": null,
"timestampField": "1970-01-01T00:00:00Z",
"durationField": "0s",
"structField": {
"": null
},
"valueField": null,
"listValueField": [
null
],
"boolValueField": true,
"bytesValueField": null,
"doubleValueField": 0,
"floatValueField": 0,
"int32ValueField": 0,
"int64ValueField": "0",
"stringValueField": "",
"uint32ValueField": 0,
"uint64ValueField": "0"
}`

stdout, stderr, err := getCommandOut(t, cmd)

require.Nil(t, err)
require.Equal(t, "", stderr)
require.Contains(t, stdout, expected)
}
110 changes: 110 additions & 0 deletions cmd/cmd_buld.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package cmd

import (
"github.com/golang/protobuf/jsonpb"
"github.com/jhump/protoreflect/desc"
"github.com/jhump/protoreflect/dynamic"
"github.com/spf13/cobra"
"google.golang.org/protobuf/types/descriptorpb"
"google.golang.org/protobuf/types/known/anypb"
)

func NewBuildCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "build <MessageName>",
Short: "Build json by proto message",
Args: messageNameRequired,
RunE: func(cmd *cobra.Command, args []string) (err error) {
// parse protofiles & create proto object
p, err := parseProtofiles()
if err != nil {
return
}

messageDescriptor, err := findMessage(p, args[0])
if err != nil {
return
}

msg := buildMessage(dynamic.NewMessage(messageDescriptor))

b, err := msg.MarshalJSONPB(&jsonpb.Marshaler{
EmitDefaults: true,
Indent: " ",
})
if err != nil {
return
}

cmd.Println(string(b))

return
},
}

return cmd
}

func buildMessage(message *dynamic.Message) *dynamic.Message {
for _, field := range message.GetMessageDescriptor().GetFields() {
switch {
case field.IsRepeated():
message.SetField(field, []interface{}{buildDefaultValue(field)})
case field.IsMap():
message.SetField(
field,
map[interface{}]interface{}{
buildDefaultValue(field.GetMapKeyType()): buildDefaultValue(field.GetMapValueType()),
},
)
case field.GetOneOf() != nil:
oneOfField := field.GetOneOf().GetChoices()[0]
message.SetField(oneOfField, buildDefaultValue(oneOfField))
default:
message.SetField(field, buildDefaultValue(field))
}
}

return message
}

func buildDefaultValue(field *desc.FieldDescriptor) interface{} {
switch field.GetType() {
case descriptorpb.FieldDescriptorProto_TYPE_FIXED32,
descriptorpb.FieldDescriptorProto_TYPE_UINT32:
return uint32(0)
case descriptorpb.FieldDescriptorProto_TYPE_SFIXED32,
descriptorpb.FieldDescriptorProto_TYPE_INT32,
descriptorpb.FieldDescriptorProto_TYPE_SINT32:
return int32(0)
case descriptorpb.FieldDescriptorProto_TYPE_FIXED64,
descriptorpb.FieldDescriptorProto_TYPE_UINT64:
return uint64(0)
case descriptorpb.FieldDescriptorProto_TYPE_SFIXED64,
descriptorpb.FieldDescriptorProto_TYPE_INT64,
descriptorpb.FieldDescriptorProto_TYPE_SINT64:
return int64(0)
case descriptorpb.FieldDescriptorProto_TYPE_FLOAT:
return float32(0)
case descriptorpb.FieldDescriptorProto_TYPE_DOUBLE:
return float64(0)
case descriptorpb.FieldDescriptorProto_TYPE_BOOL:
return true
case descriptorpb.FieldDescriptorProto_TYPE_BYTES:
return []byte(nil)
case descriptorpb.FieldDescriptorProto_TYPE_STRING:
return ""
case descriptorpb.FieldDescriptorProto_TYPE_ENUM:
return field.GetEnumType().GetValues()[0].GetNumber()
case descriptorpb.FieldDescriptorProto_TYPE_MESSAGE:
if field.GetMessageType().GetFullyQualifiedName() == "google.protobuf.Any" {
val, _ := anypb.New(nil)

return val
}

return buildMessage(dynamic.NewMessage(field.GetMessageType()))
}

return nil
}
1 change: 1 addition & 0 deletions cmd/cmd_root.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ func NewRootCmd() *cobra.Command {
NewProduceCmd(),
NewConsumeCmd(),
NewListCmd(),
NewBuildCmd(),
)

return cmd
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ require (
github.com/uber/jaeger-client-go v2.29.1+incompatible
github.com/xdg/scram v1.0.3
go.uber.org/zap v1.18.1
google.golang.org/protobuf v1.33.0
)

require (
Expand Down Expand Up @@ -55,7 +56,6 @@ require (
golang.org/x/sys v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
Expand Down
80 changes: 80 additions & 0 deletions internal/proto/testdata/types.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
syntax = "proto3";

package example;

import "google/protobuf/any.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/struct.proto";
import "google/protobuf/wrappers.proto";

// Enum definition
enum ExampleEnum {
UNKNOWN = 0;
OPTION_ONE = 1;
OPTION_TWO = 2;
}

// Message definition
message ExampleMessage {
// Scalar types
int32 int32_field = 1;
int64 int64_field = 2;
uint32 uint32_field = 3;
uint64 uint64_field = 4;
sint32 sint32_field = 5;
sint64 sint64_field = 6;
fixed32 fixed32_field = 7;
fixed64 fixed64_field = 8;
sfixed32 sfixed32_field = 9;
sfixed64 sfixed64_field = 10;
float float_field = 11;
double double_field = 12;
bool bool_field = 13;
string string_field = 14;
bytes bytes_field = 15;

// Enum type
ExampleEnum enum_field = 16;

// Nested message
message NestedMessage {
int32 nested_int32 = 1;
string nested_string = 2;
}

// Message type
NestedMessage message_field = 17;

// Repeated fields
repeated int32 repeated_int32_field = 18;
repeated string repeated_string_field = 19;

// Map fields
map<string, int32> map_string_int32_field = 20;
map<int32, NestedMessage> map_int32_message_field = 21;

// Oneof field
oneof my_oneof {
int32 option1 = 22;
string option2 = 23;
NestedMessage option3 = 24;
}

// Well-known types
google.protobuf.Any any_field = 25;
google.protobuf.Timestamp timestamp_field = 26;
google.protobuf.Duration duration_field = 27;
google.protobuf.Struct struct_field = 28;
google.protobuf.Value value_field = 29;
google.protobuf.ListValue list_value_field = 30;
google.protobuf.BoolValue bool_value_field = 31;
google.protobuf.BytesValue bytes_value_field = 32;
google.protobuf.DoubleValue double_value_field = 33;
google.protobuf.FloatValue float_value_field = 34;
google.protobuf.Int32Value int32_value_field = 35;
google.protobuf.Int64Value int64_value_field = 36;
google.protobuf.StringValue string_value_field = 37;
google.protobuf.UInt32Value uint32_value_field = 38;
google.protobuf.UInt64Value uint64_value_field = 39;
}

0 comments on commit efce9c9

Please sign in to comment.