Skip to content

Commit

Permalink
feat: Link v2 integration to V2 API (#5486)
Browse files Browse the repository at this point in the history
* feat: Link v2 integration to V2 API

* run make format
  • Loading branch information
moelasmar authored Jul 12, 2023
1 parent 123d880 commit 1aea3da
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 0 deletions.
14 changes: 14 additions & 0 deletions samcli/hook_packages/terraform/hooks/prepare/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,20 @@ class GatewayV2IntegrationToLambdaFunctionLocalVariablesLinkingLimitationExcepti
"""


class OneGatewayV2IntegrationToGatewayV2ApiLinkingLimitationException(OneResourceLinkingLimitationException):
"""
Exception specific for Gateway V2 Integration linking to more than one Gateway V2 API
"""


class GatewayV2IntegrationToGatewayV2ApiLocalVariablesLinkingLimitationException(
LocalVariablesLinkingLimitationException
):
"""
Exception specific for Gateway V2 Integration linking to Gateway V2 API using locals.
"""


class InvalidSamMetadataPropertiesException(UserException):
pass

Expand Down
71 changes: 71 additions & 0 deletions samcli/hook_packages/terraform/hooks/prepare/resource_linking.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
GatewayResourceToApiGatewayIntegrationResponseLocalVariablesLinkingLimitationException,
GatewayResourceToApiGatewayMethodLocalVariablesLinkingLimitationException,
GatewayResourceToGatewayRestApiLocalVariablesLinkingLimitationException,
GatewayV2IntegrationToGatewayV2ApiLocalVariablesLinkingLimitationException,
GatewayV2IntegrationToLambdaFunctionLocalVariablesLinkingLimitationException,
GatewayV2RouteToGatewayV2IntegrationLocalVariablesLinkingLimitationException,
InvalidResourceLinkingException,
Expand All @@ -28,6 +29,7 @@
OneGatewayResourceToApiGatewayIntegrationResponseLinkingLimitationException,
OneGatewayResourceToApiGatewayMethodLinkingLimitationException,
OneGatewayResourceToRestApiLinkingLimitationException,
OneGatewayV2IntegrationToGatewayV2ApiLinkingLimitationException,
OneGatewayV2IntegrationToLambdaFunctionLinkingLimitationException,
OneGatewayV2RouteToGatewayV2IntegrationLinkingLimitationException,
OneLambdaFunctionResourceToApiGatewayIntegrationLinkingLimitationException,
Expand Down Expand Up @@ -60,6 +62,7 @@
API_GATEWAY_RESOURCE_RESOURCE_ADDRESS_PREFIX = "aws_api_gateway_resource."
API_GATEWAY_AUTHORIZER_RESOURCE_ADDRESS_PREFIX = "aws_api_gateway_authorizer."
API_GATEWAY_V2_INTEGRATION_RESOURCE_ADDRESS_PREFIX = "aws_apigatewayv2_integration"
API_GATEWAY_V2_API_RESOURCE_ADDRESS_PREFIX = "aws_apigatewayv2_api"
TERRAFORM_LOCAL_VARIABLES_ADDRESS_PREFIX = "local."
DATA_RESOURCE_ADDRESS_PREFIX = "data."

Expand Down Expand Up @@ -1892,3 +1895,71 @@ def _link_gateway_v2_integration_to_lambda_function(
linking_exceptions=exceptions,
)
ResourceLinker(resource_linking_pair).link_resources()


def _link_gateway_v2_integration_to_api_callback(
gateway_v2_integration_cfn_resource: Dict, gateway_v2_api_resources: List[ReferenceType]
):
"""
Callback function that is used by the linking algorithm to update a CFN V2 Integration Resource with
a reference to the Gateway V2 Api resource
Parameters
----------
gateway_v2_integration_cfn_resource: Dict
API Gateway V2 Integration CFN resource
gateway_v2_api_resources: List[ReferenceType]
List of referenced Gateway V2 Apis either as the logical id of Apis resources
defined in the customer project, or ARN values for actual Api defined
in customer's account. This list should always contain one element only.
"""
if len(gateway_v2_api_resources) > 1:
raise InvalidResourceLinkingException("Could not link multiple Gateway V2 Apis to one Gateway V2 Integration")

if not gateway_v2_api_resources:
LOG.info(
"Unable to find any references to Gateway V2 APIs, skip linking Gateway V2 Integration to Gateway V2 APIs"
)
return

logical_id = gateway_v2_api_resources[0]
gateway_v2_integration_cfn_resource["Properties"]["ApiId"] = (
{"Ref": logical_id.value} if isinstance(logical_id, LogicalIdReference) else logical_id.value
)


def _link_gateway_v2_integration_to_api(
gateway_integration_config_resources: Dict[str, TFResource],
gateway_integration_config_address_cfn_resources_map: Dict[str, List],
api_resources: Dict[str, Dict],
) -> None:
"""
Iterate through all the resources and link the corresponding
Gateway V2 Integration resources to each Gateway V2 Api
Parameters
----------
gateway_integration_config_resources: Dict[str, TFResource]
Dictionary of configuration Gateway Integrations
gateway_integration_config_address_cfn_resources_map: Dict[str, List]
Dictionary containing resolved configuration addresses matched up to the cfn Gateway Integration
api_resources: Dict[str, Dict]
Dictionary of all Terraform Gateway V2 Api resources (not configuration resources).
The dictionary's key is the calculated logical id for each resource.
"""
exceptions = ResourcePairExceptions(
multiple_resource_linking_exception=OneGatewayV2IntegrationToGatewayV2ApiLinkingLimitationException,
local_variable_linking_exception=GatewayV2IntegrationToGatewayV2ApiLocalVariablesLinkingLimitationException,
)
resource_linking_pair = ResourceLinkingPair(
source_resource_cfn_resource=gateway_integration_config_address_cfn_resources_map,
source_resource_tf_config=gateway_integration_config_resources,
destination_resource_tf=api_resources,
tf_destination_attribute_name="id",
terraform_link_field_name="api_id",
cfn_link_field_name="ApiId",
terraform_resource_type_prefix=API_GATEWAY_V2_API_RESOURCE_ADDRESS_PREFIX,
cfn_resource_update_call_back_function=_link_gateway_v2_integration_to_api_callback,
linking_exceptions=exceptions,
)
ResourceLinker(resource_linking_pair).link_resources()
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,15 @@ def __init__(self):
super(ApiGatewayV2RouteProperties, self).__init__()


class ApiGatewayV2ApiProperties(ResourceProperties):
"""
Contains the collection logic of the required properties for linking the aws_apigatewayv2_api resources.
"""

def __init__(self):
super(ApiGatewayV2ApiProperties, self).__init__()


class ApiGatewayV2IntegrationProperties(ResourceProperties):
"""
Contains the collection logic of the required properties for linking the aws_api_gateway_v2_authorizer resources.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
TF_AWS_API_GATEWAY_RESOURCE,
TF_AWS_API_GATEWAY_REST_API,
TF_AWS_API_GATEWAY_STAGE,
TF_AWS_API_GATEWAY_V2_API,
TF_AWS_API_GATEWAY_V2_INTEGRATION,
TF_AWS_API_GATEWAY_V2_ROUTE,
TF_AWS_LAMBDA_FUNCTION,
Expand All @@ -26,6 +27,7 @@
_link_gateway_methods_to_gateway_rest_apis,
_link_gateway_resources_to_gateway_rest_apis,
_link_gateway_stage_to_rest_api,
_link_gateway_v2_integration_to_api,
_link_gateway_v2_integration_to_lambda_function,
_link_gateway_v2_route_to_integration,
_link_lambda_functions_to_layers,
Expand Down Expand Up @@ -104,4 +106,9 @@
dest=TF_AWS_LAMBDA_FUNCTION,
linking_func=_link_gateway_v2_integration_to_lambda_function,
),
LinkingPairCaller(
source=TF_AWS_API_GATEWAY_V2_INTEGRATION,
dest=TF_AWS_API_GATEWAY_V2_API,
linking_func=_link_gateway_v2_integration_to_api,
),
]
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
TF_AWS_API_GATEWAY_RESOURCE,
TF_AWS_API_GATEWAY_REST_API,
TF_AWS_API_GATEWAY_STAGE,
TF_AWS_API_GATEWAY_V2_API,
TF_AWS_API_GATEWAY_V2_INTEGRATION,
TF_AWS_API_GATEWAY_V2_ROUTE,
TF_AWS_LAMBDA_FUNCTION,
Expand All @@ -20,6 +21,7 @@
ApiGatewayResourceProperties,
ApiGatewayRestApiProperties,
ApiGatewayStageProperties,
ApiGatewayV2ApiProperties,
ApiGatewayV2IntegrationProperties,
ApiGatewayV2RouteProperties,
)
Expand Down Expand Up @@ -55,4 +57,5 @@ def get_resource_property_mapping() -> Dict[str, ResourceProperties]:
TF_AWS_API_GATEWAY_AUTHORIZER: ApiGatewayAuthorizerProperties(),
TF_AWS_API_GATEWAY_V2_ROUTE: ApiGatewayV2RouteProperties(),
TF_AWS_API_GATEWAY_V2_INTEGRATION: ApiGatewayV2IntegrationProperties(),
TF_AWS_API_GATEWAY_V2_API: ApiGatewayV2ApiProperties(),
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
GatewayV2RouteToGatewayV2IntegrationLocalVariablesLinkingLimitationException,
OneGatewayV2IntegrationToLambdaFunctionLinkingLimitationException,
GatewayV2IntegrationToLambdaFunctionLocalVariablesLinkingLimitationException,
OneGatewayV2IntegrationToGatewayV2ApiLinkingLimitationException,
GatewayV2IntegrationToGatewayV2ApiLocalVariablesLinkingLimitationException,
)

from samcli.hook_packages.terraform.hooks.prepare.resource_linking import (
Expand Down Expand Up @@ -91,6 +93,9 @@
_link_gateway_v2_integration_to_lambda_function_callback,
_link_gateway_v2_integration_to_lambda_function,
_extract_gateway_v2_integration_id_from_route_target_value,
_link_gateway_v2_integration_to_api,
API_GATEWAY_V2_API_RESOURCE_ADDRESS_PREFIX,
_link_gateway_v2_integration_to_api_callback,
)
from samcli.hook_packages.terraform.hooks.prepare.utilities import get_configuration_address
from samcli.hook_packages.terraform.hooks.prepare.types import (
Expand Down Expand Up @@ -2126,6 +2131,10 @@ def test_link_gateway_integration_to_function_call_back(
_link_gateway_v2_integration_to_lambda_function_callback,
"Could not link multiple lambda functions to one Gateway V2 Integration",
),
(
_link_gateway_v2_integration_to_api_callback,
"Could not link multiple Gateway V2 Apis to one Gateway V2 Integration",
),
]
)
def test_linking_callbacks_raises_multiple_reference_exception(self, linking_call_back_method, expected_message):
Expand All @@ -2142,6 +2151,7 @@ def test_linking_callbacks_raises_multiple_reference_exception(self, linking_cal
(_link_gateway_method_to_gateway_authorizer_call_back,),
(_link_gateway_v2_route_to_integration_callback,),
(_link_gateway_v2_integration_to_lambda_function_callback,),
(_link_gateway_v2_integration_to_api_callback,),
]
)
def test_linking_callbacks_skips_empty_references(self, linking_call_back_method):
Expand Down Expand Up @@ -2573,3 +2583,69 @@ def test_link_gateway_v2_integration_to_lambda_function_callback(
def test_extract_gateway_v2_integration_id_from_route_target_value(self, input_value, expected_output):
output = _extract_gateway_v2_integration_id_from_route_target_value(input_value)
self.assertEqual(output, expected_output)

@patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking._link_gateway_v2_integration_to_api_callback")
@patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourceLinker")
@patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourceLinkingPair")
@patch("samcli.hook_packages.terraform.hooks.prepare.resource_linking.ResourcePairExceptions")
def test_link_gateway_v2_integration_to_gateway_v2_api(
self,
mock_resource_linking_exceptions,
mock_resource_linking_pair,
mock_resource_linker,
mock_link_gateway_v2_integration_to_api_callback,
):
integrations_v2_cfn_resources = Mock()
integrations_v2_config_resources = Mock()
apis_v2_tf_resources = Mock()

_link_gateway_v2_integration_to_api(
integrations_v2_config_resources, integrations_v2_cfn_resources, apis_v2_tf_resources
)

mock_resource_linking_exceptions.assert_called_once_with(
multiple_resource_linking_exception=OneGatewayV2IntegrationToGatewayV2ApiLinkingLimitationException,
local_variable_linking_exception=GatewayV2IntegrationToGatewayV2ApiLocalVariablesLinkingLimitationException,
)

mock_resource_linking_pair.assert_called_once_with(
source_resource_cfn_resource=integrations_v2_cfn_resources,
source_resource_tf_config=integrations_v2_config_resources,
destination_resource_tf=apis_v2_tf_resources,
tf_destination_attribute_name="id",
terraform_link_field_name="api_id",
cfn_link_field_name="ApiId",
terraform_resource_type_prefix=API_GATEWAY_V2_API_RESOURCE_ADDRESS_PREFIX,
cfn_resource_update_call_back_function=mock_link_gateway_v2_integration_to_api_callback,
linking_exceptions=mock_resource_linking_exceptions(),
)

mock_resource_linker.assert_called_once_with(mock_resource_linking_pair())

@parameterized.expand(
[
(
{
"Type": "AWS::ApiGatewayV2::Integration",
"Properties": {"ApiId": "api_id"},
},
[LogicalIdReference("myapi")],
{"Ref": "myapi"},
),
(
{
"Type": "AWS::ApiGatewayV2::Integration",
"Properties": {"ApiId": "api_id"},
},
[ExistingResourceReference("myapi_arn")],
"myapi_arn",
),
]
)
def test_link_gateway_v2_integration_to_api_callback(
self, input_gateway_v2_integration, logical_ids, expected_api_reference
):
gateway_resource = deepcopy(input_gateway_v2_integration)
_link_gateway_v2_integration_to_api_callback(gateway_resource, logical_ids)
input_gateway_v2_integration["Properties"]["ApiId"] = expected_api_reference
self.assertEqual(gateway_resource, input_gateway_v2_integration)

0 comments on commit 1aea3da

Please sign in to comment.