Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expand references in PatternProperties when the property has no type #60

Merged
merged 1 commit into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"
]
}
}
}