diff --git a/cfn/cfn_test.go b/cfn/cfn_test.go index b5d5f259..76af76f3 100644 --- a/cfn/cfn_test.go +++ b/cfn/cfn_test.go @@ -10,7 +10,6 @@ import ( "testing" "time" - "github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/encoding" "github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/handler" "github.com/aws/aws-lambda-go/lambdacontext" "github.com/aws/aws-sdk-go/aws" @@ -195,16 +194,16 @@ func TestMakeEventFuncModel(t *testing.T) { t.Errorf("TestMakeEventFuncModel() = %v", err) return } - model, err := encoding.Stringify(got.ResourceModel) + model, err := json.Marshal(got.ResourceModel) if err != nil { t.Errorf("TestMakeEventFuncModel() = %v", err) } - wantrModel, err := encoding.Stringify(tt.want) + wantrModel, err := json.Marshal(tt.want) if err != nil { t.Errorf("TestMakeEventFuncModel() = %v", err) } - if wantrModel != model { - t.Errorf("response = %v; want %v", model, wantrModel) + if string(wantrModel) != string(model) { + t.Errorf("response = %v; want %v", string(model), string(wantrModel)) } }) diff --git a/cfn/encoding/encoding.go b/cfn/encoding/encoding.go deleted file mode 100644 index fc2483af..00000000 --- a/cfn/encoding/encoding.go +++ /dev/null @@ -1,133 +0,0 @@ -/* -Package encoding defines types and functions used for dealing with stringified-JSON. -*/ -package encoding - -import ( - "encoding/json" - "fmt" - "strconv" -) - -// String is a string type to be used when the default json marshaler/unmarshaler cannot be avoided -type String string - -func NewString(ss string) *String { - s := String(ss) - return &s -} - -func (s *String) Value() *string { - return (*string)(s) -} - -func (s String) MarshalJSON() ([]byte, error) { - return json.Marshal(string(s)) -} - -func (s *String) UnmarshalJSON(data []byte) error { - var ss string - err := json.Unmarshal(data, &ss) - if err != nil { - return err - } - - *s = String(ss) - return nil -} - -// Bool is a bool type to be used when the default json marshaler/unmarshaler cannot be avoided -type Bool bool - -func NewBool(bb bool) *Bool { - b := Bool(bb) - return &b -} - -func (b *Bool) Value() *bool { - return (*bool)(b) -} - -func (b Bool) MarshalJSON() ([]byte, error) { - return json.Marshal(fmt.Sprint(bool(b))) -} - -func (b *Bool) UnmarshalJSON(data []byte) error { - var s string - err := json.Unmarshal(data, &s) - if err != nil { - return err - } - - val, err := strconv.ParseBool(s) - if err != nil { - return err - } - - *b = Bool(val) - return nil -} - -// Int is an int type to be used when the default json marshaler/unmarshaler cannot be avoided -type Int int64 - -func NewInt(ii int64) *Int { - i := Int(ii) - return &i -} - -func (i *Int) Value() *int64 { - return (*int64)(i) -} - -func (i Int) MarshalJSON() ([]byte, error) { - return json.Marshal(fmt.Sprint(int64(i))) -} - -func (i *Int) UnmarshalJSON(data []byte) error { - var s string - err := json.Unmarshal(data, &s) - if err != nil { - return err - } - - val, err := strconv.ParseInt(s, 0, 64) - if err != nil { - return err - } - - *i = Int(val) - return nil -} - -// Float is a float type to be used when the default json marshaler/unmarshaler cannot be avoided -type Float float64 - -func NewFloat(ff float64) *Float { - f := Float(ff) - return &f -} - -func (f *Float) Value() *float64 { - return (*float64)(f) -} - -func (f Float) MarshalJSON() ([]byte, error) { - return json.Marshal(fmt.Sprint(float64(f))) -} - -func (f *Float) UnmarshalJSON(data []byte) error { - var s string - err := json.Unmarshal(data, &s) - if err != nil { - return err - } - - val, err := strconv.ParseFloat(s, 64) - if err != nil { - return err - } - - *f = Float(val) - return nil -} diff --git a/cfn/encoding/encoding_test.go b/cfn/encoding/encoding_test.go deleted file mode 100644 index d35fd379..00000000 --- a/cfn/encoding/encoding_test.go +++ /dev/null @@ -1,103 +0,0 @@ -package encoding_test - -import ( - "encoding/json" - "testing" - - "github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/encoding" - - "github.com/google/go-cmp/cmp" - - "github.com/aws/aws-sdk-go/aws" -) - -func TestEncoding(t *testing.T) { - type Nested struct { - SP *string `json:",omitempty"` - BP *bool `json:",omitempty"` - IP *int `json:"intField,omitempty"` - FP *float64 `json:"floatPointer,omitempty"` - - S string `json:"stringValue,omitempty"` - B bool `json:",omitempty"` - I int - F float64 `json:",omitempty"` - } - - type Main struct { - SP *string - BP *bool `json:",omitempty"` - IP *int `json:",omitempty"` - FP *float64 `json:",omitempty"` - NP *Nested `json:"nestedPointer,omitempty"` - - S string `json:",omitempty"` - B bool `json:"boolValue,omitempty"` - I int `json:",omitempty"` - F float64 - N Nested `json:",omitempty"` - } - - m := Main{ - SP: aws.String("foo"), - IP: aws.Int(42), - NP: &Nested{ - BP: aws.Bool(true), - FP: aws.Float64(3.14), - }, - - B: true, - F: 2.72, - N: Nested{ - S: "bar", - I: 54, - }, - } - - stringMap := map[string]interface{}{ - "SP": "foo", - "IP": "42", - "nestedPointer": map[string]interface{}{ - "BP": "true", - "I": "0", - "floatPointer": "3.14", - }, - - "boolValue": "true", - "F": "2.72", - "N": map[string]interface{}{ - "stringValue": "bar", - "I": "54", - }, - } - - var err error - - rep, err := encoding.Marshal(m) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - - // Test that rep can be unmarshalled as regular JSON - var jsonTest map[string]interface{} - err = json.Unmarshal(rep, &jsonTest) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - - // And check it matches the expected form - if diff := cmp.Diff(jsonTest, stringMap); diff != "" { - t.Errorf(diff) - } - - // Now check we can get the original struct back - var b Main - err = encoding.Unmarshal(rep, &b) - if err != nil { - panic(err) - } - - if diff := cmp.Diff(m, b); diff != "" { - t.Errorf(diff) - } -} diff --git a/cfn/encoding/marshal.go b/cfn/encoding/marshal.go deleted file mode 100644 index b086c3b2..00000000 --- a/cfn/encoding/marshal.go +++ /dev/null @@ -1,15 +0,0 @@ -package encoding - -import ( - "encoding/json" -) - -// Marshal converts a value into stringified-JSON -func Marshal(v interface{}) ([]byte, error) { - stringified, err := Stringify(v) - if err != nil { - return nil, err - } - - return json.Marshal(stringified) -} diff --git a/cfn/encoding/marshal_test.go b/cfn/encoding/marshal_test.go deleted file mode 100644 index d4c0a961..00000000 --- a/cfn/encoding/marshal_test.go +++ /dev/null @@ -1,103 +0,0 @@ -package encoding_test - -import ( - "encoding/json" - "testing" - - "github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/encoding" - - "github.com/google/go-cmp/cmp" - - "github.com/aws/aws-sdk-go/aws" -) - -func TestMarshaling(t *testing.T) { - type Nested struct { - SP *string `json:",omitempty"` - BP *bool `json:",omitempty"` - IP *int `json:"intField,omitempty"` - FP *float64 `json:"floatPointer,omitempty"` - - S string `json:"stringValue,omitempty"` - B bool `json:",omitempty"` - I int - F float64 `json:",omitempty"` - } - - type Main struct { - SP *string - BP *bool `json:",omitempty"` - IP *int `json:",omitempty"` - FP *float64 `json:",omitempty"` - NP *Nested `json:"nestedPointer,omitempty"` - - S string `json:",omitempty"` - B bool `json:"boolValue,omitempty"` - I int `json:",omitempty"` - F float64 - N Nested `json:",omitempty"` - } - - m := Main{ - SP: aws.String("foo"), - IP: aws.Int(42), - NP: &Nested{ - BP: aws.Bool(true), - FP: aws.Float64(3.14), - }, - - B: true, - F: 2.72, - N: Nested{ - S: "bar", - I: 54, - }, - } - - stringMap := map[string]interface{}{ - "SP": "foo", - "IP": "42", - "nestedPointer": map[string]interface{}{ - "BP": "true", - "I": "0", - "floatPointer": "3.14", - }, - - "boolValue": "true", - "F": "2.72", - "N": map[string]interface{}{ - "stringValue": "bar", - "I": "54", - }, - } - - var err error - - rep, err := encoding.Marshal(m) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - - // Test that rep can be unmarshalled as regular JSON - var jsonTest map[string]interface{} - err = json.Unmarshal(rep, &jsonTest) - if err != nil { - t.Errorf("Unexpected error: %v", err) - } - - // And check it matches the expected form - if diff := cmp.Diff(jsonTest, stringMap); diff != "" { - t.Errorf(diff) - } - - // Now check we can get the original struct back - var b Main - err = encoding.Unmarshal(rep, &b) - if err != nil { - panic(err) - } - - if diff := cmp.Diff(m, b); diff != "" { - t.Errorf(diff) - } -} diff --git a/cfn/encoding/stringify.go b/cfn/encoding/stringify.go deleted file mode 100644 index 4d4f4840..00000000 --- a/cfn/encoding/stringify.go +++ /dev/null @@ -1,113 +0,0 @@ -package encoding - -import ( - "fmt" - "reflect" - "strings" -) - -func stringifyType(t reflect.Type) reflect.Type { - switch t.Kind() { - case reflect.Map: - return reflect.MapOf(stringType, interfaceType) - case reflect.Slice: - return reflect.SliceOf(interfaceType) - case reflect.Struct: - return stringifyStructType(t) - case reflect.Ptr: - return stringifyType(t.Elem()) - default: - return stringType - } -} - -func stringifyStructType(t reflect.Type) reflect.Type { - fields := make([]reflect.StructField, t.NumField()) - - for i := 0; i < t.NumField(); i++ { - f := t.Field(i) - - fields[i] = reflect.StructField{ - Name: f.Name, - Type: stringifyType(f.Type), - Tag: f.Tag, - } - } - - return reflect.StructOf(fields) -} - -// Stringify converts any supported type into a stringified value -func Stringify(v interface{}) (interface{}, error) { - var err error - - if v == nil { - return nil, nil - } - - val := reflect.ValueOf(v) - - switch val.Kind() { - case reflect.String, reflect.Bool, reflect.Int, reflect.Float64: - return fmt.Sprint(v), nil - case reflect.Map: - out := make(map[string]interface{}) - for _, key := range val.MapKeys() { - v, err := Stringify(val.MapIndex(key).Interface()) - switch { - case err != nil: - return nil, err - case v != nil: - out[key.String()] = v - } - } - return out, nil - case reflect.Slice: - out := make([]interface{}, val.Len()) - for i := 0; i < val.Len(); i++ { - v, err = Stringify(val.Index(i).Interface()) - switch { - case err != nil: - return nil, err - case v != nil: - out[i] = v - } - } - return out, nil - case reflect.Struct: - t := val.Type() - - out := reflect.New(stringifyStructType(t)).Elem() - for i := 0; i < t.NumField(); i++ { - f := t.Field(i) - - v := val.FieldByName(f.Name) - - if tag, ok := f.Tag.Lookup("json"); ok { - if strings.Contains(tag, ",omitempty") { - if v.IsZero() { - continue - } - } - } - - s, err := Stringify(v.Interface()) - switch { - case err != nil: - return nil, err - case s != nil: - out.Field(i).Set(reflect.ValueOf(s)) - } - } - - return out.Interface(), nil - case reflect.Ptr: - if val.IsNil() { - return nil, nil - } - - return Stringify(val.Elem().Interface()) - } - - return nil, fmt.Errorf("Unsupported type: '%v'", val.Kind()) -} diff --git a/cfn/encoding/stringify_test.go b/cfn/encoding/stringify_test.go deleted file mode 100644 index 8387222f..00000000 --- a/cfn/encoding/stringify_test.go +++ /dev/null @@ -1,115 +0,0 @@ -package encoding_test - -import ( - "testing" - - "github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/encoding" - "github.com/aws/aws-sdk-go/aws" - "github.com/google/go-cmp/cmp" -) - -func TestStringifyTypes(t *testing.T) { - type Struct struct { - S string - } - - s := "foo" - b := true - i := 42 - f := 3.14 - l := []interface{}{s, b, i, f} - m := map[string]interface{}{ - "l": l, - } - o := Struct{S: s} - var nilPointer *Struct - - for _, testCase := range []struct { - data interface{} - expected interface{} - }{ - // Basic types - {s, "foo"}, - {b, "true"}, - {i, "42"}, - {f, "3.14"}, - {l, []interface{}{"foo", "true", "42", "3.14"}}, - {m, map[string]interface{}{"l": []interface{}{"foo", "true", "42", "3.14"}}}, - {o, struct{ S string }{S: "foo"}}, - - // Pointers - {&s, "foo"}, - {&b, "true"}, - {&i, "42"}, - {&f, "3.14"}, - {&l, []interface{}{"foo", "true", "42", "3.14"}}, - {&m, map[string]interface{}{"l": []interface{}{"foo", "true", "42", "3.14"}}}, - {&o, struct{ S string }{S: "foo"}}, - - // Nils are stripped - {map[string]interface{}{"foo": nil}, map[string]interface{}{}}, - - // Nil pointers are nil - {nilPointer, nil}, - - // Nils are nil - {nil, nil}, - } { - actual, err := encoding.Stringify(testCase.data) - if err != nil { - t.Fatal(err) - } - - if d := cmp.Diff(actual, testCase.expected); d != "" { - t.Errorf(d) - } - } -} - -func TestStringifyModel(t *testing.T) { - type Model struct { - BucketName *string - Key *string - Body *string - IsBase64Encoded *bool - ContentType *string - ContentLength *int - ACL *string - Grants map[string][]string - } - - m := Model{ - BucketName: aws.String("foo"), - Key: aws.String("bar"), - Body: aws.String("baz"), - ContentType: aws.String("quux"), - ACL: aws.String("mooz"), - } - - expected := struct { - BucketName string - Key string - Body string - IsBase64Encoded string - ContentType string - ContentLength string - ACL string - Grants map[string]interface{} - }{ - BucketName: "foo", - Key: "bar", - Body: "baz", - ContentType: "quux", - ACL: "mooz", - Grants: map[string]interface{}{}, - } - - actual, err := encoding.Stringify(m) - if err != nil { - t.Fatal(err) - } - - if d := cmp.Diff(actual, expected); d != "" { - t.Errorf(d) - } -} diff --git a/cfn/encoding/unmarshal.go b/cfn/encoding/unmarshal.go deleted file mode 100644 index c079cd65..00000000 --- a/cfn/encoding/unmarshal.go +++ /dev/null @@ -1,22 +0,0 @@ -package encoding - -import ( - "encoding/json" -) - -// Unmarshal converts stringified-JSON into the passed-in type -func Unmarshal(data []byte, v interface{}) error { - var dataMap map[string]interface{} - var err error - err = json.Unmarshal(data, &dataMap) - if err != nil { - return err - } - - err = Unstringify(dataMap, v) - if err != nil { - return err - } - - return nil -} diff --git a/cfn/encoding/unstringify.go b/cfn/encoding/unstringify.go deleted file mode 100644 index 6b8821b7..00000000 --- a/cfn/encoding/unstringify.go +++ /dev/null @@ -1,237 +0,0 @@ -package encoding - -import ( - "fmt" - "reflect" - "strconv" - "strings" -) - -func convertStruct(i interface{}, t reflect.Type, pointer bool) (reflect.Value, error) { - m, ok := i.(map[string]interface{}) - if !ok { - return zeroValue, fmt.Errorf("Cannot convert %T to struct", i) - } - - out := reflect.New(t) - - err := Unstringify(m, out.Interface()) - if err != nil { - return zeroValue, err - } - - if !pointer { - out = out.Elem() - } - - return out, nil -} - -func convertSlice(i interface{}, t reflect.Type, pointer bool) (reflect.Value, error) { - s, ok := i.([]interface{}) - if !ok { - return zeroValue, fmt.Errorf("Cannot convert %T to slice", i) - } - - out := reflect.New(t) - out.Elem().Set(reflect.MakeSlice(t, len(s), len(s))) - - for j, v := range s { - val, err := convertType(t.Elem(), v) - if err != nil { - return zeroValue, err - } - - out.Elem().Index(j).Set(val) - } - - if !pointer { - out = out.Elem() - } - - return out, nil -} - -func convertMap(i interface{}, t reflect.Type, pointer bool) (reflect.Value, error) { - m, ok := i.(map[string]interface{}) - if !ok { - return zeroValue, fmt.Errorf("Cannot convert %T to map with string keys", i) - } - - out := reflect.New(t) - out.Elem().Set(reflect.MakeMap(t)) - - for k, v := range m { - val, err := convertType(t.Elem(), v) - if err != nil { - return zeroValue, err - } - - out.Elem().SetMapIndex(reflect.ValueOf(k), val) - } - - if !pointer { - out = out.Elem() - } - - return out, nil -} - -func convertString(i interface{}, pointer bool) (reflect.Value, error) { - s, ok := i.(string) - - if !ok { - return zeroValue, fmt.Errorf("Cannot convert %T to string", i) - } - - if pointer { - return reflect.ValueOf(&s), nil - } - - return reflect.ValueOf(s), nil -} - -func convertBool(i interface{}, pointer bool) (reflect.Value, error) { - var b bool - var err error - - switch v := i.(type) { - case bool: - b = v - - case string: - b, err = strconv.ParseBool(v) - if err != nil { - return zeroValue, err - } - - default: - return zeroValue, fmt.Errorf("Cannot convert %T to bool", i) - } - - if pointer { - return reflect.ValueOf(&b), nil - } - - return reflect.ValueOf(b), nil -} - -func convertInt(i interface{}, pointer bool) (reflect.Value, error) { - var n int - - switch v := i.(type) { - case int: - n = v - - case float64: - n = int(v) - - case string: - n64, err := strconv.ParseInt(v, 0, 32) - if err != nil { - return zeroValue, err - } - - n = int(n64) - - default: - return zeroValue, fmt.Errorf("Cannot convert %T to bool", i) - } - - if pointer { - return reflect.ValueOf(&n), nil - } - - return reflect.ValueOf(n), nil -} - -func convertFloat64(i interface{}, pointer bool) (reflect.Value, error) { - var f float64 - var err error - - switch v := i.(type) { - case float64: - f = v - - case int: - f = float64(v) - - case string: - f, err = strconv.ParseFloat(v, 64) - if err != nil { - return zeroValue, err - } - - default: - return zeroValue, fmt.Errorf("Cannot convert %T to bool", i) - } - - if pointer { - return reflect.ValueOf(&f), nil - } - - return reflect.ValueOf(f), nil -} - -func convertType(t reflect.Type, i interface{}) (reflect.Value, error) { - pointer := false - if t.Kind() == reflect.Ptr { - pointer = true - t = t.Elem() - } - - switch t.Kind() { - case reflect.Struct: - return convertStruct(i, t, pointer) - - case reflect.Slice: - return convertSlice(i, t, pointer) - - case reflect.Map: - return convertMap(i, t, pointer) - - case reflect.String: - return convertString(i, pointer) - - case reflect.Bool: - return convertBool(i, pointer) - - case reflect.Int: - return convertInt(i, pointer) - - case reflect.Float64: - return convertFloat64(i, pointer) - - default: - return zeroValue, fmt.Errorf("Unsupported type %v", t) - } -} - -// Unstringify takes a stringified representation of a value -// and populates it into the supplied interface -func Unstringify(data map[string]interface{}, v interface{}) error { - t := reflect.TypeOf(v).Elem() - - val := reflect.ValueOf(v).Elem() - - for i := 0; i < t.NumField(); i++ { - f := t.Field(i) - - jsonName := f.Name - jsonTag := strings.Split(f.Tag.Get("json"), ",") - if len(jsonTag) > 0 && jsonTag[0] != "" { - jsonName = jsonTag[0] - } - - if value, ok := data[jsonName]; ok { - newValue, err := convertType(f.Type, value) - if err != nil { - return err - } - - val.FieldByName(f.Name).Set(newValue) - } - } - - return nil -} diff --git a/cfn/encoding/unstringify_test.go b/cfn/encoding/unstringify_test.go deleted file mode 100644 index 8907853d..00000000 --- a/cfn/encoding/unstringify_test.go +++ /dev/null @@ -1,315 +0,0 @@ -package encoding_test - -import ( - "testing" - - "github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/encoding" - "github.com/aws/aws-sdk-go/aws" - "github.com/google/go-cmp/cmp" -) - -func TestUnstringifyStruct(t *testing.T) { - type Model struct { - S string - SP *string - B bool - BP *bool - I int - IP *int - F float64 - FP *float64 - } - - expected := Model{ - S: "foo", - SP: aws.String("bar"), - B: true, - BP: aws.Bool(true), - I: 42, - IP: aws.Int(42), - F: 3.14, - FP: aws.Float64(22), - } - - t.Run("Convert strings", func(t *testing.T) { - var actual Model - - err := encoding.Unstringify(map[string]interface{}{ - "S": "foo", - "SP": "bar", - "B": "true", - "BP": "true", - "I": "42", - "IP": "42", - "F": "3.14", - "FP": "22", - }, &actual) - - if err != nil { - t.Fatal(err) - } - - if d := cmp.Diff(actual, expected); d != "" { - t.Error(d) - } - }) - - t.Run("Original types", func(t *testing.T) { - var actual Model - - err := encoding.Unstringify(map[string]interface{}{ - "S": "foo", - "SP": "bar", - "B": true, - "BP": true, - "I": 42, - "IP": 42, - "F": 3.14, - "FP": 22.0, - }, &actual) - - if err != nil { - t.Fatal(err) - } - - if d := cmp.Diff(actual, expected); d != "" { - t.Error(d) - } - }) - - t.Run("Compatible types", func(t *testing.T) { - var actual Model - - err := encoding.Unstringify(map[string]interface{}{ - "S": "foo", - "SP": "bar", - "B": true, - "BP": true, - "I": float64(42), - "IP": float64(42), - "F": 3.14, - "FP": int(22), - }, &actual) - - if err != nil { - t.Fatal(err) - } - - if d := cmp.Diff(actual, expected); d != "" { - t.Error(d) - } - }) -} - -func TestUnstringifySlices(t *testing.T) { - type Model struct { - S []string - SP []*string - B []bool - BP []*bool - I []int - IP []*int - F []float64 - FP []*float64 - } - - expected := Model{ - S: []string{"foo"}, - SP: []*string{aws.String("bar")}, - B: []bool{true}, - BP: []*bool{aws.Bool(true)}, - I: []int{42}, - IP: []*int{aws.Int(42)}, - F: []float64{3.14}, - FP: []*float64{aws.Float64(22)}, - } - - t.Run("Convert strings", func(t *testing.T) { - var actual Model - - err := encoding.Unstringify(map[string]interface{}{ - "S": []interface{}{"foo"}, - "SP": []interface{}{"bar"}, - "B": []interface{}{"true"}, - "BP": []interface{}{"true"}, - "I": []interface{}{"42"}, - "IP": []interface{}{"42"}, - "F": []interface{}{"3.14"}, - "FP": []interface{}{"22"}, - }, &actual) - - if err != nil { - t.Fatal(err) - } - - if d := cmp.Diff(actual, expected); d != "" { - t.Error(d) - } - }) - - t.Run("Original types", func(t *testing.T) { - var actual Model - - err := encoding.Unstringify(map[string]interface{}{ - "S": []interface{}{"foo"}, - "SP": []interface{}{"bar"}, - "B": []interface{}{true}, - "BP": []interface{}{true}, - "I": []interface{}{42}, - "IP": []interface{}{42}, - "F": []interface{}{3.14}, - "FP": []interface{}{22.0}, - }, &actual) - - if err != nil { - t.Fatal(err) - } - - if d := cmp.Diff(actual, expected); d != "" { - t.Error(d) - } - }) - - t.Run("Compatible types", func(t *testing.T) { - var actual Model - - err := encoding.Unstringify(map[string]interface{}{ - "S": []interface{}{"foo"}, - "SP": []interface{}{"bar"}, - "B": []interface{}{true}, - "BP": []interface{}{true}, - "I": []interface{}{float64(42)}, - "IP": []interface{}{float64(42)}, - "F": []interface{}{3.14}, - "FP": []interface{}{int(22)}, - }, &actual) - - if err != nil { - t.Fatal(err) - } - - if d := cmp.Diff(actual, expected); d != "" { - t.Error(d) - } - }) -} - -func TestUnstringifyMaps(t *testing.T) { - type Model struct { - S map[string]string - SP map[string]*string - B map[string]bool - BP map[string]*bool - I map[string]int - IP map[string]*int - F map[string]float64 - FP map[string]*float64 - } - - expected := Model{ - S: map[string]string{"Val": "foo"}, - SP: map[string]*string{"Val": aws.String("bar")}, - B: map[string]bool{"Val": true}, - BP: map[string]*bool{"Val": aws.Bool(true)}, - I: map[string]int{"Val": 42}, - IP: map[string]*int{"Val": aws.Int(42)}, - F: map[string]float64{"Val": 3.14}, - FP: map[string]*float64{"Val": aws.Float64(22)}, - } - - t.Run("Convert strings", func(t *testing.T) { - var actual Model - - err := encoding.Unstringify(map[string]interface{}{ - "S": map[string]interface{}{"Val": "foo"}, - "SP": map[string]interface{}{"Val": "bar"}, - "B": map[string]interface{}{"Val": "true"}, - "BP": map[string]interface{}{"Val": "true"}, - "I": map[string]interface{}{"Val": "42"}, - "IP": map[string]interface{}{"Val": "42"}, - "F": map[string]interface{}{"Val": "3.14"}, - "FP": map[string]interface{}{"Val": "22"}, - }, &actual) - - if err != nil { - t.Fatal(err) - } - - if d := cmp.Diff(actual, expected); d != "" { - t.Error(d) - } - }) - - t.Run("Original types", func(t *testing.T) { - var actual Model - - err := encoding.Unstringify(map[string]interface{}{ - "S": map[string]interface{}{"Val": "foo"}, - "SP": map[string]interface{}{"Val": "bar"}, - "B": map[string]interface{}{"Val": true}, - "BP": map[string]interface{}{"Val": true}, - "I": map[string]interface{}{"Val": 42}, - "IP": map[string]interface{}{"Val": 42}, - "F": map[string]interface{}{"Val": 3.14}, - "FP": map[string]interface{}{"Val": 22.0}, - }, &actual) - - if err != nil { - t.Fatal(err) - } - - if d := cmp.Diff(actual, expected); d != "" { - t.Error(d) - } - }) - - t.Run("Compatible types", func(t *testing.T) { - var actual Model - - err := encoding.Unstringify(map[string]interface{}{ - "S": map[string]interface{}{"Val": "foo"}, - "SP": map[string]interface{}{"Val": "bar"}, - "B": map[string]interface{}{"Val": true}, - "BP": map[string]interface{}{"Val": true}, - "I": map[string]interface{}{"Val": float64(42)}, - "IP": map[string]interface{}{"Val": float64(42)}, - "F": map[string]interface{}{"Val": 3.14}, - "FP": map[string]interface{}{"Val": int(22)}, - }, &actual) - - if err != nil { - t.Fatal(err) - } - - if d := cmp.Diff(actual, expected); d != "" { - t.Error(d) - } - }) -} - -func TestUnstringifyPointers(t *testing.T) { - type Model struct { - SSP *[]string - SMP *map[string]string - } - - expected := Model{ - SSP: &[]string{"foo"}, - SMP: &map[string]string{"bar": "baz"}, - } - - var actual Model - - err := encoding.Unstringify(map[string]interface{}{ - "SSP": []interface{}{"foo"}, - "SMP": map[string]interface{}{"bar": "baz"}, - }, &actual) - - if err != nil { - t.Fatal(err) - } - - if d := cmp.Diff(actual, expected); d != "" { - t.Error(d) - } -} diff --git a/cfn/encoding/values.go b/cfn/encoding/values.go deleted file mode 100644 index 94a5fddb..00000000 --- a/cfn/encoding/values.go +++ /dev/null @@ -1,10 +0,0 @@ -package encoding - -import ( - "reflect" -) - -var zeroValue reflect.Value - -var interfaceType = reflect.TypeOf((*interface{})(nil)).Elem() -var stringType = reflect.TypeOf("") diff --git a/cfn/event.go b/cfn/event.go index 2fba2f58..d23921da 100644 --- a/cfn/event.go +++ b/cfn/event.go @@ -5,7 +5,6 @@ import ( "github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/cfnerr" "github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/credentials" - "github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/encoding" "gopkg.in/validator.v2" ) @@ -17,7 +16,7 @@ type event struct { Region string `json:"region" validate:"nonzero"` Action string `json:"action"` ResourceType string `json:"resourceType"` - ResourceTypeVersion encoding.Float `json:"resourceTypeVersion"` + ResourceTypeVersion float64 `json:"resourceTypeVersion,string"` CallbackContext map[string]interface{} `json:"callbackContext,omitempty"` RequestData requestData `json:"requestData"` StackID string `json:"stackId"` diff --git a/cfn/handler/event_test.go b/cfn/handler/event_test.go index 82af0926..5a4808ea 100644 --- a/cfn/handler/event_test.go +++ b/cfn/handler/event_test.go @@ -1,20 +1,19 @@ package handler import ( + "github.com/aws/aws-sdk-go/aws" "testing" "github.com/aws/aws-sdk-go/service/cloudformation" "github.com/google/go-cmp/cmp" "encoding/json" - - "github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/encoding" ) func TestProgressEventMarshalJSON(t *testing.T) { type Model struct { - Name *encoding.String - Version *encoding.Float + Name *string `json:",omitempty"` + Version *float64 `json:",omitempty,string"` } for _, tt := range []struct { @@ -28,8 +27,8 @@ func TestProgressEventMarshalJSON(t *testing.T) { Message: "foo", OperationStatus: Failed, ResourceModel: Model{ - Name: encoding.NewString("Douglas"), - Version: encoding.NewFloat(42.1), + Name: aws.String("Douglas"), + Version: aws.Float64(42.1), }, HandlerErrorCode: cloudformation.HandlerErrorCodeNotUpdatable, }, @@ -41,8 +40,8 @@ func TestProgressEventMarshalJSON(t *testing.T) { OperationStatus: Success, ResourceModels: []interface{}{ Model{ - Name: encoding.NewString("Douglas"), - Version: encoding.NewFloat(42.1), + Name: aws.String("Douglas"), + Version: aws.Float64(42.1), }, }, }, diff --git a/cfn/handler/request.go b/cfn/handler/request.go index f44c2cb6..b0edf67e 100644 --- a/cfn/handler/request.go +++ b/cfn/handler/request.go @@ -1,10 +1,10 @@ package handler import ( + "encoding/json" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/cfnerr" - "github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/encoding" ) const ( @@ -79,7 +79,7 @@ func (r *Request) UnmarshalPrevious(v interface{}) error { return nil } - if err := encoding.Unmarshal(r.previousResourcePropertiesBody, v); err != nil { + if err := json.Unmarshal(r.previousResourcePropertiesBody, v); err != nil { return cfnerr.New(marshalingError, "Unable to convert type", err) } @@ -93,7 +93,7 @@ func (r *Request) Unmarshal(v interface{}) error { return cfnerr.New(bodyEmptyError, "Body is empty", nil) } - if err := encoding.Unmarshal(r.resourcePropertiesBody, v); err != nil { + if err := json.Unmarshal(r.resourcePropertiesBody, v); err != nil { return cfnerr.New(marshalingError, "Unable to convert type", err) } diff --git a/cfn/handler/request_test.go b/cfn/handler/request_test.go index f7728fd2..e7233b2a 100644 --- a/cfn/handler/request_test.go +++ b/cfn/handler/request_test.go @@ -9,13 +9,13 @@ import ( func TestUnmarshal(t *testing.T) { type Detail struct { - Build *int - IsProduction *bool + Build *int `json:",string"` + IsProduction *bool `json:",string"` } type Model struct { Name *string - Version *float64 + Version *float64 `json:",string"` Detail *Detail } diff --git a/cfn/response.go b/cfn/response.go index 1d69fb19..96a9634a 100644 --- a/cfn/response.go +++ b/cfn/response.go @@ -1,7 +1,6 @@ package cfn import ( - "github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/encoding" "github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/handler" "github.com/aws/aws-sdk-go/service/cloudformation" ) @@ -65,30 +64,13 @@ func newFailedResponse(err error, bearerToken string) response { // newResponse converts a progress event into a useable reponse // for the CloudFormation Resource Provider service to understand. func newResponse(pevt *handler.ProgressEvent, bearerToken string) (response, error) { - model, err := encoding.Stringify(pevt.ResourceModel) - if err != nil { - return response{}, err - } - - var models []interface{} - if pevt.ResourceModels != nil { - models = make([]interface{}, len(pevt.ResourceModels)) - for i := range pevt.ResourceModels { - m, err := encoding.Stringify(pevt.ResourceModels[i]) - if err != nil { - return response{}, err - } - - models[i] = m - } - } resp := response{ BearerToken: bearerToken, Message: pevt.Message, OperationStatus: pevt.OperationStatus, - ResourceModel: model, - ResourceModels: models, + ResourceModel: pevt.ResourceModel, + ResourceModels: pevt.ResourceModels, NextToken: pevt.NextToken, CallbackContext: pevt.CallbackContext, CallbackDelaySeconds: pevt.CallbackDelaySeconds, diff --git a/cfn/response_test.go b/cfn/response_test.go index fb296079..dda19977 100644 --- a/cfn/response_test.go +++ b/cfn/response_test.go @@ -1,6 +1,7 @@ package cfn import ( + "github.com/aws/aws-sdk-go/aws" "testing" "github.com/aws/aws-sdk-go/service/cloudformation" @@ -8,14 +9,13 @@ import ( "encoding/json" - "github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/encoding" "github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/handler" ) func TestResponseMarshalJSON(t *testing.T) { type Model struct { - Name *encoding.String - Version *encoding.Float + Name *string `json:",omitempty"` + Version *float64 `json:",omitempty,string"` } for _, tt := range []struct { @@ -29,8 +29,8 @@ func TestResponseMarshalJSON(t *testing.T) { Message: "foo", OperationStatus: handler.Failed, ResourceModel: Model{ - Name: encoding.NewString("Douglas"), - Version: encoding.NewFloat(42.1), + Name: aws.String("Douglas"), + Version: aws.Float64(42.1), }, ErrorCode: cloudformation.HandlerErrorCodeNotUpdatable, BearerToken: "xyzzy", @@ -43,8 +43,8 @@ func TestResponseMarshalJSON(t *testing.T) { OperationStatus: handler.Success, ResourceModels: []interface{}{ Model{ - Name: encoding.NewString("Douglas"), - Version: encoding.NewFloat(42.1), + Name: aws.String("Douglas"), + Version: aws.Float64(42.1), }, }, BearerToken: "xyzzy", @@ -69,6 +69,7 @@ func TestResponseMarshalJSON(t *testing.T) { } if diff := cmp.Diff(string(actual), tt.expected); diff != "" { + t.Errorf("response = %v; want %v", string(actual), tt.expected) t.Errorf(diff) } }) diff --git a/python/rpdk/go/templates/types.go.tple b/python/rpdk/go/templates/types.go.tple index e54eac02..2485be3b 100644 --- a/python/rpdk/go/templates/types.go.tple +++ b/python/rpdk/go/templates/types.go.tple @@ -12,7 +12,7 @@ package resource // {{model_name}} is autogenerated from the json schema type {{model_name}} struct { {% for name, type in properties.items() %} - {{ name|uppercase_first_letter }} {{ type|translate_type }} `json:",omitempty"` + {{ name|uppercase_first_letter }} {{ type|translate_type }} `json:",omitempty{% if type|translate_type in ["*bool", "*int", "*float64"] %},string{% endif %}"` {% endfor %} }