Skip to content

Commit

Permalink
fix FieldMask are converted to/from lower-camel naming conventions. (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
tonybase authored Dec 28, 2021
1 parent b6b9508 commit 11a6120
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 8 deletions.
6 changes: 3 additions & 3 deletions encoding/form/form.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ func (c codec) Marshal(v interface{}) ([]byte, error) {
var vs url.Values
var err error
if m, ok := v.(proto.Message); ok {
vs, err = EncodeMap(m)
vs, err = EncodeValues(m)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -66,9 +66,9 @@ func (c codec) Unmarshal(data []byte, v interface{}) error {
rv = rv.Elem()
}
if m, ok := v.(proto.Message); ok {
return MapProto(m, vs)
return DecodeValues(m, vs)
} else if m, ok := reflect.Indirect(reflect.ValueOf(v)).Interface().(proto.Message); ok {
return MapProto(m, vs)
return DecodeValues(m, vs)
}

return c.decoder.Decode(v, vs)
Expand Down
28 changes: 26 additions & 2 deletions encoding/form/proto_decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/base64"
"errors"
"fmt"
"net/url"
"strconv"
"strings"
"time"
Expand All @@ -19,7 +20,8 @@ import (
"google.golang.org/protobuf/types/known/wrapperspb"
)

func MapProto(msg proto.Message, values map[string][]string) error {
// DecodeValues decode url value into proto message.
func DecodeValues(msg proto.Message, values url.Values) error {
for key, values := range values {
if err := populateFieldValues(msg.ProtoReflect(), strings.Split(key, "."), values); err != nil {
return err
Expand Down Expand Up @@ -285,7 +287,9 @@ func parseMessage(md protoreflect.MessageDescriptor, value string) (protoreflect
msg = wrapperspb.Bytes(v)
case "google.protobuf.FieldMask":
fm := &field_mask.FieldMask{}
fm.Paths = append(fm.Paths, strings.Split(value, ",")...)
for _, fv := range strings.Split(value, ",") {
fm.Paths = append(fm.Paths, jsonSnakeCase(fv))
}
msg = fm
case "google.protobuf.Value":
fm, err := structpb.NewValue(value)
Expand All @@ -298,3 +302,23 @@ func parseMessage(md protoreflect.MessageDescriptor, value string) (protoreflect
}
return protoreflect.ValueOfMessage(msg.ProtoReflect()), nil
}

// jsonSnakeCase converts a camelCase identifier to a snake_case identifier,
// according to the protobuf JSON specification.
// references: https://github.com/protocolbuffers/protobuf-go/blob/master/encoding/protojson/well_known_types.go#L864
func jsonSnakeCase(s string) string {
var b []byte
for i := 0; i < len(s); i++ { // proto identifiers are always ASCII
c := s[i]
if isASCIIUpper(c) {
b = append(b, '_')
c += 'a' - 'A' // convert to lowercase
}
b = append(b, c)
}
return string(b)
}

func isASCIIUpper(c byte) bool {
return 'A' <= c && c <= 'Z'
}
30 changes: 28 additions & 2 deletions encoding/form/proto_encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import (
"google.golang.org/protobuf/reflect/protoreflect"
)

// EncodeMap encode proto message to url query.
func EncodeMap(msg proto.Message) (url.Values, error) {
// EncodeValues encode a message into url values.
func EncodeValues(msg proto.Message) (url.Values, error) {
if msg == nil || (reflect.ValueOf(msg).Kind() == reflect.Ptr && reflect.ValueOf(msg).IsNil()) {
return url.Values{}, nil
}
Expand Down Expand Up @@ -165,8 +165,34 @@ func encodeMessage(msgDescriptor protoreflect.MessageDescriptor, value protorefl
if !ok {
return "", nil
}
for i, v := range m.Paths {
m.Paths[i] = jsonCamelCase(v)
}
return strings.Join(m.Paths, ","), nil
default:
return "", fmt.Errorf("unsupported message type: %q", string(msgDescriptor.FullName()))
}
}

// JSONCamelCase converts a snake_case identifier to a camelCase identifier,
// according to the protobuf JSON specification.
// references: https://github.com/protocolbuffers/protobuf-go/blob/master/encoding/protojson/well_known_types.go#L842
func jsonCamelCase(s string) string {
var b []byte
var wasUnderscore bool
for i := 0; i < len(s); i++ { // proto identifiers are always ASCII
c := s[i]
if c != '_' {
if wasUnderscore && isASCIILower(c) {
c -= 'a' - 'A' // convert to uppercase
}
b = append(b, c)
}
wasUnderscore = c == '_'
}
return string(b)
}

func isASCIILower(c byte) bool {
return 'a' <= c && c <= 'z'
}
2 changes: 1 addition & 1 deletion transport/http/binding/encode.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func EncodeURL(pathTemplate string, msg proto.Message, needQuery bool) string {
return in
})
if needQuery {
u, err := form.EncodeMap(msg)
u, err := form.EncodeValues(msg)
if err == nil && len(u) > 0 {
for key := range pathParams {
delete(u, key)
Expand Down

0 comments on commit 11a6120

Please sign in to comment.