Commit 8f4cf682 by Mike Dikan

Adding support for UTM Tracking to apply to free purhases

ECOM-6450

Updating commerce api endpoint to capture user UTM cookies and resend them in the server request to the ecommerce IDA.
Currently, the non-free purchases are tracked because the user makes the request directly on ecommerce and user cookie
contains UTM data, but for free purchases, the UTM cookie isn't sent in the background server request to the ecommerce
IDA.
parent a56b3049
......@@ -31,6 +31,11 @@ from student.tests.tests import EnrollmentEventTestMixin
from xmodule.modulestore.django import modulestore
from commerce.api.v0.views import SAILTHRU_CAMPAIGN_COOKIE
UTM_COOKIE_NAME = 'edx.test.utm'
UTM_COOKIE_CONTENTS = {
'utm_source': 'test-source'
}
@attr(shard=1)
@ddt.ddt
......@@ -39,7 +44,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
"""
Tests for the commerce orders view.
"""
def _post_to_view(self, course_id=None, marketing_email_opt_in=False):
def _post_to_view(self, course_id=None, marketing_email_opt_in=False, include_utm_cookie=False):
"""
POST to the view being tested.
......@@ -55,6 +60,8 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
payload["email_opt_in"] = True
self.client.cookies[SAILTHRU_CAMPAIGN_COOKIE] = 'sailthru id'
if include_utm_cookie:
self.client.cookies[UTM_COOKIE_NAME] = json.dumps(UTM_COOKIE_CONTENTS)
return self.client.post(self.url, payload)
def assertResponseMessage(self, response, expected_msg):
......@@ -160,12 +167,12 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
self.assertValidEcommerceInternalRequestErrorResponse(response)
self.assertUserNotEnrolled()
def _test_successful_ecommerce_api_call(self, is_completed=True):
def _test_successful_ecommerce_api_call(self, is_completed=True, utm_tracking_present=False):
"""
Verifies that the view contacts the E-Commerce API with the correct data and headers.
"""
with mock.patch('commerce.api.v0.views.audit_log') as mock_audit_log:
response = self._post_to_view()
response = self._post_to_view(include_utm_cookie=utm_tracking_present)
# Verify that an audit message was logged
self.assertTrue(mock_audit_log.called)
......@@ -177,9 +184,15 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
else:
self.assertResponsePaymentData(response)
# make sure ecommerce API call forwards Sailthru cookie
# Make sure ecommerce API call forwards Sailthru cookie
self.assertIn('{}=sailthru id'.format(SAILTHRU_CAMPAIGN_COOKIE), httpretty.last_request().headers['cookie'])
# Check that UTM tracking cookie is passed along in request to ecommerce for attribution
if utm_tracking_present:
cookie_string = '{cookie_name}={cookie_contents}'.format(
cookie_name=UTM_COOKIE_NAME, cookie_contents=json.dumps(UTM_COOKIE_CONTENTS))
self.assertIn(cookie_string, httpretty.last_request().headers['cookie'])
@ddt.data(True, False)
def test_course_with_honor_seat_sku(self, user_is_active):
"""
......@@ -193,7 +206,14 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
return_value = {'id': TEST_BASKET_ID, 'payment_data': None, 'order': {'number': TEST_ORDER_NUMBER}}
with mock_create_basket(response=return_value):
# Test that call without utm tracking works
self._test_successful_ecommerce_api_call()
with mock.patch('student.models.RegistrationCookieConfiguration.current') as config:
instance = config.return_value
instance.utm_cookie_name = UTM_COOKIE_NAME
# Test that call with cookie passes cookie along
self._test_successful_ecommerce_api_call(utm_tracking_present=True)
@ddt.data(True, False)
def test_course_with_paid_seat_sku(self, user_is_active):
......@@ -207,7 +227,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
return_value = {'id': TEST_BASKET_ID, 'payment_data': TEST_PAYMENT_DATA, 'order': None}
with mock_create_basket(response=return_value):
self._test_successful_ecommerce_api_call(False)
self._test_successful_ecommerce_api_call(is_completed=False)
def _test_course_without_sku(self, enrollment_mode=CourseMode.DEFAULT_MODE_SLUG):
"""
......@@ -334,7 +354,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
self.assertIsNotNone(get_enrollment(self.user.username, unicode(self.course.id)))
with mock_create_basket():
self._test_successful_ecommerce_api_call(False)
self._test_successful_ecommerce_api_call(is_completed=False)
@mock.patch('commerce.api.v0.views.update_email_opt_in')
@ddt.data(*itertools.product((False, True), (False, True), (False, True)))
......
......@@ -22,7 +22,7 @@ from openedx.core.djangoapps.commerce.utils import ecommerce_api_client
from openedx.core.djangoapps.user_api.preferences.api import update_email_opt_in
from openedx.core.lib.api.authentication import OAuth2AuthenticationAllowInactiveUser
from openedx.core.lib.log_utils import audit_log
from student.models import CourseEnrollment
from student.models import CourseEnrollment, RegistrationCookieConfiguration
from util.json_request import JsonResponse
......@@ -150,13 +150,11 @@ class BasketsView(APIView):
# Make the API call
try:
# Pass along Sailthru campaign id
campaign_cookie = request.COOKIES.get(SAILTHRU_CAMPAIGN_COOKIE)
if campaign_cookie:
cookie = {SAILTHRU_CAMPAIGN_COOKIE: campaign_cookie}
if api_session.cookies:
requests.utils.add_dict_to_cookiejar(api_session.cookies, cookie)
else:
api_session.cookies = requests.utils.cookiejar_from_dict(cookie)
self._add_request_cookie_to_api_session(api_session, request, SAILTHRU_CAMPAIGN_COOKIE)
# Pass along UTM tracking info
utm_cookie_name = RegistrationCookieConfiguration.current().utm_cookie_name
self._add_request_cookie_to_api_session(api_session, request, utm_cookie_name)
response_data = api.baskets.post({
'products': [{'sku': default_enrollment_mode.sku}],
......@@ -194,6 +192,18 @@ class BasketsView(APIView):
self._handle_marketing_opt_in(request, course_key, user)
return response
def _add_request_cookie_to_api_session(self, server_session, request, cookie_name):
""" Add cookie from user request into server session """
user_cookie = None
if cookie_name:
user_cookie = request.COOKIES.get(cookie_name)
if user_cookie:
server_cookie = {cookie_name: user_cookie}
if server_session.cookies:
requests.utils.add_dict_to_cookiejar(server_session.cookies, server_cookie)
else:
server_session.cookies = requests.utils.cookiejar_from_dict(server_cookie)
class BasketOrderView(APIView):
""" Retrieve the order associated with a basket. """
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment