From 5749b23d972a80f0a708549e48c2eb399d5ed7fd Mon Sep 17 00:00:00 2001 From: Graham Jenson Date: Wed, 13 Mar 2019 08:30:39 +0000 Subject: [PATCH] fix(parser): Select the correct AWS CloudFormation resource type based on similarity (#183) * [Fix] Select the correct polymorphic type --- .../awsserverlessapi_definitionuri.go | 15 ++-- .../awsserverlessapplication_location.go | 15 ++-- .../awsserverlessfunction_codeuri.go | 15 ++-- .../awsserverlessfunction_policies.go | 15 ++-- .../awsserverlessfunction_properties.go | 69 +++++-------------- cloudformation/resources/utils.go | 36 ++++++++++ generate/property_test.go | 37 ++++++++++ .../templates/polymorphic-property.template | 24 +++---- 8 files changed, 139 insertions(+), 87 deletions(-) create mode 100644 cloudformation/resources/utils.go diff --git a/cloudformation/resources/awsserverlessapi_definitionuri.go b/cloudformation/resources/awsserverlessapi_definitionuri.go index 6ed25a14d7..821914efab 100644 --- a/cloudformation/resources/awsserverlessapi_definitionuri.go +++ b/cloudformation/resources/awsserverlessapi_definitionuri.go @@ -1,9 +1,9 @@ package resources import ( - "encoding/json" + "sort" - "reflect" + "encoding/json" "github.com/mitchellh/mapstructure" ) @@ -21,12 +21,15 @@ func (r AWSServerlessApi_DefinitionUri) value() interface{} { return r.String } - if r.S3Location != nil && !reflect.DeepEqual(r.S3Location, &AWSServerlessApi_S3Location{}) { - return r.S3Location - } + ret := []interface{}{} if r.S3Location != nil { - return r.S3Location + ret = append(ret, *r.S3Location) + } + + sort.Sort(byJSONLength(ret)) + if len(ret) > 0 { + return ret[0] } return nil diff --git a/cloudformation/resources/awsserverlessapplication_location.go b/cloudformation/resources/awsserverlessapplication_location.go index 8ec4812f27..1db8eb2f56 100644 --- a/cloudformation/resources/awsserverlessapplication_location.go +++ b/cloudformation/resources/awsserverlessapplication_location.go @@ -1,9 +1,9 @@ package resources import ( - "encoding/json" + "sort" - "reflect" + "encoding/json" "github.com/mitchellh/mapstructure" ) @@ -21,12 +21,15 @@ func (r AWSServerlessApplication_Location) value() interface{} { return r.String } - if r.ApplicationLocation != nil && !reflect.DeepEqual(r.ApplicationLocation, &AWSServerlessApplication_ApplicationLocation{}) { - return r.ApplicationLocation - } + ret := []interface{}{} if r.ApplicationLocation != nil { - return r.ApplicationLocation + ret = append(ret, *r.ApplicationLocation) + } + + sort.Sort(byJSONLength(ret)) + if len(ret) > 0 { + return ret[0] } return nil diff --git a/cloudformation/resources/awsserverlessfunction_codeuri.go b/cloudformation/resources/awsserverlessfunction_codeuri.go index ee412ecfdd..197d588640 100644 --- a/cloudformation/resources/awsserverlessfunction_codeuri.go +++ b/cloudformation/resources/awsserverlessfunction_codeuri.go @@ -1,9 +1,9 @@ package resources import ( - "encoding/json" + "sort" - "reflect" + "encoding/json" "github.com/mitchellh/mapstructure" ) @@ -21,12 +21,15 @@ func (r AWSServerlessFunction_CodeUri) value() interface{} { return r.String } - if r.S3Location != nil && !reflect.DeepEqual(r.S3Location, &AWSServerlessFunction_S3Location{}) { - return r.S3Location - } + ret := []interface{}{} if r.S3Location != nil { - return r.S3Location + ret = append(ret, *r.S3Location) + } + + sort.Sort(byJSONLength(ret)) + if len(ret) > 0 { + return ret[0] } return nil diff --git a/cloudformation/resources/awsserverlessfunction_policies.go b/cloudformation/resources/awsserverlessfunction_policies.go index d1bb976696..e4ea3e625e 100644 --- a/cloudformation/resources/awsserverlessfunction_policies.go +++ b/cloudformation/resources/awsserverlessfunction_policies.go @@ -1,9 +1,9 @@ package resources import ( - "encoding/json" + "sort" - "reflect" + "encoding/json" "github.com/mitchellh/mapstructure" ) @@ -29,12 +29,15 @@ func (r AWSServerlessFunction_Policies) value() interface{} { return r.StringArray } - if r.IAMPolicyDocument != nil && !reflect.DeepEqual(r.IAMPolicyDocument, &AWSServerlessFunction_IAMPolicyDocument{}) { - return r.IAMPolicyDocument - } + ret := []interface{}{} if r.IAMPolicyDocument != nil { - return r.IAMPolicyDocument + ret = append(ret, *r.IAMPolicyDocument) + } + + sort.Sort(byJSONLength(ret)) + if len(ret) > 0 { + return ret[0] } if r.IAMPolicyDocumentArray != nil { diff --git a/cloudformation/resources/awsserverlessfunction_properties.go b/cloudformation/resources/awsserverlessfunction_properties.go index 2b90913cad..235ccb78de 100644 --- a/cloudformation/resources/awsserverlessfunction_properties.go +++ b/cloudformation/resources/awsserverlessfunction_properties.go @@ -1,9 +1,9 @@ package resources import ( - "encoding/json" + "sort" - "reflect" + "encoding/json" "github.com/mitchellh/mapstructure" ) @@ -24,84 +24,51 @@ type AWSServerlessFunction_Properties struct { func (r AWSServerlessFunction_Properties) value() interface{} { - if r.S3Event != nil && !reflect.DeepEqual(r.S3Event, &AWSServerlessFunction_S3Event{}) { - return r.S3Event - } - - if r.SNSEvent != nil && !reflect.DeepEqual(r.SNSEvent, &AWSServerlessFunction_SNSEvent{}) { - return r.SNSEvent - } - - if r.SQSEvent != nil && !reflect.DeepEqual(r.SQSEvent, &AWSServerlessFunction_SQSEvent{}) { - return r.SQSEvent - } - - if r.KinesisEvent != nil && !reflect.DeepEqual(r.KinesisEvent, &AWSServerlessFunction_KinesisEvent{}) { - return r.KinesisEvent - } - - if r.DynamoDBEvent != nil && !reflect.DeepEqual(r.DynamoDBEvent, &AWSServerlessFunction_DynamoDBEvent{}) { - return r.DynamoDBEvent - } - - if r.ApiEvent != nil && !reflect.DeepEqual(r.ApiEvent, &AWSServerlessFunction_ApiEvent{}) { - return r.ApiEvent - } - - if r.ScheduleEvent != nil && !reflect.DeepEqual(r.ScheduleEvent, &AWSServerlessFunction_ScheduleEvent{}) { - return r.ScheduleEvent - } - - if r.CloudWatchEventEvent != nil && !reflect.DeepEqual(r.CloudWatchEventEvent, &AWSServerlessFunction_CloudWatchEventEvent{}) { - return r.CloudWatchEventEvent - } - - if r.IoTRuleEvent != nil && !reflect.DeepEqual(r.IoTRuleEvent, &AWSServerlessFunction_IoTRuleEvent{}) { - return r.IoTRuleEvent - } - - if r.AlexaSkillEvent != nil && !reflect.DeepEqual(r.AlexaSkillEvent, &AWSServerlessFunction_AlexaSkillEvent{}) { - return r.AlexaSkillEvent - } + ret := []interface{}{} if r.S3Event != nil { - return r.S3Event + ret = append(ret, *r.S3Event) } if r.SNSEvent != nil { - return r.SNSEvent + ret = append(ret, *r.SNSEvent) } if r.SQSEvent != nil { - return r.SQSEvent + ret = append(ret, *r.SQSEvent) } if r.KinesisEvent != nil { - return r.KinesisEvent + ret = append(ret, *r.KinesisEvent) } if r.DynamoDBEvent != nil { - return r.DynamoDBEvent + ret = append(ret, *r.DynamoDBEvent) } if r.ApiEvent != nil { - return r.ApiEvent + ret = append(ret, *r.ApiEvent) } if r.ScheduleEvent != nil { - return r.ScheduleEvent + ret = append(ret, *r.ScheduleEvent) } if r.CloudWatchEventEvent != nil { - return r.CloudWatchEventEvent + ret = append(ret, *r.CloudWatchEventEvent) } if r.IoTRuleEvent != nil { - return r.IoTRuleEvent + ret = append(ret, *r.IoTRuleEvent) } if r.AlexaSkillEvent != nil { - return r.AlexaSkillEvent + ret = append(ret, *r.AlexaSkillEvent) + } + + sort.Sort(byJSONLength(ret)) + if len(ret) > 0 { + return ret[0] } return nil diff --git a/cloudformation/resources/utils.go b/cloudformation/resources/utils.go new file mode 100644 index 0000000000..87cdf4021a --- /dev/null +++ b/cloudformation/resources/utils.go @@ -0,0 +1,36 @@ +package resources + +import ( + "encoding/json" +) + +type byJSONLength []interface{} + +func (s byJSONLength) Len() int { + return len(s) +} + +func (s byJSONLength) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +func (s byJSONLength) Less(i, j int) bool { + // Nil is always at the end + if s[i] == nil { + return false + } + if s[j] == nil { + return true + } + jsoni, _ := json.Marshal(s[i]) + jsonj, _ := json.Marshal(s[j]) + + if jsoni == nil { + return false + } + if jsonj == nil { + return true + } + + return len(jsoni) > len(jsonj) +} diff --git a/generate/property_test.go b/generate/property_test.go index 677fe94330..2ff07ea9dd 100644 --- a/generate/property_test.go +++ b/generate/property_test.go @@ -50,6 +50,43 @@ var _ = Describe("Goformation Code Generator", func() { Context("with a polymorphic property", func() { + Context("with multiple types", func() { + + Context("properly marshals and unmarshals values", func() { + + property := []byte(`{"Properties":{"BatchSize":10,"StartingPosition":"LATEST","Stream":"arn"},"Type":"Kinesis"}`) + + result := &resources.AWSServerlessFunction_EventSource{} + err := json.Unmarshal(property, result) + output, err2 := json.Marshal(result) + + It("should marshal and unmarhal to same value", func() { + Expect(err).To(BeNil()) + Expect(err2).To(BeNil()) + Expect(output).To(Equal(property)) + }) + + }) + + Context("properly Marshals best value", func() { + expected := []byte(`{"BatchSize":10,"Stream":"arn"}`) + + result := &resources.AWSServerlessFunction_Properties{ + SQSEvent: &resources.AWSServerlessFunction_SQSEvent{BatchSize: 10}, + KinesisEvent: &resources.AWSServerlessFunction_KinesisEvent{BatchSize: 10, Stream: "arn"}, + } + + output, err := result.MarshalJSON() + + It("should marshal and unmarhal to same value", func() { + Expect(err).To(BeNil()) + Expect(output).To(Equal(expected)) + }) + + }) + + }) + Context("with a primitive value", func() { Context("specified as a Go struct", func() { diff --git a/generate/templates/polymorphic-property.template b/generate/templates/polymorphic-property.template index a4dafb367f..81b194898f 100644 --- a/generate/templates/polymorphic-property.template +++ b/generate/templates/polymorphic-property.template @@ -1,10 +1,10 @@ package resources import ( - "encoding/json" {{ if (.Property.Types) }} - "reflect" + "sort" {{end}} + "encoding/json" {{ if (or .Property.Types .Property.ItemTypes .Property.PrimitiveItemTypes)}} "github.com/mitchellh/mapstructure" {{end}} @@ -12,7 +12,6 @@ import ( // {{.Name}} is a helper struct that can hold either a {{.TypesJoined}} value type {{.Name}} struct { - {{range $type := $.Property.PrimitiveTypes}} {{$type}} *{{convertToGoType $type}}{{end}} @@ -40,15 +39,16 @@ func (r {{.Name}}) value() interface{} { } {{end}} - {{range $type := $.Property.Types}} - if r.{{$type}} != nil && !reflect.DeepEqual(r.{{$type}}, &{{$.Basename}}_{{$type}}{}) { - return r.{{$type}} - } - {{end}} - - {{range $type := $.Property.Types}} - if r.{{$type}} != nil { - return r.{{$type}} + {{ if (.Property.Types) }} + ret := []interface{}{} + {{range $type := $.Property.Types}} + if r.{{$type}} != nil { + ret = append(ret, *r.{{$type}}) + } + {{end}} + sort.Sort(byJSONLength(ret)) + if len(ret) > 0 { + return ret[0] } {{end}}