Skip to content

Commit 619c547

Browse files
committed
refactor: rules_context to context
1 parent e9f87dc commit 619c547

File tree

3 files changed

+35
-60
lines changed

3 files changed

+35
-60
lines changed

aws_lambda_powertools/utilities/feature_toggles/configuration_store.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,13 @@ def _match_by_action(self, action: str, condition_value: Any, context_value: Any
3838
self._logger.error(f"caught exception while matching action, action={action}, exception={str(exc)}")
3939
return False
4040

41-
def _is_rule_matched(self, feature_name: str, rule: Dict[str, Any], rules_context: Dict[str, Any]) -> bool:
41+
def _is_rule_matched(self, feature_name: str, rule: Dict[str, Any], context: Dict[str, Any]) -> bool:
4242
rule_name = rule.get(schema.RULE_NAME_KEY, "")
4343
rule_default_value = rule.get(schema.RULE_DEFAULT_VALUE)
4444
conditions = cast(List[Dict], rule.get(schema.CONDITIONS_KEY))
4545

4646
for condition in conditions:
47-
context_value = rules_context.get(str(condition.get(schema.CONDITION_KEY)))
47+
context_value = context.get(str(condition.get(schema.CONDITION_KEY)))
4848
if not self._match_by_action(
4949
condition.get(schema.CONDITION_ACTION, ""),
5050
condition.get(schema.CONDITION_VALUE),
@@ -68,13 +68,13 @@ def _handle_rules(
6868
self,
6969
*,
7070
feature_name: str,
71-
rules_context: Dict[str, Any],
71+
context: Dict[str, Any],
7272
feature_default_value: bool,
7373
rules: List[Dict[str, Any]],
7474
) -> bool:
7575
for rule in rules:
7676
rule_default_value = rule.get(schema.RULE_DEFAULT_VALUE)
77-
if self._is_rule_matched(feature_name, rule, rules_context):
77+
if self._is_rule_matched(feature_name, rule, context):
7878
return bool(rule_default_value)
7979
# no rule matched, return default value of feature
8080
logger.debug(
@@ -104,7 +104,7 @@ def get_configuration(self) -> Dict[str, Any]:
104104
return config
105105

106106
def get_feature_toggle(
107-
self, *, feature_name: str, rules_context: Optional[Dict[str, Any]] = None, value_if_missing: bool
107+
self, *, feature_name: str, context: Optional[Dict[str, Any]] = None, value_if_missing: bool
108108
) -> bool:
109109
"""Get a feature toggle boolean value. Value is calculated according to a set of rules and conditions.
110110
@@ -114,7 +114,7 @@ def get_feature_toggle(
114114
----------
115115
feature_name: str
116116
feature name that you wish to fetch
117-
rules_context: Optional[Dict[str, Any]]
117+
context: Optional[Dict[str, Any]]
118118
dict of attributes that you would like to match the rules
119119
against, can be {'tenant_id: 'X', 'username':' 'Y', 'region': 'Z'} etc.
120120
value_if_missing: bool
@@ -132,8 +132,8 @@ def get_feature_toggle(
132132
the defined feature
133133
3. feature exists and a rule matches -> rule_default_value of rule is returned
134134
"""
135-
if rules_context is None:
136-
rules_context = {}
135+
if context is None:
136+
context = {}
137137

138138
try:
139139
toggles_dict: Dict[str, Any] = self.get_configuration()
@@ -164,18 +164,18 @@ def get_feature_toggle(
164164
)
165165
return self._handle_rules(
166166
feature_name=feature_name,
167-
rules_context=rules_context,
167+
context=context,
168168
feature_default_value=bool(feature_default_value),
169169
rules=cast(List, rules_list),
170170
)
171171

172-
def get_all_enabled_feature_toggles(self, *, rules_context: Optional[Dict[str, Any]] = None) -> List[str]:
172+
def get_all_enabled_feature_toggles(self, *, context: Optional[Dict[str, Any]] = None) -> List[str]:
173173
"""Get all enabled feature toggles while also taking into account rule_context
174174
(when a feature has defined rules)
175175
176176
Parameters
177177
----------
178-
rules_context: Optional[Dict[str, Any]]
178+
context: Optional[Dict[str, Any]]
179179
dict of attributes that you would like to match the rules
180180
against, can be `{'tenant_id: 'X', 'username':' 'Y', 'region': 'Z'}` etc.
181181
@@ -185,8 +185,8 @@ def get_all_enabled_feature_toggles(self, *, rules_context: Optional[Dict[str, A
185185
a list of all features name that are enabled by also taking into account
186186
rule_context (when a feature has defined rules)
187187
"""
188-
if rules_context is None:
189-
rules_context = {}
188+
if context is None:
189+
context = {}
190190

191191
try:
192192
toggles_dict: Dict[str, Any] = self.get_configuration()
@@ -206,7 +206,7 @@ def get_all_enabled_feature_toggles(self, *, rules_context: Optional[Dict[str, A
206206
ret_list.append(feature_name)
207207
elif self._handle_rules(
208208
feature_name=feature_name,
209-
rules_context=rules_context,
209+
context=context,
210210
feature_default_value=feature_default_value,
211211
rules=rules_list,
212212
):

docs/utilities/feature_flags.md

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -56,24 +56,15 @@ By default, this utility provides AWS AppConfig as a configuration store. As suc
5656

5757
> NOTE: Share example on how customers can unit test their feature flags
5858
59-
## TODO
60-
61-
* Review param names and UX
62-
- Deep dive on naming - ConfigurationStore vs SchemaFetcher vs Schema
63-
- How can we make it easier to get started
64-
* Getting started
65-
- Sample Infrastructure
66-
- Quickest way to start --- defaults, built-ins
67-
* Advanced section
68-
- Bring your own
69-
7059
## Changes
7160

7261
Potential changes to be validated when docs are in a better shape
7362

74-
- [ ] `rules_context` to `context`
63+
- [x] ~~`rules_context` to `context`~~
7564
- [ ] `ConfigurationStore` to `FeatureFlags`
7665
- [ ] `SchemaFetcher` to `StoreProvider`
7766
- [ ] Use `base.py` for interfaces for consistency (e.g. Metrics, Tracer, etc.)
7867
- [ ] Some docstrings and logger refer to AWS AppConfig only (outdated given SchemaFetcher)
79-
- [ ]
68+
- [ ] Review why we're testing a private method(`is_rule_matched`)
69+
70+
**Q: Why is `get_configuration()` public?**

tests/functional/feature_toggles/test_feature_toggles.py

Lines changed: 17 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def init_fetcher_side_effect(mocker, config: Config, side_effect) -> AppConfigFe
4343

4444

4545
# this test checks that we get correct value of feature that exists in the schema.
46-
# we also don't send an empty rules_context dict in this case
46+
# we also don't send an empty context dict in this case
4747
def test_toggles_rule_does_not_match(mocker, config):
4848
expected_value = True
4949
mocked_app_config_schema = {
@@ -68,7 +68,7 @@ def test_toggles_rule_does_not_match(mocker, config):
6868
}
6969

7070
conf_store = init_configuration_store(mocker, mocked_app_config_schema, config)
71-
toggle = conf_store.get_feature_toggle(feature_name="my_feature", rules_context={}, value_if_missing=False)
71+
toggle = conf_store.get_feature_toggle(feature_name="my_feature", context={}, value_if_missing=False)
7272
assert toggle == expected_value
7373

7474

@@ -79,18 +79,18 @@ def test_toggles_no_conditions_feature_does_not_exist(mocker, config):
7979
mocked_app_config_schema = {"features": {"my_fake_feature": {"feature_default_value": True}}}
8080

8181
conf_store = init_configuration_store(mocker, mocked_app_config_schema, config)
82-
toggle = conf_store.get_feature_toggle(feature_name="my_feature", rules_context={}, value_if_missing=expected_value)
82+
toggle = conf_store.get_feature_toggle(feature_name="my_feature", context={}, value_if_missing=expected_value)
8383
assert toggle == expected_value
8484

8585

86-
# check that feature match works when they are no rules and we send rules_context.
86+
# check that feature match works when they are no rules and we send context.
8787
# default value is False but the feature has a True default_value.
8888
def test_toggles_no_rules(mocker, config):
8989
expected_value = True
9090
mocked_app_config_schema = {"features": {"my_feature": {"feature_default_value": expected_value}}}
9191
conf_store = init_configuration_store(mocker, mocked_app_config_schema, config)
9292
toggle = conf_store.get_feature_toggle(
93-
feature_name="my_feature", rules_context={"tenant_id": "6", "username": "a"}, value_if_missing=False
93+
feature_name="my_feature", context={"tenant_id": "6", "username": "a"}, value_if_missing=False
9494
)
9595
assert toggle == expected_value
9696

@@ -120,9 +120,7 @@ def test_toggles_conditions_no_match(mocker, config):
120120
}
121121
conf_store = init_configuration_store(mocker, mocked_app_config_schema, config)
122122
toggle = conf_store.get_feature_toggle(
123-
feature_name="my_feature",
124-
rules_context={"tenant_id": "6", "username": "a"}, # rule will not match
125-
value_if_missing=False,
123+
feature_name="my_feature", context={"tenant_id": "6", "username": "a"}, value_if_missing=False
126124
)
127125
assert toggle == expected_value
128126

@@ -160,7 +158,7 @@ def test_toggles_conditions_rule_match_equal_multiple_conditions(mocker, config)
160158
conf_store = init_configuration_store(mocker, mocked_app_config_schema, config)
161159
toggle = conf_store.get_feature_toggle(
162160
feature_name="my_feature",
163-
rules_context={
161+
context={
164162
"tenant_id": tenant_id_val,
165163
"username": username_val,
166164
},
@@ -201,7 +199,7 @@ def test_toggles_conditions_no_rule_match_equal_multiple_conditions(mocker, conf
201199
}
202200
conf_store = init_configuration_store(mocker, mocked_app_config_schema, config)
203201
toggle = conf_store.get_feature_toggle(
204-
feature_name="my_feature", rules_context={"tenant_id": "6", "username": "a"}, value_if_missing=False
202+
feature_name="my_feature", context={"tenant_id": "6", "username": "a"}, value_if_missing=False
205203
)
206204
assert toggle == expected_val
207205

@@ -262,29 +260,23 @@ def test_toggles_conditions_rule_match_multiple_actions_multiple_rules_multiple_
262260
conf_store = init_configuration_store(mocker, mocked_app_config_schema, config)
263261
# match first rule
264262
toggle = conf_store.get_feature_toggle(
265-
feature_name="my_feature",
266-
rules_context={"tenant_id": "6", "username": "abcd"},
267-
value_if_missing=False,
263+
feature_name="my_feature", context={"tenant_id": "6", "username": "abcd"}, value_if_missing=False
268264
)
269265
assert toggle == expected_value_first_check
270266
# match second rule
271267
toggle = conf_store.get_feature_toggle(
272-
feature_name="my_feature",
273-
rules_context={"tenant_id": "4446", "username": "az"},
274-
value_if_missing=False,
268+
feature_name="my_feature", context={"tenant_id": "4446", "username": "az"}, value_if_missing=False
275269
)
276270
assert toggle == expected_value_second_check
277271
# match no rule
278272
toggle = conf_store.get_feature_toggle(
279-
feature_name="my_feature",
280-
rules_context={"tenant_id": "11114446", "username": "ab"},
281-
value_if_missing=False,
273+
feature_name="my_feature", context={"tenant_id": "11114446", "username": "ab"}, value_if_missing=False
282274
)
283275
assert toggle == expected_value_third_check
284276
# feature doesn't exist
285277
toggle = conf_store.get_feature_toggle(
286278
feature_name="my_fake_feature",
287-
rules_context={"tenant_id": "11114446", "username": "ab"},
279+
context={"tenant_id": "11114446", "username": "ab"},
288280
value_if_missing=expected_value_fourth_case,
289281
)
290282
assert toggle == expected_value_fourth_case
@@ -315,9 +307,7 @@ def test_toggles_match_rule_with_contains_action(mocker, config):
315307
}
316308
conf_store = init_configuration_store(mocker, mocked_app_config_schema, config)
317309
toggle = conf_store.get_feature_toggle(
318-
feature_name="my_feature",
319-
rules_context={"tenant_id": "6", "username": "a"}, # rule will match
320-
value_if_missing=False,
310+
feature_name="my_feature", context={"tenant_id": "6", "username": "a"}, value_if_missing=False
321311
)
322312
assert toggle == expected_value
323313

@@ -346,9 +336,7 @@ def test_toggles_no_match_rule_with_contains_action(mocker, config):
346336
}
347337
conf_store = init_configuration_store(mocker, mocked_app_config_schema, config)
348338
toggle = conf_store.get_feature_toggle(
349-
feature_name="my_feature",
350-
rules_context={"tenant_id": "6", "username": "a"}, # rule will not match
351-
value_if_missing=False,
339+
feature_name="my_feature", context={"tenant_id": "6", "username": "a"}, value_if_missing=False
352340
)
353341
assert toggle == expected_value
354342

@@ -379,9 +367,7 @@ def test_multiple_features_enabled(mocker, config):
379367
},
380368
}
381369
conf_store = init_configuration_store(mocker, mocked_app_config_schema, config)
382-
enabled_list: List[str] = conf_store.get_all_enabled_feature_toggles(
383-
rules_context={"tenant_id": "6", "username": "a"}
384-
)
370+
enabled_list: List[str] = conf_store.get_all_enabled_feature_toggles(context={"tenant_id": "6", "username": "a"})
385371
assert enabled_list == expected_value
386372

387373

@@ -430,9 +416,7 @@ def test_multiple_features_only_some_enabled(mocker, config):
430416
},
431417
}
432418
conf_store = init_configuration_store(mocker, mocked_app_config_schema, config)
433-
enabled_list: List[str] = conf_store.get_all_enabled_feature_toggles(
434-
rules_context={"tenant_id": "6", "username": "a"}
435-
)
419+
enabled_list: List[str] = conf_store.get_all_enabled_feature_toggles(context={"tenant_id": "6", "username": "a"})
436420
assert enabled_list == expected_value
437421

438422

@@ -454,7 +438,7 @@ def test_get_all_enabled_feature_toggles_handles_error(mocker, config):
454438
conf_store = ConfigurationStore(schema_fetcher)
455439

456440
# WHEN calling get_all_enabled_feature_toggles
457-
toggles = conf_store.get_all_enabled_feature_toggles(rules_context=None)
441+
toggles = conf_store.get_all_enabled_feature_toggles(context=None)
458442

459443
# THEN handle the error and return an empty list
460444
assert toggles == []

0 commit comments

Comments
 (0)