From 9a0df0f9098fb9528f5883e7a5fa97a84e3e37f2 Mon Sep 17 00:00:00 2001 From: ozayr-zaviar Date: Mon, 26 Apr 2021 17:29:32 +0500 Subject: [PATCH 1/6] test entry --- tests/test_decision_service.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_decision_service.py b/tests/test_decision_service.py index f4023d0a..2b72bb78 100644 --- a/tests/test_decision_service.py +++ b/tests/test_decision_service.py @@ -1478,3 +1478,4 @@ def test_get_experiment_in_group__returns_none_if_user_not_in_group(self): mock_decision_service_logging.info.assert_called_once_with( 'User with bucketing ID "test_user" is not in any experiments of group 19228.' ) + From e8e559a571c501cafe510d696dc2cdf0479ed36c Mon Sep 17 00:00:00 2001 From: ozayr-zaviar Date: Mon, 26 Apr 2021 17:33:27 +0500 Subject: [PATCH 2/6] dummy commit --- tests/test_decision_service.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_decision_service.py b/tests/test_decision_service.py index 2b72bb78..cc1615f0 100644 --- a/tests/test_decision_service.py +++ b/tests/test_decision_service.py @@ -1467,7 +1467,7 @@ def test_get_experiment_in_group__returns_none_if_user_not_in_group(self): group = self.project_config.get_group("19228") with mock.patch( "optimizely.bucketer.Bucketer.find_bucket", return_value=None - ), self.mock_decision_logger as mock_decision_service_logging: + ), self.mock_decision_logger as mock_decision_service_logging_1: variation_received, _ = self.decision_service.get_experiment_in_group( self.project_config, group, "test_user" ) @@ -1475,7 +1475,6 @@ def test_get_experiment_in_group__returns_none_if_user_not_in_group(self): variation_received ) - mock_decision_service_logging.info.assert_called_once_with( + mock_decision_service_logging_1.info.assert_called_once_with( 'User with bucketing ID "test_user" is not in any experiments of group 19228.' ) - From 8f5e7a56888ee87a70419a1a4cf1bcf0e81ba805 Mon Sep 17 00:00:00 2001 From: ozayr-zaviar Date: Tue, 27 Apr 2021 16:35:19 +0500 Subject: [PATCH 3/6] test cases order corrected --- tests/test_decision_service.py | 83 ++++++---------------------------- 1 file changed, 13 insertions(+), 70 deletions(-) diff --git a/tests/test_decision_service.py b/tests/test_decision_service.py index cc1615f0..b3a5c068 100644 --- a/tests/test_decision_service.py +++ b/tests/test_decision_service.py @@ -1276,8 +1276,7 @@ def test_get_variation_for_feature__returns_variation_if_user_not_in_experiment_ "optimizely.helpers.audience.does_user_meet_audience_conditions", side_effect=[[False, []], [True, []]], ) as mock_audience_check, self.mock_decision_logger as mock_decision_service_logging, mock.patch( - "optimizely.bucketer.Bucketer.bucket", return_value=[expected_variation, []]): - + "optimizely.bucketer.Bucketer.bucket", return_value=[expected_variation, []]): decision, _ = self.decision_service.get_variation_for_feature( self.project_config, feature, "test_user" ) @@ -1320,9 +1319,6 @@ def test_get_variation_for_feature__returns_variation_for_feature_in_group(self) "group_exp_1", "28901" ) with mock.patch( - "optimizely.decision_service.DecisionService.get_experiment_in_group", - return_value=(self.project_config.get_experiment_from_key("group_exp_1"), []), - ) as mock_get_experiment_in_group, mock.patch( "optimizely.decision_service.DecisionService.get_variation", return_value=(expected_variation, []), ) as mock_decision: @@ -1338,9 +1334,6 @@ def test_get_variation_for_feature__returns_variation_for_feature_in_group(self) variation_received, ) - mock_get_experiment_in_group.assert_called_once_with( - self.project_config, self.project_config.get_group("19228"), 'test_user') - mock_decision.assert_called_once_with( self.project_config, self.project_config.get_experiment_from_key("group_exp_1"), @@ -1356,11 +1349,9 @@ def test_get_variation_for_feature__returns_none_for_user_not_in_group(self): feature = self.project_config.get_feature_from_key("test_feature_in_group") with mock.patch( - "optimizely.decision_service.DecisionService.get_experiment_in_group", - return_value=[None, []], - ) as mock_get_experiment_in_group, mock.patch( - "optimizely.decision_service.DecisionService.get_variation" - ) as mock_decision: + "optimizely.decision_service.DecisionService.get_variation", + return_value=[None, []], + ): variation_received, _ = self.decision_service.get_variation_for_feature( self.project_config, feature, "test_user" ) @@ -1369,11 +1360,6 @@ def test_get_variation_for_feature__returns_none_for_user_not_in_group(self): variation_received, ) - mock_get_experiment_in_group.assert_called_once_with( - self.project_config, self.project_config.get_group("19228"), "test_user") - - self.assertFalse(mock_decision.called) - def test_get_variation_for_feature__returns_none_for_user_not_in_experiment(self): """ Test that get_variation_for_feature returns None for user not in the associated experiment. """ @@ -1405,16 +1391,12 @@ def test_get_variation_for_feature__returns_none_for_invalid_group_id(self): feature = self.project_config.get_feature_from_key("test_feature_in_group") feature.groupId = "aabbccdd" - with self.mock_decision_logger as mock_decision_service_logging: - variation_received, _ = self.decision_service.get_variation_for_feature( - self.project_config, feature, "test_user" - ) - self.assertEqual( - decision_service.Decision(None, None, enums.DecisionSources.ROLLOUT), - variation_received, - ) - mock_decision_service_logging.error.assert_called_once_with( - enums.Errors.INVALID_GROUP_ID.format("_get_variation_for_feature") + variation_received, _ = self.decision_service.get_variation_for_feature( + self.project_config, feature, "test_user" + ) + self.assertEqual( + decision_service.Decision(None, None, enums.DecisionSources.ROLLOUT), + variation_received, ) def test_get_variation_for_feature__returns_none_for_user_in_group_experiment_not_associated_with_feature( @@ -1424,10 +1406,9 @@ def test_get_variation_for_feature__returns_none_for_user_in_group_experiment_no not targeting a feature, then None is returned. """ feature = self.project_config.get_feature_from_key("test_feature_in_group") - with mock.patch( - "optimizely.decision_service.DecisionService.get_experiment_in_group", - return_value=[self.project_config.get_experiment_from_key("group_exp_2"), []], + "optimizely.decision_service.DecisionService.get_variation", + return_value=[None, []], ) as mock_decision: variation_received, _ = self.decision_service.get_variation_for_feature( self.project_config, feature, "test_user" @@ -1438,43 +1419,5 @@ def test_get_variation_for_feature__returns_none_for_user_in_group_experiment_no ) mock_decision.assert_called_once_with( - self.project_config, self.project_config.get_group("19228"), "test_user" - ) - - def test_get_experiment_in_group(self): - """ Test that get_experiment_in_group returns the bucketed experiment for the user. """ - - group = self.project_config.get_group("19228") - experiment = self.project_config.get_experiment_from_id("32222") - with mock.patch( - "optimizely.bucketer.Bucketer.find_bucket", return_value="32222" - ), self.mock_decision_logger as mock_decision_service_logging: - variation_received, _ = self.decision_service.get_experiment_in_group( - self.project_config, group, "test_user" - ) - self.assertEqual( - experiment, - variation_received, - ) - - mock_decision_service_logging.info.assert_called_once_with( - 'User with bucketing ID "test_user" is in experiment group_exp_1 of group 19228.' - ) - - def test_get_experiment_in_group__returns_none_if_user_not_in_group(self): - """ Test that get_experiment_in_group returns None if the user is not bucketed into the group. """ - - group = self.project_config.get_group("19228") - with mock.patch( - "optimizely.bucketer.Bucketer.find_bucket", return_value=None - ), self.mock_decision_logger as mock_decision_service_logging_1: - variation_received, _ = self.decision_service.get_experiment_in_group( - self.project_config, group, "test_user" - ) - self.assertIsNone( - variation_received - ) - - mock_decision_service_logging_1.info.assert_called_once_with( - 'User with bucketing ID "test_user" is not in any experiments of group 19228.' + self.project_config, self.project_config.get_experiment_from_id("32222"), "test_user", None, False ) From 62fe04e4d63eaf808d5b7f35d1ee46a81e77c281 Mon Sep 17 00:00:00 2001 From: ozayr-zaviar Date: Wed, 26 May 2021 11:33:49 +0500 Subject: [PATCH 4/6] Update test_decision_service.py --- tests/test_decision_service.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_decision_service.py b/tests/test_decision_service.py index 8b5bd722..52cbb075 100644 --- a/tests/test_decision_service.py +++ b/tests/test_decision_service.py @@ -1276,7 +1276,7 @@ def test_get_variation_for_feature__returns_variation_if_user_not_in_experiment_ "optimizely.helpers.audience.does_user_meet_audience_conditions", side_effect=[[False, []], [True, []]], ) as mock_audience_check, self.mock_decision_logger as mock_decision_service_logging, mock.patch( - "optimizely.bucketer.Bucketer.bucket", return_value=[expected_variation, []]): + "optimizely.bucketer.Bucketer.bucket", return_value=[expected_variation, []]): decision, _ = self.decision_service.get_variation_for_feature( self.project_config, feature, "test_user" ) From c48c223e502130247b3daba903159119eecc4930 Mon Sep 17 00:00:00 2001 From: ozayr-zaviar Date: Thu, 27 May 2021 17:05:50 +0500 Subject: [PATCH 5/6] Create optimizely_factory.py --- optimizely/optimizely_factory.py | 165 +++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 optimizely/optimizely_factory.py diff --git a/optimizely/optimizely_factory.py b/optimizely/optimizely_factory.py new file mode 100644 index 00000000..5b7a879a --- /dev/null +++ b/optimizely/optimizely_factory.py @@ -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 + ) From 64b7ed7399c6bbc807bfb4875274eecef7187589 Mon Sep 17 00:00:00 2001 From: ozayr-zaviar Date: Thu, 27 May 2021 17:15:56 +0500 Subject: [PATCH 6/6] Revert "Create optimizely_factory.py" This reverts commit c48c223e502130247b3daba903159119eecc4930. --- optimizely/optimizely_factory.py | 165 ------------------------------- 1 file changed, 165 deletions(-) delete mode 100644 optimizely/optimizely_factory.py diff --git a/optimizely/optimizely_factory.py b/optimizely/optimizely_factory.py deleted file mode 100644 index 5b7a879a..00000000 --- a/optimizely/optimizely_factory.py +++ /dev/null @@ -1,165 +0,0 @@ -# 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 - )