Skip to content
Snippets Groups Projects
Commit da062310 authored by Régis Behmo's avatar Régis Behmo
Browse files

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.
parent e4cb3dfc
No related branches found
No related tags found
No related merge requests found
"""
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)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment