Skip to content

Commit 922ecf2

Browse files
authored
feat(feature_flags): Added inequality conditions (#721)
1 parent 47ae544 commit 922ecf2

File tree

4 files changed

+350
-0
lines changed

4 files changed

+350
-0
lines changed

aws_lambda_powertools/utilities/feature_flags/feature_flags.py

+5
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ def _match_by_action(self, action: str, condition_value: Any, context_value: Any
4545
return False
4646
mapping_by_action = {
4747
schema.RuleAction.EQUALS.value: lambda a, b: a == b,
48+
schema.RuleAction.NOT_EQUALS.value: lambda a, b: a != b,
49+
schema.RuleAction.KEY_GREATER_THAN_VALUE.value: lambda a, b: a > b,
50+
schema.RuleAction.KEY_GREATER_THAN_OR_EQUAL_VALUE.value: lambda a, b: a >= b,
51+
schema.RuleAction.KEY_LESS_THAN_VALUE.value: lambda a, b: a < b,
52+
schema.RuleAction.KEY_LESS_THAN_OR_EQUAL_VALUE.value: lambda a, b: a <= b,
4853
schema.RuleAction.STARTSWITH.value: lambda a, b: a.startswith(b),
4954
schema.RuleAction.ENDSWITH.value: lambda a, b: a.endswith(b),
5055
schema.RuleAction.IN.value: lambda a, b: a in b,

aws_lambda_powertools/utilities/feature_flags/schema.py

+5
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,11 @@
1717

1818
class RuleAction(str, Enum):
1919
EQUALS = "EQUALS"
20+
NOT_EQUALS = "NOT_EQUALS"
21+
KEY_GREATER_THAN_VALUE = "KEY_GREATER_THAN_VALUE"
22+
KEY_GREATER_THAN_OR_EQUAL_VALUE = "KEY_GREATER_THAN_OR_EQUAL_VALUE"
23+
KEY_LESS_THAN_VALUE = "KEY_LESS_THAN_VALUE"
24+
KEY_LESS_THAN_OR_EQUAL_VALUE = "KEY_LESS_THAN_OR_EQUAL_VALUE"
2025
STARTSWITH = "STARTSWITH"
2126
ENDSWITH = "ENDSWITH"
2227
IN = "IN"

docs/utilities/feature_flags.md

+5
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,11 @@ The `action` configuration can have the following values, where the expressions
455455
Action | Equivalent expression
456456
------------------------------------------------- | ---------------------------------------------------------------------------------
457457
**EQUALS** | `lambda a, b: a == b`
458+
**NOT_EQUALS** | `lambda a, b: a != b`
459+
**KEY_GREATER_THAN_VALUE** | `lambda a, b: a > b`
460+
**KEY_GREATER_THAN_OR_EQUAL_VALUE** | `lambda a, b: a >= b`
461+
**KEY_LESS_THAN_VALUE** | `lambda a, b: a < b`
462+
**KEY_LESS_THAN_OR_EQUAL_VALUE** | `lambda a, b: a <= b`
458463
**STARTSWITH** | `lambda a, b: a.startswith(b)`
459464
**ENDSWITH** | `lambda a, b: a.endswith(b)`
460465
**KEY_IN_VALUE** | `lambda a, b: a in b`

tests/functional/feature_flags/test_feature_flags.py

+335
Original file line numberDiff line numberDiff line change
@@ -802,3 +802,338 @@ def test_get_configuration_with_envelope_and_raw(mocker, config):
802802

803803
assert "log_level" in config
804804
assert "log_level" not in features_config
805+
806+
##
807+
## Inequality test cases
808+
##
809+
810+
# Test not equals
811+
def test_flags_not_equal_no_match(mocker, config):
812+
expected_value = False
813+
mocked_app_config_schema = {
814+
"my_feature": {
815+
"default": expected_value,
816+
"rules": {
817+
"tenant id not equals 345345435": {
818+
"when_match": True,
819+
"conditions": [
820+
{
821+
"action": RuleAction.NOT_EQUALS.value,
822+
"key": "tenant_id",
823+
"value": "345345435",
824+
}
825+
],
826+
}
827+
},
828+
}
829+
}
830+
feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config)
831+
toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "345345435", "username": "a"}, default=False)
832+
assert toggle == expected_value
833+
834+
def test_flags_not_equal_match(mocker, config):
835+
expected_value = True
836+
mocked_app_config_schema = {
837+
"my_feature": {
838+
"default": expected_value,
839+
"rules": {
840+
"tenant id not equals 345345435": {
841+
"when_match": True,
842+
"conditions": [
843+
{
844+
"action": RuleAction.NOT_EQUALS.value,
845+
"key": "tenant_id",
846+
"value": "345345435",
847+
}
848+
],
849+
}
850+
},
851+
}
852+
}
853+
feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config)
854+
toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "", "username": "a"}, default=False)
855+
assert toggle == expected_value
856+
857+
858+
# Test less than
859+
def test_flags_less_than_no_match_1(mocker, config):
860+
expected_value = False
861+
mocked_app_config_schema = {
862+
"my_feature": {
863+
"default": expected_value,
864+
"rules": {
865+
"Date less than 2021.10.31": {
866+
"when_match": True,
867+
"conditions": [
868+
{
869+
"action": RuleAction.KEY_LESS_THAN_VALUE.value,
870+
"key": "current_date",
871+
"value": "2021.10.31",
872+
}
873+
],
874+
}
875+
},
876+
}
877+
}
878+
feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config)
879+
toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "345345435", "username": "a", "current_date": "2021.12.25"}, default=False)
880+
assert toggle == expected_value
881+
882+
def test_flags_less_than_no_match_2(mocker, config):
883+
expected_value = False
884+
mocked_app_config_schema = {
885+
"my_feature": {
886+
"default": expected_value,
887+
"rules": {
888+
"Date less than 2021.10.31": {
889+
"when_match": True,
890+
"conditions": [
891+
{
892+
"action": RuleAction.KEY_LESS_THAN_VALUE.value,
893+
"key": "current_date",
894+
"value": "2021.10.31",
895+
}
896+
],
897+
}
898+
},
899+
}
900+
}
901+
feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config)
902+
toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "345345435", "username": "a", "current_date": "2021.10.31"}, default=False)
903+
assert toggle == expected_value
904+
905+
def test_flags_less_than_match(mocker, config):
906+
expected_value = True
907+
mocked_app_config_schema = {
908+
"my_feature": {
909+
"default": expected_value,
910+
"rules": {
911+
"Date less than 2021.10.31": {
912+
"when_match": True,
913+
"conditions": [
914+
{
915+
"action": RuleAction.KEY_LESS_THAN_VALUE.value,
916+
"key": "current_date",
917+
"value": "2021.10.31",
918+
}
919+
],
920+
}
921+
},
922+
}
923+
}
924+
feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config)
925+
toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "345345435", "username": "a", "current_date": "2021.04.01"}, default=False)
926+
assert toggle == expected_value
927+
928+
# Test less than or equal to
929+
def test_flags_less_than_or_equal_no_match(mocker, config):
930+
expected_value = False
931+
mocked_app_config_schema = {
932+
"my_feature": {
933+
"default": expected_value,
934+
"rules": {
935+
"Date less than or equal 2021.10.31": {
936+
"when_match": True,
937+
"conditions": [
938+
{
939+
"action": RuleAction.KEY_LESS_THAN_OR_EQUAL_VALUE.value,
940+
"key": "current_date",
941+
"value": "2021.10.31",
942+
}
943+
],
944+
}
945+
},
946+
}
947+
}
948+
feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config)
949+
toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "345345435", "username": "a", "current_date": "2021.12.25"}, default=False)
950+
assert toggle == expected_value
951+
952+
def test_flags_less_than_or_equal_match_1(mocker, config):
953+
expected_value = True
954+
mocked_app_config_schema = {
955+
"my_feature": {
956+
"default": expected_value,
957+
"rules": {
958+
"Date less than or equal 2021.10.31": {
959+
"when_match": True,
960+
"conditions": [
961+
{
962+
"action": RuleAction.KEY_LESS_THAN_OR_EQUAL_VALUE.value,
963+
"key": "current_date",
964+
"value": "2021.10.31",
965+
}
966+
],
967+
}
968+
},
969+
}
970+
}
971+
feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config)
972+
toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "345345435", "username": "a", "current_date": "2021.04.01"}, default=False)
973+
assert toggle == expected_value
974+
975+
976+
def test_flags_less_than_or_equal_match_2(mocker, config):
977+
expected_value = True
978+
mocked_app_config_schema = {
979+
"my_feature": {
980+
"default": expected_value,
981+
"rules": {
982+
"Date less than or equal 2021.10.31": {
983+
"when_match": True,
984+
"conditions": [
985+
{
986+
"action": RuleAction.KEY_LESS_THAN_OR_EQUAL_VALUE.value,
987+
"key": "current_date",
988+
"value": "2021.10.31",
989+
}
990+
],
991+
}
992+
},
993+
}
994+
}
995+
feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config)
996+
toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "345345435", "username": "a", "current_date": "2021.10.31"}, default=False)
997+
assert toggle == expected_value
998+
999+
# Test greater than
1000+
def test_flags_greater_than_no_match_1(mocker, config):
1001+
expected_value = False
1002+
mocked_app_config_schema = {
1003+
"my_feature": {
1004+
"default": expected_value,
1005+
"rules": {
1006+
"Date greater than 2021.10.31": {
1007+
"when_match": True,
1008+
"conditions": [
1009+
{
1010+
"action": RuleAction.KEY_GREATER_THAN_VALUE.value,
1011+
"key": "current_date",
1012+
"value": "2021.10.31",
1013+
}
1014+
],
1015+
}
1016+
},
1017+
}
1018+
}
1019+
feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config)
1020+
toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "345345435", "username": "a", "current_date": "2021.04.01"}, default=False)
1021+
assert toggle == expected_value
1022+
1023+
def test_flags_greater_than_no_match_2(mocker, config):
1024+
expected_value = False
1025+
mocked_app_config_schema = {
1026+
"my_feature": {
1027+
"default": expected_value,
1028+
"rules": {
1029+
"Date greater than 2021.10.31": {
1030+
"when_match": True,
1031+
"conditions": [
1032+
{
1033+
"action": RuleAction.KEY_GREATER_THAN_VALUE.value,
1034+
"key": "current_date",
1035+
"value": "2021.10.31",
1036+
}
1037+
],
1038+
}
1039+
},
1040+
}
1041+
}
1042+
feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config)
1043+
toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "345345435", "username": "a", "current_date": "2021.10.31"}, default=False)
1044+
assert toggle == expected_value
1045+
1046+
def test_flags_greater_than_match(mocker, config):
1047+
expected_value = True
1048+
mocked_app_config_schema = {
1049+
"my_feature": {
1050+
"default": expected_value,
1051+
"rules": {
1052+
"Date greater than 2021.10.31": {
1053+
"when_match": True,
1054+
"conditions": [
1055+
{
1056+
"action": RuleAction.KEY_GREATER_THAN_VALUE.value,
1057+
"key": "current_date",
1058+
"value": "2021.10.31",
1059+
}
1060+
],
1061+
}
1062+
},
1063+
}
1064+
}
1065+
feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config)
1066+
toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "345345435", "username": "a", "current_date": "2021.12.25"}, default=False)
1067+
assert toggle == expected_value
1068+
1069+
# Test greater than or equal to
1070+
def test_flags_greater_than_or_equal_no_match(mocker, config):
1071+
expected_value = False
1072+
mocked_app_config_schema = {
1073+
"my_feature": {
1074+
"default": expected_value,
1075+
"rules": {
1076+
"Date greater than or equal 2021.10.31": {
1077+
"when_match": True,
1078+
"conditions": [
1079+
{
1080+
"action": RuleAction.KEY_GREATER_THAN_OR_EQUAL_VALUE.value,
1081+
"key": "current_date",
1082+
"value": "2021.10.31",
1083+
}
1084+
],
1085+
}
1086+
},
1087+
}
1088+
}
1089+
feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config)
1090+
toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "345345435", "username": "a", "current_date": "2021.04.01"}, default=False)
1091+
assert toggle == expected_value
1092+
1093+
def test_flags_greater_than_or_equal_match_1(mocker, config):
1094+
expected_value = True
1095+
mocked_app_config_schema = {
1096+
"my_feature": {
1097+
"default": expected_value,
1098+
"rules": {
1099+
"Date greater than or equal 2021.10.31": {
1100+
"when_match": True,
1101+
"conditions": [
1102+
{
1103+
"action": RuleAction.KEY_GREATER_THAN_OR_EQUAL_VALUE.value,
1104+
"key": "current_date",
1105+
"value": "2021.10.31",
1106+
}
1107+
],
1108+
}
1109+
},
1110+
}
1111+
}
1112+
feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config)
1113+
toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "345345435", "username": "a", "current_date": "2021.12.25"}, default=False)
1114+
assert toggle == expected_value
1115+
1116+
1117+
def test_flags_greater_than_or_equal_match_2(mocker, config):
1118+
expected_value = True
1119+
mocked_app_config_schema = {
1120+
"my_feature": {
1121+
"default": expected_value,
1122+
"rules": {
1123+
"Date greater than or equal 2021.10.31": {
1124+
"when_match": True,
1125+
"conditions": [
1126+
{
1127+
"action": RuleAction.KEY_GREATER_THAN_OR_EQUAL_VALUE.value,
1128+
"key": "current_date",
1129+
"value": "2021.10.31",
1130+
}
1131+
],
1132+
}
1133+
},
1134+
}
1135+
}
1136+
feature_flags = init_feature_flags(mocker, mocked_app_config_schema, config)
1137+
toggle = feature_flags.evaluate(name="my_feature", context={"tenant_id": "345345435", "username": "a", "current_date": "2021.10.31"}, default=False)
1138+
assert toggle == expected_value
1139+

0 commit comments

Comments
 (0)