From b338b63a18729e04d9e8e27ff2739eed94f6bf1b Mon Sep 17 00:00:00 2001 From: Daniel Castro Date: Sat, 9 Mar 2024 09:43:40 +0100 Subject: [PATCH 1/5] test update --- deepobject_test.go | 49 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/deepobject_test.go b/deepobject_test.go index 237673ad..d3d3dd13 100644 --- a/deepobject_test.go +++ b/deepobject_test.go @@ -10,27 +10,38 @@ import ( "github.com/stretchr/testify/require" ) +type InnerArrayObject struct { + Names []string `json:"names"` +} + type InnerObject struct { Name string ID int } +type InnerObject2 struct { + Foo string + Is bool +} // These are all possible field types, mandatory and optional. type AllFields struct { - I int `json:"i"` - Oi *int `json:"oi,omitempty"` - F float32 `json:"f"` - Of *float32 `json:"of,omitempty"` - B bool `json:"b"` - Ob *bool `json:"ob,omitempty"` - As []string `json:"as"` - Oas *[]string `json:"oas,omitempty"` - O InnerObject `json:"o"` - Oo *InnerObject `json:"oo,omitempty"` - D MockBinder `json:"d"` - Od *MockBinder `json:"od,omitempty"` - M map[string]int `json:"m"` - Om *map[string]int `json:"om,omitempty"` + I int `json:"i"` + Oi *int `json:"oi,omitempty"` + Ab *[]bool `json:"ab,omitempty"` + F float32 `json:"f"` + Of *float32 `json:"of,omitempty"` + B bool `json:"b"` + Ob *bool `json:"ob,omitempty"` + As []string `json:"as"` + Oas *[]string `json:"oas,omitempty"` + O InnerObject `json:"o"` + Ao []InnerObject2 `json:"ao"` + Onas InnerArrayObject `json:"onas"` + Oo *InnerObject `json:"oo,omitempty"` + D MockBinder `json:"d"` + Od *MockBinder `json:"od,omitempty"` + M map[string]int `json:"m"` + Om *map[string]int `json:"om,omitempty"` } func TestDeepObject(t *testing.T) { @@ -54,12 +65,20 @@ func TestDeepObject(t *testing.T) { Of: &of, B: true, Ob: &ob, + Ab: &[]bool{true}, As: []string{"hello", "world"}, Oas: &oas, O: InnerObject{ Name: "Joe Schmoe", ID: 456, }, + Ao: []InnerObject2{ + {Foo: "bar", Is: true}, + {Foo: "baz", Is: false}, + }, + Onas: InnerArrayObject{ + Names: []string{"Bill", "Frank"}, + }, Oo: &oo, D: d, Od: &d, @@ -69,7 +88,7 @@ func TestDeepObject(t *testing.T) { marshaled, err := MarshalDeepObject(srcObj, "p") require.NoError(t, err) - t.Log(marshaled) + require.EqualValues(t, "p[ab][0]=true&p[ao][0][Foo]=bar&p[ao][0][Is]=true&p[ao][1][Foo]=baz&p[ao][1][Is]=false&p[as][0]=hello&p[as][1]=world&p[b]=true&p[d]=2020-02-01&p[f]=4.2&p[i]=12&p[m][additional]=1&p[o][ID]=456&p[o][Name]=Joe Schmoe&p[oas][0]=foo&p[oas][1]=bar&p[ob]=true&p[od]=2020-02-01&p[of]=3.7&p[oi]=5&p[om][additional]=1&p[onas][names][0]=Bill&p[onas][names][1]=Frank&p[oo][ID]=123&p[oo][Name]=Marcin Romaszewicz", marshaled) params := make(url.Values) marshaledParts := strings.Split(marshaled, "&") From d98d7fbf672a507db55036572c6c61248694aaa5 Mon Sep 17 00:00:00 2001 From: Daniel Castro Date: Sat, 9 Mar 2024 10:08:52 +0100 Subject: [PATCH 2/5] fix array of objects unmarshaling --- deepobject.go | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/deepobject.go b/deepobject.go index 7ec2f027..c1bf78a9 100644 --- a/deepobject.go +++ b/deepobject.go @@ -111,7 +111,6 @@ func (f *fieldOrValue) appendPathValue(path []string, value string) { } func makeFieldOrValue(paths [][]string, values []string) fieldOrValue { - f := fieldOrValue{ fields: make(map[string]fieldOrValue), } @@ -193,7 +192,7 @@ func fieldIndicesByJSONTag(i interface{}) (map[string]int, error) { } func assignPathValues(dst interface{}, pathValues fieldOrValue) error { - //t := reflect.TypeOf(dst) + // t := reflect.TypeOf(dst) v := reflect.ValueOf(dst) iv := reflect.Indirect(v) @@ -337,7 +336,7 @@ func assignPathValues(dst interface{}, pathValues fieldOrValue) error { func assignSlice(dst reflect.Value, pathValues fieldOrValue) error { // Gather up the values nValues := len(pathValues.fields) - values := make([]string, nValues) + // We expect to have consecutive array indices in the map for i := 0; i < nValues; i++ { indexStr := strconv.Itoa(i) @@ -345,14 +344,9 @@ func assignSlice(dst reflect.Value, pathValues fieldOrValue) error { if !found { return errors.New("array deepObjects must have consecutive indices") } - values[i] = fv.value - } - // This could be cleaner, but we can call into assignPathValues to - // avoid recreating this logic. - for i := 0; i < nValues; i++ { dstElem := dst.Index(i).Addr() - err := assignPathValues(dstElem.Interface(), fieldOrValue{value: values[i]}) + err := assignPathValues(dstElem.Interface(), fv) if err != nil { return fmt.Errorf("error binding array: %w", err) } From cadf21c4d43f908ab935fb309c378ad0d53e6518 Mon Sep 17 00:00:00 2001 From: Daniel Castro Date: Sat, 9 Mar 2024 10:12:53 +0100 Subject: [PATCH 3/5] update tests --- deepobject_test.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/deepobject_test.go b/deepobject_test.go index d3d3dd13..9633b568 100644 --- a/deepobject_test.go +++ b/deepobject_test.go @@ -18,11 +18,17 @@ type InnerObject struct { Name string ID int } + type InnerObject2 struct { Foo string Is bool } +type InnerObject3 struct { + Foo string + Count *int +} + // These are all possible field types, mandatory and optional. type AllFields struct { I int `json:"i"` @@ -36,6 +42,7 @@ type AllFields struct { Oas *[]string `json:"oas,omitempty"` O InnerObject `json:"o"` Ao []InnerObject2 `json:"ao"` + Aop *[]InnerObject3 `json:"aop"` Onas InnerArrayObject `json:"onas"` Oo *InnerObject `json:"oo,omitempty"` D MockBinder `json:"d"` @@ -58,6 +65,9 @@ func TestDeepObject(t *testing.T) { } d := MockBinder{Time: time.Date(2020, 2, 1, 0, 0, 0, 0, time.UTC)} + one := 1 + two := 1 + srcObj := AllFields{ I: 12, Oi: &oi, @@ -76,6 +86,10 @@ func TestDeepObject(t *testing.T) { {Foo: "bar", Is: true}, {Foo: "baz", Is: false}, }, + Aop: &[]InnerObject3{ + {Foo: "a", Count: &one}, + {Foo: "b", Count: &two}, + }, Onas: InnerArrayObject{ Names: []string{"Bill", "Frank"}, }, @@ -88,7 +102,7 @@ func TestDeepObject(t *testing.T) { marshaled, err := MarshalDeepObject(srcObj, "p") require.NoError(t, err) - require.EqualValues(t, "p[ab][0]=true&p[ao][0][Foo]=bar&p[ao][0][Is]=true&p[ao][1][Foo]=baz&p[ao][1][Is]=false&p[as][0]=hello&p[as][1]=world&p[b]=true&p[d]=2020-02-01&p[f]=4.2&p[i]=12&p[m][additional]=1&p[o][ID]=456&p[o][Name]=Joe Schmoe&p[oas][0]=foo&p[oas][1]=bar&p[ob]=true&p[od]=2020-02-01&p[of]=3.7&p[oi]=5&p[om][additional]=1&p[onas][names][0]=Bill&p[onas][names][1]=Frank&p[oo][ID]=123&p[oo][Name]=Marcin Romaszewicz", marshaled) + require.EqualValues(t, "p[ab][0]=true&p[ao][0][Foo]=bar&p[ao][0][Is]=true&p[ao][1][Foo]=baz&p[ao][1][Is]=false&p[aop][0][Count]=1&p[aop][0][Foo]=a&p[aop][1][Count]=1&p[aop][1][Foo]=b&p[as][0]=hello&p[as][1]=world&p[b]=true&p[d]=2020-02-01&p[f]=4.2&p[i]=12&p[m][additional]=1&p[o][ID]=456&p[o][Name]=Joe Schmoe&p[oas][0]=foo&p[oas][1]=bar&p[ob]=true&p[od]=2020-02-01&p[of]=3.7&p[oi]=5&p[om][additional]=1&p[onas][names][0]=Bill&p[onas][names][1]=Frank&p[oo][ID]=123&p[oo][Name]=Marcin Romaszewicz", marshaled) params := make(url.Values) marshaledParts := strings.Split(marshaled, "&") From cfb20bf08e0d763e100503a689aec007f992079c Mon Sep 17 00:00:00 2001 From: Daniel Castro Date: Sat, 9 Mar 2024 10:15:55 +0100 Subject: [PATCH 4/5] update tests --- deepobject_test.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/deepobject_test.go b/deepobject_test.go index 9633b568..349a77da 100644 --- a/deepobject_test.go +++ b/deepobject_test.go @@ -26,7 +26,7 @@ type InnerObject2 struct { type InnerObject3 struct { Foo string - Count *int + Count *int `json:"count,omitempty"` } // These are all possible field types, mandatory and optional. @@ -65,8 +65,7 @@ func TestDeepObject(t *testing.T) { } d := MockBinder{Time: time.Date(2020, 2, 1, 0, 0, 0, 0, time.UTC)} - one := 1 - two := 1 + two := 2 srcObj := AllFields{ I: 12, @@ -84,10 +83,10 @@ func TestDeepObject(t *testing.T) { }, Ao: []InnerObject2{ {Foo: "bar", Is: true}, - {Foo: "baz", Is: false}, + {Foo: "baz"}, }, Aop: &[]InnerObject3{ - {Foo: "a", Count: &one}, + {Foo: "a"}, {Foo: "b", Count: &two}, }, Onas: InnerArrayObject{ @@ -102,7 +101,7 @@ func TestDeepObject(t *testing.T) { marshaled, err := MarshalDeepObject(srcObj, "p") require.NoError(t, err) - require.EqualValues(t, "p[ab][0]=true&p[ao][0][Foo]=bar&p[ao][0][Is]=true&p[ao][1][Foo]=baz&p[ao][1][Is]=false&p[aop][0][Count]=1&p[aop][0][Foo]=a&p[aop][1][Count]=1&p[aop][1][Foo]=b&p[as][0]=hello&p[as][1]=world&p[b]=true&p[d]=2020-02-01&p[f]=4.2&p[i]=12&p[m][additional]=1&p[o][ID]=456&p[o][Name]=Joe Schmoe&p[oas][0]=foo&p[oas][1]=bar&p[ob]=true&p[od]=2020-02-01&p[of]=3.7&p[oi]=5&p[om][additional]=1&p[onas][names][0]=Bill&p[onas][names][1]=Frank&p[oo][ID]=123&p[oo][Name]=Marcin Romaszewicz", marshaled) + require.EqualValues(t, "p[ab][0]=true&p[ao][0][Foo]=bar&p[ao][0][Is]=true&p[ao][1][Foo]=baz&p[ao][1][Is]=false&p[aop][0][Foo]=a&p[aop][1][Foo]=b&p[aop][1][count]=2&p[as][0]=hello&p[as][1]=world&p[b]=true&p[d]=2020-02-01&p[f]=4.2&p[i]=12&p[m][additional]=1&p[o][ID]=456&p[o][Name]=Joe Schmoe&p[oas][0]=foo&p[oas][1]=bar&p[ob]=true&p[od]=2020-02-01&p[of]=3.7&p[oi]=5&p[om][additional]=1&p[onas][names][0]=Bill&p[onas][names][1]=Frank&p[oo][ID]=123&p[oo][Name]=Marcin Romaszewicz", marshaled) params := make(url.Values) marshaledParts := strings.Split(marshaled, "&") From 95863fa98b5fcff60e166d48f2447a704e15e6e7 Mon Sep 17 00:00:00 2001 From: Daniel Castro Date: Sat, 9 Mar 2024 10:33:13 +0100 Subject: [PATCH 5/5] update tests --- bindparam_test.go | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/bindparam_test.go b/bindparam_test.go index 080f8506..602c0fa3 100644 --- a/bindparam_test.go +++ b/bindparam_test.go @@ -293,12 +293,20 @@ func TestSplitParameter(t *testing.T) { func TestBindQueryParameter(t *testing.T) { t.Run("deepObject", func(t *testing.T) { + type Object struct { + Count int `json:"count"` + } + type Nested struct { + Object Object `json:"object"` + Objects []Object `json:"objects"` + } type ID struct { FirstName *string `json:"firstName"` LastName *string `json:"lastName"` Role string `json:"role"` Birthday *types.Date `json:"birthday"` Married *MockBinder `json:"married"` + Nested Nested `json:"nested"` } expectedName := "Alex" @@ -307,16 +315,23 @@ func TestBindQueryParameter(t *testing.T) { Role: "admin", Birthday: &types.Date{Time: time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)}, Married: &MockBinder{time.Date(2020, 2, 2, 0, 0, 0, 0, time.UTC)}, + Nested: Nested{ + Object: Object{Count: 123}, + Objects: []Object{{Count: 1}, {Count: 2}}, + }, } actual := new(ID) paramName := "id" queryParams := url.Values{ - "id[firstName]": {"Alex"}, - "id[role]": {"admin"}, - "foo": {"bar"}, - "id[birthday]": {"2020-01-01"}, - "id[married]": {"2020-02-02"}, + "id[firstName]": {"Alex"}, + "id[role]": {"admin"}, + "foo": {"bar"}, + "id[birthday]": {"2020-01-01"}, + "id[married]": {"2020-02-02"}, + "id[nested][object][count]": {"123"}, + "id[nested][objects][0][count]": {"1"}, + "id[nested][objects][1][count]": {"2"}, } err := BindQueryParameter("deepObject", true, false, paramName, queryParams, &actual) @@ -356,7 +371,6 @@ func TestBindQueryParameter(t *testing.T) { assert.Error(t, err) err = BindQueryParameter("form", true, true, "notfound", queryParams, &optionalNumber) assert.Error(t, err) - }) }