diff --git a/cms/djangoapps/models/settings/course_metadata.py b/cms/djangoapps/models/settings/course_metadata.py index fd5219dfb4..07ee360ed7 100644 --- a/cms/djangoapps/models/settings/course_metadata.py +++ b/cms/djangoapps/models/settings/course_metadata.py @@ -217,7 +217,10 @@ class CourseMetadata: try: val = model['value'] if hasattr(block, key) and getattr(block, key) != val: - key_values[key] = block.fields[key].from_json(val) + if key == 'proctoring_provider': + key_values[key] = block.fields[key].from_json(val, validate_providers=True) + else: + key_values[key] = block.fields[key].from_json(val) except (TypeError, ValueError) as err: raise ValueError(_("Incorrect format for field '{name}'. {detailed_message}").format( # lint-amnesty, pylint: disable=raise-missing-from name=model['display_name'], detailed_message=str(err))) @@ -253,7 +256,10 @@ class CourseMetadata: try: val = model['value'] if hasattr(block, key) and getattr(block, key) != val: - key_values[key] = block.fields[key].from_json(val) + if key == 'proctoring_provider': + key_values[key] = block.fields[key].from_json(val, validate_providers=True) + else: + key_values[key] = block.fields[key].from_json(val) except (TypeError, ValueError, ValidationError) as err: did_validate = False errors.append({'key': key, 'message': str(err), 'model': model}) diff --git a/xmodule/course_block.py b/xmodule/course_block.py index 52422ffa34..46ad7476f6 100644 --- a/xmodule/course_block.py +++ b/xmodule/course_block.py @@ -228,7 +228,7 @@ class ProctoringProvider(String): and default that pulls from edx platform settings. """ - def from_json(self, value): + def from_json(self, value, validate_providers=False): """ Return ProctoringProvider as full featured Python type. Perform validation on the provider and include any inherited values from the platform default. @@ -237,7 +237,8 @@ class ProctoringProvider(String): if settings.FEATURES.get('ENABLE_PROCTORED_EXAMS'): # Only validate the provider value if ProctoredExams are enabled on the environment # Otherwise, the passed in provider does not matter. We should always return default - self._validate_proctoring_provider(value) + if validate_providers: + self._validate_proctoring_provider(value) value = self._get_proctoring_value(value) return value else: diff --git a/xmodule/tests/test_course_block.py b/xmodule/tests/test_course_block.py index 39c6c39e87..f2956cca0d 100644 --- a/xmodule/tests/test_course_block.py +++ b/xmodule/tests/test_course_block.py @@ -542,14 +542,27 @@ class ProctoringProviderTestCase(unittest.TestCase): with override_settings(FEATURES=FEATURES_WITH_PROCTORED_EXAMS): if proctored_exams_setting_enabled: with pytest.raises(InvalidProctoringProvider) as context_manager: - self.proctoring_provider.from_json(provider) + self.proctoring_provider.from_json(provider, validate_providers=True) expected_error = f'The selected proctoring provider, {provider}, is not a valid provider. ' \ f'Please select from one of {allowed_proctoring_providers}.' assert str(context_manager.value) == expected_error else: - provider_value = self.proctoring_provider.from_json(provider) + provider_value = self.proctoring_provider.from_json(provider, validate_providers=True) assert provider_value == self.proctoring_provider.default + def test_from_json_validate_providers(self): + """ + Test that an invalid provider is ignored if validate providers is set to false + """ + provider = 'invalid-provider' + + FEATURES_WITH_PROCTORED_EXAMS = settings.FEATURES.copy() + FEATURES_WITH_PROCTORED_EXAMS['ENABLE_PROCTORED_EXAMS'] = True + + with override_settings(FEATURES=FEATURES_WITH_PROCTORED_EXAMS): + provider_value = self.proctoring_provider.from_json(provider, validate_providers=False) + assert provider_value == provider + def test_from_json_adds_platform_default_for_missing_provider(self): """ Test that a value with no provider will inherit the default provider