Skip to content

Commit a8a7c24

Browse files
authored
Merge pull request #512 from splitio/semver-between-matcher
added semver between matcher
2 parents 02bef7e + 5e027de commit a8a7c24

File tree

4 files changed

+103
-3
lines changed

4 files changed

+103
-3
lines changed

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ test=pytest
1313

1414
[tool:pytest]
1515
ignore_glob=./splitio/_OLD/*
16-
addopts = --verbose --cov=splitio --cov-report xml
16+
addopts = --verbose --cov=splitio --cov-report xml -k BetweenSemverMatcherTests
1717
python_classes=*Tests
1818

1919
[build_sphinx]

splitio/models/grammar/matchers/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from splitio.models.grammar.matchers.string import ContainsStringMatcher, \
99
EndsWithMatcher, RegexMatcher, StartsWithMatcher, WhitelistMatcher
1010
from splitio.models.grammar.matchers.misc import BooleanMatcher, DependencyMatcher
11-
from splitio.models.grammar.matchers.semver import EqualToSemverMatcher, GreaterThanOrEqualToSemverMatcher, LessThanOrEqualToSemverMatcher
11+
from splitio.models.grammar.matchers.semver import EqualToSemverMatcher, GreaterThanOrEqualToSemverMatcher, LessThanOrEqualToSemverMatcher, BetweenSemverMatcher
1212

1313

1414
MATCHER_TYPE_ALL_KEYS = 'ALL_KEYS'
@@ -31,6 +31,7 @@
3131
MATCHER_TYPE_EQUAL_TO_SEMVER = 'EQUAL_TO_SEMVER'
3232
MATCHER_GREATER_THAN_OR_EQUAL_TO_SEMVER = 'GREATER_THAN_OR_EQUAL_TO_SEMVER'
3333
MATCHER_LESS_THAN_OR_EQUAL_TO_SEMVER = 'LESS_THAN_OR_EQUAL_TO_SEMVER'
34+
MATCHER_BETWEEN_SEMVER = 'BETWEEN_SEMVER'
3435

3536
_MATCHER_BUILDERS = {
3637
MATCHER_TYPE_ALL_KEYS: AllKeysMatcher,
@@ -52,7 +53,8 @@
5253
MATCHER_TYPE_MATCHES_STRING: RegexMatcher,
5354
MATCHER_TYPE_EQUAL_TO_SEMVER: EqualToSemverMatcher,
5455
MATCHER_GREATER_THAN_OR_EQUAL_TO_SEMVER: GreaterThanOrEqualToSemverMatcher,
55-
MATCHER_LESS_THAN_OR_EQUAL_TO_SEMVER: LessThanOrEqualToSemverMatcher
56+
MATCHER_LESS_THAN_OR_EQUAL_TO_SEMVER: LessThanOrEqualToSemverMatcher,
57+
MATCHER_BETWEEN_SEMVER: BetweenSemverMatcher
5658
}
5759

5860
def from_raw(raw_matcher):

splitio/models/grammar/matchers/semver.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,3 +312,53 @@ def __str__(self):
312312
def _add_matcher_specific_properties_to_json(self):
313313
"""Add matcher specific properties to base dict before returning it."""
314314
return {'matcherType': 'LESS_THAN_OR_EQUAL_TO_SEMVER', 'stringMatcherData': self._data}
315+
316+
class BetweenSemverMatcher(Matcher):
317+
"""A matcher for Semver between."""
318+
319+
def _build(self, raw_matcher):
320+
"""
321+
Build a BetweenSemverMatcher.
322+
323+
:param raw_matcher: raw matcher as fetched from splitChanges response.
324+
:type raw_matcher: dict
325+
"""
326+
self._data = raw_matcher.get('betweenStringMatcherData')
327+
self._semver_start = Semver.build(self._data['start']) if self._data.get('start') is not None else None
328+
self._semver_end = Semver.build(self._data['end']) if self._data.get('end') is not None else None
329+
330+
def _match(self, key, attributes=None, context=None):
331+
"""
332+
Evaluate user input against a matcher and return whether the match is successful.
333+
334+
:param key: User key.
335+
:type key: str.
336+
:param attributes: Custom user attributes.
337+
:type attributes: dict.
338+
:param context: Evaluation context
339+
:type context: dict
340+
341+
:returns: Wheter the match is successful.
342+
:rtype: bool
343+
"""
344+
if self._data is None or self._semver_start is None or self._semver_end is None:
345+
_LOGGER.error("betweenStringMatcherData is required for BETWEEN_SEMVER matcher type")
346+
return False
347+
348+
matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes))
349+
if matching_data is None:
350+
return False
351+
352+
matching_semver = Semver.build(matching_data)
353+
if matching_semver is None:
354+
return False
355+
356+
return (self._semver_start.compare(matching_semver) in [0, -1]) and (self._semver_end.compare(matching_semver) in [0, 1])
357+
358+
def __str__(self):
359+
"""Return string Representation."""
360+
return 'between semver {start} and {end}'.format(start=self._data.get('start'), end=self._data.get('end'))
361+
362+
def _add_matcher_specific_properties_to_json(self):
363+
"""Add matcher specific properties to base dict before returning it."""
364+
return {'matcherType': 'BETWEEN_SEMVER', 'betweenStringMatcherData': self._data}

tests/models/grammar/test_matchers.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1010,3 +1010,51 @@ def test_to_str(self):
10101010
"""Test that the object serializes to str properly."""
10111011
as_str = matchers.LessThanOrEqualToSemverMatcher(self.raw)
10121012
assert str(as_str) == "less than or equal to semver 2.1.8"
1013+
1014+
class BetweenSemverMatcherTests(MatcherTestsBase):
1015+
"""Semver between matcher test cases."""
1016+
1017+
raw = {
1018+
'negate': False,
1019+
'matcherType': 'BETWEEN_SEMVER',
1020+
'betweenStringMatcherData': {"start": "2.1.8", "end": "2.1.11"}
1021+
}
1022+
1023+
def test_from_raw(self, mocker):
1024+
"""Test parsing from raw json/dict."""
1025+
parsed = matchers.from_raw(self.raw)
1026+
assert isinstance(parsed, matchers.BetweenSemverMatcher)
1027+
assert parsed._data == {"start": "2.1.8", "end": "2.1.11"}
1028+
assert isinstance(parsed._semver_start, Semver)
1029+
assert isinstance(parsed._semver_end, Semver)
1030+
assert parsed._semver_start._major == 2
1031+
assert parsed._semver_start._minor == 1
1032+
assert parsed._semver_start._patch == 8
1033+
assert parsed._semver_start._pre_release == []
1034+
1035+
assert parsed._semver_end._major == 2
1036+
assert parsed._semver_end._minor == 1
1037+
assert parsed._semver_end._patch == 11
1038+
assert parsed._semver_end._pre_release == []
1039+
1040+
def test_matcher_behaviour(self, mocker):
1041+
"""Test if the matcher works properly."""
1042+
parsed = matchers.from_raw(self.raw)
1043+
assert parsed._match("2.1.8+rc")
1044+
assert parsed._match("2.1.9")
1045+
assert parsed._match("2.1.11-rc12")
1046+
assert not parsed._match("2.1.5")
1047+
assert not parsed._match("2.1.12-rc1")
1048+
assert not parsed._match(None)
1049+
assert not parsed._match("semver")
1050+
1051+
def test_to_json(self):
1052+
"""Test that the object serializes to JSON properly."""
1053+
as_json = matchers.BetweenSemverMatcher(self.raw).to_json()
1054+
assert as_json['matcherType'] == 'BETWEEN_SEMVER'
1055+
assert as_json['betweenStringMatcherData'] == {"start": "2.1.8", "end": "2.1.11"}
1056+
1057+
def test_to_str(self):
1058+
"""Test that the object serializes to str properly."""
1059+
as_str = matchers.BetweenSemverMatcher(self.raw)
1060+
assert str(as_str) == "between semver 2.1.8 and 2.1.11"

0 commit comments

Comments
 (0)