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

fix: Allow Qualifier to be defined with Id in Serverless Connectors #2768

Merged
merged 4 commits into from
Jan 6, 2023
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
12 changes: 7 additions & 5 deletions samtranslator/model/connector/connector.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,20 +80,22 @@ def get_event_source_mappings(event_source_id: str, function_id: str, resource_r

def _is_valid_resource_reference(obj: Dict[str, Any]) -> bool:
id_provided = "Id" in obj
non_id_provided = len([k for k in obj.keys() if k != "Id"]) > 0
# Must provide either Id, or a combination of other properties, but not both
non_id_provided = len([k for k in obj.keys() if k not in ["Id", "Qualifier"]]) > 0
# Must provide Id (with optional Qualifier) or a supported combination of other properties.
return id_provided != non_id_provided


def get_resource_reference(
obj: Dict[str, Any], resource_resolver: ResourceResolver, connecting_obj: Dict[str, Any]
) -> ConnectorResourceReference:
if not _is_valid_resource_reference(obj):
raise ConnectorResourceError("Must provide either 'Id' or a combination of the other properties, not both.")
raise ConnectorResourceError(
"Must provide 'Id' (with optional 'Qualifier') or a supported combination of other properties."
)

logical_id = obj.get("Id")

# Must either provide Id or a combination of the other properties (not both).
# Must provide Id (with optional Qualifier) or a supported combination of other properties
# If Id is not provided, all values must come from overrides.
if not logical_id:
resource_type = obj.get("Type")
Expand Down Expand Up @@ -136,7 +138,7 @@ def get_resource_reference(

name = _get_resource_name(logical_id, resource_type)

qualifier = _get_resource_qualifier(resource_type)
qualifier = obj.get("Qualifier") if "Qualifier" in obj else _get_resource_qualifier(resource_type)

return ConnectorResourceReference(
logical_id=logical_id,
Expand Down
22 changes: 22 additions & 0 deletions tests/translator/input/connector_hardcoded_props.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,28 @@ Resources:
Permissions:
- Write

ApiV1ToLambdaWithId:
Type: AWS::Serverless::Connector
Properties:
Source:
Id: MyApiV1
Qualifier: Prod/GET/foobar
Destination:
Id: MyFunction
Permissions:
- Write

ApiV2ToLambdaWithId:
Type: AWS::Serverless::Connector
Properties:
Source:
Id: MyApiV2
Qualifier: '*'
Destination:
Id: MyFunction
Permissions:
- Write

SfnToSfn:
Type: AWS::Serverless::Connector
Properties:
Expand Down
72 changes: 72 additions & 0 deletions tests/translator/output/aws-cn/connector_hardcoded_props.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,41 @@
{
"Resources": {
"ApiV1ToLambdaWithIdWriteLambdaPermission": {
"Metadata": {
"aws:sam:connectors": {
"ApiV1ToLambdaWithId": {
"Destination": {
"Type": "AWS::Lambda::Function"
},
"Source": {
"Type": "AWS::ApiGateway::RestApi"
}
}
}
},
"Properties": {
"Action": "lambda:InvokeFunction",
"FunctionName": {
"Fn::GetAtt": [
"MyFunction",
"Arn"
]
},
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Sub": [
"arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${SourceResourceId}/${SourceQualifier}",
{
"SourceQualifier": "Prod/GET/foobar",
"SourceResourceId": {
"Ref": "MyApiV1"
}
}
]
}
},
"Type": "AWS::Lambda::Permission"
},
"ApiV1ToLambdaWriteLambdaPermission": {
"Metadata": {
"aws:sam:connectors": {
Expand Down Expand Up @@ -36,6 +72,42 @@
},
"Type": "AWS::Lambda::Permission"
},
"ApiV2ToLambdaWithIdWriteLambdaPermission": {
"Metadata": {
"aws:sam:connectors": {
"ApiV2ToLambdaWithId": {
"Destination": {
"Type": "AWS::Lambda::Function"
},
"Source": {
"Type": "AWS::ApiGatewayV2::Api"
}
}
}
},
"Properties": {
"Action": "lambda:InvokeFunction",
"FunctionName": {
"Fn::GetAtt": [
"MyFunction",
"Arn"
]
},
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Sub": [
"arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${SourceResourceId}/${SourceQualifier}",
{
"SourceQualifier": "*",
"SourceResourceId": {
"Ref": "MyApiV2"
}
}
]
}
},
"Type": "AWS::Lambda::Permission"
},
"ApiV2ToLambdaWriteLambdaPermission": {
"Metadata": {
"aws:sam:connectors": {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,41 @@
{
"Resources": {
"ApiV1ToLambdaWithIdWriteLambdaPermission": {
"Metadata": {
"aws:sam:connectors": {
"ApiV1ToLambdaWithId": {
"Destination": {
"Type": "AWS::Lambda::Function"
},
"Source": {
"Type": "AWS::ApiGateway::RestApi"
}
}
}
},
"Properties": {
"Action": "lambda:InvokeFunction",
"FunctionName": {
"Fn::GetAtt": [
"MyFunction",
"Arn"
]
},
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Sub": [
"arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${SourceResourceId}/${SourceQualifier}",
{
"SourceQualifier": "Prod/GET/foobar",
"SourceResourceId": {
"Ref": "MyApiV1"
}
}
]
}
},
"Type": "AWS::Lambda::Permission"
},
"ApiV1ToLambdaWriteLambdaPermission": {
"Metadata": {
"aws:sam:connectors": {
Expand Down Expand Up @@ -36,6 +72,42 @@
},
"Type": "AWS::Lambda::Permission"
},
"ApiV2ToLambdaWithIdWriteLambdaPermission": {
"Metadata": {
"aws:sam:connectors": {
"ApiV2ToLambdaWithId": {
"Destination": {
"Type": "AWS::Lambda::Function"
},
"Source": {
"Type": "AWS::ApiGatewayV2::Api"
}
}
}
},
"Properties": {
"Action": "lambda:InvokeFunction",
"FunctionName": {
"Fn::GetAtt": [
"MyFunction",
"Arn"
]
},
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Sub": [
"arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${SourceResourceId}/${SourceQualifier}",
{
"SourceQualifier": "*",
"SourceResourceId": {
"Ref": "MyApiV2"
}
}
]
}
},
"Type": "AWS::Lambda::Permission"
},
"ApiV2ToLambdaWriteLambdaPermission": {
"Metadata": {
"aws:sam:connectors": {
Expand Down
72 changes: 72 additions & 0 deletions tests/translator/output/connector_hardcoded_props.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,41 @@
{
"Resources": {
"ApiV1ToLambdaWithIdWriteLambdaPermission": {
"Metadata": {
"aws:sam:connectors": {
"ApiV1ToLambdaWithId": {
"Destination": {
"Type": "AWS::Lambda::Function"
},
"Source": {
"Type": "AWS::ApiGateway::RestApi"
}
}
}
},
"Properties": {
"Action": "lambda:InvokeFunction",
"FunctionName": {
"Fn::GetAtt": [
"MyFunction",
"Arn"
]
},
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Sub": [
"arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${SourceResourceId}/${SourceQualifier}",
{
"SourceQualifier": "Prod/GET/foobar",
"SourceResourceId": {
"Ref": "MyApiV1"
}
}
]
}
},
"Type": "AWS::Lambda::Permission"
},
"ApiV1ToLambdaWriteLambdaPermission": {
"Metadata": {
"aws:sam:connectors": {
Expand Down Expand Up @@ -36,6 +72,42 @@
},
"Type": "AWS::Lambda::Permission"
},
"ApiV2ToLambdaWithIdWriteLambdaPermission": {
"Metadata": {
"aws:sam:connectors": {
"ApiV2ToLambdaWithId": {
"Destination": {
"Type": "AWS::Lambda::Function"
},
"Source": {
"Type": "AWS::ApiGatewayV2::Api"
}
}
}
},
"Properties": {
"Action": "lambda:InvokeFunction",
"FunctionName": {
"Fn::GetAtt": [
"MyFunction",
"Arn"
]
},
"Principal": "apigateway.amazonaws.com",
"SourceArn": {
"Fn::Sub": [
"arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:${SourceResourceId}/${SourceQualifier}",
{
"SourceQualifier": "*",
"SourceResourceId": {
"Ref": "MyApiV2"
}
}
]
}
},
"Type": "AWS::Lambda::Permission"
},
"ApiV2ToLambdaWriteLambdaPermission": {
"Metadata": {
"aws:sam:connectors": {
Expand Down
4 changes: 2 additions & 2 deletions tests/translator/output/error_connector.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 16. Resource with id [BothIdAndOtherProps] is invalid. Must provide either 'Id' or a combination of the other properties, not both. Resource with id [EmptyListPermissionConnector] is invalid. 'Permissions' cannot be empty; valid values are: Read, Write. Resource with id [EmptyPermissionConnector] is invalid. Missing required property 'Permissions'. Resource with id [MissingLambdaFunctionArn] is invalid. Source.Arn is missing. Resource with id [MissingRole] is invalid. Unable to get IAM role name from 'Source' resource. Resource with id [MissingRoleDestination] is invalid. Unable to get IAM role name from 'Destination' resource. Resource with id [MissingSnsTopicArn] is invalid. Destination.Arn is missing. Resource with id [MissingSqsQueueUrl] is invalid. Destination.Arn is missing. Resource with id [NoIdMissingType] is invalid. 'Type' is missing or not a string. Resource with id [NoPermissionConnector] is invalid. Missing required property 'Permissions'. Resource with id [NonExistentLogicalId] is invalid. Unable to find resource with logical ID 'ThisDoesntExist'. Resource with id [NonStrId] is invalid. 'Id' is missing or not a string. Resource with id [ResourceWithoutType] is invalid. 'Type' is missing or not a string. Resource with id [UnsupportedAccessCategory] is invalid. Unsupported 'Permissions' provided; valid values are: Read, Write. Resource with id [UnsupportedAccessCategoryCombination] is invalid. Unsupported 'Permissions' provided; valid combinations are: Read + Write. Resource with id [UnsupportedType] is invalid. Unable to create connector from AWS::Fancy::CoolType to AWS::Lambda::Function; it's not supported or the template is invalid.",
"errorMessage": "Invalid Serverless Application Specification document. Number of errors found: 16. Resource with id [BothIdAndOtherProps] is invalid. Must provide 'Id' (with optional 'Qualifier') or a supported combination of other properties. Resource with id [EmptyListPermissionConnector] is invalid. 'Permissions' cannot be empty; valid values are: Read, Write. Resource with id [EmptyPermissionConnector] is invalid. Missing required property 'Permissions'. Resource with id [MissingLambdaFunctionArn] is invalid. Source.Arn is missing. Resource with id [MissingRole] is invalid. Unable to get IAM role name from 'Source' resource. Resource with id [MissingRoleDestination] is invalid. Unable to get IAM role name from 'Destination' resource. Resource with id [MissingSnsTopicArn] is invalid. Destination.Arn is missing. Resource with id [MissingSqsQueueUrl] is invalid. Destination.Arn is missing. Resource with id [NoIdMissingType] is invalid. 'Type' is missing or not a string. Resource with id [NoPermissionConnector] is invalid. Missing required property 'Permissions'. Resource with id [NonExistentLogicalId] is invalid. Unable to find resource with logical ID 'ThisDoesntExist'. Resource with id [NonStrId] is invalid. 'Id' is missing or not a string. Resource with id [ResourceWithoutType] is invalid. 'Type' is missing or not a string. Resource with id [UnsupportedAccessCategory] is invalid. Unsupported 'Permissions' provided; valid values are: Read, Write. Resource with id [UnsupportedAccessCategoryCombination] is invalid. Unsupported 'Permissions' provided; valid combinations are: Read + Write. Resource with id [UnsupportedType] is invalid. Unable to create connector from AWS::Fancy::CoolType to AWS::Lambda::Function; it's not supported or the template is invalid.",
"errors": [
{
"errorMessage": "Resource with id [NoIdMissingType] is invalid. 'Type' is missing or not a string."
Expand Down Expand Up @@ -38,7 +38,7 @@
"errorMessage": "Resource with id [MissingSnsTopicArn] is invalid. Destination.Arn is missing."
},
{
"errorMessage": "Resource with id [BothIdAndOtherProps] is invalid. Must provide either 'Id' or a combination of the other properties, not both."
"errorMessage": "Resource with id [BothIdAndOtherProps] is invalid. Must provide 'Id' (with optional 'Qualifier') or a supported combination of other properties."
},
{
"errorMessage": "Resource with id [NoPermissionConnector] is invalid. Missing required property 'Permissions'"
Expand Down