Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
course-discovery
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
course-discovery
Commits
8e35484f
Commit
8e35484f
authored
Apr 13, 2016
by
Clinton Blackburn
Committed by
Clinton Blackburn
Apr 14, 2016
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added ability to execute the refresh_course_metadata management command via HTTP
ECOM-4204
parent
a2fdb164
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
113 additions
and
3 deletions
+113
-3
course_discovery/apps/api/v1/tests/test_views/test_management.py
+63
-0
course_discovery/apps/api/v1/urls.py
+1
-0
course_discovery/apps/api/v1/views.py
+47
-1
requirements/base.txt
+2
-2
No files found.
course_discovery/apps/api/v1/tests/test_views/test_management.py
0 → 100644
View file @
8e35484f
import
mock
from
django.core.urlresolvers
import
reverse
from
rest_framework.test
import
APITestCase
from
course_discovery.apps.core.tests.factories
import
UserFactory
class
RefreshCourseMetadataTests
(
APITestCase
):
""" Tests for the refresh_course_metadata management endpoint. """
path
=
reverse
(
'api:v1:management-refresh-course-metadata'
)
call_command_path
=
'course_discovery.apps.api.v1.views.call_command'
def
setUp
(
self
):
super
(
RefreshCourseMetadataTests
,
self
)
.
setUp
()
self
.
superuser
=
UserFactory
(
is_superuser
=
True
)
self
.
client
.
force_authenticate
(
self
.
superuser
)
# pylint: disable=no-member
def
assert_access_forbidden
(
self
):
""" Asserts that a call to the endpoint fails with HTTP status 403. """
response
=
self
.
client
.
post
(
self
.
path
)
self
.
assertEqual
(
response
.
status_code
,
403
)
def
test_superuser_required
(
self
):
""" Verify only superusers can access the endpoint. """
with
mock
.
patch
(
self
.
call_command_path
,
return_value
=
None
):
response
=
self
.
client
.
post
(
self
.
path
)
self
.
assertEqual
(
response
.
status_code
,
200
)
# Anonymous user
self
.
client
.
logout
()
self
.
assert_access_forbidden
()
# Normal and staff users
users
=
(
UserFactory
(),
UserFactory
(
is_staff
=
True
),)
for
user
in
users
:
self
.
client
.
force_authenticate
(
user
)
# pylint: disable=no-member
self
.
assert_access_forbidden
()
def
test_success_response
(
self
):
""" Verify a successful response calls the management command and returns the plain text output. """
self
.
assert_successful_response
()
self
.
assert_successful_response
(
'abc123'
)
def
assert_successful_response
(
self
,
access_token
=
None
):
""" Asserts the endpoint called the refresh_course_metadata management command with the correct arguments,
and the endpoint returns HTTP 200 with text/plain content type. """
data
=
{
'access_token'
:
access_token
}
if
access_token
else
None
with
mock
.
patch
(
self
.
call_command_path
,
return_value
=
None
)
as
mocked_call_command
:
response
=
self
.
client
.
post
(
self
.
path
,
data
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
content_type
,
'text/plain'
)
args
,
kwargs
=
mocked_call_command
.
call_args
expected
=
{
'settings'
:
'course_discovery.settings.test'
}
if
access_token
:
expected
[
'access_token'
]
=
access_token
self
.
assertTrue
(
mocked_call_command
.
called
)
self
.
assertEqual
(
args
[
0
],
'refresh_course_metadata'
)
self
.
assertDictContainsSubset
(
expected
,
kwargs
)
course_discovery/apps/api/v1/urls.py
View file @
8e35484f
...
@@ -9,5 +9,6 @@ router = routers.SimpleRouter()
...
@@ -9,5 +9,6 @@ router = routers.SimpleRouter()
router
.
register
(
r'catalogs'
,
views
.
CatalogViewSet
)
router
.
register
(
r'catalogs'
,
views
.
CatalogViewSet
)
router
.
register
(
r'courses'
,
views
.
CourseViewSet
,
base_name
=
'course'
)
router
.
register
(
r'courses'
,
views
.
CourseViewSet
,
base_name
=
'course'
)
router
.
register
(
r'course_runs'
,
views
.
CourseRunViewSet
,
base_name
=
'course_run'
)
router
.
register
(
r'course_runs'
,
views
.
CourseRunViewSet
,
base_name
=
'course_run'
)
router
.
register
(
r'management'
,
views
.
ManagementViewSet
,
base_name
=
'management'
)
urlpatterns
+=
router
.
urls
urlpatterns
+=
router
.
urls
course_discovery/apps/api/v1/views.py
View file @
8e35484f
import
logging
import
logging
import
os
from
io
import
StringIO
from
django.core.management
import
call_command
from
django.db.models.functions
import
Lower
from
django.db.models.functions
import
Lower
from
dry_rest_permissions.generics
import
DRYPermissions
from
dry_rest_permissions.generics
import
DRYPermissions
from
edx_rest_framework_extensions.permissions
import
IsSuperuser
from
rest_framework
import
viewsets
from
rest_framework
import
viewsets
from
rest_framework.decorators
import
detail_route
from
rest_framework.decorators
import
detail_route
,
list_route
from
rest_framework.permissions
import
IsAuthenticated
from
rest_framework.permissions
import
IsAuthenticated
from
rest_framework.response
import
Response
from
rest_framework.response
import
Response
...
@@ -156,3 +160,45 @@ class CourseRunViewSet(viewsets.ReadOnlyModelViewSet):
...
@@ -156,3 +160,45 @@ class CourseRunViewSet(viewsets.ReadOnlyModelViewSet):
def
retrieve
(
self
,
request
,
*
args
,
**
kwargs
):
def
retrieve
(
self
,
request
,
*
args
,
**
kwargs
):
""" Retrieve details for a course run. """
""" Retrieve details for a course run. """
return
super
(
CourseRunViewSet
,
self
)
.
retrieve
(
request
,
*
args
,
**
kwargs
)
return
super
(
CourseRunViewSet
,
self
)
.
retrieve
(
request
,
*
args
,
**
kwargs
)
class
ManagementViewSet
(
viewsets
.
ViewSet
):
permission_classes
=
(
IsSuperuser
,)
@list_route
(
methods
=
[
'post'
])
def
refresh_course_metadata
(
self
,
request
):
""" Refresh the course metadata from external data sources.
---
parameters:
- name: access_token
description: OAuth access token to use in lieu of that issued to the service.
required: false
type: string
paramType: form
multiple: false
"""
access_token
=
request
.
data
.
get
(
'access_token'
)
# Capture all output and logging
out
=
StringIO
()
err
=
StringIO
()
log
=
StringIO
()
root_logger
=
logging
.
getLogger
()
log_handler
=
logging
.
StreamHandler
(
log
)
formatter
=
logging
.
Formatter
(
"
%(asctime)
s -
%(name)
s -
%(levelname)
s -
%(message)
s"
)
log_handler
.
setFormatter
(
formatter
)
root_logger
.
addHandler
(
log_handler
)
logger
.
info
(
'Updating course metadata per request of [
%
s]...'
,
request
.
user
.
username
)
kwargs
=
{
'access_token'
:
access_token
}
if
access_token
else
{}
call_command
(
'refresh_course_metadata'
,
settings
=
os
.
environ
[
'DJANGO_SETTINGS_MODULE'
],
stdout
=
out
,
stderr
=
err
,
**
kwargs
)
# Format the output for display
output
=
'STDOUT
\n
{out}
\n\n
STDERR
\n
{err}
\n\n
LOG
\n
{log}'
.
format
(
out
=
out
.
getvalue
(),
err
=
err
.
getvalue
(),
log
=
log
.
getvalue
())
return
Response
(
output
,
content_type
=
'text/plain'
)
requirements/base.txt
View file @
8e35484f
...
@@ -7,11 +7,11 @@ django-sortedm2m==1.1.1
...
@@ -7,11 +7,11 @@ django-sortedm2m==1.1.1
django-waffle==0.11
django-waffle==0.11
djangorestframework==3.3.3
djangorestframework==3.3.3
djangorestframework-jwt==1.7.2
djangorestframework-jwt==1.7.2
django-rest-swagger[reST]==0.3.
4
django-rest-swagger[reST]==0.3.
5
dry-rest-permissions==0.1.6
dry-rest-permissions==0.1.6
edx-auth-backends==0.1.3
edx-auth-backends==0.1.3
edx-ccx-keys==0.2.0
edx-ccx-keys==0.2.0
edx-drf-extensions==0.
2
.0
edx-drf-extensions==0.
3
.0
edx-opaque-keys==0.3.0
edx-opaque-keys==0.3.0
edx-rest-api-client==1.5.0
edx-rest-api-client==1.5.0
elasticsearch>=1.0.0,<2.0.0
elasticsearch>=1.0.0,<2.0.0
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment