Skip to content

Commit

Permalink
Expand references in PatternProperties when the property has no type.
Browse files Browse the repository at this point in the history
  • Loading branch information
ewbankkit committed May 21, 2024
1 parent 67e7ca5 commit e6d3bb9
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 36 deletions.
70 changes: 38 additions & 32 deletions resource_expand.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,10 @@ func (r *Resource) Expand() error {
func (r *Resource) ResolveProperties(properties map[string]*Property) error {
for propertyName, property := range properties {
// For example:
//
// "Configuration": {
// "$ref": "#/definitions/ClusterConfiguration"
// },
// }
resolved, err := r.ResolveProperty(property)

if err != nil {
Expand All @@ -53,12 +54,13 @@ func (r *Resource) ResolveProperties(properties map[string]*Property) error {
switch property.Type.String() {
case PropertyTypeArray:
// For example:
//
// "DefaultCapacityProviderStrategy": {
// "type": "array",
// "items": {
// "$ref": "#/definitions/CapacityProviderStrategyItem"
// }
// },
// }
resolved, err = r.ResolveProperty(property.Items)

if err != nil {
Expand All @@ -71,6 +73,7 @@ func (r *Resource) ResolveProperties(properties map[string]*Property) error {

if property.Items.Type.String() == PropertyTypeObject {
// For example:
//
// "Tags": {
// "type": "array",
// "items": {
Expand All @@ -88,29 +91,45 @@ func (r *Resource) ResolveProperties(properties map[string]*Property) error {
}
}

case PropertyTypeObject:
case PropertyTypeObject, "":
err = r.UnwrapOneOfProperties(property)

if err != nil {
return fmt.Errorf("unwrapping %s OneOf Properties: %w", propertyName, err)
}

// For example:
//
// "ClusterConfiguration": {
// "type": "object",
// "properties": {
// "ExecuteCommandConfiguration": {
// "$ref": "#/definitions/ExecuteCommandConfiguration"
// }
// }
// },
// }
//
// or
//
// "PresignedUrlConfig": {
// "properties": {
// "RoleArn": {
// "$ref": "#/definitions/RoleArn"
// },
// "ExpiresInSec": {
// "$ref": "#/definitions/ExpiresInSec"
// }
// }
// }

err = r.ResolveProperties(property.Properties)

if err != nil {
return fmt.Errorf("resolving %s Properties: %w", propertyName, err)
}

// For example:
//
// "LambdaFunctionRecipeSource": {
// "type": "object",
// "properties": {
Expand All @@ -123,38 +142,24 @@ func (r *Resource) ResolveProperties(properties map[string]*Property) error {
// }
// }
// }
// },
// }
//
// or
//
// "RouteParameters": {
// "patternProperties": {
// "": {
// "$ref": "#/definitions/ParameterConstraints"
// }
// },
// "additionalProperties": false
// }

err = r.ResolveProperties(property.PatternProperties)

if err != nil {
return fmt.Errorf("resolving %s PatternProperties: %w", propertyName, err)
}

case "":
err = r.UnwrapOneOfProperties(property)

if err != nil {
return fmt.Errorf("unwrapping %s OneOf Properties: %w", propertyName, err)
}

if len(property.Properties) > 0 {
// For example:
// "PresignedUrlConfig": {
// "properties": {
// "RoleArn": {
// "$ref": "#/definitions/RoleArn"
// },
// "ExpiresInSec": {
// "$ref": "#/definitions/ExpiresInSec"
// }
// }
// },
err = r.ResolveProperties(property.Properties)

if err != nil {
return fmt.Errorf("resolving %s Properties: %w", propertyName, err)
}
}
}
}

Expand Down Expand Up @@ -202,6 +207,7 @@ func (r *Resource) ResolveProperty(property *Property) (bool, error) {
func (r *Resource) UnwrapOneOfProperties(property *Property) error {
if len(property.Properties) == 0 && len(property.PatternProperties) == 0 && len(property.OneOf) > 0 {
// For example:
//
// "ContentTransformation": {
// "type": "object",
// "oneOf": [
Expand All @@ -217,7 +223,7 @@ func (r *Resource) UnwrapOneOfProperties(property *Property) error {
// ]
// }
// ]
// },
// }
unwrappedProperties := make(map[string]*Property)

for _, propertySubschema := range property.OneOf {
Expand Down
16 changes: 12 additions & 4 deletions resource_expand_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,13 @@ func TestResourceExpand_PatternProperties(t *testing.T) {
PropertyPath: []string{"LambdaFunction", "ComponentDependencies", "VersionRequirement"},
ExpectedPropertyType: cfschema.PropertyTypeString,
},
{
TestDescription: "valid no type specified",
MetaSchemaPath: "provider.definition.schema.v1.json",
ResourceSchemaPath: "AWS_ApiGatewayV2_RouteResponse.json",
PropertyPath: []string{"ResponseParameters", "Required"},
ExpectedPropertyType: cfschema.PropertyTypeBoolean,
},
}

for _, testCase := range testCases {
Expand Down Expand Up @@ -210,16 +217,17 @@ func TestResourceExpand_PatternProperties(t *testing.T) {
}
}

if property.Type == nil {
t.Fatalf("unexpected resource property (%s) type, got none", propertyName)
propertyType := cfschema.Type(cfschema.PropertyTypeObject)
if property.Type != nil {
propertyType = *property.Type
}

if i == len(testCase.PropertyPath)-1 {
if actual, expected := *property.Type, testCase.ExpectedPropertyType; actual != expected {
if actual, expected := propertyType, testCase.ExpectedPropertyType; actual != expected {
t.Errorf("expected resource property (%s) type (%s), got: %s", propertyName, expected, actual)
}
} else {
if actual, expected := *property.Type, cfschema.Type(cfschema.PropertyTypeObject); actual != expected {
if actual, expected := propertyType, cfschema.Type(cfschema.PropertyTypeObject); actual != expected {
t.Fatalf("expected resource property (%s) type (%s), got: %s", propertyName, expected, actual)
}

Expand Down
112 changes: 112 additions & 0 deletions testdata/AWS_ApiGatewayV2_RouteResponse.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
{
"typeName": "AWS::ApiGatewayV2::RouteResponse",
"description": "The ``AWS::ApiGatewayV2::RouteResponse`` resource creates a route response for a WebSocket API. For more information, see [Set up Route Responses for a WebSocket API in API Gateway](https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api-route-response.html) in the *API Gateway Developer Guide*.",
"additionalProperties": false,
"properties": {
"RouteResponseKey": {
"type": "string",
"description": "The route response key."
},
"ResponseParameters": {
"$ref": "#/definitions/RouteParameters",
"description": "The route response parameters."
},
"RouteId": {
"type": "string",
"description": "The route ID."
},
"ModelSelectionExpression": {
"type": "string",
"description": "The model selection expression for the route response. Supported only for WebSocket APIs."
},
"ApiId": {
"type": "string",
"description": "The API identifier."
},
"ResponseModels": {
"type": "object",
"description": "The response models for the route response."
},
"RouteResponseId": {
"type": "string",
"description": ""
}
},
"definitions": {
"ParameterConstraints": {
"type": "object",
"properties": {
"Required": {
"type": "boolean",
"description": "Specifies whether the parameter is required."
}
},
"required": [
"Required"
],
"additionalProperties": false,
"description": "Specifies whether the parameter is required."
},
"RouteParameters": {
"patternProperties": {
"": {
"$ref": "#/definitions/ParameterConstraints"
}
},
"additionalProperties": false
}
},
"required": [
"RouteResponseKey",
"RouteId",
"ApiId"
],
"createOnlyProperties": [
"/properties/ApiId",
"/properties/RouteId"
],
"readOnlyProperties": [
"/properties/RouteResponseId"
],
"primaryIdentifier": [
"/properties/ApiId",
"/properties/RouteId",
"/properties/RouteResponseId"
],
"tagging": {
"taggable": false,
"tagOnCreate": false,
"tagUpdatable": false,
"cloudFormationSystemTags": false
},
"handlers": {
"create": {
"permissions": [
"apigateway:POST"
]
},
"update": {
"permissions": [
"apigateway:PATCH",
"apigateway:GET",
"apigateway:PUT"
]
},
"read": {
"permissions": [
"apigateway:GET"
]
},
"delete": {
"permissions": [
"apigateway:GET",
"apigateway:DELETE"
]
},
"list": {
"permissions": [
"apigateway:GET"
]
}
}
}

0 comments on commit e6d3bb9

Please sign in to comment.