Skip to content

Commit 7973aa8

Browse files
Fixing multiple issues in notification_center. (#182)
1 parent 3ce6411 commit 7973aa8

File tree

3 files changed

+323
-126
lines changed

3 files changed

+323
-126
lines changed

optimizely/notification_center.py

Lines changed: 67 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2017, Optimizely
1+
# Copyright 2017-2019, Optimizely
22
# Licensed under the Apache License, Version 2.0 (the "License");
33
# you may not use this file except in compliance with the License.
44
# You may obtain a copy of the License at
@@ -11,46 +11,53 @@
1111
# See the License for the specific language governing permissions and
1212
# limitations under the License.
1313

14-
from functools import reduce
15-
1614
from .helpers import enums
15+
from . import logger as optimizely_logger
16+
17+
18+
NOTIFICATION_TYPES = tuple(getattr(enums.NotificationTypes, attr)
19+
for attr in dir(enums.NotificationTypes)
20+
if not attr.startswith('__'))
1721

1822

1923
class NotificationCenter(object):
20-
""" Class encapsulating Broadcast Notifications. The enums.NotifcationTypes includes predefined notifications."""
24+
""" Class encapsulating methods to manage notifications and their listeners.
25+
The enums.NotificationTypes includes predefined notifications."""
2126

22-
def __init__(self, logger):
23-
self.notification_id = 1
24-
self.notifications = {}
25-
for (attr, value) in enums.NotificationTypes.__dict__.items():
26-
self.notifications[value] = []
27-
self.logger = logger
27+
def __init__(self, logger=None):
28+
self.listener_id = 1
29+
self.notification_listeners = {}
30+
for notification_type in NOTIFICATION_TYPES:
31+
self.notification_listeners[notification_type] = []
32+
self.logger = optimizely_logger.adapt_logger(logger or optimizely_logger.NoOpLogger())
2833

2934
def add_notification_listener(self, notification_type, notification_callback):
30-
""" Add a notification callback to the notification center.
35+
""" Add a notification callback to the notification center for a given notification type.
3136
3237
Args:
33-
notification_type: A string representing the notification type from .helpers.enums.NotificationTypes
34-
notification_callback: closure of function to call when event is triggered.
38+
notification_type: A string representing the notification type from helpers.enums.NotificationTypes
39+
notification_callback: Closure of function to call when event is triggered.
3540
3641
Returns:
37-
Integer notification id used to remove the notification or -1 if the notification has already been added.
42+
Integer notification ID used to remove the notification or
43+
-1 if the notification listener has already been added or
44+
if the notification type is invalid.
3845
"""
3946

40-
if notification_type not in self.notifications:
41-
self.notifications[notification_type] = [(self.notification_id, notification_callback)]
42-
else:
43-
if reduce(lambda a, b: a + 1,
44-
filter(lambda tup: tup[1] == notification_callback, self.notifications[notification_type]),
45-
0) > 0:
46-
return -1
47-
self.notifications[notification_type].append((self.notification_id, notification_callback))
47+
if notification_type not in NOTIFICATION_TYPES:
48+
self.logger.error('Invalid notification_type: {} provided. Not adding listener.'.format(notification_type))
49+
return -1
4850

49-
ret_val = self.notification_id
51+
for _, listener in self.notification_listeners[notification_type]:
52+
if listener == notification_callback:
53+
self.logger.error('Listener has already been added. Not adding it again.')
54+
return -1
5055

51-
self.notification_id += 1
56+
self.notification_listeners[notification_type].append((self.listener_id, notification_callback))
57+
current_listener_id = self.listener_id
58+
self.listener_id += 1
5259

53-
return ret_val
60+
return current_listener_id
5461

5562
def remove_notification_listener(self, notification_id):
5663
""" Remove a previously added notification callback.
@@ -62,40 +69,61 @@ def remove_notification_listener(self, notification_id):
6269
The function returns boolean true if found and removed, false otherwise.
6370
"""
6471

65-
for v in self.notifications.values():
66-
toRemove = list(filter(lambda tup: tup[0] == notification_id, v))
67-
if len(toRemove) > 0:
68-
v.remove(toRemove[0])
72+
for listener in self.notification_listeners.values():
73+
listener_to_remove = list(filter(lambda tup: tup[0] == notification_id, listener))
74+
if len(listener_to_remove) > 0:
75+
listener.remove(listener_to_remove[0])
6976
return True
7077

7178
return False
7279

73-
def clear_all_notifications(self):
74-
""" Remove all notifications """
75-
for key in self.notifications.keys():
76-
self.notifications[key] = []
80+
def clear_notification_listeners(self, notification_type):
81+
""" Remove notification listeners for a certain notification type.
82+
83+
Args:
84+
notification_type: String denoting notification type.
85+
"""
86+
87+
if notification_type not in NOTIFICATION_TYPES:
88+
self.logger.error('Invalid notification_type: {} provided. Not removing any listener.'.format(notification_type))
89+
self.notification_listeners[notification_type] = []
7790

7891
def clear_notifications(self, notification_type):
79-
""" Remove notifications for a certain notification type
92+
""" (DEPRECATED since 3.2.0, use clear_notification_listeners)
93+
Remove notification listeners for a certain notification type.
8094
8195
Args:
8296
notification_type: key to the list of notifications .helpers.enums.NotificationTypes
8397
"""
98+
self.clear_notification_listeners(notification_type)
8499

85-
self.notifications[notification_type] = []
100+
def clear_all_notification_listeners(self):
101+
""" Remove all notification listeners. """
102+
for notification_type in self.notification_listeners.keys():
103+
self.clear_notification_listeners(notification_type)
104+
105+
def clear_all_notifications(self):
106+
""" (DEPRECATED since 3.2.0, use clear_all_notification_listeners)
107+
Remove all notification listeners. """
108+
self.clear_all_notification_listeners()
86109

87110
def send_notifications(self, notification_type, *args):
88111
""" Fires off the notification for the specific event. Uses var args to pass in a
89112
arbitrary list of parameter according to which notification type was fired.
90113
91114
Args:
92115
notification_type: Type of notification to fire (String from .helpers.enums.NotificationTypes)
93-
args: variable list of arguments to the callback.
116+
args: Variable list of arguments to the callback.
94117
"""
95118

96-
if notification_type in self.notifications:
97-
for notification_id, callback in self.notifications[notification_type]:
119+
if notification_type not in NOTIFICATION_TYPES:
120+
self.logger.error('Invalid notification_type: {} provided. '
121+
'Not triggering any notification.'.format(notification_type))
122+
return
123+
124+
if notification_type in self.notification_listeners:
125+
for notification_id, callback in self.notification_listeners[notification_type]:
98126
try:
99127
callback(*args)
100128
except:
101-
self.logger.exception('Problem calling notify callback!')
129+
self.logger.exception('Unknown problem when sending "{}" type notification.'.format(notification_type))

0 commit comments

Comments
 (0)