Skip to content

Commit 7c59502

Browse files
leandrodamascenaheitorlessa
authored andcommitted
fix(parameters): AppConfigProvider when retrieving multiple unique configuration names (#2378)
Co-authored-by: heitorlessa <[email protected]>
1 parent 993697c commit 7c59502

File tree

3 files changed

+63
-6
lines changed

3 files changed

+63
-6
lines changed

aws_lambda_powertools/utilities/parameters/appconfig.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ def __init__(
9494
self.environment = environment
9595
self.current_version = ""
9696

97-
self._next_token = "" # nosec - token for get_latest_configuration executions
97+
self._next_token: Dict[str, str] = {} # nosec - token for get_latest_configuration executions
9898
self.last_returned_value = ""
9999

100100
def _get(self, name: str, **sdk_options) -> str:
@@ -108,19 +108,19 @@ def _get(self, name: str, **sdk_options) -> str:
108108
sdk_options: dict, optional
109109
SDK options to propagate to `start_configuration_session` API call
110110
"""
111-
if not self._next_token:
111+
if name not in self._next_token:
112112
sdk_options["ConfigurationProfileIdentifier"] = name
113113
sdk_options["ApplicationIdentifier"] = self.application
114114
sdk_options["EnvironmentIdentifier"] = self.environment
115115
response_configuration = self.client.start_configuration_session(**sdk_options)
116-
self._next_token = response_configuration["InitialConfigurationToken"]
116+
self._next_token[name] = response_configuration["InitialConfigurationToken"]
117117

118118
# The new AppConfig APIs require two API calls to return the configuration
119119
# First we start the session and after that we retrieve the configuration
120120
# We need to store the token to use in the next execution
121-
response = self.client.get_latest_configuration(ConfigurationToken=self._next_token)
121+
response = self.client.get_latest_configuration(ConfigurationToken=self._next_token[name])
122122
return_value = response["Configuration"].read()
123-
self._next_token = response["NextPollConfigurationToken"]
123+
self._next_token[name] = response["NextPollConfigurationToken"]
124124

125125
if return_value:
126126
self.last_returned_value = return_value

docs/utilities/parameters.md

+3
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ You can fetch application configurations in AWS AppConfig using `get_app_config`
9191

9292
The following will retrieve the latest version and store it in the cache.
9393

94+
???+ warning
95+
We make two API calls to fetch each unique configuration name during the first time. This is by design in AppConfig. Please consider adjusting `max_age` parameter to enhance performance.
96+
9497
=== "getting_started_appconfig.py"
9598
```python hl_lines="5 12"
9699
--8<-- "examples/parameters/src/getting_started_appconfig.py"

tests/functional/test_utilities_parameters.py

+55-1
Original file line numberDiff line numberDiff line change
@@ -2171,14 +2171,68 @@ def test_appconf_provider_get_configuration_no_transform(mock_name, config):
21712171
stubber.activate()
21722172

21732173
try:
2174-
value: str = provider.get(mock_name)
2174+
value: bytes = provider.get(mock_name)
21752175
str_value = value.decode("utf-8")
21762176
assert str_value == json.dumps(mock_body_json)
21772177
stubber.assert_no_pending_responses()
21782178
finally:
21792179
stubber.deactivate()
21802180

21812181

2182+
def test_appconf_provider_multiple_unique_config_names(mock_name, config):
2183+
"""
2184+
Test appconfig_provider.get with multiple config names
2185+
"""
2186+
2187+
# GIVEN a provider instance, we should be able to retrieve multiple appconfig profiles.
2188+
environment = "dev"
2189+
application = "myapp"
2190+
provider = parameters.AppConfigProvider(environment=environment, application=application, config=config)
2191+
2192+
mock_body_json_first_call = {"myenvvar1": "Black Panther", "myenvvar2": 3}
2193+
encoded_message_first_call = json.dumps(mock_body_json_first_call).encode("utf-8")
2194+
mock_value_first_call = StreamingBody(BytesIO(encoded_message_first_call), len(encoded_message_first_call))
2195+
2196+
mock_body_json_second_call = {"myenvvar1": "Thor", "myenvvar2": 5}
2197+
encoded_message_second_call = json.dumps(mock_body_json_second_call).encode("utf-8")
2198+
mock_value_second_call = StreamingBody(BytesIO(encoded_message_second_call), len(encoded_message_second_call))
2199+
2200+
# WHEN making two API calls using the same provider instance.
2201+
stubber = stub.Stubber(provider.client)
2202+
2203+
response_get_latest_config_first_call = {
2204+
"Configuration": mock_value_first_call,
2205+
"NextPollConfigurationToken": "initial_token",
2206+
"ContentType": "application/json",
2207+
}
2208+
2209+
response_start_config_session = {"InitialConfigurationToken": "initial_token"}
2210+
stubber.add_response("start_configuration_session", response_start_config_session)
2211+
stubber.add_response("get_latest_configuration", response_get_latest_config_first_call)
2212+
2213+
response_get_latest_config_second_call = {
2214+
"Configuration": mock_value_second_call,
2215+
"NextPollConfigurationToken": "initial_token",
2216+
"ContentType": "application/json",
2217+
}
2218+
response_start_config_session = {"InitialConfigurationToken": "initial_token"}
2219+
stubber.add_response("start_configuration_session", response_start_config_session)
2220+
stubber.add_response("get_latest_configuration", response_get_latest_config_second_call)
2221+
2222+
stubber.activate()
2223+
2224+
try:
2225+
# THEN we should expect different return values.
2226+
value_first_call: bytes = provider.get(mock_name)
2227+
value_second_call: bytes = provider.get(f"{mock_name}_ second_config")
2228+
2229+
assert value_first_call != value_second_call
2230+
stubber.assert_no_pending_responses()
2231+
2232+
finally:
2233+
stubber.deactivate()
2234+
2235+
21822236
def test_appconf_get_app_config_no_transform(monkeypatch, mock_name):
21832237
"""
21842238
Test get_app_config()

0 commit comments

Comments
 (0)