def test_canvas_section_error_sends_support_email( self, send_failure_msg_to_support, course_generation_job__objects__filter, course_generation_job__objects__create, update_course_generation_workflow_state, get_course_data, create_course_section, create_new_course, get_default_template_for_school): """ Test to assert that a support email is sent when there is a CanvasAPIError resulting in CanvasSectionCreateError. """ job = Mock(spec=CanvasCourseGenerationJob()) course_generation_job__objects__create.return_value = job query_set = Mock(get=Mock(return_value=job)) course_generation_job__objects__filter.return_value = query_set create_course_section.side_effect = CanvasAPIError(status_code=400) exception_data = CanvasSectionCreateError(self.sis_course_id) get_default_template_for_school.side_effect = NoTemplateExistsForSchool( self.school_id) with self.assertRaises(CanvasSectionCreateError): controller.create_canvas_course(self.sis_course_id, self.sis_user_id) send_failure_msg_to_support.assert_called_with( self.sis_course_id, self.sis_user_id, exception_data.display_text)
def test_404_exception_n_create_new_course_method_invokes_update_workflow_state_with_bulk_job_id( self, update_mock, course_generation_job__objects__filter, get_course_data, create_course_section, create_new_course, get_default_template_for_school): """ Test to assert that a CanvasCourseCreateError is raised when the create_new_course SDK call throws a CanvasAPIError, and update_content_migration_workflow_state is invoked to update the status to STATUS_SETUP_FAILED """ query_set = Mock(get=Mock(return_value=Mock( spec=CanvasCourseGenerationJob))) course_generation_job__objects__filter.return_value = query_set get_default_template_for_school.side_effect = NoTemplateExistsForSchool( self.school_id) create_new_course.side_effect = CanvasAPIError(status_code=404) with self.assertRaises(CanvasCourseCreateError): controller.create_canvas_course(self.sis_course_id, self.sis_user_id, bulk_job=self.bulk_job) update_mock.assert_called_with( self.sis_course_id, CanvasCourseGenerationJob.STATUS_SETUP_FAILED, course_job_id=None, bulk_job_id=self.bulk_job_id)
def test_instance_str_for_canvas_api_error(self): """ Test string representation of CanvasAPIError with custom attributes """ status = 404 error_msg = 'This is a test message' error_json = {'Some error json'} api_error = CanvasAPIError(status_code=status, msg=error_msg, error_json=error_json) self.assertEqual('%d: %s' % (status, error_msg), str(api_error))
def test_add_to_section_logs_exception_on_failure_status_code( self, log_replacement, add_to_section_replacement): """ Assert that logger is logging an exception when return from backend is status code 4XX """ request = self.add_to_section_stub() add_to_section_replacement.side_effect = CanvasAPIError() response = add_to_section(request) assert log_replacement.called self.assertEqual(len(json.loads(response.content)['failed']), 2)
def test_exception_when_create_new_course_method_raises_api_400( self, get_course_data, create_course_section, create_new_course, get_default_template_for_school): """ Test to assert that a CanvasCourseAlreadyExistsError is raised when the create_new_course method throws a CanvasAPIError """ get_default_template_for_school.side_effect = NoTemplateExistsForSchool( self.school_id) create_new_course.side_effect = CanvasAPIError(status_code=400) with self.assertRaises(CanvasCourseAlreadyExistsError): controller.create_canvas_course(self.sis_course_id, self.sis_user_id)
def test_exception_when_create_new_course_method_raises_api_404( self, send_failure_msg_to_support, get_course_data, create_course_section, create_new_course, get_default_template_for_school): """ Test to assert that a RenderableException is raised when the create_new_course SDK call throws an CanvasAPIError """ create_new_course.side_effect = CanvasAPIError(status_code=404) get_default_template_for_school.side_effect = NoTemplateExistsForSchool( self.school_id) with self.assertRaises(CanvasCourseCreateError): controller.create_canvas_course(self.sis_course_id, self.sis_user_id)
def test_canvas_course_already_exists_error_doesnt_set_support_notified( self, send_failure_msg_to_support, get_course_data, create_course_section, create_new_course, get_default_template_for_school): """ Test to assert that support_notified is NOT set on CanvasCourseAlreadyExistsError """ create_new_course.side_effect = CanvasAPIError(status_code=400) get_default_template_for_school.side_effect = NoTemplateExistsForSchool( self.school_id) with self.assertRaises(CanvasCourseAlreadyExistsError) as cm: controller.create_canvas_course(self.sis_course_id, self.sis_user_id) self.assertTrue('support_notified' not in cm.exception)
def test_canvas_course_exists_error_doesnt_send_support_email( self, send_failure_msg_to_support, get_course_data, create_course_section, create_new_course, get_default_template_for_school): """ Test to assert that a support email is NOT sent when canvas course already exists (there is a CanvasCourseAlreadyExistsError) """ create_new_course.side_effect = CanvasAPIError(status_code=400) get_default_template_for_school.side_effect = NoTemplateExistsForSchool( self.school_id) with self.assertRaises(CanvasCourseAlreadyExistsError): controller.create_canvas_course(self.sis_course_id, self.sis_user_id) self.assertFalse(send_failure_msg_to_support.called)
def _get_course(context, course_id): url = "{}/v1/courses/{}?include[]=syllabus_body&include[]=term".format( context.base_api_url, course_id) response = requests.get( url, headers={"Authorization": "Bearer {}".format(context.auth_token)}) if response.status_code != 200: if response.status_code == 401 and 'WWW-Authenticate' in response.headers: raise InvalidOAuthTokenError( "OAuth Token used to make request to %s is invalid" % response.url) raise CanvasAPIError( status_code=response.status_code, msg=unicode(response.json()), error_json=response.json(), ) return response.json()
def test_canvas_course_create_error_sets_support_notified( self, send_failure_msg_to_support, get_course_data, create_course_section, create_new_course, get_default_template_for_school): """ Test to assert that support_notified is set on CanvasCourseCreateError """ create_new_course.side_effect = CanvasAPIError(status_code=404) exception_data = CanvasCourseCreateError( msg_details=self.sis_course_id) get_default_template_for_school.side_effect = NoTemplateExistsForSchool( self.school_id) with self.assertRaises(CanvasCourseCreateError): controller.create_canvas_course(self.sis_course_id, self.sis_user_id) self.assertTrue(exception_data.support_notified)
def test_when_create_course_section_method_raises_api_error( self, course_generation_job__objects__create, update_course_generation_workflow_state, send_failure_msg_to_support, get_course_data, create_course_section, create_new_course, get_default_template_for_school): """ Test to assert that a RenderableException is raised when the create_course_section SDK call throws an CanvasAPIError """ job = Mock(spec=CanvasCourseGenerationJob()) course_generation_job__objects__create.return_value = job create_course_section.side_effect = CanvasAPIError(status_code=400) get_default_template_for_school.side_effect = NoTemplateExistsForSchool( self.school_id) with self.assertRaises(RenderableException): controller.create_canvas_course(self.sis_course_id, self.sis_user_id, None)
def test_canvas_course_create_error_doesnt_send_support_email_for_bulk_created_course( self, send_failure_msg_to_support, course_generation_job__objects__filter, update_course_generation_workflow_state, get_course_data, create_course_section, create_new_course, get_default_template_for_school): """ Test to assert that a for a course that is created as part of a bulk job, the support email is not sent when there is an CanvasAPIError resulting in CanvasCourseCreateError. """ query_set = Mock(get=Mock(return_value=Mock( spec=CanvasCourseGenerationJob))) course_generation_job__objects__filter.return_value = query_set create_new_course.side_effect = CanvasAPIError(status_code=404) get_default_template_for_school.side_effect = NoTemplateExistsForSchool( self.school_id) with self.assertRaises(CanvasCourseCreateError): controller.create_canvas_course(self.sis_course_id, self.sis_user_id, self.bulk_job) self.assertFalse(send_failure_msg_to_support.called)
def test_404_exception_n_create_new_course_method_invokes_update_workflow_state( self, update_mock, get_course_data, create_course_section, create_new_course, get_default_template_for_school): """ A RenderableException should be raised and and update_content_generation_workflow_state() is invoked when the create_new_course SDK call throws an CanvasAPIError """ get_default_template_for_school.side_effect = NoTemplateExistsForSchool( self.school_id) create_new_course.side_effect = CanvasAPIError(status_code=404) get_default_template_for_school.side_effect = NoTemplateExistsForSchool( self.school_id) with self.assertRaises(CanvasCourseCreateError): controller.create_canvas_course(self.sis_course_id, self.sis_user_id) update_mock.assert_called_with( self.sis_course_id, CanvasCourseGenerationJob.STATUS_SETUP_FAILED, course_job_id=ANY, bulk_job_id=None)
def test_canvas_course_create_error_sends_support_email( self, send_failure_msg_to_support, get_course_data, create_course_section, create_new_course, get_default_template_for_school): """ Test to assert that a support email is sent when there is an CanvasAPIError resulting in CanvasCourseCreateError and that the correct error message from the exception is sent as a param to the mail helper method """ create_new_course.side_effect = CanvasAPIError(status_code=404) exception_data = CanvasCourseCreateError( msg_details=self.sis_course_id) get_default_template_for_school.side_effect = NoTemplateExistsForSchool( self.school_id) with self.assertRaises(CanvasCourseCreateError): controller.create_canvas_course(self.sis_course_id, self.sis_user_id) send_failure_msg_to_support.assert_called_with( self.sis_course_id, self.sis_user_id, exception_data.display_text) self.assertTrue( 'Error: SIS ID not applied for CID' in exception_data.display_text)
def call(action, url, request_context, params=None, data=None, max_retries=None, auth_token=None, files=None, headers=None, cookies=None, timeout=None, proxies=None, verify=None, cert=None, allow_redirects=True): """This method servers as a pass-through to the requests library request functionality, but provides some configurable default values. Constructs and sends a :class:`requests.Request <Request>`. Returns :class:`requests.Response <Response>` object. :param action: method for the new :class:`Request` object. :param url: Absolute url path to API method :param request_context: Instance of :class:`RequestContext` that holds a request session and other default values :param params: (optional) Dictionary or bytes to be sent in the query string for the :class:`Request`. :param data: (optional) Dictionary, bytes, or file-like object to send in the body of the :class:`Request`. :param int max_retries: (optional) Number of times a request that generates a certain class of HTTP exception will be retried before being raised back to the caller. See :py:mod:`client.base` for a list of those error types. :param files: (optional) Dictionary of 'name': file-like-objects (or {'name': ('filename', fileobj)}) for multipart encoding upload. :param dictionary headers: (optional) dictionary of headers to send for each request. Will be merged with a default set of headers. :param cookies: (optional) Cookies to attach to each requests. :type cookies: dictionary or CookieJar :param str auth_token: (optional) OAuth2 token retrieved from a Canvas site :param float timeout: (optional) The timeout of the request in seconds. :param dictionary proxies: (optional) Mapping protocol to the URL of the proxy. :param verify: (optional) if ``True``, the SSL cert will be verified. A CA_BUNDLE path can also be provided. :type verify: boolean or str :param cert: (optional) if String, path to ssl client cert file (.pem). If Tuple, ('cert', 'key') pair. :type cert: str or Tuple :param bool allow_redirects: (optional) Set to True if POST/PUT/DELETE redirect following is allowed. Defaults to True. """ # This will be a requests.Session object with defaults set for context canvas_session = request_context.session retries = max_retries or request_context.max_retries # Default back to value in request_context if retries is None: retries = 0 # Fall back in case max_retries in context was explicitly set to None # Set up an authentication callable using OAuth2Bearer if a token was passed in auth = None if auth_token: auth = OAuth2Bearer(auth_token) # try the request until max_retries is reached. we need to account for the # fact that the first iteration through isn't a retry, so add 1 to max_retries for retry in range(retries + 1): try: # build and send the request response = canvas_session.request(action, url, params=params, data=data, headers=headers, cookies=cookies, files=files, auth=auth, timeout=timeout, proxies=proxies, verify=verify, cert=cert, allow_redirects=allow_redirects) # raise an http exception if one occured response.raise_for_status() except HTTPError as http_error: # Need to check its an error code that can be retried status_code = http_error.response.status_code # If we can't retry the request, raise a CanvasAPIError if status_code not in RETRY_ERROR_CODES or retry >= retries: error_json = http_error.response.json() raise CanvasAPIError( status_code=status_code, msg=str(error_json), error_json=error_json, ) else: return response
def mock_unauthorized_exception(self): exception = CanvasAPIError() exception.response = MagicMock() exception.status_code = 401 return exception
def test_redirect_on_API_auth_error(self, oauth_mock): with patch('edx2canvas.canvas_api.get_courses') as courses_mock: courses_mock.side_effect = CanvasAPIError(status_code=401) views.main(self.request) oauth_mock.assert_called_once_with(self.request, self.canvas_user_id)
def mock_api_exception(self): exception = CanvasAPIError() exception.response = MagicMock() return exception
def setUp(self): self.default_api_error = CanvasAPIError()