diff --git a/src-ui/src/app/components/admin/tasks/tasks.component.spec.ts b/src-ui/src/app/components/admin/tasks/tasks.component.spec.ts index 376cd23ce..026f5c062 100644 --- a/src-ui/src/app/components/admin/tasks/tasks.component.spec.ts +++ b/src-ui/src/app/components/admin/tasks/tasks.component.spec.ts @@ -19,7 +19,6 @@ import { PaperlessTaskTriggerSource, PaperlessTaskType, } from 'src/app/data/paperless-task' -import { Results } from 'src/app/data/results' import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive' import { PermissionsGuard } from 'src/app/guards/permissions.guard' import { CustomDatePipe } from 'src/app/pipes/custom-date.pipe' @@ -153,11 +152,6 @@ const tasks: PaperlessTask[] = [ }, ] -const paginatedTasks: Results = { - count: tasks.length, - results: tasks, -} - const sectionCountResponse = { all: 7, needs_attention: 2, @@ -204,7 +198,7 @@ describe('TasksComponent', () => { }).compileComponents() tasksService = TestBed.inject(TasksService) - reloadSpy = jest.spyOn(tasksService, 'reload') + reloadSpy = jest.spyOn(tasksService, 'reload').mockImplementation(() => {}) httpTestingController = TestBed.inject(HttpTestingController) modalService = TestBed.inject(NgbModal) router = TestBed.inject(Router) @@ -214,15 +208,6 @@ describe('TasksComponent', () => { jest.useFakeTimers() fixture.detectChanges() - httpTestingController - .expectOne( - (req) => - req.url === `${environment.apiBaseUrl}tasks/` && - req.params.get('acknowledged') === 'false' && - req.params.get('page_size') === '1000' - ) - .flush(paginatedTasks) - httpTestingController .expectOne( (req) => diff --git a/src-ui/src/app/components/app-frame/app-frame.component.html b/src-ui/src/app/components/app-frame/app-frame.component.html index 84b8dea3f..c6d3c1c06 100644 --- a/src-ui/src/app/components/app-frame/app-frame.component.html +++ b/src-ui/src/app/components/app-frame/app-frame.component.html @@ -344,11 +344,11 @@ ngbPopover="Tasks" i18n-ngbPopover [disablePopover]="!slimSidebarPopoversEnabled" placement="end" container="body" triggers="mouseenter:mouseleave" popoverClass="popover-slim"> Tasks - @if (tasksService.needsAttentionTasks.length > 0 && !slimSidebarEnabled) { - {{tasksService.needsAttentionTasks.length}} + @if (tasksService.needsAttentionTasksCount > 0 && !slimSidebarEnabled) { + {{tasksService.needsAttentionTasksCount}} } - @if (tasksService.needsAttentionTasks.length > 0 && slimSidebarEnabled) { - {{tasksService.needsAttentionTasks.length}} + @if (tasksService.needsAttentionTasksCount > 0 && slimSidebarEnabled) { + {{tasksService.needsAttentionTasksCount}} } diff --git a/src-ui/src/app/components/app-frame/app-frame.component.spec.ts b/src-ui/src/app/components/app-frame/app-frame.component.spec.ts index 971a4ceeb..83cebd889 100644 --- a/src-ui/src/app/components/app-frame/app-frame.component.spec.ts +++ b/src-ui/src/app/components/app-frame/app-frame.component.spec.ts @@ -588,8 +588,8 @@ describe('AppFrameComponent', () => { it('should show tasks badge for needs-attention tasks', () => { jest - .spyOn(tasksService, 'needsAttentionTasks', 'get') - .mockReturnValue([{} as any, {} as any]) + .spyOn(tasksService, 'needsAttentionTasksCount', 'get') + .mockReturnValue(2) fixture.detectChanges() diff --git a/src-ui/src/app/services/tasks.service.spec.ts b/src-ui/src/app/services/tasks.service.spec.ts index c8dca41fb..2d03b677b 100644 --- a/src-ui/src/app/services/tasks.service.spec.ts +++ b/src-ui/src/app/services/tasks.service.spec.ts @@ -9,11 +9,7 @@ import { } from '@angular/common/http/testing' import { TestBed } from '@angular/core/testing' import { environment } from 'src/environments/environment' -import { - PaperlessTaskStatus, - PaperlessTaskTriggerSource, - PaperlessTaskType, -} from '../data/paperless-task' +import { PaperlessTaskType } from '../data/paperless-task' import { TasksService } from './tasks.service' describe('TasksService', () => { @@ -38,23 +34,23 @@ describe('TasksService', () => { httpTestingController.verify() }) - it('calls tasks api endpoint on reload', () => { + it('calls tasks status counts api endpoint on reload', () => { tasksService.reload() const req = httpTestingController.expectOne( (req: HttpRequest) => - req.url === `${environment.apiBaseUrl}tasks/` && - req.params.get('acknowledged') === 'false' && - req.params.get('page_size') === '1000' + req.url === `${environment.apiBaseUrl}tasks/status_counts/` && + req.params.get('acknowledged') === 'false' ) expect(req.request.method).toEqual('GET') - req.flush({ count: 0, results: [] }) + req.flush({ all: 5, needs_attention: 3, in_progress: 1, completed: 1 }) + expect(tasksService.needsAttentionTasksCount).toEqual(3) }) it('cancels an in-progress reload when reloading again', () => { tasksService.reload() const staleReload = httpTestingController.expectOne( (req: HttpRequest) => - req.url === `${environment.apiBaseUrl}tasks/` + req.url === `${environment.apiBaseUrl}tasks/status_counts/` ) tasksService.reload() @@ -62,9 +58,9 @@ describe('TasksService', () => { httpTestingController .expectOne( (req: HttpRequest) => - req.url === `${environment.apiBaseUrl}tasks/` + req.url === `${environment.apiBaseUrl}tasks/status_counts/` ) - .flush({ count: 0, results: [] }) + .flush({ all: 0, needs_attention: 0, in_progress: 0, completed: 0 }) }) it('continues reloading after a reload request fails', () => { @@ -72,7 +68,7 @@ describe('TasksService', () => { httpTestingController .expectOne( (req: HttpRequest) => - req.url === `${environment.apiBaseUrl}tasks/` + req.url === `${environment.apiBaseUrl}tasks/status_counts/` ) .flush('error', { status: 500, statusText: 'error' }) @@ -82,16 +78,16 @@ describe('TasksService', () => { httpTestingController .expectOne( (req: HttpRequest) => - req.url === `${environment.apiBaseUrl}tasks/` + req.url === `${environment.apiBaseUrl}tasks/status_counts/` ) - .flush({ count: 0, results: [] }) + .flush({ all: 0, needs_attention: 0, in_progress: 0, completed: 0 }) }) it('reloads after dismissing a task while a reload is already in progress', () => { tasksService.reload() const staleReload = httpTestingController.expectOne( (req: HttpRequest) => - req.url === `${environment.apiBaseUrl}tasks/` && + req.url === `${environment.apiBaseUrl}tasks/status_counts/` && req.params.get('acknowledged') === 'false' ) @@ -104,12 +100,12 @@ describe('TasksService', () => { httpTestingController .expectOne( (req: HttpRequest) => - req.url === `${environment.apiBaseUrl}tasks/` && + req.url === `${environment.apiBaseUrl}tasks/status_counts/` && req.params.get('acknowledged') === 'false' ) - .flush({ count: 0, results: [] }) + .flush({ all: 50, needs_attention: 42, in_progress: 3, completed: 5 }) - expect(tasksService.needsAttentionTasks).toHaveLength(0) + expect(tasksService.needsAttentionTasksCount).toEqual(42) }) it('calls acknowledge_tasks api endpoint on dismiss and reloads', () => { @@ -126,11 +122,10 @@ describe('TasksService', () => { httpTestingController .expectOne( (req: HttpRequest) => - req.url === `${environment.apiBaseUrl}tasks/` && - req.params.get('acknowledged') === 'false' && - req.params.get('page_size') === '1000' + req.url === `${environment.apiBaseUrl}tasks/status_counts/` && + req.params.get('acknowledged') === 'false' ) - .flush({ count: 0, results: [] }) + .flush({ all: 0, needs_attention: 0, in_progress: 0, completed: 0 }) }) it('calls acknowledge_tasks api endpoint on dismiss all and reloads', () => { @@ -147,65 +142,10 @@ describe('TasksService', () => { httpTestingController .expectOne( (req: HttpRequest) => - req.url === `${environment.apiBaseUrl}tasks/` && - req.params.get('acknowledged') === 'false' && - req.params.get('page_size') === '1000' + req.url === `${environment.apiBaseUrl}tasks/status_counts/` && + req.params.get('acknowledged') === 'false' ) - .flush({ count: 0, results: [] }) - }) - - it('includes revoked tasks in needs attention', () => { - const mockTasks = [ - { - task_type: PaperlessTaskType.SanityCheck, - trigger_source: PaperlessTaskTriggerSource.System, - status: PaperlessTaskStatus.Failure, - acknowledged: false, - task_id: '1235', - input_data: {}, - date_created: new Date(), - related_document_ids: [], - }, - { - task_type: PaperlessTaskType.MailFetch, - trigger_source: PaperlessTaskTriggerSource.Scheduled, - status: PaperlessTaskStatus.Revoked, - acknowledged: false, - task_id: '1236', - input_data: {}, - date_created: new Date(), - related_document_ids: [], - }, - { - task_type: PaperlessTaskType.EmptyTrash, - trigger_source: PaperlessTaskTriggerSource.Manual, - status: PaperlessTaskStatus.Success, - acknowledged: false, - task_id: '1238', - input_data: {}, - date_created: new Date(), - related_document_ids: [], - }, - ] - - tasksService.reload() - - const req = httpTestingController.expectOne( - (req: HttpRequest) => - req.url === `${environment.apiBaseUrl}tasks/` && - req.params.get('acknowledged') === 'false' && - req.params.get('page_size') === '1000' - ) - - req.flush({ count: mockTasks.length, results: mockTasks }) - - expect(tasksService.needsAttentionTasks).toHaveLength(2) - expect(tasksService.needsAttentionTasks.map((task) => task.status)).toEqual( - expect.arrayContaining([ - PaperlessTaskStatus.Failure, - PaperlessTaskStatus.Revoked, - ]) - ) + .flush({ all: 0, needs_attention: 0, in_progress: 0, completed: 0 }) }) it('supports running tasks', () => { diff --git a/src-ui/src/app/services/tasks.service.ts b/src-ui/src/app/services/tasks.service.ts index 189d3ac62..c54b72c13 100644 --- a/src-ui/src/app/services/tasks.service.ts +++ b/src-ui/src/app/services/tasks.service.ts @@ -5,14 +5,12 @@ import { catchError, finalize, first, - map, switchMap, takeUntil, tap, } from 'rxjs/operators' import { PaperlessTask, - PaperlessTaskStatus, PaperlessTaskStatusCounts, PaperlessTaskType, } from 'src/app/data/paperless-task' @@ -27,50 +25,42 @@ export class TasksService { private baseUrl: string = environment.apiBaseUrl private endpoint: string = 'tasks' - private readonly defaultReloadPageSize = 1000 public loading: boolean = false - private readonly tasks = signal([]) + private readonly tasksStatusCounts = signal({ + all: 0, + needs_attention: 0, + in_progress: 0, + completed: 0, + }) private readonly reloadNotifier = new Subject() private unsubscribeNotifer: Subject = new Subject() constructor() { this.reloadNotifier - .pipe( - switchMap(() => { - this.loading = true - return this.http - .get>(`${this.baseUrl}${this.endpoint}/`, { - params: { - acknowledged: 'false', - page_size: this.defaultReloadPageSize, - }, - }) - .pipe( - map((response) => response.results), - takeUntil(this.unsubscribeNotifer), - catchError(() => EMPTY), - finalize(() => { - this.loading = false - }) - ) - }) - ) - .subscribe((tasks) => { - this.tasks.set(tasks) + .pipe(switchMap(() => this.fetchUnacknowledgedTasksCounts())) + .subscribe((taskStatusCounts) => { + this.tasksStatusCounts.set(taskStatusCounts) }) } - public get needsAttentionTasks(): PaperlessTask[] { - return this.tasks().filter((t) => - [PaperlessTaskStatus.Failure, PaperlessTaskStatus.Revoked].includes( - t.status - ) + private fetchUnacknowledgedTasksCounts(): Observable { + this.loading = true + return this.statusCounts({ acknowledged: false }).pipe( + takeUntil(this.unsubscribeNotifer), + catchError(() => EMPTY), + finalize(() => { + this.loading = false + }) ) } + public get needsAttentionTasksCount(): number { + return this.tasksStatusCounts().needs_attention + } + public reload() { this.reloadNotifier.next() } @@ -105,7 +95,7 @@ export class TasksService { public dismissTasks(task_ids: Set): Observable { return this.http - .post(`${this.baseUrl}tasks/acknowledge/`, { + .post(`${this.baseUrl}${this.endpoint}/acknowledge/`, { tasks: [...task_ids], }) .pipe( @@ -119,7 +109,7 @@ export class TasksService { public dismissAllTasks(): Observable { return this.http - .post(`${this.baseUrl}tasks/acknowledge/`, { + .post(`${this.baseUrl}${this.endpoint}/acknowledge/`, { all: true, }) .pipe(