Skip to content

Commit

Permalink
Merge 641cef9 into ddf06ed
Browse files Browse the repository at this point in the history
  • Loading branch information
pthompson127 committed Jul 6, 2020
2 parents ddf06ed + 641cef9 commit fbeeeee
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 41 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,15 +127,15 @@ implements `PollingConfigManager` and asynchronously polls for authenticated dat
by making HTTP requests.

auth_datafile_polling_config_manager = AuthDatafilePollingConfigManager(
access_token,
datafile_access_token,
*args,
**kwargs
)

**Note**: To use [AuthDatafilePollingConfigManager](#authdatafilepollingconfigmanager), you must create a secure environment for
your project and generate an access token for your datafile.

**access_token** The access_token is attached to the outbound HTTP request header to authorize the request and fetch the datafile.
**datafile_access_token** The datafile_access_token is attached to the outbound HTTP request header to authorize the request and fetch the datafile.

#### Advanced configuration

Expand Down
38 changes: 19 additions & 19 deletions optimizely/config_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,9 @@ def __init__(
def _set_config(self, datafile):
""" Looks up and sets datafile and config based on response body.
Args:
datafile: JSON string representing the Optimizely project.
"""
Args:
datafile: JSON string representing the Optimizely project.
"""

if self.validate_schema:
if not validator.is_datafile_valid(datafile):
Expand Down Expand Up @@ -239,9 +239,9 @@ def get_datafile_url(sdk_key, url, url_template):
def _set_config(self, datafile):
""" Looks up and sets datafile and config based on response body.
Args:
datafile: JSON string representing the Optimizely project.
"""
Args:
datafile: JSON string representing the Optimizely project.
"""
if datafile or self._config_ready_event.is_set():
super(PollingConfigManager, self)._set_config(datafile=datafile)
self._config_ready_event.set()
Expand All @@ -261,7 +261,7 @@ def set_update_interval(self, update_interval):
""" Helper method to set frequency at which datafile has to be polled and ProjectConfig updated.
Args:
update_interval: Time in seconds after which to update datafile.
update_interval: Time in seconds after which to update datafile.
"""
if update_interval is None:
update_interval = enums.ConfigManager.DEFAULT_UPDATE_INTERVAL
Expand All @@ -287,7 +287,7 @@ def set_blocking_timeout(self, blocking_timeout):
""" Helper 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.
blocking_timeout: Time in seconds to block the config call.
"""
if blocking_timeout is None:
blocking_timeout = enums.ConfigManager.DEFAULT_BLOCKING_TIMEOUT
Expand All @@ -312,9 +312,9 @@ def set_blocking_timeout(self, blocking_timeout):
def set_last_modified(self, response_headers):
""" Looks up and sets last modified time based on Last-Modified header in the response.
Args:
response_headers: requests.Response.headers
"""
Args:
response_headers: requests.Response.headers
"""
self.last_modified = response_headers.get(enums.HTTPHeaders.LAST_MODIFIED)

def _handle_response(self, response):
Expand Down Expand Up @@ -379,32 +379,32 @@ class AuthDatafilePollingConfigManager(PollingConfigManager):

def __init__(
self,
access_token,
datafile_access_token,
*args,
**kwargs
):
""" Initialize config manager. One of sdk_key or url has to be set to be able to use.
Args:
access_token: String to be attached to the request header to fetch the authenticated datafile.
datafile_access_token: String to be attached to the request header to fetch the authenticated datafile.
*args: Refer to arguments descriptions in PollingConfigManager.
**kwargs: Refer to keyword arguments descriptions in PollingConfigManager.
"""
self._set_access_token(access_token)
self._set_datafile_access_token(datafile_access_token)
super(AuthDatafilePollingConfigManager, self).__init__(*args, **kwargs)

def _set_access_token(self, access_token):
def _set_datafile_access_token(self, datafile_access_token):
""" Checks for valid access token input and sets it. """
if not access_token:
if not datafile_access_token:
raise optimizely_exceptions.InvalidInputException(
'access_token cannot be empty or None.')
self.access_token = access_token
'datafile_access_token cannot be empty or None.')
self.datafile_access_token = datafile_access_token

def fetch_datafile(self):
""" Fetch authenticated datafile and set ProjectConfig. """
request_headers = {
enums.HTTPHeaders.AUTHORIZATION: enums.ConfigManager.AUTHORIZATION_HEADER_DATA_TEMPLATE.format(
access_token=self.access_token
datafile_access_token=self.datafile_access_token
)
}

Expand Down
2 changes: 1 addition & 1 deletion optimizely/helpers/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class RolloutRuleAudienceEvaluationLogs(CommonAudienceEvaluationLogs):

class ConfigManager(object):
AUTHENTICATED_DATAFILE_URL_TEMPLATE = 'https://config.optimizely.com/datafiles/auth/{sdk_key}.json'
AUTHORIZATION_HEADER_DATA_TEMPLATE = 'Bearer {access_token}'
AUTHORIZATION_HEADER_DATA_TEMPLATE = 'Bearer {datafile_access_token}'
DATAFILE_URL_TEMPLATE = 'https://cdn.optimizely.com/datafiles/{sdk_key}.json'
# Default time in seconds to block the 'get_config' method call until 'config' instance has been initialized.
DEFAULT_BLOCKING_TIMEOUT = 10
Expand Down
8 changes: 4 additions & 4 deletions optimizely/optimizely.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def __init__(
config_manager=None,
notification_center=None,
event_processor=None,
access_token=None,
datafile_access_token=None,
):
""" Optimizely init method for managing Custom projects.
Expand All @@ -67,7 +67,7 @@ def __init__(
By default optimizely.event.event_processor.ForwardingEventProcessor is used
which simply forwards events to the event dispatcher.
To enable event batching configure and use optimizely.event.event_processor.BatchEventProcessor.
access_token: Optional string used to fetch authenticated datafile for a secure project environment.
datafile_access_token: Optional string used to fetch authenticated datafile for a secure project environment.
"""
self.logger_name = '.'.join([__name__, self.__class__.__name__])
self.is_valid = True
Expand Down Expand Up @@ -101,8 +101,8 @@ def __init__(
if not self.config_manager:
if sdk_key:
config_manager_options['sdk_key'] = sdk_key
if access_token:
config_manager_options['access_token'] = access_token
if datafile_access_token:
config_manager_options['datafile_access_token'] = datafile_access_token
self.config_manager = AuthDatafilePollingConfigManager(**config_manager_options)
else:
self.config_manager = PollingConfigManager(**config_manager_options)
Expand Down
25 changes: 13 additions & 12 deletions tests/test_config_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,34 +402,34 @@ def test_is_running(self, _):

@mock.patch('requests.get')
class AuthDatafilePollingConfigManagerTest(base.BaseTest):
def test_init__access_token_none__fails(self, _):
""" Test that initialization fails if access_token is None. """
def test_init__datafile_access_token_none__fails(self, _):
""" Test that initialization fails if datafile_access_token is None. """
self.assertRaisesRegexp(
optimizely_exceptions.InvalidInputException,
'access_token cannot be empty or None.',
'datafile_access_token cannot be empty or None.',
config_manager.AuthDatafilePollingConfigManager,
access_token=None
datafile_access_token=None
)

def test_set_access_token(self, _):
""" Test that access_token is properly set as instance variable. """
access_token = 'some_token'
def test_set_datafile_access_token(self, _):
""" Test that datafile_access_token is properly set as instance variable. """
datafile_access_token = 'some_token'
sdk_key = 'some_key'
with mock.patch('optimizely.config_manager.AuthDatafilePollingConfigManager.fetch_datafile'):
project_config_manager = config_manager.AuthDatafilePollingConfigManager(
access_token=access_token, sdk_key=sdk_key)
datafile_access_token=datafile_access_token, sdk_key=sdk_key)

self.assertEqual(access_token, project_config_manager.access_token)
self.assertEqual(datafile_access_token, project_config_manager.datafile_access_token)

def test_fetch_datafile(self, _):
""" Test that fetch_datafile sets authorization header in request header and sets config based on response. """
access_token = 'some_token'
datafile_access_token = 'some_token'
sdk_key = 'some_key'
with mock.patch('optimizely.config_manager.AuthDatafilePollingConfigManager.fetch_datafile'), mock.patch(
'optimizely.config_manager.AuthDatafilePollingConfigManager._run'
):
project_config_manager = config_manager.AuthDatafilePollingConfigManager(
access_token=access_token, sdk_key=sdk_key)
datafile_access_token=datafile_access_token, sdk_key=sdk_key)
expected_datafile_url = enums.ConfigManager.AUTHENTICATED_DATAFILE_URL_TEMPLATE.format(sdk_key=sdk_key)
test_headers = {'Last-Modified': 'New Time'}
test_datafile = json.dumps(self.config_dict_with_features)
Expand All @@ -445,7 +445,8 @@ def test_fetch_datafile(self, _):

mock_request.assert_called_once_with(
expected_datafile_url,
headers={'Authorization': 'Bearer {access_token}'.format(access_token=access_token)},
headers={'Authorization': 'Bearer {datafile_access_token}'.format(
datafile_access_token=datafile_access_token)},
timeout=enums.ConfigManager.REQUEST_TIMEOUT,
)

Expand Down
9 changes: 6 additions & 3 deletions tests/test_optimizely.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,13 +252,16 @@ def test_init__sdk_key_and_datafile(self):

self.assertIs(type(opt_obj.config_manager), config_manager.PollingConfigManager)

def test_init__sdk_key_and_access_token(self):
""" Test that if both sdk_key and access_token is provided then AuthDatafilePollingConfigManager is used. """
def test_init__sdk_key_and_datafile_access_token(self):
"""
Test that if both sdk_key and datafile_access_token is provided then AuthDatafilePollingConfigManager
is used.
"""

with mock.patch('optimizely.config_manager.AuthDatafilePollingConfigManager._set_config'), mock.patch(
'threading.Thread.start'
):
opt_obj = optimizely.Optimizely(access_token='test_access_token', sdk_key='test_sdk_key')
opt_obj = optimizely.Optimizely(datafile_access_token='test_datafile_access_token', sdk_key='test_sdk_key')

self.assertIs(type(opt_obj.config_manager), config_manager.AuthDatafilePollingConfigManager)

Expand Down

0 comments on commit fbeeeee

Please sign in to comment.