Skip to content
This repository was archived by the owner on Nov 8, 2024. It is now read-only.

Commit a52c582

Browse files
vpaileoromanovsky
andauthored
add support for targeting rule evaluation with semver (#27)
* add logic to support semver comparisons * bump to 1.3.1 --------- Co-authored-by: Leo Romanovsky <[email protected]>
1 parent 4ff7636 commit a52c582

File tree

5 files changed

+54
-32
lines changed

5 files changed

+54
-32
lines changed

CHANGELOG.md

Lines changed: 0 additions & 28 deletions
This file was deleted.

eppo_client/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from eppo_client.http_client import HttpClient, SdkParams
1111
from eppo_client.read_write_lock import ReadWriteLock
1212

13-
__version__ = "1.3.0"
13+
__version__ = "1.3.1"
1414

1515
__client: Optional[EppoClient] = None
1616
__lock = ReadWriteLock()

eppo_client/rules.py

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import numbers
22
import re
3+
import semver
34
from enum import Enum
45
from typing import Any, List
56

@@ -55,9 +56,13 @@ def evaluate_condition(subject_attributes: dict, condition: Condition) -> bool:
5556
value.lower() for value in condition.value
5657
]
5758
else:
58-
return isinstance(
59-
subject_value, numbers.Number
60-
) and evaluate_numeric_condition(subject_value, condition)
59+
# Numeric operator: value could be numeric or semver.
60+
if isinstance(subject_value, numbers.Number):
61+
return evaluate_numeric_condition(subject_value, condition)
62+
elif is_valid_semver(subject_value):
63+
return compare_semver(
64+
subject_value, condition.value, condition.operator
65+
)
6166
return False
6267

6368

@@ -70,4 +75,31 @@ def evaluate_numeric_condition(subject_value: numbers.Number, condition: Conditi
7075
return subject_value < condition.value
7176
elif condition.operator == OperatorType.LTE:
7277
return subject_value <= condition.value
78+
79+
return False
80+
81+
82+
def is_valid_semver(value: str):
83+
try:
84+
# Parse the string. If it's a valid semver, it will return without errors.
85+
semver.VersionInfo.parse(value)
86+
return True
87+
except ValueError:
88+
# If a ValueError is raised, the string is not a valid semver.
89+
return False
90+
91+
92+
def compare_semver(attribute_value: Any, condition_value: Any, operator: OperatorType):
93+
if not is_valid_semver(attribute_value) or not is_valid_semver(condition_value):
94+
return False
95+
96+
if operator == OperatorType.GT:
97+
return semver.compare(attribute_value, condition_value) > 0
98+
elif operator == OperatorType.GTE:
99+
return semver.compare(attribute_value, condition_value) >= 0
100+
elif operator == OperatorType.LT:
101+
return semver.compare(attribute_value, condition_value) < 0
102+
elif operator == OperatorType.LTE:
103+
return semver.compare(attribute_value, condition_value) <= 0
104+
73105
return False

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ requests==2.31.*
44
cachetools==5.3.*
55
types-cachetools==5.3.*
66
types-requests==2.31.*
7+
semver==3.0.*

test/rules_test.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,23 @@ def test_find_matching_rule_with_numeric_value_and_regex():
5858
assert find_matching_rule({"age": 99}, [rule]) == rule
5959

6060

61+
def test_find_matching_rule_with_semver():
62+
semver_greater_than_condition = Condition(
63+
operator=OperatorType.GTE, value="1.0.0", attribute="version"
64+
)
65+
semver_less_than_condition = Condition(
66+
operator=OperatorType.LTE, value="2.0.0", attribute="version"
67+
)
68+
semver_rule = Rule(
69+
allocation_key="allocation",
70+
conditions=[semver_less_than_condition, semver_greater_than_condition],
71+
)
72+
73+
assert find_matching_rule({"version": "1.1.0"}, [semver_rule]) is semver_rule
74+
assert find_matching_rule({"version": "2.0.0"}, [semver_rule]) is semver_rule
75+
assert find_matching_rule({"version": "2.1.0"}, [semver_rule]) is None
76+
77+
6178
def test_one_of_operator_with_boolean():
6279
oneOfRule = Rule(
6380
allocation_key="allocation",

0 commit comments

Comments
 (0)