Commit eb5fc311 by Albert St. Aubin

Refactored the API to be part of the Entitlement API, removed it from

the Enrollment API
parent b0a19e94
......@@ -5,7 +5,6 @@ import datetime
import itertools
import json
import unittest
import uuid
import ddt
import httpretty
......@@ -25,11 +24,9 @@ from xmodule.modulestore.tests.factories import CourseFactory, check_mongo_calls
from course_modes.models import CourseMode
from course_modes.tests.factories import CourseModeFactory
from entitlements.tests.factories import CourseEntitlementFactory
from enrollment import api
from enrollment.errors import CourseEnrollmentError
from enrollment.views import EnrollmentUserThrottle
from entitlements.models import CourseEntitlement
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.embargo.models import Country, CountryAccessRule, RestrictedCourse
from openedx.core.djangoapps.embargo.test_utils import restrict_course
......@@ -38,7 +35,7 @@ from openedx.core.lib.django_test_client_utils import get_absolute_url
from openedx.features.enterprise_support.tests.mixins.enterprise import EnterpriseServiceMockMixin
from student.models import CourseEnrollment
from student.roles import CourseStaffRole
from student.tests.factories import AdminFactory, UserFactory, TEST_PASSWORD
from student.tests.factories import AdminFactory, UserFactory
from util.models import RateLimitConfiguration
from util.testing import UrlResetMixin
......@@ -50,7 +47,6 @@ class EnrollmentTestMixin(object):
def assert_enrollment_status(
self,
course_id=None,
course_uuid=None,
username=None,
expected_status=status.HTTP_200_OK,
email_opt_in=None,
......@@ -81,9 +77,6 @@ class EnrollmentTestMixin(object):
'enrollment_attributes': enrollment_attributes
}
if course_uuid:
data['course_details']['course_uuid'] = course_uuid
if is_active is not None:
data['is_active'] = is_active
......@@ -140,93 +133,6 @@ class EnrollmentTestMixin(object):
self.assertEqual(actual_mode, expected_mode)
# @override_settings(EDX_API_KEY="i am a key")
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
class EntitlementEnrollmentTest(EnrollmentTestMixin, ModuleStoreTestCase, APITestCase):
def setUp(self):
super(EntitlementEnrollmentTest, self).setUp()
self.course = CourseFactory()
self.user = UserFactory()
CourseModeFactory.create(
course_id=self.course.id,
mode_slug=CourseMode.VERIFIED,
mode_display_name=CourseMode.VERIFIED,
)
self.client.login(username=self.user.username, password=TEST_PASSWORD)
def test_enroll_entitlement(self):
entitlement = CourseEntitlementFactory.create(user=self.user, mode='verified')
resp = self.assert_enrollment_status(
course_id=unicode(self.course.id),
course_uuid=str(entitlement.course_uuid),
is_active=True,
mode=None,
max_mongo_calls=4
)
data = json.loads(resp.content)
self.assertEqual(self.course.display_name_with_default, data['course_details']['course_name'])
# Verify that the enrollment was created correctly
self.assertTrue(CourseEnrollment.is_enrolled(self.user, self.course.id))
course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
self.assertTrue(is_active)
self.assertEqual(course_mode, entitlement.mode)
entitlement.refresh_from_db()
# Verify the Entitlement settings are correct
self.assertIsNotNone(entitlement.enrollment_course_run)
self.assertEqual(entitlement.enrollment_course_run.course_id, self.course.id)
def test_unenroll_entitlement(self):
entitlement = CourseEntitlementFactory.create(user=self.user, mode='verified')
# Enroll user
self.assert_enrollment_status(
course_id=unicode(self.course.id),
course_uuid=str(entitlement.course_uuid),
is_active=True,
mode=None,
max_mongo_calls=4
)
# Unenroll the user
resp = self.assert_enrollment_status(
course_id=unicode(self.course.id),
course_uuid=str(entitlement.course_uuid),
is_active=False,
mode=None,
max_mongo_calls=4
)
data = json.loads(resp.content)
self.assertEqual(self.course.display_name_with_default, data['course_details']['course_name'])
# Verify that the enrollment was created correctly
self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course.id))
course_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
self.assertFalse(is_active)
self.assertEqual(course_mode, entitlement.mode)
entitlement.refresh_from_db()
self.assertIsNone(entitlement.enrollment_course_run)
def test_enroll_no_entitlement(self):
resp = self.assert_enrollment_status(
course_id=unicode(self.course.id),
course_uuid=str(uuid.uuid4()),
is_active=True,
mode=None,
max_mongo_calls=4,
expected_status=status.HTTP_400_BAD_REQUEST
)
data = json.loads(resp.content)
self.assertEqual(self.course.display_name_with_default, data['course_details']['course_name'])
# Verify that the enrollment was created correctly
self.assertFalse(CourseEnrollment.is_enrolled(self.user, self.course.id))
@attr(shard=3)
@override_settings(EDX_API_KEY="i am a key")
@ddt.ddt
......
from django.conf.urls import url, include
from rest_framework.routers import DefaultRouter
from .views import EntitlementViewSet
from .views import EntitlementViewSet, EntitlementEnrollmentViewSet
router = DefaultRouter()
router.register(r'entitlements', EntitlementViewSet, base_name='entitlements')
enrollments_view = EntitlementEnrollmentViewSet.as_view({
'post': 'create',
'delete': 'destroy',
})
urlpatterns = [
url(r'', include(router.urls)),
url(
r'entitlements/(?P<uuid>[0-9a-f-]+)/enrollments/$',
enrollments_view,
name='enrollments'
)
]
......@@ -3,13 +3,19 @@ import logging
from django.utils import timezone
from django_filters.rest_framework import DjangoFilterBackend
from edx_rest_framework_extensions.authentication import JwtAuthentication
from rest_framework import permissions, viewsets
from rest_framework import permissions, viewsets, status
from rest_framework.response import Response
from rest_framework.authentication import SessionAuthentication
from openedx.core.djangoapps.catalog.utils import get_course_runs_for_course
from entitlements.api.v1.filters import CourseEntitlementFilter
from entitlements.api.v1.permissions import IsAdminOrAuthenticatedReadOnly
from entitlements.models import CourseEntitlement
from entitlements.api.v1.serializers import CourseEntitlementSerializer
from entitlements.models import CourseEntitlement
from openedx.core.djangoapps.cors_csrf.authentication import SessionAuthenticationCrossDomainCsrf
from opaque_keys.edx.keys import CourseKey
from student.models import CourseEnrollment
log = logging.getLogger(__name__)
......@@ -57,3 +63,105 @@ class EntitlementViewSet(viewsets.ModelViewSet):
)
if save_model:
instance.save()
class EntitlementEnrollmentViewSet(viewsets.GenericViewSet):
authentication_classes = (JwtAuthentication, SessionAuthentication,)
permission_classes = (permissions.IsAuthenticated,)
queryset = CourseEntitlement.objects.all()
serializer_class = CourseEntitlementSerializer
def _enroll_entitlement(self, entitlement, course_session_key, user):
enrollment = CourseEnrollment.enroll(
user=user,
course_key=course_session_key,
mode=entitlement.mode,
)
CourseEntitlement.set_enrollment(entitlement, enrollment)
def _unenroll_entitlement(self, entitlement, course_session_key, user):
CourseEnrollment.unenroll(user, course_session_key, skip_refund=True)
CourseEntitlement.set_enrollment(entitlement, None)
def create(self, request, uuid):
course_session_id = request.data.get('course_session_id', None)
if not course_session_id:
return Response(
status=status.HTTP_400_BAD_REQUEST,
data="The Course Run ID was not provided."
)
# Verify that the user has an Entitlement for the provided Course UUID.
try:
entitlement = CourseEntitlement.objects.get(uuid=uuid, user=request.user, expired_at=None)
except CourseEntitlement.DoesNotExist:
return Response(
status=status.HTTP_400_BAD_REQUEST,
data="The Entitlement for this UUID does not exist or is Expired."
)
# Verify the course run ID is of the same type as the Course entitlement.
course_run_valid = False
course_runs = get_course_runs_for_course(entitlement.course_uuid)
for run in course_runs:
if course_session_id == run.get('key', ''):
course_run_valid = True
if not course_run_valid:
return Response(
status=status.HTTP_400_BAD_REQUEST,
data="The Course Run ID is not a match for this Course Entitlement."
)
# Determine if this is a Switch session or a simple enroll and handle both.
if entitlement.enrollment_course_run is None:
self._enroll_entitlement(
entitlement=entitlement,
course_session_key=CourseKey.from_string(course_session_id),
user=request.user
)
else:
if entitlement.enrollment_course_run.course_id != course_session_id:
self._unenroll_entitlement(
entitlement=entitlement,
course_session_key=entitlement.enrollment_course_run.course_id,
user=request.user
)
self._enroll_entitlement(
entitlement=entitlement,
course_session_key=CourseKey.from_string(course_session_id),
user=request.user
)
return Response(
status=status.HTTP_201_CREATED,
data={
'uuid': entitlement.uuid,
'course_run_id': course_session_id,
'is_active': True
}
)
def destroy(self, request, uuid):
"""
On DELETE call to this API we will unenroll the course enrollment for the provided uuid
"""
try:
entitlement = CourseEntitlement.objects.get(uuid=uuid, user=request.user, expired_at=None)
except CourseEntitlement.DoesNotExist:
return Response(
status=status.HTTP_400_BAD_REQUEST,
data="The Entitlement for this UUID does not exist or is Expired."
)
if entitlement.enrollment_course_run is None:
return Response()
self._unenroll_entitlement(
entitlement=entitlement,
course_session_key=entitlement.enrollment_course_run.course_id,
user=request.user
)
return Response(status=status.HTTP_204_NO_CONTENT)
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('entitlements', '0002_auto_20171102_0719'),
]
operations = [
migrations.AlterField(
model_name='courseentitlement',
name='uuid',
field=models.UUIDField(default=uuid.uuid4, unique=True, editable=False),
),
]
......@@ -11,7 +11,7 @@ class CourseEntitlement(TimeStampedModel):
"""
user = models.ForeignKey(settings.AUTH_USER_MODEL)
uuid = models.UUIDField(default=uuid_tools.uuid4, editable=False)
uuid = models.UUIDField(default=uuid_tools.uuid4, editable=False, unique=True)
course_uuid = models.UUIDField(help_text='UUID for the Course, not the Course Run')
expired_at = models.DateTimeField(
null=True,
......
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