From d208275877287f6870866d2189327cbeecbb5557 Mon Sep 17 00:00:00 2001 From: Brandur Date: Fri, 5 Jan 2018 12:19:49 -0800 Subject: [PATCH 1/2] Support slices in `event.GetObjValue` and add tests Modifies `event.GetObjValue` so that when specifying additional keys to descend into, it's now possible to specify integer keys if the target object is a slice of type `[]interface{}` (which should be what anything coming out of event data deserializes to). I initially expressed some concern about expanding this interface, but given that the previous behavior would have been an outright panic, I changed my mind and don't think it's too bad to add support for this given that it's been requested by a user. Fixes #503. --- event.go | 24 +++++++++++++++++++++- event_test.go | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 event_test.go diff --git a/event.go b/event.go index cbfa07e9d1..4d534298f5 100644 --- a/event.go +++ b/event.go @@ -3,6 +3,7 @@ package stripe import ( "encoding/json" "fmt" + "strconv" ) // Event is the resource representing a Stripe event. @@ -82,7 +83,28 @@ func getValue(m map[string]interface{}, keys []string) string { node := m[keys[0]] for i := 1; i < len(keys); i++ { - node = node.(map[string]interface{})[keys[i]] + key := keys[i] + + sliceNode, ok := node.([]interface{}) + if ok { + intKey, err := strconv.Atoi(key) + if err != nil { + panic(fmt.Sprintf( + "Cannot access nested slice element with non-integer key: %s", + key)) + } + node = sliceNode[intKey] + continue + } + + mapNode, ok := node.(map[string]interface{}) + if ok { + node = mapNode[key] + continue + } + + panic(fmt.Sprintf( + "Cannot descend into non-map non-slice object with key: %s", key)) } if node == nil { diff --git a/event_test.go b/event_test.go new file mode 100644 index 0000000000..2335e08941 --- /dev/null +++ b/event_test.go @@ -0,0 +1,55 @@ +package stripe + +import ( + "testing" + + assert "github.com/stretchr/testify/require" +) + +func TestGetObjValue(t *testing.T) { + event := &Event{ + Data: &EventData{ + Obj: map[string]interface{}{ + "top_level_key": "top_level", + "integer_key": 123, + "map": map[string]interface{}{ + "nested_key": "nested", + }, + "slice": []interface{}{ + "index-0", + "index-1", + "index-2", + }, + "slice_of_maps": []interface{}{ + map[string]interface{}{ + "slice_nested_key": "slice_nested", + }, + }, + }, + }, + } + + assert.Equal(t, "top_level", event.GetObjValue("top_level_key")) + + // Check that it coerces non-string values into strings (this behavior is + // somewhat questionable, but I'm going with how it already works) + assert.Equal(t, "123", event.GetObjValue("integer_key")) + + assert.Equal(t, "nested", event.GetObjValue("map", "nested_key")) + assert.Equal(t, "index-1", event.GetObjValue("slice", "1")) + assert.Equal(t, "slice_nested", + event.GetObjValue("slice_of_maps", "0", "slice_nested_key")) + + // By design a `nil` just returns an empty string + assert.Equal(t, "", event.GetObjValue("bad_key")) + + // Panic conditions. Usually the function tries to just return a value is + // fairly forgiving, but it does panic under certain obviously impossible + // cases. + assert.Panicsf(t, func() { + event.GetObjValue("slice", "string_key") + }, "Cannot access nested slice element with non-integer key: %s", "bad_key") + assert.Panicsf(t, func() { + event.GetObjValue("top_level_key", "bad_key") + }, "Cannot descend into non-map non-slice object with key: %s", "bad_key") +} From eece6437fd47be0b2627eff65761a895909e0712 Mon Sep 17 00:00:00 2001 From: Brandur Date: Mon, 8 Jan 2018 15:12:57 -0800 Subject: [PATCH 2/2] Fix panic checks (we want `PanicsWithValue` not `Panicsf`) --- event_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/event_test.go b/event_test.go index 2335e08941..73af4be950 100644 --- a/event_test.go +++ b/event_test.go @@ -46,10 +46,10 @@ func TestGetObjValue(t *testing.T) { // Panic conditions. Usually the function tries to just return a value is // fairly forgiving, but it does panic under certain obviously impossible // cases. - assert.Panicsf(t, func() { + assert.PanicsWithValue(t, "Cannot access nested slice element with non-integer key: string_key", func() { event.GetObjValue("slice", "string_key") - }, "Cannot access nested slice element with non-integer key: %s", "bad_key") - assert.Panicsf(t, func() { + }) + assert.PanicsWithValue(t, "Cannot descend into non-map non-slice object with key: bad_key", func() { event.GetObjValue("top_level_key", "bad_key") - }, "Cannot descend into non-map non-slice object with key: %s", "bad_key") + }) }