Skip to content

Commit

Permalink
Merge 5c7ff3e into 7607cf4
Browse files Browse the repository at this point in the history
  • Loading branch information
ozayr-zaviar authored Jun 3, 2021
2 parents 7607cf4 + 5c7ff3e commit a3b4775
Show file tree
Hide file tree
Showing 2 changed files with 327 additions and 0 deletions.
165 changes: 165 additions & 0 deletions optimizely/optimizely_factory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
# Copyright 2021, Optimizely
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from . import logger as optimizely_logger
from .config_manager import PollingConfigManager
from .error_handler import NoOpErrorHandler
from .event.event_processor import BatchEventProcessor
from .event_dispatcher import EventDispatcher
from .notification_center import NotificationCenter
from .optimizely import Optimizely


class OptimizelyFactory(object):
""" Optimizely factory to provides basic utility to instantiate the Optimizely
SDK with a minimal number of configuration options."""

max_event_batch_size = None
max_event_flush_interval = None
polling_interval = None
blocking_timeout = None

@staticmethod
def set_batch_size(batch_size):
""" Convenience method for setting the maximum number of events contained within a batch.
Args:
batch_size: Sets size of event_queue.
"""

OptimizelyFactory.max_event_batch_size = batch_size
return OptimizelyFactory.max_event_batch_size

@staticmethod
def set_flush_interval(flush_interval):
""" Convenience method for setting the maximum time interval in milliseconds between event dispatches.
Args:
flush_interval: Time interval between event dispatches.
"""

OptimizelyFactory.max_event_flush_interval = flush_interval
return OptimizelyFactory.max_event_flush_interval

@staticmethod
def set_polling_interval(polling_interval):
""" Method to set frequency at which datafile has to be polled.
Args:
polling_interval: Time in seconds after which to update datafile.
"""
OptimizelyFactory.polling_interval = polling_interval
return OptimizelyFactory.polling_interval

@staticmethod
def set_blocking_timeout(blocking_timeout):
""" Method to set time in seconds to block the config call until config has been initialized.
Args:
blocking_timeout: Time in seconds to block the config call.
"""
OptimizelyFactory.blocking_timeout = blocking_timeout
return OptimizelyFactory.blocking_timeout

@staticmethod
def default_instance(sdk_key, datafile=None):
""" Returns a new optimizely instance..
Args:
sdk_key: Required string uniquely identifying the fallback datafile corresponding to project.
datafile: Optional JSON string datafile.
"""
error_handler = NoOpErrorHandler()
logger = optimizely_logger.NoOpLogger()
notification_center = NotificationCenter(logger)

config_manager_options = {
'sdk_key': sdk_key,
'update_interval': OptimizelyFactory.polling_interval,
'blocking_timeout': OptimizelyFactory.blocking_timeout,
'datafile': datafile,
'logger': logger,
'error_handler': error_handler,
'notification_center': notification_center,
}

config_manager = PollingConfigManager(**config_manager_options)

event_processor = BatchEventProcessor(
event_dispatcher=EventDispatcher(),
logger=logger,
batch_size=OptimizelyFactory.max_event_batch_size,
flush_interval=OptimizelyFactory.max_event_flush_interval,
notification_center=notification_center,
)

optimizely = Optimizely(
datafile, None, logger, error_handler, None, None, sdk_key, config_manager, notification_center,
event_processor
)
return optimizely

@staticmethod
def default_instance_with_config_manager(config_manager):
return Optimizely(
config_manager=config_manager
)

@staticmethod
def custom_instance(sdk_key, datafile=None, event_dispatcher=None, logger=None, error_handler=None,
skip_json_validation=None, user_profile_service=None, config_manager=None,
notification_center=None):

""" Returns a new optimizely instance.
if max_event_batch_size and max_event_flush_interval are None then default batch_size and flush_interval
will be used to setup BatchEventProcessor.
Args:
sdk_key: Required string uniquely identifying the fallback datafile corresponding to project.
datafile: Optional JSON string datafile.
event_dispatcher: Optional EventDispatcher interface provides a dispatch_event method which if given a
URL and params sends a request to it.
logger: Optional Logger interface provides a log method to log messages.
By default nothing would be logged.
error_handler: Optional ErrorHandler interface which provides a handle_error method to handle exceptions.
By default all exceptions will be suppressed.
skip_json_validation: Optional boolean param to skip JSON schema validation of the provided datafile.
user_profile_service: Optional UserProfileService interface provides methods to store and retrieve
user profiles.
config_manager: Optional ConfigManager interface responds to 'config' method.
notification_center: Optional Instance of NotificationCenter.
"""

error_handler = error_handler or NoOpErrorHandler()
logger = logger or optimizely_logger.NoOpLogger()
notification_center = notification_center if isinstance(notification_center,
NotificationCenter) else NotificationCenter(logger)

event_processor = BatchEventProcessor(
event_dispatcher=event_dispatcher or EventDispatcher(),
logger=logger,
batch_size=OptimizelyFactory.max_event_batch_size,
flush_interval=OptimizelyFactory.max_event_flush_interval,
notification_center=notification_center,
)

config_manager_options = {
'sdk_key': sdk_key,
'update_interval': OptimizelyFactory.polling_interval,
'blocking_timeout': OptimizelyFactory.blocking_timeout,
'datafile': datafile,
'logger': logger,
'error_handler': error_handler,
'skip_json_validation': skip_json_validation,
'notification_center': notification_center,
}
config_manager = config_manager or PollingConfigManager(**config_manager_options)

return Optimizely(
datafile, event_dispatcher, logger, error_handler, skip_json_validation, user_profile_service,
sdk_key, config_manager, notification_center, event_processor
)
162 changes: 162 additions & 0 deletions tests/test_optimizely_factory.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# Copyright 2021, Optimizely
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import mock

from optimizely.config_manager import PollingConfigManager
from optimizely.error_handler import NoOpErrorHandler
from optimizely.event_dispatcher import EventDispatcher
from optimizely.notification_center import NotificationCenter
from optimizely.optimizely_factory import OptimizelyFactory
from optimizely.user_profile import UserProfileService
from . import base


@mock.patch('requests.get')
class OptimizelyFactoryTest(base.BaseTest):
def setUp(self):
self.datafile = '{ revision: "42" }'
self.error_handler = NoOpErrorHandler()
self.mock_client_logger = mock.MagicMock()
self.notification_center = NotificationCenter(self.mock_client_logger)
self.event_dispatcher = EventDispatcher()
self.user_profile_service = UserProfileService()

def test_default_instance__should_create_config_manager_when_sdk_key_is_given(self, _):
optimizely_instance = OptimizelyFactory.default_instance('sdk_key')
self.assertIsInstance(optimizely_instance.config_manager, PollingConfigManager)

def test_default_instance__should_create_config_manager_when_params_are_set_valid(self, _):
OptimizelyFactory.set_polling_interval(40)
OptimizelyFactory.set_blocking_timeout(5)
OptimizelyFactory.set_flush_interval(30)
OptimizelyFactory.set_batch_size(10)
optimizely_instance = OptimizelyFactory.default_instance('sdk_key', datafile=self.datafile)
# Verify that values set in OptimizelyFactory are being used inside config manager.
self.assertEqual(optimizely_instance.config_manager.update_interval, 40)
self.assertEqual(optimizely_instance.config_manager.blocking_timeout, 5)
# Verify values set for batch_size and flush_interval
self.assertEqual(optimizely_instance.event_processor.flush_interval.seconds, 30)
self.assertEqual(optimizely_instance.event_processor.batch_size, 10)

def test_default_instance__should_create_config_set_default_values_params__invalid(self, _):
OptimizelyFactory.set_polling_interval(-40)
OptimizelyFactory.set_blocking_timeout(-85)
OptimizelyFactory.set_flush_interval(30)
OptimizelyFactory.set_batch_size(10)

optimizely_instance = OptimizelyFactory.default_instance('sdk_key', datafile=self.datafile)
# Verify that values set in OptimizelyFactory are not being used inside config manager.
self.assertEqual(optimizely_instance.config_manager.update_interval, 300)
self.assertEqual(optimizely_instance.config_manager.blocking_timeout, 10)
# Verify values set for batch_size and flush_interval
self.assertEqual(optimizely_instance.event_processor.flush_interval.seconds, 30)
self.assertEqual(optimizely_instance.event_processor.batch_size, 10)

def test_default_instance__should_create_http_config_manager_with_the_same_components_as_the_instance(self, _):
optimizely_instance = OptimizelyFactory.default_instance('sdk_key', None)
self.assertEqual(optimizely_instance.error_handler, optimizely_instance.config_manager.error_handler)
self.assertEqual(optimizely_instance.logger, optimizely_instance.config_manager.logger)
self.assertEqual(optimizely_instance.notification_center,
optimizely_instance.config_manager.notification_center)

def test_custom_instance__should_set_input_values_when_sdk_key_polling_interval_and_blocking_timeout_are_given(
self, _):
OptimizelyFactory.set_polling_interval(50)
OptimizelyFactory.set_blocking_timeout(10)

optimizely_instance = OptimizelyFactory.custom_instance('sdk_key', None, self.event_dispatcher,
self.mock_client_logger, self.error_handler, False,
self.user_profile_service, None,
self.notification_center)

self.assertEqual(optimizely_instance.config_manager.update_interval, 50)
self.assertEqual(optimizely_instance.config_manager.blocking_timeout, 10)

def test_custom_instance__should_set_default_values_when_sdk_key_polling_interval_and_blocking_timeout_are_invalid(
self, _):
OptimizelyFactory.set_polling_interval(-50)
OptimizelyFactory.set_blocking_timeout(-10)
optimizely_instance = OptimizelyFactory.custom_instance('sdk_key', None, self.event_dispatcher,
self.mock_client_logger, self.error_handler, False,
self.user_profile_service, None,
self.notification_center)
self.assertEqual(optimizely_instance.config_manager.update_interval, 300)
self.assertEqual(optimizely_instance.config_manager.blocking_timeout, 10)

def test_custom_instance__should_take_event_processor_when_flush_interval_and_batch_size_are_set_valid(self, _):
OptimizelyFactory.set_flush_interval(5)
OptimizelyFactory.set_batch_size(100)

optimizely_instance = OptimizelyFactory.custom_instance('sdk_key')
self.assertEqual(optimizely_instance.event_processor.flush_interval.seconds, 5)
self.assertEqual(optimizely_instance.event_processor.batch_size, 100)

def test_custom_instance__should_take_event_processor_set_default_values_when_flush_int_and_batch_size_are_invalid(
self, _):
OptimizelyFactory.set_flush_interval(-50)
OptimizelyFactory.set_batch_size(-100)
optimizely_instance = OptimizelyFactory.custom_instance('sdk_key')
self.assertEqual(optimizely_instance.event_processor.flush_interval.seconds, 30)
self.assertEqual(optimizely_instance.event_processor.batch_size, 10)

def test_custom_instance__should_assign_passed_components_to_both_the_instance_and_config_manager(self, _):
optimizely_instance = OptimizelyFactory.custom_instance('sdk_key', None, self.event_dispatcher,
self.mock_client_logger, self.error_handler, False,
self.user_profile_service, None,
self.notification_center)
# Config manager assertion
self.assertEqual(self.error_handler, optimizely_instance.config_manager.error_handler)
self.assertEqual(self.mock_client_logger, optimizely_instance.config_manager.logger)
self.assertEqual(self.notification_center,
optimizely_instance.config_manager.notification_center)

# instance assertions
self.assertEqual(self.error_handler, optimizely_instance.error_handler)
self.assertEqual(self.mock_client_logger, optimizely_instance.logger)
self.assertEqual(self.notification_center,
optimizely_instance.notification_center)

def test_set_batch_size_and_set_flush_interval___should_set_values_valid_or_invalid(self, _):

# pass valid value so no default value is set
OptimizelyFactory.set_flush_interval(5)
OptimizelyFactory.set_batch_size(100)
optimizely_instance = OptimizelyFactory.custom_instance('sdk_key')
self.assertEqual(optimizely_instance.event_processor.flush_interval.seconds, 5)
self.assertEqual(optimizely_instance.event_processor.batch_size, 100)

# pass invalid value so set default value
OptimizelyFactory.set_flush_interval('test')
OptimizelyFactory.set_batch_size('test')
optimizely_instance = OptimizelyFactory.custom_instance('sdk_key')
self.assertEqual(optimizely_instance.event_processor.flush_interval.seconds, 30)
self.assertEqual(optimizely_instance.event_processor.batch_size, 10)

OptimizelyFactory.set_flush_interval(20.5)
OptimizelyFactory.set_batch_size(85.5)
optimizely_instance = OptimizelyFactory.custom_instance('sdk_key')
self.assertEqual(optimizely_instance.event_processor.flush_interval.seconds, 20)
self.assertEqual(optimizely_instance.event_processor.batch_size, 10)

OptimizelyFactory.set_flush_interval(None)
OptimizelyFactory.set_batch_size(None)
optimizely_instance = OptimizelyFactory.custom_instance('sdk_key')
self.assertEqual(optimizely_instance.event_processor.flush_interval.seconds, 30)
self.assertEqual(optimizely_instance.event_processor.batch_size, 10)

OptimizelyFactory.set_flush_interval(True)
OptimizelyFactory.set_batch_size(True)
optimizely_instance = OptimizelyFactory.custom_instance('sdk_key')
self.assertEqual(optimizely_instance.event_processor.flush_interval.seconds, 30)
self.assertEqual(optimizely_instance.event_processor.batch_size, 10)

0 comments on commit a3b4775

Please sign in to comment.