Skip to content

Commit b2b0a40

Browse files
author
Bilal Al
committed
added semver between matcher
1 parent 688e527 commit b2b0a40

File tree

3 files changed

+99
-5
lines changed

3 files changed

+99
-5
lines changed

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: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ def _build(self, raw_matcher):
153153
:param raw_matcher: raw matcher as fetched from splitChanges response.
154154
:type raw_matcher: dict
155155
"""
156-
self._data = raw_matcher['stringMatcherData']
156+
self._data = raw_matcher.get('stringMatcherData')
157157
self._semver = Semver(self._data)
158158

159159
def _match(self, key, attributes=None, context=None):
@@ -198,7 +198,7 @@ def _build(self, raw_matcher):
198198
:param raw_matcher: raw matcher as fetched from splitChanges response.
199199
:type raw_matcher: dict
200200
"""
201-
self._data = raw_matcher['stringMatcherData']
201+
self._data = raw_matcher.get('stringMatcherData')
202202
self._semver = Semver(self._data)
203203

204204
def _match(self, key, attributes=None, context=None):
@@ -243,7 +243,7 @@ def _build(self, raw_matcher):
243243
:param raw_matcher: raw matcher as fetched from splitChanges response.
244244
:type raw_matcher: dict
245245
"""
246-
self._data = raw_matcher['stringMatcherData']
246+
self._data = raw_matcher.get('stringMatcherData')
247247
self._semver = Semver(self._data)
248248

249249
def _match(self, key, attributes=None, context=None):
@@ -277,3 +277,49 @@ def __str__(self):
277277
def _add_matcher_specific_properties_to_json(self):
278278
"""Add matcher specific properties to base dict before returning it."""
279279
return {'matcherType': 'LESS_THAN_OR_EQUAL_TO_SEMVER', 'stringMatcherData': self._data}
280+
281+
class BetweenSemverMatcher(Matcher):
282+
"""A matcher for Semver between."""
283+
284+
def _build(self, raw_matcher):
285+
"""
286+
Build a BetweenSemverMatcher.
287+
288+
:param raw_matcher: raw matcher as fetched from splitChanges response.
289+
:type raw_matcher: dict
290+
"""
291+
self._data = raw_matcher.get('betweenStringMatcherData')
292+
self._semver_start = Semver(self._data['start']) if self._data.get('start') is not None else None
293+
self._semver_end = Semver(self._data['end']) if self._data.get('end') is not None else None
294+
295+
def _match(self, key, attributes=None, context=None):
296+
"""
297+
Evaluate user input against a matcher and return whether the match is successful.
298+
299+
:param key: User key.
300+
:type key: str.
301+
:param attributes: Custom user attributes.
302+
:type attributes: dict.
303+
:param context: Evaluation context
304+
:type context: dict
305+
306+
:returns: Wheter the match is successful.
307+
:rtype: bool
308+
"""
309+
if self._data is None or self._semver_start is None or self._semver_end is None:
310+
_LOGGER.error("betweenStringMatcherData is required for BETWEEN_SEMVER matcher type")
311+
return None
312+
313+
matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes))
314+
if matching_data is None:
315+
return False
316+
317+
return (self._semver_start.compare(Semver(matching_data)) in [0, -1]) and (self._semver_end.compare(Semver(matching_data)) in [0, 1])
318+
319+
def __str__(self):
320+
"""Return string Representation."""
321+
return 'between semver {start} and {end}'.format(start=self._data.get('start'), end=self._data.get('end'))
322+
323+
def _add_matcher_specific_properties_to_json(self):
324+
"""Add matcher specific properties to base dict before returning it."""
325+
return {'matcherType': 'BETWEEN_SEMVER', 'betweenStringMatcherData': self._data}

tests/models/grammar/test_matchers.py

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

0 commit comments

Comments
 (0)