splitio-client 9.6.1__tar.gz → 9.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- splitio_client-9.7.0/LICENSE.txt +13 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/PKG-INFO +2 -2
- {splitio_client-9.6.1 → splitio_client-9.7.0}/README.md +1 -1
- {splitio_client-9.6.1 → splitio_client-9.7.0}/setup.py +9 -10
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/api/auth.py +2 -1
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/api/commons.py +14 -5
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/impressions/adapters.py +14 -0
- splitio_client-9.7.0/splitio/models/__init__.py +6 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/grammar/condition.py +2 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/grammar/matchers/__init__.py +15 -3
- splitio_client-9.7.0/splitio/models/grammar/matchers/semver.py +260 -0
- splitio_client-9.7.0/splitio/models/grammar/matchers/utils/utils.py +168 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/splits.py +38 -1
- splitio_client-9.7.0/splitio/spec.py +1 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/storage/pluggable.py +1 -1
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/storage/redis.py +2 -2
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/sync/segment.py +2 -2
- splitio_client-9.7.0/splitio/version.py +1 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio_client.egg-info/PKG-INFO +2 -2
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio_client.egg-info/SOURCES.txt +34 -1
- splitio_client-9.7.0/splitio_client.egg-info/requires.txt +24 -0
- splitio_client-9.7.0/tests/__init__.py +0 -0
- splitio_client-9.7.0/tests/helpers/__init__.py +9 -0
- splitio_client-9.7.0/tests/helpers/mockserver.py +249 -0
- splitio_client-9.7.0/tests/integration/__init__.py +42 -0
- splitio_client-9.7.0/tests/integration/test_client_e2e.py +1652 -0
- splitio_client-9.7.0/tests/integration/test_redis_integration.py +250 -0
- splitio_client-9.7.0/tests/integration/test_streaming_e2e.py +1478 -0
- splitio_client-9.7.0/tests/push/__init__.py +0 -0
- splitio_client-9.7.0/tests/push/test_manager.py +252 -0
- splitio_client-9.7.0/tests/push/test_parser.py +116 -0
- splitio_client-9.7.0/tests/push/test_processor.py +58 -0
- splitio_client-9.7.0/tests/push/test_segment_worker.py +60 -0
- splitio_client-9.7.0/tests/push/test_split_worker.py +201 -0
- splitio_client-9.7.0/tests/push/test_splitsse.py +126 -0
- splitio_client-9.7.0/tests/push/test_sse.py +125 -0
- splitio_client-9.7.0/tests/push/test_status_tracker.py +195 -0
- splitio_client-9.7.0/tests/recorder/__init__.py +0 -0
- splitio_client-9.7.0/tests/recorder/test_recorder.py +79 -0
- splitio_client-9.7.0/tests/sync/__init__.py +0 -0
- splitio_client-9.7.0/tests/sync/test_events_synchronizer.py +68 -0
- splitio_client-9.7.0/tests/sync/test_impressions_count_synchronizer.py +38 -0
- splitio_client-9.7.0/tests/sync/test_impressions_synchronizer.py +68 -0
- splitio_client-9.7.0/tests/sync/test_manager.py +123 -0
- splitio_client-9.7.0/tests/sync/test_segments_synchronizer.py +358 -0
- splitio_client-9.7.0/tests/sync/test_splits_synchronizer.py +670 -0
- splitio_client-9.7.0/tests/sync/test_synchronizer.py +445 -0
- splitio_client-9.7.0/tests/sync/test_telemetry.py +150 -0
- splitio_client-9.7.0/tests/sync/test_unique_keys_sync.py +54 -0
- splitio_client-9.6.1/splitio/version.py +0 -1
- splitio_client-9.6.1/splitio_client.egg-info/requires.txt +0 -27
- {splitio_client-9.6.1 → splitio_client-9.7.0}/setup.cfg +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/__init__.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/api/__init__.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/api/client.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/api/events.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/api/impressions.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/api/segments.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/api/splits.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/api/telemetry.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/client/__init__.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/client/client.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/client/config.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/client/factory.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/client/input_validator.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/client/key.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/client/listener.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/client/localhost.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/client/manager.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/client/util.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/__init__.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/cache/__init__.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/cache/lru.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/evaluator.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/filters.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/hashfns/__init__.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/hashfns/legacy.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/hashfns/murmur3py.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/impressions/__init__.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/impressions/impressions.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/impressions/manager.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/impressions/strategies.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/impressions/unique_keys_tracker.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/splitters.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/engine/telemetry.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/exceptions.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/factories.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/impressions.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/key.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/datatypes.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/events.py +0 -0
- {splitio_client-9.6.1/splitio/models → splitio_client-9.7.0/splitio/models/grammar}/__init__.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/grammar/matchers/base.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/grammar/matchers/keys.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/grammar/matchers/misc.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/grammar/matchers/numeric.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/grammar/matchers/sets.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/grammar/matchers/string.py +0 -0
- {splitio_client-9.6.1/splitio/models/grammar → splitio_client-9.7.0/splitio/models/grammar/matchers/utils}/__init__.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/grammar/partitions.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/impressions.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/notification.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/segments.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/telemetry.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/models/token.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/push/__init__.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/push/manager.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/push/parser.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/push/processor.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/push/segmentworker.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/push/splitsse.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/push/splitworker.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/push/sse.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/push/status_tracker.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/recorder/__init__.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/recorder/recorder.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/storage/__init__.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/storage/adapters/__init__.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/storage/adapters/cache_trait.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/storage/adapters/redis.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/storage/adapters/util.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/storage/inmemmory.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/sync/__init__.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/sync/event.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/sync/impression.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/sync/manager.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/sync/split.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/sync/synchronizer.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/sync/telemetry.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/sync/unique_keys.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/sync/util.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/tasks/__init__.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/tasks/events_sync.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/tasks/impressions_sync.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/tasks/segment_sync.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/tasks/split_sync.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/tasks/telemetry_sync.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/tasks/unique_keys_sync.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/tasks/util/__init__.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/tasks/util/asynctask.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/tasks/util/workerpool.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/util/__init__.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/util/backoff.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/util/decorators.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/util/storage_helper.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/util/threadutil.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio/util/time.py +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio_client.egg-info/dependency_links.txt +0 -0
- {splitio_client-9.6.1 → splitio_client-9.7.0}/splitio_client.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright © 2024 Split Software, Inc.
|
|
2
|
+
|
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
you may not use this file except in compliance with the License.
|
|
5
|
+
You may obtain a copy of the License at
|
|
6
|
+
|
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
|
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
See the License for the specific language governing permissions and
|
|
13
|
+
limitations under the License.
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: splitio_client
|
|
3
|
-
Version: 9.
|
|
3
|
+
Version: 9.7.0
|
|
4
4
|
Summary: Split.io Python Client
|
|
5
5
|
Home-page: https://github.com/splitio/python-client
|
|
6
6
|
Author: Patricio Echague, Sebastian Arrubia
|
|
7
7
|
Author-email: pato@split.io, sebastian@split.io
|
|
8
8
|
License: Apache License 2.0
|
|
9
|
-
Download-URL: https://github.com/splitio/python-client/tarball/9.
|
|
9
|
+
Download-URL: https://github.com/splitio/python-client/tarball/9.7.0
|
|
10
10
|
Description: UNKNOWN
|
|
11
11
|
Platform: UNKNOWN
|
|
12
12
|
Classifier: Environment :: Console
|
|
@@ -7,7 +7,7 @@ This SDK is designed to work with Split, the platform for controlled rollouts, w
|
|
|
7
7
|
[](https://twitter.com/intent/follow?screen_name=splitsoftware)
|
|
8
8
|
|
|
9
9
|
## Compatibility
|
|
10
|
-
This SDK is compatible with **Python 3 and higher**.
|
|
10
|
+
This SDK is compatible with **Python 3.7 and higher**.
|
|
11
11
|
|
|
12
12
|
## Getting started
|
|
13
13
|
Below is a simple example that describes the instantiation and most basic usage of our SDK:
|
|
@@ -6,21 +6,20 @@ from setuptools import setup, find_packages
|
|
|
6
6
|
|
|
7
7
|
TESTS_REQUIRES = [
|
|
8
8
|
'flake8',
|
|
9
|
-
'pytest==7.0
|
|
10
|
-
'pytest-mock
|
|
11
|
-
'coverage==
|
|
9
|
+
'pytest==7.1.0',
|
|
10
|
+
'pytest-mock==3.11.1',
|
|
11
|
+
'coverage==7.2.7',
|
|
12
12
|
'pytest-cov',
|
|
13
|
-
'importlib-metadata==
|
|
14
|
-
'tomli
|
|
15
|
-
'iniconfig
|
|
16
|
-
'attrs
|
|
13
|
+
'importlib-metadata==6.7',
|
|
14
|
+
'tomli',
|
|
15
|
+
'iniconfig',
|
|
16
|
+
'attrs'
|
|
17
17
|
]
|
|
18
18
|
|
|
19
19
|
INSTALL_REQUIRES = [
|
|
20
|
-
'requests
|
|
21
|
-
'pyyaml
|
|
20
|
+
'requests',
|
|
21
|
+
'pyyaml',
|
|
22
22
|
'docopt>=0.6.2',
|
|
23
|
-
'enum34;python_version<"3.4"',
|
|
24
23
|
'bloom-filter2>=2.0.0'
|
|
25
24
|
]
|
|
26
25
|
|
|
@@ -5,6 +5,7 @@ import json
|
|
|
5
5
|
|
|
6
6
|
from splitio.api import APIException
|
|
7
7
|
from splitio.api.commons import headers_from_metadata, record_telemetry
|
|
8
|
+
from splitio.spec import SPEC_VERSION
|
|
8
9
|
from splitio.util.time import get_current_epoch_time_ms
|
|
9
10
|
from splitio.api.client import HttpClientException
|
|
10
11
|
from splitio.models.token import from_raw
|
|
@@ -43,7 +44,7 @@ class AuthAPI(object): # pylint: disable=too-few-public-methods
|
|
|
43
44
|
try:
|
|
44
45
|
response = self._client.get(
|
|
45
46
|
'auth',
|
|
46
|
-
'/v2/auth',
|
|
47
|
+
'/v2/auth?s=' + SPEC_VERSION,
|
|
47
48
|
self._sdk_key,
|
|
48
49
|
extra_headers=self._metadata,
|
|
49
50
|
)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"""Commons module."""
|
|
2
2
|
from splitio.util.time import get_current_epoch_time_ms
|
|
3
|
+
from splitio.spec import SPEC_VERSION
|
|
3
4
|
|
|
4
5
|
_CACHE_CONTROL = 'Cache-Control'
|
|
5
6
|
_CACHE_CONTROL_NO_CACHE = 'no-cache'
|
|
6
7
|
|
|
7
|
-
|
|
8
8
|
def headers_from_metadata(sdk_metadata, client_key=None):
|
|
9
9
|
"""
|
|
10
10
|
Generate a dict with headers required by data-recording API endpoints.
|
|
@@ -57,7 +57,7 @@ def record_telemetry(status_code, elapsed, metric_name, telemetry_runtime_produc
|
|
|
57
57
|
class FetchOptions(object):
|
|
58
58
|
"""Fetch Options object."""
|
|
59
59
|
|
|
60
|
-
def __init__(self, cache_control_headers=False, change_number=None, sets=None):
|
|
60
|
+
def __init__(self, cache_control_headers=False, change_number=None, sets=None, spec=SPEC_VERSION):
|
|
61
61
|
"""
|
|
62
62
|
Class constructor.
|
|
63
63
|
|
|
@@ -73,6 +73,7 @@ class FetchOptions(object):
|
|
|
73
73
|
self._cache_control_headers = cache_control_headers
|
|
74
74
|
self._change_number = change_number
|
|
75
75
|
self._sets = sets
|
|
76
|
+
self._spec = spec
|
|
76
77
|
|
|
77
78
|
@property
|
|
78
79
|
def cache_control_headers(self):
|
|
@@ -89,6 +90,11 @@ class FetchOptions(object):
|
|
|
89
90
|
"""Return sets."""
|
|
90
91
|
return self._sets
|
|
91
92
|
|
|
93
|
+
@property
|
|
94
|
+
def spec(self):
|
|
95
|
+
"""Return sets."""
|
|
96
|
+
return self._spec
|
|
97
|
+
|
|
92
98
|
def __eq__(self, other):
|
|
93
99
|
"""Match between other options."""
|
|
94
100
|
if self._cache_control_headers != other._cache_control_headers:
|
|
@@ -97,6 +103,8 @@ class FetchOptions(object):
|
|
|
97
103
|
return False
|
|
98
104
|
if self._sets != other._sets:
|
|
99
105
|
return False
|
|
106
|
+
if self._spec != other._spec:
|
|
107
|
+
return False
|
|
100
108
|
return True
|
|
101
109
|
|
|
102
110
|
|
|
@@ -116,14 +124,15 @@ def build_fetch(change_number, fetch_options, metadata):
|
|
|
116
124
|
:return: Objects for fetch
|
|
117
125
|
:rtype: dict, dict
|
|
118
126
|
"""
|
|
119
|
-
query = {'
|
|
127
|
+
query = {'s': fetch_options.spec} if fetch_options.spec is not None else {}
|
|
128
|
+
query['since'] = change_number
|
|
120
129
|
extra_headers = metadata
|
|
121
130
|
if fetch_options is None:
|
|
122
131
|
return query, extra_headers
|
|
123
132
|
if fetch_options.cache_control_headers:
|
|
124
133
|
extra_headers[_CACHE_CONTROL] = _CACHE_CONTROL_NO_CACHE
|
|
125
|
-
if fetch_options.change_number is not None:
|
|
126
|
-
query['till'] = fetch_options.change_number
|
|
127
134
|
if fetch_options.sets is not None:
|
|
128
135
|
query['sets'] = fetch_options.sets
|
|
136
|
+
if fetch_options.change_number is not None:
|
|
137
|
+
query['till'] = fetch_options.change_number
|
|
129
138
|
return query, extra_headers
|
|
@@ -40,6 +40,9 @@ class InMemorySenderAdapter(ImpressionsSenderAdapter):
|
|
|
40
40
|
:param uniques: unique keys disctionary
|
|
41
41
|
:type uniques: Dictionary {'feature_flag1': set(), 'feature_flag2': set(), .. }
|
|
42
42
|
"""
|
|
43
|
+
if len(uniques) == 0:
|
|
44
|
+
return
|
|
45
|
+
|
|
43
46
|
self._telemtry_http_client.record_unique_keys({'keys': self._uniques_formatter(uniques)})
|
|
44
47
|
|
|
45
48
|
def _uniques_formatter(self, uniques):
|
|
@@ -73,6 +76,9 @@ class RedisSenderAdapter(ImpressionsSenderAdapter):
|
|
|
73
76
|
:param uniques: unique keys disctionary
|
|
74
77
|
:type uniques: Dictionary {'feature_flag1': set(), 'feature_flag2': set(), .. }
|
|
75
78
|
"""
|
|
79
|
+
if len(uniques) == 0:
|
|
80
|
+
return
|
|
81
|
+
|
|
76
82
|
bulk_mtks = _uniques_formatter(uniques)
|
|
77
83
|
try:
|
|
78
84
|
inserted = self._redis_client.rpush(_MTK_QUEUE_KEY, *bulk_mtks)
|
|
@@ -90,6 +96,9 @@ class RedisSenderAdapter(ImpressionsSenderAdapter):
|
|
|
90
96
|
:param to_send: unique keys disctionary
|
|
91
97
|
:type to_send: Dictionary {'feature_flag1': set(), 'feature_flag2': set(), .. }
|
|
92
98
|
"""
|
|
99
|
+
if len(to_send) == 0:
|
|
100
|
+
return
|
|
101
|
+
|
|
93
102
|
try:
|
|
94
103
|
resulted = 0
|
|
95
104
|
counted = 0
|
|
@@ -140,6 +149,9 @@ class PluggableSenderAdapter(ImpressionsSenderAdapter):
|
|
|
140
149
|
:param uniques: unique keys disctionary
|
|
141
150
|
:type uniques: Dictionary {'feature_flag1': set(), 'feature_flag2': set(), .. }
|
|
142
151
|
"""
|
|
152
|
+
if len(uniques) == 0:
|
|
153
|
+
return
|
|
154
|
+
|
|
143
155
|
bulk_mtks = _uniques_formatter(uniques)
|
|
144
156
|
try:
|
|
145
157
|
_LOGGER.debug("record_unique_keys")
|
|
@@ -159,6 +171,8 @@ class PluggableSenderAdapter(ImpressionsSenderAdapter):
|
|
|
159
171
|
:param to_send: unique keys disctionary
|
|
160
172
|
:type to_send: Dictionary {'feature_flag1': set(), 'feature_flag2': set(), .. }
|
|
161
173
|
"""
|
|
174
|
+
if len(to_send) == 0:
|
|
175
|
+
return
|
|
162
176
|
try:
|
|
163
177
|
resulted = 0
|
|
164
178
|
for pf_count in to_send:
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from enum import Enum
|
|
4
4
|
|
|
5
|
+
from splitio.models import MatcherNotFoundException
|
|
5
6
|
from splitio.models.grammar import matchers
|
|
6
7
|
from splitio.models.grammar import partitions
|
|
7
8
|
|
|
@@ -124,6 +125,7 @@ def from_raw(raw_condition):
|
|
|
124
125
|
]
|
|
125
126
|
|
|
126
127
|
matcher_objects = [matchers.from_raw(x) for x in raw_condition['matcherGroup']['matchers']]
|
|
128
|
+
|
|
127
129
|
combiner = _MATCHER_COMBINERS[raw_condition['matcherGroup']['combiner']]
|
|
128
130
|
label = raw_condition.get('label')
|
|
129
131
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Matchers entrypoint module."""
|
|
2
|
+
from splitio.models import MatcherNotFoundException
|
|
2
3
|
from splitio.models.grammar.matchers.keys import AllKeysMatcher, UserDefinedSegmentMatcher
|
|
3
4
|
from splitio.models.grammar.matchers.numeric import BetweenMatcher, EqualToMatcher, \
|
|
4
5
|
GreaterThanOrEqualMatcher, LessThanOrEqualMatcher
|
|
@@ -7,6 +8,8 @@ from splitio.models.grammar.matchers.sets import ContainsAllOfSetMatcher, \
|
|
|
7
8
|
from splitio.models.grammar.matchers.string import ContainsStringMatcher, \
|
|
8
9
|
EndsWithMatcher, RegexMatcher, StartsWithMatcher, WhitelistMatcher
|
|
9
10
|
from splitio.models.grammar.matchers.misc import BooleanMatcher, DependencyMatcher
|
|
11
|
+
from splitio.models.grammar.matchers.semver import EqualToSemverMatcher, GreaterThanOrEqualToSemverMatcher, LessThanOrEqualToSemverMatcher, \
|
|
12
|
+
BetweenSemverMatcher, InListSemverMatcher
|
|
10
13
|
|
|
11
14
|
|
|
12
15
|
MATCHER_TYPE_ALL_KEYS = 'ALL_KEYS'
|
|
@@ -26,6 +29,11 @@ MATCHER_TYPE_CONTAINS_STRING = 'CONTAINS_STRING'
|
|
|
26
29
|
MATCHER_TYPE_IN_SPLIT_TREATMENT = 'IN_SPLIT_TREATMENT'
|
|
27
30
|
MATCHER_TYPE_EQUAL_TO_BOOLEAN = 'EQUAL_TO_BOOLEAN'
|
|
28
31
|
MATCHER_TYPE_MATCHES_STRING = 'MATCHES_STRING'
|
|
32
|
+
MATCHER_TYPE_EQUAL_TO_SEMVER = 'EQUAL_TO_SEMVER'
|
|
33
|
+
MATCHER_GREATER_THAN_OR_EQUAL_TO_SEMVER = 'GREATER_THAN_OR_EQUAL_TO_SEMVER'
|
|
34
|
+
MATCHER_LESS_THAN_OR_EQUAL_TO_SEMVER = 'LESS_THAN_OR_EQUAL_TO_SEMVER'
|
|
35
|
+
MATCHER_BETWEEN_SEMVER = 'BETWEEN_SEMVER'
|
|
36
|
+
MATCHER_INLIST_SEMVER = 'IN_LIST_SEMVER'
|
|
29
37
|
|
|
30
38
|
|
|
31
39
|
_MATCHER_BUILDERS = {
|
|
@@ -45,10 +53,14 @@ _MATCHER_BUILDERS = {
|
|
|
45
53
|
MATCHER_TYPE_CONTAINS_STRING: ContainsStringMatcher,
|
|
46
54
|
MATCHER_TYPE_IN_SPLIT_TREATMENT: DependencyMatcher,
|
|
47
55
|
MATCHER_TYPE_EQUAL_TO_BOOLEAN: BooleanMatcher,
|
|
48
|
-
MATCHER_TYPE_MATCHES_STRING: RegexMatcher
|
|
56
|
+
MATCHER_TYPE_MATCHES_STRING: RegexMatcher,
|
|
57
|
+
MATCHER_TYPE_EQUAL_TO_SEMVER: EqualToSemverMatcher,
|
|
58
|
+
MATCHER_GREATER_THAN_OR_EQUAL_TO_SEMVER: GreaterThanOrEqualToSemverMatcher,
|
|
59
|
+
MATCHER_LESS_THAN_OR_EQUAL_TO_SEMVER: LessThanOrEqualToSemverMatcher,
|
|
60
|
+
MATCHER_BETWEEN_SEMVER: BetweenSemverMatcher,
|
|
61
|
+
MATCHER_INLIST_SEMVER: InListSemverMatcher
|
|
49
62
|
}
|
|
50
63
|
|
|
51
|
-
|
|
52
64
|
def from_raw(raw_matcher):
|
|
53
65
|
"""
|
|
54
66
|
Parse a condition from a JSON portion of splitChanges.
|
|
@@ -63,5 +75,5 @@ def from_raw(raw_matcher):
|
|
|
63
75
|
try:
|
|
64
76
|
builder = _MATCHER_BUILDERS[matcher_type]
|
|
65
77
|
except KeyError:
|
|
66
|
-
raise
|
|
78
|
+
raise MatcherNotFoundException('Invalid matcher type %s' % matcher_type)
|
|
67
79
|
return builder(raw_matcher)
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"""Semver matcher classes."""
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
from splitio.models.grammar.matchers.base import Matcher
|
|
5
|
+
from splitio.models.grammar.matchers.string import Sanitizer
|
|
6
|
+
from splitio.models.grammar.matchers.utils.utils import build_semver_or_none
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
_LOGGER = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class EqualToSemverMatcher(Matcher):
|
|
13
|
+
"""A matcher for Semver equal to."""
|
|
14
|
+
|
|
15
|
+
def _build(self, raw_matcher):
|
|
16
|
+
"""
|
|
17
|
+
Build an EqualToSemverMatcher.
|
|
18
|
+
|
|
19
|
+
:param raw_matcher: raw matcher as fetched from splitChanges response.
|
|
20
|
+
:type raw_matcher: dict
|
|
21
|
+
"""
|
|
22
|
+
self._data = raw_matcher.get('stringMatcherData')
|
|
23
|
+
self._semver = build_semver_or_none(raw_matcher.get('stringMatcherData'))
|
|
24
|
+
|
|
25
|
+
def _match(self, key, attributes=None, context=None):
|
|
26
|
+
"""
|
|
27
|
+
Evaluate user input against a matcher and return whether the match is successful.
|
|
28
|
+
|
|
29
|
+
:param key: User key.
|
|
30
|
+
:type key: str.
|
|
31
|
+
:param attributes: Custom user attributes.
|
|
32
|
+
:type attributes: dict.
|
|
33
|
+
:param context: Evaluation context
|
|
34
|
+
:type context: dict
|
|
35
|
+
|
|
36
|
+
:returns: Wheter the match is successful.
|
|
37
|
+
:rtype: bool
|
|
38
|
+
"""
|
|
39
|
+
if self._semver is None:
|
|
40
|
+
_LOGGER.error("stringMatcherData is required for EQUAL_TO_SEMVER matcher type")
|
|
41
|
+
return False
|
|
42
|
+
|
|
43
|
+
matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes))
|
|
44
|
+
if matching_data is None:
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
matching_semver = build_semver_or_none(matching_data)
|
|
48
|
+
if matching_semver is None:
|
|
49
|
+
return False
|
|
50
|
+
|
|
51
|
+
return self._semver.version == matching_semver.version
|
|
52
|
+
|
|
53
|
+
def __str__(self):
|
|
54
|
+
"""Return string Representation."""
|
|
55
|
+
return f'equal semver {self._data}'
|
|
56
|
+
|
|
57
|
+
def _add_matcher_specific_properties_to_json(self):
|
|
58
|
+
"""Add matcher specific properties to base dict before returning it."""
|
|
59
|
+
return {'matcherType': 'EQUAL_TO_SEMVER', 'stringMatcherData': self._data}
|
|
60
|
+
|
|
61
|
+
class GreaterThanOrEqualToSemverMatcher(Matcher):
|
|
62
|
+
"""A matcher for Semver greater than or equal to."""
|
|
63
|
+
|
|
64
|
+
def _build(self, raw_matcher):
|
|
65
|
+
"""
|
|
66
|
+
Build a GreaterThanOrEqualToSemverMatcher.
|
|
67
|
+
|
|
68
|
+
:param raw_matcher: raw matcher as fetched from splitChanges response.
|
|
69
|
+
:type raw_matcher: dict
|
|
70
|
+
"""
|
|
71
|
+
self._data = raw_matcher.get('stringMatcherData')
|
|
72
|
+
self._semver = build_semver_or_none(raw_matcher.get('stringMatcherData'))
|
|
73
|
+
|
|
74
|
+
def _match(self, key, attributes=None, context=None):
|
|
75
|
+
"""
|
|
76
|
+
Evaluate user input against a matcher and return whether the match is successful.
|
|
77
|
+
|
|
78
|
+
:param key: User key.
|
|
79
|
+
:type key: str.
|
|
80
|
+
:param attributes: Custom user attributes.
|
|
81
|
+
:type attributes: dict.
|
|
82
|
+
:param context: Evaluation context
|
|
83
|
+
:type context: dict
|
|
84
|
+
|
|
85
|
+
:returns: Wheter the match is successful.
|
|
86
|
+
:rtype: bool
|
|
87
|
+
"""
|
|
88
|
+
if self._semver is None:
|
|
89
|
+
_LOGGER.error("stringMatcherData is required for GREATER_THAN_OR_EQUAL_TO_SEMVER matcher type")
|
|
90
|
+
return False
|
|
91
|
+
|
|
92
|
+
matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes))
|
|
93
|
+
if matching_data is None:
|
|
94
|
+
return False
|
|
95
|
+
|
|
96
|
+
matching_semver = build_semver_or_none(matching_data)
|
|
97
|
+
if matching_semver is None:
|
|
98
|
+
return False
|
|
99
|
+
|
|
100
|
+
return matching_semver.compare(self._semver) in [0, 1]
|
|
101
|
+
|
|
102
|
+
def __str__(self):
|
|
103
|
+
"""Return string Representation."""
|
|
104
|
+
return f'greater than or equal to semver {self._data}'
|
|
105
|
+
|
|
106
|
+
def _add_matcher_specific_properties_to_json(self):
|
|
107
|
+
"""Add matcher specific properties to base dict before returning it."""
|
|
108
|
+
return {'matcherType': 'GREATER_THAN_OR_EQUAL_TO_SEMVER', 'stringMatcherData': self._data}
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class LessThanOrEqualToSemverMatcher(Matcher):
|
|
112
|
+
"""A matcher for Semver less than or equal to."""
|
|
113
|
+
|
|
114
|
+
def _build(self, raw_matcher):
|
|
115
|
+
"""
|
|
116
|
+
Build a LessThanOrEqualToSemverMatcher.
|
|
117
|
+
|
|
118
|
+
:param raw_matcher: raw matcher as fetched from splitChanges response.
|
|
119
|
+
:type raw_matcher: dict
|
|
120
|
+
"""
|
|
121
|
+
self._data = raw_matcher.get('stringMatcherData')
|
|
122
|
+
self._semver = build_semver_or_none(raw_matcher.get('stringMatcherData'))
|
|
123
|
+
|
|
124
|
+
def _match(self, key, attributes=None, context=None):
|
|
125
|
+
"""
|
|
126
|
+
Evaluate user input against a matcher and return whether the match is successful.
|
|
127
|
+
|
|
128
|
+
:param key: User key.
|
|
129
|
+
:type key: str.
|
|
130
|
+
:param attributes: Custom user attributes.
|
|
131
|
+
:type attributes: dict.
|
|
132
|
+
:param context: Evaluation context
|
|
133
|
+
:type context: dict
|
|
134
|
+
|
|
135
|
+
:returns: Wheter the match is successful.
|
|
136
|
+
:rtype: bool
|
|
137
|
+
"""
|
|
138
|
+
if self._semver is None:
|
|
139
|
+
_LOGGER.error("stringMatcherData is required for LESS_THAN_OR_EQUAL_TO_SEMVER matcher type")
|
|
140
|
+
return False
|
|
141
|
+
|
|
142
|
+
matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes))
|
|
143
|
+
if matching_data is None:
|
|
144
|
+
return False
|
|
145
|
+
|
|
146
|
+
matching_semver = build_semver_or_none(matching_data)
|
|
147
|
+
if matching_semver is None:
|
|
148
|
+
return False
|
|
149
|
+
|
|
150
|
+
return matching_semver.compare(self._semver) in [0, -1]
|
|
151
|
+
|
|
152
|
+
def __str__(self):
|
|
153
|
+
"""Return string Representation."""
|
|
154
|
+
return f'less than or equal to semver {self._data}'
|
|
155
|
+
|
|
156
|
+
def _add_matcher_specific_properties_to_json(self):
|
|
157
|
+
"""Add matcher specific properties to base dict before returning it."""
|
|
158
|
+
return {'matcherType': 'LESS_THAN_OR_EQUAL_TO_SEMVER', 'stringMatcherData': self._data}
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class BetweenSemverMatcher(Matcher):
|
|
162
|
+
"""A matcher for Semver between."""
|
|
163
|
+
|
|
164
|
+
def _build(self, raw_matcher):
|
|
165
|
+
"""
|
|
166
|
+
Build a BetweenSemverMatcher.
|
|
167
|
+
|
|
168
|
+
:param raw_matcher: raw matcher as fetched from splitChanges response.
|
|
169
|
+
:type raw_matcher: dict
|
|
170
|
+
"""
|
|
171
|
+
self._data = raw_matcher.get('betweenStringMatcherData')
|
|
172
|
+
self._semver_start = build_semver_or_none(self._data['start'])
|
|
173
|
+
self._semver_end = build_semver_or_none(self._data['end'])
|
|
174
|
+
|
|
175
|
+
def _match(self, key, attributes=None, context=None):
|
|
176
|
+
"""
|
|
177
|
+
Evaluate user input against a matcher and return whether the match is successful.
|
|
178
|
+
|
|
179
|
+
:param key: User key.
|
|
180
|
+
:type key: str.
|
|
181
|
+
:param attributes: Custom user attributes.
|
|
182
|
+
:type attributes: dict.
|
|
183
|
+
:param context: Evaluation context
|
|
184
|
+
:type context: dict
|
|
185
|
+
|
|
186
|
+
:returns: Wheter the match is successful.
|
|
187
|
+
:rtype: bool
|
|
188
|
+
"""
|
|
189
|
+
if self._semver_start is None or self._semver_end is None:
|
|
190
|
+
_LOGGER.error("betweenStringMatcherData is required for BETWEEN_SEMVER matcher type")
|
|
191
|
+
return False
|
|
192
|
+
|
|
193
|
+
matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes))
|
|
194
|
+
if matching_data is None:
|
|
195
|
+
return False
|
|
196
|
+
|
|
197
|
+
matching_semver = build_semver_or_none(matching_data)
|
|
198
|
+
if matching_semver is None:
|
|
199
|
+
return False
|
|
200
|
+
|
|
201
|
+
return (self._semver_start.compare(matching_semver) in [0, -1]) and (self._semver_end.compare(matching_semver) in [0, 1])
|
|
202
|
+
|
|
203
|
+
def __str__(self):
|
|
204
|
+
"""Return string Representation."""
|
|
205
|
+
return 'between semver {start} and {end}'.format(start=self._data.get('start'), end=self._data.get('end'))
|
|
206
|
+
|
|
207
|
+
def _add_matcher_specific_properties_to_json(self):
|
|
208
|
+
"""Add matcher specific properties to base dict before returning it."""
|
|
209
|
+
return {'matcherType': 'BETWEEN_SEMVER', 'betweenStringMatcherData': self._data}
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class InListSemverMatcher(Matcher):
|
|
213
|
+
"""A matcher for Semver in list."""
|
|
214
|
+
|
|
215
|
+
def _build(self, raw_matcher):
|
|
216
|
+
"""
|
|
217
|
+
Build a InListSemverMatcher.
|
|
218
|
+
|
|
219
|
+
:param raw_matcher: raw matcher as fetched from splitChanges response.
|
|
220
|
+
:type raw_matcher: dict
|
|
221
|
+
"""
|
|
222
|
+
self._data = raw_matcher['whitelistMatcherData']['whitelist']
|
|
223
|
+
semver_list = [build_semver_or_none(item) for item in self._data if item]
|
|
224
|
+
self._semver_list = frozenset([item.version for item in semver_list if item])
|
|
225
|
+
|
|
226
|
+
def _match(self, key, attributes=None, context=None):
|
|
227
|
+
"""
|
|
228
|
+
Evaluate user input against a matcher and return whether the match is successful.
|
|
229
|
+
|
|
230
|
+
:param key: User key.
|
|
231
|
+
:type key: str.
|
|
232
|
+
:param attributes: Custom user attributes.
|
|
233
|
+
:type attributes: dict.
|
|
234
|
+
:param context: Evaluation context
|
|
235
|
+
:type context: dict
|
|
236
|
+
|
|
237
|
+
:returns: Wheter the match is successful.
|
|
238
|
+
:rtype: bool
|
|
239
|
+
"""
|
|
240
|
+
if self._semver_list is None:
|
|
241
|
+
_LOGGER.error("whitelistMatcherData is required for IN_LIST_SEMVER matcher type")
|
|
242
|
+
return False
|
|
243
|
+
|
|
244
|
+
matching_data = Sanitizer.ensure_string(self._get_matcher_input(key, attributes))
|
|
245
|
+
if matching_data is None:
|
|
246
|
+
return False
|
|
247
|
+
|
|
248
|
+
matching_semver = build_semver_or_none(matching_data)
|
|
249
|
+
if matching_semver is None:
|
|
250
|
+
return False
|
|
251
|
+
|
|
252
|
+
return matching_semver.version in self._semver_list
|
|
253
|
+
|
|
254
|
+
def __str__(self):
|
|
255
|
+
"""Return string Representation."""
|
|
256
|
+
return 'in list semver {data}'.format(data=self._data)
|
|
257
|
+
|
|
258
|
+
def _add_matcher_specific_properties_to_json(self):
|
|
259
|
+
"""Add matcher specific properties to base dict before returning it."""
|
|
260
|
+
return {'matcherType': 'IN_LIST_SEMVER', 'whitelistMatcherData': {'whitelist': self._data}}
|