Skip to content

Commit 5a94f1a

Browse files
author
Jason Yellick
committed
[FAB-4101] Proto translator nested msg component
The proto translation framework introduced in FAB-4100 needs to be recursively applied to sub-messages within proto messages. This CR adds a nested proto msg handler to the proto translation framework. Change-Id: Ib992908c431a1c5fe376b44070dec32a4b675675 Signed-off-by: Jason Yellick <jyellick@us.ibm.com>
1 parent de54331 commit 5a94f1a

File tree

5 files changed

+304
-17
lines changed

5 files changed

+304
-17
lines changed

common/tools/protolator/json.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ type protoField interface {
5454
PopulateTo() (interface{}, error)
5555
}
5656

57+
var (
58+
protoMsgType = reflect.TypeOf((*proto.Message)(nil)).Elem()
59+
mapStringInterfaceType = reflect.TypeOf(map[string]interface{}{})
60+
)
61+
5762
type baseField struct {
5863
msg proto.Message
5964
name string
@@ -235,7 +240,11 @@ func jsonToMap(marshaled []byte) (map[string]interface{}, error) {
235240
// The factory implementations, listed in order of most greedy to least.
236241
// Factories listed lower, may depend on factories listed higher being
237242
// evaluated first.
238-
var fieldFactories = []protoFieldFactory{}
243+
var fieldFactories = []protoFieldFactory{
244+
nestedSliceFieldFactory{},
245+
nestedMapFieldFactory{},
246+
nestedFieldFactory{},
247+
}
239248

240249
func protoFields(msg proto.Message, uMsg proto.Message) ([]protoField, error) {
241250
var result []protoField

common/tools/protolator/nested.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
Copyright IBM Corp. 2017 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package protolator
18+
19+
import (
20+
"reflect"
21+
22+
"github.com/golang/protobuf/proto"
23+
)
24+
25+
func nestedFrom(value interface{}, destType reflect.Type) (reflect.Value, error) {
26+
tree := value.(map[string]interface{}) // Safe, already checked
27+
result := reflect.New(destType.Elem())
28+
nMsg := result.Interface().(proto.Message) // Safe, already checked
29+
if err := recursivelyPopulateMessageFromTree(tree, nMsg); err != nil {
30+
return reflect.Value{}, err
31+
}
32+
return result, nil
33+
}
34+
35+
func nestedTo(value reflect.Value) (interface{}, error) {
36+
nMsg := value.Interface().(proto.Message) // Safe, already checked
37+
return recursivelyCreateTreeFromMessage(nMsg)
38+
}
39+
40+
type nestedFieldFactory struct{}
41+
42+
func (nff nestedFieldFactory) Handles(msg proto.Message, fieldName string, fieldType reflect.Type, fieldValue reflect.Value) bool {
43+
return fieldType.Kind() == reflect.Ptr && fieldType.AssignableTo(protoMsgType)
44+
}
45+
46+
func (nff nestedFieldFactory) NewProtoField(msg proto.Message, fieldName string, fieldType reflect.Type, fieldValue reflect.Value) (protoField, error) {
47+
return &plainField{
48+
baseField: baseField{
49+
msg: msg,
50+
name: fieldName,
51+
fType: mapStringInterfaceType,
52+
vType: fieldType,
53+
value: fieldValue,
54+
},
55+
populateFrom: nestedFrom,
56+
populateTo: nestedTo,
57+
}, nil
58+
}
59+
60+
type nestedMapFieldFactory struct{}
61+
62+
func (nmff nestedMapFieldFactory) Handles(msg proto.Message, fieldName string, fieldType reflect.Type, fieldValue reflect.Value) bool {
63+
return fieldType.Kind() == reflect.Map && fieldType.Elem().AssignableTo(protoMsgType)
64+
}
65+
66+
func (nmff nestedMapFieldFactory) NewProtoField(msg proto.Message, fieldName string, fieldType reflect.Type, fieldValue reflect.Value) (protoField, error) {
67+
return &mapField{
68+
baseField: baseField{
69+
msg: msg,
70+
name: fieldName,
71+
fType: mapStringInterfaceType,
72+
vType: fieldType,
73+
value: fieldValue,
74+
},
75+
populateFrom: func(k string, v interface{}, dT reflect.Type) (reflect.Value, error) {
76+
return nestedFrom(v, dT)
77+
},
78+
populateTo: func(k string, v reflect.Value) (interface{}, error) {
79+
return nestedTo(v)
80+
},
81+
}, nil
82+
}
83+
84+
type nestedSliceFieldFactory struct{}
85+
86+
func (nmff nestedSliceFieldFactory) Handles(msg proto.Message, fieldName string, fieldType reflect.Type, fieldValue reflect.Value) bool {
87+
return fieldType.Kind() == reflect.Slice && fieldType.Elem().AssignableTo(protoMsgType)
88+
}
89+
90+
func (nmff nestedSliceFieldFactory) NewProtoField(msg proto.Message, fieldName string, fieldType reflect.Type, fieldValue reflect.Value) (protoField, error) {
91+
return &sliceField{
92+
baseField: baseField{
93+
msg: msg,
94+
name: fieldName,
95+
fType: mapStringInterfaceType,
96+
vType: fieldType,
97+
value: fieldValue,
98+
},
99+
populateFrom: func(i int, v interface{}, dT reflect.Type) (reflect.Value, error) {
100+
return nestedFrom(v, dT)
101+
},
102+
populateTo: func(i int, v reflect.Value) (interface{}, error) {
103+
return nestedTo(v)
104+
},
105+
}, nil
106+
}
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
/*
2+
Copyright IBM Corp. 2017 All Rights Reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package protolator
18+
19+
import (
20+
"bytes"
21+
"testing"
22+
23+
"github.com/hyperledger/fabric/common/tools/protolator/testprotos"
24+
25+
"github.com/stretchr/testify/assert"
26+
)
27+
28+
func TestPlainNestedMsg(t *testing.T) {
29+
fromPrefix := "from"
30+
toPrefix := "to"
31+
tppff := &testProtoPlainFieldFactory{
32+
fromPrefix: fromPrefix,
33+
toPrefix: toPrefix,
34+
}
35+
36+
fieldFactories = []protoFieldFactory{tppff}
37+
38+
pfValue := "foo"
39+
startMsg := &testprotos.NestedMsg{
40+
PlainNestedField: &testprotos.SimpleMsg{
41+
PlainField: pfValue,
42+
},
43+
}
44+
45+
var buffer bytes.Buffer
46+
assert.NoError(t, DeepMarshalJSON(&buffer, startMsg))
47+
newMsg := &testprotos.NestedMsg{}
48+
assert.NoError(t, DeepUnmarshalJSON(bytes.NewReader(buffer.Bytes()), newMsg))
49+
assert.NotEqual(t, fromPrefix+toPrefix+startMsg.PlainNestedField.PlainField, newMsg.PlainNestedField.PlainField)
50+
51+
fieldFactories = []protoFieldFactory{tppff, nestedFieldFactory{}}
52+
53+
buffer.Reset()
54+
assert.NoError(t, DeepMarshalJSON(&buffer, startMsg))
55+
assert.NoError(t, DeepUnmarshalJSON(bytes.NewReader(buffer.Bytes()), newMsg))
56+
assert.Equal(t, fromPrefix+toPrefix+startMsg.PlainNestedField.PlainField, newMsg.PlainNestedField.PlainField)
57+
}
58+
59+
func TestMapNestedMsg(t *testing.T) {
60+
fromPrefix := "from"
61+
toPrefix := "to"
62+
tppff := &testProtoPlainFieldFactory{
63+
fromPrefix: fromPrefix,
64+
toPrefix: toPrefix,
65+
}
66+
67+
fieldFactories = []protoFieldFactory{tppff}
68+
69+
pfValue := "foo"
70+
mapKey := "bar"
71+
startMsg := &testprotos.NestedMsg{
72+
MapNestedField: map[string]*testprotos.SimpleMsg{
73+
mapKey: &testprotos.SimpleMsg{
74+
PlainField: pfValue,
75+
},
76+
},
77+
}
78+
79+
var buffer bytes.Buffer
80+
assert.NoError(t, DeepMarshalJSON(&buffer, startMsg))
81+
newMsg := &testprotos.NestedMsg{}
82+
assert.NoError(t, DeepUnmarshalJSON(bytes.NewReader(buffer.Bytes()), newMsg))
83+
assert.NotEqual(t, fromPrefix+toPrefix+startMsg.MapNestedField[mapKey].PlainField, newMsg.MapNestedField[mapKey].PlainField)
84+
85+
fieldFactories = []protoFieldFactory{tppff, nestedMapFieldFactory{}}
86+
87+
buffer.Reset()
88+
assert.NoError(t, DeepMarshalJSON(&buffer, startMsg))
89+
assert.NoError(t, DeepUnmarshalJSON(bytes.NewReader(buffer.Bytes()), newMsg))
90+
assert.Equal(t, fromPrefix+toPrefix+startMsg.MapNestedField[mapKey].PlainField, newMsg.MapNestedField[mapKey].PlainField)
91+
}
92+
93+
func TestSliceNestedMsg(t *testing.T) {
94+
fromPrefix := "from"
95+
toPrefix := "to"
96+
tppff := &testProtoPlainFieldFactory{
97+
fromPrefix: fromPrefix,
98+
toPrefix: toPrefix,
99+
}
100+
101+
fieldFactories = []protoFieldFactory{tppff}
102+
103+
pfValue := "foo"
104+
startMsg := &testprotos.NestedMsg{
105+
SliceNestedField: []*testprotos.SimpleMsg{
106+
&testprotos.SimpleMsg{
107+
PlainField: pfValue,
108+
},
109+
},
110+
}
111+
112+
var buffer bytes.Buffer
113+
assert.NoError(t, DeepMarshalJSON(&buffer, startMsg))
114+
newMsg := &testprotos.NestedMsg{}
115+
assert.NoError(t, DeepUnmarshalJSON(bytes.NewReader(buffer.Bytes()), newMsg))
116+
assert.NotEqual(t, fromPrefix+toPrefix+startMsg.SliceNestedField[0].PlainField, newMsg.SliceNestedField[0].PlainField)
117+
118+
fieldFactories = []protoFieldFactory{tppff, nestedSliceFieldFactory{}}
119+
120+
buffer.Reset()
121+
assert.NoError(t, DeepMarshalJSON(&buffer, startMsg))
122+
assert.NoError(t, DeepUnmarshalJSON(bytes.NewReader(buffer.Bytes()), newMsg))
123+
assert.Equal(t, fromPrefix+toPrefix+startMsg.SliceNestedField[0].PlainField, newMsg.SliceNestedField[0].PlainField)
124+
}

common/tools/protolator/testprotos/sample.pb.go

Lines changed: 57 additions & 16 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

common/tools/protolator/testprotos/sample.proto

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,10 @@ message SimpleMsg {
2727
map<string, string> map_field = 2;
2828
repeated string slice_field = 3;
2929
}
30+
31+
// NestedMsg is designed to test the nested message component
32+
message NestedMsg {
33+
SimpleMsg plain_nested_field = 1;
34+
map<string,SimpleMsg> map_nested_field = 2;
35+
repeated SimpleMsg slice_nested_field = 3;
36+
}

0 commit comments

Comments
 (0)