Repository: adnanh/webhook (go)

Issue:
Return JSON format when referenced value is not a simple type
As @adnanh wrote in https://github.com/adnanh/webhook/pull/446#issuecomment-667168746:

> when fetching the referenced parameter which turns out to be an object or an array, return a JSON string representation of it. At the moment it will invoke ToString() on the object which usually ends up with something like `map[foo: bar]` for payload such as `{"foo": "bar"}`


Pull request description:
Update ExtractParameterAsString to return JSON on complex types
Fixes #448

Patch:
diff --git a/internal/hook/hook.go b/internal/hook/hook.go
index dd5546c..2c2d787 100644
--- a/internal/hook/hook.go
+++ b/internal/hook/hook.go
@@ -392,14 +392,27 @@ func GetParameter(s string, params interface{}) (interface{}, error) {
 	return nil, &ParameterNodeError{s}
 }
 
-// ExtractParameterAsString extracts value from interface{} as string based on the passed string
+// ExtractParameterAsString extracts value from interface{} as string based on
+// the passed string.  Complex data types are rendered as JSON instead of the Go
+// Stringer format.
 func ExtractParameterAsString(s string, params interface{}) (string, error) {
 	pValue, err := GetParameter(s, params)
 	if err != nil {
 		return "", err
 	}
 
-	return fmt.Sprintf("%v", pValue), nil
+	switch v := reflect.ValueOf(pValue); v.Kind() {
+	case reflect.Array, reflect.Map, reflect.Slice:
+		r, err := json.Marshal(pValue)
+		if err != nil {
+			return "", err
+		}
+
+		return string(r), nil
+
+	default:
+		return fmt.Sprintf("%v", pValue), nil
+	}
 }
 
 // Argument type specifies the parameter key name and the source it should


Test patch:
diff --git a/internal/hook/hook_test.go b/internal/hook/hook_test.go
index e348930..46bd444 100644
--- a/internal/hook/hook_test.go
+++ b/internal/hook/hook_test.go
@@ -223,6 +223,9 @@ var extractParameterTests = []struct {
 	{"a.b.0", map[string]interface{}{"a": map[string]interface{}{"b": []interface{}{"x", "y", "z"}}}, "x", true},
 	{"a.1.b", map[string]interface{}{"a": []interface{}{map[string]interface{}{"b": "y"}, map[string]interface{}{"b": "z"}}}, "z", true},
 	{"a.1.b.c", map[string]interface{}{"a": []interface{}{map[string]interface{}{"b": map[string]interface{}{"c": "y"}}, map[string]interface{}{"b": map[string]interface{}{"c": "z"}}}}, "z", true},
+	{"b", map[string]interface{}{"b": map[string]interface{}{"z": 1}}, `{"z":1}`, true},
+	{"c", map[string]interface{}{"c": []interface{}{"y", "z"}}, `["y","z"]`, true},
+	{"d", map[string]interface{}{"d": [2]interface{}{"y", "z"}}, `["y","z"]`, true},
 	// failures
 	{"check_nil", nil, "", false},
 	{"a.X", map[string]interface{}{"a": map[string]interface{}{"b": "z"}}, "", false},                                                      // non-existent parameter reference
@@ -239,7 +242,7 @@ func TestExtractParameter(t *testing.T) {
 	for _, tt := range extractParameterTests {
 		value, err := ExtractParameterAsString(tt.s, tt.params)
 		if (err == nil) != tt.ok || value != tt.value {
-			t.Errorf("failed to extract parameter %q:\nexpected {value:%#v, ok:%#v},\ngot {value:%#v, err:%s}", tt.s, tt.value, tt.ok, value, err)
+			t.Errorf("failed to extract parameter %q:\nexpected {value:%#v, ok:%#v},\ngot {value:%#v, err:%v}", tt.s, tt.value, tt.ok, value, err)
 		}
 	}
 }
@@ -266,7 +269,7 @@ func TestArgumentGet(t *testing.T) {
 		a := Argument{tt.source, tt.name, "", false}
 		value, err := a.Get(tt.headers, tt.query, tt.payload)
 		if (err == nil) != tt.ok || value != tt.value {
-			t.Errorf("failed to get {%q, %q}:\nexpected {value:%#v, ok:%#v},\ngot {value:%#v, err:%s}", tt.source, tt.name, tt.value, tt.ok, value, err)
+			t.Errorf("failed to get {%q, %q}:\nexpected {value:%#v, ok:%#v},\ngot {value:%#v, err:%v}", tt.source, tt.name, tt.value, tt.ok, value, err)
 		}
 	}
 }