Skip to content

Commit c3ac760

Browse files
authored
Move stringifying encoder into response constructor (#113)
* Move stringifying encoder into response constructor * Deal with composite types properly in the type resolver * Complete implementation of Stringify * Move populate into its own file and rename it Unstringify
1 parent 578d785 commit c3ac760

18 files changed

+680
-167
lines changed

cfn/cfn.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/callback"
1212
"github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/cfnerr"
1313
"github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/credentials"
14+
"github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/encoding"
1415
"github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/handler"
1516
"github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/logging"
1617
"github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/metrics"
@@ -152,6 +153,14 @@ func invoke(handlerFn handlerFunc, request handler.Request, metricsPublisher *me
152153

153154
// Report the work is done.
154155
progEvt := handlerFn(request)
156+
157+
marshaled, _ := encoding.Marshal(progEvt.ResourceModel)
158+
log.Printf("Received event: %s\nMessage: %s\nBody: %s",
159+
progEvt.OperationStatus,
160+
progEvt.Message,
161+
marshaled,
162+
)
163+
155164
elapsed := time.Since(start)
156165
metricsPublisher.PublishDurationMetric(time.Now(), string(action), elapsed.Seconds()*1e3)
157166
ch <- progEvt
@@ -209,6 +218,7 @@ func processinvoke(handlerFn handlerFunc, event *event, request handler.Request,
209218
progEvt, err := invoke(handlerFn, request, metricsPublisher, event.Action)
210219

211220
if err != nil {
221+
log.Printf("Handler invocation failed: %v", err)
212222
return handler.NewFailedEvent(err)
213223
}
214224
return progEvt
@@ -306,8 +316,8 @@ func makeEventFunc(h Handler) eventFunc {
306316
cusCtx, delay := marshalCallback(&progEvt)
307317

308318
r, err := newResponse(&progEvt, event.BearerToken)
309-
310319
if err != nil {
320+
log.Printf("Error creating response: %v", err)
311321
return re.report(event, "Response error", err, unmarshalingError)
312322
}
313323

cfn/cfn_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ func TestMakeTestEventFunc(t *testing.T) {
276276
OperationStatus: handler.Success,
277277
Message: "Create complete",
278278
}, false},
279-
{"Test simple INVALID", args{&MockHandler{f1}, lc, loadTestEvent("test.invalid.json", &testEvent{})}, handler.ProgressEvent{
279+
{"Test simple INVALID", args{&MockHandler{f1}, lc, loadTestEvent("test.INVALID.json", &testEvent{})}, handler.ProgressEvent{
280280
OperationStatus: handler.Failed,
281281
Message: "InvalidRequest: No action/invalid action specified",
282282
}, true},

cfn/encoding/encoding.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/*
2-
Package encoding defines custom types for strings, bools, ints, and floats
3-
that must be used my resource provider model structs.
2+
Package encoding defines types and functions used for dealing with stringified-JSON.
43
*/
54
package encoding
65

@@ -10,6 +9,7 @@ import (
109
"strconv"
1110
)
1211

12+
// String is a string type to be used when the default json marshaler/unmarshaler cannot be avoided
1313
type String string
1414

1515
func NewString(ss string) *String {
@@ -36,6 +36,7 @@ func (s *String) UnmarshalJSON(data []byte) error {
3636
return nil
3737
}
3838

39+
// Bool is a bool type to be used when the default json marshaler/unmarshaler cannot be avoided
3940
type Bool bool
4041

4142
func NewBool(bb bool) *Bool {
@@ -67,6 +68,7 @@ func (b *Bool) UnmarshalJSON(data []byte) error {
6768
return nil
6869
}
6970

71+
// Int is an int type to be used when the default json marshaler/unmarshaler cannot be avoided
7072
type Int int64
7173

7274
func NewInt(ii int64) *Int {
@@ -98,6 +100,7 @@ func (i *Int) UnmarshalJSON(data []byte) error {
98100
return nil
99101
}
100102

103+
// Float is a float type to be used when the default json marshaler/unmarshaler cannot be avoided
101104
type Float float64
102105

103106
func NewFloat(ff float64) *Float {

cfn/encoding/encoding_test.go

Lines changed: 67 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -7,138 +7,97 @@ import (
77
"github.com/aws-cloudformation/cloudformation-cli-go-plugin/cfn/encoding"
88

99
"github.com/google/go-cmp/cmp"
10+
11+
"github.com/aws/aws-sdk-go/aws"
1012
)
1113

12-
type Model struct {
13-
ModelString *encoding.String `json:"modelString,omitempty"`
14-
ModelBool *encoding.Bool `json:"modelBool,omitempty"`
15-
ModelInt *encoding.Int `json:"modelInt,omitempty"`
16-
ModelFloat *encoding.Float `json:"modelFloat,omitempty"`
17-
ModelSlice []Inner `json:"modelSlice,omitempty"`
18-
ModelMap map[string]Inner `json:"modelMap,omitempty"`
19-
ModelNested json.RawMessage `json:"embedded,omitempty"`
20-
}
14+
func TestEncoding(t *testing.T) {
15+
type Nested struct {
16+
SP *string `json:",omitempty"`
17+
BP *bool `json:",omitempty"`
18+
IP *int `json:"intField,omitempty"`
19+
FP *float64 `json:"floatPointer,omitempty"`
20+
21+
S string `json:"stringValue,omitempty"`
22+
B bool `json:",omitempty"`
23+
I int
24+
F float64 `json:",omitempty"`
25+
}
2126

22-
type Inner struct {
23-
InnerString *encoding.String `json:"innerString,omitempty"`
24-
InnerBool *encoding.Bool `json:"innerBool,omitempty"`
25-
InnerInt *encoding.Int `json:"innerInt,omitempty"`
26-
InnerFloat *encoding.Float `json:"innerFloat"` // No omitempty
27-
}
27+
type Main struct {
28+
SP *string
29+
BP *bool `json:",omitempty"`
30+
IP *int `json:",omitempty"`
31+
FP *float64 `json:",omitempty"`
32+
NP *Nested `json:"nestedPointer,omitempty"`
33+
34+
S string `json:",omitempty"`
35+
B bool `json:"boolValue,omitempty"`
36+
I int `json:",omitempty"`
37+
F float64
38+
N Nested `json:",omitempty"`
39+
}
2840

29-
var model = Model{
30-
ModelBool: encoding.NewBool(false),
31-
ModelInt: encoding.NewInt(42),
32-
ModelFloat: encoding.NewFloat(3.14),
33-
ModelSlice: []Inner{
34-
{
35-
InnerString: encoding.NewString("bar"),
36-
InnerInt: encoding.NewInt(43),
37-
InnerFloat: encoding.NewFloat(6.28),
38-
},
39-
},
40-
ModelMap: map[string]Inner{
41-
"ModelMapInner": {
42-
InnerString: encoding.NewString("baz"),
43-
InnerBool: encoding.NewBool(false),
44-
InnerFloat: encoding.NewFloat(9.42),
41+
m := Main{
42+
SP: aws.String("foo"),
43+
IP: aws.Int(42),
44+
NP: &Nested{
45+
BP: aws.Bool(true),
46+
FP: aws.Float64(3.14),
4547
},
46-
},
47-
ModelNested: []byte(`{"innerBool":"true","innerFloat":null,"innerInt":"45"}`),
48-
}
4948

50-
var stringified = map[string]interface{}{
51-
"modelBool": "false",
52-
"modelInt": "42",
53-
"modelFloat": "3.14",
54-
"modelSlice": []interface{}{
55-
map[string]interface{}{
56-
"innerString": "bar",
57-
"innerInt": "43",
58-
"innerFloat": "6.28",
59-
},
60-
},
61-
"modelMap": map[string]interface{}{
62-
"ModelMapInner": map[string]interface{}{
63-
"innerString": "baz",
64-
"innerBool": "false",
65-
"innerFloat": "9.42",
49+
B: true,
50+
F: 2.72,
51+
N: Nested{
52+
S: "bar",
53+
I: 54,
6654
},
67-
},
68-
"embedded": map[string]interface{}{
69-
"innerBool": "true",
70-
"innerInt": "45",
71-
"innerFloat": nil,
72-
},
73-
}
74-
75-
func TestString(t *testing.T) {
76-
v := "Hello, world!"
77-
s := encoding.NewString(v)
78-
79-
// Value
80-
if *s.Value() != v {
81-
t.Errorf("Value failed: %v", s.Value())
8255
}
8356

84-
// Marshal
85-
data, err := json.Marshal(s)
86-
if err != nil {
87-
t.Error(err)
88-
}
89-
90-
if string(data) != `"Hello, world!"` {
91-
t.Error("Marshal failed: " + string(data))
92-
}
93-
94-
// Unmarshal value
95-
v = "Unmarshal me"
96-
data, err = json.Marshal(v)
97-
if err != nil {
98-
panic(err)
99-
}
57+
stringMap := map[string]interface{}{
58+
"SP": "foo",
59+
"IP": "42",
60+
"nestedPointer": map[string]interface{}{
61+
"BP": "true",
62+
"I": "0",
63+
"floatPointer": "3.14",
64+
},
10065

101-
err = json.Unmarshal(data, s)
102-
if err != nil {
103-
t.Error(err)
66+
"boolValue": "true",
67+
"F": "2.72",
68+
"N": map[string]interface{}{
69+
"stringValue": "bar",
70+
"I": "54",
71+
},
10472
}
10573

106-
if *s.Value() != v {
107-
t.Errorf("Unmarshal value failed: %v", s.Value())
108-
}
109-
}
74+
var err error
11075

111-
func TestMarshal(t *testing.T) {
112-
data, err := json.Marshal(model)
76+
rep, err := encoding.Marshal(m)
11377
if err != nil {
114-
t.Error(err)
78+
t.Errorf("Unexpected error: %v", err)
11579
}
11680

117-
actual := make(map[string]interface{})
118-
err = json.Unmarshal(data, &actual)
81+
// Test that rep can be unmarshalled as regular JSON
82+
var jsonTest map[string]interface{}
83+
err = json.Unmarshal(rep, &jsonTest)
11984
if err != nil {
120-
t.Error(err)
85+
t.Errorf("Unexpected error: %v", err)
12186
}
12287

123-
if diff := cmp.Diff(stringified, actual); diff != "" {
124-
t.Error(diff)
88+
// And check it matches the expected form
89+
if diff := cmp.Diff(jsonTest, stringMap); diff != "" {
90+
t.Errorf(diff)
12591
}
126-
}
12792

128-
func TestUnmarshal(t *testing.T) {
129-
data, err := json.Marshal(stringified)
93+
// Now check we can get the original struct back
94+
var b Main
95+
err = encoding.Unmarshal(rep, &b)
13096
if err != nil {
13197
panic(err)
13298
}
13399

134-
actual := Model{}
135-
136-
err = json.Unmarshal(data, &actual)
137-
if err != nil {
138-
t.Error(err)
139-
}
140-
141-
if diff := cmp.Diff(model, actual); diff != "" {
142-
t.Error(diff)
100+
if diff := cmp.Diff(m, b); diff != "" {
101+
t.Errorf(diff)
143102
}
144103
}

cfn/encoding/marshal.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package encoding
2+
3+
import (
4+
"encoding/json"
5+
)
6+
7+
// Marshal converts a value into stringified-JSON
8+
func Marshal(v interface{}) ([]byte, error) {
9+
stringified, err := Stringify(v)
10+
if err != nil {
11+
return nil, err
12+
}
13+
14+
return json.Marshal(stringified)
15+
}

0 commit comments

Comments
 (0)