|
@@ -4,7 +4,9 @@ import (
|
|
|
"fmt"
|
|
"fmt"
|
|
|
"net/url"
|
|
"net/url"
|
|
|
"reflect"
|
|
"reflect"
|
|
|
|
|
+ "strconv"
|
|
|
"strings"
|
|
"strings"
|
|
|
|
|
+ "time"
|
|
|
|
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
"github.com/golang/protobuf/proto"
|
|
|
"github.com/grpc-ecosystem/grpc-gateway/utilities"
|
|
"github.com/grpc-ecosystem/grpc-gateway/utilities"
|
|
@@ -38,13 +40,15 @@ func populateFieldValueFromPath(msg proto.Message, fieldPath []string, values []
|
|
|
if m.Kind() != reflect.Ptr {
|
|
if m.Kind() != reflect.Ptr {
|
|
|
return fmt.Errorf("unexpected type %T: %v", msg, msg)
|
|
return fmt.Errorf("unexpected type %T: %v", msg, msg)
|
|
|
}
|
|
}
|
|
|
|
|
+ var props *proto.Properties
|
|
|
m = m.Elem()
|
|
m = m.Elem()
|
|
|
for i, fieldName := range fieldPath {
|
|
for i, fieldName := range fieldPath {
|
|
|
isLast := i == len(fieldPath)-1
|
|
isLast := i == len(fieldPath)-1
|
|
|
if !isLast && m.Kind() != reflect.Struct {
|
|
if !isLast && m.Kind() != reflect.Struct {
|
|
|
return fmt.Errorf("non-aggregate type in the mid of path: %s", strings.Join(fieldPath, "."))
|
|
return fmt.Errorf("non-aggregate type in the mid of path: %s", strings.Join(fieldPath, "."))
|
|
|
}
|
|
}
|
|
|
- f := fieldByProtoName(m, fieldName)
|
|
|
|
|
|
|
+ var f reflect.Value
|
|
|
|
|
+ f, props = fieldByProtoName(m, fieldName)
|
|
|
if !f.IsValid() {
|
|
if !f.IsValid() {
|
|
|
grpclog.Printf("field not found in %T: %s", msg, strings.Join(fieldPath, "."))
|
|
grpclog.Printf("field not found in %T: %s", msg, strings.Join(fieldPath, "."))
|
|
|
return nil
|
|
return nil
|
|
@@ -52,17 +56,20 @@ func populateFieldValueFromPath(msg proto.Message, fieldPath []string, values []
|
|
|
|
|
|
|
|
switch f.Kind() {
|
|
switch f.Kind() {
|
|
|
case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64, reflect.String, reflect.Uint32, reflect.Uint64:
|
|
case reflect.Bool, reflect.Float32, reflect.Float64, reflect.Int32, reflect.Int64, reflect.String, reflect.Uint32, reflect.Uint64:
|
|
|
|
|
+ if !isLast {
|
|
|
|
|
+ return fmt.Errorf("unexpected nested field %s in %s", fieldPath[i+1], strings.Join(fieldPath[:i+1], "."))
|
|
|
|
|
+ }
|
|
|
m = f
|
|
m = f
|
|
|
case reflect.Slice:
|
|
case reflect.Slice:
|
|
|
// TODO(yugui) Support []byte
|
|
// TODO(yugui) Support []byte
|
|
|
if !isLast {
|
|
if !isLast {
|
|
|
return fmt.Errorf("unexpected repeated field in %s", strings.Join(fieldPath, "."))
|
|
return fmt.Errorf("unexpected repeated field in %s", strings.Join(fieldPath, "."))
|
|
|
}
|
|
}
|
|
|
- return populateRepeatedField(f, values)
|
|
|
|
|
|
|
+ return populateRepeatedField(f, values, props)
|
|
|
case reflect.Ptr:
|
|
case reflect.Ptr:
|
|
|
if f.IsNil() {
|
|
if f.IsNil() {
|
|
|
m = reflect.New(f.Type().Elem())
|
|
m = reflect.New(f.Type().Elem())
|
|
|
- f.Set(m)
|
|
|
|
|
|
|
+ f.Set(m.Convert(f.Type()))
|
|
|
}
|
|
}
|
|
|
m = f.Elem()
|
|
m = f.Elem()
|
|
|
continue
|
|
continue
|
|
@@ -80,39 +87,73 @@ func populateFieldValueFromPath(msg proto.Message, fieldPath []string, values []
|
|
|
default:
|
|
default:
|
|
|
grpclog.Printf("too many field values: %s", strings.Join(fieldPath, "."))
|
|
grpclog.Printf("too many field values: %s", strings.Join(fieldPath, "."))
|
|
|
}
|
|
}
|
|
|
- return populateField(m, values[0])
|
|
|
|
|
|
|
+ return populateField(m, values[0], props)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// fieldByProtoName looks up a field whose corresponding protobuf field name is "name".
|
|
// fieldByProtoName looks up a field whose corresponding protobuf field name is "name".
|
|
|
// "m" must be a struct value. It returns zero reflect.Value if no such field found.
|
|
// "m" must be a struct value. It returns zero reflect.Value if no such field found.
|
|
|
-func fieldByProtoName(m reflect.Value, name string) reflect.Value {
|
|
|
|
|
|
|
+func fieldByProtoName(m reflect.Value, name string) (reflect.Value, *proto.Properties) {
|
|
|
props := proto.GetProperties(m.Type())
|
|
props := proto.GetProperties(m.Type())
|
|
|
for _, p := range props.Prop {
|
|
for _, p := range props.Prop {
|
|
|
if p.OrigName == name {
|
|
if p.OrigName == name {
|
|
|
- return m.FieldByName(p.Name)
|
|
|
|
|
|
|
+ return m.FieldByName(p.Name), p
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- return reflect.Value{}
|
|
|
|
|
|
|
+ return reflect.Value{}, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func populateRepeatedField(f reflect.Value, values []string) error {
|
|
|
|
|
|
|
+func populateRepeatedField(f reflect.Value, values []string, props *proto.Properties) error {
|
|
|
elemType := f.Type().Elem()
|
|
elemType := f.Type().Elem()
|
|
|
|
|
+
|
|
|
|
|
+ // is the destination field a slice of an enumeration type?
|
|
|
|
|
+ if enumValMap := proto.EnumValueMap(props.Enum); enumValMap != nil {
|
|
|
|
|
+ return populateFieldEnumRepeated(f, values, enumValMap)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
conv, ok := convFromType[elemType.Kind()]
|
|
conv, ok := convFromType[elemType.Kind()]
|
|
|
if !ok {
|
|
if !ok {
|
|
|
return fmt.Errorf("unsupported field type %s", elemType)
|
|
return fmt.Errorf("unsupported field type %s", elemType)
|
|
|
}
|
|
}
|
|
|
- f.Set(reflect.MakeSlice(f.Type(), len(values), len(values)))
|
|
|
|
|
|
|
+ f.Set(reflect.MakeSlice(f.Type(), len(values), len(values)).Convert(f.Type()))
|
|
|
for i, v := range values {
|
|
for i, v := range values {
|
|
|
result := conv.Call([]reflect.Value{reflect.ValueOf(v)})
|
|
result := conv.Call([]reflect.Value{reflect.ValueOf(v)})
|
|
|
if err := result[1].Interface(); err != nil {
|
|
if err := result[1].Interface(); err != nil {
|
|
|
return err.(error)
|
|
return err.(error)
|
|
|
}
|
|
}
|
|
|
- f.Index(i).Set(result[0])
|
|
|
|
|
|
|
+ f.Index(i).Set(result[0].Convert(f.Index(i).Type()))
|
|
|
}
|
|
}
|
|
|
return nil
|
|
return nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func populateField(f reflect.Value, value string) error {
|
|
|
|
|
|
|
+func populateField(f reflect.Value, value string, props *proto.Properties) error {
|
|
|
|
|
+ // Handle well known type
|
|
|
|
|
+ type wkt interface {
|
|
|
|
|
+ XXX_WellKnownType() string
|
|
|
|
|
+ }
|
|
|
|
|
+ if wkt, ok := f.Addr().Interface().(wkt); ok {
|
|
|
|
|
+ switch wkt.XXX_WellKnownType() {
|
|
|
|
|
+ case "Timestamp":
|
|
|
|
|
+ if value == "null" {
|
|
|
|
|
+ f.Field(0).SetInt(0)
|
|
|
|
|
+ f.Field(1).SetInt(0)
|
|
|
|
|
+ return nil
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ t, err := time.Parse(time.RFC3339Nano, value)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return fmt.Errorf("bad Timestamp: %v", err)
|
|
|
|
|
+ }
|
|
|
|
|
+ f.Field(0).SetInt(int64(t.Unix()))
|
|
|
|
|
+ f.Field(1).SetInt(int64(t.Nanosecond()))
|
|
|
|
|
+ return nil
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // is the destination field an enumeration type?
|
|
|
|
|
+ if enumValMap := proto.EnumValueMap(props.Enum); enumValMap != nil {
|
|
|
|
|
+ return populateFieldEnum(f, value, enumValMap)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
conv, ok := convFromType[f.Kind()]
|
|
conv, ok := convFromType[f.Kind()]
|
|
|
if !ok {
|
|
if !ok {
|
|
|
return fmt.Errorf("unsupported field type %T", f)
|
|
return fmt.Errorf("unsupported field type %T", f)
|
|
@@ -121,7 +162,48 @@ func populateField(f reflect.Value, value string) error {
|
|
|
if err := result[1].Interface(); err != nil {
|
|
if err := result[1].Interface(); err != nil {
|
|
|
return err.(error)
|
|
return err.(error)
|
|
|
}
|
|
}
|
|
|
- f.Set(result[0])
|
|
|
|
|
|
|
+ f.Set(result[0].Convert(f.Type()))
|
|
|
|
|
+ return nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func convertEnum(value string, t reflect.Type, enumValMap map[string]int32) (reflect.Value, error) {
|
|
|
|
|
+ // see if it's an enumeration string
|
|
|
|
|
+ if enumVal, ok := enumValMap[value]; ok {
|
|
|
|
|
+ return reflect.ValueOf(enumVal).Convert(t), nil
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // check for an integer that matches an enumeration value
|
|
|
|
|
+ eVal, err := strconv.Atoi(value)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return reflect.Value{}, fmt.Errorf("%s is not a valid %s", value, t)
|
|
|
|
|
+ }
|
|
|
|
|
+ for _, v := range enumValMap {
|
|
|
|
|
+ if v == int32(eVal) {
|
|
|
|
|
+ return reflect.ValueOf(eVal).Convert(t), nil
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return reflect.Value{}, fmt.Errorf("%s is not a valid %s", value, t)
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func populateFieldEnum(f reflect.Value, value string, enumValMap map[string]int32) error {
|
|
|
|
|
+ cval, err := convertEnum(value, f.Type(), enumValMap)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+ f.Set(cval)
|
|
|
|
|
+ return nil
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+func populateFieldEnumRepeated(f reflect.Value, values []string, enumValMap map[string]int32) error {
|
|
|
|
|
+ elemType := f.Type().Elem()
|
|
|
|
|
+ f.Set(reflect.MakeSlice(f.Type(), len(values), len(values)).Convert(f.Type()))
|
|
|
|
|
+ for i, v := range values {
|
|
|
|
|
+ result, err := convertEnum(v, elemType, enumValMap)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return err
|
|
|
|
|
+ }
|
|
|
|
|
+ f.Index(i).Set(result)
|
|
|
|
|
+ }
|
|
|
return nil
|
|
return nil
|
|
|
}
|
|
}
|
|
|
|
|
|