From 4c1bd31d3919c32de2bd63a5d8e9e2ceab8d3258 Mon Sep 17 00:00:00 2001 From: Sameen Fatima <55431213+sameenfatima78@users.noreply.github.com> Date: Tue, 9 Apr 2024 18:27:41 +0500 Subject: [PATCH] feat: save cornerstone learner information (#2068) --- CHANGELOG.rst | 4 + enterprise/__init__.py | 2 +- enterprise/views.py | 17 +- .../api/v1/cornerstone/urls.py | 12 +- .../api/v1/cornerstone/views.py | 103 ++++++++++++ .../cornerstone/exporters/learner_data.py | 69 ++++---- integrated_channels/cornerstone/utils.py | 24 ++- .../test_api/test_cornerstone/test_views.py | 158 +++++++++++++++++- .../test_exporters/test_learner_data.py | 14 ++ .../test_cornerstone/test_utils.py | 74 +++++--- 10 files changed, 405 insertions(+), 72 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index edef324f71..2335dddf9d 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -15,6 +15,10 @@ Change Log Unreleased ---------- +[4.15.2] +-------- +* feat: save cornerstone learner's information received from frontend. + [4.15.1] -------- * feat: allowing for sorting and filtering of the enterprise group learner endpoints diff --git a/enterprise/__init__.py b/enterprise/__init__.py index bbd36b5529..b2368fa707 100644 --- a/enterprise/__init__.py +++ b/enterprise/__init__.py @@ -2,4 +2,4 @@ Your project description goes here. """ -__version__ = "4.15.1" +__version__ = "4.15.2" diff --git a/enterprise/views.py b/enterprise/views.py index 7c93bbb224..2516c7e2cf 100644 --- a/enterprise/views.py +++ b/enterprise/views.py @@ -2363,6 +2363,7 @@ def get(self, request, *args, **kwargs): - Look to see whether a request is eligible for direct audit enrollment, and if so, directly enroll the user. """ + user_id = request.user.id enterprise_customer_uuid, course_run_id, course_key, program_uuid = RouterView.get_path_variables(**kwargs) enterprise_customer = get_enterprise_customer_or_404(enterprise_customer_uuid) if course_key: @@ -2382,15 +2383,17 @@ def get(self, request, *args, **kwargs): 'CornerstoneEnterpriseCustomerConfiguration' ) with transaction.atomic(): - # The presense of a sessionToken and subdomain param indicates a Cornerstone redirect + # The presence of a sessionToken and subdomain param indicates a Cornerstone redirect # We need to store this sessionToken for api access + csod_user_guid = request.GET.get('userGuid') + csod_callback_url = request.GET.get('callbackUrl') csod_session_token = request.GET.get('sessionToken') csod_subdomain = request.GET.get("subdomain") if csod_session_token and csod_subdomain: LOGGER.info( f'integrated_channel=CSOD, ' f'integrated_channel_enterprise_customer_uuid={enterprise_customer.uuid}, ' - f'integrated_channel_lms_user={request.user.id}, ' + f'integrated_channel_lms_user={user_id}, ' f'integrated_channel_course_key={course_key}, ' 'enrollment redirect' ) @@ -2403,7 +2406,15 @@ def get(self, request, *args, **kwargs): cornerstone_customer_configuration.session_token = csod_session_token cornerstone_customer_configuration.session_token_modified = localized_utcnow() cornerstone_customer_configuration.save() - create_cornerstone_learner_data(request, cornerstone_customer_configuration, course_key) + create_cornerstone_learner_data( + user_id, + csod_user_guid, + csod_session_token, + csod_callback_url, + csod_subdomain, + cornerstone_customer_configuration, + course_key + ) else: LOGGER.error( f'integrated_channel=CSOD, ' diff --git a/integrated_channels/api/v1/cornerstone/urls.py b/integrated_channels/api/v1/cornerstone/urls.py index dbf68a8d86..951270ba1a 100644 --- a/integrated_channels/api/v1/cornerstone/urls.py +++ b/integrated_channels/api/v1/cornerstone/urls.py @@ -4,9 +4,17 @@ from rest_framework import routers -from .views import CornerstoneConfigurationViewSet +from django.urls import path + +from .views import CornerstoneConfigurationViewSet, CornerstoneLearnerInformationView app_name = 'cornerstone' router = routers.DefaultRouter() router.register(r'configuration', CornerstoneConfigurationViewSet, basename="configuration") -urlpatterns = router.urls +urlpatterns = [ + path('save-learner-information', CornerstoneLearnerInformationView.as_view(), + name='save-learner-information' + ), +] + +urlpatterns += router.urls diff --git a/integrated_channels/api/v1/cornerstone/views.py b/integrated_channels/api/v1/cornerstone/views.py index 821693a5f6..e54261d44f 100644 --- a/integrated_channels/api/v1/cornerstone/views.py +++ b/integrated_channels/api/v1/cornerstone/views.py @@ -1,17 +1,120 @@ """ Viewsets for integrated_channels/v1/cornerstone/ """ +from logging import getLogger + +from edx_rest_framework_extensions.auth.jwt.authentication import JwtAuthentication from rest_framework import permissions, viewsets +from rest_framework.authentication import SessionAuthentication +from rest_framework.response import Response +from rest_framework.status import HTTP_200_OK, HTTP_404_NOT_FOUND +from rest_framework.views import APIView + +from django.contrib import auth +from django.db import transaction +from enterprise.api.throttles import ServiceUserThrottle +from enterprise.utils import get_enterprise_customer_or_404, get_enterprise_customer_user, localized_utcnow from integrated_channels.api.v1.mixins import PermissionRequiredForIntegratedChannelMixin from integrated_channels.cornerstone.models import CornerstoneEnterpriseCustomerConfiguration +from integrated_channels.cornerstone.utils import create_cornerstone_learner_data from .serializers import CornerstoneConfigSerializer +LOGGER = getLogger(__name__) +User = auth.get_user_model() + class CornerstoneConfigurationViewSet(PermissionRequiredForIntegratedChannelMixin, viewsets.ModelViewSet): + """Viewset for CornerstoneEnterpriseCustomerConfiguration""" serializer_class = CornerstoneConfigSerializer permission_classes = (permissions.IsAuthenticated,) permission_required = 'enterprise.can_access_admin_dashboard' configuration_model = CornerstoneEnterpriseCustomerConfiguration + + +class CornerstoneLearnerInformationView(APIView): + """Viewset for saving information of a cornerstone learner""" + permission_classes = (permissions.IsAuthenticated,) + authentication_classes = (JwtAuthentication, SessionAuthentication,) + throttle_classes = (ServiceUserThrottle,) + + def post(self, request): + """ + An endpoint to save a cornerstone learner information received from frontend. + integrated_channels/api/v1/cornerstone/save-learner-information + Requires a JSON object in the following format: + { + "courseKey": "edX+DemoX", + "enterpriseUUID": "enterprise-uuid-goes-right-here", + "userGuid": "user-guid-from-csod", + "callbackUrl": "https://example.com/csod/callback/1", + "sessionToken": "123123123", + "subdomain": "edx.csod.com" + } + """ + user_id = request.user.id + enterprise_customer_uuid = request.data.get('enterpriseUUID') + enterprise_customer = get_enterprise_customer_or_404(enterprise_customer_uuid) + course_key = request.data.get('courseKey') + with transaction.atomic(): + csod_user_guid = request.data.get('userGuid') + csod_callback_url = request.data.get('callbackUrl') + csod_session_token = request.data.get('sessionToken') + csod_subdomain = request.data.get("subdomain") + + if csod_session_token and csod_subdomain: + LOGGER.info( + f'integrated_channel=CSOD, ' + f'integrated_channel_enterprise_customer_uuid={enterprise_customer_uuid}, ' + f'integrated_channel_lms_user={user_id}, ' + f'integrated_channel_course_key={course_key}, ' + 'saving CSOD learner information' + ) + cornerstone_customer_configuration = \ + CornerstoneEnterpriseCustomerConfiguration.get_by_customer_and_subdomain( + enterprise_customer=enterprise_customer, + customer_subdomain=csod_subdomain + ) + if cornerstone_customer_configuration: + # check if request user is linked as a learner with the given enterprise before savin anything + enterprise_customer_user = get_enterprise_customer_user(user_id, enterprise_customer_uuid) + if enterprise_customer_user: + # saving session token in enterprise config to access cornerstone apis + cornerstone_customer_configuration.session_token = csod_session_token + cornerstone_customer_configuration.session_token_modified = localized_utcnow() + cornerstone_customer_configuration.save() + # saving learner information received from cornerstone + create_cornerstone_learner_data( + user_id, + csod_user_guid, + csod_session_token, + csod_callback_url, + csod_subdomain, + cornerstone_customer_configuration, + course_key + ) + else: + LOGGER.error( + f'integrated_channel=CSOD, ' + f'integrated_channel_enterprise_customer_uuid={enterprise_customer_uuid}, ' + f'integrated_channel_lms_user={user_id}, ' + f'integrated_channel_course_key={course_key}, ' + f'user is not linked to the given enterprise' + ) + message = (f'Cornerstone information could not be saved for learner with user_id={user_id}' + f'because user is not linked to the given enterprise {enterprise_customer_uuid}') + return Response(data={'error': message}, status=HTTP_404_NOT_FOUND) + else: + LOGGER.error( + f'integrated_channel=CSOD, ' + f'integrated_channel_enterprise_customer_uuid={enterprise_customer_uuid}, ' + f'integrated_channel_lms_user={user_id}, ' + f'integrated_channel_course_key={course_key}, ' + f'unable to find cornerstone config matching subdomain {csod_subdomain}' + ) + message = (f'Cornerstone information could not be saved for learner with user_id={user_id}' + f'because no config exist with the subdomain {csod_subdomain}') + return Response(data={'error': message}, status=HTTP_404_NOT_FOUND) + return Response(status=HTTP_200_OK) diff --git a/integrated_channels/cornerstone/exporters/learner_data.py b/integrated_channels/cornerstone/exporters/learner_data.py index 9b5bcbf791..99ab1e7c2c 100644 --- a/integrated_channels/cornerstone/exporters/learner_data.py +++ b/integrated_channels/cornerstone/exporters/learner_data.py @@ -36,47 +36,44 @@ def get_learner_data_records( 'cornerstone', 'CornerstoneLearnerDataTransmissionAudit' ) - enterprise_customer_user = enterprise_enrollment.enterprise_customer_user - # get the proper internal representation of the course key - course_id = get_course_id_for_enrollment(enterprise_enrollment) - # because CornerstoneLearnerDataTransmissionAudit records are created with a click-through - # the internal edX course_id is always used on the CornerstoneLearnerDataTransmissionAudit records - # rather than the external_course_id mapped via CornerstoneCourseKey - transmission_exists = CornerstoneLearnerDataTransmissionAudit.objects.filter( - user_id=enterprise_enrollment.enterprise_customer_user.user.id, - course_id=course_id, - plugin_configuration_id=self.enterprise_configuration.id, - enterprise_customer_uuid=self.enterprise_configuration.enterprise_customer.uuid, - ).exists() - if transmission_exists or enterprise_customer_user.user_email is not None: - csod_transmission_record, __ = CornerstoneLearnerDataTransmissionAudit.objects.update_or_create( - user_id=enterprise_customer_user.user.id, + try: + # get the proper internal representation of the course key + course_id = get_course_id_for_enrollment(enterprise_enrollment) + # because CornerstoneLearnerDataTransmissionAudit records are created with a click-through + # the internal edX course_id is always used on the CornerstoneLearnerDataTransmissionAudit records + # rather than the external_course_id mapped via CornerstoneCourseKey + csod_learner_data_transmission = CornerstoneLearnerDataTransmissionAudit.objects.get( + user_id=enterprise_enrollment.enterprise_customer_user.user.id, course_id=course_id, plugin_configuration_id=self.enterprise_configuration.id, enterprise_customer_uuid=self.enterprise_configuration.enterprise_customer.uuid, - defaults={ - "enterprise_course_enrollment_id": enterprise_enrollment.id, - "grade": grade, - "course_completed": course_completed, - "completed_timestamp": completed_date, - "user_email": enterprise_customer_user.user_email, - }, ) - return [csod_transmission_record] - else: - LOGGER.info( - generate_formatted_log( - self.enterprise_configuration.channel_code(), - enterprise_customer_user.enterprise_customer.uuid, - enterprise_customer_user.user_id, - enterprise_enrollment.course_id, - ( - 'get_learner_data_records finished. No learner data was sent for this LMS User Id because ' - 'Cornerstone User ID not found for [{name}]'.format( - name=enterprise_customer_user.enterprise_customer.name - ) + csod_learner_data_transmission.enterprise_course_enrollment_id = enterprise_enrollment.id + csod_learner_data_transmission.grade = grade + csod_learner_data_transmission.course_completed = course_completed + csod_learner_data_transmission.completed_timestamp = completed_date + + # Used for api error reporting + csod_learner_data_transmission.user_email = enterprise_enrollment.enterprise_customer_user.user_email + + enterprise_customer = enterprise_enrollment.enterprise_customer_user.enterprise_customer + csod_learner_data_transmission.enterprise_customer_uuid = enterprise_customer.uuid + csod_learner_data_transmission.plugin_configuration_id = self.enterprise_configuration.id + return [ + csod_learner_data_transmission + ] + except CornerstoneLearnerDataTransmissionAudit.DoesNotExist: + LOGGER.info(generate_formatted_log( + self.enterprise_configuration.channel_code(), + enterprise_enrollment.enterprise_customer_user.enterprise_customer.uuid, + enterprise_enrollment.enterprise_customer_user.user_id, + enterprise_enrollment.course_id, + ( + 'get_learner_data_records finished. No learner data was sent for this LMS User Id {user_id} ' + 'because Cornerstone User ID not found'.format( + user_id=enterprise_enrollment.enterprise_customer_user.user_id ) ) - ) + )) return None diff --git a/integrated_channels/cornerstone/utils.py b/integrated_channels/cornerstone/utils.py index e6c1defbb9..7bae39d7cd 100644 --- a/integrated_channels/cornerstone/utils.py +++ b/integrated_channels/cornerstone/utils.py @@ -25,22 +25,30 @@ def cornerstone_course_key_model(): LOGGER = getLogger(__name__) -def create_cornerstone_learner_data(request, cornerstone_customer_configuration, course_id): +def create_cornerstone_learner_data( + user_id, + user_guid, + session_token, + callback_url, + subdomain, + cornerstone_customer_configuration, + course_id +): """ updates or creates CornerstoneLearnerDataTransmissionAudit """ enterprise_customer_uuid = cornerstone_customer_configuration.enterprise_customer.uuid try: defaults = { - 'user_guid': request.GET['userGuid'], - 'session_token': request.GET['sessionToken'], - 'callback_url': request.GET['callbackUrl'], - 'subdomain': request.GET['subdomain'], + 'user_guid': user_guid, + 'session_token': session_token, + 'callback_url': callback_url, + 'subdomain': subdomain, } cornerstone_learner_data_transmission_audit().objects.update_or_create( enterprise_customer_uuid=enterprise_customer_uuid, plugin_configuration_id=cornerstone_customer_configuration.id, - user_id=request.user.id, + user_id=user_id, course_id=course_id, defaults=defaults ) @@ -49,7 +57,7 @@ def create_cornerstone_learner_data(request, cornerstone_customer_configuration, LOGGER.exception( f'integrated_channel=CSOD, ' f'integrated_channel_enterprise_customer_uuid={enterprise_customer_uuid}, ' - f'integrated_channel_lms_user={request.user.id}, ' + f'integrated_channel_lms_user={user_id}, ' f'integrated_channel_course_key={course_id}, ' 'malformed cornerstone request missing a param' ) @@ -57,7 +65,7 @@ def create_cornerstone_learner_data(request, cornerstone_customer_configuration, LOGGER.exception( f'integrated_channel=CSOD, ' f'integrated_channel_enterprise_customer_uuid={enterprise_customer_uuid}, ' - f'integrated_channel_lms_user={request.user.id}, ' + f'integrated_channel_lms_user={user_id}, ' f'integrated_channel_course_key={course_id}, ' f'Unable to Create/Update CornerstoneLearnerDataTransmissionAudit.' ) diff --git a/tests/test_integrated_channels/test_api/test_cornerstone/test_views.py b/tests/test_integrated_channels/test_api/test_cornerstone/test_views.py index 00891152c6..037aa56f7d 100644 --- a/tests/test_integrated_channels/test_api/test_cornerstone/test_views.py +++ b/tests/test_integrated_channels/test_api/test_cornerstone/test_views.py @@ -5,11 +5,15 @@ from unittest import mock from uuid import uuid4 +from django.conf import settings from django.urls import reverse from enterprise.constants import ENTERPRISE_ADMIN_ROLE from enterprise.utils import localized_utcnow -from integrated_channels.cornerstone.models import CornerstoneEnterpriseCustomerConfiguration +from integrated_channels.cornerstone.models import ( + CornerstoneEnterpriseCustomerConfiguration, + CornerstoneLearnerDataTransmissionAudit, +) from test_utils import APITest, factories ENTERPRISE_ID = str(uuid4()) @@ -151,3 +155,155 @@ def test_is_valid_field(self, mock_current_request): data = json.loads(response.content.decode('utf-8')).get('results') missing, incorrect = data[0].get('is_valid') assert not missing.get('missing') and not incorrect.get('incorrect') + + +class CornerstoneLearnerInformationViewTests(APITest): + """ + Tests for CornerstoneLearnerInformationView API endpoints + """ + def setUp(self): + super().setUp() + self.enterprise_customer = factories.EnterpriseCustomerFactory(uuid=ENTERPRISE_ID) + self.enterprise_customer_user = factories.EnterpriseCustomerUserFactory( + enterprise_customer=self.enterprise_customer, + user_id=self.user.id, + ) + self.csod_subdomain = 'dummy_subdomain' + self.cornerstone_config = CornerstoneEnterpriseCustomerConfiguration( + enterprise_customer=self.enterprise_customer, + active=True, + cornerstone_base_url=f'https://{self.csod_subdomain}.com', + ) + self.cornerstone_config.save() + self.course_key = 'edX+DemoX' + self.path = settings.TEST_SERVER + '/integrated_channels/api/v1/cornerstone/save-learner-information' + + def test_save_learner_endpoint_happy_path(self): + """ + Test the happy path where csod information for a learner gets saved successfully. + """ + dummy_token = "123123123" + post_data = { + "courseKey": self.course_key, + "enterpriseUUID": self.enterprise_customer.uuid, + "userGuid": "24142313", + "callbackUrl": "https://example.com/csod/callback/1", + "sessionToken": dummy_token, + "subdomain": self.csod_subdomain + } + response = self.client.post(self.path, post_data) + assert response.status_code == 200 + self.cornerstone_config.refresh_from_db() + assert self.cornerstone_config.session_token == dummy_token + assert CornerstoneLearnerDataTransmissionAudit.objects.filter( + enterprise_customer_uuid=self.enterprise_customer.uuid, + plugin_configuration_id=self.cornerstone_config.id, + course_id=self.course_key, + user_id=self.user.id + ).exists() + + def test_save_learner_endpoint_enterprise_customer_does_not_exist(self): + """ + Test when enterprise customer does not exist. + """ + dummy_token = "123123123" + post_data = { + "courseKey": self.course_key, + "enterpriseUUID": 'invalid-uuid', + "userGuid": "24142313", + "callbackUrl": "https://example.com/csod/callback/1", + "sessionToken": dummy_token, + "subdomain": self.csod_subdomain + } + response = self.client.post(self.path, post_data) + self.cornerstone_config.refresh_from_db() + assert self.cornerstone_config.session_token != dummy_token + assert response.status_code == 404 + assert CornerstoneLearnerDataTransmissionAudit.objects.filter( + enterprise_customer_uuid=self.enterprise_customer.uuid, + plugin_configuration_id=self.cornerstone_config.id, + course_id=self.course_key, + user_id=self.user.id + ).count() == 0 + + def test_save_learner_endpoint_cornerstone_config_does_not_exist(self): + """ + Test when cornerstone config is not found. + """ + dummy_token = "123123123" + post_data = { + "courseKey": self.course_key, + "enterpriseUUID": self.enterprise_customer.uuid, + "userGuid": "24142313", + "callbackUrl": "https://example.com/csod/callback/1", + "sessionToken": dummy_token, + "subdomain": 'invalid-subdomain' + } + response = self.client.post(self.path, post_data) + self.cornerstone_config.refresh_from_db() + assert self.cornerstone_config.session_token != dummy_token + assert response.status_code == 404 + assert CornerstoneLearnerDataTransmissionAudit.objects.filter( + enterprise_customer_uuid=self.enterprise_customer.uuid, + plugin_configuration_id=self.cornerstone_config.id, + course_id=self.course_key, + user_id=self.user.id + ).count() == 0 + + def test_save_learner_endpoint_learner_not_linked(self): + """ + Test when learner is not linked to the given enterprise. We should not be saving anything in that case. + """ + # Delete EnterpriseCustomerUser record. + self.enterprise_customer_user.delete() + dummy_token = "123123123" + post_data = { + "courseKey": self.course_key, + "enterpriseUUID": self.enterprise_customer.uuid, + "userGuid": "24142313", + "callbackUrl": "https://example.com/csod/callback/1", + "sessionToken": dummy_token, + "subdomain": self.csod_subdomain + } + response = self.client.post(self.path, post_data) + self.cornerstone_config.refresh_from_db() + assert self.cornerstone_config.session_token != dummy_token + assert response.status_code == 404 + assert CornerstoneLearnerDataTransmissionAudit.objects.filter( + enterprise_customer_uuid=self.enterprise_customer.uuid, + plugin_configuration_id=self.cornerstone_config.id, + course_id=self.course_key, + user_id=self.user.id + ).count() == 0 + + def test_save_learner_endpoint_update_existing_record(self): + """ + When an existing transmisison record is found, we should update that one instead of creating a duplicate. + """ + # Create transmission record + CornerstoneLearnerDataTransmissionAudit.objects.create( + enterprise_customer_uuid=self.enterprise_customer.uuid, + plugin_configuration_id=self.cornerstone_config.id, + user_id=self.user.id, + course_id=self.course_key, + session_token='123456' + ) + dummy_token = "123123123" + post_data = { + "courseKey": self.course_key, + "enterpriseUUID": self.enterprise_customer.uuid, + "userGuid": "24142313", + "callbackUrl": "https://example.com/csod/callback/1", + "sessionToken": dummy_token, + "subdomain": self.csod_subdomain + } + response = self.client.post(self.path, post_data) + assert response.status_code == 200 + self.cornerstone_config.refresh_from_db() + assert self.cornerstone_config.session_token == dummy_token + assert CornerstoneLearnerDataTransmissionAudit.objects.filter( + enterprise_customer_uuid=self.enterprise_customer.uuid, + plugin_configuration_id=self.cornerstone_config.id, + course_id=self.course_key, + user_id=self.user.id + ).count() == 1 diff --git a/tests/test_integrated_channels/test_cornerstone/test_exporters/test_learner_data.py b/tests/test_integrated_channels/test_cornerstone/test_exporters/test_learner_data.py index 39e10b469a..6f8ea97ea6 100644 --- a/tests/test_integrated_channels/test_cornerstone/test_exporters/test_learner_data.py +++ b/tests/test_integrated_channels/test_cornerstone/test_exporters/test_learner_data.py @@ -131,6 +131,20 @@ def test_retrieve_same_learner_data_record(self, mock_course_catalog_api): assert learner_data_records_1.id == learner_data_records_2.id + def test_get_learner_data_record_not_exist(self): + """ + If learner data does not already exist, nothing is returned. + """ + exporter = CornerstoneLearnerExporter('fake-user', self.config) + enterprise_course_enrollment = factories.EnterpriseCourseEnrollmentFactory( + enterprise_customer_user=factories.EnterpriseCustomerUserFactory( + user_id=self.other_user.id, + enterprise_customer=self.enterprise_customer, + ), + course_id=self.course_id, + ) + assert exporter.get_learner_data_records(enterprise_course_enrollment) is None + @responses.activate @mock.patch('integrated_channels.cornerstone.client.requests.post') @mock.patch('integrated_channels.integrated_channel.exporters.learner_data.get_course_certificate') diff --git a/tests/test_integrated_channels/test_cornerstone/test_utils.py b/tests/test_integrated_channels/test_cornerstone/test_utils.py index 38c572ae6a..a161260843 100644 --- a/tests/test_integrated_channels/test_cornerstone/test_utils.py +++ b/tests/test_integrated_channels/test_cornerstone/test_utils.py @@ -45,19 +45,19 @@ def setUp(self): super().setUp() @staticmethod - def _assert_learner_data_transmission_audit(transmission_audit, user, course_id, querystring): + def _assert_learner_data_transmission_audit(transmission_audit, user, course_id, csod_params): """ Asserts CornerstoneLearnerDataTransmissionAudit values""" assert transmission_audit.user == user assert transmission_audit.course_id == course_id - assert transmission_audit.user_guid == querystring['userGuid'] - assert transmission_audit.session_token == querystring['sessionToken'] - assert transmission_audit.callback_url == querystring['callbackUrl'] - assert transmission_audit.subdomain == querystring['subdomain'] + assert transmission_audit.user_guid == csod_params['userGuid'] + assert transmission_audit.session_token == csod_params['sessionToken'] + assert transmission_audit.callback_url == csod_params['callbackUrl'] + assert transmission_audit.subdomain == csod_params['subdomain'] @staticmethod - def _get_request(querystring, user=None): + def _get_request(csod_params, user=None): """ returns mocked request """ - request = RequestFactory().get(path='/', data=querystring) + request = RequestFactory().get(path='/', data=csod_params) request.user = user if user else UserFactory() return request @@ -88,10 +88,18 @@ def _get_request(querystring, user=None): ) @ddt.unpack @mark.django_db - def test_update_cornerstone_learner_data_transmission_audit(self, querystring, course_id, expected_result): + def test_update_cornerstone_learner_data_transmission_audit(self, csod_params, course_id, expected_result): """ test creating records """ - request = self._get_request(querystring) - create_cornerstone_learner_data(request, self.config, course_id) + request = self._get_request(csod_params) + create_cornerstone_learner_data( + request.user.id, + csod_params.get('userGuid'), + csod_params.get('sessionToken'), + csod_params.get('callbackUrl'), + csod_params.get('subdomain'), + self.config, + course_id + ) actual_result = request.user.cornerstone_transmission_audit.filter(course_id=course_id).exists() assert actual_result == expected_result if expected_result: @@ -104,7 +112,7 @@ def test_update_cornerstone_learner_data_transmission_audit_with_existing_data(s """ test updating audit records """ user = UserFactory() course_id = 'dummy_courseId' - querystring = { + csod_params = { 'userGuid': 'dummy_id', 'sessionToken': 'dummy_session_token', 'callbackUrl': 'dummy_callbackUrl', @@ -112,24 +120,48 @@ def test_update_cornerstone_learner_data_transmission_audit_with_existing_data(s } # creating data for first time - request = self._get_request(querystring, user) - create_cornerstone_learner_data(request, self.config, course_id) + request = self._get_request(csod_params, user) + create_cornerstone_learner_data( + request.user.id, + csod_params.get('userGuid'), + csod_params.get('sessionToken'), + csod_params.get('callbackUrl'), + csod_params.get('subdomain'), + self.config, + course_id + ) records = CornerstoneLearnerDataTransmissionAudit.objects.all() assert records.count() == 1 - self._assert_learner_data_transmission_audit(records.first(), user, course_id, querystring) + self._assert_learner_data_transmission_audit(records.first(), user, course_id, csod_params) # Updating just sessionToken Should NOT create new records, instead update old one. - querystring['sessionToken'] = 'updated_dummy_session_token' - request = self._get_request(querystring, user) - create_cornerstone_learner_data(request, self.config, course_id) + csod_params['sessionToken'] = 'updated_dummy_session_token' + request = self._get_request(csod_params, user) + create_cornerstone_learner_data( + request.user.id, + csod_params.get('userGuid'), + csod_params.get('sessionToken'), + csod_params.get('callbackUrl'), + csod_params.get('subdomain'), + self.config, + course_id + ) records = CornerstoneLearnerDataTransmissionAudit.objects.all() assert records.count() == 1 - self._assert_learner_data_transmission_audit(records.first(), user, course_id, querystring) + self._assert_learner_data_transmission_audit(records.first(), user, course_id, csod_params) # But updating courseId Should create fresh record. course_id = 'updated_dummy_courseId' - request = self._get_request(querystring, user) - create_cornerstone_learner_data(request, self.config, course_id) + request = self._get_request(csod_params, user) + create_cornerstone_learner_data( + request.user.id, + csod_params.get('userGuid'), + csod_params.get('sessionToken'), + csod_params.get('callbackUrl'), + csod_params.get('subdomain'), + self.config, + course_id + ) records = CornerstoneLearnerDataTransmissionAudit.objects.all() assert records.count() == 2 - self._assert_learner_data_transmission_audit(records[1], user, course_id, querystring) + self._assert_learner_data_transmission_audit(records[1], user, course_id, csod_params)