Expose SettingToggle and SettingDictToggle objects in the API
Note that settings for which a corresponding SettingToggle or SettingDictToggle exists are no longer exposed in the "django_settings" list of the API.
This commit is contained in:
@@ -1,7 +1,10 @@
|
||||
"""
|
||||
Tests for waffle utils views.
|
||||
"""
|
||||
from django.conf import settings
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from edx_toggles.toggles import SettingDictToggle, SettingToggle
|
||||
from edx_toggles.toggles.testutils import override_waffle_flag
|
||||
from rest_framework.test import APIRequestFactory
|
||||
from waffle.testutils import override_switch
|
||||
@@ -11,8 +14,8 @@ from student.tests.factories import UserFactory
|
||||
from .. import WaffleFlag, WaffleFlagNamespace
|
||||
from ..views import ToggleStateView
|
||||
|
||||
TEST_WAFFLE_FLAG_NAMESPACE = WaffleFlagNamespace('test')
|
||||
TEST_WAFFLE_FLAG = WaffleFlag(TEST_WAFFLE_FLAG_NAMESPACE, 'flag', __name__)
|
||||
TEST_WAFFLE_FLAG_NAMESPACE = WaffleFlagNamespace("test")
|
||||
TEST_WAFFLE_FLAG = WaffleFlag(TEST_WAFFLE_FLAG_NAMESPACE, "flag", __name__)
|
||||
|
||||
|
||||
# TODO: Missing coverage for:
|
||||
@@ -21,7 +24,7 @@ TEST_WAFFLE_FLAG = WaffleFlag(TEST_WAFFLE_FLAG_NAMESPACE, 'flag', __name__)
|
||||
class ToggleStateViewTests(TestCase):
|
||||
|
||||
def test_success_for_staff(self):
|
||||
response = self._get_toggle_state_response(is_staff=True)
|
||||
response = self._get_toggle_state_response()
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertTrue(response.data)
|
||||
|
||||
@@ -31,19 +34,105 @@ class ToggleStateViewTests(TestCase):
|
||||
|
||||
@override_waffle_flag(TEST_WAFFLE_FLAG, True)
|
||||
def test_response_with_waffle_flag(self):
|
||||
response = self._get_toggle_state_response(is_staff=True)
|
||||
response = self._get_toggle_state_response()
|
||||
self.assertIn('waffle_flags', response.data)
|
||||
self.assertTrue(response.data['waffle_flags'])
|
||||
# This is no longer the first flag
|
||||
#self.assertEqual(response.data['waffle_flags'][0]['name'], 'test.flag')
|
||||
waffle_names = [waffle["name"] for waffle in response.data['waffle_flags']]
|
||||
self.assertIn('test.flag', waffle_names)
|
||||
|
||||
@override_switch('test.switch', True)
|
||||
def test_response_with_waffle_switch(self):
|
||||
response = self._get_toggle_state_response(is_staff=True)
|
||||
response = self._get_toggle_state_response()
|
||||
self.assertIn('waffle_switches', response.data)
|
||||
self.assertTrue(response.data['waffle_switches'])
|
||||
# This is no longer the first switch
|
||||
#self.assertEqual(response.data['waffle_switches'][0]['name'], 'test.switch')
|
||||
waffle_names = [waffle["name"] for waffle in response.data['waffle_switches']]
|
||||
self.assertIn('test.switch', waffle_names)
|
||||
|
||||
def test_response_with_setting_toggle(self):
|
||||
_toggle = SettingToggle("MYSETTING", default=False, module_name="module1")
|
||||
with override_settings(MYSETTING=True):
|
||||
response = self._get_toggle_state_response()
|
||||
|
||||
self.assertIn(
|
||||
{
|
||||
"name": "MYSETTING",
|
||||
"is_active": True,
|
||||
"module": "module1",
|
||||
"class": "SettingToggle",
|
||||
},
|
||||
response.data["django_settings"],
|
||||
)
|
||||
|
||||
def test_response_with_existing_setting_dict_toggle(self):
|
||||
response = self._get_toggle_state_response()
|
||||
self.assertIn(
|
||||
{
|
||||
"name": "FEATURES['MILESTONES_APP']",
|
||||
"is_active": True,
|
||||
"module": "util.milestones_helpers",
|
||||
"class": "SettingDictToggle",
|
||||
},
|
||||
response.data["django_settings"],
|
||||
)
|
||||
|
||||
def test_response_with_new_setting_dict_toggle(self):
|
||||
_toggle = SettingDictToggle(
|
||||
"CUSTOM_FEATURES", "MYSETTING", default=False, module_name="module1"
|
||||
)
|
||||
with override_settings(CUSTOM_FEATURES={"MYSETTING": True}):
|
||||
response = self._get_toggle_state_response()
|
||||
|
||||
setting_dict = {toggle["name"]: toggle for toggle in response.data["django_settings"]}
|
||||
|
||||
self.assertEqual(
|
||||
{
|
||||
"name": "CUSTOM_FEATURES['MYSETTING']",
|
||||
"is_active": True,
|
||||
"module": "module1",
|
||||
"class": "SettingDictToggle",
|
||||
},
|
||||
setting_dict["CUSTOM_FEATURES['MYSETTING']"],
|
||||
)
|
||||
|
||||
def test_setting_overridden_by_setting_toggle(self):
|
||||
_toggle2 = SettingToggle(
|
||||
"MYSETTING2", module_name="module1"
|
||||
)
|
||||
_toggle3 = SettingDictToggle(
|
||||
"MYDICT", "MYSETTING3", module_name="module1"
|
||||
)
|
||||
with override_settings(MYSETTING1=True, MYSETTING2=False, MYDICT={"MYSETTING3": False}):
|
||||
# Need to pre-load settings, otherwise they are not picked up by the view
|
||||
self.assertTrue(settings.MYSETTING1)
|
||||
response = self._get_toggle_state_response()
|
||||
|
||||
setting_dict = {toggle["name"]: toggle for toggle in response.data["django_settings"]}
|
||||
|
||||
# Check that Django settings for which a SettingToggle exists have both the correct is_active and class values
|
||||
self.assertTrue(setting_dict["MYSETTING1"]["is_active"])
|
||||
self.assertNotIn("class", setting_dict["MYSETTING1"])
|
||||
self.assertFalse(setting_dict["MYSETTING2"]["is_active"])
|
||||
self.assertEqual("SettingToggle", setting_dict["MYSETTING2"]["class"])
|
||||
self.assertFalse(setting_dict["MYDICT['MYSETTING3']"]["is_active"])
|
||||
self.assertEqual("SettingDictToggle", setting_dict["MYDICT['MYSETTING3']"]["class"])
|
||||
|
||||
def test_no_duplicate_setting_toggle(self):
|
||||
_toggle1 = SettingToggle(
|
||||
"MYSETTING1", module_name="module1"
|
||||
)
|
||||
_toggle2 = SettingDictToggle(
|
||||
"MYDICT", "MYSETTING2", module_name="module1"
|
||||
)
|
||||
with override_settings(MYSETTING1=True, MYDICT={"MYSETTING2": False}):
|
||||
response = self._get_toggle_state_response()
|
||||
|
||||
# Check there are no duplicate setting/toggle
|
||||
response_toggles_1 = [toggle for toggle in response.data["django_settings"] if toggle["name"] == "MYSETTING1"]
|
||||
response_toggles_2 = [
|
||||
toggle for toggle in response.data["django_settings"] if toggle["name"] == "MYDICT['MYSETTING2']"
|
||||
]
|
||||
self.assertEqual(1, len(response_toggles_1))
|
||||
self.assertEqual(1, len(response_toggles_2))
|
||||
|
||||
def test_code_owners_without_module_information(self):
|
||||
# Create a waffle flag without any associated module_name
|
||||
|
||||
@@ -7,6 +7,7 @@ from django.conf import settings
|
||||
from edx_django_utils.monitoring import get_code_owner_from_module
|
||||
from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication
|
||||
from edx_rest_framework_extensions.permissions import IsStaff
|
||||
from edx_toggles.toggles import SettingDictToggle, SettingToggle
|
||||
from rest_framework import permissions, views
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
from rest_framework.response import Response
|
||||
@@ -214,20 +215,52 @@ class ToggleStateView(views.APIView):
|
||||
|
||||
def _get_settings_state(self):
|
||||
"""
|
||||
Returns a dictionary of settings values. Will only return values that are set to true or false.
|
||||
Return a list of setting-based toggles: Django settings, SettingToggle and SettingDictToggle instances.
|
||||
SettingToggle and SettingDictToggle override the settings with identical names (if any).
|
||||
"""
|
||||
settings_dict = {}
|
||||
self._add_settings(settings_dict)
|
||||
self._add_setting_toggles(settings_dict)
|
||||
self._add_setting_dict_toggles(settings_dict)
|
||||
return sorted(settings_dict.values(), key=(lambda toggle: toggle['name']))
|
||||
|
||||
bool_settings = list()
|
||||
def _add_settings(self, settings_dict):
|
||||
"""
|
||||
Fill the `settings_dict`: will only include values that are set to true or false.
|
||||
"""
|
||||
for setting_name, setting_value in vars(settings).items():
|
||||
if isinstance(setting_value, dict):
|
||||
for dict_name, dict_value in setting_value.items():
|
||||
if isinstance(dict_value, bool):
|
||||
bool_settings.append(
|
||||
{
|
||||
'name': "{setting_name}['{dict_name}']".format(setting_name=setting_name, dict_name=dict_name),
|
||||
'is_active': dict_value,
|
||||
}
|
||||
)
|
||||
name = setting_dict_name(setting_name, dict_name)
|
||||
toggle_response = self._get_or_create_toggle_response(settings_dict, name)
|
||||
toggle_response['is_active'] = dict_value
|
||||
elif isinstance(setting_value, bool):
|
||||
bool_settings.append({'name': setting_name, 'is_active': setting_value})
|
||||
return bool_settings
|
||||
toggle_response = self._get_or_create_toggle_response(settings_dict, setting_name)
|
||||
toggle_response['is_active'] = setting_value
|
||||
|
||||
def _add_setting_toggles(self, settings_dict):
|
||||
"""
|
||||
Fill the `settings_dict` with values from the list of SettingToggle instances.
|
||||
"""
|
||||
for toggle in SettingToggle.get_instances():
|
||||
toggle_response = self._get_or_create_toggle_response(settings_dict, toggle.name)
|
||||
toggle_response["is_active"] = toggle.is_enabled()
|
||||
self._add_toggle_instance_details(toggle_response, toggle)
|
||||
|
||||
def _add_setting_dict_toggles(self, settings_dict):
|
||||
"""
|
||||
Fill the `settings_dict` with values from the list of SettingDictToggle instances.
|
||||
"""
|
||||
for toggle in SettingDictToggle.get_instances():
|
||||
name = setting_dict_name(toggle.name, toggle.key)
|
||||
toggle_response = self._get_or_create_toggle_response(settings_dict, name)
|
||||
toggle_response["is_active"] = toggle.is_enabled()
|
||||
self._add_toggle_instance_details(toggle_response, toggle)
|
||||
|
||||
|
||||
def setting_dict_name(dict_name, key):
|
||||
"""
|
||||
Return the name associated to a `dict_name[key]` setting.
|
||||
"""
|
||||
return "{dict_name}['{key}']".format(dict_name=dict_name, key=key)
|
||||
|
||||
Reference in New Issue
Block a user