diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index 9dc2df42d..2854df87c 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -754,11 +754,11 @@ src/app/components/manage/mail/mail.component.html - 114 + 122 src/app/components/manage/mail/mail.component.html - 178 + 186 src/app/components/manage/management-list/management-list.component.html @@ -1177,15 +1177,15 @@ src/app/components/manage/mail/mail.component.html - 76 + 78 src/app/components/manage/mail/mail.component.html - 140 + 148 src/app/components/manage/mail/mail.component.html - 152 + 160 src/app/components/manage/management-list/management-list.component.html @@ -1656,7 +1656,7 @@ src/app/components/manage/mail/mail.component.html - 103 + 111 src/app/components/manage/management-list/management-list.component.html @@ -1776,7 +1776,7 @@ src/app/components/manage/mail/mail.component.html - 107 + 115 src/app/components/manage/management-list/management-list.component.html @@ -2073,15 +2073,15 @@ src/app/components/manage/mail/mail.component.html - 79 + 81 src/app/components/manage/mail/mail.component.html - 141 + 149 src/app/components/manage/mail/mail.component.html - 155 + 163 src/app/components/manage/management-list/management-list.component.html @@ -2209,7 +2209,7 @@ src/app/components/manage/mail/mail.component.ts - 259 + 270 src/app/components/manage/management-list/management-list.component.ts @@ -2396,15 +2396,15 @@ src/app/components/manage/mail/mail.component.html - 73 + 75 src/app/components/manage/mail/mail.component.html - 139 + 147 src/app/components/manage/mail/mail.component.html - 149 + 157 src/app/components/manage/management-list/management-list.component.html @@ -2548,7 +2548,7 @@ src/app/components/manage/mail/mail.component.ts - 261 + 272 src/app/components/manage/management-list/management-list.component.ts @@ -3778,7 +3778,7 @@ src/app/components/manage/mail/mail.component.html - 105 + 113 @@ -3800,7 +3800,7 @@ src/app/components/manage/mail/mail.component.html - 128 + 136 src/app/components/manage/workflows/workflows.component.html @@ -5394,11 +5394,11 @@ src/app/components/manage/mail/mail.component.html - 142 + 150 src/app/components/manage/mail/mail.component.html - 160 + 168 src/app/components/manage/workflows/workflows.component.html @@ -5800,7 +5800,7 @@ src/app/components/manage/mail/mail.component.html - 106 + 114 src/app/components/manage/workflows/workflows.component.html @@ -8078,39 +8078,50 @@ 31 + + Process Mail + + src/app/components/manage/mail/mail.component.html + 68 + + + src/app/components/manage/mail/mail.component.html + 86 + + No mail accounts defined. src/app/components/manage/mail/mail.component.html - 87 + 95 Mail rules src/app/components/manage/mail/mail.component.html - 95 + 103 Add Rule src/app/components/manage/mail/mail.component.html - 97 + 105 Sort Order src/app/components/manage/mail/mail.component.html - 104 + 112 Disabled src/app/components/manage/mail/mail.component.html - 128 + 136 src/app/components/manage/workflows/workflows.component.html @@ -8121,7 +8132,7 @@ No mail rules defined. src/app/components/manage/mail/mail.component.html - 169 + 177 @@ -8194,81 +8205,95 @@ 197 + + Processing mail account + + src/app/components/manage/mail/mail.component.ts + 208 + + + + Error processing mail account + + src/app/components/manage/mail/mail.component.ts + 211 + + Saved rule "". src/app/components/manage/mail/mail.component.ts - 216 + 227 Error saving rule. src/app/components/manage/mail/mail.component.ts - 227 + 238 Rule "" enabled. src/app/components/manage/mail/mail.component.ts - 243 + 254 Rule "" disabled. src/app/components/manage/mail/mail.component.ts - 244 + 255 Error toggling rule. src/app/components/manage/mail/mail.component.ts - 248 + 259 Confirm delete mail rule src/app/components/manage/mail/mail.component.ts - 257 + 268 This operation will permanently delete this mail rule. src/app/components/manage/mail/mail.component.ts - 258 + 269 Deleted mail rule src/app/components/manage/mail/mail.component.ts - 267 + 278 Error deleting mail rule. src/app/components/manage/mail/mail.component.ts - 276 + 287 Permissions updated src/app/components/manage/mail/mail.component.ts - 298 + 309 Error updating permissions src/app/components/manage/mail/mail.component.ts - 303 + 314 src/app/components/manage/management-list/management-list.component.ts diff --git a/src-ui/src/app/components/manage/mail/mail.component.html b/src-ui/src/app/components/manage/mail/mail.component.html index 9b6fe0423..fc786840c 100644 --- a/src-ui/src/app/components/manage/mail/mail.component.html +++ b/src-ui/src/app/components/manage/mail/mail.component.html @@ -65,19 +65,27 @@ + -
- - - +
diff --git a/src-ui/src/app/components/manage/mail/mail.component.spec.ts b/src-ui/src/app/components/manage/mail/mail.component.spec.ts index d3194459f..5f9a3c73a 100644 --- a/src-ui/src/app/components/manage/mail/mail.component.spec.ts +++ b/src-ui/src/app/components/manage/mail/mail.component.spec.ts @@ -219,6 +219,23 @@ describe('MailComponent', () => { expect(toastInfoSpy).toHaveBeenCalledWith('Deleted mail account') }) + it('should support process mail account, show error if needed', () => { + completeSetup() + const processSpy = jest.spyOn(mailAccountService, 'processAccount') + const toastErrorSpy = jest.spyOn(toastService, 'showError') + const toastInfoSpy = jest.spyOn(toastService, 'showInfo') + component.processAccount(mailAccounts[0] as MailAccount) + expect(processSpy).toHaveBeenCalled() + processSpy.mockReturnValueOnce( + throwError(() => new Error('error processing mail account')) + ) + component.processAccount(mailAccounts[0] as MailAccount) + expect(toastErrorSpy).toHaveBeenCalled() + processSpy.mockReturnValueOnce(of(true)) + component.processAccount(mailAccounts[0] as MailAccount) + expect(toastInfoSpy).toHaveBeenCalledWith('Processing mail account') + }) + it('should support edit / create mail rule, show error if needed', () => { completeSetup() let modal: NgbModalRef diff --git a/src-ui/src/app/components/manage/mail/mail.component.ts b/src-ui/src/app/components/manage/mail/mail.component.ts index dce23b4b6..c15ef28af 100644 --- a/src-ui/src/app/components/manage/mail/mail.component.ts +++ b/src-ui/src/app/components/manage/mail/mail.component.ts @@ -202,6 +202,17 @@ export class MailComponent }) } + processAccount(account: MailAccount) { + this.mailAccountService.processAccount(account).subscribe({ + next: () => { + this.toastService.showInfo($localize`Processing mail account`) + }, + error: (e) => { + this.toastService.showError($localize`Error processing mail account`, e) + }, + }) + } + editMailRule(rule: MailRule = null, forceCreate = false) { const modal = this.modalService.open(MailRuleEditDialogComponent, { backdrop: 'static', diff --git a/src-ui/src/app/services/rest/mail-account.service.spec.ts b/src-ui/src/app/services/rest/mail-account.service.spec.ts index c9d1da7d1..ef69b979e 100644 --- a/src-ui/src/app/services/rest/mail-account.service.spec.ts +++ b/src-ui/src/app/services/rest/mail-account.service.spec.ts @@ -68,6 +68,14 @@ describe(`Additional service tests for MailAccountService`, () => { expect(service.allAccounts).toEqual(mail_accounts) }) + it('should support processAccount', () => { + subscription = service.processAccount(mail_accounts[0]).subscribe() + const req = httpTestingController.expectOne( + `${environment.apiBaseUrl}${endpoint}/${mail_accounts[0].id}/process/` + ) + expect(req.request.method).toEqual('POST') + }) + beforeEach(() => { // Dont need to setup again diff --git a/src-ui/src/app/services/rest/mail-account.service.ts b/src-ui/src/app/services/rest/mail-account.service.ts index c5c2c79e0..e946b1399 100644 --- a/src-ui/src/app/services/rest/mail-account.service.ts +++ b/src-ui/src/app/services/rest/mail-account.service.ts @@ -47,4 +47,8 @@ export class MailAccountService extends AbstractPaperlessService { delete account['set_permissions'] return this.http.post(this.getResourceUrl() + 'test/', account) } + + processAccount(account: MailAccount) { + return this.http.post(this.getResourceUrl(account.id, 'process'), {}) + } } diff --git a/src/paperless_mail/tasks.py b/src/paperless_mail/tasks.py index 67bd620cf..df1f30d91 100644 --- a/src/paperless_mail/tasks.py +++ b/src/paperless_mail/tasks.py @@ -11,9 +11,14 @@ logger = logging.getLogger("paperless.mail.tasks") @shared_task -def process_mail_accounts(): +def process_mail_accounts(account_ids: list[int] | None = None) -> str: total_new_documents = 0 - for account in MailAccount.objects.all(): + accounts = ( + MailAccount.objects.filter(pk__in=account_ids) + if account_ids + else MailAccount.objects.all() + ) + for account in accounts: if not MailRule.objects.filter(account=account, enabled=True).exists(): logger.info(f"No rules enabled for account {account}. Skipping.") continue diff --git a/src/paperless_mail/tests/test_mail.py b/src/paperless_mail/tests/test_mail.py index acd5ac0f7..0e58fc6f7 100644 --- a/src/paperless_mail/tests/test_mail.py +++ b/src/paperless_mail/tests/test_mail.py @@ -1596,6 +1596,40 @@ class TestTasks(TestCase): tasks.process_mail_accounts() self.assertEqual(m.call_count, 0) + @mock.patch("paperless_mail.tasks.MailAccountHandler.handle_mail_account") + def test_process_with_account_ids(self, m): + m.side_effect = lambda account: 6 + + account_a = MailAccount.objects.create( + name="A", + imap_server="A", + username="A", + password="A", + ) + account_b = MailAccount.objects.create( + name="B", + imap_server="A", + username="A", + password="A", + ) + MailRule.objects.create( + name="A", + account=account_a, + ) + MailRule.objects.create( + name="B", + account=account_b, + ) + + result = tasks.process_mail_accounts(account_ids=[account_a.id]) + + self.assertEqual(m.call_count, 1) + self.assertIn("Added 6", result) + + m.side_effect = lambda account: 0 + result = tasks.process_mail_accounts(account_ids=[account_b.id]) + self.assertIn("No new", result) + class TestMailAccountTestView(APITestCase): def setUp(self): @@ -1720,3 +1754,30 @@ class TestMailAccountTestView(APITestCase): error_str = cm.output[0] expected_str = "Unable to refresh oauth token" self.assertIn(expected_str, error_str) + + +class TestMailAccountProcess(APITestCase): + def setUp(self): + self.mailMocker = MailMocker() + self.mailMocker.setUp() + self.user = User.objects.create_superuser( + username="testuser", + password="testpassword", + ) + self.client.force_authenticate(user=self.user) + self.account = MailAccount.objects.create( + imap_server="imap.example.com", + imap_port=993, + imap_security=MailAccount.ImapSecurity.SSL, + username="admin", + password="secret", + account_type=MailAccount.MailAccountType.IMAP, + owner=self.user, + ) + self.url = f"/api/mail_accounts/{self.account.pk}/process/" + + @mock.patch("paperless_mail.tasks.process_mail_accounts.delay") + def test_mail_account_process_view(self, m): + response = self.client.post(self.url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + m.assert_called_once() diff --git a/src/paperless_mail/views.py b/src/paperless_mail/views.py index 724d8e91e..170d5c6c1 100644 --- a/src/paperless_mail/views.py +++ b/src/paperless_mail/views.py @@ -24,6 +24,7 @@ from paperless_mail.models import MailRule from paperless_mail.oauth import PaperlessMailOAuth2Manager from paperless_mail.serialisers import MailAccountSerializer from paperless_mail.serialisers import MailRuleSerializer +from paperless_mail.tasks import process_mail_accounts class MailAccountViewSet(ModelViewSet, PassUserMixin): @@ -87,6 +88,13 @@ class MailAccountViewSet(ModelViewSet, PassUserMixin): ) return HttpResponseBadRequest("Unable to connect to server") + @action(methods=["post"], detail=True) + def process(self, request, pk=None): + account = self.get_object() + process_mail_accounts.delay([account.pk]) + + return Response({"result": "OK"}) + class MailRuleViewSet(ModelViewSet, PassUserMixin): model = MailRule