diff --git a/lms/djangoapps/commerce/__init__.py b/lms/djangoapps/commerce/__init__.py index dac799ea63..01ef271447 100644 --- a/lms/djangoapps/commerce/__init__.py +++ b/lms/djangoapps/commerce/__init__.py @@ -1,9 +1,19 @@ """ Commerce app. """ from django.conf import settings from ecommerce_api_client.client import EcommerceApiClient +from eventtracking import tracker + + +def create_tracking_context(user): + """ Assembles attributes from user and request objects to be sent along + in ecommerce api calls for tracking purposes. """ + return { + 'lms_user_id': user.id, + 'lms_client_id': tracker.get_tracker().resolve_context().get('client_id') + } def ecommerce_api_client(user): """ Returns an E-Commerce API client setup with authentication for the specified user. """ return EcommerceApiClient(settings.ECOMMERCE_API_URL, settings.ECOMMERCE_API_SIGNING_KEY, user.username, - user.email) + user.email, tracking_context=create_tracking_context(user)) diff --git a/lms/djangoapps/commerce/tests/__init__.py b/lms/djangoapps/commerce/tests/__init__.py index 0024e50cf1..ca85990fd7 100644 --- a/lms/djangoapps/commerce/tests/__init__.py +++ b/lms/djangoapps/commerce/tests/__init__.py @@ -1,11 +1,58 @@ """ Commerce app tests package. """ import json +from django.test import TestCase +from django.test.utils import override_settings +from ecommerce_api_client.client import EcommerceApiClient import httpretty import jwt import mock -from ecommerce_api_client.client import EcommerceApiClient +from commerce import ecommerce_api_client +from student.tests.factories import UserFactory + + +class EcommerceApiClientTest(TestCase): + """ Tests to ensure the client is initialized properly. """ + + TEST_SIGNING_KEY = 'edx' + TEST_API_URL = 'http://example.com/api' + TEST_USER_EMAIL = 'test@example.com' + TEST_CLIENT_ID = 'test-client-id' + + @override_settings(ECOMMERCE_API_SIGNING_KEY=TEST_SIGNING_KEY, ECOMMERCE_API_URL=TEST_API_URL) + @httpretty.activate + def test_tracking_context(self): + """ Ensure the tracking context is set up in the api client correctly + and automatically. """ + user = UserFactory() + user.email = self.TEST_USER_EMAIL + user.save() # pylint: disable=no-member + + # fake an ecommerce api request. + httpretty.register_uri( + httpretty.POST, + '{}/baskets/1/'.format(self.TEST_API_URL), + status=200, body='{}', + adding_headers={'Content-Type': 'application/json'} + ) + mock_tracker = mock.Mock() + mock_tracker.resolve_context = mock.Mock(return_value={'client_id': self.TEST_CLIENT_ID}) + with mock.patch('commerce.tracker.get_tracker', return_value=mock_tracker): + ecommerce_api_client(user).baskets(1).post() + + # make sure the request's JWT token payload included correct tracking context values. + actual_header = httpretty.last_request().headers['Authorization'] + expected_payload = { + 'username': user.username, + 'email': user.email, + 'tracking_context': { + 'lms_user_id': user.id, # pylint: disable=no-member + 'lms_client_id': self.TEST_CLIENT_ID, + }, + } + expected_header = 'JWT {}'.format(jwt.encode(expected_payload, self.TEST_SIGNING_KEY)) + self.assertEqual(actual_header, expected_header) class EcommerceApiTestMixin(object): @@ -29,22 +76,6 @@ class EcommerceApiTestMixin(object): } ECOMMERCE_API_SUCCESSFUL_BODY_JSON = json.dumps(ECOMMERCE_API_SUCCESSFUL_BODY) # pylint: disable=invalid-name - def assertValidJWTAuthHeader(self, request, user, key): - """ Verifies that the JWT Authorization header is correct. """ - expected_jwt = jwt.encode({'username': user.username, 'email': user.email}, key) - self.assertEqual(request.headers['Authorization'], 'JWT {}'.format(expected_jwt)) - - def assertValidBasketRequest(self, request, user, jwt_signing_key, sku, processor): - """ Verifies that an order request to the E-Commerce Service is valid. """ - self.assertValidJWTAuthHeader(request, user, jwt_signing_key) - expected_body_data = { - 'products': [{'sku': sku}], - 'checkout': True, - 'payment_processor_name': processor - } - self.assertEqual(json.loads(request.body), expected_body_data) - self.assertEqual(request.headers['Content-Type'], 'application/json') - def _mock_ecommerce_api(self, status=200, body=None, is_payment_required=False): """ Mock calls to the E-Commerce API. diff --git a/requirements/edx/github.txt b/requirements/edx/github.txt index 5c18392a2f..2a09124494 100644 --- a/requirements/edx/github.txt +++ b/requirements/edx/github.txt @@ -48,7 +48,7 @@ git+https://github.com/edx/edx-lint.git@8bf82a32ecb8598c415413df66f5232ab8d974e9 -e git+https://github.com/edx/xblock-utils.git@581ed636c862b286002bb9a3724cc883570eb54c#egg=xblock-utils -e git+https://github.com/edx-solutions/xblock-google-drive.git@138e6fa0bf3a2013e904a085b9fed77dab7f3f21#egg=xblock-google-drive -e git+https://github.com/edx/edx-reverification-block.git@5da515ef229e73a137d366beb05ea4aea5881960#egg=edx-reverification-block -git+https://github.com/edx/ecommerce-api-client.git@0.2.0#egg=ecommerce-api-client +git+https://github.com/edx/ecommerce-api-client.git@0.3.0#egg=ecommerce-api-client # Third Party XBlocks -e git+https://github.com/mitodl/edx-sga@172a90fd2738f8142c10478356b2d9ed3e55334a#egg=edx-sga