From 206ab868231ff36be1525b4e38d2bc9a9c18f65a Mon Sep 17 00:00:00 2001 From: Vishal Nayak Date: Mon, 4 May 2020 18:22:28 -0400 Subject: [PATCH] Add TypeFloat to field data (#8923) * Add TypeFloat to field data * Handle zero value case * Address review feedback --- sdk/framework/backend.go | 2 + sdk/framework/field_data.go | 11 ++- sdk/framework/field_data_test.go | 149 +++++++++++++++++++++++++++++-- sdk/framework/field_type.go | 5 ++ 4 files changed, 159 insertions(+), 8 deletions(-) diff --git a/sdk/framework/backend.go b/sdk/framework/backend.go index 8a9e313b85..903c2e8309 100644 --- a/sdk/framework/backend.go +++ b/sdk/framework/backend.go @@ -623,6 +623,8 @@ func (t FieldType) Zero() interface{} { return []int{} case TypeHeader: return http.Header{} + case TypeFloat: + return 0.0 default: panic("unknown type: " + t.String()) } diff --git a/sdk/framework/field_data.go b/sdk/framework/field_data.go index 46b120c432..da161cb6ad 100644 --- a/sdk/framework/field_data.go +++ b/sdk/framework/field_data.go @@ -40,7 +40,7 @@ func (d *FieldData) Validate() error { switch schema.Type { case TypeBool, TypeInt, TypeMap, TypeDurationSecond, TypeSignedDurationSecond, TypeString, TypeLowerCaseString, TypeNameString, TypeSlice, TypeStringSlice, TypeCommaStringSlice, - TypeKVPairs, TypeCommaIntSlice, TypeHeader: + TypeKVPairs, TypeCommaIntSlice, TypeHeader, TypeFloat: _, _, err := d.getPrimitive(field, schema) if err != nil { return errwrap.Wrapf(fmt.Sprintf("error converting input %v for field %q: {{err}}", value, field), err) @@ -133,7 +133,7 @@ func (d *FieldData) GetOkErr(k string) (interface{}, bool, error) { switch schema.Type { case TypeBool, TypeInt, TypeMap, TypeDurationSecond, TypeSignedDurationSecond, TypeString, TypeLowerCaseString, TypeNameString, TypeSlice, TypeStringSlice, TypeCommaStringSlice, - TypeKVPairs, TypeCommaIntSlice, TypeHeader: + TypeKVPairs, TypeCommaIntSlice, TypeHeader, TypeFloat: return d.getPrimitive(k, schema) default: return nil, false, @@ -162,6 +162,13 @@ func (d *FieldData) getPrimitive(k string, schema *FieldSchema) (interface{}, bo } return result, true, nil + case TypeFloat: + var result float64 + if err := mapstructure.WeakDecode(raw, &result); err != nil { + return nil, false, err + } + return result, true, nil + case TypeString: var result string if err := mapstructure.WeakDecode(raw, &result); err != nil { diff --git a/sdk/framework/field_data_test.go b/sdk/framework/field_data_test.go index b1ef7aa266..d3f91e7a7c 100644 --- a/sdk/framework/field_data_test.go +++ b/sdk/framework/field_data_test.go @@ -8,10 +8,11 @@ import ( func TestFieldDataGet(t *testing.T) { cases := map[string]struct { - Schema map[string]*FieldSchema - Raw map[string]interface{} - Key string - Value interface{} + Schema map[string]*FieldSchema + Raw map[string]interface{} + Key string + Value interface{} + ExpectError bool }{ "string type, string value": { map[string]*FieldSchema{ @@ -22,6 +23,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", "bar", + false, }, "string type, int value": { @@ -33,6 +35,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", "42", + false, }, "string type, unset value": { @@ -42,6 +45,7 @@ func TestFieldDataGet(t *testing.T) { map[string]interface{}{}, "foo", "", + false, }, "string type, unset value with default": { @@ -54,6 +58,7 @@ func TestFieldDataGet(t *testing.T) { map[string]interface{}{}, "foo", "bar", + false, }, "lowercase string type, lowercase string value": { @@ -65,6 +70,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", "bar", + false, }, "lowercase string type, mixed-case string value": { @@ -76,6 +82,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", "bar", + false, }, "lowercase string type, int value": { @@ -87,6 +94,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", "42", + false, }, "lowercase string type, unset value": { @@ -96,6 +104,7 @@ func TestFieldDataGet(t *testing.T) { map[string]interface{}{}, "foo", "", + false, }, "lowercase string type, unset value with lowercase default": { @@ -108,6 +117,7 @@ func TestFieldDataGet(t *testing.T) { map[string]interface{}{}, "foo", "bar", + false, }, "int type, int value": { @@ -119,6 +129,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", 42, + false, }, "bool type, bool value": { @@ -130,6 +141,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", false, + false, }, "map type, map value": { @@ -145,6 +157,7 @@ func TestFieldDataGet(t *testing.T) { map[string]interface{}{ "child": true, }, + false, }, "duration type, string value": { @@ -156,6 +169,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", 42, + false, }, "duration type, string duration value": { @@ -167,6 +181,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", 2520, + false, }, "duration type, int value": { @@ -178,6 +193,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", 42, + false, }, "duration type, float value": { @@ -189,6 +205,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", 42, + false, }, "duration type, nil value": { @@ -200,6 +217,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", 0, + false, }, "duration type, 0 value": { @@ -211,6 +229,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", 0, + false, }, "signed duration type, positive string value": { @@ -222,6 +241,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", 42, + false, }, "signed duration type, positive string duration value": { @@ -233,6 +253,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", 2520, + false, }, "signed duration type, positive int value": { @@ -244,6 +265,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", 42, + false, }, "signed duration type, positive float value": { @@ -255,6 +277,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", 42, + false, }, "signed duration type, negative string value": { @@ -266,6 +289,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", -42, + false, }, "signed duration type, negative string duration value": { @@ -277,6 +301,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", -2520, + false, }, "signed duration type, negative int value": { @@ -288,6 +313,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", -42, + false, }, "signed duration type, negative float value": { @@ -299,6 +325,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", -42, + false, }, "signed duration type, nil value": { @@ -310,6 +337,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", 0, + false, }, "signed duration type, 0 value": { @@ -321,6 +349,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", 0, + false, }, "slice type, empty slice": { @@ -332,6 +361,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", []interface{}{}, + false, }, "slice type, filled, mixed slice": { @@ -343,6 +373,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", []interface{}{123, "abc"}, + false, }, "string slice type, filled slice": { @@ -354,6 +385,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", []string{"123", "abc"}, + false, }, "string slice type, single value": { @@ -365,6 +397,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", []string{"abc"}, + false, }, "string slice type, empty string": { @@ -376,6 +409,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", []string{}, + false, }, "comma string slice type, empty string": { @@ -387,6 +421,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", []string{}, + false, }, "comma string slice type, comma string with one value": { @@ -398,6 +433,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", []string{"value1"}, + false, }, "comma string slice type, comma string with multi value": { @@ -409,6 +445,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", []string{"value1", "value2", "value3"}, + false, }, "comma string slice type, nil string slice value": { @@ -420,6 +457,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", []string{}, + false, }, "comma string slice type, string slice with one value": { @@ -431,6 +469,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", []string{"value1"}, + false, }, "comma string slice type, string slice with multi value": { @@ -442,6 +481,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", []string{"value1", "value2", "value3"}, + false, }, "comma string slice type, empty string slice value": { @@ -453,6 +493,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", []string{}, + false, }, "comma int slice type, comma int with one value": { @@ -464,6 +505,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", []int{1}, + false, }, "comma int slice type, comma int with multi value slice": { @@ -475,6 +517,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", []int{1, 2, 3}, + false, }, "comma int slice type, comma int with multi value": { @@ -486,6 +529,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", []int{1, 2, 3}, + false, }, "comma int slice type, nil int slice value": { @@ -497,6 +541,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", []int{}, + false, }, "comma int slice type, int slice with one value": { @@ -508,6 +553,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", []int{1}, + false, }, "comma int slice type, int slice with multi value strings": { @@ -519,6 +565,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", []int{1, 2, 3}, + false, }, "comma int slice type, int slice with multi value": { @@ -530,6 +577,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", []int{1, 2, 3}, + false, }, "comma int slice type, empty int slice value": { @@ -541,6 +589,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", []int{}, + false, }, "name string type, valid string": { map[string]*FieldSchema{ @@ -551,6 +600,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", "bar", + false, }, "name string type, valid value with special characters": { @@ -562,6 +612,7 @@ func TestFieldDataGet(t *testing.T) { }, "foo", "bar.baz-bay123", + false, }, "keypair type, valid value map type": { @@ -581,6 +632,7 @@ func TestFieldDataGet(t *testing.T) { "key2": "value2", "key3": "1", }, + false, }, "keypair type, list of equal sign delim key pairs type": { @@ -596,6 +648,7 @@ func TestFieldDataGet(t *testing.T) { "key2": "value2", "key3": "1", }, + false, }, "keypair type, single equal sign delim value": { @@ -609,6 +662,7 @@ func TestFieldDataGet(t *testing.T) { map[string]string{ "key1": "value1", }, + false, }, "type header, keypair string array": { @@ -624,6 +678,7 @@ func TestFieldDataGet(t *testing.T) { "Key2": []string{"value2"}, "Key3": []string{"1"}, }, + false, }, "type header, b64 string": { @@ -643,6 +698,7 @@ func TestFieldDataGet(t *testing.T) { "Authorization": []string{"AWS4-HMAC-SHA256 Credential=foo/20160930/us-east-1/sts/aws4_request, SignedHeaders=content-length;content-type;host;x-amz-date;x-vault-server, Signature=a69fd750a3445c4e553e1b3e79d3da90eef54047f1eb4efe8ffbc9c428c2655b"}, "Foo": []string{"42"}, }, + false, }, "type header, json string": { @@ -659,6 +715,7 @@ func TestFieldDataGet(t *testing.T) { "Guten Tag": []string{"42"}, "你好": []string{"10", "20", "3.14"}, }, + false, }, "type header, keypair string array with dupe key": { @@ -674,6 +731,7 @@ func TestFieldDataGet(t *testing.T) { "Key2": []string{"value2"}, "Key3": []string{"1", "true"}, }, + false, }, "type header, map string slice": { @@ -693,6 +751,7 @@ func TestFieldDataGet(t *testing.T) { "Key2": []string{"value2"}, "Key3": []string{"1"}, }, + false, }, "name string type, not supplied": { @@ -702,6 +761,7 @@ func TestFieldDataGet(t *testing.T) { map[string]interface{}{}, "foo", "", + false, }, "string type, not supplied": { @@ -711,6 +771,7 @@ func TestFieldDataGet(t *testing.T) { map[string]interface{}{}, "foo", "", + false, }, "type int, not supplied": { @@ -720,6 +781,7 @@ func TestFieldDataGet(t *testing.T) { map[string]interface{}{}, "foo", 0, + false, }, "type bool, not supplied": { @@ -729,6 +791,7 @@ func TestFieldDataGet(t *testing.T) { map[string]interface{}{}, "foo", false, + false, }, "type map, not supplied": { @@ -738,6 +801,7 @@ func TestFieldDataGet(t *testing.T) { map[string]interface{}{}, "foo", map[string]interface{}{}, + false, }, "type duration second, not supplied": { @@ -747,6 +811,7 @@ func TestFieldDataGet(t *testing.T) { map[string]interface{}{}, "foo", 0, + false, }, "type signed duration second, not supplied": { @@ -756,6 +821,7 @@ func TestFieldDataGet(t *testing.T) { map[string]interface{}{}, "foo", 0, + false, }, "type slice, not supplied": { @@ -765,6 +831,7 @@ func TestFieldDataGet(t *testing.T) { map[string]interface{}{}, "foo", []interface{}{}, + false, }, "type string slice, not supplied": { @@ -774,6 +841,7 @@ func TestFieldDataGet(t *testing.T) { map[string]interface{}{}, "foo", []string{}, + false, }, "type comma string slice, not supplied": { @@ -783,6 +851,7 @@ func TestFieldDataGet(t *testing.T) { map[string]interface{}{}, "foo", []string{}, + false, }, "type kv pair, not supplied": { @@ -792,6 +861,7 @@ func TestFieldDataGet(t *testing.T) { map[string]interface{}{}, "foo", map[string]string{}, + false, }, "type header, not supplied": { @@ -801,6 +871,65 @@ func TestFieldDataGet(t *testing.T) { map[string]interface{}{}, "foo", http.Header{}, + false, + }, + + "float type, positive with decimals, as string": { + map[string]*FieldSchema{ + "foo": {Type: TypeFloat}, + }, + map[string]interface{}{ + "foo": "1234567.891234567", + }, + "foo", + 1234567.891234567, + false, + }, + + "float type, negative with decimals, as string": { + map[string]*FieldSchema{ + "foo": {Type: TypeFloat}, + }, + map[string]interface{}{ + "foo": "-1234567.891234567", + }, + "foo", + -1234567.891234567, + false, + }, + + "float type, positive without decimals": { + map[string]*FieldSchema{ + "foo": {Type: TypeFloat}, + }, + map[string]interface{}{ + "foo": 1234567, + }, + "foo", + 1234567.0, + false, + }, + + "type float, not supplied": { + map[string]*FieldSchema{ + "foo": {Type: TypeFloat}, + }, + map[string]interface{}{}, + "foo", + 0.0, + false, + }, + + "type float, invalid value": { + map[string]*FieldSchema{ + "foo": {Type: TypeFloat}, + }, + map[string]interface{}{ + "foo": "invalid0.0", + }, + "foo", + 0.0, + true, }, } @@ -813,8 +942,16 @@ func TestFieldDataGet(t *testing.T) { Schema: tc.Schema, } - if err := data.Validate(); err != nil { - t.Fatalf("bad: %s", err) + err := data.Validate() + switch { + case tc.ExpectError && err == nil: + t.Fatalf("expected error") + case tc.ExpectError && err != nil: + return + case !tc.ExpectError && err != nil: + t.Fatal(err) + default: + // Continue if !tc.ExpectError && err == nil } actual := data.Get(tc.Key) diff --git a/sdk/framework/field_type.go b/sdk/framework/field_type.go index 2e1121ca6d..cd631360c7 100644 --- a/sdk/framework/field_type.go +++ b/sdk/framework/field_type.go @@ -53,6 +53,9 @@ const ( // benevolent MITM for a request, and the headers are sent through and // parsed. TypeHeader + + // TypeFloat parses both float32 and float64 values + TypeFloat ) func (t FieldType) String() string { @@ -77,6 +80,8 @@ func (t FieldType) String() string { return "slice" case TypeHeader: return "header" + case TypeFloat: + return "float" default: return "unknown type" }