forked from openedx/course-discovery
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Create course discovery API for adding new client (#18)
- Loading branch information
1 parent
300f452
commit 7bfa88b
Showing
18 changed files
with
318 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
19
course_discovery/apps/edly_discovery_app/api/v1/constants.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
29
course_discovery/apps/edly_discovery_app/api/v1/helpers.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
21
course_discovery/apps/edly_discovery_app/api/v1/permissions.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
120
course_discovery/apps/edly_discovery_app/api/v1/tests/test_views.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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'), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from django.db import models | ||
|
||
# Create your models here. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from django.test import TestCase | ||
|
||
# Create your tests here. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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')), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from django.shortcuts import render | ||
|
||
# Create your views here. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters