Skip to content

Commit

Permalink
sdk_key and environment_key support (#338)
Browse files Browse the repository at this point in the history
* added sdk and environment key

* [MAINTENANCE] Remove Deprecated warnings during build

            - assertRaisesRegexp -> assertRaisesRegex
            - assertEquals -> assertEqual
            - isAlive() -> is_alive()
            - Check added to base.py to confirm attribute assertRaisesRegex for backwards compatibility to Python2.7

* [OASIS-7757] Fix spelling of environment to fix testcases from failing

* [OASIS-7757] - Added additional test cases to test_optimizely and test_user_context.

* [OASIS-7757] - update copyright years and add more testcases for sdk_key and environment_key. Move decide tests to test_user_context from test_optimizely.

Co-authored-by: ozayr-zaviar <uzairzaviar@gmail.com>
  • Loading branch information
The-inside-man and ozayr-zaviar committed Jun 23, 2021
1 parent c3b191b commit 9e9c4d0
Show file tree
Hide file tree
Showing 6 changed files with 158 additions and 25 deletions.
32 changes: 29 additions & 3 deletions optimizely/optimizely_config.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2020, Optimizely
# Copyright 2020-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
Expand All @@ -17,11 +17,13 @@


class OptimizelyConfig(object):
def __init__(self, revision, experiments_map, features_map, datafile=None):
def __init__(self, revision, experiments_map, features_map, datafile=None, sdk_key=None, environment_key=None):
self.revision = revision
self.experiments_map = experiments_map
self.features_map = features_map
self._datafile = datafile
self.sdk_key = sdk_key
self.environment_key = environment_key

def get_datafile(self):
""" Get the datafile associated with OptimizelyConfig.
Expand All @@ -31,6 +33,22 @@ def get_datafile(self):
"""
return self._datafile

def get_sdk_key(self):
""" Get the sdk key associated with OptimizelyConfig.
Returns:
A string containing sdk key.
"""
return self.sdk_key

def get_environment_key(self):
""" Get the environemnt key associated with OptimizelyConfig.
Returns:
A string containing environment key.
"""
return self.environment_key


class OptimizelyExperiment(object):
def __init__(self, id, key, variations_map):
Expand Down Expand Up @@ -82,6 +100,8 @@ def __init__(self, project_config):
self.feature_flags = project_config.feature_flags
self.groups = project_config.groups
self.revision = project_config.revision
self.sdk_key = project_config.sdk_key
self.environment_key = project_config.environment_key

self._create_lookup_maps()

Expand All @@ -98,7 +118,13 @@ def get_config(self):
experiments_key_map, experiments_id_map = self._get_experiments_maps()
features_map = self._get_features_map(experiments_id_map)

return OptimizelyConfig(self.revision, experiments_key_map, features_map, self._datafile)
return OptimizelyConfig(
self.revision,
experiments_key_map,
features_map,
self._datafile,
self.sdk_key,
self.environment_key)

def _create_lookup_maps(self):
""" Creates lookup maps to avoid redundant iteration of config objects. """
Expand Down
1 change: 0 additions & 1 deletion optimizely/optimizely_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@ def default_instance_with_config_manager(config_manager):
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.
Expand Down
22 changes: 21 additions & 1 deletion optimizely/project_config.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2016-2019, Optimizely
# Copyright 2016-2019, 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
Expand Down Expand Up @@ -52,6 +52,8 @@ def __init__(self, datafile, logger, error_handler):
self.account_id = config.get('accountId')
self.project_id = config.get('projectId')
self.revision = config.get('revision')
self.sdk_key = config.get('sdkKey', None)
self.environment_key = config.get('environmentKey', None)
self.groups = config.get('groups', [])
self.experiments = config.get('experiments', [])
self.events = config.get('events', [])
Expand Down Expand Up @@ -213,6 +215,24 @@ def get_revision(self):

return self.revision

def get_sdk_key(self):
""" Get sdk key from the datafile.
Returns:
Revision of the sdk key.
"""

return self.sdk_key

def get_environment_key(self):
""" Get environment key from the datafile.
Returns:
Revision of the environment key.
"""

return self.environment_key

def get_account_id(self):
""" Get account ID from the config.
Expand Down
19 changes: 0 additions & 19 deletions tests/test_optimizely.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
from optimizely import optimizely_config
from optimizely import project_config
from optimizely import version
from optimizely.decision.optimizely_decide_option import OptimizelyDecideOption as DecideOption
from optimizely.event.event_factory import EventFactory
from optimizely.helpers import enums
from . import base
Expand Down Expand Up @@ -677,24 +676,6 @@ def on_activate(experiment, user_id, attributes, variation, event):
self.assertEqual(1, mock_process.call_count)
self.assertEqual(True, access_callback[0])

def test_decide_experiment(self):
""" Test that the feature is enabled for the user if bucketed into variation of a rollout.
Also confirm that no impression event is processed. """

opt_obj = optimizely.Optimizely(json.dumps(self.config_dict_with_features))
project_config = opt_obj.config_manager.get_config()

mock_experiment = project_config.get_experiment_from_key('test_experiment')
mock_variation = project_config.get_variation_from_id('test_experiment', '111129')
with mock.patch(
'optimizely.decision_service.DecisionService.get_variation_for_feature',
return_value=(decision_service.Decision(mock_experiment,
mock_variation, enums.DecisionSources.FEATURE_TEST), []),
):
user_context = opt_obj.create_user_context('test_user')
decision = user_context.decide('test_feature_in_experiment', [DecideOption.DISABLE_DECISION_EVENT])
self.assertTrue(decision.enabled, "decision should be enabled")

def test_activate__with_attributes__audience_match(self):
""" Test that activate calls process with right params and returns expected
variation when attributes are provided and audience conditions are met. """
Expand Down
60 changes: 59 additions & 1 deletion tests/test_optimizely_config.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright 2020, Optimizely
# Copyright 2020-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
Expand Down Expand Up @@ -26,6 +26,8 @@ def setUp(self):
self.opt_config_service = optimizely_config.OptimizelyConfigService(self.project_config)

self.expected_config = {
'sdk_key': None,
'environment_key': None,
'experiments_map': {
'test_experiment2': {
'variations_map': {
Expand Down Expand Up @@ -732,3 +734,59 @@ def test__get_datafile(self):
actual_datafile = self.actual_config.get_datafile()

self.assertEqual(expected_datafile, actual_datafile)

def test__get_sdk_key(self):
""" Test that get_sdk_key returns the expected value. """

config = optimizely_config.OptimizelyConfig(
revision='101',
experiments_map={},
features_map={},
sdk_key='testSdkKey',
)

expected_value = 'testSdkKey'

self.assertEqual(expected_value, config.get_sdk_key())

def test__get_sdk_key_invalid(self):
""" Negative Test that tests get_sdk_key does not return the expected value. """

config = optimizely_config.OptimizelyConfig(
revision='101',
experiments_map={},
features_map={},
sdk_key='testSdkKey',
)

invalid_value = 123

self.assertNotEqual(invalid_value, config.get_sdk_key())

def test__get_environment_key(self):
""" Test that get_environment_key returns the expected value. """

config = optimizely_config.OptimizelyConfig(
revision='101',
experiments_map={},
features_map={},
environment_key='TestEnvironmentKey'
)

expected_value = 'TestEnvironmentKey'

self.assertEqual(expected_value, config.get_environment_key())

def test__get_environment_key_invalid(self):
""" Negative Test that tests get_environment_key does not return the expected value. """

config = optimizely_config.OptimizelyConfig(
revision='101',
experiments_map={},
features_map={},
environment_key='testEnvironmentKey'
)

invalid_value = 321

self.assertNotEqual(invalid_value, config.get_environment_key())
49 changes: 49 additions & 0 deletions tests/test_user_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import mock

from optimizely.decision.optimizely_decision import OptimizelyDecision
from optimizely.decision.optimizely_decide_option import OptimizelyDecideOption as DecideOption
from optimizely.helpers import enums
from . import base
from optimizely import optimizely, decision_service
Expand Down Expand Up @@ -60,6 +61,23 @@ def test_user_context(self):
self.assertEqual("firefox", uc.get_user_attributes()["browser"])
self.assertEqual("red", uc.get_user_attributes()["color"])

def test_user_and_attributes_as_json(self):
"""
tests user context as json
"""
uc = OptimizelyUserContext(self.optimizely, "test_user")

# set an attribute
uc.set_attribute("browser", "safari")

# set expected json obj
expected_json = {
"user_id": uc.user_id,
"attributes": uc.get_user_attributes(),
}

self.assertEqual(uc.as_json(), expected_json)

def test_attributes_are_cloned_when_passed_to_user_context(self):
user_id = 'test_user'
attributes = {"browser": "chrome"}
Expand Down Expand Up @@ -1247,3 +1265,34 @@ def test_decide_reasons__whitelisted_variation(self):
expected_reasons = ['User "user_1" is forced in variation "control".']

self.assertEqual(expected_reasons, actual.reasons)

def test_init__invalid_default_decide_options(self):
"""
Test to confirm that default decide options passed not as a list will trigger setting
self.deafulat_decide_options as an empty list.
"""
invalid_decide_options = {"testKey": "testOption"}

mock_client_logger = mock.MagicMock()
with mock.patch('optimizely.logger.reset_logger', return_value=mock_client_logger):
opt_obj = optimizely.Optimizely(default_decide_options=invalid_decide_options)

self.assertEqual(opt_obj.default_decide_options, [])

def test_decide_experiment(self):
""" Test that the feature is enabled for the user if bucketed into variation of a rollout.
Also confirm that no impression event is processed. """

opt_obj = optimizely.Optimizely(json.dumps(self.config_dict_with_features))
project_config = opt_obj.config_manager.get_config()

mock_experiment = project_config.get_experiment_from_key('test_experiment')
mock_variation = project_config.get_variation_from_id('test_experiment', '111129')
with mock.patch(
'optimizely.decision_service.DecisionService.get_variation_for_feature',
return_value=(decision_service.Decision(mock_experiment,
mock_variation, enums.DecisionSources.FEATURE_TEST), []),
):
user_context = opt_obj.create_user_context('test_user')
decision = user_context.decide('test_feature_in_experiment', [DecideOption.DISABLE_DECISION_EVENT])
self.assertTrue(decision.enabled, "decision should be enabled")

0 comments on commit 9e9c4d0

Please sign in to comment.