Commit 3f6fec46 by Matt Drayer

mattdrayer/multitenant-fulfillment: Add site-specific configuration support

* mattdrayer: Enable site-level overrides for all settings
* mattdrayer: Update edx-lint to 0.5.1
* mattdrayer: Increment minor version
parent 7d0c31c8
...@@ -47,3 +47,7 @@ JWT_ISSUER = None ...@@ -47,3 +47,7 @@ JWT_ISSUER = None
ECOMMERCE_SERVICE_USERNAME = 'ecommerce_worker' ECOMMERCE_SERVICE_USERNAME = 'ecommerce_worker'
# END AUTHENTICATION # END AUTHENTICATION
# Site Overrides provide support for site/partner-specific configuration settings where applicable
# For example, the ECOMMERCE_API_ROOT value is different from one ecommerce site to the next
SITE_OVERRIDES = None
...@@ -20,9 +20,14 @@ logger_config = get_logger_config(debug=True, dev_env=True, local_loglevel='DEBU ...@@ -20,9 +20,14 @@ logger_config = get_logger_config(debug=True, dev_env=True, local_loglevel='DEBU
dictConfig(logger_config) dictConfig(logger_config)
# END LOGGING # END LOGGING
# AUTHENTICATION # AUTHENTICATION
JWT_SECRET_KEY = 'test-key' JWT_SECRET_KEY = 'test-key'
JWT_ISSUER = 'ecommerce_worker' JWT_ISSUER = 'ecommerce_worker'
ECOMMERCE_SERVICE_USERNAME = 'service' ECOMMERCE_SERVICE_USERNAME = 'service'
# END AUTHENTICATION # END AUTHENTICATION
# SITE-SPECIFIC CONFIGURATION OVERRIDES
SITE_OVERRIDES = {}
...@@ -11,7 +11,7 @@ logger = get_task_logger(__name__) # pylint: disable=invalid-name ...@@ -11,7 +11,7 @@ logger = get_task_logger(__name__) # pylint: disable=invalid-name
@shared_task(bind=True, ignore_result=True) @shared_task(bind=True, ignore_result=True)
def fulfill_order(self, order_number): def fulfill_order(self, order_number, site_code=None):
"""Fulfills an order. """Fulfills an order.
Arguments: Arguments:
...@@ -20,11 +20,11 @@ def fulfill_order(self, order_number): ...@@ -20,11 +20,11 @@ def fulfill_order(self, order_number):
Returns: Returns:
None None
""" """
ecommerce_api_root = get_configuration('ECOMMERCE_API_ROOT') ecommerce_api_root = get_configuration('ECOMMERCE_API_ROOT', site_code=site_code)
max_fulfillment_retries = get_configuration('MAX_FULFILLMENT_RETRIES') max_fulfillment_retries = get_configuration('MAX_FULFILLMENT_RETRIES', site_code=site_code)
signing_key = get_configuration('JWT_SECRET_KEY') signing_key = get_configuration('JWT_SECRET_KEY', site_code=site_code)
issuer = get_configuration('JWT_ISSUER') issuer = get_configuration('JWT_ISSUER', site_code=site_code)
service_username = get_configuration('ECOMMERCE_SERVICE_USERNAME') service_username = get_configuration('ECOMMERCE_SERVICE_USERNAME', site_code=site_code)
api = EdxRestApiClient(ecommerce_api_root, signing_key=signing_key, issuer=issuer, username=service_username) api = EdxRestApiClient(ecommerce_api_root, signing_key=signing_key, issuer=issuer, username=service_username)
try: try:
......
"""
Tests for unversioned packages and modules
"""
""" Test coverage for ecommerce_worker/utils.py """
from unittest import TestCase
import ddt
import mock
from ecommerce_worker.configuration.test import ECOMMERCE_API_ROOT
from ecommerce_worker.utils import get_configuration
@ddt.ddt
class GetConfigurationTests(TestCase):
"""Tests covering the get_configuration operation."""
SITE_OVERRIDES_MODULE = 'ecommerce_worker.configuration.test.SITE_OVERRIDES'
TEST_SETTING = 'ECOMMERCE_API_ROOT'
OVERRIDES_DICT = {
'openedx': {'ECOMMERCE_API_ROOT': 'http://openedx.org'},
'test': {'ECOMMERCE_API_ROOT': 'http://example.com'},
'novalue': {'ECOMMERCE_API_ROOT': None},
'emptydict': {}
}
def test_configuration_no_site_overrides(self):
test_setting = get_configuration(self.TEST_SETTING)
self.assertEqual(test_setting, ECOMMERCE_API_ROOT)
def test_configuration_no_site_code_specified(self):
with mock.patch.dict(self.SITE_OVERRIDES_MODULE, self.OVERRIDES_DICT):
test_setting = get_configuration(self.TEST_SETTING)
self.assertEqual(test_setting, ECOMMERCE_API_ROOT)
@ddt.data('openedx', 'test')
def test_configuration_valid_site_code_dict_value(self, site_code):
"""
Confirm that valid SITE_OVERRIDES parameters are correctly returned
"""
with mock.patch.dict(self.SITE_OVERRIDES_MODULE, self.OVERRIDES_DICT):
test_setting = get_configuration(self.TEST_SETTING, site_code=site_code)
self.assertEqual(test_setting, self.OVERRIDES_DICT[site_code][self.TEST_SETTING])
@ddt.data(None, 'invalid', 'emptydict', 'novalue')
def test_configuration_no_site_code_matched(self, site_code):
"""
Test various states of SITE_OVERRIDES to ensure all branches (ie, use cases) are covered
"""
with mock.patch.dict(self.SITE_OVERRIDES_MODULE, self.OVERRIDES_DICT):
test_setting = get_configuration(self.TEST_SETTING, site_code=site_code)
self.assertEqual(test_setting, ECOMMERCE_API_ROOT)
...@@ -5,15 +5,19 @@ import sys ...@@ -5,15 +5,19 @@ import sys
from ecommerce_worker.configuration import CONFIGURATION_MODULE from ecommerce_worker.configuration import CONFIGURATION_MODULE
def get_configuration(variable): def get_configuration(variable, site_code=None):
"""Get a value from configuration. """
Get a value from configuration.
Retrieves the value corresponding to the given variable from the Retrieves the value corresponding to the given variable from the configuration module
configuration module currently in use by the app. currently in use by the app. Specify a site_code value to check for a site-specific override.
Arguments: Arguments:
variable (str): The name of a variable from the configuration module. variable (str): The name of a variable from the configuration module.
Keyword Arguments:
site_code (str): The SITE_OVERRIDES key to inspect for site-specific values
Returns: Returns:
The value corresponding to the variable, or None if the variable is not found. The value corresponding to the variable, or None if the variable is not found.
""" """
...@@ -25,7 +29,16 @@ def get_configuration(variable): ...@@ -25,7 +29,16 @@ def get_configuration(variable):
__import__(name) __import__(name)
module = sys.modules[name] module = sys.modules[name]
value = getattr(module, variable, None) # Locate the setting in the specified module, then attempt to apply a site-specific override
if value is None: setting_value = getattr(module, variable, None)
site_overrides = getattr(module, 'SITE_OVERRIDES', None)
if site_overrides:
site_specific_overrides = site_overrides.get(site_code)
if site_specific_overrides:
override_value = site_specific_overrides.get(variable)
if override_value:
setting_value = override_value
if setting_value is None:
raise RuntimeError('Worker is improperly configured: {} is unset in {}.'.format(variable, module)) raise RuntimeError('Worker is improperly configured: {} is unset in {}.'.format(variable, module))
return value return setting_value
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
coverage==4.0.3 coverage==4.0.3
ddt==1.0.1 ddt==1.0.1
edx-lint==0.4.3 edx-lint==0.5.1
httpretty==0.8.10 httpretty==0.8.10
mock==1.3.0 mock==1.3.0
nose==1.3.7 nose==1.3.7
......
...@@ -6,7 +6,7 @@ with open('README.rst') as readme: ...@@ -6,7 +6,7 @@ with open('README.rst') as readme:
setup( setup(
name='edx-ecommerce-worker', name='edx-ecommerce-worker',
version='0.3.3', version='0.4.0',
description='Celery tasks supporting the operations of edX\'s ecommerce service', description='Celery tasks supporting the operations of edX\'s ecommerce service',
long_description=long_description, long_description=long_description,
classifiers=[ classifiers=[
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment