-
Notifications
You must be signed in to change notification settings - Fork 36
feat: Semantic Versioning #293
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
Changes from 3 commits
ac7e81f
ee29674
4a5e75d
01ed4da
796af4d
f318995
e016cd8
857e8de
5f793a3
cfb12df
379d637
1c37fbf
dcd391f
2affb9d
032d9c1
8577f7c
32bed45
426d2b7
ac8113e
84f6c26
ca7984a
9256e6a
15fa71b
8414a92
78ce524
aa1fbaf
c8c0e75
907fcf2
8b4567c
8856665
7ed20f0
4f2c05e
4070805
4c471bd
68708d9
09533b1
ad17c02
93cbb2a
25ab44d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ | |
|
||
from . import validator | ||
from .enums import CommonAudienceEvaluationLogs as audience_logs | ||
from .enums import Errors, SemverType | ||
|
||
|
||
class ConditionOperatorTypes(object): | ||
|
@@ -31,6 +32,11 @@ class ConditionMatchTypes(object): | |
EXISTS = 'exists' | ||
GREATER_THAN = 'gt' | ||
LESS_THAN = 'lt' | ||
SEMVER_EQ = 'semver_eq' | ||
SEMVER_GE = 'semver_ge' | ||
SEMVER_GT = 'semver_gt' | ||
SEMVER_LE = 'semver_le' | ||
SEMVER_LT = 'semver_lt' | ||
SUBSTRING = 'substring' | ||
|
||
|
||
|
@@ -233,12 +239,221 @@ def substring_evaluator(self, index): | |
|
||
return condition_value in user_value | ||
|
||
def semver_equal_evaluator(self, index): | ||
""" Evaluate the given semantic version equal match target version for the user version. | ||
|
||
Args: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit. Indentation is off. This needs to align with the quotes above. |
||
index: Index of the condition to be evaluated. | ||
|
||
Returns: | ||
Boolean: | ||
- True if the user version is equal (==) to the target version. | ||
- False if the user version is not equal (!=) to the target version. | ||
None: | ||
- if the user version value is not string type or is null. | ||
""" | ||
return self.compare_user_version_with_target_version(index) == 0 | ||
|
||
def semver_greater_than_evaluator(self, index): | ||
""" Evaluate the given semantic version greater than match target version for the user version. | ||
|
||
Args: | ||
index: Index of the condition to be evaluated. | ||
|
||
Returns: | ||
Boolean: | ||
- True if the user version is greater than the target version. | ||
- False if the user version is less than or equal to the target version. | ||
None: | ||
- if the user version value is not string type or is null. | ||
""" | ||
return self.compare_user_version_with_target_version(index) > 0 | ||
|
||
def semver_less_than_evaluator(self, index): | ||
""" Evaluate the given semantic version less than match target version for the user version. | ||
|
||
Args: | ||
index: Index of the condition to be evaluated. | ||
|
||
Returns: | ||
Boolean: | ||
- True if the user version is less than the target version. | ||
- False if the user version is greater than or equal to the target version. | ||
None: | ||
- if the user version value is not string type or is null. | ||
""" | ||
return self.compare_user_version_with_target_version(index) < 0 | ||
|
||
def semver_less_than_or_equal_evaluator(self, index): | ||
""" Evaluate the given semantic version less than or equal to match target version for the user version. | ||
|
||
Args: | ||
index: Index of the condition to be evaluated. | ||
|
||
Returns: | ||
Boolean: | ||
- True if the user version is less than or equal to the target version. | ||
- False if the user version is greater than the target version. | ||
None: | ||
- if the user version value is not string type or is null. | ||
""" | ||
return self.compare_user_version_with_target_version(index) <= 0 | ||
|
||
def semver_greater_than_or_equal_evaluator(self, index): | ||
""" Evaluate the given semantic version greater than or equal to match target version for the user version. | ||
|
||
Args: | ||
index: Index of the condition to be evaluated. | ||
|
||
Returns: | ||
Boolean: | ||
- True if the user version is greater than or equal to the target version. | ||
- False if the user version is less than the target version. | ||
None: | ||
- if the user version value is not string type or is null. | ||
""" | ||
return self.compare_user_version_with_target_version(index) >= 0 | ||
|
||
def split_semantic_version(self, target): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. More appropriate name will be |
||
""" Method to split the given version. | ||
|
||
Args: | ||
target: Given version. | ||
|
||
Returns: | ||
List: | ||
- The array of version split into smaller parts i.e major, minor, patch etc | ||
Exception: | ||
- if the given version is invalid in format | ||
""" | ||
target_prefix = target | ||
target_suffix = "" | ||
target_parts = [] | ||
|
||
if self.has_white_space(target): | ||
raise Exception(Errors.INVALID_ATTRIBUTE_FORMAT) | ||
|
||
if self.is_pre_release(target): | ||
target_parts = target.split(SemverType.IS_PRE_RELEASE) | ||
elif self.is_build(target): | ||
target_parts = target.split(SemverType.IS_BUILD) | ||
|
||
if target_parts: | ||
if len(target_parts) < 1: | ||
raise Exception(Errors.INVALID_ATTRIBUTE_FORMAT) | ||
target_prefix = str(target_parts[0]) | ||
target_suffix = target_parts[1:] | ||
|
||
dot_count = target_prefix.count(".") | ||
if dot_count > 2: | ||
raise Exception(Errors.INVALID_ATTRIBUTE_FORMAT) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add comments over each of these about what is being checked in the if condition? |
||
|
||
target_version_parts = target_prefix.split(".") | ||
if len(target_version_parts) != dot_count + 1: | ||
raise Exception(Errors.INVALID_ATTRIBUTE_FORMAT) | ||
for part in target_version_parts: | ||
if not part.isdigit(): | ||
raise Exception(Errors.INVALID_ATTRIBUTE_FORMAT) | ||
|
||
if target_suffix: | ||
target_version_parts.extend(target_suffix) | ||
return target_version_parts | ||
|
||
def is_pre_release(self, target): | ||
""" Method to check if the given version contains "-" | ||
|
||
Args: | ||
target: Given version in string. | ||
|
||
Returns: | ||
Boolean: | ||
- True if the given version does contain "-" | ||
- False if it doesn't | ||
""" | ||
return SemverType.IS_PRE_RELEASE in target | ||
|
||
def is_patch_pre_release(self, idx, idx_value): | ||
return idx == SemverType.PATCH_INDEX and idx_value in SemverType.IS_PATCH_PRE_RELEASE | ||
|
||
def is_build(self, target): | ||
""" Method to check if the given version contains "+" | ||
|
||
Args: | ||
target: Given version in string. | ||
|
||
Returns: | ||
Boolean: | ||
- True if the given version does contain "+" | ||
- False if it doesn't | ||
""" | ||
return SemverType.IS_BUILD in target | ||
|
||
def has_white_space(self, target): | ||
""" Method to check if the given version contains " " (white space) | ||
|
||
Args: | ||
target: Given version in string. | ||
|
||
Returns: | ||
Boolean: | ||
- True if the given version does contain " " | ||
- False if it doesn't | ||
""" | ||
return SemverType.HAS_WHITE_SPACE in target | ||
|
||
def compare_user_version_with_target_version(self, index): | ||
""" Method to compare user version with target version. | ||
|
||
Args: | ||
index: Index of the condition to be evaluated. | ||
|
||
Returns: | ||
Int: | ||
- 0 if user version is equal to target version. | ||
- 1 if user version is greater than target version. | ||
- -1 if user version is less than target version or, in case of exact string match, doesn't match the target | ||
version. | ||
None: | ||
- if the user version value is not string type or is null. | ||
""" | ||
condition_name = self.condition_data[index][0] | ||
target_version = self.condition_data[index][1] | ||
user_version = self.attributes.get(condition_name) | ||
|
||
target_version_parts = self.split_semantic_version(target_version) | ||
user_version_parts = self.split_semantic_version(user_version) | ||
user_version_parts_len = len(user_version_parts) | ||
|
||
for (idx, _) in enumerate(target_version_parts): | ||
if user_version_parts_len <= idx: | ||
return 1 if self.is_pre_release(target_version) else -1 | ||
elif not user_version_parts[idx].isdigit(): | ||
if user_version_parts[idx] < target_version_parts[idx]: | ||
return -1 | ||
elif user_version_parts[idx] > target_version_parts[idx]: | ||
return 1 | ||
else: | ||
user_version_part = int(user_version_parts[idx]) | ||
target_version_part = int(target_version_parts[idx]) | ||
if user_version_part > target_version_part: | ||
return 1 | ||
elif user_version_part < target_version_part: | ||
return -1 | ||
if self.is_pre_release(user_version) and not self.is_pre_release(target_version): | ||
return -1 | ||
return 0 | ||
msohailhussain marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
EVALUATORS_BY_MATCH_TYPE = { | ||
ConditionMatchTypes.EXACT: exact_evaluator, | ||
ConditionMatchTypes.EXISTS: exists_evaluator, | ||
ConditionMatchTypes.GREATER_THAN: greater_than_evaluator, | ||
ConditionMatchTypes.LESS_THAN: less_than_evaluator, | ||
ConditionMatchTypes.SUBSTRING: substring_evaluator, | ||
ConditionMatchTypes.SEMVER_EQ: semver_equal_evaluator, | ||
ConditionMatchTypes.SEMVER_GE: semver_greater_than_or_equal_evaluator, | ||
ConditionMatchTypes.SEMVER_GT: semver_greater_than_evaluator, | ||
ConditionMatchTypes.SEMVER_LE: semver_less_than_or_equal_evaluator, | ||
ConditionMatchTypes.SEMVER_LT: semver_less_than_evaluator, | ||
ConditionMatchTypes.SUBSTRING: substring_evaluator | ||
} | ||
|
||
def evaluate(self, index): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -157,3 +157,9 @@ class NotificationTypes(object): | |
OPTIMIZELY_CONFIG_UPDATE = 'OPTIMIZELY_CONFIG_UPDATE' | ||
TRACK = 'TRACK:event_key, user_id, attributes, event_tags, event' | ||
LOG_EVENT = 'LOG_EVENT:log_event' | ||
|
||
|
||
class SemverType(object): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this should be |
||
IS_PRE_RELEASE = '-' | ||
HAS_WHITE_SPACE = " " | ||
IS_BUILD = '+' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should use single quotes for all strings. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit. Convention is to import on separate lines. So please change this to: