From 579c9e3d1368a01a81fdd771054b8c6a6eb8d75d Mon Sep 17 00:00:00 2001 From: Mohammed Alkindi Date: Fri, 4 Sep 2026 19:39:18 +0400 Subject: [PATCH] fix(parameters): honor auto transform in batched get_parameters_by_name transform="auto" infers the transform method from the parameter name suffix, but _transform_and_cache_get_parameters_response called transform_value() without key=name. The batched GetParameters path therefore inferred from an empty string and silently fell back to the identity transform, returning raw strings instead of parsed values. Parameters routed to the single GetParameter path (decrypt=True) already pass the key via BaseProvider.get(), so a single get_parameters_by_name call could return a parsed dict for some parameters and a raw string for others. --- .../utilities/parameters/ssm.py | 7 ++++- .../_boto3/test_utilities_parameters.py | 28 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/aws_lambda_powertools/utilities/parameters/ssm.py b/aws_lambda_powertools/utilities/parameters/ssm.py index 7c2b5e1017e..6ce4f853459 100644 --- a/aws_lambda_powertools/utilities/parameters/ssm.py +++ b/aws_lambda_powertools/utilities/parameters/ssm.py @@ -654,7 +654,12 @@ def _transform_and_cache_get_parameters_response( # NOTE: If transform is set, we do it before caching to reduce number of operations if transform: - value = transform_value(value=value, transform=transform, raise_on_transform_error=raise_on_error) # type: ignore[assignment] + value = transform_value( + value=value, + transform=transform, + raise_on_transform_error=raise_on_error, + key=name, + ) # type: ignore[assignment] _cache_key = (name, transform) self.add_to_cache(key=_cache_key, value=value, max_age=options["max_age"]) diff --git a/tests/functional/parameters/_boto3/test_utilities_parameters.py b/tests/functional/parameters/_boto3/test_utilities_parameters.py index 71a5841d05c..dc7643795c1 100644 --- a/tests/functional/parameters/_boto3/test_utilities_parameters.py +++ b/tests/functional/parameters/_boto3/test_utilities_parameters.py @@ -1142,6 +1142,34 @@ def test_ssm_provider_get_parameters_by_name_do_not_raise_on_failure_transform(m stubber.deactivate() +def test_ssm_provider_get_parameters_by_name_auto_transform_infers_from_parameter_name(mock_name, mock_value, config): + # GIVEN transform="auto" and a parameter name suffixed with ".json" + # (auto-transform must infer the transform method from the parameter name, not its value) + success = f"/dev/{mock_name}.json" + fail = "/does_not_exist" # stub model doesn't support all-success yet + params = {success: {}, fail: {}} + param_names = list(params.keys()) + expected_value = {"value": mock_value} + stub_params = {success: json.dumps(expected_value)} + + expected_stub_response = build_get_parameters_stub(params=stub_params, invalid_parameters=[fail]) + expected_stub_params = {"Names": param_names} + + provider = parameters.SSMProvider(boto_config=config) + stubber = stub.Stubber(provider.client) + stubber.add_response("get_parameters", expected_stub_response, expected_stub_params) + stubber.activate() + + try: + ret = provider.get_parameters_by_name(parameters=params, transform="auto", raise_on_error=False) + + stubber.assert_no_pending_responses() + # THEN the value should be parsed as JSON, not returned as a raw string + assert ret[success] == expected_value + finally: + stubber.deactivate() + + def test_ssm_provider_get_parameters_by_name_do_not_raise_on_failure_with_decrypt(mock_name, config): # GIVEN one parameter requires decryption and an arbitrary SDK error occurs param = f"/{mock_name}"