diff --git a/aws_lambda_powertools/utilities/parameters/ssm.py b/aws_lambda_powertools/utilities/parameters/ssm.py index 6ce4f853459..a404b29f764 100644 --- a/aws_lambda_powertools/utilities/parameters/ssm.py +++ b/aws_lambda_powertools/utilities/parameters/ssm.py @@ -713,7 +713,7 @@ def _split_batch_and_decrypt_parameters( for parameter, options in parameters.items(): # NOTE: TypeDict later - _overrides = options or {} + _overrides = {**(options or {})} _overrides["transform"] = _overrides.get("transform") or transform # These values can be falsy (False, 0) diff --git a/tests/functional/parameters/_boto3/test_utilities_parameters.py b/tests/functional/parameters/_boto3/test_utilities_parameters.py index dc7643795c1..bf11dbcf1c4 100644 --- a/tests/functional/parameters/_boto3/test_utilities_parameters.py +++ b/tests/functional/parameters/_boto3/test_utilities_parameters.py @@ -1279,6 +1279,40 @@ def test_ssm_provider_get_parameters_by_name_all_decrypt_should_use_get_paramete stubber.deactivate() +def test_ssm_provider_get_parameters_by_name_does_not_mutate_input_and_honors_new_globals( + mock_name, + mock_value, + config, +): + # GIVEN a reusable parameters dict with a non-empty per-parameter override + param = f"/a/{mock_name}" + fail = "/does_not_exist" # stub model doesn't support all-success yet + all_params = {param: {"max_age": 0}, fail: {}} + names = list(all_params.keys()) + stub_response = build_get_parameters_stub(params={param: mock_value}, invalid_parameters=[fail]) + + provider = parameters.SSMProvider(boto_config=config) + stubber = stub.Stubber(provider.client) + # first call: global decrypt defaults to False -> no WithDecryption + stubber.add_response("get_parameters", stub_response, {"Names": names}) + # second call: new global decrypt=True must be honored + stubber.add_response("get_parameters", stub_response, {"Names": names, "WithDecryption": True}) + stubber.activate() + + # WHEN get_parameters_by_name is called twice with different global options + try: + provider.get_parameters_by_name(parameters=all_params, raise_on_error=False) + + # THEN the caller's dict must not be modified + assert all_params == {param: {"max_age": 0}, fail: {}} + + # AND a later global decrypt=True must not be overridden by stale values + provider.get_parameters_by_name(parameters=all_params, decrypt=True, raise_on_error=False) + stubber.assert_no_pending_responses() + finally: + stubber.deactivate() + + def test_dynamodb_provider_clear_cache(mock_name, mock_value, config): # GIVEN a provider is initialized with a cached value provider = parameters.DynamoDBProvider(table_name="test", boto_config=config)