Skip to content

Commit

Permalink
feat(feature flags): Add not_in action and rename contains to in (#589)
Browse files Browse the repository at this point in the history
Co-authored-by: Heitor Lessa <heitor.lessa@hotmail.com>
  • Loading branch information
ran-isenberg and heitorlessa committed Aug 10, 2021
1 parent d07a3e1 commit 6952d83
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ def _match_by_action(action: str, condition_value: Any, context_value: Any) -> b
schema.RuleAction.EQUALS.value: lambda a, b: a == b,
schema.RuleAction.STARTSWITH.value: lambda a, b: a.startswith(b),
schema.RuleAction.ENDSWITH.value: lambda a, b: a.endswith(b),
schema.RuleAction.CONTAINS.value: lambda a, b: a in b,
schema.RuleAction.IN.value: lambda a, b: a in b,
schema.RuleAction.NOT_IN.value: lambda a, b: a not in b,
}

try:
Expand Down
5 changes: 3 additions & 2 deletions aws_lambda_powertools/utilities/feature_flags/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ class RuleAction(str, Enum):
EQUALS = "EQUALS"
STARTSWITH = "STARTSWITH"
ENDSWITH = "ENDSWITH"
CONTAINS = "CONTAINS"
IN = "IN"
NOT_IN = "NOT_IN"


class SchemaValidator(BaseValidator):
Expand Down Expand Up @@ -79,7 +80,7 @@ class SchemaValidator(BaseValidator):
The value MUST contain the following members:
* **action**: `str`. Operation to perform to match a key and value.
The value MUST be either EQUALS, STARTSWITH, ENDSWITH, CONTAINS
The value MUST be either EQUALS, STARTSWITH, ENDSWITH, IN, NOT_IN
* **key**: `str`. Key in given context to perform operation
* **value**: `Any`. Value in given context that should match action operation.
Expand Down
62 changes: 55 additions & 7 deletions tests/functional/feature_flags/test_feature_flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ def test_flags_conditions_rule_match_multiple_actions_multiple_rules_multiple_co


# check a case where the feature exists but the rule doesn't match so we revert to the default value of the feature
def test_flags_match_rule_with_contains_action(mocker, config):
def test_flags_match_rule_with_in_action(mocker, config):
expected_value = True
mocked_app_config_schema = {
"my_feature": {
Expand All @@ -273,7 +273,7 @@ def test_flags_match_rule_with_contains_action(mocker, config):
"when_match": expected_value,
"conditions": [
{
"action": RuleAction.CONTAINS.value,
"action": RuleAction.IN.value,
"key": "tenant_id",
"value": ["6", "2"],
}
Expand All @@ -287,7 +287,7 @@ def test_flags_match_rule_with_contains_action(mocker, config):
assert toggle == expected_value


def test_flags_no_match_rule_with_contains_action(mocker, config):
def test_flags_no_match_rule_with_in_action(mocker, config):
expected_value = False
mocked_app_config_schema = {
"my_feature": {
Expand All @@ -297,7 +297,7 @@ def test_flags_no_match_rule_with_contains_action(mocker, config):
"when_match": True,
"conditions": [
{
"action": RuleAction.CONTAINS.value,
"action": RuleAction.IN.value,
"key": "tenant_id",
"value": ["8", "2"],
}
Expand All @@ -311,6 +311,54 @@ def test_flags_no_match_rule_with_contains_action(mocker, config):
assert toggle == expected_value


def test_flags_match_rule_with_not_in_action(mocker, config):
expected_value = True
mocked_app_config_schema = {
"my_feature": {
"default": False,
"rules": {
"tenant id is contained in [8, 2]": {
"when_match": expected_value,
"conditions": [
{
"action": RuleAction.NOT_IN.value,
"key": "tenant_id",
"value": ["10", "4"],
}
],
}
},
}
}
feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config)
toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "6", "username": "a"}, default=False)
assert toggle == expected_value


def test_flags_no_match_rule_with_not_in_action(mocker, config):
expected_value = False
mocked_app_config_schema = {
"my_feature": {
"default": expected_value,
"rules": {
"tenant id is contained in [8, 2]": {
"when_match": True,
"conditions": [
{
"action": RuleAction.NOT_IN.value,
"key": "tenant_id",
"value": ["6", "4"],
}
],
}
},
}
}
feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config)
toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "6", "username": "a"}, default=False)
assert toggle == expected_value


def test_multiple_features_enabled(mocker, config):
expected_value = ["my_feature", "my_feature2"]
mocked_app_config_schema = {
Expand All @@ -321,7 +369,7 @@ def test_multiple_features_enabled(mocker, config):
"when_match": True,
"conditions": [
{
"action": RuleAction.CONTAINS.value,
"action": RuleAction.IN.value,
"key": "tenant_id",
"value": ["6", "2"],
}
Expand Down Expand Up @@ -351,7 +399,7 @@ def test_multiple_features_only_some_enabled(mocker, config):
"when_match": True,
"conditions": [
{
"action": RuleAction.CONTAINS.value,
"action": RuleAction.IN.value,
"key": "tenant_id",
"value": ["6", "2"],
}
Expand Down Expand Up @@ -464,7 +512,7 @@ def test_features_jmespath_envelope(mocker, config):
assert toggle == expected_value


# test_match_rule_with_contains_action
# test_match_rule_with_equals_action
def test_match_condition_with_dict_value(mocker, config):
expected_value = True
mocked_app_config_schema = {
Expand Down
7 changes: 6 additions & 1 deletion tests/functional/feature_flags/test_schema_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,15 @@ def test_valid_condition_all_actions():
CONDITION_VALUE: "a",
},
{
CONDITION_ACTION: RuleAction.CONTAINS.value,
CONDITION_ACTION: RuleAction.IN.value,
CONDITION_KEY: "username",
CONDITION_VALUE: ["a", "b"],
},
{
CONDITION_ACTION: RuleAction.NOT_IN.value,
CONDITION_KEY: "username",
CONDITION_VALUE: ["c"],
},
],
}
},
Expand Down

0 comments on commit 6952d83

Please sign in to comment.