Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(feature-flags): Add not_in action and rename contains to in #589

Merged
merged 5 commits into from
Aug 10, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 7 additions & 4 deletions aws_lambda_powertools/utilities/feature_flags/feature_flags.py
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 All @@ -65,10 +66,12 @@ def _evaluate_conditions(

for condition in conditions:
context_value = context.get(str(condition.get(schema.CONDITION_KEY)))
cond_action = condition.get(schema.CONDITION_ACTION, "")
cond_value = condition.get(schema.CONDITION_VALUE)
condition_action = condition.get(schema.CONDITION_ACTION, "")
condition_value = condition.get(schema.CONDITION_VALUE)
ran-isenberg marked this conversation as resolved.
Show resolved Hide resolved

if not self._match_by_action(action=cond_action, condition_value=cond_value, context_value=context_value):
if not self._match_by_action(
action=condition_action, condition_value=condition_value, context_value=context_value
):
logger.debug(
f"rule did not match action, rule_name={rule_name}, rule_value={rule_match_value}, "
f"name={feature_name}, context_value={str(context_value)} "
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
heitorlessa marked this conversation as resolved.
Show resolved Hide resolved
* **key**: `str`. Key in given context to perform operation
* **value**: `Any`. Value in given context that should match action operation.

Expand Down
38 changes: 31 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 @@ -310,6 +310,30 @@ def test_flags_no_match_rule_with_contains_action(mocker, config):
toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "6", "username": "a"}, default=False)
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_multiple_features_enabled(mocker, config):
expected_value = ["my_feature", "my_feature2"]
Expand All @@ -321,7 +345,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 +375,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 +488,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