Merge pull request #18697 from edx/bbaker/LEARNER-6004
Properly handle errors in award program certificates
This commit is contained in:
@@ -174,11 +174,32 @@ def award_program_certificates(self, username):
|
||||
try:
|
||||
award_program_certificate(credentials_client, username, program_uuid)
|
||||
LOGGER.info('Awarded certificate for program %s to user %s', program_uuid, username)
|
||||
except exceptions.HttpClientError:
|
||||
except exceptions.HttpNotFoundError:
|
||||
LOGGER.exception(
|
||||
'Certificate for program %s not configured, unable to award certificate to %s',
|
||||
program_uuid, username
|
||||
"""Certificate for program {uuid} could not be found. Unable to award certificate to user
|
||||
{username}. The program might not be configured.""".format(uuid=program_uuid, username=username)
|
||||
)
|
||||
except exceptions.HttpClientError as exc:
|
||||
# Grab the status code from the client error, because our API
|
||||
# client handles all 4XX errors the same way. In the future,
|
||||
# we may want to fork slumber, add 429 handling, and use that
|
||||
# in edx_rest_api_client.
|
||||
# A status code looks like:
|
||||
# "Client Error 429: http://example-endpoint/"
|
||||
status_code = int(str(exc).split(':')[0][-3:])
|
||||
if status_code == 429:
|
||||
rate_limit_countdown = 60
|
||||
LOGGER.info(
|
||||
"""Rate limited. Retrying task to award certificates to user {username} in {countdown}
|
||||
seconds""".format(username=username, countdown=rate_limit_countdown)
|
||||
)
|
||||
# Retry after 60 seconds, when we should be in a new throttling window
|
||||
raise self.retry(exc=exc, countdown=rate_limit_countdown, max_retries=MAX_RETRIES)
|
||||
else:
|
||||
LOGGER.exception(
|
||||
"""Unable to award certificate to user {username} for program {uuid}. The program might not be
|
||||
configured.""".format(username=username, uuid=program_uuid)
|
||||
)
|
||||
except Exception: # pylint: disable=broad-except
|
||||
# keep trying to award other certs, but retry the whole task to fix any missing entries
|
||||
LOGGER.warning('Failed to award certificate for program {uuid} to user {username}.'.format(
|
||||
|
||||
@@ -292,6 +292,42 @@ class AwardProgramCertificatesTestCase(CatalogIntegrationMixin, CredentialsApiCo
|
||||
self.assertEqual(mock_get_certified_programs.call_count, 2)
|
||||
self.assertEqual(mock_award_program_certificate.call_count, 1)
|
||||
|
||||
def test_retry_on_credentials_api_429_error(
|
||||
self,
|
||||
mock_get_completed_programs,
|
||||
mock_get_certified_programs, # pylint: disable=unused-argument
|
||||
mock_award_program_certificate,
|
||||
):
|
||||
"""
|
||||
Verify that a 429 error causes the task to fail and then retry.
|
||||
"""
|
||||
mock_get_completed_programs.return_value = [1, 2]
|
||||
mock_award_program_certificate.side_effect = self._make_side_effect(
|
||||
[exceptions.HttpClientError('Client Error 429: http://example-endpoint/'), None]
|
||||
)
|
||||
|
||||
tasks.award_program_certificates.delay(self.student.username).get()
|
||||
|
||||
self.assertEqual(mock_award_program_certificate.call_count, 3)
|
||||
|
||||
def test_no_retry_oncredentials_api_404_error(
|
||||
self,
|
||||
mock_get_completed_programs,
|
||||
mock_get_certified_programs, # pylint: disable=unused-argument
|
||||
mock_award_program_certificate,
|
||||
):
|
||||
"""
|
||||
Verify that a 404 error causes the task to fail but there is no retry.
|
||||
"""
|
||||
mock_get_completed_programs.return_value = [1, 2]
|
||||
mock_award_program_certificate.side_effect = self._make_side_effect(
|
||||
[exceptions.HttpNotFoundError(), None]
|
||||
)
|
||||
|
||||
tasks.award_program_certificates.delay(self.student.username).get()
|
||||
|
||||
self.assertEqual(mock_award_program_certificate.call_count, 2)
|
||||
|
||||
def test_no_retry_on_credentials_api_not_found_errors(
|
||||
self,
|
||||
mock_get_completed_programs,
|
||||
@@ -301,7 +337,7 @@ class AwardProgramCertificatesTestCase(CatalogIntegrationMixin, CredentialsApiCo
|
||||
mock_get_completed_programs.return_value = [1, 2]
|
||||
mock_get_certified_programs.side_effect = [[], [2]]
|
||||
mock_award_program_certificate.side_effect = self._make_side_effect(
|
||||
[exceptions.HttpClientError(), None]
|
||||
[exceptions.HttpClientError('Client Error 418: http://example-endpoint/'), None]
|
||||
)
|
||||
|
||||
tasks.award_program_certificates.delay(self.student.username).get()
|
||||
|
||||
Reference in New Issue
Block a user