Commit b8e48146 by Simon Chen Committed by GitHub

Merge pull request #15811 from edx/schen/LEARNER-2222

Stop audit and honor mode creating basket and order
parents e1ad295a 893b7a00
...@@ -15,7 +15,7 @@ from django.test.utils import override_settings ...@@ -15,7 +15,7 @@ from django.test.utils import override_settings
from edx_rest_api_client import exceptions from edx_rest_api_client import exceptions
from nose.plugins.attrib import attr from nose.plugins.attrib import attr
from commerce.api.v0.views import SAILTHRU_CAMPAIGN_COOKIE from commerce.api.v0.views import SAILTHRU_CAMPAIGN_COOKIE, STOP_BASKET_CREATION_FLAG
from commerce.constants import Messages from commerce.constants import Messages
from commerce.tests import TEST_BASKET_ID, TEST_ORDER_NUMBER, TEST_PAYMENT_DATA from commerce.tests import TEST_BASKET_ID, TEST_ORDER_NUMBER, TEST_PAYMENT_DATA
from commerce.tests.mocks import mock_basket_order, mock_create_basket from commerce.tests.mocks import mock_basket_order, mock_create_basket
...@@ -23,6 +23,7 @@ from commerce.tests.test_views import UserMixin ...@@ -23,6 +23,7 @@ from commerce.tests.test_views import UserMixin
from course_modes.models import CourseMode from course_modes.models import CourseMode
from enrollment.api import get_enrollment from enrollment.api import get_enrollment
from openedx.core.djangoapps.embargo.test_utils import restrict_course from openedx.core.djangoapps.embargo.test_utils import restrict_course
from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag
from openedx.core.lib.django_test_client_utils import get_absolute_url from openedx.core.lib.django_test_client_utils import get_absolute_url
from student.models import CourseEnrollment from student.models import CourseEnrollment
from student.tests.factories import CourseModeFactory from student.tests.factories import CourseModeFactory
...@@ -192,6 +193,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase) ...@@ -192,6 +193,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
cookie_name=UTM_COOKIE_NAME, cookie_contents=json.dumps(UTM_COOKIE_CONTENTS)) cookie_name=UTM_COOKIE_NAME, cookie_contents=json.dumps(UTM_COOKIE_CONTENTS))
self.assertIn(cookie_string, httpretty.last_request().headers['cookie']) self.assertIn(cookie_string, httpretty.last_request().headers['cookie'])
@override_waffle_flag(STOP_BASKET_CREATION_FLAG, active=False)
@ddt.data(True, False) @ddt.data(True, False)
def test_course_with_honor_seat_sku(self, user_is_active): def test_course_with_honor_seat_sku(self, user_is_active):
""" """
...@@ -214,6 +216,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase) ...@@ -214,6 +216,7 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
# Test that call with cookie passes cookie along # Test that call with cookie passes cookie along
self._test_successful_ecommerce_api_call(utm_tracking_present=True) self._test_successful_ecommerce_api_call(utm_tracking_present=True)
@override_waffle_flag(STOP_BASKET_CREATION_FLAG, active=False)
@ddt.data(True, False) @ddt.data(True, False)
def test_course_with_paid_seat_sku(self, user_is_active): def test_course_with_paid_seat_sku(self, user_is_active):
""" """
...@@ -228,6 +231,27 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase) ...@@ -228,6 +231,27 @@ class BasketsViewTests(EnrollmentEventTestMixin, UserMixin, ModuleStoreTestCase)
with mock_create_basket(response=return_value): with mock_create_basket(response=return_value):
self._test_successful_ecommerce_api_call(is_completed=False) self._test_successful_ecommerce_api_call(is_completed=False)
@override_waffle_flag(STOP_BASKET_CREATION_FLAG, active=True)
@ddt.data(True, False)
def test_course_without_creating_order(self, user_is_active):
"""
If the course has a SKU, and the STOP_BASKET_CREATION waffle flag is on,
the enrollment should happen without contacting ecommerce api
"""
# Set user's active flag
self.user.is_active = user_is_active
self.user.save() # pylint: disable=no-member
with mock_create_basket(expect_called=False):
response = self._post_to_view()
# Validate the response content
self.assertEqual(response.status_code, 200)
msg = Messages.ENROLL_DIRECTLY.format(
course_id=self.course.id,
username=self.user.username
)
self.assertResponseMessage(response, msg)
def _test_course_without_sku(self, enrollment_mode=CourseMode.DEFAULT_MODE_SLUG): def _test_course_without_sku(self, enrollment_mode=CourseMode.DEFAULT_MODE_SLUG):
""" """
Validates the view bypasses the E-Commerce API when the course has no CourseModes with SKUs. Validates the view bypasses the E-Commerce API when the course has no CourseModes with SKUs.
......
...@@ -13,6 +13,7 @@ from rest_framework.views import APIView ...@@ -13,6 +13,7 @@ from rest_framework.views import APIView
from commerce.constants import Messages from commerce.constants import Messages
from commerce.exceptions import InvalidResponseError from commerce.exceptions import InvalidResponseError
from commerce.http import DetailResponse, InternalRequestErrorResponse from commerce.http import DetailResponse, InternalRequestErrorResponse
from commerce.utils import COMMERCE_API_WAFFLE_FLAG_NAMESPACE
from course_modes.models import CourseMode from course_modes.models import CourseMode
from courseware import courses from courseware import courses
from enrollment.api import add_enrollment from enrollment.api import add_enrollment
...@@ -20,6 +21,7 @@ from enrollment.views import EnrollmentCrossDomainSessionAuth ...@@ -20,6 +21,7 @@ from enrollment.views import EnrollmentCrossDomainSessionAuth
from openedx.core.djangoapps.commerce.utils import ecommerce_api_client from openedx.core.djangoapps.commerce.utils import ecommerce_api_client
from openedx.core.djangoapps.embargo import api as embargo_api from openedx.core.djangoapps.embargo import api as embargo_api
from openedx.core.djangoapps.user_api.preferences.api import update_email_opt_in from openedx.core.djangoapps.user_api.preferences.api import update_email_opt_in
from openedx.core.djangoapps.waffle_utils import WaffleFlag
from openedx.core.lib.api.authentication import OAuth2AuthenticationAllowInactiveUser from openedx.core.lib.api.authentication import OAuth2AuthenticationAllowInactiveUser
from openedx.core.lib.log_utils import audit_log from openedx.core.lib.log_utils import audit_log
from student.models import CourseEnrollment, RegistrationCookieConfiguration from student.models import CourseEnrollment, RegistrationCookieConfiguration
...@@ -27,6 +29,7 @@ from util.json_request import JsonResponse ...@@ -27,6 +29,7 @@ from util.json_request import JsonResponse
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
SAILTHRU_CAMPAIGN_COOKIE = 'sailthru_bid' SAILTHRU_CAMPAIGN_COOKIE = 'sailthru_bid'
STOP_BASKET_CREATION_FLAG = WaffleFlag(COMMERCE_API_WAFFLE_FLAG_NAMESPACE, 'stop_basket_creation')
class BasketsView(APIView): class BasketsView(APIView):
...@@ -82,7 +85,7 @@ class BasketsView(APIView): ...@@ -82,7 +85,7 @@ class BasketsView(APIView):
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
""" """
Attempt to create the basket and enroll the user. Attempt to enroll the user, and if needed, create the basket.
""" """
user = request.user user = request.user
valid, course_key, error = self._is_data_valid(request) valid, course_key, error = self._is_data_valid(request)
...@@ -121,8 +124,13 @@ class BasketsView(APIView): ...@@ -121,8 +124,13 @@ class BasketsView(APIView):
if not default_enrollment_mode: if not default_enrollment_mode:
msg = Messages.NO_DEFAULT_ENROLLMENT_MODE.format(course_id=course_id) msg = Messages.NO_DEFAULT_ENROLLMENT_MODE.format(course_id=course_id)
return DetailResponse(msg, status=HTTP_406_NOT_ACCEPTABLE) return DetailResponse(msg, status=HTTP_406_NOT_ACCEPTABLE)
elif default_enrollment_mode and not default_enrollment_mode.sku: elif not default_enrollment_mode.sku or STOP_BASKET_CREATION_FLAG.is_enabled():
# If there are no course modes with SKUs, enroll the user without contacting the external API. msg = Messages.ENROLL_DIRECTLY.format(
username=user.username,
course_id=course_id
)
if not default_enrollment_mode.sku:
# If there are no course modes with SKUs, return a different message.
msg = Messages.NO_SKU_ENROLLED.format( msg = Messages.NO_SKU_ENROLLED.format(
enrollment_mode=default_enrollment_mode.slug, enrollment_mode=default_enrollment_mode.slug,
course_id=course_id, course_id=course_id,
...@@ -132,15 +140,33 @@ class BasketsView(APIView): ...@@ -132,15 +140,33 @@ class BasketsView(APIView):
self._enroll(course_key, user, default_enrollment_mode.slug) self._enroll(course_key, user, default_enrollment_mode.slug)
self._handle_marketing_opt_in(request, course_key, user) self._handle_marketing_opt_in(request, course_key, user)
return DetailResponse(msg) return DetailResponse(msg)
else:
return self._create_basket_to_order(request, user, course_key, default_enrollment_mode)
# Setup the API 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)
def _create_basket_to_order(self, request, user, course_key, default_enrollment_mode):
"""
Connect to the ecommerce service to create the basket and the order to do the enrollment
"""
# Setup the API
course_id = unicode(course_key)
try: try:
api_session = requests.Session() api_session = requests.Session()
api = ecommerce_api_client(user, session=api_session) api = ecommerce_api_client(user, session=api_session)
except ValueError: except ValueError:
self._enroll(course_key, user) self._enroll(course_key, user)
msg = Messages.NO_ECOM_API.format(username=user.username, course_id=unicode(course_key)) msg = Messages.NO_ECOM_API.format(username=user.username, course_id=course_id)
log.debug(msg) log.debug(msg)
return DetailResponse(msg) return DetailResponse(msg)
...@@ -191,18 +217,6 @@ class BasketsView(APIView): ...@@ -191,18 +217,6 @@ class BasketsView(APIView):
self._handle_marketing_opt_in(request, course_key, user) self._handle_marketing_opt_in(request, course_key, user)
return response 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): class BasketOrderView(APIView):
""" Retrieve the order associated with a basket. """ """ Retrieve the order associated with a basket. """
......
...@@ -12,6 +12,7 @@ class Messages(object): ...@@ -12,6 +12,7 @@ class Messages(object):
""" Strings used to populate response messages. """ """ Strings used to populate response messages. """
NO_ECOM_API = u'E-Commerce API not setup. Enrolled {username} in {course_id} directly.' NO_ECOM_API = u'E-Commerce API not setup. Enrolled {username} in {course_id} directly.'
NO_SKU_ENROLLED = u'The {enrollment_mode} mode for {course_id} does not have a SKU. Enrolling {username} directly.' NO_SKU_ENROLLED = u'The {enrollment_mode} mode for {course_id} does not have a SKU. Enrolling {username} directly.'
ENROLL_DIRECTLY = u'Enroll {username} in {course_id} directly because no need for E-Commerce baskets and orders.'
ORDER_COMPLETED = u'Order {order_number} was completed.' ORDER_COMPLETED = u'Order {order_number} was completed.'
ORDER_INCOMPLETE_ENROLLED = u'Order {order_number} was created, but is not yet complete. User was enrolled.' ORDER_INCOMPLETE_ENROLLED = u'Order {order_number} was created, but is not yet complete. User was enrolled.'
NO_HONOR_MODE = u'Course {course_id} does not have an honor mode.' NO_HONOR_MODE = u'Course {course_id} does not have an honor mode.'
......
...@@ -7,6 +7,9 @@ from django.conf import settings ...@@ -7,6 +7,9 @@ from django.conf import settings
from commerce.models import CommerceConfiguration from commerce.models import CommerceConfiguration
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.core.djangoapps.waffle_utils import WaffleFlagNamespace
COMMERCE_API_WAFFLE_FLAG_NAMESPACE = WaffleFlagNamespace(name='commerce_api')
def is_account_activation_requirement_disabled(): def is_account_activation_requirement_disabled():
......
...@@ -208,7 +208,7 @@ class WaffleFlagNamespace(WaffleNamespace): ...@@ -208,7 +208,7 @@ class WaffleFlagNamespace(WaffleNamespace):
""" """
# validate arguments # validate arguments
namespaced_flag_name = self._namespaced_name(flag_name) namespaced_flag_name = self._namespaced_name(flag_name)
value = None
if check_before_waffle_callback: if check_before_waffle_callback:
value = check_before_waffle_callback(namespaced_flag_name) value = check_before_waffle_callback(namespaced_flag_name)
......
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