Repository: stripe/stripe-go (go)

Issue:
Cannot remove subscription metadata
I'm trying to remove some metadata on a subscription, and before I simply provided an empty string, but now that does not seem to do anything. 

My Code
```go
params := &stripe.SubParams{}
params.AddMeta("Test", "")
sub, _ := sc.Subs.Update("sub_Bz2wy74jBPoaUD", params)
log.Debugf("METADATA VALUES %v", sub.Meta)
```
I have looked at the request that is created and it does not add the metadata field if the value is empty. I cannot find anything to suggest that removing metadata has changed, so if it has could you kindly show me where that is.

Pull request:
Encode empty map values
Because most parameter fields in stripe-go are non-pointers, we
generally require that a value be non-empty before we encode it, because
otherwise we can't tell whether or not it was explicitly set.

It recently came up in #511 that it's no longer possible to set an empty
value in a map, which is a direct result of this behavior.

This patch tweaks the implementation slightly so that we always encode
all values that we find in maps, even if they're zero values. This makes
sense because unlike a struct which has default fields, we can always
guarantee that anything present in a map *was* explicitly added. This
resolves the problem described above.

Fixes #511.

r? @remi-stripe
cc @ob-stripe

Patch:
diff --git a/form/form.go b/form/form.go
index 0e7d3136..5fc54996 100644
--- a/form/form.go
+++ b/form/form.go
@@ -298,11 +298,13 @@ func intEncoder(values *Values, v reflect.Value, keyParts []string, encodeZero b
 	values.Add(FormatKey(keyParts), strconv.FormatInt(val, 10))
 }
 
-func interfaceEncoder(values *Values, v reflect.Value, keyParts []string, _ bool, _ *formOptions) {
+func interfaceEncoder(values *Values, v reflect.Value, keyParts []string, encodeZero bool, _ *formOptions) {
+	// interfaceEncoder never encodes a `nil`, but it will pass through an
+	// `encodeZero` value into its chained encoder
 	if v.IsNil() {
 		return
 	}
-	reflectValue(values, v.Elem(), false, keyParts)
+	reflectValue(values, v.Elem(), encodeZero, keyParts)
 }
 
 func isAppender(t reflect.Type) bool {
@@ -315,7 +317,11 @@ func mapEncoder(values *Values, v reflect.Value, keyParts []string, _ bool, _ *f
 			panic("Don't support serializing maps with non-string keys")
 		}
 
-		reflectValue(values, v.MapIndex(keyVal), false, append(keyParts, keyVal.String()))
+		// Unlike a property on a struct which will contain a zero value even
+		// if never set, any value found in a map has been explicitly set, so
+		// we always make an effort to encode them, even if a zero value
+		// (that's why we pass through `true` here).
+		reflectValue(values, v.MapIndex(keyVal), true, append(keyParts, keyVal.String()))
 	}
 }
 
@@ -335,12 +341,17 @@ func uintEncoder(values *Values, v reflect.Value, keyParts []string, encodeZero
 	values.Add(FormatKey(keyParts), strconv.FormatUint(val, 10))
 }
 
-func reflectValue(values *Values, v reflect.Value, _ bool, keyParts []string) {
+// reflectValue is roughly the shared entry point of any AppendTo functions.
+// It's also called recursively in cases where a precise type isn't yet known
+// and its encoding needs to be deferred down the chain; for example, when
+// encoding interface{} or the values in an array or map containing
+// interface{}.
+func reflectValue(values *Values, v reflect.Value, encodeZero bool, keyParts []string) {
 	t := v.Type()
 
 	f := getCachedOrBuildTypeEncoder(t)
 	if f != nil {
-		f(values, v, keyParts, v.Kind() == reflect.Ptr, nil)
+		f(values, v, keyParts, encodeZero || v.Kind() == reflect.Ptr, nil)
 	}
 
 	if isAppender(t) {


Test patch:
diff --git a/form/form_test.go b/form/form_test.go
index 36dd0af6..f7d34c94 100644
--- a/form/form_test.go
+++ b/form/form_test.go
@@ -229,6 +229,20 @@ func TestAppendTo(t *testing.T) {
 			"bar",
 		},
 
+		// Tests map with an empty value
+		{
+			"map[empty]",
+			&testStruct{Map: map[string]interface{}{
+				// Note that we use an empty integer instead of an empty string
+				// here because `Value`''s `Get` implementation will return an
+				// empty string for an unset value which means that we can't
+				// differentiate between a missing and empty value. The empty
+				// value for int64 is 0, so we can.
+				"empty": int64(0),
+			}},
+			"0",
+		},
+
 		// Tests map nested inside of another map
 		{
 			"map[foo][bar]",