integrate request builder into HTTP client for googleapis support (#157)
This commit is contained in:
116
builder/body.go
Normal file
116
builder/body.go
Normal file
@@ -0,0 +1,116 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
)
|
||||
|
||||
func buildSingleFieldBody(msg proto.Message, fieldName string) (proto.Message, error) {
|
||||
msgReflect := msg.ProtoReflect()
|
||||
|
||||
fd, found := findFieldByName(msgReflect, fieldName)
|
||||
if !found || fd == nil {
|
||||
return nil, fmt.Errorf("field %s not found", fieldName)
|
||||
}
|
||||
if !msgReflect.Has(fd) {
|
||||
return nil, fmt.Errorf("field %s is not set", fieldName)
|
||||
}
|
||||
|
||||
val := msgReflect.Get(fd)
|
||||
|
||||
if fd.Kind() == protoreflect.MessageKind {
|
||||
return val.Message().Interface(), nil
|
||||
}
|
||||
|
||||
newMsg := proto.Clone(msg)
|
||||
newMsgReflect := newMsg.ProtoReflect()
|
||||
newMsgReflect.Range(func(f protoreflect.FieldDescriptor, _ protoreflect.Value) bool {
|
||||
if f != fd {
|
||||
newMsgReflect.Clear(f)
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
return newMsg, nil
|
||||
}
|
||||
|
||||
func buildFullBody(msg proto.Message, usedFieldsPath *usedFields) (proto.Message, error) {
|
||||
var (
|
||||
msgReflect = msg.ProtoReflect()
|
||||
newMsg = msgReflect.New().Interface()
|
||||
newMsgReflect = newMsg.ProtoReflect()
|
||||
)
|
||||
|
||||
fields := msgReflect.Descriptor().Fields()
|
||||
for i := 0; i < fields.Len(); i++ {
|
||||
fd := fields.Get(i)
|
||||
fieldName := fd.JSONName()
|
||||
|
||||
if usedFieldsPath.hasTopLevelKey(fieldName) {
|
||||
continue
|
||||
}
|
||||
|
||||
val := msgReflect.Get(fd)
|
||||
if !val.IsValid() {
|
||||
continue
|
||||
}
|
||||
|
||||
// Note: order of the cases is important!
|
||||
switch {
|
||||
case fd.IsList():
|
||||
list := val.List()
|
||||
newList := newMsgReflect.Mutable(fd).List()
|
||||
|
||||
if fd.Kind() == protoreflect.MessageKind {
|
||||
for j := 0; j < list.Len(); j++ {
|
||||
elem, err := buildFullBody(list.Get(j).Message().Interface(), usedFieldsPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("recursive build full body: %w", err)
|
||||
}
|
||||
newList.Append(protoreflect.ValueOfMessage(elem.ProtoReflect()))
|
||||
}
|
||||
} else {
|
||||
for j := 0; j < list.Len(); j++ {
|
||||
newList.Append(list.Get(j))
|
||||
}
|
||||
}
|
||||
|
||||
case fd.IsMap():
|
||||
var (
|
||||
m = val.Map()
|
||||
newMap = newMsgReflect.Mutable(fd).Map()
|
||||
rangeErr error
|
||||
)
|
||||
m.Range(func(k protoreflect.MapKey, v protoreflect.Value) bool {
|
||||
if fd.MapValue().Kind() == protoreflect.MessageKind {
|
||||
elem, err := buildFullBody(v.Message().Interface(), usedFieldsPath)
|
||||
if err != nil {
|
||||
rangeErr = fmt.Errorf("recursive build full body: %w", err)
|
||||
return false
|
||||
}
|
||||
newMap.Set(k, protoreflect.ValueOfMessage(elem.ProtoReflect()))
|
||||
} else {
|
||||
newMap.Set(k, v)
|
||||
}
|
||||
return true
|
||||
})
|
||||
if rangeErr != nil {
|
||||
return nil, fmt.Errorf("map range error: %w", rangeErr)
|
||||
}
|
||||
|
||||
case fd.Kind() == protoreflect.MessageKind:
|
||||
elem, err := buildFullBody(val.Message().Interface(), usedFieldsPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("recursive build full body: %w", err)
|
||||
}
|
||||
newMsgReflect.Set(fd, protoreflect.ValueOfMessage(elem.ProtoReflect()))
|
||||
|
||||
default:
|
||||
newMsgReflect.Set(fd, val)
|
||||
}
|
||||
}
|
||||
|
||||
return newMsg, nil
|
||||
}
|
Reference in New Issue
Block a user