/
attribute_transform.go
148 lines (128 loc) · 4.54 KB
/
attribute_transform.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package subtypes
import (
"fmt"
"github.com/hashicorp/boundary/internal/servers/controller/handlers"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/reflect/protoreflect"
"google.golang.org/protobuf/types/known/structpb"
)
func convertAttributesToSubtype(msg proto.Message, st Subtype) error {
r := msg.ProtoReflect()
d := r.Descriptor()
defaultAttrField, err := attributeField(d, defaultSubtype)
if err != nil {
// If unable to get a default attribute field, the message is either
// not registered or not in the format needed for conversion, so no
// conversion should be performed. The most likely case here is that
// the message has not been changed to use a oneof for attributes yet.
return nil
}
stAttrField, err := attributeField(d, st)
if err != nil {
// This error should not be possible, since any registration issue
// would trigger the previous call, and if this particular subtype
// is unknown, the default should be returned.
return nil
}
if defaultAttrField == stAttrField {
// no need to convert
return nil
}
defaultAttrs, ok := r.Get(defaultAttrField).Message().Interface().(*structpb.Struct)
if !ok {
// This should not be possible since this is checked in
// (attributeRegistry).register at initialization time and would panic
// if this was the case.
return fmt.Errorf("found default attribute field that is not structpb.Struct: %s %s", d.FullName(), defaultAttrField.FullName())
}
stAttrs := r.Get(stAttrField).Message().New().Interface()
if err := handlers.StructToProto(defaultAttrs, stAttrs); err != nil {
return err
}
// implicitly clears any previously set oneof value
r.Set(stAttrField, protoreflect.ValueOfMessage(stAttrs.ProtoReflect()))
return nil
}
func convertAttributesToDefault(msg proto.Message, st Subtype) error {
r := msg.ProtoReflect()
d := r.Descriptor()
defaultAttrField, err := attributeField(d, defaultSubtype)
if err != nil {
// If unable to get a default attribute field, the message is either
// not registered or not in the format needed for conversion, so no
// conversion should be performed. The most likely case here is that
// the message has not been changed to use a oneof for attributes yet.
return nil
}
stAttrField, err := attributeField(d, st)
if err != nil {
// This error should not be possible, since any registration issue
// would trigger the previous call, and if this particular subtype
// is unknown, the default should be returned.
return nil
}
if defaultAttrField == stAttrField {
// no need to convert
return nil
}
stAttrs, ok := r.Get(stAttrField).Message().Interface().(proto.Message)
if !ok {
return fmt.Errorf("found subtype attribute field that is not proto.Message: %s %s", d.FullName(), stAttrField.FullName())
}
defaultAttrs, err := handlers.ProtoToStruct(stAttrs)
if err != nil {
return err
}
// implicitly clears any previously set oneof value
r.Set(defaultAttrField, protoreflect.ValueOfMessage(defaultAttrs.ProtoReflect()))
return nil
}
// Filterable converts a proto.Message so any subtype attributes fields are
// structed like the API so filter strings will be correctly applied. If the
// given proto.Message does not have any subtype attributes, the original
// proto.Message is returned. To determine if the message needs
// transformation, it looks for a oneof field named "attrs". It also expects
// that there is a structpb.Struct field named "attributes" as part of the
// oneof. Thus the message must be like:
//
// message Foo {
// // other fields
// oneof attrs {
// google.protobuf.Struct attributes = 100;
// // other attribute fields
// }
// }
//
// If the message does not conform to this structure,
// the original message is returned.
func Filterable(item proto.Message) (proto.Message, error) {
clone := proto.Clone(item)
r := clone.ProtoReflect()
attrsField := r.Descriptor().Oneofs().ByName("attrs")
if attrsField == nil {
return item, nil
}
defaultAttrField := attrsField.Fields().ByName("attributes")
if defaultAttrField == nil {
return item, nil
}
var attr proto.Message
var pbAttrs proto.Message
var err error
oneofField := r.WhichOneof(attrsField)
if oneofField == nil {
// attrs field is not set, nothing to do
return item, nil
}
attr = r.Get(oneofField).Message().Interface()
pbAttrs, err = handlers.ProtoToStruct(attr)
if err != nil {
return nil, err
}
r.Set(defaultAttrField, protoreflect.ValueOfMessage(pbAttrs.ProtoReflect()))
f, err := handlers.ProtoToStruct(r.Interface())
if err != nil {
return nil, err
}
return f, nil
}