Skip to content

Commit 8b8b8b3

Browse files
author
Ran Isenberg
committed
feat (feature flags): Add not_in action and rename contains to in
1 parent 4eb4aaa commit 8b8b8b3

File tree

4 files changed

+47
-14
lines changed

4 files changed

+47
-14
lines changed

aws_lambda_powertools/utilities/feature_flags/feature_flags.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@ def _match_by_action(action: str, condition_value: Any, context_value: Any) -> b
4646
schema.RuleAction.EQUALS.value: lambda a, b: a == b,
4747
schema.RuleAction.STARTSWITH.value: lambda a, b: a.startswith(b),
4848
schema.RuleAction.ENDSWITH.value: lambda a, b: a.endswith(b),
49-
schema.RuleAction.CONTAINS.value: lambda a, b: a in b,
49+
schema.RuleAction.IN.value: lambda a, b: a in b,
50+
schema.RuleAction.NOT_IN.value: lambda a, b: a not in b,
5051
}
5152

5253
try:
@@ -65,10 +66,12 @@ def _evaluate_conditions(
6566

6667
for condition in conditions:
6768
context_value = context.get(str(condition.get(schema.CONDITION_KEY)))
68-
cond_action = condition.get(schema.CONDITION_ACTION, "")
69-
cond_value = condition.get(schema.CONDITION_VALUE)
69+
condition_action = condition.get(schema.CONDITION_ACTION, "")
70+
condition_value = condition.get(schema.CONDITION_VALUE)
7071

71-
if not self._match_by_action(action=cond_action, condition_value=cond_value, context_value=context_value):
72+
if not self._match_by_action(
73+
action=condition_action, condition_value=condition_value, context_value=context_value
74+
):
7275
logger.debug(
7376
f"rule did not match action, rule_name={rule_name}, rule_value={rule_match_value}, "
7477
f"name={feature_name}, context_value={str(context_value)} "

aws_lambda_powertools/utilities/feature_flags/schema.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ class RuleAction(str, Enum):
2020
EQUALS = "EQUALS"
2121
STARTSWITH = "STARTSWITH"
2222
ENDSWITH = "ENDSWITH"
23-
CONTAINS = "CONTAINS"
23+
IN = "IN"
24+
NOT_IN = "NOT_IN"
2425

2526

2627
class SchemaValidator(BaseValidator):
@@ -79,7 +80,7 @@ class SchemaValidator(BaseValidator):
7980
The value MUST contain the following members:
8081
8182
* **action**: `str`. Operation to perform to match a key and value.
82-
The value MUST be either EQUALS, STARTSWITH, ENDSWITH, CONTAINS
83+
The value MUST be either EQUALS, STARTSWITH, ENDSWITH, IN
8384
* **key**: `str`. Key in given context to perform operation
8485
* **value**: `Any`. Value in given context that should match action operation.
8586

tests/functional/feature_flags/test_feature_flags.py

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ def test_flags_conditions_rule_match_multiple_actions_multiple_rules_multiple_co
263263

264264

265265
# check a case where the feature exists but the rule doesn't match so we revert to the default value of the feature
266-
def test_flags_match_rule_with_contains_action(mocker, config):
266+
def test_flags_match_rule_with_in_action(mocker, config):
267267
expected_value = True
268268
mocked_app_config_schema = {
269269
"my_feature": {
@@ -273,7 +273,7 @@ def test_flags_match_rule_with_contains_action(mocker, config):
273273
"when_match": expected_value,
274274
"conditions": [
275275
{
276-
"action": RuleAction.CONTAINS.value,
276+
"action": RuleAction.IN.value,
277277
"key": "tenant_id",
278278
"value": ["6", "2"],
279279
}
@@ -287,7 +287,7 @@ def test_flags_match_rule_with_contains_action(mocker, config):
287287
assert toggle == expected_value
288288

289289

290-
def test_flags_no_match_rule_with_contains_action(mocker, config):
290+
def test_flags_no_match_rule_with_in_action(mocker, config):
291291
expected_value = False
292292
mocked_app_config_schema = {
293293
"my_feature": {
@@ -297,7 +297,7 @@ def test_flags_no_match_rule_with_contains_action(mocker, config):
297297
"when_match": True,
298298
"conditions": [
299299
{
300-
"action": RuleAction.CONTAINS.value,
300+
"action": RuleAction.IN.value,
301301
"key": "tenant_id",
302302
"value": ["8", "2"],
303303
}
@@ -310,6 +310,30 @@ def test_flags_no_match_rule_with_contains_action(mocker, config):
310310
toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "6", "username": "a"}, default=False)
311311
assert toggle == expected_value
312312

313+
def test_flags_match_rule_with_not_in_action(mocker, config):
314+
expected_value = True
315+
mocked_app_config_schema = {
316+
"my_feature": {
317+
"default": False,
318+
"rules": {
319+
"tenant id is contained in [8, 2]": {
320+
"when_match": expected_value,
321+
"conditions": [
322+
{
323+
"action": RuleAction.NOT_IN.value,
324+
"key": "tenant_id",
325+
"value": ["10", "4"],
326+
}
327+
],
328+
}
329+
},
330+
}
331+
}
332+
feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config)
333+
toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "6", "username": "a"}, default=False)
334+
assert toggle == expected_value
335+
336+
313337

314338
def test_multiple_features_enabled(mocker, config):
315339
expected_value = ["my_feature", "my_feature2"]
@@ -321,7 +345,7 @@ def test_multiple_features_enabled(mocker, config):
321345
"when_match": True,
322346
"conditions": [
323347
{
324-
"action": RuleAction.CONTAINS.value,
348+
"action": RuleAction.IN.value,
325349
"key": "tenant_id",
326350
"value": ["6", "2"],
327351
}
@@ -351,7 +375,7 @@ def test_multiple_features_only_some_enabled(mocker, config):
351375
"when_match": True,
352376
"conditions": [
353377
{
354-
"action": RuleAction.CONTAINS.value,
378+
"action": RuleAction.IN.value,
355379
"key": "tenant_id",
356380
"value": ["6", "2"],
357381
}
@@ -464,7 +488,7 @@ def test_features_jmespath_envelope(mocker, config):
464488
assert toggle == expected_value
465489

466490

467-
# test_match_rule_with_contains_action
491+
# test_match_rule_with_equals_action
468492
def test_match_condition_with_dict_value(mocker, config):
469493
expected_value = True
470494
mocked_app_config_schema = {

tests/functional/feature_flags/test_schema_validation.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,10 +211,15 @@ def test_valid_condition_all_actions():
211211
CONDITION_VALUE: "a",
212212
},
213213
{
214-
CONDITION_ACTION: RuleAction.CONTAINS.value,
214+
CONDITION_ACTION: RuleAction.IN.value,
215215
CONDITION_KEY: "username",
216216
CONDITION_VALUE: ["a", "b"],
217217
},
218+
{
219+
CONDITION_ACTION: RuleAction.NOT_IN.value,
220+
CONDITION_KEY: "username",
221+
CONDITION_VALUE: ["c"],
222+
},
218223
],
219224
}
220225
},

0 commit comments

Comments
 (0)