feat: add signal emitters for IDV (#28511)

MST-805. A signal should be emitted upon an IDV attempt being submitted or reviewed for consumption by other applications.
This commit is contained in:
alangsto
2021-08-26 15:29:48 -04:00
committed by GitHub
parent 0b4aaa90c7
commit b6cb629849
2 changed files with 68 additions and 0 deletions

View File

@@ -3,7 +3,9 @@ Signal handler for setting default course verification dates
""" """
from django.db.models.signals import post_save
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.dispatch import Signal
from django.dispatch.dispatcher import receiver from django.dispatch.dispatcher import receiver
from openedx.core.djangoapps.user_api.accounts.signals import USER_RETIRE_LMS_CRITICAL from openedx.core.djangoapps.user_api.accounts.signals import USER_RETIRE_LMS_CRITICAL
@@ -12,6 +14,10 @@ from xmodule.modulestore.django import SignalHandler, modulestore
from .models import SoftwareSecurePhotoVerification, VerificationDeadline from .models import SoftwareSecurePhotoVerification, VerificationDeadline
# Signal for emitting IDV submission and review updates
idv_update_signal = Signal(providing_args=["attempt_id", "user_id", "status", "full_name", "profile_name"])
@receiver(SignalHandler.course_published) @receiver(SignalHandler.course_published)
def _listen_for_course_publish(sender, course_key, **kwargs): # pylint: disable=unused-argument def _listen_for_course_publish(sender, course_key, **kwargs): # pylint: disable=unused-argument
""" """
@@ -32,3 +38,21 @@ def _listen_for_course_publish(sender, course_key, **kwargs): # pylint: disable
def _listen_for_lms_retire(sender, **kwargs): # pylint: disable=unused-argument def _listen_for_lms_retire(sender, **kwargs): # pylint: disable=unused-argument
user = kwargs.get('user') user = kwargs.get('user')
SoftwareSecurePhotoVerification.retire_user(user.id) SoftwareSecurePhotoVerification.retire_user(user.id)
@receiver(post_save, sender=SoftwareSecurePhotoVerification)
def send_idv_update(sender, instance, **kwargs): # pylint: disable=unused-argument
"""
Catches the post save signal from the SoftwareSecurePhotoVerification model, and emits
another signal with limited information from the model. We are choosing to re-emit a signal
as opposed to relying only on the post_save signal to avoid the chance that other apps
import the SoftwareSecurePhotoVerification model.
"""
idv_update_signal.send(
sender='idv_update',
attempt_id=instance.id,
user_id=instance.user.id,
status=instance.status,
full_name=instance.name,
profile_name=instance.user.profile.name
)

View File

@@ -6,6 +6,7 @@ Unit tests for the VerificationDeadline signals
from datetime import timedelta from datetime import timedelta
from django.utils.timezone import now from django.utils.timezone import now
from unittest.mock import patch
from common.djangoapps.student.tests.factories import UserFactory from common.djangoapps.student.tests.factories import UserFactory
from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification, VerificationDeadline from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification, VerificationDeadline
@@ -102,3 +103,46 @@ class RetirementSignalTest(ModuleStoreTestCase):
# All values for this user should now be empty string # All values for this user should now be empty string
for field in ('name', 'face_image_url', 'photo_id_image_url', 'photo_id_key'): for field in ('name', 'face_image_url', 'photo_id_image_url', 'photo_id_key'):
assert '' == getattr(ver_obj, field) assert '' == getattr(ver_obj, field)
class PostSavePhotoVerificationTest(ModuleStoreTestCase):
"""
Tests for the post_save signal on the SoftwareSecurePhotoVerification model.
This receiver should emit another signal that contains limited data about
the verification attempt that was updated.
"""
@patch('lms.djangoapps.verify_student.signals.idv_update_signal.send')
def test_post_save_signal(self, mock_signal):
user = UserFactory.create()
# create new softwaresecureverification
attempt = SoftwareSecurePhotoVerification.objects.create(
user=user,
name='Bob Doe',
face_image_url='https://test.face',
photo_id_image_url='https://test.photo',
photo_id_key='test+key'
)
self.assertTrue(mock_signal.called)
mock_signal.assert_called_with(
sender='idv_update',
attempt_id=attempt.id,
user_id=attempt.user.id,
status=attempt.status,
full_name=attempt.name,
profile_name=attempt.user.profile.name
)
mock_signal.reset_mock()
attempt.mark_ready()
self.assertTrue(mock_signal.called)
mock_signal.assert_called_with(
sender='idv_update',
attempt_id=attempt.id,
user_id=attempt.user.id,
status=attempt.status,
full_name=attempt.name,
profile_name=attempt.user.profile.name
)