Skip to content

Commit

Permalink
Feature / breaking change: add preserve_non_string_maps and change … (
Browse files Browse the repository at this point in the history
#3)

* Feature / breaking change: add `preserve_non_string_maps` and change default behavior to turn all maps into string-keyed ones.

* Fix tests
  • Loading branch information
dorner authored Feb 27, 2024
1 parent b108644 commit 5714441
Show file tree
Hide file tree
Showing 28 changed files with 564 additions and 209 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

# UNRELEASED

- Feature / breaking change: add `preserve_non_string_maps` and change default behavior to turn all maps into string-keyed ones.

# 0.6.1 - 2024-02-27

- Fix: `collapse_fields` was only working on the first instance of the field, not when it was found inside a map etc. later on.
Expand Down
52 changes: 52 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,58 @@ enum Category {
}
```

* `preserve_non_string_maps` - if set to true, will replace maps with non-string keys with records. E.g. if you have a map like:
```protobuf
message MyRecord {
map<int32, string> my_field = 1;
}
```

...with this option off, it will be translated into:

```json
{
"type": "record",
"name": "MyRecord",
"fields": [
{
"name": "my_field",
"type": {
"type": "map",
"values": "string"
}
}
]
}
```

...but with it on, it will be translated into:

```json
{
"type": "record",
"name": "MyRecord",
"fields": [
{
"name": "my_field",
"type": {
"type": "record",
"fields": [
{
"name": "key",
"type": "int"
},
{
"name": "value",
"type": "string"
}
]
}
}
]
}
```

---

To Do List:
Expand Down
20 changes: 11 additions & 9 deletions avro/record.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,19 @@ func (t Record) ToJSON(types *TypeRepo) (any, error) {
return jsonMap, nil
}

func RecordFromProto(proto *descriptorpb.DescriptorProto, namespace string) []NamedType {
func RecordFromProto(proto *descriptorpb.DescriptorProto, namespace string, typeRepo *TypeRepo) []NamedType {
// Protobuf `map` is actually a record with a `map_entry` boolean set, and two fields ("key" and "value").
// Avro only supports maps with string keys, but Protobuf supports maps with any key type.
// If we detect a string key, we can turn this into an Avro map, but otherwise we have to leave it as a record.
if proto.Options.GetMapEntry() && proto.Field[0].GetType() == descriptorpb.FieldDescriptorProto_TYPE_STRING {
return []NamedType{
Map{
Namespace: namespace,
Name: proto.GetName(),
Values: FieldTypeFromProto(proto.Field[1]),
},
if proto.Options.GetMapEntry() {
if !typeRepo.PreserveNonStringMaps || proto.Field[0].GetType() == descriptorpb.FieldDescriptorProto_TYPE_STRING {
return []NamedType{
Map{
Namespace: namespace,
Name: proto.GetName(),
Values: FieldTypeFromProto(proto.Field[1]),
},
}
}
}

Expand All @@ -61,7 +63,7 @@ func RecordFromProto(proto *descriptorpb.DescriptorProto, namespace string) []Na
nested := make([]NamedType, len(proto.NestedType))
// enums := make([]Enum, len(proto.EnumType))
for i, field := range proto.NestedType {
nested[i] = RecordFromProto(field, fmt.Sprintf("%s.%s", namespace, proto.GetName()))[0]
nested[i] = RecordFromProto(field, fmt.Sprintf("%s.%s", namespace, proto.GetName()), typeRepo)[0]
}
//for i, field := range proto.EnumType {
// enums[i] = EnumFromProto(field)
Expand Down
2 changes: 2 additions & 0 deletions avro/typeRepo.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type TypeRepo struct {
NamespaceMap map[string]string
CollapseFields []string
RemoveEnumPrefixes bool
PreserveNonStringMaps bool
}

func NewTypeRepo(params input.Params) *TypeRepo {
Expand All @@ -21,6 +22,7 @@ func NewTypeRepo(params input.Params) *TypeRepo {
NamespaceMap: params.NamespaceMap,
CollapseFields: params.CollapseFields,
RemoveEnumPrefixes: params.RemoveEnumPrefixes,
PreserveNonStringMaps: params.PreserveNonStringMaps,
}
}

Expand Down
3 changes: 3 additions & 0 deletions input/params.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type Params struct {
NamespaceMap map[string]string
CollapseFields []string
RemoveEnumPrefixes bool
PreserveNonStringMaps bool
}

func ReadRequest() (*pluginpb.CodeGeneratorRequest, error) {
Expand Down Expand Up @@ -60,6 +61,8 @@ func ParseParams(req *pluginpb.CodeGeneratorRequest) Params {
params.CollapseFields = strings.Split(v, ";")
} else if k == "remove_enum_prefixes" {
params.RemoveEnumPrefixes = v == "true"
} else if k == "preserve_non_string_maps" {
params.PreserveNonStringMaps = true
}
}
return params
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ var params input.Params
var typeRepo *avro.TypeRepo

func processMessage(proto *descriptorpb.DescriptorProto, protoPackage string) {
records := avro.RecordFromProto(proto, protoPackage)
records := avro.RecordFromProto(proto, protoPackage, typeRepo)
for _, record := range records {
typeRepo.AddType(record)
}
Expand Down
13 changes: 11 additions & 2 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,13 @@ func runTest(t *testing.T, directory string, options map[string]string) {
}
protoc(t, args)

assertEqualFiles(t, workdir + "/testdata/" + directory, tmpdir)

testDir := workdir + "/testdata/" + directory
if os.Getenv("UPDATE_SNAPSHOTS") != "" {
cmd := exec.Command("/bin/sh", "-c", fmt.Sprintf("cp %v/* %v", tmpdir, testDir))
cmd.Run()
} else {
assertEqualFiles(t, testDir, tmpdir)
}
}

func Test_Base(t *testing.T) {
Expand All @@ -85,6 +90,10 @@ func Test_NamespaceMap(t *testing.T) {
runTest(t, "namespace_map", map[string]string{"namespace_map": "testdata:mynamespace"})
}

func Test_PreserveNonStringMaps(t *testing.T) {
runTest(t, "preserve_non_string_maps", map[string]string{"preserve_non_string_maps": "true"})
}

func assertEqualFiles(t *testing.T, original, generated string) {
names, err := fileNames(original, false)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion testdata/base/AOneOf.avsc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"namespace": "testdata",
"fields": [
{
"name": "a_oneof",
"name": "oneof_types",
"type": [
{
"type": "record",
Expand Down
22 changes: 3 additions & 19 deletions testdata/base/Foobar.avsc
Original file line number Diff line number Diff line change
Expand Up @@ -110,26 +110,10 @@
{
"name": "a_yowza_map",
"type": {
"type": "array",
"items": {
"type": "record",
"name": "AYowzaMapEntry",
"namespace": "testdata.Foobar",
"fields": [
{
"name": "key",
"type": "boolean",
"default": false
},
{
"name": "value",
"type": "testdata.Yowza",
"default": ""
}
]
}
"type": "map",
"values": "testdata.Yowza"
},
"default": []
"default": {}
}
]
}
24 changes: 4 additions & 20 deletions testdata/base/Widget.avsc
Original file line number Diff line number Diff line change
Expand Up @@ -130,26 +130,10 @@
{
"name": "a_yowza_map",
"type": {
"type": "array",
"items": {
"type": "record",
"name": "AYowzaMapEntry",
"namespace": "testdata.Foobar",
"fields": [
{
"name": "key",
"type": "boolean",
"default": false
},
{
"name": "value",
"type": "testdata.Yowza",
"default": ""
}
]
}
"type": "map",
"values": "testdata.Yowza"
},
"default": []
"default": {}
}
]
},
Expand Down Expand Up @@ -192,7 +176,7 @@
"namespace": "testdata",
"fields": [
{
"name": "a_oneof",
"name": "oneof_types",
"type": [
{
"type": "record",
Expand Down
2 changes: 1 addition & 1 deletion testdata/collapse_fields/AOneOf.avsc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"namespace": "testdata",
"fields": [
{
"name": "a_oneof",
"name": "oneof_types",
"type": [
{
"type": "record",
Expand Down
28 changes: 0 additions & 28 deletions testdata/collapse_fields/AYowzaMapEntry.avsc

This file was deleted.

22 changes: 3 additions & 19 deletions testdata/collapse_fields/Foobar.avsc
Original file line number Diff line number Diff line change
Expand Up @@ -110,26 +110,10 @@
{
"name": "a_yowza_map",
"type": {
"type": "array",
"items": {
"type": "record",
"name": "AYowzaMapEntry",
"namespace": "testdata.Foobar",
"fields": [
{
"name": "key",
"type": "boolean",
"default": false
},
{
"name": "value",
"type": "testdata.Yowza",
"default": ""
}
]
}
"type": "map",
"values": "testdata.Yowza"
},
"default": []
"default": {}
}
]
}
24 changes: 4 additions & 20 deletions testdata/collapse_fields/Widget.avsc
Original file line number Diff line number Diff line change
Expand Up @@ -130,26 +130,10 @@
{
"name": "a_yowza_map",
"type": {
"type": "array",
"items": {
"type": "record",
"name": "AYowzaMapEntry",
"namespace": "testdata.Foobar",
"fields": [
{
"name": "key",
"type": "boolean",
"default": false
},
{
"name": "value",
"type": "testdata.Yowza",
"default": ""
}
]
}
"type": "map",
"values": "testdata.Yowza"
},
"default": []
"default": {}
}
]
},
Expand Down Expand Up @@ -188,7 +172,7 @@
"namespace": "testdata",
"fields": [
{
"name": "a_oneof",
"name": "oneof_types",
"type": [
{
"type": "record",
Expand Down
Loading

0 comments on commit 5714441

Please sign in to comment.