Skip to content

Commit

Permalink
Merge pull request #3346 from aws/release-v1.75.0
Browse files Browse the repository at this point in the history
Release 1.75.0 (to main)
  • Loading branch information
ssenchenko committed Sep 19, 2023
2 parents 43f5744 + f3b0300 commit c928dc4
Show file tree
Hide file tree
Showing 102 changed files with 15,917 additions and 2,097 deletions.
1 change: 1 addition & 0 deletions .cfnlintrc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ ignore_templates:
- tests/translator/output/**/function_with_mq_using_autogen_role.json # Property "EventSourceArn" can Fn::GetAtt to a resource of types [AWS::DynamoDB::GlobalTable, AWS::DynamoDB::Table, AWS::Kinesis::Stream, AWS::Kinesis::StreamConsumer, AWS::SQS::Queue]
- tests/translator/output/**/function_with_tracing.json # Obsolete DependsOn on resource
- tests/translator/output/**/api_with_propagate_tags.json # TODO: Intentional error transform tests. Will be updated.
- tests/translator/output/**/function_with_intrinsics_resource_attribute.json # CFN now supports intrinsics in DeletionPolicy
ignore_checks:
- E2531 # Deprecated runtime; not relevant for transform tests
- W2531 # EOL runtime; not relevant for transform tests
Expand Down
4 changes: 2 additions & 2 deletions bin/_file_formatter.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ def decode_exception() -> Type[Exception]:
def file_extension() -> str:
"""Return file extension of files to format."""

@classmethod
def config_additional_args(cls) -> None: # noqa: empty-method-without-abstract-decorator
@classmethod # noqa: B027
def config_additional_args(cls) -> None:
"""Optionally configure additional args to arg parser."""

def process_file(self, file_path: Path) -> None:
Expand Down
38 changes: 35 additions & 3 deletions integration/combination/test_connectors.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
from time import sleep
from unittest import SkipTest
from unittest.case import skipIf

from parameterized import parameterized
from tenacity import retry, retry_if_exception, stop_after_attempt

from integration.config.service_names import SCHEDULE_EVENT
from integration.conftest import clean_bucket
from integration.helpers.base_test import S3_BUCKET_PREFIX, BaseTest
from integration.helpers.resource import generate_suffix
from integration.helpers.base_test import S3_BUCKET_PREFIX, BaseTest, nonblocking
from integration.helpers.resource import current_region_does_not_support, generate_suffix

retry_once = retry(
stop=stop_after_attempt(2),
Expand All @@ -15,6 +17,37 @@
)


# Explicitly move EB tests out to handlle the failed test in some regions.
# In those regions, the tests should have been skipped but somehow not.
# Test using `skipIf` to see if it helps.
@skipIf(
current_region_does_not_support([SCHEDULE_EVENT]),
"SCHEDULE_EVENT is not supported in this testing region",
)
@nonblocking
class TestConnectorsWithEventBus(BaseTest):
@parameterized.expand(
[
("combination/connector_function_to_eventbus_write",),
]
)
@retry_once
def test_connector_by_invoking_a_function_with_eventbus(self, template_file_path):
self.create_and_verify_stack(template_file_path)

lambda_function_name = self.get_physical_id_by_logical_id("TriggerFunction")
lambda_client = self.client_provider.lambda_client

request_params = {
"FunctionName": lambda_function_name,
"InvocationType": "RequestResponse",
"Payload": "{}",
}
response = lambda_client.invoke(**request_params)
self.assertEqual(response.get("StatusCode"), 200)
self.assertEqual(response.get("FunctionError"), None)


class TestConnectors(BaseTest):
def tearDown(self):
# Some tests will create items in S3 Bucket, which result in stack DELETE_FAILED state
Expand Down Expand Up @@ -42,7 +75,6 @@ def tearDown(self):
("combination/connector_function_to_queue_write",),
("combination/connector_function_to_queue_read",),
("combination/connector_function_to_topic_write",),
("combination/connector_function_to_eventbus_write",),
("combination/connector_topic_to_queue_write",),
("combination/connector_event_rule_to_sqs_write",),
("combination/connector_event_rule_to_sns_write",),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from unittest.case import skipIf

from integration.config.service_names import REST_API
from integration.helpers.base_test import BaseTest
from integration.helpers.resource import current_region_does_not_support


@skipIf(current_region_does_not_support([REST_API]), "REST API is not supported in this testing region")
class TestFunctionWithImplicitApiWithTimeout(BaseTest):
def test_function_with_implicit_api_with_timeout(self):
self.create_and_verify_stack("combination/function_with_implicit_api_with_timeout")

# verify that TimeoutInMillis is set to expected value in the integration
expected_timeout = 5000
apigw_client = self.client_provider.api_client
rest_api_id = self.get_physical_id_by_type("AWS::ApiGateway::RestApi")
resources = apigw_client.get_resources(restApiId=rest_api_id)["items"]

resource = get_resource_by_path(resources, "/hello")
method = apigw_client.get_method(restApiId=rest_api_id, resourceId=resource["id"], httpMethod="GET")
method_integration = method["methodIntegration"]
self.assertEqual(method_integration["timeoutInMillis"], expected_timeout)


def get_resource_by_path(resources, path):
for resource in resources:
if resource["path"] == path:
return resource
return None
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from integration.helpers.base_test import BaseTest


class TestFunctionWithIntrinsicsResourceAttributes(BaseTest):
def test_function_with_intrinsics_resource_attributes(self):
# simply verify the stack is deployed successfully is enough
self.create_and_verify_stack("combination/function_with_intrinsics_resource_attribute")

stack_outputs = self.get_stack_outputs()
id_dev_stack = stack_outputs["IsDevStack"]
self.assertEqual(id_dev_stack, "true")
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,6 @@ def test_function_with_schedule(self):


def get_first_key_value_pair_in_dict(dictionary):
key = list(dictionary.keys())[0]
key = next(iter(dictionary.keys()))
value = dictionary[key]
return key, value
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,6 @@ def test_state_machine_with_cwe(self):


def get_first_key_value_pair_in_dict(dictionary):
key = list(dictionary.keys())[0]
key = next(iter(dictionary.keys()))
value = dictionary[key]
return key, value
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,6 @@ def test_state_machine_with_schedule(self):


def get_first_key_value_pair_in_dict(dictionary):
key = list(dictionary.keys())[0]
key = next(iter(dictionary.keys()))
value = dictionary[key]
return key, value
12 changes: 3 additions & 9 deletions integration/helpers/base_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -536,9 +536,7 @@ def verify_get_request_response(self, url, expected_status_code, headers=None):
response = self.do_get_request_with_logging(url, headers)
if response.status_code != expected_status_code:
raise StatusCodeError(
"Request to {} failed with status: {}, expected status: {}".format(
url, response.status_code, expected_status_code
)
f"Request to {url} failed with status: {response.status_code}, expected status: {expected_status_code}"
)
return response

Expand All @@ -565,9 +563,7 @@ def verify_options_request(self, url, expected_status_code, headers=None):
response = self.do_options_request_with_logging(url, headers)
if response.status_code != expected_status_code:
raise StatusCodeError(
"Request to {} failed with status: {}, expected status: {}".format(
url, response.status_code, expected_status_code
)
f"Request to {url} failed with status: {response.status_code}, expected status: {expected_status_code}"
)
return response

Expand All @@ -576,9 +572,7 @@ def verify_post_request(self, url: str, body_obj, expected_status_code: int):
response = self.do_post_request(url, body_obj)
if response.status_code != expected_status_code:
raise StatusCodeError(
"Request to {} failed with status: {}, expected status: {}".format(
url, response.status_code, expected_status_code
)
f"Request to {url} failed with status: {response.status_code}, expected status: {expected_status_code}"
)
return response

Expand Down
2 changes: 1 addition & 1 deletion integration/helpers/deployer/deployer.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ def _display_stack_outputs(self, stack_outputs, **kwargs):
("Value", output.get("OutputValue")),
]:
pprint_columns(
columns=["{k:<{0}}{v:<{0}}".format(MIN_OFFSET, k=k, v=v)],
columns=[f"{k:<{MIN_OFFSET}}{v:<{MIN_OFFSET}}"],
width=kwargs["width"],
margin=kwargs["margin"],
format_string=OUTPUTS_FORMAT_STRING,
Expand Down
10 changes: 3 additions & 7 deletions integration/helpers/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,7 @@ def verify_stack_resources(expected_file_path, stack_resources):
parsed_resources = _sort_resources(stack_resources["StackResourceSummaries"])

if len(expected_resources) != len(parsed_resources):
return "'{}' resources expected, '{}' found: \n{}".format(
len(expected_resources), len(parsed_resources), json.dumps(parsed_resources, default=str)
)
return f"'{len(expected_resources)}' resources expected, '{len(parsed_resources)}' found: \n{json.dumps(parsed_resources, default=str)}"

for i in range(len(expected_resources)):
exp = expected_resources[i]
Expand All @@ -65,9 +63,7 @@ def verify_stack_resources(expected_file_path, stack_resources):
"ResourceType": parsed["ResourceType"],
}

return "'{}' expected, '{}' found (Don't include the LogicalId random suffix)".format(
exp, parsed_trimed_down
)
return f"'{exp}' expected, '{parsed_trimed_down}' found (Don't include the LogicalId random suffix)"
if exp["ResourceType"] != parsed["ResourceType"]:
return "'{}' expected, '{}' found".format(exp["ResourceType"], parsed["ResourceType"])
return None
Expand Down Expand Up @@ -292,5 +288,5 @@ def first_item_in_dict(dictionary):
"""
if not dictionary:
return None
first_key = list(dictionary.keys())[0]
first_key = next(iter(dictionary.keys()))
return first_key, dictionary[first_key]
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[
{
"LogicalResourceId": "MyLambdaFunctionApiEventPermissionProd",
"ResourceType": "AWS::Lambda::Permission"
},
{
"LogicalResourceId": "MyLambdaFunctionRole",
"ResourceType": "AWS::IAM::Role"
},
{
"LogicalResourceId": "MyLambdaFunction",
"ResourceType": "AWS::Lambda::Function"
},
{
"LogicalResourceId": "ServerlessRestApiDeployment",
"ResourceType": "AWS::ApiGateway::Deployment"
},
{
"LogicalResourceId": "ServerlessRestApiProdStage",
"ResourceType": "AWS::ApiGateway::Stage"
},
{
"LogicalResourceId": "ServerlessRestApi",
"ResourceType": "AWS::ApiGateway::RestApi"
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[
{
"LogicalResourceId": "MyLambdaFunction",
"ResourceType": "AWS::Lambda::Function"
},
{
"LogicalResourceId": "MyLambdaFunctionRole",
"ResourceType": "AWS::IAM::Role"
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ Resources:
return {
principalId: 'user',
usageIdentifierKey: 'at_least_20_characters',
usageIdentifierKey: 'needatleast20characters',
context: { },
policyDocument
}
Expand All @@ -73,7 +73,7 @@ Resources:
- MyUsagePlan
Properties:
Enabled: true
Value: at_least_20_characters
Value: needatleast20characters
StageKeys:
- RestApiId:
Ref: MyApi
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ Resources:
TriggerBucket:
# See also https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-s3-bucket-notificationconfig.html
DependsOn: MyConnector
UpdateReplacePolicy: Delete
DeletionPolicy: Delete
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref BucketName
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
AWSTemplateFormatVersion: '2010-09-09'
Description: A template to test timeout support for implicit APIs.

Resources:
MyLambdaFunction:
Type: AWS::Serverless::Function
Properties:
Handler: index.handler
Runtime: nodejs16.x
MemorySize: 128
Timeout: 3
InlineCode: |
exports.handler = async () => ‘Hello World!'
Events:
ApiEvent:
Type: Api
Properties:
Path: /hello
Method: get
TimeoutInMillis: 5000

Metadata:
SamTransformTest: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
AWSTemplateFormatVersion: '2010-09-09'
Description: A template to test timeout support for implicit APIs.

Parameters:
IsDevStack: {Type: String, Default: 'true', AllowedValues: ['true', 'false']}
Conditions:
IsDevStack: !Equals [!Ref IsDevStack, 'true']
NotIsDevStack: !Not [Condition: IsDevStack]

Resources:
MyLambdaFunction:
DeletionPolicy: !If [NotIsDevStack, Retain, Delete]
Type: AWS::Serverless::Function
Properties:
Handler: index.handler
Runtime: nodejs16.x
MemorySize: 128
Timeout: 3
InlineCode: |
exports.handler = async () => 'Hello World!'
Outputs:
IsDevStack:
Value: !Ref IsDevStack

Metadata:
SamTransformTest: true
2 changes: 2 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ addopts = --cov samtranslator --cov-report term-missing --cov-fail-under 95 --re
testpaths = tests
markers =
slow: marks tests as slow (deselect with '-m "not slow"')
setup: setup companion stack
teardown: teardown companion stack
log_cli = 1
log_cli_level = INFO
filterwarnings =
Expand Down
2 changes: 1 addition & 1 deletion requirements/base.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ jsonschema<5,>=3.2 # TODO: evaluate risk of removing jsonschema 3.x support
typing_extensions>=4.4,<5 # 3.7 doesn't have Literal

# resource validation & schema generation
pydantic~=1.8
pydantic>=1.8,<3
6 changes: 3 additions & 3 deletions requirements/dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ pytest-xdist>=2.5,<4
pytest-env>=0.6,<1
pytest-rerunfailures>=9.1,<12
pyyaml~=6.0
ruff==0.0.263 # loose the requirement once it is more stable
ruff==0.0.284 # loose the requirement once it is more stable

# Test requirements
pytest>=6.2,<8
Expand All @@ -19,11 +19,11 @@ tenacity~=8.0
requests~=2.28

# formatter
black==23.1.0
black==23.3.0 # 23.3.0 is the last version supporting python 3.7
ruamel.yaml==0.17.21 # It can parse yaml while perserving comments

# type check
mypy~=1.1.0
mypy~=1.3.0

# types
boto3-stubs[appconfig,serverlessrepo]>=1.19.5,==1.*
Expand Down
5 changes: 5 additions & 0 deletions ruff.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ select = [
ignore = [
"UP006", # https://github.com/charliermarsh/ruff/pull/4427
"UP007", # https://github.com/charliermarsh/ruff/pull/4427
# Mutable class attributes should be annotated with `typing.ClassVar`
# Too many violations
"RUF012",
# Logging statement uses f-string
"G004",
]

# Mininal python version we support is 3.7
Expand Down
2 changes: 1 addition & 1 deletion samtranslator/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = "1.74.0"
__version__ = "1.75.0"
8 changes: 8 additions & 0 deletions samtranslator/compat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
try:
from pydantic import v1 as pydantic
except ImportError:
# Unfortunately mypy cannot handle this try/expect pattern, and "type: ignore"
# is the simplest work-around. See: https://github.com/python/mypy/issues/1153
import pydantic # type: ignore

__all__ = ["pydantic"]
Loading

0 comments on commit c928dc4

Please sign in to comment.