Skip to content

Commit

Permalink
Updated receipt page to use order endpoint
Browse files Browse the repository at this point in the history
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
  • Loading branch information
clintonb authored and Clinton Blackburn committed Oct 28, 2015
1 parent 352c5d1 commit 9bb3f70
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 10 deletions.
2 changes: 1 addition & 1 deletion lms/djangoapps/commerce/api/v0/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
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(
Expand Down
41 changes: 41 additions & 0 deletions lms/djangoapps/commerce/api/v1/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,19 @@
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
Expand Down Expand Up @@ -307,3 +313,38 @@ def test_create_with_non_existent_course(self):
]
}
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)
8 changes: 6 additions & 2 deletions lms/djangoapps/commerce/api/v1/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@

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')),
)
21 changes: 20 additions & 1 deletion lms/djangoapps/commerce/api/v1/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__)

Expand Down Expand Up @@ -54,3 +58,18 @@ def pre_save(self, obj):
# 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)
14 changes: 14 additions & 0 deletions lms/djangoapps/commerce/tests/mocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
23 changes: 17 additions & 6 deletions lms/static/js/commerce/views/receipt_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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]});
Expand Down

0 comments on commit 9bb3f70

Please sign in to comment.