Skip to content

Commit 59e65bd

Browse files
authored
fix: Remove str type constrain in Fn:FindInMap to avoid the issue of lookup int value (#7306)
1 parent 79838a5 commit 59e65bd

File tree

4 files changed

+42
-1
lines changed

4 files changed

+42
-1
lines changed

samcli/lib/intrinsic_resolver/intrinsic_property_resolver.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ def handle_find_in_map(self, intrinsic_value, ignore_errors):
492492
)
493493

494494
second_level_value = top_level_value.get(second_level_key)
495-
verify_intrinsic_type_str(
495+
verify_non_null(
496496
second_level_value,
497497
IntrinsicResolver.FN_FIND_IN_MAP,
498498
message="The SecondLevelKey is missing in the Mappings dictionary in Fn::FindInMap "

tests/integration/local/start_lambda/test_start_lambda.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,28 @@ def test_invoke_with_function_timeout(self, use_full_path):
286286
self.assertIsNone(response.get("FunctionError"))
287287
self.assertEqual(response.get("StatusCode"), 200)
288288

289+
@parameterized.expand([("False"), ("True")])
290+
@pytest.mark.flaky(reruns=3)
291+
@pytest.mark.timeout(timeout=300, method="thread")
292+
def test_invoke_with_function_timeout_using_lookup_value(self, use_full_path):
293+
"""
294+
This behavior does not match the actually Lambda Service. For functions that timeout, data returned like the
295+
following:
296+
{"errorMessage":"<timestamp> <request_id> Task timed out after 5.00 seconds"}
297+
298+
For Local Lambda's, however, timeouts are an interrupt on the thread that runs invokes the function. Since the
299+
invoke is on a different thread, we do not (currently) have a way to communicate this back to the caller. So
300+
when a timeout happens locally, we do not add the FunctionError: Unhandled to the response and have an empty
301+
string as the data returned (because no data was found in stdout from the container).
302+
"""
303+
response = self.lambda_client.invoke(
304+
FunctionName=f"{self.parent_path if use_full_path == 'True' else ''}TimeoutFunctionUsingLookupValue"
305+
)
306+
307+
self.assertEqual(response.get("Payload").read().decode("utf-8"), "")
308+
self.assertIsNone(response.get("FunctionError"))
309+
self.assertEqual(response.get("StatusCode"), 200)
310+
289311

290312
class TestWarmContainersBaseClass(StartLambdaIntegBaseClass):
291313
def setUp(self):

tests/integration/testdata/invoke/template.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ Parameters:
2626
Type: String
2727
Default: "2"
2828

29+
Mappings:
30+
common:
31+
LambdaFunction:
32+
Timeout: 5
33+
2934
Resources:
3035
HelloWorldServerlessFunction:
3136
Type: AWS::Serverless::Function
@@ -58,6 +63,14 @@ Resources:
5863
CodeUri: .
5964
Timeout: 5
6065

66+
TimeoutFunctionUsingLookupValue:
67+
Type: AWS::Serverless::Function
68+
Properties:
69+
Handler: main.sleep_handler
70+
Runtime: python3.9
71+
CodeUri: .
72+
Timeout: !FindInMap [common, LambdaFunction, Timeout]
73+
6174
HelloWorldSleepFunction:
6275
Type: AWS::Serverless::Function
6376
Properties:

tests/unit/lib/intrinsic_resolver/test_intrinsic_resolver.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,7 @@ def setUp(self):
209209
"Basic": {"Test": {"key": "value"}},
210210
"value": {"anotherkey": {"key": "result"}},
211211
"result": {"value": {"key": "final"}},
212+
"NonStrValue": {"Test": {"key": 0}},
212213
}
213214
}
214215
self.resolver = IntrinsicResolver(symbol_resolver=IntrinsicsSymbolTable(), template=template)
@@ -218,6 +219,11 @@ def test_basic_find_in_map(self):
218219
result = self.resolver.intrinsic_property_resolver(intrinsic, True)
219220
self.assertEqual(result, "value")
220221

222+
def test_basic_find_in_map_with_non_string_value(self):
223+
intrinsic = {"Fn::FindInMap": ["NonStrValue", "Test", "key"]}
224+
result = self.resolver.intrinsic_property_resolver(intrinsic, True)
225+
self.assertEqual(result, 0)
226+
221227
def test_nested_find_in_map(self):
222228
intrinsic_base_1 = {"Fn::FindInMap": ["Basic", "Test", "key"]}
223229
intrinsic_base_2 = {"Fn::FindInMap": [intrinsic_base_1, "anotherkey", "key"]}

0 commit comments

Comments
 (0)