Merge pull request #7151 from open-craft/ekolpakov/settings-service
SettingsService for accessing django settings from XBlock
This commit is contained in:
63
common/lib/xmodule/xmodule/services.py
Normal file
63
common/lib/xmodule/xmodule/services.py
Normal file
@@ -0,0 +1,63 @@
|
||||
"""
|
||||
Module contains various XModule/XBlock services
|
||||
"""
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
class SettingsService(object):
|
||||
"""
|
||||
Allows server-wide configuration of XBlocks on a per-type basis
|
||||
|
||||
XBlock settings are read from XBLOCK_SETTINGS settings key. Each XBlock is allowed access
|
||||
to single settings bucket. Bucket is determined by this service using the following rules:
|
||||
|
||||
* Value of SettingsService.xblock_settings_bucket_selector is examined. If XBlock have attribute/property
|
||||
with the name of that value this attribute/property is read to get the bucket key (e.g. if XBlock have
|
||||
`block_settings_key = 'my_block_settings'`, bucket key would be 'my_block_settings').
|
||||
* Otherwise, XBlock class name is used
|
||||
|
||||
Service is content-agnostic: it just returns whatever happen to be in the settings bucket (technically, it returns
|
||||
the bucket itself).
|
||||
|
||||
If `default` argument is specified it is returned if:
|
||||
* There are no XBLOCK_SETTINGS setting
|
||||
* XBLOCK_SETTINGS is empty
|
||||
* XBLOCK_SETTINGS does not contain settings bucket
|
||||
|
||||
If `default` is not specified or None, empty dictionary is used for default.
|
||||
|
||||
Example:
|
||||
|
||||
"XBLOCK_SETTINGS": {
|
||||
"my_block": {
|
||||
"setting1": 1,
|
||||
"setting2": []
|
||||
},
|
||||
"my_other_block": [1, 2, 3],
|
||||
"MyThirdBlock": "QWERTY"
|
||||
}
|
||||
|
||||
class MyBlock: block_settings_key='my_block'
|
||||
class MyOtherBlock: block_settings_key='my_other_block'
|
||||
class MyThirdBlock: pass
|
||||
class MissingBlock: pass
|
||||
|
||||
service = SettingsService()
|
||||
service.get_settings_bucket(MyBlock()) # { "setting1": 1, "setting2": [] }
|
||||
service.get_settings_bucket(MyOtherBlock()) # [1, 2, 3]
|
||||
service.get_settings_bucket(MyThirdBlock()) # "QWERTY"
|
||||
service.get_settings_bucket(MissingBlock()) # {}
|
||||
service.get_settings_bucket(MissingBlock(), "default") # "default"
|
||||
service.get_settings_bucket(MissingBlock(), None) # {}
|
||||
"""
|
||||
xblock_settings_bucket_selector = 'block_settings_key'
|
||||
|
||||
def get_settings_bucket(self, block, default=None):
|
||||
""" Gets xblock settings dictionary from settings. """
|
||||
if not block:
|
||||
raise ValueError("Expected XBlock instance, got {0} of type {1}".format(block, type(block)))
|
||||
|
||||
actual_default = default if default is not None else {}
|
||||
xblock_settings_bucket = getattr(block, self.xblock_settings_bucket_selector, block.unmixed_class.__name__)
|
||||
xblock_settings = settings.XBLOCK_SETTINGS if hasattr(settings, "XBLOCK_SETTINGS") else {}
|
||||
return xblock_settings.get(xblock_settings_bucket, actual_default)
|
||||
78
common/lib/xmodule/xmodule/tests/test_services.py
Normal file
78
common/lib/xmodule/xmodule/tests/test_services.py
Normal file
@@ -0,0 +1,78 @@
|
||||
"""
|
||||
Tests for SettingsService
|
||||
"""
|
||||
|
||||
import ddt
|
||||
import mock
|
||||
from unittest import TestCase
|
||||
|
||||
from django.conf import settings
|
||||
from django.test.utils import override_settings
|
||||
|
||||
from xblock.runtime import Mixologist
|
||||
from xmodule.services import SettingsService
|
||||
|
||||
|
||||
class _DummyBlock(object):
|
||||
""" Dummy Xblock class """
|
||||
pass
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class TestSettingsService(TestCase):
|
||||
""" Test SettingsService """
|
||||
|
||||
xblock_setting_key1 = 'dummy_block'
|
||||
xblock_setting_key2 = 'other_dummy_block'
|
||||
|
||||
def setUp(self):
|
||||
""" Setting up tests """
|
||||
super(TestSettingsService, self).setUp()
|
||||
self.settings_service = SettingsService()
|
||||
self.xblock_mock = mock.Mock()
|
||||
self.xblock_mock.block_settings_key = self.xblock_setting_key1
|
||||
self.xblock_mock.unmixed_class = mock.Mock()
|
||||
self.xblock_mock.unmixed_class.__name__ = self.xblock_setting_key2
|
||||
|
||||
def test_get_given_none_throws_value_error(self):
|
||||
""" Test that given None throws value error """
|
||||
with self.assertRaises(ValueError):
|
||||
self.settings_service.get_settings_bucket(None)
|
||||
|
||||
def test_get_return_default_if_xblock_settings_is_missing(self):
|
||||
""" Test that returns default (or None if default not set) if XBLOCK_SETTINGS is not set """
|
||||
self.assertFalse(hasattr(settings, 'XBLOCK_SETTINGS')) # precondition check
|
||||
self.assertEqual(self.settings_service.get_settings_bucket(self.xblock_mock, 'zzz'), 'zzz')
|
||||
|
||||
def test_get_return_empty_dictionary_if_xblock_settings_and_default_is_missing(self):
|
||||
""" Test that returns default (or None if default not set) if XBLOCK_SETTINGS is not set """
|
||||
self.assertFalse(hasattr(settings, 'XBLOCK_SETTINGS')) # precondition check
|
||||
self.assertEqual(self.settings_service.get_settings_bucket(self.xblock_mock), {})
|
||||
|
||||
@override_settings(XBLOCK_SETTINGS={xblock_setting_key2: {'b': 1}})
|
||||
def test_get_returns_none_or_default_if_bucket_not_found(self):
|
||||
""" Test if settings service returns default if setting not found """
|
||||
self.assertEqual(getattr(settings, 'XBLOCK_SETTINGS'), {self.xblock_setting_key2: {'b': 1}})
|
||||
self.assertEqual(self.settings_service.get_settings_bucket(self.xblock_mock), {})
|
||||
self.assertEqual(self.settings_service.get_settings_bucket(self.xblock_mock, 123), 123)
|
||||
|
||||
@override_settings(XBLOCK_SETTINGS={xblock_setting_key1: 42})
|
||||
def test_get_returns_correct_value(self):
|
||||
""" Test if settings service returns correct bucket """
|
||||
self.assertEqual(getattr(settings, 'XBLOCK_SETTINGS'), {self.xblock_setting_key1: 42})
|
||||
self.assertEqual(self.settings_service.get_settings_bucket(self.xblock_mock), 42)
|
||||
|
||||
@override_settings(XBLOCK_SETTINGS={xblock_setting_key2: "I'm a setting"})
|
||||
def test_get_respects_block_settings_key(self):
|
||||
""" Test if settings service respects block_settings_key value """
|
||||
self.assertEqual(getattr(settings, 'XBLOCK_SETTINGS'), {self.xblock_setting_key2: "I'm a setting"})
|
||||
self.xblock_mock.block_settings_key = self.xblock_setting_key2
|
||||
self.assertEqual(self.settings_service.get_settings_bucket(self.xblock_mock), "I'm a setting")
|
||||
|
||||
@override_settings(XBLOCK_SETTINGS={_DummyBlock.__name__: [1, 2, 3]})
|
||||
def test_get_uses_class_name_if_block_settings_key_is_not_set(self):
|
||||
""" Test if settings service uses class name if block_settings_key attribute does not exist """
|
||||
mixologist = Mixologist([])
|
||||
block = mixologist.mix(_DummyBlock)
|
||||
self.assertEqual(getattr(settings, 'XBLOCK_SETTINGS'), {"_DummyBlock": [1, 2, 3]})
|
||||
self.assertEqual(self.settings_service.get_settings_bucket(block), [1, 2, 3])
|
||||
Reference in New Issue
Block a user