Skip to content

Commit 330f2ba

Browse files
authored
Merge pull request #314 from splitio/release-9.4.0
Release 9.4.0
2 parents 2e34128 + 6f977b2 commit 330f2ba

File tree

7 files changed

+63
-63
lines changed

7 files changed

+63
-63
lines changed

CHANGES.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
9.4.0 (Feb 14, 2023)
1+
9.4.0 (Mar 1, 2023)
22
- Added support to use JSON files in localhost mode.
33
- Updated default periodic telemetry post time to one hour.
44
- Fixed unhandeled exception in push.manager.py class when SDK is connected to split proxy

splitio/sync/segment.py

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import logging
22
import time
33
import json
4+
import os
45

56
from splitio.api import APIException
67
from splitio.api.commons import FetchOptions
@@ -254,24 +255,22 @@ def synchronize_segment(self, segment_name, till=None):
254255
"""
255256
try:
256257
fetched = self._read_segment_from_json_file(segment_name)
257-
if fetched is None:
258-
return False
259258
fetched_sha = util._get_sha(json.dumps(fetched))
260259
if not self.segment_exist_in_storage(segment_name):
261260
self._segment_sha[segment_name] = fetched_sha
262261
self._segment_storage.put(segments.from_raw(fetched))
263262
_LOGGER.debug("segment %s is added to storage", segment_name)
264-
else:
265-
if fetched_sha != self._segment_sha[segment_name]:
266-
self._segment_sha[segment_name] = fetched_sha
267-
if self._segment_storage.get_change_number(segment_name) <= fetched['till'] or fetched['till'] == self._DEFAULT_SEGMENT_TILL:
268-
self._segment_storage.update(
269-
segment_name,
270-
fetched['added'],
271-
fetched['removed'],
272-
fetched['till']
273-
)
274-
_LOGGER.debug("segment %s is updated", segment_name)
263+
return True
264+
265+
if fetched_sha == self._segment_sha[segment_name]:
266+
return True
267+
268+
self._segment_sha[segment_name] = fetched_sha
269+
if self._segment_storage.get_change_number(segment_name) > fetched['till'] and fetched['till'] != self._DEFAULT_SEGMENT_TILL:
270+
return True
271+
272+
self._segment_storage.update(segment_name, fetched['added'], fetched['removed'], fetched['till'])
273+
_LOGGER.debug("segment %s is updated", segment_name)
275274
except Exception as e:
276275
_LOGGER.error("Could not fetch segment: %s \n" + str(e), segment_name)
277276
return False
@@ -289,7 +288,7 @@ def _read_segment_from_json_file(self, filename):
289288
:rtype: Dict
290289
"""
291290
try:
292-
with open(self._segment_folder + '/' + filename + '.json', 'r') as flo:
291+
with open(os.path.join(self._segment_folder, "%s.json" % filename), 'r') as flo:
293292
parsed = json.load(flo)
294293
santitized_segment = self._sanitize_segment(parsed)
295294
return santitized_segment
@@ -308,10 +307,10 @@ def _sanitize_segment(self, parsed):
308307
"""
309308
if 'name' not in parsed or parsed['name'] is None:
310309
_LOGGER.warning("Segment does not have [name] element, skipping")
311-
return None
310+
raise Exception("Segment does not have [name] element")
312311
if parsed['name'].strip() == '':
313312
_LOGGER.warning("Segment [name] element is blank, skipping")
314-
return None
313+
raise Exception("Segment [name] element is blank")
315314

316315
for element in [('till', -1, -1, None, None, [0]),
317316
('added', [], None, None, None, None),

splitio/sync/split.py

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -323,18 +323,11 @@ def synchronize_splits(self, till=None): # pylint:disable=unused-argument
323323
"""Update splits in storage."""
324324
_LOGGER.info('Synchronizing splits now.')
325325
try:
326-
if self._localhost_mode == LocalhostMode.JSON:
327-
return self._synchronize_json()
328-
else:
329-
return self._synchronize_legacy()
326+
return self._synchronize_json() if self._localhost_mode == LocalhostMode.JSON else self._synchronize_legacy()
330327
except Exception as exc:
331328
_LOGGER.error(str(exc))
332329
raise APIException("Error fetching splits information") from exc
333330

334-
# _LOGGER.error("Error fetching splits information")
335-
# _LOGGER.error(str(e))
336-
# return []
337-
338331
def _synchronize_legacy(self):
339332
"""
340333
Update splits in storage for legacy mode.
@@ -368,19 +361,21 @@ def _synchronize_json(self):
368361
fetched, till = self._read_splits_from_json_file(self._filename)
369362
segment_list = set()
370363
fecthed_sha = util._get_sha(json.dumps(fetched))
371-
if fecthed_sha != self._current_json_sha:
372-
self._current_json_sha = fecthed_sha
373-
if self._split_storage.get_change_number() <= till or till == self._DEFAULT_SPLIT_TILL:
374-
for split in fetched:
375-
if split['status'] == splits.Status.ACTIVE.value:
376-
parsed = splits.from_raw(split)
377-
self._split_storage.put(parsed)
378-
_LOGGER.debug("split %s is updated", parsed.name)
379-
segment_list.update(set(parsed.get_segment_names()))
380-
else:
381-
self._split_storage.remove(split['name'])
382-
383-
self._split_storage.set_change_number(till)
364+
if fecthed_sha == self._current_json_sha:
365+
return []
366+
self._current_json_sha = fecthed_sha
367+
if self._split_storage.get_change_number() > till and till != self._DEFAULT_SPLIT_TILL:
368+
return []
369+
for split in fetched:
370+
if split['status'] == splits.Status.ACTIVE.value:
371+
parsed = splits.from_raw(split)
372+
self._split_storage.put(parsed)
373+
_LOGGER.debug("split %s is updated", parsed.name)
374+
segment_list.update(set(parsed.get_segment_names()))
375+
else:
376+
self._split_storage.remove(split['name'])
377+
378+
self._split_storage.set_change_number(till)
384379
return segment_list
385380
except Exception as exc:
386381
raise ValueError("Error reading splits from json.") from exc
@@ -392,14 +387,13 @@ def _read_splits_from_json_file(self, filename):
392387
:param filename: Path of the file containing split
393388
:type filename: str.
394389
395-
:return: Tuple: sanitized split structure dict, since and till
396-
:rtype: Tuple(Dict, int, int)
390+
:return: Tuple: sanitized split structure dict and till
391+
:rtype: Tuple(Dict, int)
397392
"""
398393
try:
399394
with open(filename, 'r') as flo:
400395
parsed = json.load(flo)
401396
santitized = self._sanitize_split(parsed)
402-
flo.close
403397
return santitized['splits'], santitized['till']
404398
except Exception as exc:
405399
_LOGGER.error(str(exc))
@@ -464,11 +458,11 @@ def _sanitize_split_elements(self, parsed_splits):
464458
('changeNumber', 0, 0, None, None, None),
465459
('algo', 2, 2, 2, None, None)]:
466460
split = util._sanitize_object_element(split, 'split', element[0], element[1], lower_value=element[2], upper_value=element[3], in_list=element[4], not_in_list=element[5])
467-
split = self._santizie_condition(split)
461+
split = self._sanitize_condition(split)
468462
sanitized_splits.append(split)
469463
return sanitized_splits
470464

471-
def _santizie_condition(self, split):
465+
def _sanitize_condition(self, split):
472466
"""
473467
Sanitize split and ensure a condition type ROLLOUT and matcher exist with ALL_KEYS elements.
474468
@@ -479,8 +473,7 @@ def _santizie_condition(self, split):
479473
:rtype: Dict
480474
"""
481475
found_all_keys_matcher = False
482-
if 'conditions' not in split or split['conditions'] is None:
483-
split['conditions'] = []
476+
split['conditions'] = split.get('conditions', [])
484477
if len(split['conditions']) > 0:
485478
last_condition = split['conditions'][-1]
486479
if 'conditionType' in last_condition:

splitio/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = '9.4.0-rc2'
1+
__version__ = '9.4.0'

tests/sync/test_manager.py

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import threading
44
import unittest.mock as mock
55
import time
6+
import pytest
67

78
from splitio.api.auth import AuthAPI
89
from splitio.api import auth, client, APIException
@@ -73,26 +74,23 @@ def test_start_streaming_false(self, mocker):
7374
assert len(synchronizer.start_periodic_data_recording.mock_calls) == 1
7475

7576
def test_telemetry(self, mocker):
76-
httpclient = mocker.Mock(spec=client.HttpClient)
77-
token = "eyJhbGciOiJIUzI1NiIsImtpZCI6IjVZOU05US45QnJtR0EiLCJ0eXAiOiJKV1QifQ.eyJ4LWFibHktY2FwYWJpbGl0eSI6IntcIk56TTJNREk1TXpjMF9NVGd5TlRnMU1UZ3dOZz09X3NlZ21lbnRzXCI6W1wic3Vic2NyaWJlXCJdLFwiTnpNMk1ESTVNemMwX01UZ3lOVGcxTVRnd05nPT1fc3BsaXRzXCI6W1wic3Vic2NyaWJlXCJdLFwiY29udHJvbF9wcmlcIjpbXCJzdWJzY3JpYmVcIixcImNoYW5uZWwtbWV0YWRhdGE6cHVibGlzaGVyc1wiXSxcImNvbnRyb2xfc2VjXCI6W1wic3Vic2NyaWJlXCIsXCJjaGFubmVsLW1ldGFkYXRhOnB1Ymxpc2hlcnNcIl19IiwieC1hYmx5LWNsaWVudElkIjoiY2xpZW50SWQiLCJleHAiOjE2MDIwODgxMjcsImlhdCI6MTYwMjA4NDUyN30.5_MjWonhs6yoFhw44hNJm3H7_YMjXpSW105DwjjppqE"
78-
payload = '{{"pushEnabled": true, "token": "{token}"}}'.format(token=token)
79-
cfg = DEFAULT_CONFIG.copy()
80-
cfg.update({'IPAddressesEnabled': True, 'machineName': 'some_machine_name', 'machineIp': '123.123.123.123'})
81-
sdk_metadata = get_metadata(cfg)
82-
httpclient.get.return_value = client.HttpResponse(200, payload)
83-
telemetry_storage = InMemoryTelemetryStorage()
84-
telemetry_producer = TelemetryStorageProducer(telemetry_storage)
85-
telemetry_runtime_producer = telemetry_producer.get_telemetry_runtime_producer()
86-
auth_api = auth.AuthAPI(httpclient, 'some_api_key', sdk_metadata, telemetry_runtime_producer)
8777
splits_ready_event = threading.Event()
8878
synchronizer = mocker.Mock(spec=Synchronizer)
8979
telemetry_storage = InMemoryTelemetryStorage()
9080
telemetry_producer = TelemetryStorageProducer(telemetry_storage)
9181
telemetry_runtime_producer = telemetry_producer.get_telemetry_runtime_producer()
92-
manager = Manager(splits_ready_event, synchronizer, auth_api, True, sdk_metadata, telemetry_runtime_producer)
93-
manager.start()
82+
manager = Manager(splits_ready_event, synchronizer, mocker.Mock(), True, SdkMetadata('1.0', 'some', '1.2.3.4'), telemetry_runtime_producer)
83+
try:
84+
manager.start()
85+
except:
86+
pass
87+
splits_ready_event.wait(2)
88+
89+
manager._queue.put(Status.PUSH_SUBSYSTEM_UP)
90+
manager._queue.put(Status.PUSH_NONRETRYABLE_ERROR)
9491
time.sleep(1)
95-
manager._push_status_handler_active = True
92+
assert(telemetry_storage._streaming_events._streaming_events[len(telemetry_storage._streaming_events._streaming_events)-2]._type == StreamingEventTypes.SYNC_MODE_UPDATE.value)
93+
assert(telemetry_storage._streaming_events._streaming_events[len(telemetry_storage._streaming_events._streaming_events)-2]._data == SSESyncMode.STREAMING.value)
9694
assert(telemetry_storage._streaming_events._streaming_events[len(telemetry_storage._streaming_events._streaming_events)-1]._type == StreamingEventTypes.SYNC_MODE_UPDATE.value)
9795
assert(telemetry_storage._streaming_events._streaming_events[len(telemetry_storage._streaming_events._streaming_events)-1]._data == SSESyncMode.POLLING.value)
9896

tests/sync/test_segments_synchronizer.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -305,11 +305,21 @@ def test_json_elements_sanitization(self, mocker):
305305

306306
# should reject segment if 'name' is null
307307
segment2 = {"name": None, "added": [], "removed": [], "since": -1, "till": 12}
308-
assert(segment_synchronizer._sanitize_segment(segment2) == None)
308+
exception_called = False
309+
try:
310+
segment_synchronizer._sanitize_segment(segment2)
311+
except:
312+
exception_called = True
313+
assert(exception_called)
309314

310315
# should reject segment if 'name' does not exist
311316
segment2 = {"added": [], "removed": [], "since": -1, "till": 12}
312-
assert(segment_synchronizer._sanitize_segment(segment2) == None)
317+
exception_called = False
318+
try:
319+
segment_synchronizer._sanitize_segment(segment2)
320+
except:
321+
exception_called = True
322+
assert(exception_called)
313323

314324
# should add missing 'added' element
315325
segment2 = {"name": 'seg', "removed": [], "since": -1, "till": 12}

tests/sync/test_splits_synchronizer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,7 @@ def test_split_condition_sanitization(self, mocker):
500500
target_split = splits_json["splitChange1_1"]["splits"].copy()
501501
target_split[0]["conditions"][0]['partitions'][0]['size'] = 0
502502
target_split[0]["conditions"][0]['partitions'][1]['size'] = 100
503-
split[0]["conditions"] = None
503+
del split[0]["conditions"]
504504
assert (split_synchronizer._sanitize_split_elements(split) == target_split)
505505

506506
# test missing ALL_KEYS condition matcher with default rule set to 100% off

0 commit comments

Comments
 (0)