Skip to content

Commit

Permalink
Add course runs endpoint.
Browse files Browse the repository at this point in the history
ECOM-3898
  • Loading branch information
Peter Fogg committed Mar 21, 2016
1 parent 8287fef commit c147169
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 6 deletions.
17 changes: 17 additions & 0 deletions course_discovery/apps/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from rest_framework import serializers

from course_discovery.apps.catalogs.models import Catalog
from course_discovery.apps.course_metadata.models import CourseRun, Person


class CatalogSerializer(serializers.ModelSerializer):
Expand All @@ -23,3 +24,19 @@ class ContainedCoursesSerializer(serializers.Serializer): # pylint: disable=abs
child=serializers.BooleanField(),
help_text=_('Dictionary mapping course IDs to boolean values')
)


class PersonSerializer(serializers.ModelSerializer):

class Meta(object):
fields = ('id', 'key')
model = Person


class CourseRunSerializer(serializers.ModelSerializer):
url = serializers.HyperlinkedIdentityField(view_name='api:v1:course-run-detail', lookup_field='key')
people = PersonSerializer(many=True)

class Meta(object):
fields = ('id', 'key', 'course', 'url', 'people')
model = CourseRun
54 changes: 52 additions & 2 deletions course_discovery/apps/api/v1/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@
from rest_framework.reverse import reverse
from rest_framework.test import APITestCase, APIRequestFactory

from course_discovery.apps.api.serializers import CatalogSerializer, CourseSerializer
from course_discovery.apps.api.serializers import CatalogSerializer, CourseSerializer, CourseRunSerializer
from course_discovery.apps.catalogs.models import Catalog
from course_discovery.apps.catalogs.tests.factories import CatalogFactory
from course_discovery.apps.core.tests.factories import UserFactory, USER_PASSWORD
from course_discovery.apps.core.tests.mixins import ElasticsearchTestMixin
from course_discovery.apps.course_metadata.tests.factories import CourseFactory
from course_discovery.apps.course_metadata.tests.factories import CourseFactory, CourseRunFactory

OAUTH2_ACCESS_TOKEN_URL = 'http://example.com/oauth2/access_token/'

Expand Down Expand Up @@ -60,6 +60,9 @@ def serialize_catalog(self, catalog, many=False, format=None):
def serialize_course(self, course, many=False, format=None):
return self._serialize_object(CourseSerializer, course, many, format)

def serialize_course_run(self, course_run, many=False, format=None):
return self._serialize_object(CourseRunSerializer, course_run, many, format)


@ddt.ddt
@skip('Skip until ES search is resolved.')
Expand Down Expand Up @@ -308,3 +311,50 @@ def test_retrieve_with_oauth2_authentication(self):
self.client.logout()
self.mock_access_token_response(self.user)
self.assert_retrieve_success(HTTP_AUTHORIZATION=self.generate_oauth2_token_header(self.user))


class CourseRunViewSetTests(SerializationMixin, OAuth2Mixin, APITestCase):

def setUp(self):
super(CourseRunViewSetTests, self).setUp()
self.user = UserFactory(is_staff=True, is_superuser=True)
self.client.login(username=self.user.username, password=USER_PASSWORD)

def test_list(self):
course_runs = sorted(CourseRunFactory.create_batch(10), key=lambda c: c.id, reverse=True)
limit = 3
offset = 5
url = reverse('api:v1:course-run-list')
response = self.client.get(url, {'limit': limit, 'offset': offset})
self.assertEqual(response.status_code, 200)
self.assertEqual(
response.data['results'],
self.serialize_course_run(course_runs[offset:offset + limit], many=True)
)

def assert_retrieve_success(self, **headers):
course_run = CourseRunFactory()
url = reverse('api:v1:course-run-detail', kwargs={'key': course_run.key})
response = self.client.get(url, **headers)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data, self.serialize_course_run(course_run))

test_retrieve = assert_retrieve_success

@responses.activate
@override_settings(OAUTH2_ACCESS_TOKEN_URL=OAUTH2_ACCESS_TOKEN_URL)
def test_retrieve_with_oauth2_authentication(self):
self.client.logout()
self.mock_access_token_response(self.user)
self.assert_retrieve_success(HTTP_AUTHORIZATION=self.generate_oauth2_token_header(self.user))

def test_unauthenticated(self):
self.client.logout()
url = reverse('api:v1:course-run-list')
response = self.client.get(url)
self.assertEqual(response.status_code, 403)

def test_retrieve_not_found(self):
url = reverse('api:v1:course-run-detail', kwargs={'key': 'org/course/run'})
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
1 change: 1 addition & 0 deletions course_discovery/apps/api/v1/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@
router = routers.SimpleRouter()
router.register(r'catalogs', views.CatalogViewSet)
router.register(r'courses', views.CourseViewSet, base_name='course')
router.register(r'course-runs', views.CourseRunViewSet, base_name='course-run')

urlpatterns += router.urls
20 changes: 18 additions & 2 deletions course_discovery/apps/api/v1/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,20 @@

from rest_framework import viewsets
from rest_framework.decorators import detail_route
from rest_framework.pagination import LimitOffsetPagination
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response

from course_discovery.apps.api.pagination import ElasticsearchLimitOffsetPagination
from course_discovery.apps.api.serializers import CatalogSerializer, CourseSerializer, ContainedCoursesSerializer
from course_discovery.apps.api.serializers import (
CatalogSerializer,
CourseSerializer,
CourseRunSerializer,
ContainedCoursesSerializer
)
from course_discovery.apps.catalogs.models import Catalog
from course_discovery.apps.course_metadata.constants import COURSE_ID_REGEX
from course_discovery.apps.course_metadata.models import Course
from course_discovery.apps.course_metadata.models import Course, CourseRun

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -143,3 +149,13 @@ def list(self, request, *args, **kwargs): # pylint: disable=unused-argument
def retrieve(self, request, *args, **kwargs):
""" Retrieve details for a course. """
return super(CourseViewSet, self).retrieve(request, *args, **kwargs)


class CourseRunViewSet(viewsets.ReadOnlyModelViewSet):
""" Course run resource."""
lookup_field = 'key'
lookup_value_regex = COURSE_ID_REGEX
permission_classes = (IsAuthenticated,)
serializer_class = CourseRunSerializer
queryset = CourseRun.objects.all()
pagination_class = LimitOffsetPagination
10 changes: 9 additions & 1 deletion course_discovery/apps/course_metadata/tests/factories.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import factory
from factory.fuzzy import FuzzyText

from course_discovery.apps.course_metadata.models import Course
from course_discovery.apps.course_metadata.models import Course, CourseRun


class CourseFactory(factory.DjangoModelFactory):
Expand All @@ -10,3 +10,11 @@ class CourseFactory(factory.DjangoModelFactory):

class Meta:
model = Course


class CourseRunFactory(factory.DjangoModelFactory):
key = FuzzyText(prefix='course-run-id/', suffix='/fake')
course = factory.SubFactory(CourseFactory)

class Meta:
model = CourseRun
1 change: 0 additions & 1 deletion course_discovery/apps/course_metadata/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,4 +209,3 @@ def _update_ecommerce_course(cls, body):
# credit_hours=credit_hours
# )
# )

0 comments on commit c147169

Please sign in to comment.