Skip to content

Commit 3731be4

Browse files
oakbanialiabbasrizvi
authored andcommitted
feat(notification-center): Add LogEvent notification (#213)
Going to merge this and run compat tests on master.
1 parent ec67224 commit 3731be4

File tree

4 files changed

+71
-5
lines changed

4 files changed

+71
-5
lines changed

optimizely/event/event_processor.py

+18-2
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
from six.moves import queue
2020

2121
from optimizely import logger as _logging
22+
from optimizely import notification_center as _notification_center
2223
from optimizely.event_dispatcher import EventDispatcher as default_event_dispatcher
24+
from optimizely.helpers import enums
2325
from optimizely.helpers import validator
2426
from .event_factory import EventFactory
2527
from .user_event import UserEvent
@@ -62,8 +64,10 @@ def __init__(self,
6264
event_queue=None,
6365
batch_size=None,
6466
flush_interval=None,
65-
timeout_interval=None):
66-
""" BatchEventProcessor init method to configure event batching.
67+
timeout_interval=None,
68+
notification_center=None):
69+
""" EventProcessor init method to configure event batching.
70+
6771
Args:
6872
event_dispatcher: Provides a dispatch_event method which if given a URL and params sends a request to it.
6973
logger: Provides a log method to log messages. By default nothing would be logged.
@@ -76,6 +80,7 @@ def __init__(self,
7680
be flushed.
7781
timeout_interval: Optional floating point number representing time interval in seconds before joining the consumer
7882
thread.
83+
notification_center: Optional instance of notification_center.NotificationCenter.
7984
"""
8085
self.event_dispatcher = event_dispatcher or default_event_dispatcher
8186
self.logger = _logging.adapt_logger(logger or _logging.NoOpLogger())
@@ -88,8 +93,13 @@ def __init__(self,
8893
self.timeout_interval = timedelta(seconds=timeout_interval) \
8994
if self._validate_intantiation_props(timeout_interval, 'timeout_interval') \
9095
else self._DEFAULT_TIMEOUT_INTERVAL
96+
self.notification_center = notification_center
9197
self._current_batch = list()
9298

99+
if not validator.is_notification_center_valid(self.notification_center):
100+
self.logger.error(enums.Errors.INVALID_INPUT.format('notification_center'))
101+
self.notification_center = _notification_center.NotificationCenter()
102+
93103
if start_on_init is True:
94104
self.start()
95105

@@ -195,6 +205,12 @@ def _flush_queue(self):
195205

196206
log_event = EventFactory.create_log_event(to_process_batch, self.logger)
197207

208+
if self.notification_center is not None:
209+
self.notification_center.send_notifications(
210+
enums.NotificationTypes.LOG_EVENT,
211+
log_event
212+
)
213+
198214
try:
199215
self.event_dispatcher.dispatch_event(log_event)
200216
except Exception as e:

optimizely/helpers/enums.py

+4
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,12 @@ class NotificationTypes(object):
121121
122122
TRACK notification listener has the following parameters:
123123
str event_key, str user_id, dict attributes (can be None), event_tags (can be None), Event event
124+
125+
LOG_EVENT notification listener has the following parameter(s):
126+
LogEvent log_event
124127
"""
125128
ACTIVATE = 'ACTIVATE:experiment, user_id, attributes, variation, event'
126129
DECISION = 'DECISION:type, user_id, attributes, decision_info'
127130
OPTIMIZELY_CONFIG_UPDATE = 'OPTIMIZELY_CONFIG_UPDATE'
128131
TRACK = 'TRACK:event_key, user_id, attributes, event_tags, event'
132+
LOG_EVENT = 'LOG_EVENT:log_event'

tests/test_event_processor.py

+33-3
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@
1717
from six.moves import queue
1818

1919
from . import base
20-
from optimizely.logger import SimpleLogger
2120
from optimizely.event.payload import Decision, Visitor
22-
from optimizely.event.user_event_factory import UserEventFactory
2321
from optimizely.event.event_processor import BatchEventProcessor
22+
from optimizely.event.log_event import LogEvent
23+
from optimizely.event.user_event_factory import UserEventFactory
24+
from optimizely.helpers import enums
25+
from optimizely.logger import SimpleLogger
2426

2527

2628
class CanonicalEvent(object):
@@ -110,6 +112,7 @@ def setUp(self, *args, **kwargs):
110112
self.event_name = 'test_event'
111113
self.event_queue = queue.Queue(maxsize=self.DEFAULT_QUEUE_CAPACITY)
112114
self.optimizely.logger = SimpleLogger()
115+
self.notification_center = self.optimizely.notification_center
113116

114117
def tearDown(self):
115118
self._event_processor.stop()
@@ -125,7 +128,8 @@ def _set_event_processor(self, event_dispatcher, logger):
125128
self.event_queue,
126129
self.MAX_BATCH_SIZE,
127130
self.MAX_DURATION_SEC,
128-
self.MAX_TIMEOUT_INTERVAL_SEC
131+
self.MAX_TIMEOUT_INTERVAL_SEC,
132+
self.optimizely.notification_center
129133
)
130134

131135
def test_drain_on_stop(self):
@@ -371,3 +375,29 @@ def test_init__NaN_timeout_interval(self):
371375
# default timeout interval is 5s.
372376
self.assertEqual(self._event_processor.timeout_interval, timedelta(seconds=5))
373377
mock_config_logging.info.assert_called_with('Using default value for timeout_interval.')
378+
379+
def test_notification_center__on_log_event(self):
380+
381+
mock_event_dispatcher = mock.Mock()
382+
callback_hit = [False]
383+
384+
def on_log_event(log_event):
385+
self.assertStrictTrue(isinstance(log_event, LogEvent))
386+
callback_hit[0] = True
387+
388+
self.optimizely.notification_center.add_notification_listener(
389+
enums.NotificationTypes.LOG_EVENT, on_log_event
390+
)
391+
392+
with mock.patch.object(self.optimizely, 'logger') as mock_config_logging:
393+
self._set_event_processor(mock_event_dispatcher, mock_config_logging)
394+
395+
user_event = self._build_conversion_event(self.event_name, self.project_config)
396+
self._event_processor.process(user_event)
397+
398+
self._event_processor.stop()
399+
400+
self.assertEqual(True, callback_hit[0])
401+
self.assertEqual(1, len(self.optimizely.notification_center.notification_listeners[
402+
enums.NotificationTypes.LOG_EVENT
403+
]))

tests/test_notification_center.py

+16
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ def on_track_listener(*args):
3434
pass
3535

3636

37+
def on_log_event_listener(*args):
38+
pass
39+
40+
3741
class NotificationCenterTest(unittest.TestCase):
3842

3943
def test_add_notification_listener__valid_type(self):
@@ -59,6 +63,11 @@ def test_add_notification_listener__valid_type(self):
5963
4, test_notification_center.add_notification_listener(enums.NotificationTypes.TRACK, on_track_listener)
6064
)
6165

66+
self.assertEqual(
67+
5, test_notification_center.add_notification_listener(enums.NotificationTypes.LOG_EVENT,
68+
on_log_event_listener)
69+
)
70+
6271
def test_add_notification_listener__multiple_listeners(self):
6372
""" Test that multiple listeners of the same type can be successfully added. """
6473

@@ -138,6 +147,7 @@ def another_on_activate_listener(*args):
138147
self.assertEqual(2, len(test_notification_center.notification_listeners[enums.NotificationTypes.ACTIVATE]))
139148
self.assertEqual(1, len(test_notification_center.notification_listeners[enums.NotificationTypes.DECISION]))
140149
self.assertEqual(0, len(test_notification_center.notification_listeners[enums.NotificationTypes.TRACK]))
150+
self.assertEqual(0, len(test_notification_center.notification_listeners[enums.NotificationTypes.LOG_EVENT]))
141151

142152
# Remove one of the activate listeners and assert.
143153
self.assertTrue(test_notification_center.remove_notification_listener(3))
@@ -164,6 +174,10 @@ def another_on_activate_listener(*args):
164174
3, test_notification_center.add_notification_listener(enums.NotificationTypes.ACTIVATE,
165175
another_on_activate_listener)
166176
)
177+
self.assertEqual(
178+
4, test_notification_center.add_notification_listener(enums.NotificationTypes.LOG_EVENT,
179+
on_log_event_listener)
180+
)
167181

168182
# Try removing a listener which does not exist.
169183
self.assertFalse(test_notification_center.remove_notification_listener(42))
@@ -180,6 +194,7 @@ def test_clear_notification_listeners(self):
180194
on_config_update_listener)
181195
test_notification_center.add_notification_listener(enums.NotificationTypes.DECISION, on_decision_listener)
182196
test_notification_center.add_notification_listener(enums.NotificationTypes.TRACK, on_track_listener)
197+
test_notification_center.add_notification_listener(enums.NotificationTypes.LOG_EVENT, on_log_event_listener)
183198

184199
# Assert all listeners are there:
185200
for notification_type in notification_center.NOTIFICATION_TYPES:
@@ -210,6 +225,7 @@ def test_clear_all_notification_listeners(self):
210225
on_config_update_listener)
211226
test_notification_center.add_notification_listener(enums.NotificationTypes.DECISION, on_decision_listener)
212227
test_notification_center.add_notification_listener(enums.NotificationTypes.TRACK, on_track_listener)
228+
test_notification_center.add_notification_listener(enums.NotificationTypes.LOG_EVENT, on_log_event_listener)
213229

214230
# Assert all listeners are there:
215231
for notification_type in notification_center.NOTIFICATION_TYPES:

0 commit comments

Comments
 (0)