From 96ace718f43942e8f76daa83c0377681730cfad6 Mon Sep 17 00:00:00 2001 From: "kshitij.sobti" Date: Tue, 20 May 2025 09:58:45 +0530 Subject: [PATCH] feat: Send an LTI launch event for LTI Launches --- .../lti_provider/tests/test_views.py | 24 ++++++++++++++- lms/djangoapps/lti_provider/views.py | 30 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) diff --git a/lms/djangoapps/lti_provider/tests/test_views.py b/lms/djangoapps/lti_provider/tests/test_views.py index 8328c669cf..0e80bc11b8 100644 --- a/lms/djangoapps/lti_provider/tests/test_views.py +++ b/lms/djangoapps/lti_provider/tests/test_views.py @@ -10,6 +10,7 @@ from django.test import TestCase from django.test.client import RequestFactory from django.urls import reverse from opaque_keys.edx.locator import BlockUsageLocator, CourseLocator +from openedx_events.learning.data import UserData, UserPersonalData, LtiProviderLaunchData, LtiProviderLaunchParamsData from common.djangoapps.student.tests.factories import UserFactory from lms.djangoapps.courseware.testutils import RenderXBlockTestMixin @@ -96,15 +97,36 @@ class LtiLaunchTest(LtiTestMixin, TestCase): """ Tests for the lti_launch view """ + @patch('lms.djangoapps.lti_provider.views.LTI_PROVIDER_LAUNCH_SUCCESS.send_event') @patch('lms.djangoapps.lti_provider.views.render_courseware') @patch('lms.djangoapps.lti_provider.views.authenticate_lti_user') - def test_valid_launch(self, _authenticate, render): + def test_valid_launch(self, _authenticate, render, lti_launch_success_send_event): """ Verifies that the LTI launch succeeds when passed a valid request. """ request = build_launch_request() views.lti_launch(request, str(COURSE_KEY), str(USAGE_KEY)) render.assert_called_with(request, USAGE_KEY) + lti_launch_success_send_event.assert_called_with( + launch_data=LtiProviderLaunchData( + user=UserData( + id=request.user.id, + is_active=request.user.is_active, + pii=UserPersonalData( + username=request.user.username, + email=request.user.email, + name=request.user.profile.name, + ) + ), + course_key=COURSE_KEY, + usage_key=USAGE_KEY, + launch_params=LtiProviderLaunchParamsData( + roles='Instructor,urn:lti:instrole:ims/lis/Administrator', + context_id='lti_launch_context_id', + user_id='LTI_User', extra_params={} + ) + ) + ) @patch('lms.djangoapps.lti_provider.views.render_courseware') @patch('lms.djangoapps.lti_provider.views.store_outcome_parameters') diff --git a/lms/djangoapps/lti_provider/views.py b/lms/djangoapps/lti_provider/views.py index 1106908112..9fe15badc2 100644 --- a/lms/djangoapps/lti_provider/views.py +++ b/lms/djangoapps/lti_provider/views.py @@ -12,6 +12,8 @@ from django.views.decorators.csrf import csrf_exempt from common.djangoapps.edxmako.shortcuts import render_to_response from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey, UsageKey +from openedx_events.learning.data import LtiProviderLaunchData, LtiProviderLaunchParamsData, UserData, UserPersonalData +from openedx_events.learning.signals import LTI_PROVIDER_LAUNCH_SUCCESS from common.djangoapps.util.views import add_p3p_header from lms.djangoapps.lti_provider.models import LtiConsumer @@ -107,6 +109,34 @@ def lti_launch(request, course_id, usage_id): # used earlier to verify the oauth signature. store_outcome_parameters(params, request.user, lti_consumer) + # Make a copy of params for the event signal, and remove sensitive oauth parameters. + launch_params = params.copy() + for key in list(launch_params.keys()): + if key.startswith('oauth_'): + launch_params.pop(key) + + LTI_PROVIDER_LAUNCH_SUCCESS.send_event( + launch_data=LtiProviderLaunchData( + user=UserData( + pii=UserPersonalData( + username=request.user.username, + email=request.user.email, + name=request.user.profile.name, + ), + id=request.user.id, + is_active=request.user.is_active, + ), + course_key=launch_params.pop("course_key"), + usage_key=launch_params.pop("usage_key"), + launch_params=LtiProviderLaunchParamsData( + roles=launch_params.pop("roles"), + context_id=launch_params.pop("context_id"), + user_id=launch_params.pop("user_id"), + extra_params={key: str(val) for key, val in launch_params.items()}, + ), + ) + ) + return render_courseware(request, params['usage_key'])