fix: handle exception getting cert statuses (#31490)

If a user has a certificate in a deleted course, an issue with how the
course is loaded from the cache can cause an exception that breaks our
code. This adds a wrapper to fail gracefully and log the exception for
future tracking / investigation.
This commit is contained in:
Nathan Sprenkle
2023-01-03 14:57:20 -05:00
committed by GitHub
parent 37b3607f50
commit c8af55db6f
2 changed files with 54 additions and 4 deletions

View File

@@ -649,6 +649,42 @@ class TestDashboardView(BaseTestDashboardView):
},
)
@patch.dict(settings.FEATURES, ENTERPRISE_ENABLED=False)
@patch("lms.djangoapps.learner_home.views.cert_info")
def test_get_cert_statuses_exception(self, mock_get_cert_info):
"""Test that cert information gets loaded correctly"""
# Given I am logged in
self.log_in()
# (and we have tons of mocks to avoid integration tests)
mock_enrollment = create_test_enrollment(
self.user, course_mode=CourseMode.VERIFIED
)
# but have an issue with a particular certificate
mock_get_cert_info.side_effect = Exception("test exception")
# When I request the dashboard
response = self.client.get(self.view_url)
# Then I get the expected success response
assert response.status_code == 200
response_data = json.loads(response.content)
empty_cert_data = {
"availableDate": None,
"isRestricted": False,
"isEarned": False,
"isDownloadable": False,
"certPreviewUrl": None,
}
# with empty cert data instead of a break
self.assertDictEqual(
response_data["courses"][0]["certificate"], empty_cert_data
)
@patch.dict(settings.FEATURES, ENTERPRISE_ENABLED=False)
@patch("openedx.core.djangoapps.programs.utils.get_programs")
def test_get_for_one_of_course_programs(self, mock_get_programs):

View File

@@ -238,10 +238,24 @@ def get_ecommerce_payment_page(user):
@function_trace("get_cert_statuses")
def get_cert_statuses(user, course_enrollments):
"""Get cert status by course for user enrollments"""
return {
enrollment.course_id: cert_info(user, enrollment)
for enrollment in course_enrollments
}
cert_statuses = {}
for enrollment in course_enrollments:
# APER-2171 - trying to get a cert for a deleted course can throw an exception
# Wrap in exception handling to avoid this issue.
try:
certificate_for_course = cert_info(user, enrollment)
if certificate_for_course:
cert_statuses[enrollment.course_id] = certificate_for_course
except Exception as ex: # pylint: disable=broad-except
logger.exception(
f"Error getting certificate status for (user, course) ({user}, {enrollment.course_id}): {ex}"
)
return cert_statuses
@function_trace("get_org_block_and_allow_lists")