From 4cf60d6f531c0b4803b339c83630a7d897135c06 Mon Sep 17 00:00:00 2001 From: Baha Aiman Date: Sat, 9 Sep 2023 20:04:43 -0700 Subject: [PATCH 1/3] fix(datastore): Allow saving nested byte slice --- datastore/datastore_test.go | 69 +++++++++++++++++++++++++++++++++++++ datastore/prop.go | 2 +- 2 files changed, 70 insertions(+), 1 deletion(-) diff --git a/datastore/datastore_test.go b/datastore/datastore_test.go index 550336319e53..03755e397d5c 100644 --- a/datastore/datastore_test.go +++ b/datastore/datastore_test.go @@ -614,6 +614,75 @@ func TestPutInvalidEntity(t *testing.T) { }) } +func TestPutNestedArray(t *testing.T) { + type InnerByte struct { + ArrByte []byte + } + + type OuterByte struct { + InnerArr []InnerByte `datastore:"arrbyte,flatten"` + } + + type InnerStr struct { + ArrStr []string + } + + type OuterStr struct { + InnerArr []InnerStr `datastore:"arrstr,flatten"` + } + + ctx := context.Background() + client := &Client{ + client: &fakeDatastoreClient{ + commit: func(req *pb.CommitRequest) (*pb.CommitResponse, error) { + return &pb.CommitResponse{}, nil + }, + }, + } + + testcases := []struct { + desc string + src interface{} + key *Key + wantFailure bool + wantErrMsg string + }{ + { + desc: "Nested byte slice should pass", + src: &OuterByte{ + InnerArr: []InnerByte{ + { + ArrByte: []byte("Test string"), + }, + }, + }, + key: NameKey("OuterByte", "OuterByte1", nil), + }, + { + desc: "Nested slice not of byte type should fail", + src: &OuterStr{ + InnerArr: []InnerStr{ + { + ArrStr: []string{"a", "b"}, + }, + }, + }, + key: NameKey("OuterStr", "OuterStr1", nil), + wantFailure: true, + wantErrMsg: "flattening nested structs leads to a slice of slices", + }, + } + + for _, tc := range testcases { + _, gotErr := client.Put(ctx, tc.key, tc.src) + gotFailure := gotErr != nil + if gotFailure != tc.wantFailure || + (gotErr != nil && !strings.Contains(gotErr.Error(), tc.wantErrMsg)) { + t.Errorf("%q: Mismatch in error got: %v, want: %q", tc.desc, gotErr, tc.wantErrMsg) + } + } +} + func TestDeferred(t *testing.T) { type Ent struct { A int diff --git a/datastore/prop.go b/datastore/prop.go index ff7466bf8a33..bab830aebaf8 100644 --- a/datastore/prop.go +++ b/datastore/prop.go @@ -183,7 +183,7 @@ func validateChildType(t reflect.Type, fieldName string, flatten, prevSlice bool switch t.Kind() { case reflect.Slice: - if flatten && prevSlice { + if flatten && prevSlice && t.Elem().Kind() != reflect.Uint8 { return fmt.Errorf("datastore: flattening nested structs leads to a slice of slices: field %q", fieldName) } return validateChildType(t.Elem(), fieldName, flatten, true, prevTypes) From 49fac9a14052bdbffdf04380b859aa13d009b44e Mon Sep 17 00:00:00 2001 From: Baha Aiman Date: Wed, 13 Sep 2023 10:15:38 -0700 Subject: [PATCH 2/3] fix(datastore): Adding comment --- datastore/prop.go | 1 + 1 file changed, 1 insertion(+) diff --git a/datastore/prop.go b/datastore/prop.go index bab830aebaf8..26e26ae5dd82 100644 --- a/datastore/prop.go +++ b/datastore/prop.go @@ -183,6 +183,7 @@ func validateChildType(t reflect.Type, fieldName string, flatten, prevSlice bool switch t.Kind() { case reflect.Slice: + // Uint8 is an alias for byte. A slice of bytes is acceptable. A slice of others types is not. if flatten && prevSlice && t.Elem().Kind() != reflect.Uint8 { return fmt.Errorf("datastore: flattening nested structs leads to a slice of slices: field %q", fieldName) } From 3e559d0bb2d475fb037601f369a609eb911a2942 Mon Sep 17 00:00:00 2001 From: Baha Aiman Date: Fri, 15 Sep 2023 10:41:11 -0700 Subject: [PATCH 3/3] fix(datastore): Assert returned value --- datastore/datastore_test.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/datastore/datastore_test.go b/datastore/datastore_test.go index 03755e397d5c..e483936c3e58 100644 --- a/datastore/datastore_test.go +++ b/datastore/datastore_test.go @@ -674,12 +674,15 @@ func TestPutNestedArray(t *testing.T) { } for _, tc := range testcases { - _, gotErr := client.Put(ctx, tc.key, tc.src) + gotKey, gotErr := client.Put(ctx, tc.key, tc.src) gotFailure := gotErr != nil if gotFailure != tc.wantFailure || (gotErr != nil && !strings.Contains(gotErr.Error(), tc.wantErrMsg)) { t.Errorf("%q: Mismatch in error got: %v, want: %q", tc.desc, gotErr, tc.wantErrMsg) } + if gotErr == nil && !gotKey.Equal(tc.key) { + t.Errorf("%q: Mismatch in key got: %v, want: %q", tc.desc, gotKey, tc.key) + } } }