Commit 9bb3f703 by Clinton Blackburn Committed by Clinton Blackburn

Updated receipt page to use order endpoint

The receipt page now retrieves data for orders instead of baskets. Going forward baskets will be deleted after an order has been placed, so there should be no permanent references to baskets. Orders will continue to be persisted permanently.

ECOM-2653
parent 352c5d13
......@@ -7,7 +7,7 @@ from commerce.api.v0 import views
BASKET_URLS = patterns(
'',
url(r'^$', views.BasketsView.as_view(), name='create'),
url(r'^{}/order/$'.format(r'(?P<basket_id>[\w]+)'), views.BasketOrderView.as_view(), name='retrieve_order'),
url(r'^(?P<basket_id>[\w]+)/order/$', views.BasketOrderView.as_view(), name='retrieve_order'),
)
urlpatterns = patterns(
......
......@@ -7,13 +7,19 @@ import ddt
from django.conf import settings
from django.contrib.auth.models import Permission
from django.core.urlresolvers import reverse
from django.test import TestCase
from django.test.utils import override_settings
from edx_rest_api_client import exceptions
from flaky import flaky
from nose.plugins.attrib import attr
import pytz
from rest_framework.utils.encoders import JSONEncoder
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
from commerce.tests import TEST_API_URL, TEST_API_SIGNING_KEY
from commerce.tests.mocks import mock_order_endpoint
from commerce.tests.test_views import UserMixin
from course_modes.models import CourseMode
from student.tests.factories import UserFactory
from verify_student.models import VerificationDeadline
......@@ -307,3 +313,38 @@ class CourseRetrieveUpdateViewTests(CourseApiViewTestMixin, ModuleStoreTestCase)
]
}
self.assertDictEqual(expected_dict, json.loads(response.content))
@attr('shard_1')
@override_settings(ECOMMERCE_API_URL=TEST_API_URL, ECOMMERCE_API_SIGNING_KEY=TEST_API_SIGNING_KEY)
class OrderViewTests(UserMixin, TestCase):
""" Tests for the basket order view. """
view_name = 'commerce_api:v1:orders:detail'
ORDER_NUMBER = 'EDX-100001'
MOCK_ORDER = {'number': ORDER_NUMBER}
path = reverse(view_name, kwargs={'number': ORDER_NUMBER})
def setUp(self):
super(OrderViewTests, self).setUp()
self._login()
def test_order_found(self):
""" If the order is located, the view should pass the data from the API. """
with mock_order_endpoint(order_number=self.ORDER_NUMBER, response=self.MOCK_ORDER):
response = self.client.get(self.path)
self.assertEqual(response.status_code, 200)
actual = json.loads(response.content)
self.assertEqual(actual, self.MOCK_ORDER)
def test_order_not_found(self):
""" If the order is not found, the view should return a 404. """
with mock_order_endpoint(order_number=self.ORDER_NUMBER, exception=exceptions.HttpNotFoundError):
response = self.client.get(self.path)
self.assertEqual(response.status_code, 404)
def test_login_required(self):
""" The view should return 403 if the user is not logged in. """
self.client.logout()
response = self.client.get(self.path)
self.assertEqual(response.status_code, 403)
......@@ -4,15 +4,19 @@ from django.conf.urls import patterns, url, include
from commerce.api.v1 import views
COURSE_URLS = patterns(
'',
url(r'^$', views.CourseListView.as_view(), name='list'),
url(r'^{}/$'.format(settings.COURSE_ID_PATTERN), views.CourseRetrieveUpdateView.as_view(), name='retrieve_update'),
)
ORDER_URLS = patterns(
'',
url(r'^(?P<number>[-\w]+)/$', views.OrderView.as_view(), name='detail'),
)
urlpatterns = patterns(
'',
url(r'^courses/', include(COURSE_URLS, namespace='courses')),
url(r'^orders/', include(ORDER_URLS, namespace='orders')),
)
......@@ -2,16 +2,20 @@
import logging
from django.http import Http404
from edx_rest_api_client import exceptions
from rest_framework.authentication import SessionAuthentication
from rest_framework_oauth.authentication import OAuth2Authentication
from rest_framework.views import APIView
from rest_framework.generics import RetrieveUpdateAPIView, ListAPIView
from rest_framework.permissions import IsAuthenticated
from rest_framework_oauth.authentication import OAuth2Authentication
from commerce import ecommerce_api_client
from commerce.api.v1.models import Course
from commerce.api.v1.permissions import ApiKeyOrModelPermission
from commerce.api.v1.serializers import CourseSerializer
from course_modes.models import CourseMode
from openedx.core.lib.api.mixins import PutAsCreateMixin
from util.json_request import JsonResponse
log = logging.getLogger(__name__)
......@@ -54,3 +58,18 @@ class CourseRetrieveUpdateView(PutAsCreateMixin, RetrieveUpdateAPIView):
# There is nothing to pre-save. The default behavior changes the Course.id attribute from
# a CourseKey to a string, which is not desired.
pass
class OrderView(APIView):
""" Retrieve order details. """
authentication_classes = (SessionAuthentication,)
permission_classes = (IsAuthenticated,)
def get(self, request, number): # pylint:disable=unused-argument
""" HTTP handler. """
try:
order = ecommerce_api_client(request.user).orders(number).get()
return JsonResponse(order)
except exceptions.HttpNotFoundError:
return JsonResponse(status=404)
......@@ -103,3 +103,17 @@ class mock_create_refund(mock_ecommerce_api_endpoint): # pylint: disable=invali
def get_uri(self):
return TEST_API_URL + '/refunds/'
class mock_order_endpoint(mock_ecommerce_api_endpoint): # pylint: disable=invalid-name
""" Mocks calls to E-Commerce API client basket order method. """
default_response = {'number': 'EDX-100001'}
method = httpretty.GET
def __init__(self, order_number, **kwargs):
super(mock_order_endpoint, self).__init__(**kwargs)
self.order_number = order_number
def get_uri(self):
return TEST_API_URL + '/orders/{}/'.format(self.order_number)
......@@ -10,9 +10,13 @@ var edx = edx || {};
edx.commerce.ReceiptView = Backbone.View.extend({
useEcommerceApi: true,
ecommerceBasketId: null,
ecommerceOrderNumber: null,
initialize: function () {
this.useEcommerceApi = !!($.url('?basket_id'));
this.ecommerceBasketId = $.url('?basket_id');
this.ecommerceOrderNumber = $.url('?orderNum');
this.useEcommerceApi = this.ecommerceBasketId || this.ecommerceOrderNumber;
_.bindAll(this, 'renderReceipt', 'renderError', 'getProviderData', 'renderProvider', 'getCourseData');
/* Mix non-conflicting functions from underscore.string (all but include, contains, and reverse) into
......@@ -75,7 +79,7 @@ var edx = edx || {};
render: function () {
var self = this,
orderId = $.url('?basket_id') || $.url('?payment-order-num');
orderId = this.ecommerceOrderNumber || this.ecommerceBasketId || $.url('?payment-order-num');
if (orderId && this.$el.data('is-payment-complete') === 'True') {
// Get the order details
......@@ -106,14 +110,21 @@ var edx = edx || {};
/**
* Retrieve receipt data from Oscar (via LMS).
* @param {int} basketId The basket that was purchased.
* @param {string} orderId Identifier of the order that was purchased.
* @return {object} JQuery Promise.
*/
getReceiptData: function (basketId) {
var urlFormat = this.useEcommerceApi ? '/api/commerce/v0/baskets/%s/order/' : '/shoppingcart/receipt/%s/';
getReceiptData: function (orderId) {
var urlFormat = '/shoppingcart/receipt/%s/';
if (this.ecommerceOrderNumber) {
urlFormat = '/api/commerce/v1/orders/%s/';
} else if (this.ecommerceBasketId){
urlFormat = '/api/commerce/v0/baskets/%s/order/';
}
return $.ajax({
url: _.sprintf(urlFormat, basketId),
url: _.sprintf(urlFormat, orderId),
type: 'GET',
dataType: 'json'
}).retry({times: 5, timeout: 2000, statusCodes: [404]});
......
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