Skip to content

Commit

Permalink
Merge pull request #18 from edx/ahsan/ECOM-3486-GET-organizations-API…
Browse files Browse the repository at this point in the history
…-in-edx-organizations

Create GET organizations API in edx-organizations
  • Loading branch information
Ahsan Ulhaq committed Jan 22, 2016
2 parents a57396b + 6ea074a commit 8dc3314
Show file tree
Hide file tree
Showing 14 changed files with 197 additions and 13 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
Matt Drayer <[email protected]>
Zia Fazal <[email protected]>
Ahsan Ulhaq <[email protected]>
11 changes: 10 additions & 1 deletion organizations/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@
Data layer serialization operations. Converts querysets to simple
python containers (mainly arrays and dicts).
"""
from . import models
from rest_framework import serializers

from organizations import models


# pylint: disable=too-few-public-methods
class OrganizationSerializer(serializers.ModelSerializer):
""" Serializes the Organization object."""
class Meta(object): # pylint: disable=missing-docstring
model = models.Organization


def serialize_organization(organization):
Expand Down
37 changes: 37 additions & 0 deletions organizations/tests/factories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""
Provides factory for User.
"""
# pylint: disable=too-few-public-methods
from django.contrib.auth.models import User
import factory
from factory.django import DjangoModelFactory

from organizations.models import Organization


class UserFactory(DjangoModelFactory):
""" User creation factory."""
class Meta(object): # pylint: disable=missing-docstring
model = User
django_get_or_create = ('email', 'username')

username = factory.Sequence(u'robot{0}'.format)
email = factory.Sequence(u'robot+test+{0}@edx.org'.format)
password = factory.PostGenerationMethodCall('set_password', 'test')
first_name = factory.Sequence(u'Robot{0}'.format)
last_name = 'Test'
is_staff = False
is_active = True
is_superuser = False


class OrganizationFactory(DjangoModelFactory):
""" Organization creation factory."""
class Meta(object): # pylint: disable=missing-docstring
model = Organization

name = factory.Sequence(u'organization name {}'.format)
short_name = factory.Sequence(u'name{}'.format)
description = factory.Sequence(u'description{}'.format)
logo = None
active = True
33 changes: 33 additions & 0 deletions organizations/tests/test_serializers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
"""
Tests for Organizations API serializers.
"""
# pylint: disable=no-member

from __future__ import unicode_literals
from django.test import TestCase
from rest_framework.settings import api_settings

from organizations.serializers import OrganizationSerializer
from organizations.tests.factories import OrganizationFactory


class TestOrganizationSerializer(TestCase):
""" OrganizationSerializer tests."""
def setUp(self):
super(TestOrganizationSerializer, self).setUp()
self.organization = OrganizationFactory.create()

def test_data(self):
""" Verify that OrganizationSerializer serialize data correctly."""
serialize_data = OrganizationSerializer(self.organization)
expected = {
"id": self.organization.id,
"name": self.organization.name,
"short_name": self.organization.short_name,
"description": self.organization.description,
"logo": None,
"active": self.organization.active,
"created": self.organization.created.strftime(api_settings.DATETIME_FORMAT),
"modified": self.organization.modified.strftime(api_settings.DATETIME_FORMAT)
}
self.assertEqual(serialize_data.data, expected)
8 changes: 7 additions & 1 deletion organizations/urls.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
"""
urls.py -- useful some day when views.py comes alive
URLS for organizations
"""
from django.conf.urls import url, include

# pylint: disable=invalid-name
urlpatterns = [
url(r'^v0/', include('organizations.v0.urls', namespace='v0')),
]
Empty file added organizations/v0/__init__.py
Empty file.
Empty file.
58 changes: 58 additions & 0 deletions organizations/v0/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""
Organizations Views Test Cases.
"""
from django.test import TestCase
from django.core.urlresolvers import reverse

from organizations.serializers import OrganizationSerializer
from organizations.tests.factories import UserFactory, OrganizationFactory


# pylint: disable=no-member, no-self-use
class TestOrganizationsView(TestCase):
""" Test Organizations View."""
def setUp(self):
super(TestOrganizationsView, self).setUp()

self.user_password = 'test'
self.user = UserFactory(password=self.user_password)
self.organization = OrganizationFactory.create()
self.organization_list_url = reverse('v0:organization-list')
self.client.login(username=self.user.username, password=self.user_password)

def _get_organization_url(self, organization):
""" Return organization specific URL."""
return reverse('v0:organization-detail', kwargs={'short_name': organization.short_name})

def test_authentication_required(self):
""" Verify that the authentication is required to access view."""
self.client.logout()
response = self.client.get(self.organization_list_url)
self.assertEqual(response.status_code, 403)

def test_authenticated_user(self):
""" Verify that the authenticated user gets data."""
OrganizationFactory.create()
response = self.client.get(self.organization_list_url)
self.assertEqual(response.status_code, 200)
self.assertEqual(len(response.data['results']), 2)

def test_single_organization(self):
"""verify single organization data could be fetched using short name"""
url = self._get_organization_url(self.organization)
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertEqual(response.data, OrganizationSerializer(self.organization).data)

def test_inactive_organization(self):
""" Verify inactive organization are filtered out."""
organization = OrganizationFactory(active=False)
url = self._get_organization_url(organization)
response = self.client.get(url)
self.assertEqual(response.status_code, 404)

def test_nonexistent_organization(self):
""" Verify that nonexistent organization have proper status code."""
url = reverse('v0:organization-detail', kwargs={'short_name': 'dummy'})
response = self.client.get(url)
self.assertEqual(response.status_code, 404)
12 changes: 12 additions & 0 deletions organizations/v0/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""
URLS for organizations end points.
"""
# pylint: disable=invalid-name
from rest_framework import routers

from organizations.v0.views import OrganizationsViewSet

router = routers.SimpleRouter()
router.register(r'organizations', OrganizationsViewSet)

urlpatterns = router.urls
22 changes: 22 additions & 0 deletions organizations/v0/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# pylint: disable=too-many-ancestors
"""
Views for organizations end points.
"""
from rest_framework import viewsets
from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework_jwt.authentication import JSONWebTokenAuthentication

from organizations.models import Organization
from organizations.serializers import OrganizationSerializer


class OrganizationsViewSet(viewsets.ReadOnlyModelViewSet):
"""Organization view to fetch list organization data or single organization
using organization short name.
"""
queryset = Organization.objects.filter(active=True) # pylint: disable=no-member
serializer_class = OrganizationSerializer
lookup_field = 'short_name'
authentication_classes = (SessionAuthentication, JSONWebTokenAuthentication,)
permission_classes = (IsAuthenticated,)
8 changes: 0 additions & 8 deletions organizations/views.py

This file was deleted.

3 changes: 3 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ django-model-utils==1.4.0
# Testing Packages
coverage==3.7.1
django_nose==1.4.1
djangorestframework==3.2.3
djangorestframework-jwt==1.7.2
factory-boy==2.5.2
nose==1.3.3
httpretty==0.8.0
pep8==1.5.7
Expand Down
15 changes: 13 additions & 2 deletions settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@
'django_nose',
)

MIDDLEWARE_CLASSES = {
'django.contrib.messages.middleware.MessageMiddleware'
MIDDLEWARE_CLASSES = (
'django.contrib.sessions.middleware.SessionMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
)

ROOT_URLCONF = 'organizations.urls'

# DRF Settings
REST_FRAMEWORK = {
'PAGE_SIZE': 20,
'DATETIME_FORMAT': '%Y-%m-%dT%H:%M:%SZ'
}
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

setup(
name='edx-organizations',
version='0.2.0',
version='0.3.0',
description='Organization management module for Open edX',
long_description=open('README.md').read(),
author='edX',
Expand Down

0 comments on commit 8dc3314

Please sign in to comment.