def test_get_all_message_id_list_paged(self, get_http_mock): """ Test the GmailConnector in retrieving all message id's without errors on the API call. Messages are paged and need two API calls. """ mock_api_calls = [ # Retrieve the history_id. HttpMock('lily/messaging/email/tests/data/get_history_id.json', {'status': '200'}), # Retrieve a list of all the messages in the email box. HttpMock( 'lily/messaging/email/tests/data/all_message_id_list_paged_1.json', {'status': '200'}), HttpMock( 'lily/messaging/email/tests/data/all_message_id_list_paged_2.json', {'status': '200'}), ] # Mock the http instance with succesive http mock objects. get_http_mock.side_effect = mock_api_calls email_account = EmailAccount.objects.first() # Retrieve all messages. connector = GmailConnector(email_account) messages = connector.get_all_message_id_list() # Verify that all messages are retrieved and that the history id is set correct, self.assertEqual( len(messages), 10, "{0} Messages found, it should be {1}.".format(len(messages), 10)) self.assertEqual( connector.history_id, u'8095', "History id {0} is incorrect, it should be {1}.".format( connector.history_id, 8095))
def test_execute_service_call_http_access_token_refresh_error( self, execute_service_mock): """ Test if the execute service call raises an exception after it isn't able to get a correct access token and correctly deauthorizes the email account. """ # Let the mocked execute service call raise an error. execute_service_mock.side_effect = HttpAccessTokenRefreshError() email_account = EmailAccount.objects.first() connector = GmailConnector(email_account) try: # Execute service call. connector.execute_service_call( connector.gmail_service.service.users().messages().list( userId='me', quotaUser=email_account.id, q='!in:chats', )) self.fail('HttpAccessTokenRefreshError should have been raised.') except HttpAccessTokenRefreshError: pass self.assertFalse(email_account.is_authorized, "Email account shouldn't be authorized.")
def test_execute_service_call(self, get_http_mock): """ Test if the execute service call returns the content of the json file. """ # Mock the http instance. get_http_mock.return_value = HttpMock( 'lily/messaging/email/tests/data/all_message_id_list_single_page.json', {'status': '200'}) email_account = EmailAccount.objects.first() connector = GmailConnector(email_account) # Execute service call. response = connector.execute_service_call( connector.gmail_service.service.users().messages().list( userId='me', quotaUser=email_account.id, q='!in:chats', )) # Verify that the service call returned the correct json object. with open( 'lily/messaging/email/tests/data/all_message_id_list_single_page.json' ) as infile: json_obj = json.load(infile) self.assertEqual(response, json_obj)
def test_get_all_message_id_list_paged(self, get_http_mock): """ Test the GmailConnector in retrieving all message id's without errors on the API call. Messages are paged and need two API calls. """ mock_api_calls = [ # Retrieve the history_id. HttpMock('lily/messaging/email/tests/data/get_history_id.json', {'status': '200'}), # Retrieve a list of all the messages in the email box. HttpMock('lily/messaging/email/tests/data/all_message_id_list_paged_1.json', {'status': '200'}), HttpMock('lily/messaging/email/tests/data/all_message_id_list_paged_2.json', {'status': '200'}), ] # Mock the http instance with succesive http mock objects. get_http_mock.side_effect = mock_api_calls email_account = EmailAccount.objects.first() # Retrieve all messages. connector = GmailConnector(email_account) messages = connector.get_all_message_id_list() # Verify that all messages are retrieved and that the history id is set correct, self.assertEqual(len(messages), 10, "{0} Messages found, it should be {1}.".format(len(messages), 10)) self.assertEqual(connector.history_id, u'8095', "History id {0} is incorrect, it should be {1}.".format(connector.history_id, 8095))
def test_execute_service_call_rate_limit_exceeded_once( self, get_http_mock): """ Test if the execute service call returns the content of the json file after one rate limit error. """ mock_api_calls = [ # Simulate one rateLimitExceeded error. HttpMock('lily/messaging/email/tests/data/403.json', {'status': '403'}), HttpMock( 'lily/messaging/email/tests/data/all_message_id_list_single_page.json', {'status': '200'}), ] # Mock the http instance with succesive http mock objects. get_http_mock.side_effect = mock_api_calls email_account = EmailAccount.objects.first() connector = GmailConnector(email_account) # Execute service call. response = connector.execute_service_call( connector.gmail_service.service.users().messages().list( userId='me', quotaUser=email_account.id, q='!in:chats', )) # Verify that the service call returned the correct json object. with open( 'lily/messaging/email/tests/data/all_message_id_list_single_page.json' ) as infile: json_obj = json.load(infile) self.assertEqual(response, json_obj)
def test_execute_service_call_failed_service_call_exception(self, get_http_mock): """ Test if the execute service call raises an exception after it fails after six rate limit errors. """ mock_api_calls = [ # Simulate one FailedServiceCallException by getting six times a rateLimitExceeded error. HttpMock('lily/messaging/email/tests/data/403.json', {'status': '403'}), HttpMock('lily/messaging/email/tests/data/403.json', {'status': '403'}), HttpMock('lily/messaging/email/tests/data/403.json', {'status': '403'}), HttpMock('lily/messaging/email/tests/data/403.json', {'status': '403'}), HttpMock('lily/messaging/email/tests/data/403.json', {'status': '403'}), HttpMock('lily/messaging/email/tests/data/403.json', {'status': '403'}), ] # Mock the http instance with succesive http mock objects. get_http_mock.side_effect = mock_api_calls email_account = EmailAccount.objects.first() connector = GmailConnector(email_account) try: # Execute service call. connector.execute_service_call( connector.gmail_service.service.users().messages().list( userId='me', quotaUser=email_account.id, q='!in:chats', )) self.fail('FailedServiceCallException should have been raised.') except FailedServiceCallException: pass
def test_cleanup(self, get_http_mock): """ Test if the GmailConnector cleans up the right data. """ mock_api_calls = [ # Simulate one rateLimitExceeded error. HttpMock('lily/messaging/email/tests/data/get_history_id.json', {'status': '200'}), HttpMock('lily/messaging/email/tests/data/all_message_id_list_single_page.json', {'status': '200'}), ] get_http_mock.side_effect = mock_api_calls # Initialze a connector and retrieve the history id. email_account = EmailAccount.objects.first() connector = GmailConnector(email_account) connector.get_all_message_id_list() # Establish that the cleanup method up to test has actual data to cleanup. self.assertIsNotNone(connector.gmail_service) self.assertIsNotNone(connector.email_account) self.assertIsNotNone(connector.history_id) connector.cleanup() # Verify that data is cleaned up. self.assertIsNone(connector.gmail_service) self.assertIsNone(connector.email_account) self.assertIsNone(connector.history_id)
def test_execute_service_call_rate_limit_exceeded_once(self, get_http_mock): """ Test if the execute service call returns the content of the json file after one rate limit error. """ mock_api_calls = [ # Simulate one rateLimitExceeded error. HttpMock('lily/messaging/email/tests/data/403.json', {'status': '403'}), HttpMock('lily/messaging/email/tests/data/all_message_id_list_single_page.json', {'status': '200'}), ] # Mock the http instance with succesive http mock objects. get_http_mock.side_effect = mock_api_calls email_account = EmailAccount.objects.first() connector = GmailConnector(email_account) # Execute service call. response = connector.execute_service_call( connector.gmail_service.service.users().messages().list( userId='me', quotaUser=email_account.id, q='!in:chats', )) # Verify that the service call returned the correct json object. with open('lily/messaging/email/tests/data/all_message_id_list_single_page.json') as infile: json_obj = json.load(infile) self.assertEqual(response, json_obj)
def test_send_email_message_reply(self, get_http_mock): """ Test the GmailConnector on replying on an email message. """ message_id = '15af6279e8b72e9c' get_http_mock.return_value = HttpMock( 'lily/messaging/email/tests/data/send_email_message_reply.json', {'status': '200'}) email_account = EmailAccount.objects.first() email_outbox_message = """Content-Type: multipart/related; boundary="===============0529811256475331541==" MIME-Version: 1.0 Subject: Mauris ex tortor, hendrerit non sem eu, mollis varius purus. From: "Firstname Lastname" <*****@*****.**> To: [email protected] --===============0529811256475331541== Content-Type: multipart/alternative; boundary="===============6835128886458232912==" MIME-Version: 1.0 --===============6835128886458232912== MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit In hac habitasse platea dictumst. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Ut aliquet elit sed augue bibendum malesuada. --===============6835128886458232912== MIME-Version: 1.0 Content-Type: text/html; charset="utf-8" Content-Transfer-Encoding: 7bit <html><body><br/>In hac habitasse platea dictumst. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Ut aliquet elit sed augue bibendum malesuada.</body></html> --===============6835128886458232912==-- --===============0529811256475331541==--""" connector = GmailConnector(email_account) response = connector.send_email_message(email_outbox_message, message_id) # Verify that the service call returned the correct json object. with open( 'lily/messaging/email/tests/data/send_email_message_reply.json' ) as infile: json_obj = json.load(infile) self.assertEqual(response, json_obj)
def test_send_email_message_reply(self, get_http_mock): """ Test the GmailConnector on replying on an email message. """ message_id = '15af6279e8b72e9c' get_http_mock.return_value = HttpMock('lily/messaging/email/tests/data/send_email_message_reply.json', {'status': '200'}) email_account = EmailAccount.objects.first() email_outbox_message = """Content-Type: multipart/related; boundary="===============0529811256475331541==" MIME-Version: 1.0 Subject: Mauris ex tortor, hendrerit non sem eu, mollis varius purus. From: "Firstname Lastname" <*****@*****.**> To: [email protected] --===============0529811256475331541== Content-Type: multipart/alternative; boundary="===============6835128886458232912==" MIME-Version: 1.0 --===============6835128886458232912== MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit In hac habitasse platea dictumst. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Ut aliquet elit sed augue bibendum malesuada. --===============6835128886458232912== MIME-Version: 1.0 Content-Type: text/html; charset="utf-8" Content-Transfer-Encoding: 7bit <html><body><br/>In hac habitasse platea dictumst. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Ut aliquet elit sed augue bibendum malesuada.</body></html> --===============6835128886458232912==-- --===============0529811256475331541==--""" connector = GmailConnector(email_account) response = connector.send_email_message(email_outbox_message, message_id) # Verify that the service call returned the correct json object. with open('lily/messaging/email/tests/data/send_email_message_reply.json') as infile: json_obj = json.load(infile) self.assertEqual(response, json_obj)
def test_get_history_id(self, get_http_mock): """ Test the GmailConnector in retrieving the history id. """ get_http_mock.return_value = HttpMock('lily/messaging/email/tests/data/get_history_id.json', {'status': '200'}) email_account = EmailAccount.objects.first() connector = GmailConnector(email_account) response = connector.get_history_id() # Verify that the service call returned the correct json object. with open('lily/messaging/email/tests/data/get_history_id.json') as infile: json_obj = json.load(infile) self.assertEqual(response, json_obj)
def test_trash_email_message(self, get_http_mock): """ Test the GmailConnector on trashing an email message. """ message_id = '15af6279f554fd15' get_http_mock.return_value = HttpMock( 'lily/messaging/email/tests/data/trash_email_message_{0}.json'.format(message_id), {'status': '200'}) email_account = EmailAccount.objects.first() connector = GmailConnector(email_account) response = connector.trash_email_message(message_id) # Verify that the service call returned the correct json object. with open('lily/messaging/email/tests/data/trash_email_message_{0}.json'.format(message_id)) as infile: json_obj = json.load(infile) self.assertEqual(response, json_obj)
def test_save_history_id(self, get_http_mock): """ Test the GmailConnector in saving the updated historty id to the email account. """ get_http_mock.return_value = HttpMock('lily/messaging/email/tests/data/get_history_archived.json', {'status': '200'}) email_account = EmailAccount.objects.first() connector = GmailConnector(email_account) # First do a history update, so a new history id is retreived. connector.get_history() # Save the history id to the email account. connector.save_history_id() # Verify that the history is indeed saved on the email account. email_account.refresh_from_db() self.assertEqual(email_account.history_id, 8170)
def test_extracting_attachment(self, get_message_info_mock, get_attachment_mock): message_id = '16740205f39700d1' self.mock_get_message_info(get_message_info_mock, message_id) self.mock_get_attachment(get_attachment_mock, message_id) connector = GmailConnector(self.email_account) message_info = connector.get_message_info(message_id) payload = message_info['payload'] body_html = get_body_html_from_payload(payload, message_id) attachments = get_attachments_from_payload(payload, body_html, message_id, connector) self.email_message.attachments.add(bulk=False, *attachments) get_message_info_mock.assert_called_once() get_attachment_mock.assert_called_once() self.assertEqual(len(attachments), 1)
def test_get_history_id(self, get_http_mock): """ Test the GmailConnector in retrieving the history id. """ get_http_mock.return_value = HttpMock( 'lily/messaging/email/tests/data/get_history_id.json', {'status': '200'}) email_account = EmailAccount.objects.first() connector = GmailConnector(email_account) response = connector.get_history_id() # Verify that the service call returned the correct json object. with open('lily/messaging/email/tests/data/get_history_id.json' ) as infile: json_obj = json.load(infile) self.assertEqual(response, json_obj)
def test_get_message_info(self, get_http_mock): """ Test the GmailConnector in retrieving the info of a single email message. """ get_http_mock.return_value = HttpMock('lily/messaging/email/tests/data/get_message_info_15a6008a4baa65f3.json', {'status': '200'}) email_account = EmailAccount.objects.first() connector = GmailConnector(email_account) response = connector.get_message_info('15a6008a4baa65f3') # Verify that the service call returned the correct json object. with open('lily/messaging/email/tests/data/get_message_info_15a6008a4baa65f3.json') as infile: json_obj = json.load(infile) self.assertEqual(response, json_obj) # Verify that the history id is not retrieved from the get API response. self.assertEqual(connector.history_id, None)
def test_get_short_message_info(self, get_http_mock): """ Test the GmailConnector in retrieving the short message info for a specific email message. """ message_id = '15a6008a4baa65f3' get_http_mock.return_value = HttpMock( 'lily/messaging/email/tests/data/get_short_message_info_{0}_archived.json' .format(message_id), {'status': '200'}) email_account = EmailAccount.objects.first() connector = GmailConnector(email_account) response = connector.get_short_message_info(message_id) # Verify that the service call returned the correct json object. with open( 'lily/messaging/email/tests/data/get_short_message_info_{0}_archived.json' .format(message_id)) as infile: json_obj = json.load(infile) self.assertEqual(response, json_obj)
def test_get_labels_and_thread_id_for_message_id(self, get_http_mock): """ Test the GmailConnector in retrieving the short message info for a specific email message. """ message_id = '15a6008a4baa65f3' get_http_mock.return_value = HttpMock( 'lily/messaging/email/tests/data/' 'get_labels_and_thread_id_for_message_id_{0}_archived.json'.format(message_id), {'status': '200'}) email_account = EmailAccount.objects.first() connector = GmailConnector(email_account) response = connector.get_labels_and_thread_id_for_message_id(message_id) # Verify that the service call returned the correct json object. with open('lily/messaging/email/tests/data/get_labels_and_thread_id_for_message_id_{0}_archived.json'.format( message_id)) as infile: json_obj = json.load(infile) self.assertEqual(response, json_obj)
def test_trash_email_message(self, get_http_mock): """ Test the GmailConnector on trashing an email message. """ message_id = '15af6279f554fd15' get_http_mock.return_value = HttpMock( 'lily/messaging/email/tests/data/trash_email_message_{0}.json'. format(message_id), {'status': '200'}) email_account = EmailAccount.objects.first() connector = GmailConnector(email_account) response = connector.trash_email_message(message_id) # Verify that the service call returned the correct json object. with open( 'lily/messaging/email/tests/data/trash_email_message_{0}.json'. format(message_id)) as infile: json_obj = json.load(infile) self.assertEqual(response, json_obj)
def test_get_all_message_id_list_http_access_token_refresh_error(self, execute_service_call_mock): """ Test the GmailConnector in retrieving all message id's with a HttpAccessTokenRefreshError on the API call. """ execute_service_call_mock.side_effect = HttpAccessTokenRefreshError() email_account = EmailAccount.objects.first() messages = None connector = GmailConnector(email_account) # Retrieve all messages. try: messages = connector.get_all_message_id_list() self.fail('HttpAccessTokenRefreshError should have been raised.') except HttpAccessTokenRefreshError: pass # Verify that no messages are retrieved and that the history id is not set, self.assertIsNone(messages) self.assertIsNone(connector.history_id)
def test_get_all_message_id_list_failed_service_call_error(self, execute_service_call_mock): """ Test the GmailConnector in retrieving all message id's with a FailedServiceCallException on the API call. """ execute_service_call_mock.side_effect = FailedServiceCallException() email_account = EmailAccount.objects.first() connector = GmailConnector(email_account) # Retrieve all messages. messages = None try: messages = connector.get_all_message_id_list() self.fail('FailedServiceCallException should have been raised.') except FailedServiceCallException: pass # Verify that no messages are retrieved and that the history id is not set, self.assertIsNone(messages) self.assertIsNone(connector.history_id)
def test_cleanup(self, get_http_mock): """ Test if the GmailConnector cleans up the right data. """ mock_api_calls = [ # Simulate one rateLimitExceeded error. HttpMock('lily/messaging/email/tests/data/get_history_id.json', {'status': '200'}), HttpMock( 'lily/messaging/email/tests/data/all_message_id_list_single_page.json', {'status': '200'}), ] get_http_mock.side_effect = mock_api_calls # Initialze a connector and retrieve the history id. email_account = EmailAccount.objects.first() connector = GmailConnector(email_account) connector.get_all_message_id_list() # Establish that the cleanup method up to test has actual data to cleanup. self.assertIsNotNone(connector.gmail_service) self.assertIsNotNone(connector.email_account) self.assertIsNotNone(connector.history_id) connector.cleanup() # Verify that data is cleaned up. self.assertIsNone(connector.gmail_service) self.assertIsNone(connector.email_account) self.assertIsNone(connector.history_id)
def test_get_message_info(self, get_http_mock): """ Test the GmailConnector in retrieving the info of a single email message. """ get_http_mock.return_value = HttpMock( 'lily/messaging/email/tests/data/get_message_info_15a6008a4baa65f3.json', {'status': '200'}) email_account = EmailAccount.objects.first() connector = GmailConnector(email_account) response = connector.get_message_info('15a6008a4baa65f3') # Verify that the service call returned the correct json object. with open( 'lily/messaging/email/tests/data/get_message_info_15a6008a4baa65f3.json' ) as infile: json_obj = json.load(infile) self.assertEqual(response, json_obj) # Verify that the history id is not retrieved from the get API response. self.assertEqual(connector.history_id, None)
def test_get_all_message_id_list_failed_service_call_error( self, execute_service_call_mock): """ Test the GmailConnector in retrieving all message id's with a FailedServiceCallException on the API call. """ execute_service_call_mock.side_effect = FailedServiceCallException() email_account = EmailAccount.objects.first() connector = GmailConnector(email_account) # Retrieve all messages. messages = None try: messages = connector.get_all_message_id_list() self.fail('FailedServiceCallException should have been raised.') except FailedServiceCallException: pass # Verify that no messages are retrieved and that the history id is not set, self.assertIsNone(messages) self.assertIsNone(connector.history_id)
def test_get_all_message_id_list_http_access_token_refresh_error( self, execute_service_call_mock): """ Test the GmailConnector in retrieving all message id's with a HttpAccessTokenRefreshError on the API call. """ execute_service_call_mock.side_effect = HttpAccessTokenRefreshError() email_account = EmailAccount.objects.first() messages = None connector = GmailConnector(email_account) # Retrieve all messages. try: messages = connector.get_all_message_id_list() self.fail('HttpAccessTokenRefreshError should have been raised.') except HttpAccessTokenRefreshError: pass # Verify that no messages are retrieved and that the history id is not set, self.assertIsNone(messages) self.assertIsNone(connector.history_id)
def test_execute_service_call_failed_service_call_exception( self, get_http_mock): """ Test if the execute service call raises an exception after it fails after six rate limit errors. """ mock_api_calls = [ # Simulate one FailedServiceCallException by getting six times a rateLimitExceeded error. HttpMock('lily/messaging/email/tests/data/403.json', {'status': '403'}), HttpMock('lily/messaging/email/tests/data/403.json', {'status': '403'}), HttpMock('lily/messaging/email/tests/data/403.json', {'status': '403'}), HttpMock('lily/messaging/email/tests/data/403.json', {'status': '403'}), HttpMock('lily/messaging/email/tests/data/403.json', {'status': '403'}), HttpMock('lily/messaging/email/tests/data/403.json', {'status': '403'}), ] # Mock the http instance with succesive http mock objects. get_http_mock.side_effect = mock_api_calls email_account = EmailAccount.objects.first() connector = GmailConnector(email_account) try: # Execute service call. connector.execute_service_call( connector.gmail_service.service.users().messages().list( userId='me', quotaUser=email_account.id, q='!in:chats', )) self.fail('FailedServiceCallException should have been raised.') except FailedServiceCallException: pass
def test_execute_service_call_http_access_token_refresh_error(self, execute_service_mock): """ Test if the execute service call raises an exception after it isn't able to get a correct access token and correctly deauthorizes the email account. """ # Let the mocked execute service call raise an error. execute_service_mock.side_effect = HttpAccessTokenRefreshError() email_account = EmailAccount.objects.first() connector = GmailConnector(email_account) try: # Execute service call. connector.execute_service_call( connector.gmail_service.service.users().messages().list( userId='me', quotaUser=email_account.id, q='!in:chats', )) self.fail('HttpAccessTokenRefreshError should have been raised.') except HttpAccessTokenRefreshError: pass self.assertFalse(email_account.is_authorized, "Email account shouldn't be authorized.")
def test_execute_service_call(self, get_http_mock): """ Test if the execute service call returns the content of the json file. """ # Mock the http instance. get_http_mock.return_value = HttpMock('lily/messaging/email/tests/data/all_message_id_list_single_page.json', {'status': '200'}) email_account = EmailAccount.objects.first() connector = GmailConnector(email_account) # Execute service call. response = connector.execute_service_call( connector.gmail_service.service.users().messages().list( userId='me', quotaUser=email_account.id, q='!in:chats', )) # Verify that the service call returned the correct json object. with open('lily/messaging/email/tests/data/all_message_id_list_single_page.json') as infile: json_obj = json.load(infile) self.assertEqual(response, json_obj)
def test_save_history_id(self, get_http_mock): """ Test the GmailConnector in saving the updated historty id to the email account. """ get_http_mock.return_value = HttpMock( 'lily/messaging/email/tests/data/get_history_archived.json', {'status': '200'}) email_account = EmailAccount.objects.first() connector = GmailConnector(email_account) # First do a history update, so a new history id is retreived. connector.get_history() # Save the history id to the email account. connector.save_history_id() # Verify that the history is indeed saved on the email account. email_account.refresh_from_db() self.assertEqual(email_account.history_id, 8170)
def get(self, request, format=None): with tracer.trace('SearchView.get: parameter setup'): user = request.user # Search query. q = request.query_params.get('q', '').strip() # Paging parameters. page = request.query_params.get('page', '0') page = int(page) + 1 size = int(request.query_params.get('size', '20')) sort = request.query_params.get('sort', '-sent_date') # Mail labeling parameters. label_id = request.query_params.get('label', None) account_ids = request.query_params.get('account', None) # None means search in all owned email accounts. with tracer.trace('SearchView.get: email_accounts setup'): # Get a list of all email accounts added by the user or publicly shared with the user as a group inbox. email_accounts = get_shared_email_accounts(user, True) if account_ids: # Only search within the email accounts indicated by the account_ids parameter. account_ids = account_ids.split(',') email_accounts = email_accounts.filter(pk__in=account_ids) email_accounts = email_accounts.exclude( Q(is_active=False) | Q(is_deleted=True) | Q(is_authorized=False) ) message_list = EmailMessage.objects if q: # System Gmail labels visible in Lily, where the user can search in. gmail_labels = [ settings.GMAIL_LABEL_INBOX, settings.GMAIL_LABEL_SPAM, settings.GMAIL_LABEL_TRASH, settings.GMAIL_LABEL_SENT, settings.GMAIL_LABEL_DRAFT ] if label_id and label_id not in gmail_labels: # Also retrieve related user set labels because later the label name will be queried. email_accounts = email_accounts.prefetch_related('labels') # Prevent too much calls on the search api, so restrict number of search results per email account. max_results = 3 * size messages_ids = [] with tracer.trace('SearchView.get: for all accounts'): for email_account in email_accounts: if label_id: with tracer.trace('SearchView.get: building q with label lookup'): # Retrieve the label corresponding to the label_id, otherwise Gmail defaults to all mail. label_name = label_id if label_id not in gmail_labels: # Retrieve the label name if label_id will differ from the user set label name. try: label_name = email_account.labels.get(label_id=label_id).name except EmailLabel.DoesNotExist: logger.error( "Incorrect label id {0} with search request for account {1}.".format( label_id, email_account ) ) # Failing label lookup within one account should not halt the complete search. continue q = u"{0} {1}:{2}".format(q, 'label', label_name) with tracer.trace('SearchView.get: retrieving message_ids by Gmail API'): try: connector = GmailConnector(email_account) messages = connector.search(query=q, size=max_results) messages_ids.extend([message['id'] for message in messages]) except (InvalidCredentialsError, NotFoundError, HttpAccessTokenRefreshError, FailedServiceCallException) as e: logger.error( "Failed to search within account {0} with error: {1}.".format(email_account, e) ) # Failing search within one account should not halt the complete search. continue # Retrieve messages from the database. message_list = message_list.filter( message_id__in=messages_ids, account__in=email_accounts ) with tracer.trace('SearchView.get: retrieving messages from db'): message_list = message_list.order_by(sort) # Exclude fields that aren't serialized and potential large. message_list = message_list.defer("body_html", "body_text", "snippet") # The serializer will query for account, sender and star label, so instead of the extra separate queries, # retrieve them now. message_list = message_list.select_related('account', 'sender') message_list = message_list.prefetch_related('labels', 'received_by') # It's possible Google search returns message_id's that aren't in the database (due to 'sync from now'). actual_number_of_results = len(message_list) with tracer.trace('SearchView.get: serializing messages'): # Construct paginator. limit = size * page offset = limit - size message_list = message_list[offset:limit] serializer = EmailMessageListSerializer(message_list, many=True) result = { 'hits': serializer.data, 'total': actual_number_of_results, } return Response(result)
def get(self, request, format=None): user = request.user # Search query. q = request.query_params.get('q', '').strip() # Paging parameters. page = request.query_params.get('page', '0') page = int(page) + 1 size = int(request.query_params.get('size', '20')) sort = request.query_params.get('sort', '-sent_date') # Mail labeling parameters. label_id = request.query_params.get('label', None) account_ids = request.query_params.get( 'account', None) # None means search in all owned email accounts. # Get a list of all email accounts added by the user or publicly shared with the user as a group inbox. email_accounts = get_shared_email_accounts(user, True) if account_ids: # Only search within the email accounts indicated by the account_ids parameter. account_ids = account_ids.split(',') email_accounts = email_accounts.filter(pk__in=account_ids) email_accounts = email_accounts.exclude( Q(is_active=False) | Q(is_deleted=True) | Q(is_authorized=False)) message_list = EmailMessage.objects if q: # System Gmail labels visible in Lily, where the user can search in. gmail_labels = [ settings.GMAIL_LABEL_INBOX, settings.GMAIL_LABEL_SPAM, settings.GMAIL_LABEL_TRASH, settings.GMAIL_LABEL_SENT, settings.GMAIL_LABEL_DRAFT ] if label_id and label_id not in gmail_labels: # Also retrieve related user set labels because later the label name will be queried. email_accounts = email_accounts.prefetch_related('labels') # Prevent too much calls on the search api, so restrict number of search results per email account. max_results = 3 * size messages_ids = [] for email_account in email_accounts: if label_id: # Retrieve the label corresponding to the label_id, otherwise Gmail defaults to all mail. label_name = label_id if label_id not in gmail_labels: # Retrieve the label name if label_id will differ from the user set label name. try: label_name = email_account.labels.get( label_id=label_id).name except EmailLabel.DoesNotExist: logger.error( "Incorrect label id {0} with search request for account {1}." .format(label_id, email_account)) # Failing label lookup within one account should not halt the complete search. continue q = u"{0} {1}:{2}".format(q, 'label', label_name) try: connector = GmailConnector(email_account) messages = connector.search(query=q, size=max_results) messages_ids.extend( [message['id'] for message in messages]) except (InvalidCredentialsError, NotFoundError, HttpAccessTokenRefreshError, FailedServiceCallException) as e: logger.error( "Failed to search within account {0} with error: {1}.". format(email_account, e)) # Failing search within one account should not halt the complete search. continue # Retrieve messages from the database. message_list = message_list.filter(message_id__in=messages_ids, account__in=email_accounts) message_list = message_list.order_by(sort) # Exclude fields that aren't serialized and potential large. message_list = message_list.defer("body_html", "body_text", "snippet") # The serializer will query for account, sender and star label, so instead of the extra separate queries, # retrieve them now. message_list = message_list.select_related('account', 'sender') message_list = message_list.prefetch_related('labels', 'received_by') # It's possible Google search returns message_id's that aren't in the database (due to 'sync from now'). actual_number_of_results = len(message_list) # Construct paginator. limit = size * page offset = limit - size message_list = message_list[offset:limit] serializer = EmailMessageListSerializer(message_list, many=True) result = { 'hits': serializer.data, 'total': actual_number_of_results, } return Response(result)