Commit d8799057 by Martyn James

Merge pull request #6914 from edx/dcikatic/courseware_search_index_DRF

Changing search_reindex call to be more inline with DRF convention
parents 3a9a7961 b597df2e
...@@ -306,18 +306,25 @@ def course_search_index_handler(request, course_key_string): ...@@ -306,18 +306,25 @@ def course_search_index_handler(request, course_key_string):
The restful handler for course indexing. The restful handler for course indexing.
GET GET
html: return status of indexing task html: return status of indexing task
json: return status of indexing task
""" """
# Only global staff (PMs) are able to index courses # Only global staff (PMs) are able to index courses
if not GlobalStaff().has_user(request.user): if not GlobalStaff().has_user(request.user):
raise PermissionDenied() raise PermissionDenied()
course_key = CourseKey.from_string(course_key_string) course_key = CourseKey.from_string(course_key_string)
content_type = request.META.get('CONTENT_TYPE', None)
if content_type is None:
content_type = "application/json; charset=utf-8"
with modulestore().bulk_operations(course_key): with modulestore().bulk_operations(course_key):
try: try:
reindex_course_and_check_access(course_key, request.user) reindex_course_and_check_access(course_key, request.user)
except SearchIndexingError as search_err: except SearchIndexingError as search_err:
return HttpResponse(search_err.error_list, status=500) return HttpResponse(json.dumps({
"user_message": search_err.error_list
return HttpResponse({}, status=200) }), content_type=content_type, status=500)
return HttpResponse(json.dumps({
"user_message": _("Course has been successfully reindexed.")
}), content_type=content_type, status=200)
def _course_outline_json(request, course_module): def _course_outline_json(request, course_module):
...@@ -487,7 +494,7 @@ def course_index(request, course_key): ...@@ -487,7 +494,7 @@ def course_index(request, course_key):
lms_link = get_lms_link_for_item(course_module.location) lms_link = get_lms_link_for_item(course_module.location)
reindex_link = None reindex_link = None
if settings.FEATURES.get('ENABLE_COURSEWARE_INDEX', False): if settings.FEATURES.get('ENABLE_COURSEWARE_INDEX', False):
reindex_link = "/course_search_index/{course_id}".format(course_id=unicode(course_key)) reindex_link = "/course/{course_id}/search_reindex".format(course_id=unicode(course_key))
sections = course_module.get_children() sections = course_module.get_children()
course_structure = _course_outline_json(request, course_module) course_structure = _course_outline_json(request, course_module)
locator_to_show = request.REQUEST.get('show', None) locator_to_show = request.REQUEST.get('show', None)
......
...@@ -25,6 +25,7 @@ from student.tests.factories import UserFactory ...@@ -25,6 +25,7 @@ from student.tests.factories import UserFactory
from course_action_state.managers import CourseRerunUIStateManager from course_action_state.managers import CourseRerunUIStateManager
from django.conf import settings from django.conf import settings
from django.core.exceptions import PermissionDenied from django.core.exceptions import PermissionDenied
from django.utils.translation import ugettext as _
from search.api import perform_search from search.api import perform_search
import pytz import pytz
...@@ -347,6 +348,8 @@ class TestCourseReIndex(CourseTestCase): ...@@ -347,6 +348,8 @@ class TestCourseReIndex(CourseTestCase):
TEST_INDEX_FILENAME = "test_root/index_file.dat" TEST_INDEX_FILENAME = "test_root/index_file.dat"
SUCCESSFUL_RESPONSE = _("Course has been successfully reindexed.")
def setUp(self): def setUp(self):
""" """
Set up the for the course outline tests. Set up the for the course outline tests.
...@@ -388,7 +391,7 @@ class TestCourseReIndex(CourseTestCase): ...@@ -388,7 +391,7 @@ class TestCourseReIndex(CourseTestCase):
response = self.client.get(index_url, {}, HTTP_ACCEPT='application/json') response = self.client.get(index_url, {}, HTTP_ACCEPT='application/json')
# A course with the default release date should display as "Unscheduled" # A course with the default release date should display as "Unscheduled"
self.assertEqual(response.content, '') self.assertIn(self.SUCCESSFUL_RESPONSE, response.content)
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
response = self.client.post(index_url, {}, HTTP_ACCEPT='application/json') response = self.client.post(index_url, {}, HTTP_ACCEPT='application/json')
...@@ -409,6 +412,33 @@ class TestCourseReIndex(CourseTestCase): ...@@ -409,6 +412,33 @@ class TestCourseReIndex(CourseTestCase):
response = non_staff_client.get(index_url, {}, HTTP_ACCEPT='application/json') response = non_staff_client.get(index_url, {}, HTTP_ACCEPT='application/json')
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 403)
def test_content_type_none(self):
"""
Test json content type is set if none is selected
"""
index_url = reverse_course_url('course_search_index_handler', self.course.id)
response = self.client.get(index_url, {}, CONTENT_TYPE=None)
# A course with the default release date should display as "Unscheduled"
self.assertIn(self.SUCCESSFUL_RESPONSE, response.content)
self.assertEqual(response.status_code, 200)
@mock.patch('xmodule.html_module.HtmlDescriptor.index_dictionary')
def test_reindex_course_search_index_error(self, mock_index_dictionary):
"""
Test json response with mocked error data for html
"""
# set mocked exception response
err = SearchIndexingError
mock_index_dictionary.return_value = err
index_url = reverse_course_url('course_search_index_handler', self.course.id)
# Start manual reindex and check error in response
response = self.client.get(index_url, {}, HTTP_ACCEPT='application/json')
self.assertEqual(response.status_code, 500)
def test_reindex_json_responses(self): def test_reindex_json_responses(self):
""" """
Test json response with real data Test json response with real data
...@@ -455,7 +485,7 @@ class TestCourseReIndex(CourseTestCase): ...@@ -455,7 +485,7 @@ class TestCourseReIndex(CourseTestCase):
self.assertEqual(response['results'], []) self.assertEqual(response['results'], [])
# set mocked exception response # set mocked exception response
err = Exception err = SearchIndexingError
mock_index_dictionary.return_value = err mock_index_dictionary.return_value = err
# Start manual reindex and check error in response # Start manual reindex and check error in response
...@@ -465,7 +495,7 @@ class TestCourseReIndex(CourseTestCase): ...@@ -465,7 +495,7 @@ class TestCourseReIndex(CourseTestCase):
@mock.patch('xmodule.html_module.HtmlDescriptor.index_dictionary') @mock.patch('xmodule.html_module.HtmlDescriptor.index_dictionary')
def test_reindex_html_error_json_responses(self, mock_index_dictionary): def test_reindex_html_error_json_responses(self, mock_index_dictionary):
""" """
Test json response with rmocked error data for html Test json response with mocked error data for html
""" """
# Check results not indexed # Check results not indexed
response = perform_search( response = perform_search(
...@@ -477,7 +507,7 @@ class TestCourseReIndex(CourseTestCase): ...@@ -477,7 +507,7 @@ class TestCourseReIndex(CourseTestCase):
self.assertEqual(response['results'], []) self.assertEqual(response['results'], [])
# set mocked exception response # set mocked exception response
err = Exception err = SearchIndexingError
mock_index_dictionary.return_value = err mock_index_dictionary.return_value = err
# Start manual reindex and check error in response # Start manual reindex and check error in response
...@@ -487,7 +517,7 @@ class TestCourseReIndex(CourseTestCase): ...@@ -487,7 +517,7 @@ class TestCourseReIndex(CourseTestCase):
@mock.patch('xmodule.seq_module.SequenceDescriptor.index_dictionary') @mock.patch('xmodule.seq_module.SequenceDescriptor.index_dictionary')
def test_reindex_seq_error_json_responses(self, mock_index_dictionary): def test_reindex_seq_error_json_responses(self, mock_index_dictionary):
""" """
Test json response with rmocked error data for sequence Test json response with mocked error data for sequence
""" """
# Check results not indexed # Check results not indexed
response = perform_search( response = perform_search(
......
define(["jquery", "js/common_helpers/ajax_helpers", "js/views/utils/view_utils", "js/views/pages/course_outline", define(["jquery", "sinon", "js/common_helpers/ajax_helpers", "js/views/utils/view_utils", "js/views/pages/course_outline",
"js/models/xblock_outline_info", "js/utils/date_utils", "js/spec_helpers/edit_helpers", "js/models/xblock_outline_info", "js/utils/date_utils", "js/spec_helpers/edit_helpers",
"js/common_helpers/template_helpers"], "js/common_helpers/template_helpers"],
function($, AjaxHelpers, ViewUtils, CourseOutlinePage, XBlockOutlineInfo, DateUtils, EditHelpers, TemplateHelpers) { function($, Sinon, AjaxHelpers, ViewUtils, CourseOutlinePage, XBlockOutlineInfo, DateUtils, EditHelpers, TemplateHelpers) {
describe("CourseOutlinePage", function() { describe("CourseOutlinePage", function() {
var createCourseOutlinePage, displayNameInput, model, outlinePage, requests, var createCourseOutlinePage, displayNameInput, model, outlinePage, requests,
...@@ -90,16 +90,16 @@ define(["jquery", "js/common_helpers/ajax_helpers", "js/views/utils/view_utils", ...@@ -90,16 +90,16 @@ define(["jquery", "js/common_helpers/ajax_helpers", "js/views/utils/view_utils",
createMockIndexJSON = function(option) { createMockIndexJSON = function(option) {
if(option){ if(option){
return { return JSON.stringify({
status: 200, "developer_message" : "Course has been successfully reindexed.",
responseText: '' "user_message": "Course has been successfully reindexed."
}; });
} }
else { else {
return { return JSON.stringify({
status: 500, "developer_message" : "Could not reindex course.",
responseText: JSON.stringify('Could not index item: course/slashes:mock+item') "user_message": "Could not reindex course."
}; });
} }
}; };
...@@ -324,12 +324,12 @@ define(["jquery", "js/common_helpers/ajax_helpers", "js/views/utils/view_utils", ...@@ -324,12 +324,12 @@ define(["jquery", "js/common_helpers/ajax_helpers", "js/views/utils/view_utils",
verifyItemsExpanded('section', true); verifyItemsExpanded('section', true);
}); });
it('can start reindex of a course - respond success', function() { it('can start reindex of a course', function() {
createCourseOutlinePage(this, mockSingleSectionCourseJSON); createCourseOutlinePage(this, mockSingleSectionCourseJSON);
var reindexSpy = spyOn(outlinePage, 'startReIndex').andCallThrough(); var reindexSpy = spyOn(outlinePage, 'startReIndex').andCallThrough();
var successSpy = spyOn(outlinePage, 'onIndexSuccess').andCallThrough(); var successSpy = spyOn(outlinePage, 'onIndexSuccess').andCallThrough();
var reindexButton = outlinePage.$('.button.button-reindex'); var reindexButton = outlinePage.$('.button.button-reindex');
var test_url = '/course_search_index/5'; var test_url = '/course/5/search_reindex';
reindexButton.attr('href', test_url) reindexButton.attr('href', test_url)
reindexButton.trigger('click'); reindexButton.trigger('click');
AjaxHelpers.expectJsonRequest(requests, 'GET', test_url); AjaxHelpers.expectJsonRequest(requests, 'GET', test_url);
...@@ -338,16 +338,18 @@ define(["jquery", "js/common_helpers/ajax_helpers", "js/views/utils/view_utils", ...@@ -338,16 +338,18 @@ define(["jquery", "js/common_helpers/ajax_helpers", "js/views/utils/view_utils",
expect(successSpy).toHaveBeenCalled(); expect(successSpy).toHaveBeenCalled();
}); });
it('can start reindex of a course - respond fail', function() { it('shows an error message when reindexing fails', function() {
createCourseOutlinePage(this, mockSingleSectionCourseJSON); createCourseOutlinePage(this, mockSingleSectionCourseJSON);
var reindexSpy = spyOn(outlinePage, 'startReIndex').andCallThrough(); var reindexSpy = spyOn(outlinePage, 'startReIndex').andCallThrough();
var errorSpy = spyOn(outlinePage, 'onIndexError').andCallThrough();
var reindexButton = outlinePage.$('.button.button-reindex'); var reindexButton = outlinePage.$('.button.button-reindex');
var test_url = '/course_search_index/5'; var test_url = '/course/5/search_reindex';
reindexButton.attr('href', test_url) reindexButton.attr('href', test_url)
reindexButton.trigger('click'); reindexButton.trigger('click');
AjaxHelpers.expectJsonRequest(requests, 'GET', test_url); AjaxHelpers.expectJsonRequest(requests, 'GET', test_url);
AjaxHelpers.respondWithJson(requests, createMockIndexJSON(false)); AjaxHelpers.respondWithError(requests, 500, createMockIndexJSON(false));
expect(reindexSpy).toHaveBeenCalled(); expect(reindexSpy).toHaveBeenCalled();
expect(errorSpy).toHaveBeenCalled();
}); });
}); });
......
...@@ -2,8 +2,9 @@ ...@@ -2,8 +2,9 @@
* This page is used to show the user an outline of the course. * This page is used to show the user an outline of the course.
*/ */
define(["jquery", "underscore", "gettext", "js/views/pages/base_page", "js/views/utils/xblock_utils", define(["jquery", "underscore", "gettext", "js/views/pages/base_page", "js/views/utils/xblock_utils",
"js/views/course_outline", "js/views/utils/view_utils", "js/views/feedback_alert"], "js/views/course_outline", "js/views/utils/view_utils", "js/views/feedback_alert",
function ($, _, gettext, BasePage, XBlockViewUtils, CourseOutlineView, ViewUtils, AlertView) { "js/views/feedback_notification"],
function ($, _, gettext, BasePage, XBlockViewUtils, CourseOutlineView, ViewUtils, AlertView, NoteView) {
var expandedLocators, CourseOutlinePage; var expandedLocators, CourseOutlinePage;
CourseOutlinePage = BasePage.extend({ CourseOutlinePage = BasePage.extend({
...@@ -111,21 +112,33 @@ define(["jquery", "underscore", "gettext", "js/views/pages/base_page", "js/views ...@@ -111,21 +112,33 @@ define(["jquery", "underscore", "gettext", "js/views/pages/base_page", "js/views
var target = $(event.currentTarget); var target = $(event.currentTarget);
target.css('cursor', 'wait'); target.css('cursor', 'wait');
this.startReIndex(target.attr('href')) this.startReIndex(target.attr('href'))
.done(function() {self.onIndexSuccess();}) .done(function(data) {self.onIndexSuccess(data);})
.fail(function(data) {self.onIndexError(data);})
.always(function() {target.css('cursor', 'pointer');}); .always(function() {target.css('cursor', 'pointer');});
}, },
startReIndex: function(reindex_url) { startReIndex: function(reindex_url) {
return $.ajax({ return $.ajax({
url: reindex_url, url: reindex_url,
method: 'GET' method: 'GET',
global: false,
contentType: "application/json; charset=utf-8",
dataType: "json"
}); });
}, },
onIndexSuccess: function() { onIndexSuccess: function(data) {
var msg = new AlertView.Announcement({ var msg = new AlertView.Announcement({
title: gettext('Course Index'), title: gettext('Course Index'),
message: gettext('Course has been successfully reindexed.') message: data.user_message
});
msg.show();
},
onIndexError: function(data) {
var msg = new NoteView.Error({
title: gettext('There were errors reindexing course.'),
message: data.user_message
}); });
msg.show(); msg.show();
} }
......
...@@ -81,7 +81,7 @@ urlpatterns += patterns( ...@@ -81,7 +81,7 @@ urlpatterns += patterns(
), ),
url(r'^home/$', 'course_listing', name='home'), url(r'^home/$', 'course_listing', name='home'),
url( url(
r'^course_search_index/{}?$'.format(settings.COURSE_KEY_PATTERN), r'^course/{}/search_reindex?$'.format(settings.COURSE_KEY_PATTERN),
'course_search_index_handler', 'course_search_index_handler',
name='course_search_index_handler' name='course_search_index_handler'
), ),
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment