Commit 37477204 by Clinton Blackburn

Merge pull request #623 from edx/revert-543-douglashall/WL-286/support_multitenant_oauth

Revert "WL-286 Add support for multitenant OAuth"
parents 809b7369 985a7b78
from django.conf import settings from django.conf import settings
from ecommerce.core.url_utils import get_lms_dashboard_url
def core(_request): def core(_request):
return { return {
'lms_dashboard_url': get_lms_dashboard_url(),
'platform_name': settings.PLATFORM_NAME,
'support_url': settings.SUPPORT_URL, 'support_url': settings.SUPPORT_URL,
'platform_name': settings.PLATFORM_NAME
} }
class MissingRequestError(Exception):
""" Raised when the current request is missing from threadlocal storage """
pass
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import jsonfield.fields
class Migration(migrations.Migration):
dependencies = [
('core', '0010_add_async_sample'),
]
operations = [
migrations.AddField(
model_name='siteconfiguration',
name='oauth_settings',
field=jsonfield.fields.JSONField(default=b'{}', help_text='JSON string containing OAuth backend settings.', verbose_name='OAuth settings'),
),
]
...@@ -34,13 +34,6 @@ class SiteConfiguration(models.Model): ...@@ -34,13 +34,6 @@ class SiteConfiguration(models.Model):
null=False, null=False,
blank=False blank=False
) )
oauth_settings = JSONField(
verbose_name=_('OAuth settings'),
help_text=_("JSON string containing OAuth backend settings."),
null=False,
blank=False,
default='{}'
)
class Meta(object): class Meta(object):
unique_together = ('site', 'partner') unique_together = ('site', 'partner')
......
from django.test import override_settings, RequestFactory from django.test import override_settings, RequestFactory
from ecommerce.core.context_processors import core from ecommerce.core.context_processors import core
from ecommerce.core.url_utils import get_lms_dashboard_url
from ecommerce.tests.testcases import TestCase from ecommerce.tests.testcases import TestCase
PLATFORM_NAME = 'Test Platform' PLATFORM_NAME = 'Test Platform'
...@@ -12,11 +11,4 @@ class CoreContextProcessorTests(TestCase): ...@@ -12,11 +11,4 @@ class CoreContextProcessorTests(TestCase):
@override_settings(PLATFORM_NAME=PLATFORM_NAME, SUPPORT_URL=SUPPORT_URL) @override_settings(PLATFORM_NAME=PLATFORM_NAME, SUPPORT_URL=SUPPORT_URL)
def test_core(self): def test_core(self):
request = RequestFactory().get('/') request = RequestFactory().get('/')
self.assertDictEqual( self.assertDictEqual(core(request), {'platform_name': PLATFORM_NAME, 'support_url': SUPPORT_URL})
core(request),
{
'lms_dashboard_url': get_lms_dashboard_url(),
'platform_name': PLATFORM_NAME,
'support_url': SUPPORT_URL
}
)
import mock
from ecommerce.core.exceptions import MissingRequestError
from ecommerce.core.url_utils import get_ecommerce_url, get_lms_url
from ecommerce.tests.testcases import TestCase
class UrlUtilityTests(TestCase):
@mock.patch('ecommerce.core.url_utils.get_current_request', mock.Mock(return_value=None))
def test_get_ecommerce_url_with_no_current_request(self):
with self.assertRaises(MissingRequestError):
get_ecommerce_url()
@mock.patch('ecommerce.core.url_utils.get_current_request', mock.Mock(return_value=None))
def test_get_lms_url_with_no_current_request(self):
with self.assertRaises(MissingRequestError):
get_lms_url()
...@@ -24,7 +24,6 @@ class HealthTests(TestCase): ...@@ -24,7 +24,6 @@ class HealthTests(TestCase):
"""Tests of the health endpoint.""" """Tests of the health endpoint."""
def setUp(self): def setUp(self):
super(HealthTests, self).setUp()
self.fake_lms_response = Response() self.fake_lms_response = Response()
def test_all_services_available(self, mock_lms_request): def test_all_services_available(self, mock_lms_request):
...@@ -34,8 +33,6 @@ class HealthTests(TestCase): ...@@ -34,8 +33,6 @@ class HealthTests(TestCase):
self._assert_health(status.HTTP_200_OK, Status.OK, Status.OK, Status.OK) self._assert_health(status.HTTP_200_OK, Status.OK, Status.OK, Status.OK)
@mock.patch('ecommerce.core.views.get_lms_heartbeat_url', mock.Mock(return_value=''))
@mock.patch('django.contrib.sites.middleware.get_current_site', mock.Mock(return_value=None))
@mock.patch('django.db.backends.base.base.BaseDatabaseWrapper.cursor', mock.Mock(side_effect=DatabaseError)) @mock.patch('django.db.backends.base.base.BaseDatabaseWrapper.cursor', mock.Mock(side_effect=DatabaseError))
def test_database_outage(self, mock_lms_request): def test_database_outage(self, mock_lms_request):
"""Test that the endpoint reports when the database is unavailable.""" """Test that the endpoint reports when the database is unavailable."""
......
from urlparse import urljoin
from threadlocals.threadlocals import get_current_request
from ecommerce.core.exceptions import MissingRequestError
def get_ecommerce_url(path=''):
"""
Returns path joined with the appropriate ecommerce URL root for the current site
Raises:
MissingRequestError: If the current ecommerce site is not in threadlocal storage
"""
request = get_current_request()
if request:
ecommerce_url_root = "{}://{}".format(request.scheme, request.site.domain)
return urljoin(ecommerce_url_root, path)
raise MissingRequestError
def get_lms_commerce_api_url():
return get_lms_url('/api/commerce/v1/')
def get_lms_dashboard_url():
return get_lms_url('/dashboard')
def get_lms_enrollment_api_url():
return get_lms_url('/api/enrollment/v1/enrollment')
def get_lms_heartbeat_url():
return get_lms_url('/heartbeat')
def get_lms_url(path=''):
"""
Returns path joined with the appropriate LMS URL root for the current site
Raises:
MissingRequestError: If the current ecommerce site is not in threadlocal storage
"""
request = get_current_request()
if request:
return urljoin(request.site.siteconfiguration.lms_url_root, path)
raise MissingRequestError
def get_oauth2_provider_url():
return get_lms_url('/oauth2')
...@@ -16,8 +16,6 @@ from django.views.generic import View ...@@ -16,8 +16,6 @@ from django.views.generic import View
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from ecommerce.core.constants import Status, UnavailabilityMessage from ecommerce.core.constants import Status, UnavailabilityMessage
from ecommerce.core.url_utils import get_lms_heartbeat_url
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
User = get_user_model() User = get_user_model()
...@@ -55,7 +53,7 @@ def health(_): ...@@ -55,7 +53,7 @@ def health(_):
database_status = Status.UNAVAILABLE database_status = Status.UNAVAILABLE
try: try:
response = requests.get(get_lms_heartbeat_url(), timeout=1) response = requests.get(settings.LMS_HEARTBEAT_URL, timeout=1)
if response.status_code == status.HTTP_200_OK: if response.status_code == status.HTTP_200_OK:
lms_status = Status.OK lms_status = Status.OK
......
...@@ -11,11 +11,11 @@ from oscar.test.factories import OrderFactory, ConditionalOfferFactory, VoucherF ...@@ -11,11 +11,11 @@ from oscar.test.factories import OrderFactory, ConditionalOfferFactory, VoucherF
from oscar.test.utils import RequestFactory from oscar.test.utils import RequestFactory
import pytz import pytz
from ecommerce.core.url_utils import get_lms_url, get_lms_enrollment_api_url
from ecommerce.coupons.views import get_voucher_from_code, voucher_is_valid from ecommerce.coupons.views import get_voucher_from_code, voucher_is_valid
from ecommerce.courses.tests.factories import CourseFactory from ecommerce.courses.tests.factories import CourseFactory
from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin
from ecommerce.extensions.test.factories import prepare_voucher from ecommerce.extensions.test.factories import prepare_voucher
from ecommerce.settings import get_lms_url
from ecommerce.tests.mixins import CouponMixin, LmsApiMockMixin from ecommerce.tests.mixins import CouponMixin, LmsApiMockMixin
from ecommerce.tests.testcases import TestCase from ecommerce.tests.testcases import TestCase
...@@ -187,7 +187,7 @@ class CouponOfferViewTests(CourseCatalogTestMixin, LmsApiMockMixin, TestCase): ...@@ -187,7 +187,7 @@ class CouponOfferViewTests(CourseCatalogTestMixin, LmsApiMockMixin, TestCase):
response = self.client.get(self.path_with_code) response = self.client.get(self.path_with_code)
response_text = ( response_text = (
'Could not get course information. ' 'Could not get course information. '
'[Client Error 404: http://lms.testserver.fake/api/courses/v1/courses/{}/]' '[Client Error 404: http://127.0.0.1:8000/api/courses/v1/courses/{}/]'
).format(course.id) ).format(course.id)
self.assertEqual(response.context['error'], _(response_text)) self.assertEqual(response.context['error'], _(response_text))
...@@ -264,8 +264,8 @@ class CouponRedeemViewTests(CouponMixin, CourseCatalogTestMixin, TestCase): ...@@ -264,8 +264,8 @@ class CouponRedeemViewTests(CouponMixin, CourseCatalogTestMixin, TestCase):
def test_basket_redirect_enrollment_code(self): def test_basket_redirect_enrollment_code(self):
""" Verify the view redirects to LMS when an enrollment code is provided. """ """ Verify the view redirects to LMS when an enrollment code is provided. """
self.create_and_test_coupon() self.create_and_test_coupon()
httpretty.register_uri(httpretty.POST, get_lms_enrollment_api_url(), status=200) httpretty.register_uri(httpretty.POST, settings.ENROLLMENT_API_URL, status=200)
self.assert_redemption_page_redirects(get_lms_url()) self.assert_redemption_page_redirects(settings.LMS_URL_ROOT)
@httpretty.activate @httpretty.activate
def test_multiple_vouchers(self): def test_multiple_vouchers(self):
...@@ -273,5 +273,5 @@ class CouponRedeemViewTests(CouponMixin, CourseCatalogTestMixin, TestCase): ...@@ -273,5 +273,5 @@ class CouponRedeemViewTests(CouponMixin, CourseCatalogTestMixin, TestCase):
self.create_and_test_coupon() self.create_and_test_coupon()
basket = Basket.get_basket(self.user, self.site) basket = Basket.get_basket(self.user, self.site)
basket.vouchers.add(Voucher.objects.get(code=COUPON_CODE)) basket.vouchers.add(Voucher.objects.get(code=COUPON_CODE))
httpretty.register_uri(httpretty.POST, get_lms_enrollment_api_url(), status=200) httpretty.register_uri(httpretty.POST, settings.ENROLLMENT_API_URL, status=200)
self.assert_redemption_page_redirects(get_lms_url()) self.assert_redemption_page_redirects(settings.LMS_URL_ROOT)
...@@ -15,7 +15,6 @@ from oscar.core.loading import get_class, get_model ...@@ -15,7 +15,6 @@ from oscar.core.loading import get_class, get_model
from edx_rest_api_client.client import EdxRestApiClient from edx_rest_api_client.client import EdxRestApiClient
from edx_rest_api_client.exceptions import SlumberHttpBaseException from edx_rest_api_client.exceptions import SlumberHttpBaseException
from ecommerce.core.url_utils import get_lms_url
from ecommerce.core.views import StaffOnlyMixin from ecommerce.core.views import StaffOnlyMixin
from ecommerce.extensions.analytics.utils import prepare_analytics_data from ecommerce.extensions.analytics.utils import prepare_analytics_data
from ecommerce.extensions.api.constants import APIConstants as AC from ecommerce.extensions.api.constants import APIConstants as AC
...@@ -23,6 +22,7 @@ from ecommerce.extensions.api.data import get_lms_footer ...@@ -23,6 +22,7 @@ from ecommerce.extensions.api.data import get_lms_footer
from ecommerce.extensions.basket.utils import prepare_basket from ecommerce.extensions.basket.utils import prepare_basket
from ecommerce.extensions.checkout.mixins import EdxOrderPlacementMixin from ecommerce.extensions.checkout.mixins import EdxOrderPlacementMixin
from ecommerce.extensions.offer.utils import format_benefit_value from ecommerce.extensions.offer.utils import format_benefit_value
from ecommerce.settings import get_lms_url
Applicator = get_class('offer.utils', 'Applicator') Applicator = get_class('offer.utils', 'Applicator')
......
...@@ -8,8 +8,8 @@ from edx_rest_api_client.client import EdxRestApiClient ...@@ -8,8 +8,8 @@ from edx_rest_api_client.client import EdxRestApiClient
from edx_rest_api_client.exceptions import SlumberHttpBaseException from edx_rest_api_client.exceptions import SlumberHttpBaseException
import requests import requests
from ecommerce.core.url_utils import get_lms_url, get_lms_commerce_api_url
from ecommerce.courses.utils import mode_for_seat from ecommerce.courses.utils import mode_for_seat
from ecommerce.settings import get_lms_url
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -74,9 +74,8 @@ class LMSPublisher(object): ...@@ -74,9 +74,8 @@ class LMSPublisher(object):
course_id=course_id course_id=course_id
) )
commerce_api_url = get_lms_commerce_api_url() if not settings.COMMERCE_API_URL:
if not commerce_api_url: logger.error('COMMERCE_API_URL is not set. Commerce data will not be published!')
logger.error('Commerce API URL is not set. Commerce data will not be published!')
return error_message return error_message
name = course.name name = course.name
...@@ -110,7 +109,7 @@ class LMSPublisher(object): ...@@ -110,7 +109,7 @@ class LMSPublisher(object):
'modes': modes, 'modes': modes,
} }
url = '{}/courses/{}/'.format(commerce_api_url.rstrip('/'), course_id) url = '{}/courses/{}/'.format(settings.COMMERCE_API_URL.rstrip('/'), course_id)
headers = { headers = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
......
...@@ -2,16 +2,17 @@ import datetime ...@@ -2,16 +2,17 @@ import datetime
import json import json
import ddt import ddt
from django.conf import settings
from django.test import override_settings from django.test import override_settings
import httpretty import httpretty
import mock import mock
from requests import Timeout from requests import Timeout
from testfixtures import LogCapture from testfixtures import LogCapture
from ecommerce.core.url_utils import get_lms_url, get_lms_commerce_api_url
from ecommerce.courses.publishers import LMSPublisher from ecommerce.courses.publishers import LMSPublisher
from ecommerce.courses.tests.factories import CourseFactory from ecommerce.courses.tests.factories import CourseFactory
from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin
from ecommerce.settings import get_lms_url
from ecommerce.tests.testcases import TestCase from ecommerce.tests.testcases import TestCase
EDX_API_KEY = 'edx' EDX_API_KEY = 'edx'
...@@ -20,7 +21,7 @@ LOGGER_NAME = 'ecommerce.courses.publishers' ...@@ -20,7 +21,7 @@ LOGGER_NAME = 'ecommerce.courses.publishers'
@ddt.ddt @ddt.ddt
@override_settings(EDX_API_KEY=EDX_API_KEY) @override_settings(COMMERCE_API_URL='http://example.com/commerce/api/v1/', EDX_API_KEY=EDX_API_KEY)
class LMSPublisherTests(CourseCatalogTestMixin, TestCase): class LMSPublisherTests(CourseCatalogTestMixin, TestCase):
def setUp(self): def setUp(self):
super(LMSPublisherTests, self).setUp() super(LMSPublisherTests, self).setUp()
...@@ -36,7 +37,7 @@ class LMSPublisherTests(CourseCatalogTestMixin, TestCase): ...@@ -36,7 +37,7 @@ class LMSPublisherTests(CourseCatalogTestMixin, TestCase):
self.assertTrue(httpretty.is_enabled(), 'httpretty must be enabled to mock Commerce API calls.') self.assertTrue(httpretty.is_enabled(), 'httpretty must be enabled to mock Commerce API calls.')
body = body or {} body = body or {}
url = '{}/courses/{}/'.format(get_lms_commerce_api_url().rstrip('/'), self.course.id) url = '{}/courses/{}/'.format(settings.COMMERCE_API_URL.rstrip('/'), self.course.id)
httpretty.register_uri(httpretty.PUT, url, status=status, body=json.dumps(body), httpretty.register_uri(httpretty.PUT, url, status=status, body=json.dumps(body),
content_type=JSON) content_type=JSON)
...@@ -52,16 +53,15 @@ class LMSPublisherTests(CourseCatalogTestMixin, TestCase): ...@@ -52,16 +53,15 @@ class LMSPublisherTests(CourseCatalogTestMixin, TestCase):
content_type=JSON content_type=JSON
) )
@mock.patch('ecommerce.courses.publishers.get_lms_commerce_api_url', mock.Mock(return_value=None)) @ddt.data('', None)
def test_commerce_api_url_not_set(self): def test_commerce_api_url_not_set(self, setting_value):
""" If the commerce API url cannot be retrieved, the method should log an ERROR message and return """ """ If the Commerce API is not setup, the method should log an INFO message and return """
with LogCapture(LOGGER_NAME) as l: with override_settings(COMMERCE_API_URL=setting_value):
response = self.publisher.publish(self.course) with LogCapture(LOGGER_NAME) as l:
l.check( response = self.publisher.publish(self.course)
(LOGGER_NAME, 'ERROR', 'Commerce API URL is not set. Commerce data will not be published!') l.check((LOGGER_NAME, 'ERROR', 'COMMERCE_API_URL is not set. Commerce data will not be published!'))
) self.assertIsNotNone(response)
self.assertIsNotNone(response) self.assertEqual(response, self.error_message)
self.assertEqual(response, self.error_message)
def test_api_exception(self): def test_api_exception(self):
""" If an exception is raised when communicating with the Commerce API, an ERROR message should be logged. """ """ If an exception is raised when communicating with the Commerce API, an ERROR message should be logged. """
......
...@@ -5,7 +5,7 @@ from django.conf import settings ...@@ -5,7 +5,7 @@ from django.conf import settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from testfixtures import LogCapture from testfixtures import LogCapture
from ecommerce.core.url_utils import get_lms_url from ecommerce.settings import get_lms_url
from ecommerce.tests.testcases import TestCase from ecommerce.tests.testcases import TestCase
LOGGER_NAME = 'ecommerce.courses.views' LOGGER_NAME = 'ecommerce.courses.views'
......
...@@ -12,9 +12,9 @@ from edx_rest_api_client.client import EdxRestApiClient ...@@ -12,9 +12,9 @@ from edx_rest_api_client.client import EdxRestApiClient
from requests import Timeout from requests import Timeout
from slumber.exceptions import SlumberBaseException from slumber.exceptions import SlumberBaseException
from ecommerce.core.url_utils import get_lms_url
from ecommerce.core.views import StaffOnlyMixin from ecommerce.core.views import StaffOnlyMixin
from ecommerce.extensions.partner.shortcuts import get_partner_for_site from ecommerce.extensions.partner.shortcuts import get_partner_for_site
from ecommerce.settings import get_lms_url
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
......
...@@ -11,10 +11,10 @@ import httpretty ...@@ -11,10 +11,10 @@ import httpretty
from waffle.models import Switch from waffle.models import Switch
from ecommerce.core.tests import toggle_switch from ecommerce.core.tests import toggle_switch
from ecommerce.core.url_utils import get_lms_url
from ecommerce.courses.models import Course from ecommerce.courses.models import Course
from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin
from ecommerce.extensions.payment.helpers import get_processor_class from ecommerce.extensions.payment.helpers import get_processor_class
from ecommerce.settings import get_lms_url
from ecommerce.tests.mixins import JwtMixin from ecommerce.tests.mixins import JwtMixin
from ecommerce.tests.testcases import TestCase from ecommerce.tests.testcases import TestCase
......
...@@ -14,11 +14,11 @@ from edx_rest_api_client.client import EdxRestApiClient ...@@ -14,11 +14,11 @@ from edx_rest_api_client.client import EdxRestApiClient
from slumber.exceptions import SlumberHttpBaseException from slumber.exceptions import SlumberHttpBaseException
import waffle import waffle
from ecommerce.core.url_utils import get_lms_url
from ecommerce.courses.models import Course from ecommerce.courses.models import Course
from ecommerce.extensions.analytics.utils import prepare_analytics_data from ecommerce.extensions.analytics.utils import prepare_analytics_data
from ecommerce.extensions.partner.shortcuts import get_partner_for_site from ecommerce.extensions.partner.shortcuts import get_partner_for_site
from ecommerce.extensions.payment.helpers import get_processor_class from ecommerce.extensions.payment.helpers import get_processor_class
from ecommerce.settings import get_lms_url
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
......
"""JWT authentication scheme for use with DRF.""" """JWT authentication scheme for use with DRF."""
import logging import logging
from django.conf import settings
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
import requests import requests
from rest_framework import exceptions from rest_framework import exceptions
...@@ -8,8 +9,6 @@ from rest_framework.authentication import get_authorization_header, BaseAuthenti ...@@ -8,8 +9,6 @@ from rest_framework.authentication import get_authorization_header, BaseAuthenti
from rest_framework.status import HTTP_200_OK from rest_framework.status import HTTP_200_OK
from rest_framework_jwt.authentication import JSONWebTokenAuthentication from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from ecommerce.core.url_utils import get_oauth2_provider_url
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
User = get_user_model() User = get_user_model()
...@@ -90,7 +89,7 @@ class BearerAuthentication(BaseAuthentication): ...@@ -90,7 +89,7 @@ class BearerAuthentication(BaseAuthentication):
""" """
def authenticate(self, request): def authenticate(self, request):
provider_url = get_oauth2_provider_url() provider_url = getattr(settings, 'OAUTH2_PROVIDER_URL', None)
if not provider_url: if not provider_url:
return None return None
......
...@@ -2,11 +2,12 @@ ...@@ -2,11 +2,12 @@
import logging import logging
import requests import requests
from django.conf import settings
from oscar.core.loading import get_model, get_class from oscar.core.loading import get_model, get_class
from ecommerce.core.url_utils import get_lms_url
from ecommerce.extensions.api import exceptions from ecommerce.extensions.api import exceptions
from ecommerce.extensions.api.constants import APIConstants as AC from ecommerce.extensions.api.constants import APIConstants as AC
from ecommerce.settings import get_lms_url
NoShippingRequired = get_class('shipping.methods', 'NoShippingRequired') NoShippingRequired = get_class('shipping.methods', 'NoShippingRequired')
OrderTotalCalculator = get_class('checkout.calculators', 'OrderTotalCalculator') OrderTotalCalculator = get_class('checkout.calculators', 'OrderTotalCalculator')
...@@ -58,9 +59,8 @@ def get_lms_footer(): ...@@ -58,9 +59,8 @@ def get_lms_footer():
str: HTML representation of the footer. str: HTML representation of the footer.
""" """
try: try:
footer_api_url = get_lms_url('api/branding/v1/footer')
response = requests.get( response = requests.get(
footer_api_url, get_lms_url('api/branding/v1/footer'),
data={'language': 'en'} data={'language': 'en'}
) )
if response.status_code == 200: if response.status_code == 200:
...@@ -68,11 +68,11 @@ def get_lms_footer(): ...@@ -68,11 +68,11 @@ def get_lms_footer():
else: else:
logger.error( logger.error(
'Failed retrieve provider information for %s provider. Provider API returned status code %d. Error: %s', 'Failed retrieve provider information for %s provider. Provider API returned status code %d. Error: %s',
footer_api_url, response.status_code, response.text) settings.LMS_URL_ROOT, response.status_code, response.text)
return None return None
except requests.exceptions.ConnectionError: except requests.exceptions.ConnectionError:
logger.exception('Connection error occurred during getting data for %s provider', get_lms_url()) logger.exception('Connection error occurred during getting data for %s provider', settings.LMS_URL_ROOT)
return None return None
except requests.Timeout: except requests.Timeout:
logger.exception('Failed to retrieve data for %s provider, connection timeout', get_lms_url()) logger.exception('Failed to retrieve data for %s provider, connection timeout', settings.LMS_URL_ROOT)
return None return None
...@@ -3,6 +3,7 @@ from decimal import Decimal ...@@ -3,6 +3,7 @@ from decimal import Decimal
import logging import logging
from dateutil.parser import parse from dateutil.parser import parse
from django.conf import settings
from django.db import transaction from django.db import transaction
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from oscar.core.loading import get_model, get_class from oscar.core.loading import get_model, get_class
...@@ -11,7 +12,6 @@ from rest_framework.reverse import reverse ...@@ -11,7 +12,6 @@ from rest_framework.reverse import reverse
import waffle import waffle
from ecommerce.core.constants import ISO_8601_FORMAT, COURSE_ID_REGEX from ecommerce.core.constants import ISO_8601_FORMAT, COURSE_ID_REGEX
from ecommerce.core.url_utils import get_ecommerce_url
from ecommerce.courses.models import Course from ecommerce.courses.models import Course
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -372,8 +372,8 @@ class VoucherSerializer(serializers.ModelSerializer): ...@@ -372,8 +372,8 @@ class VoucherSerializer(serializers.ModelSerializer):
return (obj.offers.first().benefit.type, obj.offers.first().benefit.value) return (obj.offers.first().benefit.type, obj.offers.first().benefit.value)
def get_redeem_url(self, obj): def get_redeem_url(self, obj):
url = get_ecommerce_url('/coupons/offer/') domain = settings.ECOMMERCE_URL_ROOT
return '{url}?code={code}'.format(url=url, code=obj.code) return "{}/coupons/offer/?code={}".format(domain, obj.code)
class Meta(object): class Meta(object):
model = Voucher model = Voucher
......
...@@ -16,11 +16,12 @@ from rest_framework.exceptions import AuthenticationFailed ...@@ -16,11 +16,12 @@ from rest_framework.exceptions import AuthenticationFailed
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework_jwt import utils from rest_framework_jwt import utils
from ecommerce.core.url_utils import get_oauth2_provider_url
from ecommerce.extensions.api.authentication import BearerAuthentication, JwtAuthentication from ecommerce.extensions.api.authentication import BearerAuthentication, JwtAuthentication
from ecommerce.tests.mixins import JwtMixin from ecommerce.tests.mixins import JwtMixin
from ecommerce.tests.testcases import TestCase from ecommerce.tests.testcases import TestCase
OAUTH2_PROVIDER_URL = 'https://example.com/oauth2'
User = get_user_model() User = get_user_model()
...@@ -40,12 +41,13 @@ class AccessTokenMixin(object): ...@@ -40,12 +41,13 @@ class AccessTokenMixin(object):
DEFAULT_TOKEN = 'abc123' DEFAULT_TOKEN = 'abc123'
def _mock_access_token_response(self, status=200, token=DEFAULT_TOKEN, username='fake-user'): def _mock_access_token_response(self, status=200, token=DEFAULT_TOKEN, username='fake-user'):
httpretty.register_uri(httpretty.GET, '{}/access_token/{}/'.format(get_oauth2_provider_url(), token), httpretty.register_uri(httpretty.GET, '{}/access_token/{}/'.format(OAUTH2_PROVIDER_URL, token),
body=json.dumps({'username': username, 'scope': 'read', 'expires_in': 60}), body=json.dumps({'username': username, 'scope': 'read', 'expires_in': 60}),
content_type="application/json", content_type="application/json",
status=status) status=status)
@override_settings(OAUTH2_PROVIDER_URL=OAUTH2_PROVIDER_URL)
class BearerAuthenticationTests(AccessTokenMixin, TestCase): class BearerAuthenticationTests(AccessTokenMixin, TestCase):
def setUp(self): def setUp(self):
super(BearerAuthenticationTests, self).setUp() super(BearerAuthenticationTests, self).setUp()
...@@ -61,9 +63,15 @@ class BearerAuthenticationTests(AccessTokenMixin, TestCase): ...@@ -61,9 +63,15 @@ class BearerAuthenticationTests(AccessTokenMixin, TestCase):
""" The method should return the string Bearer. """ """ The method should return the string Bearer. """
self.assertEqual(self.auth.authenticate_header(self._create_request()), 'Bearer') self.assertEqual(self.auth.authenticate_header(self._create_request()), 'Bearer')
@mock.patch('ecommerce.core.url_utils.get_lms_url', mock.Mock(return_value=None)) @override_settings(OAUTH2_PROVIDER_URL=None)
def test_authenticate_no_provider(self): def test_authenticate_no_provider(self):
""" If the we cannot get the LMS URL, the method returns None. """ """ If the setting OAUTH2_PROVIDER_URL is not set, the method returns None. """
# Empty value
self.assertIsNone(self.auth.authenticate(self._create_request()))
# Missing value
del settings.OAUTH2_PROVIDER_URL
self.assertIsNone(self.auth.authenticate(self._create_request())) self.assertIsNone(self.auth.authenticate(self._create_request()))
def test_authenticate_invalid_token(self): def test_authenticate_invalid_token(self):
......
import json import json
from django.conf import settings
import httpretty import httpretty
import mock import mock
import requests import requests
from testfixtures import LogCapture from testfixtures import LogCapture
from ecommerce.core.url_utils import get_lms_url
from ecommerce.extensions.api.data import get_lms_footer from ecommerce.extensions.api.data import get_lms_footer
from ecommerce.tests.testcases import TestCase from ecommerce.tests.testcases import TestCase
from ecommerce.settings import get_lms_url
LOGGER_NAME = 'ecommerce.extensions.api.data' LOGGER_NAME = 'ecommerce.extensions.api.data'
...@@ -16,9 +17,10 @@ LOGGER_NAME = 'ecommerce.extensions.api.data' ...@@ -16,9 +17,10 @@ LOGGER_NAME = 'ecommerce.extensions.api.data'
class DataFunctionsTests(TestCase): class DataFunctionsTests(TestCase):
""" Tests for api data functions. """ """ Tests for api data functions. """
footer_url = get_lms_url('api/branding/v1/footer')
def setUp(self): def setUp(self):
super(DataFunctionsTests, self).setUp() super(DataFunctionsTests, self).setUp()
self.footer_url = get_lms_url('api/branding/v1/footer')
@httpretty.activate @httpretty.activate
def test_get_lms_footer_success(self): def test_get_lms_footer_success(self):
...@@ -47,7 +49,7 @@ class DataFunctionsTests(TestCase): ...@@ -47,7 +49,7 @@ class DataFunctionsTests(TestCase):
( (
LOGGER_NAME, 'ERROR', LOGGER_NAME, 'ERROR',
u'Connection error occurred during getting data for {lms_url} provider'.format( u'Connection error occurred during getting data for {lms_url} provider'.format(
lms_url=get_lms_url() lms_url=settings.LMS_URL_ROOT
) )
) )
) )
...@@ -62,7 +64,7 @@ class DataFunctionsTests(TestCase): ...@@ -62,7 +64,7 @@ class DataFunctionsTests(TestCase):
( (
LOGGER_NAME, 'ERROR', LOGGER_NAME, 'ERROR',
u'Failed to retrieve data for {lms_url} provider, connection timeout'.format( u'Failed to retrieve data for {lms_url} provider, connection timeout'.format(
lms_url=get_lms_url() lms_url=settings.LMS_URL_ROOT
) )
) )
) )
......
...@@ -9,7 +9,7 @@ from django.test import override_settings ...@@ -9,7 +9,7 @@ from django.test import override_settings
from oscar.core.loading import get_model from oscar.core.loading import get_model
from oscar.test import factories from oscar.test import factories
from ecommerce.extensions.api.tests.test_authentication import AccessTokenMixin from ecommerce.extensions.api.tests.test_authentication import AccessTokenMixin, OAUTH2_PROVIDER_URL
from ecommerce.extensions.api.v2.tests.views import OrderDetailViewTestMixin from ecommerce.extensions.api.v2.tests.views import OrderDetailViewTestMixin
from ecommerce.extensions.fulfillment.signals import SHIPPING_EVENT_NAME from ecommerce.extensions.fulfillment.signals import SHIPPING_EVENT_NAME
from ecommerce.extensions.fulfillment.status import ORDER from ecommerce.extensions.fulfillment.status import ORDER
...@@ -42,6 +42,7 @@ class OrderListViewTests(AccessTokenMixin, ThrottlingMixin, TestCase): ...@@ -42,6 +42,7 @@ class OrderListViewTests(AccessTokenMixin, ThrottlingMixin, TestCase):
self.assertEqual(content['results'], []) self.assertEqual(content['results'], [])
@httpretty.activate @httpretty.activate
@override_settings(OAUTH2_PROVIDER_URL=OAUTH2_PROVIDER_URL)
def test_access_token(self): def test_access_token(self):
""" Verifies that the view accepts OAuth2 access tokens for authentication.""" """ Verifies that the view accepts OAuth2 access tokens for authentication."""
auth_header = 'Bearer {}'.format(self.DEFAULT_TOKEN) auth_header = 'Bearer {}'.format(self.DEFAULT_TOKEN)
......
...@@ -14,7 +14,6 @@ class PaymentProcessorListViewTests(TestCase): ...@@ -14,7 +14,6 @@ class PaymentProcessorListViewTests(TestCase):
""" Ensures correct behavior of the payment processors list view.""" """ Ensures correct behavior of the payment processors list view."""
def setUp(self): def setUp(self):
super(PaymentProcessorListViewTests, self).setUp()
self.token = self.generate_jwt_token_header(self.create_user()) self.token = self.generate_jwt_token_header(self.create_user())
self.toggle_payment_processor('dummy', True) self.toggle_payment_processor('dummy', True)
self.toggle_payment_processor('another-dummy', True) self.toggle_payment_processor('another-dummy', True)
......
...@@ -2,6 +2,7 @@ import json ...@@ -2,6 +2,7 @@ import json
import ddt import ddt
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.test import override_settings
import httpretty import httpretty
import mock import mock
from oscar.core.loading import get_model from oscar.core.loading import get_model
...@@ -9,7 +10,7 @@ from oscar.test import factories ...@@ -9,7 +10,7 @@ from oscar.test import factories
from rest_framework import status from rest_framework import status
from ecommerce.extensions.api.serializers import RefundSerializer from ecommerce.extensions.api.serializers import RefundSerializer
from ecommerce.extensions.api.tests.test_authentication import AccessTokenMixin from ecommerce.extensions.api.tests.test_authentication import AccessTokenMixin, OAUTH2_PROVIDER_URL
from ecommerce.extensions.api.v2.tests.views import JSON_CONTENT_TYPE from ecommerce.extensions.api.v2.tests.views import JSON_CONTENT_TYPE
from ecommerce.extensions.refund.tests.factories import RefundLineFactory, RefundFactory from ecommerce.extensions.refund.tests.factories import RefundLineFactory, RefundFactory
from ecommerce.extensions.refund.tests.mixins import RefundTestMixin from ecommerce.extensions.refund.tests.mixins import RefundTestMixin
...@@ -95,6 +96,7 @@ class RefundCreateViewTests(RefundTestMixin, AccessTokenMixin, JwtMixin, TestCas ...@@ -95,6 +96,7 @@ class RefundCreateViewTests(RefundTestMixin, AccessTokenMixin, JwtMixin, TestCas
self.assert_ok_response(response) self.assert_ok_response(response)
@httpretty.activate @httpretty.activate
@override_settings(OAUTH2_PROVIDER_URL=OAUTH2_PROVIDER_URL)
def test_oauth_authentication(self): def test_oauth_authentication(self):
""" Client can authenticate with OAuth. """ """ Client can authenticate with OAuth. """
self.client.logout() self.client.logout()
......
...@@ -16,12 +16,12 @@ from slumber.exceptions import SlumberBaseException ...@@ -16,12 +16,12 @@ from slumber.exceptions import SlumberBaseException
from testfixtures import LogCapture from testfixtures import LogCapture
from ecommerce.core.tests import toggle_switch from ecommerce.core.tests import toggle_switch
from ecommerce.core.url_utils import get_lms_url
from ecommerce.courses.tests.factories import CourseFactory from ecommerce.courses.tests.factories import CourseFactory
from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin
from ecommerce.extensions.offer.utils import format_benefit_value from ecommerce.extensions.offer.utils import format_benefit_value
from ecommerce.extensions.payment.tests.processors import DummyProcessor from ecommerce.extensions.payment.tests.processors import DummyProcessor
from ecommerce.extensions.test.factories import prepare_voucher from ecommerce.extensions.test.factories import prepare_voucher
from ecommerce.settings import get_lms_url
from ecommerce.tests.factories import StockRecordFactory from ecommerce.tests.factories import StockRecordFactory
from ecommerce.tests.mixins import CouponMixin, LmsApiMockMixin from ecommerce.tests.mixins import CouponMixin, LmsApiMockMixin
from ecommerce.tests.testcases import TestCase from ecommerce.tests.testcases import TestCase
......
...@@ -13,7 +13,6 @@ from oscar.apps.basket.views import * # pylint: disable=wildcard-import, unused ...@@ -13,7 +13,6 @@ from oscar.apps.basket.views import * # pylint: disable=wildcard-import, unused
from edx_rest_api_client.client import EdxRestApiClient from edx_rest_api_client.client import EdxRestApiClient
from slumber.exceptions import SlumberBaseException from slumber.exceptions import SlumberBaseException
from ecommerce.core.url_utils import get_lms_url
from ecommerce.coupons.views import get_voucher_from_code from ecommerce.coupons.views import get_voucher_from_code
from ecommerce.extensions.analytics.utils import prepare_analytics_data from ecommerce.extensions.analytics.utils import prepare_analytics_data
from ecommerce.extensions.api.data import get_lms_footer from ecommerce.extensions.api.data import get_lms_footer
...@@ -21,6 +20,7 @@ from ecommerce.extensions.basket.utils import get_certificate_type_display_value ...@@ -21,6 +20,7 @@ from ecommerce.extensions.basket.utils import get_certificate_type_display_value
from ecommerce.extensions.offer.utils import format_benefit_value from ecommerce.extensions.offer.utils import format_benefit_value
from ecommerce.extensions.payment.helpers import get_processor_class from ecommerce.extensions.payment.helpers import get_processor_class
from ecommerce.extensions.partner.shortcuts import get_partner_for_site from ecommerce.extensions.partner.shortcuts import get_partner_for_site
from ecommerce.settings import get_lms_url
Benefit = get_model('offer', 'Benefit') Benefit = get_model('offer', 'Benefit')
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
......
...@@ -4,9 +4,9 @@ from optparse import make_option ...@@ -4,9 +4,9 @@ from optparse import make_option
from dateutil.parser import parse from dateutil.parser import parse
from django.conf import settings from django.conf import settings
from django.contrib.sites.models import Site
from django.core.management import BaseCommand from django.core.management import BaseCommand
from django.db import transaction from django.db import transaction
from oscar.core.loading import get_model
import requests import requests
import waffle import waffle
...@@ -14,12 +14,13 @@ from ecommerce.courses.models import Course ...@@ -14,12 +14,13 @@ from ecommerce.courses.models import Course
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
Partner = get_model('partner', 'Partner')
class MigratedCourse(object): class MigratedCourse(object):
def __init__(self, course_id, site_domain): def __init__(self, course_id, partner_short_code):
self.course, _created = Course.objects.get_or_create(id=course_id) self.course, _created = Course.objects.get_or_create(id=course_id)
self.site_configuration = Site.objects.get(domain=site_domain).siteconfiguration self.partner = Partner.objects.get(short_code=partner_short_code)
def load_from_lms(self, access_token): def load_from_lms(self, access_token):
""" """
...@@ -38,12 +39,17 @@ class MigratedCourse(object): ...@@ -38,12 +39,17 @@ class MigratedCourse(object):
def _build_lms_url(self, path): def _build_lms_url(self, path):
# We avoid using urljoin here because it URL-encodes the path, and some LMS APIs # We avoid using urljoin here because it URL-encodes the path, and some LMS APIs
# are not capable of decoding these values. # are not capable of decoding these values.
host = self.site_configuration.lms_url_root.strip('/') host = settings.LMS_URL_ROOT.strip('/')
return '{host}/{path}'.format(host=host, path=path) return '{host}/{path}'.format(host=host, path=path)
def _query_commerce_api(self, headers): def _query_commerce_api(self, headers):
"""Get course name and verification deadline from the Commerce API.""" """Get course name and verification deadline from the Commerce API."""
url = '{}/courses/{}/'.format(self._build_lms_url('api/commerce/v1'), self.course.id) if not settings.COMMERCE_API_URL:
message = 'Aborting migration. COMMERCE_API_URL is not set.'
logger.error(message)
raise Exception(message)
url = '{}/courses/{}/'.format(settings.COMMERCE_API_URL.rstrip('/'), self.course.id)
timeout = settings.COMMERCE_API_TIMEOUT timeout = settings.COMMERCE_API_TIMEOUT
response = requests.get(url, headers=headers, timeout=timeout) response = requests.get(url, headers=headers, timeout=timeout)
...@@ -145,7 +151,7 @@ class MigratedCourse(object): ...@@ -145,7 +151,7 @@ class MigratedCourse(object):
expires = mode.get('expiration_datetime') expires = mode.get('expiration_datetime')
expires = parse(expires) if expires else None expires = parse(expires) if expires else None
self.course.create_or_update_seat( self.course.create_or_update_seat(
certificate_type, id_verification_required, price, self.site_configuration.partner, certificate_type, id_verification_required, price, self.partner,
expires=expires, remove_stale_modes=False expires=expires, remove_stale_modes=False
) )
...@@ -165,30 +171,30 @@ class Command(BaseCommand): ...@@ -165,30 +171,30 @@ class Command(BaseCommand):
default=False, default=False,
help='Save the migrated data to the database. If this is not set, ' help='Save the migrated data to the database. If this is not set, '
'migrated data will NOT be saved to the database.'), 'migrated data will NOT be saved to the database.'),
make_option('--site', make_option('--partner',
action='store', action='store',
dest='site_domain', dest='partner_short_code',
default=None, default=None,
help='Domain for the ecommerce site providing the course.'), help='Short code for the partner providing the course.'),
) )
def handle(self, *args, **options): def handle(self, *args, **options):
course_ids = args course_ids = args
access_token = options.get('access_token') access_token = options.get('access_token')
site_domain = options.get('site_domain') partner_short_code = options.get('partner_short_code')
if not access_token: if not access_token:
logger.error('Courses cannot be migrated if no access token is supplied.') logger.error('Courses cannot be migrated if no access token is supplied.')
return return
if not site_domain: if not partner_short_code:
logger.error('Courses cannot be migrated without providing a site domain.') logger.error('Courses cannot be migrated without providing a partner short code.')
return return
for course_id in course_ids: for course_id in course_ids:
course_id = unicode(course_id) course_id = unicode(course_id)
try: try:
with transaction.atomic(): with transaction.atomic():
migrated_course = MigratedCourse(course_id, site_domain) migrated_course = MigratedCourse(course_id, partner_short_code)
migrated_course.load_from_lms(access_token) migrated_course.load_from_lms(access_token)
course = migrated_course.course course = migrated_course.course
......
...@@ -6,6 +6,7 @@ import json ...@@ -6,6 +6,7 @@ import json
import logging import logging
from urlparse import urljoin, urlparse from urlparse import urljoin, urlparse
from django.conf import settings
from django.core.management import call_command from django.core.management import call_command
from django.test import override_settings from django.test import override_settings
import httpretty import httpretty
...@@ -40,6 +41,9 @@ StockRecord = get_model('partner', 'StockRecord') ...@@ -40,6 +41,9 @@ StockRecord = get_model('partner', 'StockRecord')
class CourseMigrationTestMixin(CourseCatalogTestMixin): class CourseMigrationTestMixin(CourseCatalogTestMixin):
course_id = 'aaa/bbb/ccc' course_id = 'aaa/bbb/ccc'
course_name = 'A Tést Côurse' course_name = 'A Tést Côurse'
commerce_api_url = '{}/courses/{}/'.format(settings.COMMERCE_API_URL.rstrip('/'), course_id)
course_structure_url = urljoin(settings.LMS_URL_ROOT, 'api/course_structure/v0/courses/{}/'.format(course_id))
enrollment_api_url = urljoin(settings.LMS_URL_ROOT, 'api/enrollment/v1/course/{}'.format(course_id))
prices = { prices = {
'honor': 0, 'honor': 0,
...@@ -50,23 +54,6 @@ class CourseMigrationTestMixin(CourseCatalogTestMixin): ...@@ -50,23 +54,6 @@ class CourseMigrationTestMixin(CourseCatalogTestMixin):
'credit': 0, 'credit': 0,
} }
@property
def lms_url(self):
return self.site.siteconfiguration.lms_url_root
@property
def commerce_api_url(self):
lms_commerce_api_url = urljoin(self.lms_url, '/api/commerce/v1')
return '{}/courses/{}/'.format(lms_commerce_api_url, self.course_id)
@property
def course_structure_url(self):
return urljoin(self.lms_url, 'api/course_structure/v0/courses/{}/'.format(self.course_id))
@property
def enrollment_api_url(self):
return urljoin(self.lms_url, 'api/enrollment/v1/course/{}'.format(self.course_id))
def _mock_lms_apis(self): def _mock_lms_apis(self):
self.assertTrue(httpretty.is_enabled(), 'httpretty must be enabled to mock LMS API calls.') self.assertTrue(httpretty.is_enabled(), 'httpretty must be enabled to mock LMS API calls.')
...@@ -151,7 +138,7 @@ class MigratedCourseTests(CourseMigrationTestMixin, TestCase): ...@@ -151,7 +138,7 @@ class MigratedCourseTests(CourseMigrationTestMixin, TestCase):
def _migrate_course_from_lms(self): def _migrate_course_from_lms(self):
""" Create a new MigratedCourse and simulate the loading of data from LMS. """ """ Create a new MigratedCourse and simulate the loading of data from LMS. """
self._mock_lms_apis() self._mock_lms_apis()
migrated_course = MigratedCourse(self.course_id, self.site.domain) migrated_course = MigratedCourse(self.course_id, self.partner.short_code)
migrated_course.load_from_lms(ACCESS_TOKEN) migrated_course.load_from_lms(ACCESS_TOKEN)
return migrated_course return migrated_course
...@@ -196,7 +183,7 @@ class MigratedCourseTests(CourseMigrationTestMixin, TestCase): ...@@ -196,7 +183,7 @@ class MigratedCourseTests(CourseMigrationTestMixin, TestCase):
# Try migrating the course, which should fail. # Try migrating the course, which should fail.
try: try:
migrated_course = MigratedCourse(self.course_id, self.site.domain) migrated_course = MigratedCourse(self.course_id, self.partner.short_code)
migrated_course.load_from_lms(ACCESS_TOKEN) migrated_course.load_from_lms(ACCESS_TOKEN)
except Exception as ex: # pylint: disable=broad-except except Exception as ex: # pylint: disable=broad-except
self.assertEqual(ex.message, 'Aborting migration. No name is available for {}.'.format(self.course_id)) self.assertEqual(ex.message, 'Aborting migration. No name is available for {}.'.format(self.course_id))
...@@ -222,7 +209,7 @@ class MigratedCourseTests(CourseMigrationTestMixin, TestCase): ...@@ -222,7 +209,7 @@ class MigratedCourseTests(CourseMigrationTestMixin, TestCase):
content_type=JSON content_type=JSON
) )
migrated_course = MigratedCourse(self.course_id, self.site.domain) migrated_course = MigratedCourse(self.course_id, self.partner.short_code)
migrated_course.load_from_lms(ACCESS_TOKEN) migrated_course.load_from_lms(ACCESS_TOKEN)
course = migrated_course.course course = migrated_course.course
...@@ -256,7 +243,7 @@ class MigratedCourseTests(CourseMigrationTestMixin, TestCase): ...@@ -256,7 +243,7 @@ class MigratedCourseTests(CourseMigrationTestMixin, TestCase):
} }
httpretty.register_uri(httpretty.GET, self.commerce_api_url, body=json.dumps(body), content_type=JSON) httpretty.register_uri(httpretty.GET, self.commerce_api_url, body=json.dumps(body), content_type=JSON)
migrated_course = MigratedCourse(self.course_id, self.site.domain) migrated_course = MigratedCourse(self.course_id, self.partner.short_code)
migrated_course.load_from_lms(ACCESS_TOKEN) migrated_course.load_from_lms(ACCESS_TOKEN)
course = migrated_course.course course = migrated_course.course
...@@ -284,7 +271,7 @@ class CommandTests(CourseMigrationTestMixin, TestCase): ...@@ -284,7 +271,7 @@ class CommandTests(CourseMigrationTestMixin, TestCase):
with mock.patch.object(LMSPublisher, 'publish') as mock_publish: with mock.patch.object(LMSPublisher, 'publish') as mock_publish:
mock_publish.return_value = True mock_publish.return_value = True
call_command( call_command(
'migrate_course', self.course_id, access_token=ACCESS_TOKEN, site_domain=self.site.domain 'migrate_course', self.course_id, access_token=ACCESS_TOKEN, partner_short_code=self.partner.short_code
) )
# Verify that the migrated course was not published back to the LMS # Verify that the migrated course was not published back to the LMS
...@@ -309,8 +296,8 @@ class CommandTests(CourseMigrationTestMixin, TestCase): ...@@ -309,8 +296,8 @@ class CommandTests(CourseMigrationTestMixin, TestCase):
self.course_id, self.course_id,
access_token=ACCESS_TOKEN, access_token=ACCESS_TOKEN,
commit=True, commit=True,
# `site_domain` is the option destination variable # `partner_short_code` is the option destination variable
site_domain=self.site.domain partner_short_code=self.partner.short_code
) )
# Verify that the migrated course was published back to the LMS # Verify that the migrated course was published back to the LMS
...@@ -319,8 +306,8 @@ class CommandTests(CourseMigrationTestMixin, TestCase): ...@@ -319,8 +306,8 @@ class CommandTests(CourseMigrationTestMixin, TestCase):
self.assert_course_migrated() self.assert_course_migrated()
@httpretty.activate @httpretty.activate
def test_handle_with_no_site(self): def test_handle_with_no_partner(self):
""" Verify the management command does not run if no site domain is provided. """ """ Verify the management command does not run if no partner short code is provided. """
self._mock_lms_apis() self._mock_lms_apis()
with mock.patch.object(LMSPublisher, 'publish') as mock_publish: with mock.patch.object(LMSPublisher, 'publish') as mock_publish:
...@@ -332,7 +319,7 @@ class CommandTests(CourseMigrationTestMixin, TestCase): ...@@ -332,7 +319,7 @@ class CommandTests(CourseMigrationTestMixin, TestCase):
commit=True commit=True
) )
l.check((LOGGER_NAME, 'ERROR', 'Courses cannot be migrated without providing a site domain.')) l.check((LOGGER_NAME, 'ERROR', 'Courses cannot be migrated without providing a partner short code.'))
# Verify that the migrated course was published back to the LMS # Verify that the migrated course was published back to the LMS
self.assertFalse(mock_publish.called) self.assertFalse(mock_publish.called)
...@@ -347,7 +334,7 @@ class CommandTests(CourseMigrationTestMixin, TestCase): ...@@ -347,7 +334,7 @@ class CommandTests(CourseMigrationTestMixin, TestCase):
self.course_id, self.course_id,
access_token=ACCESS_TOKEN, access_token=ACCESS_TOKEN,
commit=False, commit=False,
site=self.site.domain partner=self.partner.short_code
) )
# Verify that the migrated course was published back to the LMS # Verify that the migrated course was published back to the LMS
...@@ -365,7 +352,7 @@ class CommandTests(CourseMigrationTestMixin, TestCase): ...@@ -365,7 +352,7 @@ class CommandTests(CourseMigrationTestMixin, TestCase):
self.course_id, self.course_id,
access_token=ACCESS_TOKEN, access_token=ACCESS_TOKEN,
commit=True, commit=True,
site=self.site.domain partner=self.partner.short_code
) )
# Verify that the migrated course was published back to the LMS # Verify that the migrated course was published back to the LMS
......
...@@ -6,11 +6,11 @@ from django.dispatch import receiver ...@@ -6,11 +6,11 @@ from django.dispatch import receiver
from oscar.core.loading import get_class from oscar.core.loading import get_class
import waffle import waffle
from ecommerce.core.url_utils import get_lms_url
from ecommerce.courses.utils import mode_for_seat from ecommerce.courses.utils import mode_for_seat
from ecommerce.extensions.analytics.utils import is_segment_configured, parse_tracking_context, silence_exceptions from ecommerce.extensions.analytics.utils import is_segment_configured, parse_tracking_context, silence_exceptions
from ecommerce.extensions.checkout.utils import get_provider_data from ecommerce.extensions.checkout.utils import get_provider_data
from ecommerce.notifications.notifications import send_notification from ecommerce.notifications.notifications import send_notification
from ecommerce.settings import get_lms_url
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
......
...@@ -5,10 +5,10 @@ from oscar.test import factories ...@@ -5,10 +5,10 @@ from oscar.test import factories
from oscar.test.newfactories import BasketFactory, UserFactory from oscar.test.newfactories import BasketFactory, UserFactory
from ecommerce.core.tests import toggle_switch from ecommerce.core.tests import toggle_switch
from ecommerce.core.url_utils import get_lms_url
from ecommerce.courses.models import Course from ecommerce.courses.models import Course
from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin
from ecommerce.extensions.checkout.signals import send_course_purchase_email from ecommerce.extensions.checkout.signals import send_course_purchase_email
from ecommerce.settings import get_lms_url
from ecommerce.tests.testcases import TestCase from ecommerce.tests.testcases import TestCase
......
...@@ -4,8 +4,8 @@ import mock ...@@ -4,8 +4,8 @@ import mock
import requests import requests
from requests import ConnectionError, Timeout from requests import ConnectionError, Timeout
from ecommerce.core.url_utils import get_lms_url
from ecommerce.extensions.checkout.utils import get_provider_data from ecommerce.extensions.checkout.utils import get_provider_data
from ecommerce.settings import get_lms_url
from ecommerce.tests.testcases import TestCase from ecommerce.tests.testcases import TestCase
......
...@@ -6,9 +6,9 @@ import httpretty ...@@ -6,9 +6,9 @@ import httpretty
from oscar.core.loading import get_model from oscar.core.loading import get_model
from oscar.test import newfactories as factories from oscar.test import newfactories as factories
from ecommerce.core.url_utils import get_lms_url
from ecommerce.extensions.checkout.exceptions import BasketNotFreeError from ecommerce.extensions.checkout.exceptions import BasketNotFreeError
from ecommerce.tests.testcases import TestCase from ecommerce.tests.testcases import TestCase
from ecommerce.settings import get_lms_url
Order = get_model('order', 'Order') Order = get_model('order', 'Order')
......
...@@ -3,7 +3,7 @@ import requests ...@@ -3,7 +3,7 @@ import requests
from django.conf import settings from django.conf import settings
from ecommerce.core.url_utils import get_lms_url from ecommerce.settings import get_lms_url
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
......
...@@ -12,9 +12,9 @@ from django.views.generic import RedirectView ...@@ -12,9 +12,9 @@ from django.views.generic import RedirectView
from oscar.apps.checkout.views import * # pylint: disable=wildcard-import, unused-wildcard-import from oscar.apps.checkout.views import * # pylint: disable=wildcard-import, unused-wildcard-import
from oscar.core.loading import get_class, get_model from oscar.core.loading import get_class, get_model
from ecommerce.core.url_utils import get_lms_url
from ecommerce.extensions.checkout.exceptions import BasketNotFreeError from ecommerce.extensions.checkout.exceptions import BasketNotFreeError
from ecommerce.extensions.checkout.mixins import EdxOrderPlacementMixin from ecommerce.extensions.checkout.mixins import EdxOrderPlacementMixin
from ecommerce.settings import get_lms_url
Applicator = get_class('offer.utils', 'Applicator') Applicator = get_class('offer.utils', 'Applicator')
Basket = get_model('basket', 'Basket') Basket = get_model('basket', 'Basket')
......
import json import json
from django.conf import settings
from django.contrib.messages import constants as MSG from django.contrib.messages import constants as MSG
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
import httpretty import httpretty
...@@ -8,7 +9,6 @@ from requests import Timeout ...@@ -8,7 +9,6 @@ from requests import Timeout
from testfixtures import LogCapture from testfixtures import LogCapture
from ecommerce.core.tests import toggle_switch from ecommerce.core.tests import toggle_switch
from ecommerce.core.url_utils import get_lms_enrollment_api_url
from ecommerce.extensions.dashboard.tests import DashboardViewTestMixin from ecommerce.extensions.dashboard.tests import DashboardViewTestMixin
from ecommerce.extensions.dashboard.users.views import UserDetailView from ecommerce.extensions.dashboard.users.views import UserDetailView
from ecommerce.tests.testcases import TestCase from ecommerce.tests.testcases import TestCase
...@@ -26,7 +26,7 @@ class UserDetailViewTests(DashboardViewTestMixin, TestCase): ...@@ -26,7 +26,7 @@ class UserDetailViewTests(DashboardViewTestMixin, TestCase):
def mock_enrollment_api(self, status=200): def mock_enrollment_api(self, status=200):
self.assertTrue(httpretty.is_enabled()) self.assertTrue(httpretty.is_enabled())
httpretty.register_uri(httpretty.GET, get_lms_enrollment_api_url(), status=status, httpretty.register_uri(httpretty.GET, settings.ENROLLMENT_API_URL, status=status,
body=json.dumps(self.data), body=json.dumps(self.data),
content_type='application/json') content_type='application/json')
......
...@@ -7,9 +7,6 @@ from oscar.apps.dashboard.users.views import UserDetailView as CoreUserDetailVie ...@@ -7,9 +7,6 @@ from oscar.apps.dashboard.users.views import UserDetailView as CoreUserDetailVie
import requests import requests
import waffle import waffle
from ecommerce.core.url_utils import get_lms_enrollment_api_url
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -26,7 +23,7 @@ class UserDetailView(CoreUserDetailView): ...@@ -26,7 +23,7 @@ class UserDetailView(CoreUserDetailView):
"""Retrieve the enrollments for the User being viewed.""" """Retrieve the enrollments for the User being viewed."""
username = self.object.username username = self.object.username
try: try:
url = '{}?user={}'.format(get_lms_enrollment_api_url(), username) url = '{}?user={}'.format(settings.ENROLLMENT_API_URL, username)
timeout = settings.ENROLLMENT_FULFILLMENT_TIMEOUT timeout = settings.ENROLLMENT_FULFILLMENT_TIMEOUT
headers = { headers = {
......
...@@ -11,9 +11,8 @@ from django.conf import settings ...@@ -11,9 +11,8 @@ from django.conf import settings
from rest_framework import status from rest_framework import status
import requests import requests
from requests.exceptions import ConnectionError, Timeout from requests.exceptions import ConnectionError, Timeout
from ecommerce.core.url_utils import get_lms_enrollment_api_url
from ecommerce.courses.utils import mode_for_seat from ecommerce.courses.utils import mode_for_seat
from ecommerce.extensions.analytics.utils import audit_log, parse_tracking_context from ecommerce.extensions.analytics.utils import audit_log, parse_tracking_context
from ecommerce.extensions.fulfillment.status import LINE from ecommerce.extensions.fulfillment.status import LINE
...@@ -95,7 +94,7 @@ class EnrollmentFulfillmentModule(BaseFulfillmentModule): ...@@ -95,7 +94,7 @@ class EnrollmentFulfillmentModule(BaseFulfillmentModule):
""" """
def _post_to_enrollment_api(self, data, user): def _post_to_enrollment_api(self, data, user):
enrollment_api_url = get_lms_enrollment_api_url() enrollment_api_url = settings.ENROLLMENT_API_URL
timeout = settings.ENROLLMENT_FULFILLMENT_TIMEOUT timeout = settings.ENROLLMENT_FULFILLMENT_TIMEOUT
headers = { headers = {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
...@@ -149,10 +148,11 @@ class EnrollmentFulfillmentModule(BaseFulfillmentModule): ...@@ -149,10 +148,11 @@ class EnrollmentFulfillmentModule(BaseFulfillmentModule):
""" """
logger.info("Attempting to fulfill 'Seat' product types for order [%s]", order.number) logger.info("Attempting to fulfill 'Seat' product types for order [%s]", order.number)
enrollment_api_url = getattr(settings, 'ENROLLMENT_API_URL', None)
api_key = getattr(settings, 'EDX_API_KEY', None) api_key = getattr(settings, 'EDX_API_KEY', None)
if not api_key: if not (enrollment_api_url and api_key):
logger.error( logger.error(
'EDX_API_KEY must be set to use the EnrollmentFulfillmentModule' 'ENROLLMENT_API_URL and EDX_API_KEY must be set to use the EnrollmentFulfillmentModule'
) )
for line in lines: for line in lines:
line.set_status(LINE.FULFILLMENT_CONFIGURATION_ERROR) line.set_status(LINE.FULFILLMENT_CONFIGURATION_ERROR)
......
...@@ -5,6 +5,7 @@ import ddt ...@@ -5,6 +5,7 @@ import ddt
import httpretty import httpretty
import mock import mock
from django.conf import settings
from django.test import override_settings from django.test import override_settings
from oscar.core.loading import get_class, get_model from oscar.core.loading import get_class, get_model
from oscar.test import factories from oscar.test import factories
...@@ -12,7 +13,6 @@ from oscar.test.newfactories import UserFactory, BasketFactory ...@@ -12,7 +13,6 @@ from oscar.test.newfactories import UserFactory, BasketFactory
from requests.exceptions import ConnectionError, Timeout from requests.exceptions import ConnectionError, Timeout
from testfixtures import LogCapture from testfixtures import LogCapture
from ecommerce.core.url_utils import get_lms_enrollment_api_url
from ecommerce.courses.models import Course from ecommerce.courses.models import Course
from ecommerce.courses.utils import mode_for_seat from ecommerce.courses.utils import mode_for_seat
from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin
...@@ -83,7 +83,7 @@ class EnrollmentFulfillmentModuleTests(CourseCatalogTestMixin, FulfillmentTestMi ...@@ -83,7 +83,7 @@ class EnrollmentFulfillmentModuleTests(CourseCatalogTestMixin, FulfillmentTestMi
@mock.patch('ecommerce.extensions.fulfillment.modules.parse_tracking_context') @mock.patch('ecommerce.extensions.fulfillment.modules.parse_tracking_context')
def test_enrollment_module_fulfill(self, parse_tracking_context): def test_enrollment_module_fulfill(self, parse_tracking_context):
"""Happy path test to ensure we can properly fulfill enrollments.""" """Happy path test to ensure we can properly fulfill enrollments."""
httpretty.register_uri(httpretty.POST, get_lms_enrollment_api_url(), status=200, body='{}', content_type=JSON) httpretty.register_uri(httpretty.POST, settings.ENROLLMENT_API_URL, status=200, body='{}', content_type=JSON)
parse_tracking_context.return_value = ('user_123', 'GA-123456789', '11.22.33.44') parse_tracking_context.return_value = ('user_123', 'GA-123456789', '11.22.33.44')
# Attempt to enroll. # Attempt to enroll.
with LogCapture(LOGGER_NAME) as l: with LogCapture(LOGGER_NAME) as l:
...@@ -137,7 +137,7 @@ class EnrollmentFulfillmentModuleTests(CourseCatalogTestMixin, FulfillmentTestMi ...@@ -137,7 +137,7 @@ class EnrollmentFulfillmentModuleTests(CourseCatalogTestMixin, FulfillmentTestMi
self.assertDictContainsSubset(expected_headers, actual_headers) self.assertDictContainsSubset(expected_headers, actual_headers)
self.assertEqual(expected_body, actual_body) self.assertEqual(expected_body, actual_body)
@override_settings(EDX_API_KEY=None) @override_settings(ENROLLMENT_API_URL='')
def test_enrollment_module_not_configured(self): def test_enrollment_module_not_configured(self):
"""Test that lines receive a configuration error status if fulfillment configuration is invalid.""" """Test that lines receive a configuration error status if fulfillment configuration is invalid."""
EnrollmentFulfillmentModule().fulfill_product(self.order, list(self.order.lines.all())) EnrollmentFulfillmentModule().fulfill_product(self.order, list(self.order.lines.all()))
...@@ -167,7 +167,7 @@ class EnrollmentFulfillmentModuleTests(CourseCatalogTestMixin, FulfillmentTestMi ...@@ -167,7 +167,7 @@ class EnrollmentFulfillmentModuleTests(CourseCatalogTestMixin, FulfillmentTestMi
"""Test that lines receive a server-side error status if a server-side error occurs during fulfillment.""" """Test that lines receive a server-side error status if a server-side error occurs during fulfillment."""
# NOTE: We are testing for cases where the response does and does NOT have data. The module should be able # NOTE: We are testing for cases where the response does and does NOT have data. The module should be able
# to handle both cases. # to handle both cases.
httpretty.register_uri(httpretty.POST, get_lms_enrollment_api_url(), status=500, body=body, content_type=JSON) httpretty.register_uri(httpretty.POST, settings.ENROLLMENT_API_URL, status=500, body=body, content_type=JSON)
EnrollmentFulfillmentModule().fulfill_product(self.order, list(self.order.lines.all())) EnrollmentFulfillmentModule().fulfill_product(self.order, list(self.order.lines.all()))
self.assertEqual(LINE.FULFILLMENT_SERVER_ERROR, self.order.lines.all()[0].status) self.assertEqual(LINE.FULFILLMENT_SERVER_ERROR, self.order.lines.all()[0].status)
...@@ -175,7 +175,7 @@ class EnrollmentFulfillmentModuleTests(CourseCatalogTestMixin, FulfillmentTestMi ...@@ -175,7 +175,7 @@ class EnrollmentFulfillmentModuleTests(CourseCatalogTestMixin, FulfillmentTestMi
@mock.patch('ecommerce.extensions.fulfillment.modules.parse_tracking_context') @mock.patch('ecommerce.extensions.fulfillment.modules.parse_tracking_context')
def test_revoke_product(self, parse_tracking_context): def test_revoke_product(self, parse_tracking_context):
""" The method should call the Enrollment API to un-enroll the student, and return True. """ """ The method should call the Enrollment API to un-enroll the student, and return True. """
httpretty.register_uri(httpretty.POST, get_lms_enrollment_api_url(), status=200, body='{}', content_type=JSON) httpretty.register_uri(httpretty.POST, settings.ENROLLMENT_API_URL, status=200, body='{}', content_type=JSON)
parse_tracking_context.return_value = ('user_123', 'GA-123456789', '11.22.33.44') parse_tracking_context.return_value = ('user_123', 'GA-123456789', '11.22.33.44')
line = self.order.lines.first() line = self.order.lines.first()
...@@ -227,7 +227,7 @@ class EnrollmentFulfillmentModuleTests(CourseCatalogTestMixin, FulfillmentTestMi ...@@ -227,7 +227,7 @@ class EnrollmentFulfillmentModuleTests(CourseCatalogTestMixin, FulfillmentTestMi
""" """
message = 'Enrollment mode mismatch: active mode=x, requested mode=y. Won\'t deactivate.' message = 'Enrollment mode mismatch: active mode=x, requested mode=y. Won\'t deactivate.'
body = '{{"message": "{}"}}'.format(message) body = '{{"message": "{}"}}'.format(message)
httpretty.register_uri(httpretty.POST, get_lms_enrollment_api_url(), status=400, body=body, content_type=JSON) httpretty.register_uri(httpretty.POST, settings.ENROLLMENT_API_URL, status=400, body=body, content_type=JSON)
line = self.order.lines.first() line = self.order.lines.first()
logger_name = 'ecommerce.extensions.fulfillment.modules' logger_name = 'ecommerce.extensions.fulfillment.modules'
...@@ -243,7 +243,7 @@ class EnrollmentFulfillmentModuleTests(CourseCatalogTestMixin, FulfillmentTestMi ...@@ -243,7 +243,7 @@ class EnrollmentFulfillmentModuleTests(CourseCatalogTestMixin, FulfillmentTestMi
""" If the Enrollment API responds with a non-200 status, the method should log an error and return False. """ """ If the Enrollment API responds with a non-200 status, the method should log an error and return False. """
message = 'Meh.' message = 'Meh.'
body = '{{"message": "{}"}}'.format(message) body = '{{"message": "{}"}}'.format(message)
httpretty.register_uri(httpretty.POST, get_lms_enrollment_api_url(), status=500, body=body, content_type=JSON) httpretty.register_uri(httpretty.POST, settings.ENROLLMENT_API_URL, status=500, body=body, content_type=JSON)
line = self.order.lines.first() line = self.order.lines.first()
logger_name = 'ecommerce.extensions.fulfillment.modules' logger_name = 'ecommerce.extensions.fulfillment.modules'
...@@ -263,7 +263,7 @@ class EnrollmentFulfillmentModuleTests(CourseCatalogTestMixin, FulfillmentTestMi ...@@ -263,7 +263,7 @@ class EnrollmentFulfillmentModuleTests(CourseCatalogTestMixin, FulfillmentTestMi
def request_callback(_method, _uri, _headers): def request_callback(_method, _uri, _headers):
raise Timeout raise Timeout
httpretty.register_uri(httpretty.POST, get_lms_enrollment_api_url(), body=request_callback) httpretty.register_uri(httpretty.POST, settings.ENROLLMENT_API_URL, body=request_callback)
line = self.order.lines.first() line = self.order.lines.first()
logger_name = 'ecommerce.extensions.fulfillment.modules' logger_name = 'ecommerce.extensions.fulfillment.modules'
...@@ -279,7 +279,7 @@ class EnrollmentFulfillmentModuleTests(CourseCatalogTestMixin, FulfillmentTestMi ...@@ -279,7 +279,7 @@ class EnrollmentFulfillmentModuleTests(CourseCatalogTestMixin, FulfillmentTestMi
"""Happy path test to ensure we can properly fulfill enrollments.""" """Happy path test to ensure we can properly fulfill enrollments."""
# Create the credit certificate type and order for the credit certificate type. # Create the credit certificate type and order for the credit certificate type.
self.create_seat_and_order(certificate_type='credit', provider='MIT') self.create_seat_and_order(certificate_type='credit', provider='MIT')
httpretty.register_uri(httpretty.POST, get_lms_enrollment_api_url(), status=200, body='{}', content_type=JSON) httpretty.register_uri(httpretty.POST, settings.ENROLLMENT_API_URL, status=200, body='{}', content_type=JSON)
# Attempt to enroll. # Attempt to enroll.
with LogCapture(LOGGER_NAME) as l: with LogCapture(LOGGER_NAME) as l:
......
"""Test Order Utility classes """ """Test Order Utility classes """
import mock from django.test import override_settings
from django.test.client import RequestFactory
from oscar.core.loading import get_class from oscar.core.loading import get_class
from oscar.test.factories import create_basket as oscar_create_basket from oscar.test.factories import create_basket as oscar_create_basket
from oscar.test.newfactories import BasketFactory from oscar.test.newfactories import BasketFactory
...@@ -35,11 +33,7 @@ class OrderNumberGeneratorTests(TestCase): ...@@ -35,11 +33,7 @@ class OrderNumberGeneratorTests(TestCase):
partner = site_configuration.partner partner = site_configuration.partner
basket = BasketFactory(site=None) basket = BasketFactory(site=None)
request = RequestFactory().get('') with override_settings(SITE_ID=site.id):
request.session = None
request.site = site
with mock.patch('ecommerce.extensions.order.utils.get_current_request', mock.Mock(return_value=request)):
self.assert_order_number_matches_basket(basket, partner) self.assert_order_number_matches_basket(basket, partner)
def test_order_number_from_basket_id(self): def test_order_number_from_basket_id(self):
......
...@@ -2,14 +2,11 @@ ...@@ -2,14 +2,11 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import logging import logging
from django.contrib.sites.models import Site
from oscar.apps.order.utils import OrderCreator as OscarOrderCreator from oscar.apps.order.utils import OrderCreator as OscarOrderCreator
from oscar.core.loading import get_model
from threadlocals.threadlocals import get_current_request
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
Order = get_model('order', 'Order')
class OrderNumberGenerator(object): class OrderNumberGenerator(object):
OFFSET = 100000 OFFSET = 100000
...@@ -26,7 +23,7 @@ class OrderNumberGenerator(object): ...@@ -26,7 +23,7 @@ class OrderNumberGenerator(object):
""" """
site = basket.site site = basket.site
if not site: if not site:
site = get_current_request().site site = Site.objects.get_current()
logger.warning('Basket [%d] is not associated with a Site. Defaulting to Site [%d].', basket.id, site.id) logger.warning('Basket [%d] is not associated with a Site. Defaulting to Site [%d].', basket.id, site.id)
partner = site.siteconfiguration.partner partner = site.siteconfiguration.partner
...@@ -70,33 +67,11 @@ class OrderCreator(OscarOrderCreator): ...@@ -70,33 +67,11 @@ class OrderCreator(OscarOrderCreator):
site is used. The site value can be overridden by setting the `site` kwarg. site is used. The site value can be overridden by setting the `site` kwarg.
""" """
# If a site was not passed in with extra_order_fields, # Pull the order's site from the basket, if the basket has a site and
# use the basket's site if it has one, else get the site # a site is not already being explicitly set.
# from the current request. if basket.site and 'site' not in extra_order_fields:
site = basket.site extra_order_fields['site'] = basket.site
if not site:
site = get_current_request().site return super(OrderCreator, self).create_order_model(
user, basket, shipping_address, shipping_method, shipping_charge, billing_address, total, order_number,
order_data = {'basket': basket, status, **extra_order_fields)
'number': order_number,
'site': site,
'currency': total.currency,
'total_incl_tax': total.incl_tax,
'total_excl_tax': total.excl_tax,
'shipping_incl_tax': shipping_charge.incl_tax,
'shipping_excl_tax': shipping_charge.excl_tax,
'shipping_method': shipping_method.name,
'shipping_code': shipping_method.code}
if shipping_address:
order_data['shipping_address'] = shipping_address
if billing_address:
order_data['billing_address'] = billing_address
if user and user.is_authenticated():
order_data['user_id'] = user.id
if status:
order_data['status'] = status
if extra_order_fields:
order_data.update(extra_order_fields)
order = Order(**order_data)
order.save()
return order
...@@ -13,7 +13,6 @@ from suds.client import Client ...@@ -13,7 +13,6 @@ from suds.client import Client
from suds.sudsobject import asdict from suds.sudsobject import asdict
from suds.wsse import Security, UsernameToken from suds.wsse import Security, UsernameToken
from ecommerce.core.url_utils import get_lms_url
from ecommerce.core.constants import ISO_8601_FORMAT from ecommerce.core.constants import ISO_8601_FORMAT
from ecommerce.extensions.order.constants import PaymentEventTypeName from ecommerce.extensions.order.constants import PaymentEventTypeName
from ecommerce.extensions.payment.constants import CYBERSOURCE_CARD_TYPE_MAP from ecommerce.extensions.payment.constants import CYBERSOURCE_CARD_TYPE_MAP
...@@ -59,16 +58,10 @@ class Cybersource(BasePaymentProcessor): ...@@ -59,16 +58,10 @@ class Cybersource(BasePaymentProcessor):
self.access_key = configuration['access_key'] self.access_key = configuration['access_key']
self.secret_key = configuration['secret_key'] self.secret_key = configuration['secret_key']
self.payment_page_url = configuration['payment_page_url'] self.payment_page_url = configuration['payment_page_url']
self.receipt_page_url = configuration['receipt_page_url']
self.cancel_page_url = configuration['cancel_page_url']
self.language_code = settings.LANGUAGE_CODE self.language_code = settings.LANGUAGE_CODE
@property
def receipt_page_url(self):
return get_lms_url(self.configuration['receipt_path'])
@property
def cancel_page_url(self):
return get_lms_url(self.configuration['cancel_path'])
def get_transaction_parameters(self, basket, request=None): def get_transaction_parameters(self, basket, request=None):
""" """
Generate a dictionary of signed parameters CyberSource requires to complete a transaction. Generate a dictionary of signed parameters CyberSource requires to complete a transaction.
......
...@@ -3,13 +3,13 @@ from decimal import Decimal ...@@ -3,13 +3,13 @@ from decimal import Decimal
import logging import logging
from urlparse import urljoin from urlparse import urljoin
from django.conf import settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from oscar.apps.payment.exceptions import GatewayError from oscar.apps.payment.exceptions import GatewayError
from oscar.core.loading import get_model from oscar.core.loading import get_model
import paypalrestsdk import paypalrestsdk
import waffle import waffle
from ecommerce.core.url_utils import get_ecommerce_url, get_lms_url
from ecommerce.extensions.order.constants import PaymentEventTypeName from ecommerce.extensions.order.constants import PaymentEventTypeName
from ecommerce.extensions.payment.processors import BasePaymentProcessor from ecommerce.extensions.payment.processors import BasePaymentProcessor
from ecommerce.extensions.payment.models import PaypalWebProfile from ecommerce.extensions.payment.models import PaypalWebProfile
...@@ -42,21 +42,17 @@ class Paypal(BasePaymentProcessor): ...@@ -42,21 +42,17 @@ class Paypal(BasePaymentProcessor):
Raises: Raises:
KeyError: If a required setting is not configured for this payment processor KeyError: If a required setting is not configured for this payment processor
AttributeError: If ECOMMERCE_URL_ROOT setting is not set.
""" """
# Number of times payment execution is retried after failure. configuration = self.configuration
self.retry_attempts = self.configuration.get('retry_attempts', 1) self.receipt_url = configuration['receipt_url']
self.cancel_url = configuration['cancel_url']
@property self.error_url = configuration['error_url']
def receipt_url(self):
return get_lms_url(self.configuration['receipt_path'])
@property # Number of times payment execution is retried after failure.
def cancel_url(self): self.retry_attempts = configuration.get('retry_attempts', 1)
return get_lms_url(self.configuration['cancel_path'])
@property self.ecommerce_url_root = settings.ECOMMERCE_URL_ROOT
def error_url(self):
return get_lms_url(self.configuration['error_path'])
def get_transaction_parameters(self, basket, request=None): def get_transaction_parameters(self, basket, request=None):
""" """
...@@ -76,7 +72,7 @@ class Paypal(BasePaymentProcessor): ...@@ -76,7 +72,7 @@ class Paypal(BasePaymentProcessor):
GatewayError: Indicates a general error or unexpected behavior on the part of PayPal which prevented GatewayError: Indicates a general error or unexpected behavior on the part of PayPal which prevented
a payment from being created. a payment from being created.
""" """
return_url = urljoin(get_ecommerce_url(), reverse('paypal_execute')) return_url = urljoin(self.ecommerce_url_root, reverse('paypal_execute'))
data = { data = {
'intent': 'sale', 'intent': 'sale',
'redirect_urls': { 'redirect_urls': {
......
...@@ -21,7 +21,6 @@ from paypalrestsdk import Payment, Sale ...@@ -21,7 +21,6 @@ from paypalrestsdk import Payment, Sale
from paypalrestsdk.resource import Resource from paypalrestsdk.resource import Resource
from testfixtures import LogCapture from testfixtures import LogCapture
from ecommerce.core.url_utils import get_ecommerce_url
from ecommerce.core.constants import ISO_8601_FORMAT from ecommerce.core.constants import ISO_8601_FORMAT
from ecommerce.core.tests import toggle_switch from ecommerce.core.tests import toggle_switch
from ecommerce.core.tests.patched_httpretty import httpretty from ecommerce.core.tests.patched_httpretty import httpretty
...@@ -394,7 +393,7 @@ class PaypalTests(PaypalMixin, PaymentProcessorTestCaseMixin, TestCase): ...@@ -394,7 +393,7 @@ class PaypalTests(PaypalMixin, PaymentProcessorTestCaseMixin, TestCase):
self.assert_processor_response_recorded(self.processor.NAME, self.PAYMENT_ID, response, basket=self.basket) self.assert_processor_response_recorded(self.processor.NAME, self.PAYMENT_ID, response, basket=self.basket)
last_request_body = json.loads(httpretty.last_request().body) last_request_body = json.loads(httpretty.last_request().body)
expected = urljoin(get_ecommerce_url(), reverse('paypal_execute')) expected = urljoin(settings.ECOMMERCE_URL_ROOT, reverse('paypal_execute'))
self.assertEqual(last_request_body['redirect_urls']['return_url'], expected) self.assertEqual(last_request_body['redirect_urls']['return_url'], expected)
@httpretty.activate @httpretty.activate
......
...@@ -7,7 +7,6 @@ from oscar.core.loading import get_model, get_class ...@@ -7,7 +7,6 @@ from oscar.core.loading import get_model, get_class
from oscar.test.newfactories import UserFactory from oscar.test.newfactories import UserFactory
from testfixtures import LogCapture from testfixtures import LogCapture
from ecommerce.core.url_utils import get_lms_enrollment_api_url
from ecommerce.extensions.refund import models from ecommerce.extensions.refund import models
from ecommerce.extensions.refund.exceptions import InvalidStatus from ecommerce.extensions.refund.exceptions import InvalidStatus
from ecommerce.extensions.refund.status import REFUND, REFUND_LINE from ecommerce.extensions.refund.status import REFUND, REFUND_LINE
...@@ -130,7 +129,7 @@ class RefundTests(RefundTestMixin, StatusTestsMixin, TestCase): ...@@ -130,7 +129,7 @@ class RefundTests(RefundTestMixin, StatusTestsMixin, TestCase):
""" """
httpretty.register_uri( httpretty.register_uri(
httpretty.POST, httpretty.POST,
get_lms_enrollment_api_url(), settings.ENROLLMENT_API_URL,
status=200, status=200,
body='{}', body='{}',
content_type='application/json' content_type='application/json'
......
import datetime
from django.conf import settings
from django.db import IntegrityError from django.db import IntegrityError
from django.test import override_settings from django.test import override_settings
from oscar.core.loading import get_model
from oscar.templatetags.currency_filters import currency from oscar.templatetags.currency_filters import currency
from oscar.test.factories import * # pylint:disable=wildcard-import,unused-wildcard-import from oscar.test.factories import * # pylint:disable=wildcard-import,unused-wildcard-import
from ecommerce.core.url_utils import get_ecommerce_url
from ecommerce.courses.tests.factories import CourseFactory from ecommerce.courses.tests.factories import CourseFactory
from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin from ecommerce.extensions.catalogue.tests.mixins import CourseCatalogTestMixin
from ecommerce.extensions.voucher.utils import create_vouchers, generate_coupon_report, get_voucher_discount_info from ecommerce.extensions.voucher.utils import create_vouchers, generate_coupon_report, get_voucher_discount_info
...@@ -229,7 +232,7 @@ class UtilTests(CouponMixin, CourseCatalogTestMixin, TestCase): ...@@ -229,7 +232,7 @@ class UtilTests(CouponMixin, CourseCatalogTestMixin, TestCase):
enrollment_code_row = rows[-2] enrollment_code_row = rows[-2]
self.assertEqual(enrollment_code_row['Name'], 'Discount code') self.assertEqual(enrollment_code_row['Name'], 'Discount code')
self.assertEqual(enrollment_code_row['Code'], VOUCHER_CODE) self.assertEqual(enrollment_code_row['Code'], VOUCHER_CODE)
self.assertEqual(enrollment_code_row['URL'], get_ecommerce_url() + REDEMPTION_URL.format(VOUCHER_CODE)) self.assertEqual(enrollment_code_row['URL'], settings.ECOMMERCE_URL_ROOT + REDEMPTION_URL.format(VOUCHER_CODE))
self.assertEqual(enrollment_code_row['CourseID'], self.course.id) self.assertEqual(enrollment_code_row['CourseID'], self.course.id)
self.assertEqual(enrollment_code_row['Price'], currency(100.00)) self.assertEqual(enrollment_code_row['Price'], currency(100.00))
self.assertEqual(enrollment_code_row['Invoiced Amount'], currency(100.00)) self.assertEqual(enrollment_code_row['Invoiced Amount'], currency(100.00))
...@@ -245,7 +248,7 @@ class UtilTests(CouponMixin, CourseCatalogTestMixin, TestCase): ...@@ -245,7 +248,7 @@ class UtilTests(CouponMixin, CourseCatalogTestMixin, TestCase):
self.assertEqual(enrollment_code_row['Discount'], '$100.00') self.assertEqual(enrollment_code_row['Discount'], '$100.00')
self.assertEqual( self.assertEqual(
enrollment_code_row['URL'], enrollment_code_row['URL'],
get_ecommerce_url() + REDEMPTION_URL.format(enrollment_code_row['Code']) settings.ECOMMERCE_URL_ROOT + REDEMPTION_URL.format(enrollment_code_row['Code'])
) )
def test_get_voucher_discount_info(self): def test_get_voucher_discount_info(self):
......
...@@ -11,8 +11,6 @@ from django.utils.translation import ugettext_lazy as _ ...@@ -11,8 +11,6 @@ from django.utils.translation import ugettext_lazy as _
from oscar.core.loading import get_model from oscar.core.loading import get_model
from oscar.templatetags.currency_filters import currency from oscar.templatetags.currency_filters import currency
from ecommerce.core.url_utils import get_ecommerce_url
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
Benefit = get_model('offer', 'Benefit') Benefit = get_model('offer', 'Benefit')
...@@ -79,7 +77,7 @@ def generate_coupon_report(coupon_vouchers): ...@@ -79,7 +77,7 @@ def generate_coupon_report(coupon_vouchers):
discount = _("{percentage} %").format(percentage=benefit_value) discount = _("{percentage} %").format(percentage=benefit_value)
else: else:
discount = currency(benefit_value) discount = currency(benefit_value)
URL = '{url}?code={code}'.format(url=get_ecommerce_url('/coupons/redeem/'), code=voucher.code) URL = '{}/coupons/redeem/?code={}'.format(settings.ECOMMERCE_URL_ROOT, voucher.code)
author = history.history_user.full_name author = history.history_user.full_name
rows.append({ rows.append({
......
from urlparse import urljoin
def get_lms_url(path):
from django.conf import settings
return urljoin(settings.LMS_URL_ROOT, path)
...@@ -102,27 +102,23 @@ PAYMENT_PROCESSORS = ( ...@@ -102,27 +102,23 @@ PAYMENT_PROCESSORS = (
'ecommerce.extensions.payment.processors.paypal.Paypal', 'ecommerce.extensions.payment.processors.paypal.Paypal',
) )
PAYMENT_PROCESSOR_RECEIPT_PATH = '/commerce/checkout/receipt/'
PAYMENT_PROCESSOR_CANCEL_PATH = '/commerce/checkout/cancel/'
PAYMENT_PROCESSOR_ERROR_PATH = '/commerce/checkout/error/'
PAYMENT_PROCESSOR_CONFIG = { PAYMENT_PROCESSOR_CONFIG = {
'cybersource': { 'cybersource': {
'profile_id': None, 'profile_id': None,
'access_key': None, 'access_key': None,
'secret_key': None, 'secret_key': None,
'payment_page_url': None, 'payment_page_url': None,
'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH, 'receipt_page_url': None,
'cancel_path': PAYMENT_PROCESSOR_CANCEL_PATH, 'cancel_page_url': None,
}, },
'paypal': { 'paypal': {
# 'mode' can be either 'sandbox' or 'live' # 'mode' can be either 'sandbox' or 'live'
'mode': None, 'mode': None,
'client_id': None, 'client_id': None,
'client_secret': None, 'client_secret': None,
'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH, 'receipt_url': None,
'cancel_path': PAYMENT_PROCESSOR_CANCEL_PATH, 'cancel_url': None,
'error_path': PAYMENT_PROCESSOR_ERROR_PATH, 'error_url': None,
}, },
} }
......
...@@ -197,7 +197,6 @@ MIDDLEWARE_CLASSES = ( ...@@ -197,7 +197,6 @@ MIDDLEWARE_CLASSES = (
'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware', 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
'social.apps.django_app.middleware.SocialAuthExceptionMiddleware', 'social.apps.django_app.middleware.SocialAuthExceptionMiddleware',
'simple_history.middleware.HistoryRequestMiddleware', 'simple_history.middleware.HistoryRequestMiddleware',
'threadlocals.middleware.ThreadLocalMiddleware',
) )
# END MIDDLEWARE CONFIGURATION # END MIDDLEWARE CONFIGURATION
...@@ -206,8 +205,24 @@ MIDDLEWARE_CLASSES = ( ...@@ -206,8 +205,24 @@ MIDDLEWARE_CLASSES = (
# See: https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf # See: https://docs.djangoproject.com/en/dev/ref/settings/#root-urlconf
ROOT_URLCONF = '{}.urls'.format(SITE_NAME) ROOT_URLCONF = '{}.urls'.format(SITE_NAME)
# Absolute URL used to construct URLs pointing back to the ecommerce service.
ECOMMERCE_URL_ROOT = None
# Absolute URL used to construct LMS URLs.
LMS_URL_ROOT = None
# The location of the LMS heartbeat page
LMS_HEARTBEAT_URL = None
# The location of the LMS student dashboard
LMS_DASHBOARD_URL = None
# URL to which enrollment requests should be made
ENROLLMENT_API_URL = None
# Commerce API settings used for publishing information to LMS. # Commerce API settings used for publishing information to LMS.
COMMERCE_API_TIMEOUT = 7 COMMERCE_API_TIMEOUT = 7
COMMERCE_API_URL = None
# Cache course info from course API. # Cache course info from course API.
COURSES_API_CACHE_TIMEOUT = 3600 # Value is in seconds COURSES_API_CACHE_TIMEOUT = 3600 # Value is in seconds
...@@ -215,6 +230,9 @@ COURSES_API_CACHE_TIMEOUT = 3600 # Value is in seconds ...@@ -215,6 +230,9 @@ COURSES_API_CACHE_TIMEOUT = 3600 # Value is in seconds
# PROVIDER DATA PROCESSING # PROVIDER DATA PROCESSING
PROVIDER_DATA_PROCESSING_TIMEOUT = 15 # Value is in seconds. PROVIDER_DATA_PROCESSING_TIMEOUT = 15 # Value is in seconds.
CREDIT_PROVIDER_CACHE_TIMEOUT = 600 CREDIT_PROVIDER_CACHE_TIMEOUT = 600
# OAuth2 provider URL used for OAuth2 transactions (e.g. validating access tokens)
OAUTH2_PROVIDER_URL = None
# END URL CONFIGURATION # END URL CONFIGURATION
...@@ -324,8 +342,6 @@ INSTALLED_APPS += ['social.apps.django_app.default'] ...@@ -324,8 +342,6 @@ INSTALLED_APPS += ['social.apps.django_app.default']
AUTHENTICATION_BACKENDS = ('auth_backends.backends.EdXOpenIdConnect',) + AUTHENTICATION_BACKENDS AUTHENTICATION_BACKENDS = ('auth_backends.backends.EdXOpenIdConnect',) + AUTHENTICATION_BACKENDS
SOCIAL_AUTH_STRATEGY = 'ecommerce.social_auth.strategies.CurrentSiteDjangoStrategy'
# Set to true if using SSL and running behind a proxy # Set to true if using SSL and running behind a proxy
SOCIAL_AUTH_REDIRECT_IS_HTTPS = False SOCIAL_AUTH_REDIRECT_IS_HTTPS = False
......
"""Devstack settings""" """Devstack settings"""
from os import environ from os import environ
from django.conf import settings
import yaml import yaml
from ecommerce.settings import get_lms_url
from ecommerce.settings.base import * from ecommerce.settings.base import *
from ecommerce.settings.logger import get_logger_config from ecommerce.settings.logger import get_logger_config
...@@ -29,16 +31,16 @@ PAYMENT_PROCESSOR_CONFIG = { ...@@ -29,16 +31,16 @@ PAYMENT_PROCESSOR_CONFIG = {
'access_key': 'fake-access-key', 'access_key': 'fake-access-key',
'secret_key': 'fake-secret-key', 'secret_key': 'fake-secret-key',
'payment_page_url': 'https://testsecureacceptance.cybersource.com/pay', 'payment_page_url': 'https://testsecureacceptance.cybersource.com/pay',
'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH, 'receipt_page_url': get_lms_url(settings.RECEIPT_PAGE_PATH),
'cancel_path': PAYMENT_PROCESSOR_CANCEL_PATH, 'cancel_page_url': get_lms_url('/commerce/checkout/cancel/'),
}, },
'paypal': { 'paypal': {
'mode': 'sandbox', 'mode': 'sandbox',
'client_id': 'fake-client-id', 'client_id': 'fake-client-id',
'client_secret': 'fake-client-secret', 'client_secret': 'fake-client-secret',
'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH, 'receipt_url': get_lms_url(settings.RECEIPT_PAGE_PATH),
'cancel_path': PAYMENT_PROCESSOR_CANCEL_PATH, 'cancel_url': get_lms_url('/commerce/checkout/cancel/'),
'error_path': PAYMENT_PROCESSOR_ERROR_PATH, 'error_url': get_lms_url('/commerce/checkout/error/'),
}, },
} }
# END PAYMENT PROCESSING # END PAYMENT PROCESSING
......
"""Development settings and globals.""" """Development settings and globals."""
from __future__ import absolute_import from __future__ import absolute_import
import os
from os.path import join, normpath
from django.conf import settings
from ecommerce.settings import get_lms_url
from ecommerce.settings.base import * from ecommerce.settings.base import *
from ecommerce.settings.logger import get_logger_config from ecommerce.settings.logger import get_logger_config
...@@ -60,11 +66,34 @@ INTERNAL_IPS = ('127.0.0.1',) ...@@ -60,11 +66,34 @@ INTERNAL_IPS = ('127.0.0.1',)
# END TOOLBAR CONFIGURATION # END TOOLBAR CONFIGURATION
# URL CONFIGURATION
ECOMMERCE_URL_ROOT = 'http://localhost:8002'
LMS_URL_ROOT = 'http://127.0.0.1:8000'
# The location of the LMS heartbeat page
LMS_HEARTBEAT_URL = get_lms_url('/heartbeat')
# The location of the LMS student dashboard
LMS_DASHBOARD_URL = get_lms_url('/dashboard')
OAUTH2_PROVIDER_URL = get_lms_url('/oauth2')
COMMERCE_API_URL = get_lms_url('/api/commerce/v1/')
# END URL CONFIGURATION
# AUTHENTICATION # AUTHENTICATION
# Set these to the correct values for your OAuth2/OpenID Connect provider (e.g., devstack)
SOCIAL_AUTH_EDX_OIDC_KEY = 'replace-me'
SOCIAL_AUTH_EDX_OIDC_SECRET = 'replace-me'
SOCIAL_AUTH_EDX_OIDC_URL_ROOT = OAUTH2_PROVIDER_URL
SOCIAL_AUTH_EDX_OIDC_ID_TOKEN_DECRYPTION_KEY = SOCIAL_AUTH_EDX_OIDC_SECRET
JWT_AUTH.update({ JWT_AUTH.update({
'JWT_SECRET_KEY': 'insecure-secret-key', 'JWT_SECRET_KEY': 'insecure-secret-key',
'JWT_ISSUERS': ( 'JWT_ISSUERS': (
'127.0.0.1:8000/oauth2', OAUTH2_PROVIDER_URL,
# Must match the value of JWT_ISSUER configured for the ecommerce worker. # Must match the value of JWT_ISSUER configured for the ecommerce worker.
'ecommerce_worker', 'ecommerce_worker',
), ),
...@@ -73,6 +102,7 @@ JWT_AUTH.update({ ...@@ -73,6 +102,7 @@ JWT_AUTH.update({
# ORDER PROCESSING # ORDER PROCESSING
ENROLLMENT_API_URL = get_lms_url('/api/enrollment/v1/enrollment')
ENROLLMENT_FULFILLMENT_TIMEOUT = 15 # devstack is slow! ENROLLMENT_FULFILLMENT_TIMEOUT = 15 # devstack is slow!
EDX_API_KEY = 'replace-me' EDX_API_KEY = 'replace-me'
...@@ -89,16 +119,16 @@ PAYMENT_PROCESSOR_CONFIG = { ...@@ -89,16 +119,16 @@ PAYMENT_PROCESSOR_CONFIG = {
'access_key': 'fake-access-key', 'access_key': 'fake-access-key',
'secret_key': 'fake-secret-key', 'secret_key': 'fake-secret-key',
'payment_page_url': 'https://testsecureacceptance.cybersource.com/pay', 'payment_page_url': 'https://testsecureacceptance.cybersource.com/pay',
'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH, 'receipt_page_url': get_lms_url(settings.RECEIPT_PAGE_PATH),
'cancel_path': PAYMENT_PROCESSOR_CANCEL_PATH, 'cancel_page_url': get_lms_url('/commerce/checkout/cancel/'),
}, },
'paypal': { 'paypal': {
'mode': 'sandbox', 'mode': 'sandbox',
'client_id': 'fake-client-id', 'client_id': 'fake-client-id',
'client_secret': 'fake-client-secret', 'client_secret': 'fake-client-secret',
'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH, 'receipt_url': get_lms_url(settings.RECEIPT_PAGE_PATH),
'cancel_path': PAYMENT_PROCESSOR_CANCEL_PATH, 'cancel_url': get_lms_url('/commerce/checkout/cancel/'),
'error_path': PAYMENT_PROCESSOR_ERROR_PATH, 'error_url': get_lms_url('/commerce/checkout/error/'),
}, },
} }
# END PAYMENT PROCESSING # END PAYMENT PROCESSING
......
...@@ -11,10 +11,6 @@ from ecommerce.settings.base import * ...@@ -11,10 +11,6 @@ from ecommerce.settings.base import *
from ecommerce.settings.logger import get_logger_config from ecommerce.settings.logger import get_logger_config
# See: https://docs.djangoproject.com/en/dev/ref/settings/#site-id
# This needs to be set to None in order to support multitenancy
SITE_ID = None
# Enable offline compression of CSS/JS # Enable offline compression of CSS/JS
COMPRESS_ENABLED = True COMPRESS_ENABLED = True
COMPRESS_OFFLINE = True COMPRESS_OFFLINE = True
...@@ -62,13 +58,3 @@ DB_OVERRIDES = dict( ...@@ -62,13 +58,3 @@ DB_OVERRIDES = dict(
for override, value in DB_OVERRIDES.iteritems(): for override, value in DB_OVERRIDES.iteritems():
DATABASES['default'][override] = value DATABASES['default'][override] = value
# PAYMENT PROCESSOR OVERRIDES
for __, config in PAYMENT_PROCESSOR_CONFIG.iteritems():
config.update({
'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH,
'cancel_path': PAYMENT_PROCESSOR_CANCEL_PATH,
'error_path': PAYMENT_PROCESSOR_ERROR_PATH,
})
# END PAYMENT PROCESSOR OVERRIDES
from __future__ import absolute_import from __future__ import absolute_import
from django.conf import settings
from ecommerce.settings import get_lms_url
from ecommerce.settings.base import * from ecommerce.settings.base import *
from ecommerce.settings.logger import get_logger_config from ecommerce.settings.logger import get_logger_config
SITE_ID = 1
# TEST SETTINGS # TEST SETTINGS
INSTALLED_APPS += ( INSTALLED_APPS += (
'django_nose', 'django_nose',
...@@ -45,6 +46,21 @@ DATABASES = { ...@@ -45,6 +46,21 @@ DATABASES = {
# END IN-MEMORY TEST DATABASE # END IN-MEMORY TEST DATABASE
# URL CONFIGURATION
ECOMMERCE_URL_ROOT = 'http://localhost:8002'
LMS_URL_ROOT = 'http://127.0.0.1:8000'
# The location of the LMS heartbeat page
LMS_HEARTBEAT_URL = get_lms_url('/heartbeat')
# The location of the LMS student dashboard
LMS_DASHBOARD_URL = get_lms_url('/dashboard')
COMMERCE_API_URL = get_lms_url('/api/commerce/v1/')
# END URL CONFIGURATION
# AUTHENTICATION # AUTHENTICATION
ENABLE_AUTO_AUTH = True ENABLE_AUTO_AUTH = True
...@@ -60,6 +76,8 @@ PASSWORD_HASHERS = ( ...@@ -60,6 +76,8 @@ PASSWORD_HASHERS = (
# ORDER PROCESSING # ORDER PROCESSING
ENROLLMENT_API_URL = LMS_URL_ROOT + '/api/enrollment/v1/enrollment'
EDX_API_KEY = 'replace-me' EDX_API_KEY = 'replace-me'
# END ORDER PROCESSING # END ORDER PROCESSING
...@@ -74,16 +92,16 @@ PAYMENT_PROCESSOR_CONFIG = { ...@@ -74,16 +92,16 @@ PAYMENT_PROCESSOR_CONFIG = {
'access_key': 'fake-access-key', 'access_key': 'fake-access-key',
'secret_key': 'fake-secret-key', 'secret_key': 'fake-secret-key',
'payment_page_url': 'https://replace-me/', 'payment_page_url': 'https://replace-me/',
'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH, 'receipt_page_url': get_lms_url(settings.RECEIPT_PAGE_PATH),
'cancel_path': PAYMENT_PROCESSOR_CANCEL_PATH, 'cancel_page_url': get_lms_url('/commerce/checkout/cancel/'),
}, },
'paypal': { 'paypal': {
'mode': 'sandbox', 'mode': 'sandbox',
'client_id': 'fake-client-id', 'client_id': 'fake-client-id',
'client_secret': 'fake-client-secret', 'client_secret': 'fake-client-secret',
'receipt_path': PAYMENT_PROCESSOR_RECEIPT_PATH, 'receipt_url': get_lms_url(settings.RECEIPT_PAGE_PATH),
'cancel_path': PAYMENT_PROCESSOR_CANCEL_PATH, 'cancel_url': get_lms_url('/commerce/checkout/cancel/'),
'error_path': PAYMENT_PROCESSOR_ERROR_PATH, 'error_url': get_lms_url('/commerce/checkout/error/'),
}, },
} }
# END PAYMENT PROCESSING # END PAYMENT PROCESSING
......
from django.conf import settings
from social.strategies.django_strategy import DjangoStrategy
class CurrentSiteDjangoStrategy(DjangoStrategy):
"""
Python Social Auth strategy which accounts for the current
Site when enabling third party authentication.
"""
def get_setting(self, name):
# First check the current SiteConfiguration for the setting
setting = self.request.site.siteconfiguration.oauth_settings.get(name)
if not setting:
# Then check Django settings for the setting
setting = getattr(settings, name)
return setting
"""Tests of social auth strategies."""
from django.conf import settings
from threadlocals.threadlocals import get_current_request
from ecommerce.social_auth.strategies import CurrentSiteDjangoStrategy
from ecommerce.tests.testcases import TestCase
class CurrentSiteDjangoStrategyTests(TestCase):
"""Tests of the CurrentSiteDjangoStrategy."""
def setUp(self):
super(CurrentSiteDjangoStrategyTests, self).setUp()
self.site.siteconfiguration.oauth_settings = {
'SOCIAL_AUTH_EDX_OIDC_KEY': 'test-key'
}
self.strategy = CurrentSiteDjangoStrategy(None, get_current_request())
def test_get_setting_from_siteconfiguration(self):
"""Test that a setting can be retrieved from the site configuration."""
setting_name = 'SOCIAL_AUTH_EDX_OIDC_KEY'
self.assertEqual(
self.strategy.get_setting(setting_name),
self.site.siteconfiguration.oauth_settings.get(setting_name)
)
def test_get_setting_from_django_settings(self):
"""Test that a setting can be retrieved from django settings if it doesn't exist in site configuration."""
setting_name = 'SOCIAL_AUTH_EDX_OIDC_SECRET'
self.assertEqual(
self.strategy.get_setting(setting_name),
getattr(settings, setting_name)
)
def test_get_setting_raises_exception_on_missing_setting(self):
"""Test that a setting that does not exist raises exception."""
with self.assertRaises(AttributeError):
self.strategy.get_setting('FAKE_SETTING')
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
{% if user.is_authenticated %} {% if user.is_authenticated %}
<li class="btn-group user-menu"> <li class="btn-group user-menu">
<button type="button" class="btn btn-default hidden-xs main-btn nav-button" <button type="button" class="btn btn-default hidden-xs main-btn nav-button"
onclick="window.open('{{ lms_dashboard_url }}');"> onclick="window.open('{% settings_value 'LMS_DASHBOARD_URL' %}');">
<i class="icon fa fa-home" aria-hidden="true"></i> <i class="icon fa fa-home" aria-hidden="true"></i>
<span class="sr-only">{% trans "Dashboard for:" %}</span> <span class="sr-only">{% trans "Dashboard for:" %}</span>
{{ user.username }} {{ user.username }}
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
{% if user.is_authenticated %} {% if user.is_authenticated %}
<li class="btn-group user-menu"> <li class="btn-group user-menu">
<button type="button" class="btn btn-default hidden-xs main-btn nav-button" <button type="button" class="btn btn-default hidden-xs main-btn nav-button"
onclick="window.open('{{ lms_dashboard_url }}');"> onclick="window.open('{% settings_value 'LMS_DASHBOARD_URL' %}');">
<i class="icon fa fa-home" aria-hidden="true"></i> <i class="icon fa fa-home" aria-hidden="true"></i>
<span class="sr-only">{% trans "Dashboard for:" %}</span> <span class="sr-only">{% trans "Dashboard for:" %}</span>
{{ user.username }} {{ user.username }}
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
{% block additional_options %} {% block additional_options %}
<li class="{{ additional_class }}"> <li class="{{ additional_class }}">
<a class="nav-link" href="{{ lms_dashboard_url }}">{% trans "Student Dashboard" %}</a> <a class="nav-link" href="{% settings_value 'LMS_DASHBOARD_URL' %}">{% trans "Student Dashboard" %}</a>
</li> </li>
<li class="{{ additional_class }}"> <li class="{{ additional_class }}">
<a class="nav-link" href="{% url 'courses:app' '' %}">{% trans "Course Admin Tool" %}</a> <a class="nav-link" href="{% url 'courses:app' '' %}">{% trans "Course Admin Tool" %}</a>
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
{% if user.is_authenticated %} {% if user.is_authenticated %}
<li class="btn-group user-menu"> <li class="btn-group user-menu">
<button type="button" class="btn btn-default hidden-xs main-btn nav-button" <button type="button" class="btn btn-default hidden-xs main-btn nav-button"
onclick="window.open('{{ lms_dashboard_url }}');"> onclick="window.open('{% settings_value 'LMS_DASHBOARD_URL' %}');">
<i class="icon fa fa-home" aria-hidden="true"></i> <i class="icon fa fa-home" aria-hidden="true"></i>
<span class="sr-only">{% trans "Dashboard for:" %}</span> <span class="sr-only">{% trans "Dashboard for:" %}</span>
{{ user.username }} {{ user.username }}
......
...@@ -23,7 +23,6 @@ class SiteConfigurationFactory(factory.DjangoModelFactory): ...@@ -23,7 +23,6 @@ class SiteConfigurationFactory(factory.DjangoModelFactory):
class Meta(object): class Meta(object):
model = SiteConfiguration model = SiteConfiguration
lms_url_root = factory.LazyAttribute(lambda obj: "http://lms.testserver.fake")
site = factory.SubFactory(SiteFactory) site = factory.SubFactory(SiteFactory)
partner = factory.SubFactory(PartnerFactory) partner = factory.SubFactory(PartnerFactory)
......
...@@ -9,20 +9,18 @@ from django.contrib.auth import get_user_model ...@@ -9,20 +9,18 @@ from django.contrib.auth import get_user_model
from django.contrib.sites.models import Site from django.contrib.sites.models import Site
from django.core.cache import cache from django.core.cache import cache
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.test.client import RequestFactory
import httpretty import httpretty
import jwt import jwt
from mock import patch from mock import patch
from oscar.core.loading import get_class, get_model from oscar.core.loading import get_class, get_model
from oscar.test import factories from oscar.test import factories
from threadlocals.threadlocals import set_thread_variable
from social.apps.django_app.default.models import UserSocialAuth from social.apps.django_app.default.models import UserSocialAuth
from ecommerce.core.url_utils import get_lms_url
from ecommerce.courses.utils import mode_for_seat from ecommerce.courses.utils import mode_for_seat
from ecommerce.extensions.api.constants import APIConstants as AC from ecommerce.extensions.api.constants import APIConstants as AC
from ecommerce.extensions.api.v2.views.coupons import CouponViewSet from ecommerce.extensions.api.v2.views.coupons import CouponViewSet
from ecommerce.extensions.fulfillment.signals import SHIPPING_EVENT_NAME from ecommerce.extensions.fulfillment.signals import SHIPPING_EVENT_NAME
from ecommerce.settings import get_lms_url
from ecommerce.tests.factories import PartnerFactory, SiteConfigurationFactory from ecommerce.tests.factories import PartnerFactory, SiteConfigurationFactory
Basket = get_model('basket', 'Basket') Basket = get_model('basket', 'Basket')
...@@ -258,11 +256,6 @@ class SiteMixin(object): ...@@ -258,11 +256,6 @@ class SiteMixin(object):
self.partner = site_configuration.partner self.partner = site_configuration.partner
self.site = site_configuration.site self.site = site_configuration.site
request = RequestFactory().get('')
request.session = None
request.site = self.site
set_thread_variable('request', request)
class TestServerUrlMixin(object): class TestServerUrlMixin(object):
def get_full_url(self, path, site=None): def get_full_url(self, path, site=None):
......
from django.conf import settings
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from ecommerce.core.url_utils import get_lms_dashboard_url
from ecommerce.tests.testcases import TestCase from ecommerce.tests.testcases import TestCase
...@@ -16,4 +16,4 @@ class TestUrls(TestCase): ...@@ -16,4 +16,4 @@ class TestUrls(TestCase):
response = self.client.get(reverse('dashboard:index')) response = self.client.get(reverse('dashboard:index'))
# Test client can't fetch external URLs, so fetch_redirect_response is set to # Test client can't fetch external URLs, so fetch_redirect_response is set to
# False to avoid loading the final page # False to avoid loading the final page
self.assertRedirects(response, get_lms_dashboard_url(), fetch_redirect_response=False) self.assertRedirects(response, settings.LMS_DASHBOARD_URL, fetch_redirect_response=False)
...@@ -10,7 +10,6 @@ from django.shortcuts import redirect ...@@ -10,7 +10,6 @@ from django.shortcuts import redirect
from django.views.generic import RedirectView, TemplateView from django.views.generic import RedirectView, TemplateView
from ecommerce.core import views as core_views from ecommerce.core import views as core_views
from ecommerce.core.url_utils import get_lms_dashboard_url
from ecommerce.extensions.urls import urlpatterns as extensions_patterns from ecommerce.extensions.urls import urlpatterns as extensions_patterns
...@@ -29,7 +28,7 @@ def handler403(_): ...@@ -29,7 +28,7 @@ def handler403(_):
Oscar's front-end are redirected to the LMS student dashboard, as one would Oscar's front-end are redirected to the LMS student dashboard, as one would
usually be after signing into the LMS. usually be after signing into the LMS.
""" """
return redirect(get_lms_dashboard_url()) return redirect(settings.LMS_DASHBOARD_URL)
admin.autodiscover() admin.autodiscover()
......
...@@ -8,12 +8,11 @@ django-libsass==0.5 ...@@ -8,12 +8,11 @@ django-libsass==0.5
django-oscar==1.1.1 django-oscar==1.1.1
django-rest-swagger==0.3.5 django-rest-swagger==0.3.5
django_simple_history==1.6.3 django_simple_history==1.6.3
django-threadlocals==0.8
django-waffle==0.10.1 django-waffle==0.10.1
djangorestframework==3.2.3 djangorestframework==3.2.3
djangorestframework-jwt==1.7.2 djangorestframework-jwt==1.7.2
drf-extensions==0.2.8 drf-extensions==0.2.8
edx-auth-backends==0.2.0 edx-auth-backends==0.1.3
edx-ecommerce-worker==0.3.3 edx-ecommerce-worker==0.3.3
edx-rest-api-client==1.5.0 edx-rest-api-client==1.5.0
jsonfield==1.0.3 jsonfield==1.0.3
......
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