From 9c4a0e26197c78e3a5af1946aca9ad680c924b1c Mon Sep 17 00:00:00 2001 From: Lucas <12496191+lucashuy@users.noreply.github.com> Date: Fri, 26 May 2023 17:19:17 -0700 Subject: [PATCH] feat: Linking Authorizers to Lambda functions using the invocation URI (#5196) * Link authorizer to lambda function invoke URI * Updated doc string * Updated exception messages back * Added check for one element in reference list * Updated empty ref list check to not block * Updated log message * Fix long line lint error --------- Co-authored-by: Mohamed Elasmar <71043312+moelasmar@users.noreply.github.com> --- .../terraform/hooks/prepare/exceptions.py | 14 ++ .../hooks/prepare/resource_linking.py | 89 +++++++++ .../hooks/prepare/resources/apigw.py | 9 + .../hooks/prepare/resources/resource_links.py | 7 + .../prepare/resources/resource_properties.py | 3 + .../hooks/prepare/test_resource_linking.py | 169 +++++++++++++----- 6 files changed, 248 insertions(+), 43 deletions(-) diff --git a/samcli/hook_packages/terraform/hooks/prepare/exceptions.py b/samcli/hook_packages/terraform/hooks/prepare/exceptions.py index e3400a9abf..7168037ae9 100644 --- a/samcli/hook_packages/terraform/hooks/prepare/exceptions.py +++ b/samcli/hook_packages/terraform/hooks/prepare/exceptions.py @@ -193,6 +193,20 @@ class GatewayResourceToApiGatewayIntegrationResponseLocalVariablesLinkingLimitat """ +class OneGatewayAuthorizerToLambdaFunctionLinkingLimitationException(OneResourceLinkingLimitationException): + """ + Exception specific for Gateway Authorizer linking to more than one Lambda Function + """ + + +class GatewayAuthorizerToLambdaFunctionLocalVariablesLinkingLimitationException( + LocalVariablesLinkingLimitationException +): + """ + Exception specific for Gateway Authorizer linking to Lambda Function using locals. + """ + + class InvalidSamMetadataPropertiesException(UserException): pass diff --git a/samcli/hook_packages/terraform/hooks/prepare/resource_linking.py b/samcli/hook_packages/terraform/hooks/prepare/resource_linking.py index e2605353a3..377d7c4321 100644 --- a/samcli/hook_packages/terraform/hooks/prepare/resource_linking.py +++ b/samcli/hook_packages/terraform/hooks/prepare/resource_linking.py @@ -9,6 +9,7 @@ from samcli.hook_packages.terraform.hooks.prepare.exceptions import ( FunctionLayerLocalVariablesLinkingLimitationException, + GatewayAuthorizerToLambdaFunctionLocalVariablesLinkingLimitationException, GatewayResourceToApiGatewayIntegrationLocalVariablesLinkingLimitationException, GatewayResourceToApiGatewayIntegrationResponseLocalVariablesLinkingLimitationException, GatewayResourceToApiGatewayMethodLocalVariablesLinkingLimitationException, @@ -16,6 +17,7 @@ InvalidResourceLinkingException, LambdaFunctionToApiGatewayIntegrationLocalVariablesLinkingLimitationException, LocalVariablesLinkingLimitationException, + OneGatewayAuthorizerToLambdaFunctionLinkingLimitationException, OneGatewayResourceToApiGatewayIntegrationLinkingLimitationException, OneGatewayResourceToApiGatewayIntegrationResponseLinkingLimitationException, OneGatewayResourceToApiGatewayMethodLinkingLimitationException, @@ -1086,6 +1088,10 @@ def _link_gateway_resource_to_gateway_rest_apis_rest_api_id_call_back( if len(referenced_rest_apis_values) > 1: raise InvalidResourceLinkingException("Could not link multiple Rest APIs to one Gateway resource") + if not referenced_rest_apis_values: + LOG.info("Unable to find any references to Rest APIs, skip linking Gateway resources") + return + logical_id = referenced_rest_apis_values[0] gateway_cfn_resource["Properties"]["RestApiId"] = ( {"Ref": logical_id.value} if isinstance(logical_id, LogicalIdReference) else logical_id.value @@ -1111,6 +1117,10 @@ def _link_gateway_resource_to_gateway_resource_call_back( if len(referenced_gateway_resource_values) > 1: raise InvalidResourceLinkingException("Could not link multiple Gateway Resources to one Gateway resource") + if not referenced_gateway_resource_values: + LOG.info("Unable to find any references to the Gateway Resource, skip linking Gateway resources") + return + logical_id = referenced_gateway_resource_values[0] gateway_resource_cfn_resource["Properties"]["ResourceId"] = ( {"Ref": logical_id.value} if isinstance(logical_id, LogicalIdReference) else logical_id.value @@ -1137,6 +1147,10 @@ def _link_gateway_resource_to_gateway_rest_apis_parent_id_call_back( if len(referenced_rest_apis_values) > 1: raise InvalidResourceLinkingException("Could not link multiple Rest APIs to one Gateway resource") + if not referenced_rest_apis_values: + LOG.info("Unable to find any references to Rest APIs, skip linking Rest API to Gateway resource") + return + logical_id = referenced_rest_apis_values[0] gateway_cfn_resource["Properties"]["ParentId"] = ( {"Fn::GetAtt": [logical_id.value, "RootResourceId"]} @@ -1351,6 +1365,12 @@ def _link_gateway_integration_to_function_call_back( "Could not link multiple Lambda functions to one Gateway integration resource" ) + if not referenced_gateway_resource_values: + LOG.info( + "Unable to find any references to Lambda functions, skip linking Lambda function to Gateway integration" + ) + return + logical_id = referenced_gateway_resource_values[0] gateway_integration_cfn_resource["Properties"]["Uri"] = ( {"Fn::Sub": INVOKE_ARN_FORMAT.format(function_logical_id=logical_id.value)} @@ -1477,3 +1497,72 @@ def _link_gateway_integration_responses_to_gateway_resource( linking_exceptions=exceptions, ) ResourceLinker(resource_linking_pair).link_resources() + + +def _link_gateway_authorizer_to_lambda_function_call_back( + gateway_authorizer_cfn_resource: Dict, lambda_function_resource_values: List[ReferenceType] +) -> None: + """ + Callback function that is used by the linking algorithm to update a CFN Authorizer Resource with + a reference to the Lambda function's invocation URI + + Parameters + ---------- + gateway_authorizer_cfn_resource: Dict + API Gateway Authorizer CFN resource + lambda_function_resource_values: List[ReferenceType] + List of referenced Lambda Functions either as the logical id of Lambda Function reosurces + defined in the customer project, or ARN values for actual Lambda Functions defined + in customer's account. This list should always contain one element only. + """ + if len(lambda_function_resource_values) > 1: + raise InvalidResourceLinkingException("Could not link multiple Lambda functions to one Gateway Authorizer") + + if not lambda_function_resource_values: + LOG.info( + "Unable to find any references to Lambda functions, skip linking Lambda function to Gateway Authorizer" + ) + return + + logical_id = lambda_function_resource_values[0] + gateway_authorizer_cfn_resource["Properties"]["AuthorizerUri"] = ( + {"Fn::Sub": INVOKE_ARN_FORMAT.format(function_logical_id=logical_id.value)} + if isinstance(logical_id, LogicalIdReference) + else logical_id.value + ) + + +def _link_gateway_authorizer_to_lambda_function( + authorizer_config_resources: Dict[str, TFResource], + authorizer_cfn_resources: Dict[str, List], + authorizer_tf_resources: Dict[str, Dict], +) -> None: + """ + Iterate through all the resources and link the corresponding Authorizer to each Lambda Function + + Parameters + ---------- + authorizer_config_resources: Dict[str, TFResource] + Dictionary of configuration Authorizer resources + authorizer_cfn_resources: Dict[str, List] + Dictionary containing resolved configuration address of CFN Authorizer resources + lambda_layers_terraform_resources: Dict[str, Dict] + Dictionary of all actual terraform layers resources (not configuration resources). The dictionary's key is the + calculated logical id for each resource + """ + exceptions = ResourcePairExceptions( + multiple_resource_linking_exception=OneGatewayAuthorizerToLambdaFunctionLinkingLimitationException, + local_variable_linking_exception=GatewayAuthorizerToLambdaFunctionLocalVariablesLinkingLimitationException, + ) + resource_linking_pair = ResourceLinkingPair( + source_resource_cfn_resource=authorizer_cfn_resources, + source_resource_tf_config=authorizer_config_resources, + destination_resource_tf=authorizer_tf_resources, + tf_destination_attribute_name="invoke_arn", + terraform_link_field_name="authorizer_uri", + cfn_link_field_name="AuthorizerUri", + terraform_resource_type_prefix=LAMBDA_FUNCTION_RESOURCE_ADDRESS_PREFIX, + cfn_resource_update_call_back_function=_link_gateway_authorizer_to_lambda_function_call_back, + linking_exceptions=exceptions, + ) + ResourceLinker(resource_linking_pair).link_resources() diff --git a/samcli/hook_packages/terraform/hooks/prepare/resources/apigw.py b/samcli/hook_packages/terraform/hooks/prepare/resources/apigw.py index 17cc2dc03a..05f1676624 100644 --- a/samcli/hook_packages/terraform/hooks/prepare/resources/apigw.py +++ b/samcli/hook_packages/terraform/hooks/prepare/resources/apigw.py @@ -108,6 +108,15 @@ def __init__(self): super(ApiGatewayStageProperties, self).__init__() +class ApiGatewayAuthorizerProperties(ResourceProperties): + """ + Contains the collection logic of the required properties for linking the aws_api_gateway_authorizer resources. + """ + + def __init__(self): + super(ApiGatewayAuthorizerProperties, self).__init__() + + def add_integrations_to_methods( gateway_methods_cfn: Dict[str, List], gateway_integrations_cfn: Dict[str, List] ) -> None: diff --git a/samcli/hook_packages/terraform/hooks/prepare/resources/resource_links.py b/samcli/hook_packages/terraform/hooks/prepare/resources/resource_links.py index d9632909c0..45d7a3ed59 100644 --- a/samcli/hook_packages/terraform/hooks/prepare/resources/resource_links.py +++ b/samcli/hook_packages/terraform/hooks/prepare/resources/resource_links.py @@ -1,6 +1,7 @@ from typing import List from samcli.hook_packages.terraform.hooks.prepare.property_builder import ( + TF_AWS_API_GATEWAY_AUTHORIZER, TF_AWS_API_GATEWAY_INTEGRATION, TF_AWS_API_GATEWAY_INTEGRATION_RESPONSE, TF_AWS_API_GATEWAY_METHOD, @@ -11,6 +12,7 @@ TF_AWS_LAMBDA_LAYER_VERSION, ) from samcli.hook_packages.terraform.hooks.prepare.resource_linking import ( + _link_gateway_authorizer_to_lambda_function, _link_gateway_integration_responses_to_gateway_resource, _link_gateway_integration_responses_to_gateway_rest_apis, _link_gateway_integrations_to_function_resource, @@ -71,4 +73,9 @@ dest=TF_AWS_API_GATEWAY_RESOURCE, linking_func=_link_gateway_integration_responses_to_gateway_resource, ), + LinkingPairCaller( + source=TF_AWS_API_GATEWAY_AUTHORIZER, + dest=TF_AWS_LAMBDA_FUNCTION, + linking_func=_link_gateway_authorizer_to_lambda_function, + ), ] diff --git a/samcli/hook_packages/terraform/hooks/prepare/resources/resource_properties.py b/samcli/hook_packages/terraform/hooks/prepare/resources/resource_properties.py index af70d58831..30c31aabd0 100644 --- a/samcli/hook_packages/terraform/hooks/prepare/resources/resource_properties.py +++ b/samcli/hook_packages/terraform/hooks/prepare/resources/resource_properties.py @@ -2,6 +2,7 @@ from typing import Dict from samcli.hook_packages.terraform.hooks.prepare.property_builder import ( + TF_AWS_API_GATEWAY_AUTHORIZER, TF_AWS_API_GATEWAY_INTEGRATION, TF_AWS_API_GATEWAY_INTEGRATION_RESPONSE, TF_AWS_API_GATEWAY_METHOD, @@ -12,6 +13,7 @@ TF_AWS_LAMBDA_LAYER_VERSION, ) from samcli.hook_packages.terraform.hooks.prepare.resources.apigw import ( + ApiGatewayAuthorizerProperties, ApiGatewayMethodProperties, ApiGatewayResourceProperties, ApiGatewayRestApiProperties, @@ -46,4 +48,5 @@ def get_resource_property_mapping() -> Dict[str, ResourceProperties]: TF_AWS_API_GATEWAY_STAGE: ApiGatewayStageProperties(), TF_AWS_API_GATEWAY_INTEGRATION: InternalApiGatewayIntegrationProperties(), TF_AWS_API_GATEWAY_INTEGRATION_RESPONSE: InternalApiGatewayIntegrationResponseProperties(), + TF_AWS_API_GATEWAY_AUTHORIZER: ApiGatewayAuthorizerProperties(), } diff --git a/tests/unit/hook_packages/terraform/hooks/prepare/test_resource_linking.py b/tests/unit/hook_packages/terraform/hooks/prepare/test_resource_linking.py index 711ff606e2..2e492a2cb1 100644 --- a/tests/unit/hook_packages/terraform/hooks/prepare/test_resource_linking.py +++ b/tests/unit/hook_packages/terraform/hooks/prepare/test_resource_linking.py @@ -6,11 +6,13 @@ from parameterized import parameterized from samcli.hook_packages.terraform.hooks.prepare.exceptions import ( + GatewayAuthorizerToLambdaFunctionLocalVariablesLinkingLimitationException, InvalidResourceLinkingException, LocalVariablesLinkingLimitationException, ONE_LAMBDA_LAYER_LINKING_ISSUE_LINK, LOCAL_VARIABLES_SUPPORT_ISSUE_LINK, APPLY_WORK_AROUND_MESSAGE, + OneGatewayAuthorizerToLambdaFunctionLinkingLimitationException, OneLambdaLayerLinkingLimitationException, FunctionLayerLocalVariablesLinkingLimitationException, OneGatewayResourceToApiGatewayMethodLinkingLimitationException, @@ -35,6 +37,8 @@ from samcli.hook_packages.terraform.hooks.prepare.resource_linking import ( _clean_references_list, + _link_gateway_authorizer_to_lambda_function, + _link_gateway_authorizer_to_lambda_function_call_back, _resolve_module_output, _resolve_module_variable, _build_module, @@ -1612,7 +1616,7 @@ def test_link_lambda_functions_to_layers( ] ) def test_link_lambda_functions_to_layers_call_back(self, input_function, logical_ids, expected_layers): - lambda_function = input_function.copy() + lambda_function = deepcopy(input_function) _link_lambda_functions_to_layers_call_back(lambda_function, logical_ids) input_function["Properties"]["Layers"] = expected_layers self.assertEqual(lambda_function, input_function) @@ -1938,20 +1942,11 @@ def test_link_gateway_integrations_to_function_resource( def test_link_gateway_methods_to_gateway_rest_apis_call_back( self, input_gateway_method, logical_ids, expected_rest_api ): - gateway_method = input_gateway_method.copy() + gateway_method = deepcopy(input_gateway_method) _link_gateway_resource_to_gateway_rest_apis_rest_api_id_call_back(gateway_method, logical_ids) input_gateway_method["Properties"]["RestApiId"] = expected_rest_api self.assertEqual(gateway_method, input_gateway_method) - def test_link_gateway_methods_to_gateway_rest_apis_call_back_multiple_destinations(self): - gateway_method = Mock() - logical_ids = [Mock(), Mock()] - with self.assertRaises( - InvalidResourceLinkingException, - msg="Could not link multiple Rest APIs to one Gateway method resource", - ): - _link_gateway_resource_to_gateway_rest_apis_rest_api_id_call_back(gateway_method, logical_ids) - @parameterized.expand( [ ( @@ -1983,20 +1978,11 @@ def test_link_gateway_methods_to_gateway_rest_apis_call_back_multiple_destinatio def test_link_gateway_method_to_gateway_resource_call_back( self, input_gateway_method, logical_ids, expected_resource ): - gateway_method = input_gateway_method.copy() + gateway_method = deepcopy(input_gateway_method) _link_gateway_resource_to_gateway_resource_call_back(gateway_method, logical_ids) input_gateway_method["Properties"]["ResourceId"] = expected_resource self.assertEqual(gateway_method, input_gateway_method) - def test_link_gateway_method_to_gateway_resource_call_back_multiple_destinations(self): - gateway_method = Mock() - logical_ids = [Mock(), Mock()] - with self.assertRaises( - InvalidResourceLinkingException, - msg="Could not link multiple Gateway Resources to one Gateway method resource", - ): - _link_gateway_resource_to_gateway_resource_call_back(gateway_method, logical_ids) - @parameterized.expand( [ ( @@ -2021,27 +2007,18 @@ def test_link_gateway_method_to_gateway_resource_call_back_multiple_destinations "Properties": {}, }, [LogicalIdReference("RestApi")], - {"Fn:GetAtt": ["RestApi", "RootResourceId"]}, + {"Fn::GetAtt": ["RestApi", "RootResourceId"]}, ), ] ) def test_link_gateway_resource_to_gateway_rest_api_parent_id_call_back( self, input_gateway_resource, logical_ids, expected_rest_api ): - gateway_resource = input_gateway_resource.copy() + gateway_resource = deepcopy(input_gateway_resource) _link_gateway_resource_to_gateway_rest_apis_parent_id_call_back(gateway_resource, logical_ids) input_gateway_resource["Properties"]["ParentId"] = expected_rest_api self.assertEqual(gateway_resource, input_gateway_resource) - def test_link_gateway_resource_to_gateway_rest_apis_call_back_multiple_destinations(self): - gateway_resource = Mock() - logical_ids = [Mock(), Mock()] - with self.assertRaises( - InvalidResourceLinkingException, - msg="Could not link multiple Rest APIs to one Gateway resource", - ): - _link_gateway_resource_to_gateway_rest_apis_parent_id_call_back(gateway_resource, logical_ids) - @parameterized.expand( [ ( @@ -2051,7 +2028,7 @@ def test_link_gateway_resource_to_gateway_rest_apis_call_back_multiple_destinati }, [LogicalIdReference("FunctionA")], { - "Fn::Sub": "arn:${{AWS::Partition}}:apigateway:${{AWS::Region}}:lambda:path/2015-03-31/functions/${{FunctionA.Arn}}/invocations" + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${FunctionA.Arn}/invocations" }, ), ( @@ -2069,7 +2046,7 @@ def test_link_gateway_resource_to_gateway_rest_apis_call_back_multiple_destinati }, [LogicalIdReference("RestApi")], { - "Fn::Sub": "arn:${{AWS::Partition}}:apigateway:${{AWS::Region}}:lambda:path/2015-03-31/functions/${{FunctionA.Arn}}/invocations" + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${RestApi.Arn}/invocations" }, ), ] @@ -2077,19 +2054,55 @@ def test_link_gateway_resource_to_gateway_rest_apis_call_back_multiple_destinati def test_link_gateway_integration_to_function_call_back( self, input_gateway_integration, logical_ids, expected_integration ): - gateway_resource = input_gateway_integration.copy() + gateway_resource = deepcopy(input_gateway_integration) _link_gateway_integration_to_function_call_back(gateway_resource, logical_ids) input_gateway_integration["Properties"]["Uri"] = expected_integration self.assertEqual(gateway_resource, input_gateway_integration) - def test_link_gateway_integration_to_function_call_back_multiple_destinations(self): - gateway_integration = Mock() - logical_ids = [Mock(), Mock()] - with self.assertRaises( - InvalidResourceLinkingException, - msg="Could not link multiple Lambda functions to one Gateway integration resource", - ): - _link_gateway_integration_to_function_call_back(gateway_integration, logical_ids) + @parameterized.expand( + [ + ( + _link_gateway_integration_to_function_call_back, + "Could not link multiple Lambda functions to one Gateway integration resource", + ), + ( + _link_gateway_authorizer_to_lambda_function_call_back, + "Could not link multiple Lambda functions to one Gateway Authorizer", + ), + ( + _link_gateway_resource_to_gateway_rest_apis_parent_id_call_back, + "Could not link multiple Rest APIs to one Gateway resource", + ), + ( + _link_gateway_resource_to_gateway_resource_call_back, + "Could not link multiple Gateway Resources to one Gateway resource", + ), + ( + _link_gateway_resource_to_gateway_rest_apis_rest_api_id_call_back, + "Could not link multiple Rest APIs to one Gateway resource", + ), + ] + ) + def test_linking_callbacks_raises_multiple_reference_exception(self, linking_call_back_method, expected_message): + with self.assertRaisesRegex(InvalidResourceLinkingException, expected_regex=expected_message): + linking_call_back_method(Mock(), [Mock(), Mock()]) + + @parameterized.expand( + [ + (_link_gateway_integration_to_function_call_back,), + (_link_gateway_authorizer_to_lambda_function_call_back,), + (_link_gateway_resource_to_gateway_rest_apis_parent_id_call_back,), + (_link_gateway_resource_to_gateway_resource_call_back,), + (_link_gateway_resource_to_gateway_rest_apis_rest_api_id_call_back,), + ] + ) + def test_linking_callbacks_skips_empty_references(self, linking_call_back_method): + original_props = {"Properties": {}} + passed_props = deepcopy(original_props) + + linking_call_back_method(passed_props, []) + + self.assertEqual(original_props, passed_props) @patch( "samcli.hook_packages.terraform.hooks.prepare.resource_linking._link_gateway_resource_to_gateway_rest_apis_rest_api_id_call_back" @@ -2162,3 +2175,73 @@ def test_link_gateway_integration_response_to_gateway_resource( linking_exceptions=mock_resource_linking_exceptions(), ) mock_resource_linker.assert_called_once_with(mock_resource_linking_pair()) + + @parameterized.expand( + [ + ( + { + "Type": "AWS::ApiGateway::Authorizer", + "Properties": {"Uri": "invoke_arn"}, + }, + [LogicalIdReference("Function")], + { + "Fn::Sub": "arn:${AWS::Partition}:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Function.Arn}/invocations" + }, + ), + ( + { + "Type": "AWS::ApiGateway::Authorizer", + "Properties": {"Uri": "invoke_arn"}, + }, + [ExistingResourceReference("invoke_arn")], + "invoke_arn", + ), + ] + ) + def test_link_gateway_authorizer_to_lambda_function_call_back( + self, input_gateway_authorizer, logical_ids, expected_integration + ): + authorizer = deepcopy(input_gateway_authorizer) + _link_gateway_authorizer_to_lambda_function_call_back(authorizer, logical_ids) + input_gateway_authorizer["Properties"]["AuthorizerUri"] = expected_integration + self.assertEqual(authorizer, input_gateway_authorizer) + + @patch( + "samcli.hook_packages.terraform.hooks.prepare.resource_linking._link_gateway_authorizer_to_lambda_function_call_back" + ) + @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_authorizer_to_lambda_function( + self, + mock_resource_linking_exceptions, + mock_resource_linking_pair, + mock_resource_linker, + mock_link_gateway_authorizer_to_lambda_function_call_back, + ): + authorizer_cfn_resources = Mock() + authorizer_config_resources = Mock() + authorizer_tf_resources = Mock() + + _link_gateway_authorizer_to_lambda_function( + authorizer_config_resources, authorizer_cfn_resources, authorizer_tf_resources + ) + + mock_resource_linking_exceptions.assert_called_once_with( + multiple_resource_linking_exception=OneGatewayAuthorizerToLambdaFunctionLinkingLimitationException, + local_variable_linking_exception=GatewayAuthorizerToLambdaFunctionLocalVariablesLinkingLimitationException, + ) + + mock_resource_linking_pair.assert_called_once_with( + source_resource_cfn_resource=authorizer_cfn_resources, + source_resource_tf_config=authorizer_config_resources, + destination_resource_tf=authorizer_tf_resources, + tf_destination_attribute_name="invoke_arn", + terraform_link_field_name="authorizer_uri", + cfn_link_field_name="AuthorizerUri", + terraform_resource_type_prefix=LAMBDA_FUNCTION_RESOURCE_ADDRESS_PREFIX, + cfn_resource_update_call_back_function=mock_link_gateway_authorizer_to_lambda_function_call_back, + linking_exceptions=mock_resource_linking_exceptions(), + ) + + mock_resource_linker.assert_called_once_with(mock_resource_linking_pair())