Commit 7449f685 by jsa

Support setting email opt-in in calls to the Otto shim

XCOM-499
parent d4a5ad48
""" Commerce API v0 view tests. """ """ Commerce API v0 view tests. """
import json import json
import itertools
from uuid import uuid4 from uuid import uuid4
from nose.plugins.attrib import attr
import ddt import ddt
from django.conf import settings from django.conf import settings
...@@ -9,6 +9,7 @@ from django.core.urlresolvers import reverse ...@@ -9,6 +9,7 @@ from django.core.urlresolvers import reverse
from django.test import TestCase from django.test import TestCase
from django.test.utils import override_settings from django.test.utils import override_settings
import mock import mock
from nose.plugins.attrib import attr
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory from xmodule.modulestore.tests.factories import CourseFactory
...@@ -33,7 +34,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase) ...@@ -33,7 +34,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
""" """
Tests for the commerce orders view. Tests for the commerce orders view.
""" """
def _post_to_view(self, course_id=None): def _post_to_view(self, course_id=None, marketing_email_opt_in=False):
""" """
POST to the view being tested. POST to the view being tested.
...@@ -42,8 +43,12 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase) ...@@ -42,8 +43,12 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
:return: Response :return: Response
""" """
course_id = unicode(course_id or self.course.id) payload = {
return self.client.post(self.url, {'course_id': course_id}) "course_id": unicode(course_id or self.course.id)
}
if marketing_email_opt_in:
payload["email_opt_in"] = True
return self.client.post(self.url, payload)
def assertResponseMessage(self, response, expected_msg): def assertResponseMessage(self, response, expected_msg):
""" Asserts the detail field in the response's JSON body equals the expected message. """ """ Asserts the detail field in the response's JSON body equals the expected message. """
...@@ -297,6 +302,28 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase) ...@@ -297,6 +302,28 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
with mock_create_basket(): with mock_create_basket():
self._test_successful_ecommerce_api_call(False) self._test_successful_ecommerce_api_call(False)
@mock.patch('commerce.api.v0.views.update_email_opt_in')
@ddt.data(*itertools.product((False, True), (False, True), (False, True)))
@ddt.unpack
def test_marketing_email_opt_in(self, is_opt_in, has_sku, is_exception, mock_update):
"""
Ensures the email opt-in flag is handled, if present, and that problems handling the
flag don't cause the rest of the enrollment transaction to fail.
"""
if not has_sku:
for course_mode in CourseMode.objects.filter(course_id=self.course.id):
course_mode.sku = None
course_mode.save()
if is_exception:
mock_update.side_effect = Exception("boink")
return_value = {'id': TEST_BASKET_ID, 'payment_data': None, 'order': {'number': TEST_ORDER_NUMBER}}
with mock_create_basket(response=return_value, expect_called=has_sku):
response = self._post_to_view(marketing_email_opt_in=is_opt_in)
self.assertEqual(mock_update.called, is_opt_in)
self.assertEqual(response.status_code, 200)
@attr('shard_1') @attr('shard_1')
@override_settings(ECOMMERCE_API_URL=TEST_API_URL, ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY) @override_settings(ECOMMERCE_API_URL=TEST_API_URL, ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY)
......
...@@ -19,6 +19,7 @@ from courseware import courses ...@@ -19,6 +19,7 @@ from courseware import courses
from embargo import api as embargo_api from embargo import api as embargo_api
from enrollment.api import add_enrollment from enrollment.api import add_enrollment
from enrollment.views import EnrollmentCrossDomainSessionAuth from enrollment.views import EnrollmentCrossDomainSessionAuth
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.api.authentication import OAuth2AuthenticationAllowInactiveUser
from student.models import CourseEnrollment from student.models import CourseEnrollment
from util.json_request import JsonResponse from util.json_request import JsonResponse
...@@ -62,6 +63,22 @@ class BasketsView(APIView): ...@@ -62,6 +63,22 @@ class BasketsView(APIView):
""" Enroll the user in the course. """ """ Enroll the user in the course. """
add_enrollment(user.username, unicode(course_key)) add_enrollment(user.username, unicode(course_key))
def _handle_marketing_opt_in(self, request, course_key, user):
"""
Handle the marketing email opt-in flag, if it was set.
Errors here aren't expected, but should not break the outer enrollment transaction.
"""
email_opt_in = request.DATA.get('email_opt_in', None)
if email_opt_in is not None:
try:
update_email_opt_in(user, course_key.org, email_opt_in)
except Exception: # pylint: disable=broad-except
# log the error, return silently
log.exception(
'Failed to handle marketing opt-in flag: user="%s", course="%s"', user.username, course_key
)
def post(self, request, *args, **kwargs): # pylint: disable=unused-argument def post(self, request, *args, **kwargs): # pylint: disable=unused-argument
""" """
Attempt to create the basket and enroll the user. Attempt to create the basket and enroll the user.
...@@ -96,6 +113,7 @@ class BasketsView(APIView): ...@@ -96,6 +113,7 @@ class BasketsView(APIView):
username=user.username) username=user.username)
log.debug(msg) log.debug(msg)
self._enroll(course_key, user) self._enroll(course_key, user)
self._handle_marketing_opt_in(request, course_key, user)
return DetailResponse(msg) return DetailResponse(msg)
# Setup the API # Setup the API
...@@ -108,6 +126,8 @@ class BasketsView(APIView): ...@@ -108,6 +126,8 @@ class BasketsView(APIView):
log.debug(msg) log.debug(msg)
return DetailResponse(msg) return DetailResponse(msg)
response = None
# Make the API call # Make the API call
try: try:
response_data = api.baskets.post({ response_data = api.baskets.post({
...@@ -118,12 +138,12 @@ class BasketsView(APIView): ...@@ -118,12 +138,12 @@ class BasketsView(APIView):
payment_data = response_data["payment_data"] payment_data = response_data["payment_data"]
if payment_data: if payment_data:
# Pass data to the client to begin the payment flow. # Pass data to the client to begin the payment flow.
return JsonResponse(payment_data) response = JsonResponse(payment_data)
elif response_data['order']: elif response_data['order']:
# The order was completed immediately because there is no charge. # The order was completed immediately because there is no charge.
msg = Messages.ORDER_COMPLETED.format(order_number=response_data['order']['number']) msg = Messages.ORDER_COMPLETED.format(order_number=response_data['order']['number'])
log.debug(msg) log.debug(msg)
return DetailResponse(msg) response = DetailResponse(msg)
else: else:
msg = u'Unexpected response from basket endpoint.' msg = u'Unexpected response from basket endpoint.'
log.error( log.error(
...@@ -143,6 +163,9 @@ class BasketsView(APIView): ...@@ -143,6 +163,9 @@ class BasketsView(APIView):
user_id=user.id user_id=user.id
) )
self._handle_marketing_opt_in(request, course_key, user)
return response
class BasketOrderView(APIView): class BasketOrderView(APIView):
""" Retrieve the order associated with a basket. """ """ 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