Skip to content

Commit

Permalink
fix(schema): AWS::Serverless:Api.Cors (awslabs#246)
Browse files Browse the repository at this point in the history
AWS::Serverless::Api.Cors should be either a string, or a [cors configuration object](https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#cors-configuration).

Fixes awslabs#244
  • Loading branch information
PaulMaddox authored Nov 30, 2019
1 parent 665e4b4 commit 62fd56a
Show file tree
Hide file tree
Showing 8 changed files with 297 additions and 8 deletions.
64 changes: 64 additions & 0 deletions cloudformation/serverless/api_cors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package serverless

import (
"encoding/json"
"sort"

"github.com/awslabs/goformation/v4/cloudformation/utils"
)

// Api_Cors is a helper struct that can hold either a String or CorsConfiguration value
type Api_Cors struct {
String *string

CorsConfiguration *Api_CorsConfiguration
}

func (r Api_Cors) value() interface{} {
ret := []interface{}{}

if r.String != nil {
ret = append(ret, r.String)
}

if r.CorsConfiguration != nil {
ret = append(ret, *r.CorsConfiguration)
}

sort.Sort(utils.ByJSONLength(ret)) // Heuristic to select best attribute
if len(ret) > 0 {
return ret[0]
}

return nil
}

func (r Api_Cors) MarshalJSON() ([]byte, error) {
return json.Marshal(r.value())
}

// Hook into the marshaller
func (r *Api_Cors) UnmarshalJSON(b []byte) error {

// Unmarshal into interface{} to check it's type
var typecheck interface{}
if err := json.Unmarshal(b, &typecheck); err != nil {
return err
}

switch val := typecheck.(type) {

case string:
r.String = &val

case map[string]interface{}:
val = val // This ensures val is used to stop an error

json.Unmarshal(b, &r.CorsConfiguration)

case []interface{}:

}

return nil
}
2 changes: 1 addition & 1 deletion cloudformation/serverless/aws-serverless-api.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type Api struct {
// Cors AWS CloudFormation Property
// Required: false
// See: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessapi
Cors string `json:"Cors,omitempty"`
Cors *Api_Cors `json:"Cors,omitempty"`

// DefinitionBody AWS CloudFormation Property
// Required: false
Expand Down
49 changes: 49 additions & 0 deletions cloudformation/serverless/aws-serverless-api_corsconfiguration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package serverless

import (
"github.com/awslabs/goformation/v4/cloudformation/policies"
)

// Api_CorsConfiguration AWS CloudFormation Resource (AWS::Serverless::Api.CorsConfiguration)
// See: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#cors-configuration
type Api_CorsConfiguration struct {

// AllowCredentials AWS CloudFormation Property
// Required: false
// See: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#cors-configuration
AllowCredentials bool `json:"AllowCredentials,omitempty"`

// AllowHeaders AWS CloudFormation Property
// Required: false
// See: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#cors-configuration
AllowHeaders string `json:"AllowHeaders,omitempty"`

// AllowMethods AWS CloudFormation Property
// Required: false
// See: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#cors-configuration
AllowMethods string `json:"AllowMethods,omitempty"`

// AllowOrigin AWS CloudFormation Property
// Required: true
// See: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#cors-configuration
AllowOrigin string `json:"AllowOrigin,omitempty"`

// MaxAge AWS CloudFormation Property
// Required: false
// See: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#cors-configuration
MaxAge string `json:"MaxAge,omitempty"`

// AWSCloudFormationDeletionPolicy represents a CloudFormation DeletionPolicy
AWSCloudFormationDeletionPolicy policies.DeletionPolicy `json:"-"`

// AWSCloudFormationDependsOn stores the logical ID of the resources to be created before this resource
AWSCloudFormationDependsOn []string `json:"-"`

// AWSCloudFormationMetadata stores structured data associated with this resource
AWSCloudFormationMetadata map[string]interface{} `json:"-"`
}

// AWSCloudFormationType returns the AWS CloudFormation resource type
func (r *Api_CorsConfiguration) AWSCloudFormationType() string {
return "AWS::Serverless::Api.CorsConfiguration"
}
53 changes: 48 additions & 5 deletions generate/sam-2016-10-31.json
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,12 @@
"Cors": {
"Documentation": "https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessapi",
"Required": false,
"PrimitiveType": "String",
"PrimitiveTypes": [
"String"
],
"Types": [
"CorsConfiguration"
],
"UpdateType": "Immutable"
},
"Auth": {
Expand Down Expand Up @@ -256,8 +261,12 @@
"Location": {
"Documentation": "https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessapplication",
"Required": true,
"PrimitiveTypes": [ "String" ],
"Types": [ "ApplicationLocation" ],
"PrimitiveTypes": [
"String"
],
"Types": [
"ApplicationLocation"
],
"UpdateType": "Immutable"
},
"Parameters": {
Expand Down Expand Up @@ -909,7 +918,6 @@
}
}
},

"AWS::Serverless::Function.DomainSAMPT": {
"Documentation": "https://github.com/awslabs/serverless-application-model/blob/master/docs/policy_templates.rst",
"Properties": {
Expand Down Expand Up @@ -1121,6 +1129,41 @@
}
}
},
"AWS::Serverless::Api.CorsConfiguration": {
"Documentation": "https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#cors-configuration",
"Properties": {
"AllowMethods": {
"Documentation": "https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#cors-configuration",
"Required": false,
"PrimitiveType": "String",
"UpdateType": "Immutable"
},
"AllowHeaders": {
"Documentation": "https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#cors-configuration",
"Required": false,
"PrimitiveType": "String",
"UpdateType": "Immutable"
},
"AllowOrigin": {
"Documentation": "https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#cors-configuration",
"Required": true,
"PrimitiveType": "String",
"UpdateType": "Immutable"
},
"MaxAge": {
"Documentation": "https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#cors-configuration",
"Required": false,
"PrimitiveType": "String",
"UpdateType": "Immutable"
},
"AllowCredentials": {
"Documentation": "https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#cors-configuration",
"Required": false,
"PrimitiveType": "Boolean",
"UpdateType": "Immutable"
}
}
},
"AWS::Serverless::SimpleTable.PrimaryKey": {
"Documentation": "https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#primary-key-object",
"Properties": {
Expand Down Expand Up @@ -1167,4 +1210,4 @@
}
}
}
}
}
29 changes: 29 additions & 0 deletions goformation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,35 @@ var _ = Describe("Goformation", func() {

})

Context("with a Serverless template containing different CORS configuration formats", func() {

template, err := goformation.Open("test/yaml/aws-serverless-api-string-or-cors-configuration.yaml")
It("should successfully parse the template", func() {
Expect(err).To(BeNil())
Expect(template).ShouldNot(BeNil())
})

apis := template.GetAllServerlessApiResources()

It("should have exactly two APIs", func() {
Expect(apis).To(HaveLen(2))
Expect(apis).To(HaveKey("RestApiWithCorsConfiguration"))
Expect(apis).To(HaveKey("RestApiWithCorsString"))
})

api1 := apis["RestApiWithCorsConfiguration"]
It("should parse a Cors configuration object", func() {
Expect(api1.Cors.CorsConfiguration.AllowHeaders).To(Equal("'Authorization,authorization'"))
Expect(api1.Cors.CorsConfiguration.AllowOrigin).To(Equal("'*'"))
})

api2 := apis["RestApiWithCorsString"]
It("should parse a Cors string", func() {
Expect(api2.Cors.String).To(PointTo(Equal("'www.example.com'")))
})

})

Context("with a Serverless template containing different CodeUri formats", func() {

template, err := goformation.Open("test/yaml/aws-serverless-function-string-or-s3-location.yaml")
Expand Down
35 changes: 34 additions & 1 deletion schema/sam.go
Original file line number Diff line number Diff line change
Expand Up @@ -52099,7 +52099,16 @@ var SamSchema = `{
"type": "string"
},
"Cors": {
"type": "string"
"anyOf": [
{
"type": [
"string"
]
},
{
"$ref": "#/definitions/AWS::Serverless::Api.CorsConfiguration"
}
]
},
"DefinitionBody": {
"type": "object"
Expand Down Expand Up @@ -52186,6 +52195,30 @@ var SamSchema = `{
},
"type": "object"
},
"AWS::Serverless::Api.CorsConfiguration": {
"additionalProperties": false,
"properties": {
"AllowCredentials": {
"type": "boolean"
},
"AllowHeaders": {
"type": "string"
},
"AllowMethods": {
"type": "string"
},
"AllowOrigin": {
"type": "string"
},
"MaxAge": {
"type": "string"
}
},
"required": [
"AllowOrigin"
],
"type": "object"
},
"AWS::Serverless::Api.S3Location": {
"additionalProperties": false,
"properties": {
Expand Down
35 changes: 34 additions & 1 deletion schema/sam.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -52096,7 +52096,16 @@
"type": "string"
},
"Cors": {
"type": "string"
"anyOf": [
{
"type": [
"string"
]
},
{
"$ref": "#/definitions/AWS::Serverless::Api.CorsConfiguration"
}
]
},
"DefinitionBody": {
"type": "object"
Expand Down Expand Up @@ -52183,6 +52192,30 @@
},
"type": "object"
},
"AWS::Serverless::Api.CorsConfiguration": {
"additionalProperties": false,
"properties": {
"AllowCredentials": {
"type": "boolean"
},
"AllowHeaders": {
"type": "string"
},
"AllowMethods": {
"type": "string"
},
"AllowOrigin": {
"type": "string"
},
"MaxAge": {
"type": "string"
}
},
"required": [
"AllowOrigin"
],
"type": "object"
},
"AWS::Serverless::Api.S3Location": {
"additionalProperties": false,
"properties": {
Expand Down
38 changes: 38 additions & 0 deletions test/yaml/aws-serverless-api-string-or-cors-configuration.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: SAM template for testing AWS::Serverless::Api.Cors with both string and Cors Configuration object
Resources:

RestApiWithCorsConfiguration:
Type: AWS::Serverless::Api
Properties:
Name: !Sub "${AWS::StackName}-rest-api"
StageName: Prod
TracingEnabled: false
EndpointConfiguration: EDGE
MethodSettings:
- DataTraceEnabled: false
HttpMethod: "*"
LoggingLevel: ERROR
MetricsEnabled: true
ResourcePath: /*
Cors:
AllowHeaders: "'Authorization,authorization'"
AllowOrigin: "'*'"

RestApiWithCorsString:
Type: AWS::Serverless::Api
Properties:
Name: !Sub "${AWS::StackName}-rest-api"
StageName: Prod
TracingEnabled: false
EndpointConfiguration: EDGE
MethodSettings:
- DataTraceEnabled: false
HttpMethod: "*"
LoggingLevel: ERROR
MetricsEnabled: true
ResourcePath: /*
Cors: "'www.example.com'"


0 comments on commit 62fd56a

Please sign in to comment.