diff --git a/aws_lambda_powertools/utilities/feature_flags/feature_flags.py b/aws_lambda_powertools/utilities/feature_flags/feature_flags.py index 5a91ba26840..c66feee0536 100644 --- a/aws_lambda_powertools/utilities/feature_flags/feature_flags.py +++ b/aws_lambda_powertools/utilities/feature_flags/feature_flags.py @@ -45,6 +45,11 @@ def _match_by_action(self, action: str, condition_value: Any, context_value: Any return False mapping_by_action = { schema.RuleAction.EQUALS.value: lambda a, b: a == b, + schema.RuleAction.NOT_EQUALS.value: lambda a, b: a != b, + schema.RuleAction.KEY_GREATER_THAN_VALUE.value: lambda a, b: a > b, + schema.RuleAction.KEY_GREATER_THAN_OR_EQUAL_VALUE.value: lambda a, b: a >= b, + schema.RuleAction.KEY_LESS_THAN_VALUE.value: lambda a, b: a < b, + schema.RuleAction.KEY_LESS_THAN_OR_EQUAL_VALUE.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.IN.value: lambda a, b: a in b, diff --git a/aws_lambda_powertools/utilities/feature_flags/schema.py b/aws_lambda_powertools/utilities/feature_flags/schema.py index fc745342750..6a92508676e 100644 --- a/aws_lambda_powertools/utilities/feature_flags/schema.py +++ b/aws_lambda_powertools/utilities/feature_flags/schema.py @@ -17,6 +17,11 @@ class RuleAction(str, Enum): EQUALS = "EQUALS" + NOT_EQUALS = "NOT_EQUALS" + KEY_GREATER_THAN_VALUE = "KEY_GREATER_THAN_VALUE" + KEY_GREATER_THAN_OR_EQUAL_VALUE = "KEY_GREATER_THAN_OR_EQUAL_VALUE" + KEY_LESS_THAN_VALUE = "KEY_LESS_THAN_VALUE" + KEY_LESS_THAN_OR_EQUAL_VALUE = "KEY_LESS_THAN_OR_EQUAL_VALUE" STARTSWITH = "STARTSWITH" ENDSWITH = "ENDSWITH" IN = "IN" diff --git a/docs/utilities/feature_flags.md b/docs/utilities/feature_flags.md index 7e08cc358dd..816aac8b817 100644 --- a/docs/utilities/feature_flags.md +++ b/docs/utilities/feature_flags.md @@ -455,6 +455,11 @@ The `action` configuration can have the following values, where the expressions Action | Equivalent expression ------------------------------------------------- | --------------------------------------------------------------------------------- **EQUALS** | `lambda a, b: a == b` +**NOT_EQUALS** | `lambda a, b: a != b` +**KEY_GREATER_THAN_VALUE** | `lambda a, b: a > b` +**KEY_GREATER_THAN_OR_EQUAL_VALUE** | `lambda a, b: a >= b` +**KEY_LESS_THAN_VALUE** | `lambda a, b: a < b` +**KEY_LESS_THAN_OR_EQUAL_VALUE** | `lambda a, b: a <= b` **STARTSWITH** | `lambda a, b: a.startswith(b)` **ENDSWITH** | `lambda a, b: a.endswith(b)` **KEY_IN_VALUE** | `lambda a, b: a in b` diff --git a/tests/functional/feature_flags/test_feature_flags.py b/tests/functional/feature_flags/test_feature_flags.py index 73702de93b2..c421cc85423 100644 --- a/tests/functional/feature_flags/test_feature_flags.py +++ b/tests/functional/feature_flags/test_feature_flags.py @@ -802,3 +802,338 @@ def test_get_configuration_with_envelope_and_raw(mocker, config): assert "log_level" in config assert "log_level" not in features_config + +## +## Inequality test cases +## + +# Test not equals +def test_flags_not_equal_no_match(mocker, config): + expected_value = False + mocked_app_config_schema = { + "my_feature": { + "default": expected_value, + "rules": { + "tenant id not equals 345345435": { + "when_match": True, + "conditions": [ + { + "action": RuleAction.NOT_EQUALS.value, + "key": "tenant_id", + "value": "345345435", + } + ], + } + }, + } + } + feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config) + toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "345345435", "username": "a"}, default=False) + assert toggle == expected_value + +def test_flags_not_equal_match(mocker, config): + expected_value = True + mocked_app_config_schema = { + "my_feature": { + "default": expected_value, + "rules": { + "tenant id not equals 345345435": { + "when_match": True, + "conditions": [ + { + "action": RuleAction.NOT_EQUALS.value, + "key": "tenant_id", + "value": "345345435", + } + ], + } + }, + } + } + feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config) + toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "", "username": "a"}, default=False) + assert toggle == expected_value + + +# Test less than +def test_flags_less_than_no_match_1(mocker, config): + expected_value = False + mocked_app_config_schema = { + "my_feature": { + "default": expected_value, + "rules": { + "Date less than 2021.10.31": { + "when_match": True, + "conditions": [ + { + "action": RuleAction.KEY_LESS_THAN_VALUE.value, + "key": "current_date", + "value": "2021.10.31", + } + ], + } + }, + } + } + feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config) + toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "345345435", "username": "a", "current_date": "2021.12.25"}, default=False) + assert toggle == expected_value + +def test_flags_less_than_no_match_2(mocker, config): + expected_value = False + mocked_app_config_schema = { + "my_feature": { + "default": expected_value, + "rules": { + "Date less than 2021.10.31": { + "when_match": True, + "conditions": [ + { + "action": RuleAction.KEY_LESS_THAN_VALUE.value, + "key": "current_date", + "value": "2021.10.31", + } + ], + } + }, + } + } + feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config) + toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "345345435", "username": "a", "current_date": "2021.10.31"}, default=False) + assert toggle == expected_value + +def test_flags_less_than_match(mocker, config): + expected_value = True + mocked_app_config_schema = { + "my_feature": { + "default": expected_value, + "rules": { + "Date less than 2021.10.31": { + "when_match": True, + "conditions": [ + { + "action": RuleAction.KEY_LESS_THAN_VALUE.value, + "key": "current_date", + "value": "2021.10.31", + } + ], + } + }, + } + } + feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config) + toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "345345435", "username": "a", "current_date": "2021.04.01"}, default=False) + assert toggle == expected_value + +# Test less than or equal to +def test_flags_less_than_or_equal_no_match(mocker, config): + expected_value = False + mocked_app_config_schema = { + "my_feature": { + "default": expected_value, + "rules": { + "Date less than or equal 2021.10.31": { + "when_match": True, + "conditions": [ + { + "action": RuleAction.KEY_LESS_THAN_OR_EQUAL_VALUE.value, + "key": "current_date", + "value": "2021.10.31", + } + ], + } + }, + } + } + feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config) + toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "345345435", "username": "a", "current_date": "2021.12.25"}, default=False) + assert toggle == expected_value + +def test_flags_less_than_or_equal_match_1(mocker, config): + expected_value = True + mocked_app_config_schema = { + "my_feature": { + "default": expected_value, + "rules": { + "Date less than or equal 2021.10.31": { + "when_match": True, + "conditions": [ + { + "action": RuleAction.KEY_LESS_THAN_OR_EQUAL_VALUE.value, + "key": "current_date", + "value": "2021.10.31", + } + ], + } + }, + } + } + feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config) + toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "345345435", "username": "a", "current_date": "2021.04.01"}, default=False) + assert toggle == expected_value + + +def test_flags_less_than_or_equal_match_2(mocker, config): + expected_value = True + mocked_app_config_schema = { + "my_feature": { + "default": expected_value, + "rules": { + "Date less than or equal 2021.10.31": { + "when_match": True, + "conditions": [ + { + "action": RuleAction.KEY_LESS_THAN_OR_EQUAL_VALUE.value, + "key": "current_date", + "value": "2021.10.31", + } + ], + } + }, + } + } + feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config) + toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "345345435", "username": "a", "current_date": "2021.10.31"}, default=False) + assert toggle == expected_value + +# Test greater than +def test_flags_greater_than_no_match_1(mocker, config): + expected_value = False + mocked_app_config_schema = { + "my_feature": { + "default": expected_value, + "rules": { + "Date greater than 2021.10.31": { + "when_match": True, + "conditions": [ + { + "action": RuleAction.KEY_GREATER_THAN_VALUE.value, + "key": "current_date", + "value": "2021.10.31", + } + ], + } + }, + } + } + feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config) + toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "345345435", "username": "a", "current_date": "2021.04.01"}, default=False) + assert toggle == expected_value + +def test_flags_greater_than_no_match_2(mocker, config): + expected_value = False + mocked_app_config_schema = { + "my_feature": { + "default": expected_value, + "rules": { + "Date greater than 2021.10.31": { + "when_match": True, + "conditions": [ + { + "action": RuleAction.KEY_GREATER_THAN_VALUE.value, + "key": "current_date", + "value": "2021.10.31", + } + ], + } + }, + } + } + feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config) + toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "345345435", "username": "a", "current_date": "2021.10.31"}, default=False) + assert toggle == expected_value + +def test_flags_greater_than_match(mocker, config): + expected_value = True + mocked_app_config_schema = { + "my_feature": { + "default": expected_value, + "rules": { + "Date greater than 2021.10.31": { + "when_match": True, + "conditions": [ + { + "action": RuleAction.KEY_GREATER_THAN_VALUE.value, + "key": "current_date", + "value": "2021.10.31", + } + ], + } + }, + } + } + feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config) + toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "345345435", "username": "a", "current_date": "2021.12.25"}, default=False) + assert toggle == expected_value + +# Test greater than or equal to +def test_flags_greater_than_or_equal_no_match(mocker, config): + expected_value = False + mocked_app_config_schema = { + "my_feature": { + "default": expected_value, + "rules": { + "Date greater than or equal 2021.10.31": { + "when_match": True, + "conditions": [ + { + "action": RuleAction.KEY_GREATER_THAN_OR_EQUAL_VALUE.value, + "key": "current_date", + "value": "2021.10.31", + } + ], + } + }, + } + } + feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config) + toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "345345435", "username": "a", "current_date": "2021.04.01"}, default=False) + assert toggle == expected_value + +def test_flags_greater_than_or_equal_match_1(mocker, config): + expected_value = True + mocked_app_config_schema = { + "my_feature": { + "default": expected_value, + "rules": { + "Date greater than or equal 2021.10.31": { + "when_match": True, + "conditions": [ + { + "action": RuleAction.KEY_GREATER_THAN_OR_EQUAL_VALUE.value, + "key": "current_date", + "value": "2021.10.31", + } + ], + } + }, + } + } + feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config) + toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "345345435", "username": "a", "current_date": "2021.12.25"}, default=False) + assert toggle == expected_value + + +def test_flags_greater_than_or_equal_match_2(mocker, config): + expected_value = True + mocked_app_config_schema = { + "my_feature": { + "default": expected_value, + "rules": { + "Date greater than or equal 2021.10.31": { + "when_match": True, + "conditions": [ + { + "action": RuleAction.KEY_GREATER_THAN_OR_EQUAL_VALUE.value, + "key": "current_date", + "value": "2021.10.31", + } + ], + } + }, + } + } + feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config) + toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "345345435", "username": "a", "current_date": "2021.10.31"}, default=False) + assert toggle == expected_value +