Repository: go-kratos/kratos
Pull request #2979 (2023-08-30 11:33:30)

bugfix: fix map decode
fix https://github.com/go-kratos/kratos/pull/2468

codecov-commenter: ## [Codecov](https://app.codecov.io/gh/go-kratos/kratos/pull/2979?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos) Report
> Merging [#2979](https://app.codecov.io/gh/go-kratos/kratos/pull/2979?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos) (3421806) into [main](https://app.codecov.io/gh/go-kratos/kratos/commit/a09f4d8ebba9ffd48957d0dae92e2f974a18ab2a?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos) (a09f4d8) will **increase** coverage by `0.01%`.
> The diff coverage is `100.00%`.

> :exclamation: Current head 3421806 differs from pull request most recent head c9c0ab6. Consider uploading reports for the commit c9c0ab6 to get more accurate results

:exclamation: Your organization is not using the GitHub App Integration. As a result you may experience degraded service beginning May 15th. Please [install the Github App Integration](https://github.com/apps/codecov) for your organization. [Read more](https://about.codecov.io/blog/codecov-is-updating-its-github-integration/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos).

```diff
@@            Coverage Diff             @@
##             main    #2979      +/-   ##
==========================================
+ Coverage   84.32%   84.34%   +0.01%     
==========================================
  Files          88       88              
  Lines        3981     3986       +5     
==========================================
+ Hits         3357     3362       +5     
  Misses        448      448              
  Partials      176      176              
```


| [Files Changed](https://app.codecov.io/gh/go-kratos/kratos/pull/2979?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos) | Coverage Δ | |
|---|---|---|
| [encoding/form/proto\_decode.go](https://app.codecov.io/gh/go-kratos/kratos/pull/2979?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=go-kratos#diff-ZW5jb2RpbmcvZm9ybS9wcm90b19kZWNvZGUuZ28=) | `64.28% <100.00%> (+0.72%)` | :arrow_up: |



Patch:
diff --git a/encoding/form/proto_decode.go b/encoding/form/proto_decode.go
index 28421fef..b6931cef 100644
--- a/encoding/form/proto_decode.go
+++ b/encoding/form/proto_decode.go
@@ -20,7 +20,9 @@ import (
 	"google.golang.org/protobuf/types/known/wrapperspb"
 )
 
-var ErrInvalidFormatMapKey = errors.New("invalid formatting for map key")
+const fieldSeparater = "."
+
+var errInvalidFormatMapKey = errors.New("invalid formatting for map key")
 
 // DecodeValues decode url value into proto message.
 func DecodeValues(msg proto.Message, values url.Values) error {
@@ -91,6 +93,8 @@ func getFieldDescriptor(v protoreflect.Message, fieldName string) protoreflect.F
 			fd = getDescriptorByFieldAndName(fields, strings.TrimSuffix(fieldName, "[]"))
 		default:
 			// If the type is map, you get the string "map[kratos]", where "map" is a field of proto and "kratos" is a key of map
+			// Use symbol . for separating fields/structs. (eg. structfield.field)
+			// ref: https://github.com/go-playground/form
 			field, _, err := parseURLQueryMapKey(fieldName)
 			if err != nil {
 				break
@@ -357,8 +361,17 @@ func parseURLQueryMapKey(key string) (string, string, error) {
 		startIndex = strings.IndexByte(key, '[')
 		endIndex   = strings.IndexByte(key, ']')
 	)
+	if startIndex < 0 {
+		//nolint:gomnd
+		values := strings.SplitN(key, fieldSeparater, 2)
+		//nolint:gomnd
+		if len(values) != 2 {
+			return "", "", errInvalidFormatMapKey
+		}
+		return values[0], values[1], nil
+	}
 	if startIndex <= 0 || startIndex >= endIndex || len(key) != endIndex+1 {
-		return "", "", ErrInvalidFormatMapKey
+		return "", "", errInvalidFormatMapKey
 	}
 	return key[:startIndex], key[startIndex+1 : endIndex], nil
 }


Test patch:
diff --git a/encoding/form/proto_decode_test.go b/encoding/form/proto_decode_test.go
index 986d7c33..a82b5576 100644
--- a/encoding/form/proto_decode_test.go
+++ b/encoding/form/proto_decode_test.go
@@ -97,6 +97,24 @@ func TestPopulateMapField(t *testing.T) {
 	}
 }
 
+func TestPopulateMapSepField(t *testing.T) {
+	query, err := url.ParseQuery("map.name=kratos")
+	if err != nil {
+		t.Fatal(err)
+	}
+	comp := &complex.Complex{}
+	field := getFieldDescriptor(comp.ProtoReflect(), "map")
+	// Fill the comp map field with the url query values
+	err = populateMapField(field, comp.ProtoReflect().Mutable(field).Map(), []string{"map.name"}, query["map.name"])
+	if err != nil {
+		t.Fatal(err)
+	}
+	// Get the comp map field value
+	if query["map.name"][0] != comp.Map["name"] {
+		t.Errorf("want: %s, got: %s", query, comp.Map)
+	}
+}
+
 func TestParseField(t *testing.T) {
 	tests := []struct {
 		name                    string
@@ -230,31 +248,31 @@ func TestParseURLQueryMapKey(t *testing.T) {
 			fieldName: "map[]", field: "map", fieldKey: "", err: nil,
 		},
 		{
-			fieldName: "", field: "", fieldKey: "", err: ErrInvalidFormatMapKey,
+			fieldName: "", field: "", fieldKey: "", err: errInvalidFormatMapKey,
 		},
 		{
-			fieldName: "[[]", field: "", fieldKey: "", err: ErrInvalidFormatMapKey,
+			fieldName: "[[]", field: "", fieldKey: "", err: errInvalidFormatMapKey,
 		},
 		{
-			fieldName: "map[kratos]=", field: "", fieldKey: "", err: ErrInvalidFormatMapKey,
+			fieldName: "map[kratos]=", field: "", fieldKey: "", err: errInvalidFormatMapKey,
 		},
 		{
-			fieldName: "[kratos]", field: "", fieldKey: "", err: ErrInvalidFormatMapKey,
+			fieldName: "[kratos]", field: "", fieldKey: "", err: errInvalidFormatMapKey,
 		},
 		{
-			fieldName: "map", field: "", fieldKey: "", err: ErrInvalidFormatMapKey,
+			fieldName: "map", field: "", fieldKey: "", err: errInvalidFormatMapKey,
 		},
 		{
-			fieldName: "map[", field: "", fieldKey: "", err: ErrInvalidFormatMapKey,
+			fieldName: "map[", field: "", fieldKey: "", err: errInvalidFormatMapKey,
 		},
 		{
-			fieldName: "]kratos[", field: "", fieldKey: "", err: ErrInvalidFormatMapKey,
+			fieldName: "]kratos[", field: "", fieldKey: "", err: errInvalidFormatMapKey,
 		},
 		{
-			fieldName: "[kratos", field: "", fieldKey: "", err: ErrInvalidFormatMapKey,
+			fieldName: "[kratos", field: "", fieldKey: "", err: errInvalidFormatMapKey,
 		},
 		{
-			fieldName: "kratos]", field: "", fieldKey: "", err: ErrInvalidFormatMapKey,
+			fieldName: "kratos]", field: "", fieldKey: "", err: errInvalidFormatMapKey,
 		},
 	}
 	for _, test := range tests {