Skip to content

Commit 3df2131

Browse files
committed
added impressions pluggable storage and tests
1 parent f82a43a commit 3df2131

2 files changed

Lines changed: 174 additions & 2 deletions

File tree

splitio/storage/pluggable.py

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
"""Pluggable Storage classes."""
22

33
import logging
4+
import json
45

56
from splitio.models import splits, segments
6-
from splitio.storage import SplitStorage, SegmentStorage
7+
from splitio.models.impressions import Impression
8+
from splitio.storage import SplitStorage, SegmentStorage, ImpressionStorage
79

810
_LOGGER = logging.getLogger(__name__)
911

@@ -470,3 +472,87 @@ def put(self, segment):
470472
# except Exception:
471473
# _LOGGER.error('Error updating segment storage')
472474
# _LOGGER.debug('Error: ', exc_info=True)
475+
476+
477+
class PluggableImpressionsStorage(ImpressionStorage):
478+
479+
def __init__(self, pluggable_adapter, sdk_metadata, prefix=None):
480+
"""
481+
Class constructor.
482+
483+
:param pluggable_adapter: Storage client or compliant interface.
484+
:type pluggable_adapter: TBD
485+
:param sdk_metadata: SDK & Machine information.
486+
:type sdk_metadata: splitio.client.util.SdkMetadata
487+
"""
488+
self._pluggable_adapter = pluggable_adapter
489+
self._sdk_metadata = sdk_metadata
490+
self._impressions_queue_key = 'SPLITIO.impressions'
491+
if prefix is not None:
492+
self._impressions_queue_key = prefix + "." + self._impressions_queue_key
493+
494+
def _wrap_impressions(self, impressions):
495+
"""
496+
Wrap impressions to be stored in storage
497+
498+
:param impressions: Impression to add to the queue.
499+
:type impressions: splitio.models.impressions.Impression
500+
501+
:return: Processed impressions.
502+
:rtype: list[splitio.models.impressions.Impression]
503+
"""
504+
bulk_impressions = []
505+
for impression in impressions:
506+
if isinstance(impression, Impression):
507+
to_store = {
508+
'm': { # METADATA PORTION
509+
's': self._sdk_metadata.sdk_version,
510+
'n': self._sdk_metadata.instance_name,
511+
'i': self._sdk_metadata.instance_ip,
512+
},
513+
'i': { # IMPRESSION PORTION
514+
'k': impression.matching_key,
515+
'b': impression.bucketing_key,
516+
'f': impression.feature_name,
517+
't': impression.treatment,
518+
'r': impression.label,
519+
'c': impression.change_number,
520+
'm': impression.time,
521+
}
522+
}
523+
bulk_impressions.append(json.dumps(to_store))
524+
return bulk_impressions
525+
526+
def put(self, impressions):
527+
"""
528+
Add an impression to the pluggable storage.
529+
530+
:param impressions: Impression to add to the queue.
531+
:type impressions: splitio.models.impressions.Impression
532+
533+
:return: Whether the impression has been added or not.
534+
:rtype: bool
535+
"""
536+
bulk_impressions = self._wrap_impressions(impressions)
537+
try:
538+
self._pluggable_adapter.push_items(self._impressions_queue_key, *bulk_impressions)
539+
return True
540+
except Exception:
541+
_LOGGER.error('Something went wrong when trying to add impression to storage')
542+
_LOGGER.error('Error: ', exc_info=True)
543+
return False
544+
545+
def pop_many(self, count):
546+
"""
547+
Pop the oldest N events from storage.
548+
549+
:param count: Number of events to pop.
550+
:type count: int
551+
"""
552+
raise NotImplementedError('Only consumer mode is supported.')
553+
554+
def clear(self):
555+
"""
556+
Clear data.
557+
"""
558+
raise NotImplementedError('Only consumer mode is supported.')

tests/storage/test_pluggable.py

Lines changed: 87 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
"""Pluggable storage test module."""
2+
import json
3+
24
from splitio.models.splits import Split
35
from splitio.models import splits, segments
46
from splitio.models.segments import Segment
5-
from splitio.storage.pluggable import PluggableSplitStorage, PluggableSegmentStorage
7+
from splitio.models.impressions import Impression
8+
from splitio.storage.pluggable import PluggableSplitStorage, PluggableSegmentStorage, PluggableImpressionsStorage
9+
from splitio.client.util import get_metadata, SdkMetadata
610

711
from tests.integration import splits_json
812
import pytest
@@ -27,6 +31,13 @@ def get_many(self, keys):
2731
def set(self, key, value):
2832
self._keys[key] = value
2933

34+
def push_items(self, key, *value):
35+
items = []
36+
if key in self._keys:
37+
items = self._keys[key]
38+
[items.append(item) for item in value]
39+
self._keys[key] = items
40+
3041
def delete(self, key):
3142
if key in self._keys:
3243
del self._keys[key]
@@ -314,3 +325,78 @@ def test_get(self):
314325
# assert('myprefix.SPLITIO.segment.segment2' in self.mock_adapter._keys)
315326
# assert(self.mock_adapter._keys['myprefix.SPLITIO.segment.segment2'] == {'key1', 'key2', 'key3'})
316327
# assert(self.mock_adapter._keys['myprefix.SPLITIO.segment.segment2.till'] == 123)
328+
329+
330+
class PluggableImpressionsStorageTests(object):
331+
"""In memory impressions storage test cases."""
332+
333+
def setup_method(self):
334+
"""Prepare storages with test data."""
335+
self.mock_adapter = StorageMockAdapter()
336+
self.metadata = SdkMetadata('python-1.1.1', 'hostname', 'ip')
337+
self.pluggable_imp_storage = PluggableImpressionsStorage(self.mock_adapter, self.metadata, 'myprefix')
338+
339+
def test_init(self):
340+
assert(self.pluggable_imp_storage._impressions_queue_key == "myprefix.SPLITIO.impressions")
341+
assert(self.pluggable_imp_storage._sdk_metadata == self.metadata)
342+
343+
pluggable2 = PluggableImpressionsStorage(self.mock_adapter, self.metadata)
344+
assert(pluggable2._impressions_queue_key == "SPLITIO.impressions")
345+
346+
def test_put(self):
347+
impressions = [
348+
Impression('key1', 'feature1', 'on', 'some_label', 123456, 'buck1', 321654),
349+
Impression('key2', 'feature2', 'on', 'some_label', 123456, 'buck1', 321654),
350+
Impression('key3', 'feature2', 'on', 'some_label', 123456, 'buck1', 321654),
351+
Impression('key4', 'feature1', 'on', 'some_label', 123456, 'buck1', 321654)
352+
]
353+
self.pluggable_imp_storage.put(impressions)
354+
assert(self.pluggable_imp_storage._impressions_queue_key in self.mock_adapter._keys)
355+
assert(self.mock_adapter._keys["myprefix.SPLITIO.impressions"] == self.pluggable_imp_storage._wrap_impressions(impressions))
356+
357+
impressions2 = [
358+
Impression('key5', 'feature1', 'off', 'some_label', 123456, 'buck1', 321654),
359+
Impression('key6', 'feature2', 'off', 'some_label', 123456, 'buck1', 321654),
360+
]
361+
self.pluggable_imp_storage.put(impressions2)
362+
assert(self.mock_adapter._keys["myprefix.SPLITIO.impressions"] == self.pluggable_imp_storage._wrap_impressions(impressions + impressions2))
363+
364+
def test_wrap_impressions(self):
365+
impressions = [
366+
Impression('key1', 'feature1', 'on', 'some_label', 123456, 'buck1', 321654),
367+
Impression('key2', 'feature2', 'off', 'some_label', 123456, 'buck1', 321654),
368+
]
369+
assert(self.pluggable_imp_storage._wrap_impressions(impressions) == [
370+
json.dumps({
371+
'm': {
372+
's': self.metadata.sdk_version,
373+
'n': self.metadata.instance_name,
374+
'i': self.metadata.instance_ip,
375+
},
376+
'i': {
377+
'k': 'key1',
378+
'b': 'buck1',
379+
'f': 'feature1',
380+
't': 'on',
381+
'r': 'some_label',
382+
'c': 123456,
383+
'm': 321654,
384+
}
385+
}),
386+
json.dumps({
387+
'm': {
388+
's': self.metadata.sdk_version,
389+
'n': self.metadata.instance_name,
390+
'i': self.metadata.instance_ip,
391+
},
392+
'i': {
393+
'k': 'key2',
394+
'b': 'buck1',
395+
'f': 'feature2',
396+
't': 'off',
397+
'r': 'some_label',
398+
'c': 123456,
399+
'm': 321654,
400+
}
401+
})
402+
])

0 commit comments

Comments
 (0)