Skip to content

Commit

Permalink
Create course discovery API for adding new client (#18)
Browse files Browse the repository at this point in the history
  • Loading branch information
asadmanzoor93 authored Apr 30, 2021
1 parent 300f452 commit 7bfa88b
Show file tree
Hide file tree
Showing 18 changed files with 318 additions and 0 deletions.
Empty file.
3 changes: 3 additions & 0 deletions course_discovery/apps/edly_discovery_app/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.contrib import admin

# Register your models here.
Empty file.
19 changes: 19 additions & 0 deletions course_discovery/apps/edly_discovery_app/api/v1/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
"""
Constants for Edly Sites API.
"""
from django.utils.translation import ugettext as _

ERROR_MESSAGES = {
'CLIENT_SITES_SETUP_SUCCESS': _('Client sites setup successful.'),
'CLIENT_SITES_SETUP_FAILURE': _('Client sites setup failed.'),
}

CLIENT_SITE_SETUP_FIELDS = [
'lms_site',
'cms_site',
'discovery_site',
'payments_site',
'wordpress_site',
'partner_name',
'partner_short_code',
]
29 changes: 29 additions & 0 deletions course_discovery/apps/edly_discovery_app/api/v1/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
"""
Helper methods for Edly Sites API.
"""
import logging

from edly_discovery_app.api.v1.constants import CLIENT_SITE_SETUP_FIELDS

logger = logging.getLogger(__name__)


def validate_partner_configurations(request_data):
"""
Identify missing required fields for client's site partner setup.
Arguments:
request_data (dict): Request data passed for site setup
Returns:
validation_messages (dict): Missing fields information
"""

validation_messages = {}

for field in CLIENT_SITE_SETUP_FIELDS:
if not request_data.get(field, None):
field_title = field.replace('_', ' ').title() # Convert data field 'partner_name' to 'Partner Name'
validation_messages[field] = '{field_title} is Missing'.format(field_title=field_title)

return validation_messages
21 changes: 21 additions & 0 deletions course_discovery/apps/edly_discovery_app/api/v1/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""
Permissions for Edly Sites API.
"""
import logging

from django.conf import settings
from rest_framework import permissions

logger = logging.getLogger(__name__)


class CanAccessSiteCreation(permissions.BasePermission):
"""
Checks if a user has the access to create and update methods for sites.
"""

def has_permission(self, request, view):
"""
Checks for user's permission for current site.
"""
return request.user.is_staff or request.user.username == settings.EDLY_PANEL_WORKER_USER
Empty file.
120 changes: 120 additions & 0 deletions course_discovery/apps/edly_discovery_app/api/v1/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
"""
Unit tests for Edly Site API views.
"""
from unittest import TestCase
import pytest

from django.conf import settings
from django.contrib.sites.models import Site
from django.test.client import Client, RequestFactory
from django.urls import reverse
from rest_framework import status

from course_discovery.apps.core.models import Partner
from course_discovery.apps.core.tests.factories import (
SiteFactory,
UserFactory,
PartnerFactory,
USER_PASSWORD,
)
from edly_discovery_app.api.v1.constants import CLIENT_SITE_SETUP_FIELDS

pytestmark = pytest.mark.django_db


def get_request_object_with_partner_access(user=None, short_code=None, site=None):
"""
Get request object with partner access.
Arguments:
user (User): Django User object
short_code (String): Partner Short Code
site (Site): Django Site object
Returns:
Request: WSGI Request object with partner access
"""
request_site = SiteFactory() if not site else site
PartnerFactory(short_code=short_code, site=request_site)
request = RequestFactory().get('/')
request.site = request_site

if user:
request.user = user

return request


class EdlySiteViewSet(TestCase):
"""
Unit tests for EdlySiteViewSet View.
"""

def setUp(self):
"""
Setup data for test cases.
"""
self.user = UserFactory(username=settings.EDLY_PANEL_WORKER_USER)
self.short_code = 'red'
self.url = reverse('edly_discovery_app:v1:edly_sites')
self.request = get_request_object_with_partner_access(self.user, short_code=self.short_code)
self.client = Client(SERVER_NAME=self.request.site.domain)
self.client.login(username=self.user.username, password=USER_PASSWORD)

def test_without_authentication(self):
"""
Verify authentication is required when accessing the endpoint.
"""
self.client.logout()
response = self.client.post(self.url)
assert response.status_code == status.HTTP_403_FORBIDDEN

def test_without_permission(self):
"""
Verify permission is required when accessing the endpoint.
"""
edly_panel_user = UserFactory()
api_url = reverse('edly_discovery_app:v1:edly_sites')
request = get_request_object_with_partner_access(edly_panel_user, short_code='test_org')

client = Client(SERVER_NAME=request.site.domain)
client.login(username=edly_panel_user.username, password=USER_PASSWORD)

response = client.post(api_url)
assert response.status_code == status.HTTP_403_FORBIDDEN

def test_request_data_authentication(self):
"""
Verify authentication for request data.
"""
response = self.client.post(self.url)
request_data_fields_validations = [*response.json().keys()]

assert set(request_data_fields_validations) == set(CLIENT_SITE_SETUP_FIELDS)
assert response.status_code == status.HTTP_400_BAD_REQUEST

def test_client_setup(self):
"""
Verify authentication for request data.
"""
expected_request_data = {
'protocol': 'http',
'partner_name': 'Test',
'partner_short_code': 'test',
'lms_site': 'test.edx.devstack.lms:18000',
'cms_site': 'test.edx.devstack.lms:18010',
'discovery_site': 'test.edx.devstack.lms:18381',
'payments_site': 'test.edx.devstack.lms:18130',
'wordpress_site': 'test.wordpress.edx.devstack.lms',
}

response = self.client.post(self.url, data=expected_request_data)
assert response.status_code == status.HTTP_200_OK

discovery_site = Site.objects.get(
domain=expected_request_data['discovery_site'],
name=expected_request_data['discovery_site'],
)
partner = Partner.objects.get(site=discovery_site)
assert discovery_site.domain == expected_request_data['discovery_site']
assert partner.short_code == expected_request_data['partner_short_code']
9 changes: 9 additions & 0 deletions course_discovery/apps/edly_discovery_app/api/v1/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from django.conf.urls import include, url

from edly_discovery_app.api.v1 import views


app_name = 'v1'
urlpatterns = [
url(r'^edly_sites/', views.EdlySiteViewSet.as_view(), name='edly_sites'),
]
91 changes: 91 additions & 0 deletions course_discovery/apps/edly_discovery_app/api/v1/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
"""
Views for Edly Sites API.
"""
from django.contrib.sites.models import Site
from rest_framework import status, viewsets, filters
from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView

from course_discovery.apps.core.models import Partner
from edly_discovery_app.api.v1.constants import ERROR_MESSAGES
from edly_discovery_app.api.v1.helpers import validate_partner_configurations
from edly_discovery_app.api.v1.permissions import CanAccessSiteCreation


class EdlySiteViewSet(APIView):
"""
Create Default Site and Partner Configuration.
"""
authentication_classes = (SessionAuthentication,)
permission_classes = [IsAuthenticated, CanAccessSiteCreation]

def post(self, request):
"""
POST /edly_api/v1/edly_sites/
"""

validations_messages = validate_partner_configurations(request.data)
if len(validations_messages) > 0:
return Response(validations_messages, status=status.HTTP_400_BAD_REQUEST)

try:
self.discovery_site_setup()
return Response(
{'success': ERROR_MESSAGES.get('CLIENT_SITES_SETUP_SUCCESS')},
status=status.HTTP_200_OK
)
except TypeError:
return Response(
{'error': ERROR_MESSAGES.get('CLIENT_SITES_SETUP_FAILURE')},
status=status.HTTP_400_BAD_REQUEST
)

def discovery_site_setup(self):
"""
Discovery site setup with default partner configurations.
"""
discovery_base = self.request.data.get('discovery_site', '')
discovery_site, __ = Site.objects.get_or_create(domain=discovery_base, name=discovery_base)
return self.get_updated_site_partner(discovery_site)

def get_updated_site_partner(self, discovery_site):
"""
Get updated site partner based on request data.
"""
protocol = self.request.data.get('protocol', 'https')
lms_base = self.request.data.get('lms_site', '')
wordpress_base = self.request.data.get('wordpress_site', '')
partner, __ = Partner.objects.get_or_create(site=discovery_site)

partner.name = self.request.data.get('partner_name', '')
partner.short_code = self.request.data.get('partner_short_code', '')
partner.lms_url = '{protocol}://{lms_domain}'.format(protocol=protocol, lms_domain=lms_base)
partner.studio_url = '{protocol}://{cms_domain}'.format(
protocol=protocol,
cms_domain=self.request.data.get('cms_site', ''),
)
partner.courses_api_url = '{protocol}://{lms_domain}/api/courses/v1/'.format(
protocol=protocol,
lms_domain=lms_base,
)
partner.ecommerce_api_url = '{protocol}://{payments_domain}/api/v2/'.format(
protocol=protocol,
payments_domain=self.request.data.get('payments_site', ''),
)
partner.organizations_api_url = '{protocol}://{lms_domain}/api/organizations/v0/organizations/'.format(
protocol=protocol,
lms_domain=lms_base,
)
partner.marketing_site_url_root = '{protocol}://{wordpress_domain}/'.format(
protocol=protocol,
wordpress_domain=wordpress_base,
)
partner.marketing_site_api_url = '{protocol}://{wordpress_domain}/wp-json/edly/v1/course_runs'.format(
protocol=protocol,
wordpress_domain=wordpress_base,
)
partner.save()

return partner
5 changes: 5 additions & 0 deletions course_discovery/apps/edly_discovery_app/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from django.apps import AppConfig


class EdlyDiscoveryAppConfig(AppConfig):
name = 'edly_discovery_app'
Empty file.
3 changes: 3 additions & 0 deletions course_discovery/apps/edly_discovery_app/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.db import models

# Create your models here.
3 changes: 3 additions & 0 deletions course_discovery/apps/edly_discovery_app/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.test import TestCase

# Create your tests here.
7 changes: 7 additions & 0 deletions course_discovery/apps/edly_discovery_app/urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from django.conf.urls import include, url

app_name = 'edly_discovery_app'

urlpatterns = [
url(r'^v1/', include('course_discovery.apps.edly_discovery_app.api.v1.urls')),
]
3 changes: 3 additions & 0 deletions course_discovery/apps/edly_discovery_app/views.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from django.shortcuts import render

# Create your views here.
4 changes: 4 additions & 0 deletions course_discovery/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@
'course_discovery.apps.publisher',
'course_discovery.apps.publisher_comments',
'course_discovery.apps.journal',
'course_discovery.apps.edly_discovery_app',
]


Expand Down Expand Up @@ -637,3 +638,6 @@
}
WORDPRESS_APP_AUTH_USERNAME = 'coursediscoveryworker'
WORDPRESS_APP_AUTH_PASSWORD = ''

# Edly configuration
EDLY_PANEL_WORKER_USER = 'edly_panel_worker'
1 change: 1 addition & 0 deletions course_discovery/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
url(r'^i18n/', include('django.conf.urls.i18n')),
url(r'^jsi18n/$', JavaScriptCatalog, name='javascript-catalog'),
url(r'^taggit_autosuggest/', include('taggit_autosuggest.urls')),
url(r'^edly_api/', include('course_discovery.apps.edly_discovery_app.urls', namespace='edly_api')),
]

# edx-drf-extensions csrf app
Expand Down

0 comments on commit 7bfa88b

Please sign in to comment.