diff --git a/docs/process-services-cloud/components/process-filters-cloud.component.md b/docs/process-services-cloud/components/process-filters-cloud.component.md index 3e6012bbb1..d960153d1d 100644 --- a/docs/process-services-cloud/components/process-filters-cloud.component.md +++ b/docs/process-services-cloud/components/process-filters-cloud.component.md @@ -27,7 +27,6 @@ Lists all available process filters and allows to select a filter. | appName | `string` | "" | (required) The application name | | filterParam | `UserTaskFilterRepresentation` | | (optional) The filter to be selected by default | | showIcons | `boolean` | false | (optional) Toggles showing an icon by the side of each filter | -| searchApiMethod | `'GET' \| 'POST'` | "GET" | **Deprecated:** the counters are resolved by a single `POST /query/v1/count` call, which requires Activiti 8.7.0 forward. This input is only used to resolve the counters one filter at a time, on the backends without that endpoint, and will be removed along with the `GET` method in ADF 10.0.0. | ### Events diff --git a/docs/process-services-cloud/components/task-filters-cloud.component.md b/docs/process-services-cloud/components/task-filters-cloud.component.md index e906028fce..d7580e5dea 100644 --- a/docs/process-services-cloud/components/task-filters-cloud.component.md +++ b/docs/process-services-cloud/components/task-filters-cloud.component.md @@ -36,7 +36,6 @@ Shows all available filters. | appName | `string` | "" | Display filters available to the current user for the application with the specified name. | | filterParam | `FilterParamsModel` | | Parameters to use for the task filter cloud. If there is no match then the default filter (the first one in the list) is selected. | | showIcons | `boolean` | false | Toggles display of the filter's icons. | -| searchApiMethod | `'GET' \| 'POST'` | "GET" | **Deprecated:** the counters are resolved by a single `POST /query/v1/count` call, which requires Activiti 8.7.0 forward. This input is only used to resolve the counters one filter at a time, on the backends without that endpoint, and will be removed along with the `GET` method in ADF 10.0.0. | ### Events diff --git a/docs/process-services-cloud/services/task-filter-cloud.service.md b/docs/process-services-cloud/services/task-filter-cloud.service.md index a95478c2c6..c24be9cf15 100644 --- a/docs/process-services-cloud/services/task-filter-cloud.service.md +++ b/docs/process-services-cloud/services/task-filter-cloud.service.md @@ -31,10 +31,6 @@ Manages task filters. - _appName:_ `string` - Name of the target app - _id:_ `string` - ID of the task - **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`TaskFilterCloudModel`](../../../lib/process-services-cloud/src/lib/task/task-filters/models/filter-cloud.model.ts)`>` - Details of the task filter -- **getTaskFilterCounter**(taskFilter: [`TaskFilterCloudModel`](../../../lib/process-services-cloud/src/lib/task/task-filters/models/filter-cloud.model.ts)): [`Observable`](http://reactivex.io/documentation/observable.html)``
- Finds a task using an object with optional query properties. - - _taskFilter:_ [`TaskFilterCloudModel`](../../../lib/process-services-cloud/src/lib/task/task-filters/models/filter-cloud.model.ts) - - - **Returns** [`Observable`](http://reactivex.io/documentation/observable.html)`` - Task information - **getTaskListFilters**(appName?: `string`): [`Observable`](http://reactivex.io/documentation/observable.html)`<`[`TaskFilterCloudModel`](../../../lib/process-services-cloud/src/lib/task/task-filters/models/filter-cloud.model.ts)`[]>`
Gets all task filters for a task app. - _appName:_ `string` - (Optional) Name of the target app diff --git a/lib/process-services-cloud/src/lib/models/filter-counters-cloud.model.ts b/lib/process-services-cloud/src/lib/models/filter-counters-cloud.model.ts index e02196e0c9..5a6a4e4882 100644 --- a/lib/process-services-cloud/src/lib/models/filter-counters-cloud.model.ts +++ b/lib/process-services-cloud/src/lib/models/filter-counters-cloud.model.ts @@ -62,9 +62,7 @@ export type FilterCounters = { [entityType in FilterCounterEntityType]?: { [requestId: string]: number }; }; +/** Counters of the filters of one entity type, keyed by filter key. */ export interface FilterCountersResult { - /** Counters keyed by filter key. Empty when the batched count endpoint is not available. */ - counters: { [filterKey: string]: number }; - /** When `false`, the backend holds no batched count endpoint: count one filter at a time. */ - batched: boolean; + [filterKey: string]: number; } diff --git a/lib/process-services-cloud/src/lib/process/process-filters/components/process-filters/process-filters-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/process/process-filters/components/process-filters/process-filters-cloud.component.spec.ts index 7e372538b8..7de324d67c 100644 --- a/lib/process-services-cloud/src/lib/process/process-filters/components/process-filters/process-filters-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/process/process-filters/components/process-filters/process-filters-cloud.component.spec.ts @@ -25,7 +25,6 @@ import { PROCESS_FILTERS_SERVICE_TOKEN, TASK_FILTERS_SERVICE_TOKEN } from '../.. import { LocalPreferenceCloudService } from '../../../../services/local-preference-cloud.service'; import { mockProcessFilters } from '../../mock/process-filters-cloud.mock'; import { AppConfigService, AppConfigServiceMock, NoopAuthModule } from '@alfresco/adf-core'; -import { ProcessListCloudService } from '../../../process-list/services/process-list-cloud.service'; import { ApolloTestingModule } from 'apollo-angular/testing'; import { HarnessLoader } from '@angular/cdk/testing'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; @@ -47,24 +46,21 @@ const ProcessFilterCloudServiceMock = { describe('ProcessFiltersCloudComponent', () => { let processFilterService: ProcessFilterCloudService; let filterCountersService: FilterCountersCloudService; - let processListService: ProcessListCloudService; let component: ProcessFiltersCloudComponent; let fixture: ComponentFixture; let getProcessFiltersSpy: jasmine.Spy; let getFilterCountersSpy: jasmine.Spy; let refreshFilterCountersSpy: jasmine.Spy; - let getProcessCounterSpy: jasmine.Spy; let loader: HarnessLoader; let router: Router; - const configureTestingModule = async (searchApiMethod: 'GET' | 'POST') => { + const configureTestingModule = async () => { TestBed.configureTestingModule({ imports: [NoopAuthModule, ProcessFiltersCloudComponent, ApolloTestingModule], providers: [ { provide: PROCESS_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }, { provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }, { provide: AppConfigService, useClass: AppConfigServiceMock }, - ProcessListCloudService, { provide: ProcessFilterCloudService, useValue: ProcessFilterCloudServiceMock }, provideRouter([{ path: 'process-list-cloud', component: DummyComponent }]), { @@ -85,19 +81,15 @@ describe('ProcessFiltersCloudComponent', () => { fixture = TestBed.createComponent(ProcessFiltersCloudComponent); loader = TestbedHarnessEnvironment.loader(fixture); component = fixture.componentInstance; - component.searchApiMethod = searchApiMethod; processFilterService = TestBed.inject(ProcessFilterCloudService); filterCountersService = TestBed.inject(FilterCountersCloudService); - processListService = TestBed.inject(ProcessListCloudService); TestBed.inject(ActivatedRoute); router = TestBed.inject(Router); await RouterTestingHarness.create(); getProcessFiltersSpy = spyOn(filterCountersService, 'getProcessFilters').and.returnValue(of(mockProcessFilters)); - getFilterCountersSpy = spyOn(filterCountersService, 'getFilterCounters').and.returnValue(of({ counters: {}, batched: true })); + getFilterCountersSpy = spyOn(filterCountersService, 'getFilterCounters').and.returnValue(of({})); refreshFilterCountersSpy = spyOn(filterCountersService, 'refreshFilterCounters'); - getProcessCounterSpy = spyOn(processListService, 'getProcessCounter').and.returnValue(of(10)); - spyOn(processListService, 'getProcessListCount').and.returnValue(of(10)); }; const bindAppName = async (appName = 'my-app-1') => { @@ -110,9 +102,9 @@ describe('ProcessFiltersCloudComponent', () => { fixture.destroy(); }); - describe('searchApiMethod set to GET', () => { + describe('filters on screen', () => { beforeEach(async () => { - await configureTestingModule('GET'); + await configureTestingModule(); }); it('should attach specific icon for each filter if hasIcon is true', async () => { @@ -252,88 +244,9 @@ describe('ProcessFiltersCloudComponent', () => { }); }); - describe('searchApiMethod set to POST', () => { + describe('filter selection and counters', () => { beforeEach(async () => { - await configureTestingModule('POST'); - }); - - it('should attach specific icon for each filter if hasIcon is true', async () => { - await bindAppName(); - - component.showIcons = true; - - fixture.detectChanges(); - await fixture.whenStable(); - - expect(component.filters.length).toBe(3); - const filterIcons = await loader.getAllHarnesses(MatIconHarness.with({ selector: '[data-automation-id="adf-filter-icon"]' })); - expect(filterIcons.length).toBe(3); - expect(await filterIcons[0].getName()).toContain('adjust'); - expect(await filterIcons[1].getName()).toContain('inbox'); - expect(await filterIcons[2].getName()).toContain('done'); - }); - - it('should not attach icons for each filter if hasIcon is false', async () => { - component.showIcons = false; - await bindAppName(); - - const filterIcons = await loader.getAllHarnesses(MatIconHarness.with({ selector: '[data-automation-id="adf-filter-icon"]' })); - expect(filterIcons.length).toBe(0); - }); - - it('should display the filters', async () => { - await bindAppName(); - - component.showIcons = true; - - fixture.detectChanges(); - await fixture.whenStable(); - - const filters = fixture.debugElement.queryAll(By.css('.adf-process-filters__entry')); - expect(component.filters.length).toBe(3); - expect(filters.length).toBe(3); - expect(filters[0].nativeElement.innerText).toContain('FakeAllProcesses'); - expect(filters[1].nativeElement.innerText).toContain('FakeRunningProcesses'); - expect(filters[2].nativeElement.innerText).toContain('FakeCompletedProcesses'); - expect(Object.keys(component.counters).length).toBe(3); - }); - - it('should emit success with the filters when filters are loaded', async () => { - const successSpy = spyOn(component.success, 'emit'); - await bindAppName(); - - expect(successSpy).toHaveBeenCalledWith(mockProcessFilters); - expect(component.filters).toBeDefined(); - expect(component.filters[0].name).toEqual('FakeAllProcesses'); - expect(component.filters[1].name).toEqual('FakeRunningProcesses'); - expect(component.filters[2].name).toEqual('FakeCompletedProcesses'); - expect(Object.keys(component.counters).length).toBe(3); - }); - - it('should not select any filter as default', async () => { - await bindAppName(); - - expect(component.currentFilter).toBeUndefined(); - }); - - it('should filterClicked emit when a filter is clicked from the UI', async () => { - const filterClickedSpy = spyOn(component.filterClicked, 'emit'); - await bindAppName(); - - const filterButton = fixture.debugElement.nativeElement.querySelector(`[data-automation-id="${mockProcessFilters[0].key}_filter"]`); - filterButton.click(); - - fixture.detectChanges(); - await fixture.whenStable(); - - expect(component.currentFilter).toEqual(mockProcessFilters[0]); - expect(filterClickedSpy).toHaveBeenCalledWith(mockProcessFilters[0]); - }); - }); - - describe('API agnostic', () => { - beforeEach(async () => { - await configureTestingModule('GET'); + await configureTestingModule(); }); it('should emit an error with a bad response', async () => { @@ -468,16 +381,16 @@ describe('ProcessFiltersCloudComponent', () => { expect(component.updatedFiltersSet.has(filterKeyTest)).toBeFalsy(); }); - it('should resolve the counter only of the filters with a counter enabled', () => { + it('should hold the counter only of the filters with a counter enabled', async () => { const filterWithCounter = new ProcessFilterCloudModel({ ...mockProcessFilters[1], showCounter: true }); const filterWithoutCounter = new ProcessFilterCloudModel({ ...mockProcessFilters[2], showCounter: false }); - getProcessCounterSpy.calls.reset(); + getProcessFiltersSpy.and.returnValue(of([filterWithCounter, filterWithoutCounter])); + getFilterCountersSpy.and.returnValue(of({ [filterWithCounter.key]: 7, [filterWithoutCounter.key]: 9 })); - component.filters = [filterWithCounter, filterWithoutCounter]; - component.updateFilterCounters(); + await bindAppName('mock-app-name'); - expect(getProcessCounterSpy).toHaveBeenCalledTimes(1); - expect(getProcessCounterSpy).toHaveBeenCalledWith(filterWithCounter.appName, filterWithCounter.status); + expect(component.counters[filterWithCounter.key]).toBe(7); + expect(component.counters[filterWithoutCounter.key]).toBe(0); }); describe('Batched counters', () => { @@ -494,7 +407,7 @@ describe('ProcessFiltersCloudComponent', () => { }); it('should hold the counters resolved by the batched count request', async () => { - getFilterCountersSpy.and.returnValue(of({ counters: { FakeRunningProcesses: 9 }, batched: true })); + getFilterCountersSpy.and.returnValue(of({ FakeRunningProcesses: 9 })); await bindAppName('mock-app-name'); @@ -502,7 +415,7 @@ describe('ProcessFiltersCloudComponent', () => { }); it('should emit the filters whose counter changed', async () => { - getFilterCountersSpy.and.returnValue(of({ counters: { FakeRunningProcesses: 9 }, batched: true })); + getFilterCountersSpy.and.returnValue(of({ FakeRunningProcesses: 9 })); const updatedFilterSpy = spyOn(component.updatedFilter, 'emit'); await bindAppName('mock-app-name'); @@ -510,27 +423,19 @@ describe('ProcessFiltersCloudComponent', () => { expect(updatedFilterSpy).toHaveBeenCalledWith('FakeRunningProcesses'); }); - it('should resolve the counters one filter at a time when the batched endpoint is not available', async () => { - getFilterCountersSpy.and.returnValue(of({ counters: {}, batched: false })); - + it('should keep the counter of a filter the request left out', async () => { + getFilterCountersSpy.and.returnValue(of({ FakeRunningProcesses: 9 })); await bindAppName('mock-app-name'); + expect(component.counters['FakeRunningProcesses']).toBe(9); - expect(getProcessCounterSpy).toHaveBeenCalledTimes(3); - expect(component.counters['FakeRunningProcesses']).toBe(10); - }); - - it('should resolve the counters of the filters the batch left out on their own', async () => { - getFilterCountersSpy.and.returnValue(of({ counters: { FakeRunningProcesses: 9 }, batched: true })); - + getFilterCountersSpy.and.returnValue(of({})); await bindAppName('mock-app-name'); expect(component.counters['FakeRunningProcesses']).toBe(9); - expect(getProcessCounterSpy.calls.allArgs().map(([, status]) => status)).toEqual([null, 'COMPLETED']); }); it('should keep the counters of the other filters when one counter cannot be resolved', async () => { - getFilterCountersSpy.and.returnValue(of({ counters: { FakeRunningProcesses: 9 }, batched: true })); - getProcessCounterSpy.and.throwError('the query of the filter cannot be built'); + getFilterCountersSpy.and.returnValue(of({ FakeRunningProcesses: 9 })); await bindAppName('mock-app-name'); @@ -586,415 +491,14 @@ describe('ProcessFiltersCloudComponent', () => { fixture.detectChanges(); component.filters = mockProcessFilters.map((filter) => new ProcessFilterCloudModel({ ...filter, showCounter: true })); - counters$.next({ counters: { FakeRunningProcesses: 7 }, batched: true }); + counters$.next({ FakeRunningProcesses: 7 }); expect(component.counters['FakeRunningProcesses']).toBe(7); flush(); })); }); - describe('Highlight Selected Filter', () => { - const allProcessesFilterKey = mockProcessFilters[0].key; - const allProcessesFilterId = mockProcessFilters[0].id; - - it('should apply active CSS class on filter click', async () => { - component.enableNotifications = true; - await bindAppName('mock-app-name'); - - let link = fixture.debugElement.query(By.css(`[data-automation-id="${allProcessesFilterKey}_filter"]`)).nativeElement; - expect(link.getAttribute('href')).toBe(`/process-list-cloud?filterId=${allProcessesFilterId}`); - - link.click(); - fixture.detectChanges(); - await fixture.whenStable(); - expect(router.url).toBe(`/process-list-cloud?filterId=${allProcessesFilterId}`); - - link = fixture.debugElement.query(By.css(`[data-automation-id="${allProcessesFilterKey}_filter"]`)).nativeElement; - expect(link.classList).toContain('adf-active'); - }); - - it('should add aria-current attribute with value "page" to the active filter', async () => { - component.enableNotifications = true; - await bindAppName('mock-app-name'); - - const link = fixture.debugElement.query(By.css(`[data-automation-id="${allProcessesFilterKey}_filter"]`)).nativeElement; - expect(link.getAttribute('aria-current')).toBe('page'); - }); - - it('should not have aria-current attribute when filter is not active', async () => { - component.enableNotifications = true; - await bindAppName('mock-app-name'); - - const link = fixture.debugElement.query(By.css(`[data-automation-id="${mockProcessFilters[1].key}_filter"]`)).nativeElement; - expect(link.getAttribute('aria-current')).toBeNull(); - }); - }); - }); - - describe('searchApiMethod set to POST', () => { - beforeEach(async () => { - await configureTestingModule('POST'); - }); - - it('should attach specific icon for each filter if hasIcon is true', async () => { - await bindAppName(); - - component.showIcons = true; - - fixture.detectChanges(); - await fixture.whenStable(); - - expect(component.filters.length).toBe(3); - const filterIcons = await loader.getAllHarnesses(MatIconHarness.with({ selector: '[data-automation-id="adf-filter-icon"]' })); - expect(filterIcons.length).toBe(3); - expect(await filterIcons[0].getName()).toContain('adjust'); - expect(await filterIcons[1].getName()).toContain('inbox'); - expect(await filterIcons[2].getName()).toContain('done'); - }); - - it('should not attach icons for each filter if hasIcon is false', async () => { - component.showIcons = false; - await bindAppName(); - - const filterIcons = await loader.getAllHarnesses(MatIconHarness.with({ selector: '[data-automation-id="adf-filter-icon"]' })); - expect(filterIcons.length).toBe(0); - }); - - it('should display the filters', async () => { - await bindAppName(); - - component.showIcons = true; - - fixture.detectChanges(); - await fixture.whenStable(); - - const filters = fixture.debugElement.queryAll(By.css('.adf-process-filters__entry')); - expect(component.filters.length).toBe(3); - expect(filters.length).toBe(3); - expect(filters[0].nativeElement.innerText).toContain('FakeAllProcesses'); - expect(filters[1].nativeElement.innerText).toContain('FakeRunningProcesses'); - expect(filters[2].nativeElement.innerText).toContain('FakeCompletedProcesses'); - expect(Object.keys(component.counters).length).toBe(3); - }); - - it('should emit success with the filters when filters are loaded', async () => { - const successSpy = spyOn(component.success, 'emit'); - await bindAppName(); - - expect(successSpy).toHaveBeenCalledWith(mockProcessFilters); - expect(component.filters).toBeDefined(); - expect(component.filters[0].name).toEqual('FakeAllProcesses'); - expect(component.filters[1].name).toEqual('FakeRunningProcesses'); - expect(component.filters[2].name).toEqual('FakeCompletedProcesses'); - expect(Object.keys(component.counters).length).toBe(3); - }); - - it('should not select any filter as default', async () => { - await bindAppName(); - - expect(component.currentFilter).toBeUndefined(); - }); - - it('should filterClicked emit when a filter is clicked from the UI', async () => { - const filterClickedSpy = spyOn(component.filterClicked, 'emit'); - await bindAppName(); - - const filterButton = fixture.debugElement.nativeElement.querySelector(`[data-automation-id="${mockProcessFilters[0].key}_filter"]`); - filterButton.click(); - - fixture.detectChanges(); - await fixture.whenStable(); - - expect(component.currentFilter).toEqual(mockProcessFilters[0]); - expect(filterClickedSpy).toHaveBeenCalledWith(mockProcessFilters[0]); - }); - }); - - describe('API agnostic', () => { - beforeEach(async () => { - await configureTestingModule('GET'); - }); - - it('should emit an error with a bad response', async () => { - getProcessFiltersSpy.and.returnValue(throwError('wrong request')); - let lastValue: any; - component.error.subscribe((err) => (lastValue = err)); - - await bindAppName(); - - expect(lastValue).toBeDefined(); - }); - - it('should not select any process filter if filter input does not exist', async () => { - const change = new SimpleChange(null, { name: 'nonexistentFilter' }, true); - fixture.detectChanges(); - await fixture.whenStable(); - component.ngOnChanges({ filterParam: change }); - - expect(component.currentFilter).toBeUndefined(); - }); - - it('should select the filter based on the input by name param', async () => { - const filterSelectedSpy = spyOn(component.filterSelected, 'emit'); - const change = new SimpleChange(null, { name: 'FakeRunningProcesses' }, true); - - await bindAppName(); - component.ngOnChanges({ filterParam: change }); - - expect(component.currentFilter).toEqual(mockProcessFilters[1]); - expect(filterSelectedSpy).toHaveBeenCalledWith(mockProcessFilters[1]); - }); - - it('should select the filter based on the input by key param', async () => { - const filterSelectedSpy = spyOn(component.filterSelected, 'emit'); - const change = new SimpleChange(null, { key: 'completed-processes' }, true); - - await bindAppName(); - component.ngOnChanges({ filterParam: change }); - - expect(component.currentFilter).toEqual(mockProcessFilters[2]); - expect(filterSelectedSpy).toHaveBeenCalledWith(mockProcessFilters[2]); - }); - - it('should select the filter based on the input by index param', async () => { - const filterSelectedSpy = spyOn(component.filterSelected, 'emit'); - const change = new SimpleChange(null, { index: 2 }, true); - - await bindAppName(); - component.ngOnChanges({ filterParam: change }); - - expect(component.currentFilter).toEqual(mockProcessFilters[2]); - expect(filterSelectedSpy).toHaveBeenCalledWith(mockProcessFilters[2]); - }); - - it('should select the filter based on the input by id param', async () => { - const filterSelectedSpy = spyOn(component.filterSelected, 'emit'); - const change = new SimpleChange(null, { id: '12' }, true); - - await bindAppName(); - component.ngOnChanges({ filterParam: change }); - - expect(component.currentFilter).toEqual(mockProcessFilters[2]); - expect(filterSelectedSpy).toHaveBeenCalledWith(mockProcessFilters[2]); - }); - - it('should reset the filter when the param is undefined', () => { - const change = new SimpleChange(mockProcessFilters[0], undefined, false); - component.currentFilter = mockProcessFilters[0]; - component.ngOnChanges({ filterParam: change }); - - expect(component.currentFilter).toEqual(undefined); - }); - - it('should not emit a filter clicked event when a filter is selected through the filterParam input (filterClicked emits only through a UI click action)', async () => { - const filterClickedSpy = spyOn(component.filterClicked, 'emit'); - const change = new SimpleChange(null, { id: '10' }, true); - - await bindAppName(); - component.ngOnChanges({ filterParam: change }); - - expect(component.currentFilter).toBe(mockProcessFilters[0]); - expect(filterClickedSpy).not.toHaveBeenCalled(); - }); - - it('should reload filters by appName on binding changes', () => { - spyOn(component, 'getFilters').and.stub(); - const appName = 'my-app-1'; - - const change = new SimpleChange(null, appName, true); - component.ngOnChanges({ appName: change }); - - expect(component.getFilters).toHaveBeenCalledWith(appName); - }); - - it('should not reload filters by appName null on binding changes', () => { - spyOn(component, 'getFilters').and.stub(); - const appName = null; - - const change = new SimpleChange(undefined, appName, true); - component.ngOnChanges({ appName: change }); - - expect(component.getFilters).not.toHaveBeenCalledWith(appName); - }); - - it('should reload filters by app name on binding changes', () => { - spyOn(component, 'getFilters').and.stub(); - const appName = 'fake-app-name'; - - const change = new SimpleChange(null, appName, true); - component.ngOnChanges({ appName: change }); - - expect(component.getFilters).toHaveBeenCalledWith(appName); - }); - - it('should return the current filter after one is selected', () => { - const filter = mockProcessFilters[1]; - component.filters = mockProcessFilters; - - expect(component.currentFilter).toBeUndefined(); - component.selectFilter({ id: filter.id }); - expect(component.getCurrentFilter()).toBe(filter); - }); - - it('should remove key from set of updated filters when received refreshed filter key', async () => { - const filterKeyTest = 'filter-key-test'; - component.updatedFiltersSet.add(filterKeyTest); - - expect(component.updatedFiltersSet.size).toBe(1); - processFilterService.filterKeyToBeRefreshed$ = of(filterKeyTest); - fixture.detectChanges(); - - expect(component.updatedFiltersSet.has(filterKeyTest)).toBeFalsy(); - }); - - it('should resolve the counter only of the filters with a counter enabled', () => { - const filterWithCounter = new ProcessFilterCloudModel({ ...mockProcessFilters[1], showCounter: true }); - const filterWithoutCounter = new ProcessFilterCloudModel({ ...mockProcessFilters[2], showCounter: false }); - getProcessCounterSpy.calls.reset(); - - component.filters = [filterWithCounter, filterWithoutCounter]; - component.updateFilterCounters(); - - expect(getProcessCounterSpy).toHaveBeenCalledTimes(1); - expect(getProcessCounterSpy).toHaveBeenCalledWith(filterWithCounter.appName, filterWithCounter.status); - }); - - describe('Batched counters', () => { - beforeEach(() => { - getProcessFiltersSpy.and.returnValue( - of(mockProcessFilters.map((filter) => new ProcessFilterCloudModel({ ...filter, showCounter: true }))) - ); - }); - - it('should read the counters of the process filters of the bound app', async () => { - await bindAppName('mock-app-name'); - - expect(getFilterCountersSpy).toHaveBeenCalledWith('mock-app-name', FilterCounterEntityType.PROCESS_INSTANCE); - }); - - it('should hold the counters resolved by the batched count request', async () => { - getFilterCountersSpy.and.returnValue(of({ counters: { FakeRunningProcesses: 9 }, batched: true })); - - await bindAppName('mock-app-name'); - - expect(component.counters['FakeRunningProcesses']).toBe(9); - }); - - it('should emit the filters whose counter changed', async () => { - getFilterCountersSpy.and.returnValue(of({ counters: { FakeRunningProcesses: 9 }, batched: true })); - const updatedFilterSpy = spyOn(component.updatedFilter, 'emit'); - - await bindAppName('mock-app-name'); - - expect(updatedFilterSpy).toHaveBeenCalledWith('FakeRunningProcesses'); - }); - - it('should resolve the counters one filter at a time when the batched endpoint is not available', async () => { - getFilterCountersSpy.and.returnValue(of({ counters: {}, batched: false })); - - await bindAppName('mock-app-name'); - - expect(getProcessCounterSpy).toHaveBeenCalledTimes(3); - expect(component.counters['FakeRunningProcesses']).toBe(10); - }); - - it('should resolve the counters of the filters the batch left out on their own', async () => { - getFilterCountersSpy.and.returnValue(of({ counters: { FakeRunningProcesses: 9 }, batched: true })); - - await bindAppName('mock-app-name'); - - expect(component.counters['FakeRunningProcesses']).toBe(9); - expect(getProcessCounterSpy.calls.allArgs().map(([, status]) => status)).toEqual([null, 'COMPLETED']); - }); - - it('should keep the counters of the other filters when one counter cannot be resolved', async () => { - getFilterCountersSpy.and.returnValue(of({ counters: { FakeRunningProcesses: 9 }, batched: true })); - getProcessCounterSpy.and.throwError('the query of the filter cannot be built'); - - await bindAppName('mock-app-name'); - - expect(component.counters['FakeRunningProcesses']).toBe(9); - expect(component.counters['completed-processes']).toBe(0); - }); - - it('should refresh the counters of every filter when a filter is clicked', async () => { - await bindAppName('mock-app-name'); - - component.onFilterClick(mockProcessFilters[1]); - - expect(refreshFilterCountersSpy).toHaveBeenCalledWith('mock-app-name'); - }); - }); - - describe('Notifications config', () => { - it('should read enableNotifications and notificationDebounceTime from app config on init', () => { - const appConfigService = TestBed.inject(AppConfigService); - const getSpy = spyOn(appConfigService, 'get').and.callThrough(); - - fixture.detectChanges(); - - expect(getSpy).toHaveBeenCalledWith('notifications', true); - expect(getSpy).toHaveBeenCalledWith('notificationDebounceTime', 3000); - }); - - it('should default notificationDebounceTime to 3000 when not set in app config', () => { - fixture.detectChanges(); - - expect(component.notificationDebounceTime).toBe(3000); - }); - - it('should use notificationDebounceTime from app config', () => { - const appConfigService: AppConfigService = TestBed.inject(AppConfigService); - spyOn(appConfigService, 'get').and.callFake((key: string, defaultValue: any) => { - if (key === 'notificationDebounceTime') { - return 5000; - } - return defaultValue; - }); - - fixture.detectChanges(); - - expect(component.notificationDebounceTime).toBe(5000); - }); - - it('should keep the counters in sync with the counters stream', fakeAsync(() => { - const counters$ = new Subject(); - getFilterCountersSpy.and.returnValue(counters$.asObservable()); - component.appName = 'mock-app-name'; - - fixture.detectChanges(); - component.filters = mockProcessFilters.map((filter) => new ProcessFilterCloudModel({ ...filter, showCounter: true })); - - counters$.next({ counters: { FakeRunningProcesses: 7 }, batched: true }); - - expect(component.counters['FakeRunningProcesses']).toBe(7); - flush(); - })); - - it('should resolve the counters one filter at a time when the batched endpoint is not available', fakeAsync(() => { - const counters$ = new Subject(); - getFilterCountersSpy.and.returnValue(counters$.asObservable()); - component.appName = 'mock-app-name'; - - fixture.detectChanges(); - component.filters = mockProcessFilters.map((filter) => new ProcessFilterCloudModel({ ...filter, showCounter: true })); - getProcessCounterSpy.calls.reset(); - - counters$.next({ counters: {}, batched: false }); - - expect(getProcessCounterSpy).toHaveBeenCalledTimes(3); - flush(); - })); - }); - - describe('Highlight Selected Filter', () => { - it('should read the counters of the bound app', async () => { - component.enableNotifications = true; - await bindAppName('mock-app-name'); - - expect(getFilterCountersSpy).toHaveBeenCalledWith('mock-app-name', FilterCounterEntityType.PROCESS_INSTANCE); - }); - + describe('Counter updates', () => { it('should emit filter key when filter counter is set for first time', () => { component.currentFiltersValues = {}; const fakeFilterKey = 'testKey'; @@ -1052,5 +556,42 @@ describe('ProcessFiltersCloudComponent', () => { fixture.detectChanges(); }); }); + + describe('Highlight Selected Filter', () => { + const allProcessesFilterKey = mockProcessFilters[0].key; + const allProcessesFilterId = mockProcessFilters[0].id; + + it('should apply active CSS class on filter click', async () => { + component.enableNotifications = true; + await bindAppName('mock-app-name'); + + let link = fixture.debugElement.query(By.css(`[data-automation-id="${allProcessesFilterKey}_filter"]`)).nativeElement; + expect(link.getAttribute('href')).toBe(`/process-list-cloud?filterId=${allProcessesFilterId}`); + + link.click(); + fixture.detectChanges(); + await fixture.whenStable(); + expect(router.url).toBe(`/process-list-cloud?filterId=${allProcessesFilterId}`); + + link = fixture.debugElement.query(By.css(`[data-automation-id="${allProcessesFilterKey}_filter"]`)).nativeElement; + expect(link.classList).toContain('adf-active'); + }); + + it('should add aria-current attribute with value "page" to the active filter', async () => { + component.enableNotifications = true; + await bindAppName('mock-app-name'); + + const link = fixture.debugElement.query(By.css(`[data-automation-id="${allProcessesFilterKey}_filter"]`)).nativeElement; + expect(link.getAttribute('aria-current')).toBe('page'); + }); + + it('should not have aria-current attribute when filter is not active', async () => { + component.enableNotifications = true; + await bindAppName('mock-app-name'); + + const link = fixture.debugElement.query(By.css(`[data-automation-id="${mockProcessFilters[1].key}_filter"]`)).nativeElement; + expect(link.getAttribute('aria-current')).toBeNull(); + }); + }); }); }); diff --git a/lib/process-services-cloud/src/lib/process/process-filters/components/process-filters/process-filters-cloud.component.ts b/lib/process-services-cloud/src/lib/process/process-filters/components/process-filters/process-filters-cloud.component.ts index c4812b22f8..c457e7a46a 100644 --- a/lib/process-services-cloud/src/lib/process/process-filters/components/process-filters/process-filters-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/process/process-filters/components/process-filters/process-filters-cloud.component.ts @@ -16,16 +16,14 @@ */ import { Component, DestroyRef, EventEmitter, inject, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; -import { combineLatest, defer, EMPTY, Observable, of, Subscription } from 'rxjs'; +import { combineLatest, EMPTY, Observable, of, Subscription } from 'rxjs'; import { ProcessFilterCloudService } from '../../services/process-filter-cloud.service'; import { ProcessFilterCloudModel } from '../../models/process-filter-cloud.model'; import { AppConfigService, IconModule, TranslationService } from '@alfresco/adf-core'; import { FilterParamsModel } from '../../../../task/task-filters/models/filter-cloud.model'; import { catchError, map } from 'rxjs/operators'; -import { ProcessListCloudService } from '../../../process-list/services/process-list-cloud.service'; -import { ProcessFilterCloudAdapter } from '../../../process-list/models/process-cloud-query-request.model'; import { FilterCountersCloudService } from '../../../../services/filter-counters-cloud.service'; -import { FilterCounterEntityType } from '../../../../models/filter-counters-cloud.model'; +import { FilterCounterEntityType, FilterCountersResult } from '../../../../models/filter-counters-cloud.model'; import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop'; import { TranslatePipe } from '@ngx-translate/core'; import { AsyncPipe } from '@angular/common'; @@ -45,15 +43,6 @@ export class ProcessFiltersCloudComponent implements OnInit, OnChanges { @Input() appName: string = ''; - /** - * (optional) From Activiti 8.7.0 forward, use the 'POST' method to get the process count. - * - * @deprecated only used by the backends without `POST /query/v1/count`. It will be removed, - * along with the 'GET' method, in ADF 10.0.0. - */ - @Input() - searchApiMethod: 'GET' | 'POST' = 'GET'; - /** (optional) The filter to be selected by default */ @Input() filterParam: FilterParamsModel; @@ -92,13 +81,11 @@ export class ProcessFiltersCloudComponent implements OnInit, OnChanges { notificationDebounceTime = 3000; private filtersLoadedFor?: string; private countersSubscription?: Subscription; - private batchedCounters = true; private readonly destroyRef = inject(DestroyRef); private readonly processFilterCloudService = inject(ProcessFilterCloudService); private readonly translationService = inject(TranslationService); private readonly appConfigService = inject(AppConfigService); - private readonly processListCloudService = inject(ProcessListCloudService); private readonly filterCountersCloudService = inject(FilterCountersCloudService); private readonly activatedRoute = inject(ActivatedRoute); protected readonly currentRouteFilterId = toSignal(this.activatedRoute.queryParamMap.pipe(map((params) => params.get('filterId')))); @@ -215,7 +202,7 @@ export class ProcessFiltersCloudComponent implements OnInit, OnChanges { if (filter) { this.selectFilter(filter); this.filterClicked.emit(this.currentFilter); - this.refreshFilterCounter(this.currentFilter); + this.filterCountersCloudService.refreshFilterCounters(this.appName); this.updatedFiltersSet.delete(filter.key); } else { this.currentFilter = undefined; @@ -253,49 +240,6 @@ export class ProcessFiltersCloudComponent implements OnInit, OnChanges { return this.currentFilter.name === filter.name; } - /** - * @deprecated `getFilterCounters` keeps the counters in sync with the engine events, so nothing is - * subscribed here anymore. It will be removed in ADF 10.0.0. - */ - initProcessNotification(): void { - /* Kept for backwards compatibility. */ - } - - /** - * Iterate over filters and update counters - * - * @deprecated resolves the counters one filter at a time, for the backends without the batched - * count endpoint. It will be removed in ADF 10.0.0. - */ - updateFilterCounters(): void { - this.filters.forEach((filter) => this.updateFilterCounter(filter)); - } - - /** - * Get current value for filter and check if value has changed - * - * @param filter filter - * @deprecated resolves the counter of one filter, for the backends without the batched count - * endpoint. It will be removed in ADF 10.0.0. - */ - updateFilterCounter(filter: ProcessFilterCloudModel): void { - const filterKey = filter?.showCounter ? filter.key : undefined; - if (!filterKey) { - return; - } - - /* Building the query throws for a malformed filter: `defer` turns that into a stream error to catch. */ - defer(() => this.fetchProcessFilterCounter(filter)) - .pipe( - catchError(() => EMPTY), - takeUntilDestroyed(this.destroyRef) - ) - .subscribe((counter) => { - this.checkIfFilterValuesHasBeenUpdated(filterKey, counter); - this.counters = { ...this.counters, [filterKey]: counter }; - }); - } - checkIfFilterValuesHasBeenUpdated(filterKey: string, filterValue: number): void { if (this.currentFiltersValues[filterKey] === undefined || this.currentFiltersValues[filterKey] !== filterValue) { this.currentFiltersValues = { ...this.currentFiltersValues, [filterKey]: filterValue }; @@ -345,17 +289,10 @@ export class ProcessFiltersCloudComponent implements OnInit, OnChanges { this.filterCountersCloudService.getFilterCounters(appName, FilterCounterEntityType.PROCESS_INSTANCE) ]) .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe(([, { counters, batched }]) => { - this.batchedCounters = batched; - if (batched) { - this.applyFilterCounters(counters); - } else { - this.updateFilterCounters(); - } - }); + .subscribe(([, counters]) => this.applyFilterCounters(counters)); } - private applyFilterCounters(counters: { [filterKey: string]: number }): void { + private applyFilterCounters(counters: FilterCountersResult): void { this.filters.forEach((filter) => { /* A filter without a key holds no request id. */ const filterKey = filter?.showCounter ? filter.key : undefined; @@ -364,9 +301,8 @@ export class ProcessFiltersCloudComponent implements OnInit, OnChanges { } const counter = counters[filterKey]; + /* A filter the request left out keeps the counter it holds, rather than showing a wrong one. */ if (counter === undefined) { - /* Left out of the batch: counted on its own. */ - this.updateFilterCounter(filter); return; } @@ -374,18 +310,4 @@ export class ProcessFiltersCloudComponent implements OnInit, OnChanges { this.counters = { ...this.counters, [filterKey]: counter }; }); } - - private refreshFilterCounter(filter?: ProcessFilterCloudModel): void { - if (this.batchedCounters) { - this.filterCountersCloudService.refreshFilterCounters(this.appName); - } else if (filter) { - this.updateFilterCounter(filter); - } - } - - private fetchProcessFilterCounter(filter: ProcessFilterCloudModel): Observable { - return this.searchApiMethod === 'POST' - ? this.processListCloudService.getProcessListCount(new ProcessFilterCloudAdapter(filter)) - : this.processListCloudService.getProcessCounter(filter.appName, filter.status); - } } diff --git a/lib/process-services-cloud/src/lib/process/process-list/services/process-list-cloud.service.spec.ts b/lib/process-services-cloud/src/lib/process/process-list/services/process-list-cloud.service.spec.ts index e3f8ec54e1..d83aa469eb 100644 --- a/lib/process-services-cloud/src/lib/process/process-list/services/process-list-cloud.service.spec.ts +++ b/lib/process-services-cloud/src/lib/process/process-list/services/process-list-cloud.service.spec.ts @@ -401,40 +401,4 @@ describe('ProcessListCloudService', () => { expect(requestBodyParams.variableKeys[1]).toBe('test-two'); }); }); - - describe('getProcessListCount', () => { - it('should concat the app name to the request url', async () => { - const taskRequest = { - appName: 'fakeName' - } as ProcessListRequestModel; - requestSpy.and.callFake(returnCallUrl); - - const res = await firstValueFrom(service.getProcessListCount(taskRequest)); - - expect(res).toBeDefined(); - expect(res).not.toBeNull(); - expect(res).toContain('fakeName/query/v1/process-instances/count'); - }); - - it('should return 0 if response is falsy for getProcessListCount', async () => { - const taskRequest = { - appName: 'fakeName', - pagination: { skipCount: 0, maxItems: 20 } - } as ProcessListRequestModel; - requestSpy.and.callFake(() => Promise.resolve(null)); - - const res = await firstValueFrom(service.getProcessListCount(taskRequest)); - - expect(res).toBe(0); - }); - - it('should throw error if appName is not configured in getProcessListCount', async () => { - const taskRequest = { appName: null } as ProcessListRequestModel; - requestSpy.and.callFake(returnCallUrl); - - const res = await firstValueFrom(service.getProcessListCount(taskRequest).pipe(catchError((error) => of(error.message)))); - - expect(res).toBe('Appname not configured'); - }); - }); }); diff --git a/lib/process-services-cloud/src/lib/process/process-list/services/process-list-cloud.service.ts b/lib/process-services-cloud/src/lib/process/process-list/services/process-list-cloud.service.ts index bffa0f95dd..6c121f6cfd 100644 --- a/lib/process-services-cloud/src/lib/process/process-list/services/process-list-cloud.service.ts +++ b/lib/process-services-cloud/src/lib/process/process-list/services/process-list-cloud.service.ts @@ -174,48 +174,6 @@ export class ProcessListCloudService extends BaseCloudService { return this.fetchProcessList(requestNode).pipe(map((processes) => processes.list.pagination.totalItems)); } - /** - * Finds a process using an object with optional query properties. - * - * @param appName app name - * @param status filter status - * @returns Total items - */ - getProcessCounter(appName: string, status: string): Observable { - const callback = (url: string, queryParams: any) => this.get(url, queryParams); - let queryUrl: string; - const defaultQueryUrl = 'query/v1/process-instances'; - const requestNode: ProcessQueryCloudRequestModel = { - appName, - appVersion: '', - initiator: null, - id: '', - name: null, - processDefinitionId: '', - processDefinitionName: null, - processDefinitionKey: '', - status, - businessKey: '', - startFrom: null, - startTo: null, - completedFrom: null, - completedTo: null, - suspendedFrom: null, - suspendedTo: null, - completedDate: '', - maxItems: 1, - skipCount: 0, - sorting: [ - { - orderBy: 'startDate', - direction: 'DESC' - } - ] - }; - - return this.getProcess(callback, defaultQueryUrl, requestNode, queryUrl).pipe(map((tasks) => tasks?.list?.pagination?.totalItems)); - } - /** * Finds a process using an object with optional query properties in admin app. * @@ -239,17 +197,6 @@ export class ProcessListCloudService extends BaseCloudService { return this.getProcess(callback, defaultQueryUrl, requestNode, queryUrl); } - getProcessListCount(requestNode: ProcessListRequestModel): Observable { - if (!requestNode?.appName) { - return throwError(() => new Error('Appname not configured')); - } - - const queryUrl = `${this.getBasePath(requestNode.appName)}/query/v1/process-instances/count`; - const queryData = this.buildQueryData(requestNode); - - return this.post(queryUrl, queryData).pipe(map((response) => response || 0)); - } - private getVariableKeysFromQueryParams(queryParams: any): string[] { if (!queryParams['variableKeys'] || queryParams['variableKeys'].length <= 0) { return []; diff --git a/lib/process-services-cloud/src/lib/services/filter-counters-cloud.service.spec.ts b/lib/process-services-cloud/src/lib/services/filter-counters-cloud.service.spec.ts index 16e1b80f86..1ab400ce65 100644 --- a/lib/process-services-cloud/src/lib/services/filter-counters-cloud.service.spec.ts +++ b/lib/process-services-cloud/src/lib/services/filter-counters-cloud.service.spec.ts @@ -171,10 +171,7 @@ describe('FilterCountersCloudService', () => { }); it('should resolve the counters of both entity types with a single request', async () => { - expect(await bothCounters()).toEqual([ - { counters: { 'my-tasks': 5, 'queued-tasks': 0 }, batched: true }, - { counters: { 'running-processes': 5 }, batched: true } - ]); + expect(await bothCounters()).toEqual([{ 'my-tasks': 5, 'queued-tasks': 0 }, { 'running-processes': 5 }]); expect(postSpy).toHaveBeenCalledTimes(1); }); @@ -238,7 +235,7 @@ describe('FilterCountersCloudService', () => { it('should leave out a filter without a key, since it holds no request id', async () => { getProcessFiltersSpy.and.returnValue(of([processFilter({ key: null, status: 'RUNNING', showCounter: true })])); - expect(await processCounters()).toEqual({ counters: {}, batched: true }); + expect(await processCounters()).toEqual({}); expect(postSpy).not.toHaveBeenCalled(); }); @@ -255,40 +252,23 @@ describe('FilterCountersCloudService', () => { getTaskListFiltersSpy.and.returnValue(of([])); getProcessFiltersSpy.and.returnValue(of([])); - expect(await taskCounters()).toEqual({ counters: {}, batched: true }); + expect(await taskCounters()).toEqual({}); expect(postSpy).not.toHaveBeenCalled(); }); - describe('when the batched count endpoint is not available', () => { - it('should report the counters as not batched', async () => { - postSpy.and.returnValue(throwError(() => ({ status: 404 }))); - - expect(await taskCounters()).toEqual({ counters: {}, batched: false }); - }); - - it('should not call the endpoint again for the same app', async () => { - postSpy.and.returnValue(throwError(() => ({ status: 404 }))); - - await taskCounters(); - expect(await processCounters()).toEqual({ counters: {}, batched: false }); - - expect(postSpy).toHaveBeenCalledTimes(1); - }); - - it('should keep calling the endpoint of the apps that do hold it', async () => { - postSpy.and.returnValue(throwError(() => ({ status: 404 }))); - await taskCounters(); - - postSpy.and.returnValue(of(countersMock)); - expect(await taskCounters('other-app')).toEqual({ counters: { 'my-tasks': 5, 'queued-tasks': 0 }, batched: true }); - }); - - it('should keep calling the endpoint after a transient failure', async () => { + describe('when the count request fails', () => { + it('should resolve no counter, rather than breaking the stream', async () => { postSpy.and.returnValue(throwError(() => ({ status: 500 }))); - expect(await taskCounters()).toEqual({ counters: {}, batched: false }); + + expect(await taskCounters()).toEqual({}); + }); + + it('should keep calling the endpoint afterwards', async () => { + postSpy.and.returnValue(throwError(() => ({ status: 404 }))); + expect(await taskCounters()).toEqual({}); postSpy.and.returnValue(of(countersMock)); - expect(await taskCounters()).toEqual({ counters: { 'my-tasks': 5, 'queued-tasks': 0 }, batched: true }); + expect(await taskCounters()).toEqual({ 'my-tasks': 5, 'queued-tasks': 0 }); expect(postSpy).toHaveBeenCalledTimes(2); }); }); @@ -441,7 +421,7 @@ describe('FilterCountersCloudService', () => { const result = await firstValueFrom(tasksOnlyService.getFilterCounters('mock-app', FilterCounterEntityType.TASK)); - expect(result).toEqual({ counters: { 'my-tasks': 5, 'queued-tasks': 0 }, batched: true }); + expect(result).toEqual({ 'my-tasks': 5, 'queued-tasks': 0 }); }); it('should leave the filters of the family that is not wired out of the request', async () => { @@ -565,7 +545,7 @@ describe('FilterCountersCloudService', () => { tick(3000); expect(results.length).toBe(2); - expect(results[1]).toEqual({ counters: { 'my-tasks': 9 }, batched: true }); + expect(results[1]).toEqual({ 'my-tasks': 9 }); })); it('should not subscribe to the events of an entity type that is not on screen', fakeAsync(() => { diff --git a/lib/process-services-cloud/src/lib/services/filter-counters-cloud.service.ts b/lib/process-services-cloud/src/lib/services/filter-counters-cloud.service.ts index ce74070247..39e9311e1c 100644 --- a/lib/process-services-cloud/src/lib/services/filter-counters-cloud.service.ts +++ b/lib/process-services-cloud/src/lib/services/filter-counters-cloud.service.ts @@ -38,8 +38,6 @@ import { FilterCountersResult } from '../models/filter-counters-cloud.model'; -const BATCHED_COUNTERS_UNAVAILABLE_STATUSES = [404, 501]; - interface FilterCountersFilters { [FilterCounterEntityType.TASK]: TaskFilterCloudModel[]; [FilterCounterEntityType.PROCESS_INSTANCE]: ProcessFilterCloudModel[]; @@ -102,10 +100,9 @@ export class FilterCountersCloudService extends BaseCloudService { private readonly activeEntityTypesPerApp = new Map>(); private readonly subscribersPerEntityType = new Map(); private readonly eventSubscriptionsPerEntityType = new Map(); - private readonly appsWithoutBatchedCounters = new Set(); private readonly taskFiltersPerApp = new Map>(); private readonly processFiltersPerApp = new Map>(); - private readonly countersPerApp = new Map>(); + private readonly countersPerApp = new Map>(); get notificationDebounceTime(): number { return this.appConfigService.get('notificationDebounceTime', 3000); @@ -149,7 +146,7 @@ export class FilterCountersCloudService extends BaseCloudService { return this.getCounters(appName); }).pipe( - map(({ counters, batched }) => ({ counters: counters[entityType] ?? {}, batched })), + map((counters) => counters[entityType] ?? {}), finalize(() => this.deactivateEntityType(appName, entityType)) ); } @@ -291,7 +288,7 @@ export class FilterCountersCloudService extends BaseCloudService { return filters$; } - private getCounters(appName: string): Observable<{ counters: FilterCounters; batched: boolean }> { + private getCounters(appName: string): Observable { let counters$ = this.countersPerApp.get(appName); if (!counters$) { counters$ = this.recounts(appName).pipe( @@ -304,22 +301,12 @@ export class FilterCountersCloudService extends BaseCloudService { return counters$; } - private resolveCounters(appName: string): Observable<{ counters: FilterCounters; batched: boolean }> { - if (this.appsWithoutBatchedCounters.has(appName)) { - return of({ counters: {}, batched: false }); - } - + private resolveCounters(appName: string): Observable { return this.getFiltersForCounters(appName).pipe( take(1), switchMap((filters) => this.fetchFilterCounters(appName, this.buildRequest(filters))), - map((counters) => ({ counters, batched: true })), - catchError((error) => { - if (BATCHED_COUNTERS_UNAVAILABLE_STATUSES.includes(error?.status)) { - this.appsWithoutBatchedCounters.add(appName); - } - - return of({ counters: {}, batched: false }); - }) + /* A failed count leaves the counters as they are, rather than breaking the stream. */ + catchError(() => of({})) ); } @@ -382,7 +369,7 @@ export class FilterCountersCloudService extends BaseCloudService { try { return { ...buildQuery(filter), requestId: filter.key as string }; } catch { - /* Left out of the batch and counted on its own. */ + /* A malformed filter is left without a counter, so the others still hold one. */ return undefined; } }) diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters/task-filters-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters/task-filters-cloud.component.spec.ts index cbc46aa37b..17c30557e8 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters/task-filters-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters/task-filters-cloud.component.spec.ts @@ -25,11 +25,8 @@ import { LocalPreferenceCloudService } from '../../../../services/local-preferen import { defaultTaskFiltersMock, fakeGlobalFilter, taskNotifications } from '../../mock/task-filters-cloud.mock'; import { TaskFilterCloudService } from '../../services/task-filter-cloud.service'; import { TaskFiltersCloudComponent } from './task-filters-cloud.component'; -import { TaskListCloudService } from '../../../task-list/services/task-list-cloud.service'; import { HarnessLoader } from '@angular/cdk/testing'; -import { MatNavListItemHarness } from '@angular/material/list/testing'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; -import { TaskFilterCloudAdapter } from '../../../../models/filter-cloud-model'; import { ApolloTestingModule } from 'apollo-angular/testing'; import { TaskFilterCloudModel } from '../../models/filter-cloud.model'; import { MatIconHarness } from '@angular/material/icon/testing'; @@ -45,21 +42,18 @@ class DummyComponent {} describe('TaskFiltersCloudComponent', () => { let loader: HarnessLoader; let taskFilterService: TaskFilterCloudService; - let taskListService: TaskListCloudService; let appConfigService: AppConfigService; let component: TaskFiltersCloudComponent; let fixture: ComponentFixture; - let getTaskFilterCounterSpy: jasmine.Spy; let getTaskListFiltersSpy: jasmine.Spy; - let getTaskListCountSpy: jasmine.Spy; let getEngineEventsSpy: jasmine.Spy; let filterCountersService: FilterCountersCloudService; let getFilterCountersSpy: jasmine.Spy; let refreshFilterCountersSpy: jasmine.Spy; let router: Router; - const configureTestingModule = async (searchApiMethod: 'GET' | 'POST') => { + const configureTestingModule = async () => { TestBed.configureTestingModule({ imports: [NoopAuthModule, TaskFiltersCloudComponent, ApolloTestingModule], providers: [ @@ -82,15 +76,10 @@ describe('TaskFiltersCloudComponent', () => { ] }); taskFilterService = TestBed.inject(TaskFilterCloudService); - taskListService = TestBed.inject(TaskListCloudService); filterCountersService = TestBed.inject(FilterCountersCloudService); - getTaskFilterCounterSpy = spyOn(taskFilterService, 'getTaskFilterCounter').and.returnValue(of(11)); - getTaskListCountSpy = spyOn(taskListService, 'getTaskListCount').and.returnValue(of(11)); getEngineEventsSpy = spyOn(filterCountersService, 'getEngineEvents').and.returnValue(of(taskNotifications)); getTaskListFiltersSpy = spyOn(filterCountersService, 'getTaskFilters').and.returnValue(of(fakeGlobalFilter)); - getFilterCountersSpy = spyOn(filterCountersService, 'getFilterCounters').and.returnValue( - of({ counters: { 'fake-involved-tasks': 11 }, batched: true }) - ); + getFilterCountersSpy = spyOn(filterCountersService, 'getFilterCounters').and.returnValue(of({ 'fake-involved-tasks': 11 })); refreshFilterCountersSpy = spyOn(filterCountersService, 'refreshFilterCounters'); appConfigService = TestBed.inject(AppConfigService); @@ -99,7 +88,6 @@ describe('TaskFiltersCloudComponent', () => { component = fixture.componentInstance; loader = TestbedHarnessEnvironment.loader(fixture); - component.searchApiMethod = searchApiMethod; TestBed.inject(ActivatedRoute); router = TestBed.inject(Router); await RouterTestingHarness.create(); @@ -115,9 +103,9 @@ describe('TaskFiltersCloudComponent', () => { fixture.destroy(); }); - describe('searchApiMethod set to GET', () => { + describe('filters on screen', () => { beforeEach(async () => { - await configureTestingModule('GET'); + await configureTestingModule(); }); it('should attach specific icon for each filter if hasIcon is true', async () => { @@ -350,132 +338,9 @@ describe('TaskFiltersCloudComponent', () => { }); }); - describe('searchApiMethod set to POST', () => { + describe('filter selection and counters', () => { beforeEach(async () => { - await configureTestingModule('POST'); - component.showIcons = true; - }); - - it('should attach specific icon for each filter if hasIcon is true', async () => { - await bindAppName(); - - const filterIcons = await loader.getAllHarnesses(MatIconHarness.with({ selector: '[data-automation-id="adf-filter-icon"]' })); - - expect(component.filters.length).toBe(3); - expect(filterIcons.length).toBe(3); - expect(await filterIcons[0].getName()).toContain('adjust'); - expect(await filterIcons[1].getName()).toContain('done'); - expect(await filterIcons[2].getName()).toContain('inbox'); - }); - - it('should not attach icons for each filter if showIcons is false', async () => { - component.showIcons = false; - await bindAppName(); - - const filterIcons = await loader.getAllHarnesses(MatIconHarness.with({ selector: '[data-automation-id="adf-filter-icon"]' })); - expect(filterIcons.length).toBe(0); - }); - - it('should display the filters', async () => { - await bindAppName(); - - const filters = fixture.debugElement.queryAll(By.css('.adf-task-filters__entry')); - - expect(component.filters.length).toBe(3); - expect(filters.length).toBe(3); - expect(filters[0].nativeElement.innerText).toContain('FakeInvolvedTasks'); - expect(filters[1].nativeElement.innerText).toContain('FakeMyTasks1'); - expect(filters[2].nativeElement.innerText).toContain('FakeMyTasks2'); - }); - - it('should not select any filter as default', async () => { - await bindAppName(); - - expect(component.currentFilter).toBeUndefined(); - }); - - it('should emit filterClicked when a filter is clicked from the UI', async () => { - await bindAppName(); - const spy = spyOn(component.filterClicked, 'emit'); - - const filterButton = await loader.getHarness( - MatNavListItemHarness.with({ selector: `[data-automation-id="${fakeGlobalFilter[0].key}_filter"]` }) - ); - await filterButton.click(); - - expect(spy).toHaveBeenCalledWith(fakeGlobalFilter[0]); - }); - - it('should display filter counter if property set to true', async () => { - await bindAppName(); - - const filterCounters = fixture.debugElement.queryAll(By.css('.adf-task-filters__entry-counter')); - - expect(component.filters.length).toBe(3); - expect(filterCounters.length).toBe(1); - expect(filterCounters[0].nativeElement.innerText).toContain('11'); - }); - - it('should update filter counter when notification received', async () => { - await bindAppName(); - - const updatedFilterCounters = fixture.debugElement.queryAll(By.css('span.adf-active')); - - expect(updatedFilterCounters.length).toBe(1); - expect(Object.keys(component.counters).length).toBe(3); - expect(component.counters['fake-involved-tasks']).toBeDefined(); - }); - - it('should not update filter counter when notifications are disabled from app.config.json', async () => { - spyOn(appConfigService, 'get').and.returnValue(false); - await bindAppName(); - - expect(fixture.componentInstance.counters).toBeDefined(); - const updatedFilterCounters = fixture.debugElement.queryAll(By.css('span.adf-active')); - expect(updatedFilterCounters.length).toBe(0); - }); - - it('should reset filter counter notification when filter is selected', async () => { - await bindAppName(); - spyOn(appConfigService, 'get').and.returnValue(true); - const change = new SimpleChange(null, { key: fakeGlobalFilter[0].key }, true); - - let updatedFilterCounters = fixture.debugElement.queryAll(By.css('span.adf-active')); - expect(updatedFilterCounters.length).toBe(1); - - component.filters = fakeGlobalFilter; - component.currentFilter = null; - - component.ngOnChanges({ filterParam: change }); - fixture.detectChanges(); - - updatedFilterCounters = fixture.debugElement.queryAll(By.css('span.adf-active')); - expect(updatedFilterCounters.length).toBe(0); - }); - - it('should refresh the filter counters when a filter is selected', async () => { - await bindAppName(); - - const filterButton = await loader.getHarness( - MatNavListItemHarness.with({ selector: `[data-automation-id="${fakeGlobalFilter[0].key}_filter"]` }) - ); - await filterButton.click(); - - expect(refreshFilterCountersSpy).toHaveBeenCalledWith('my-app-1'); - }); - - it('should resolve the counters with the POST method when the batched endpoint is not available', async () => { - getFilterCountersSpy.and.returnValue(of({ counters: {}, batched: false })); - - await bindAppName(); - - expect(getTaskListCountSpy).toHaveBeenCalledWith(new TaskFilterCloudAdapter(fakeGlobalFilter[0])); - }); - }); - - describe('API agnostic', () => { - beforeEach(async () => { - await configureTestingModule('GET'); + await configureTestingModule(); }); it('should emit an error with a bad response', (done) => { @@ -681,16 +546,16 @@ describe('TaskFiltersCloudComponent', () => { expect(component.updatedCountersSet.has(fakeFilterKey)).toBe(true); }); - it('should resolve the counter only of the filters with a counter enabled', () => { + it('should hold the counter only of the filters with a counter enabled', async () => { const filterWithCounter = new TaskFilterCloudModel({ ...defaultTaskFiltersMock[0], showCounter: true }); const filterWithoutCounter = new TaskFilterCloudModel({ ...defaultTaskFiltersMock[1], showCounter: false }); - getTaskFilterCounterSpy.calls.reset(); + getTaskListFiltersSpy.and.returnValue(of([filterWithCounter, filterWithoutCounter])); + getFilterCountersSpy.and.returnValue(of({ [filterWithCounter.key]: 7, [filterWithoutCounter.key]: 9 })); - component.filters = [filterWithCounter, filterWithoutCounter]; - component.updateFilterCounters(); + await bindAppName(); - expect(getTaskFilterCounterSpy).toHaveBeenCalledTimes(1); - expect(getTaskFilterCounterSpy).toHaveBeenCalledWith(filterWithCounter); + expect(component.counters[filterWithCounter.key]).toBe(7); + expect(component.counters[filterWithoutCounter.key]).toBe(0); }); describe('Batched counters', () => { @@ -705,7 +570,7 @@ describe('TaskFiltersCloudComponent', () => { it('should hold the counters until the filters they belong to arrive', async () => { const filters$ = new Subject(); getTaskListFiltersSpy.and.returnValue(filters$.asObservable()); - getFilterCountersSpy.and.returnValue(of({ counters: { 'fake-involved-tasks': 9 }, batched: true })); + getFilterCountersSpy.and.returnValue(of({ 'fake-involved-tasks': 9 })); await bindAppName(); expect(component.counters['fake-involved-tasks']).toBeUndefined(); @@ -723,7 +588,7 @@ describe('TaskFiltersCloudComponent', () => { }); it('should hold the counters resolved by the batched count request', async () => { - getFilterCountersSpy.and.returnValue(of({ counters: { 'fake-involved-tasks': 9 }, batched: true })); + getFilterCountersSpy.and.returnValue(of({ 'fake-involved-tasks': 9 })); await bindAppName(); @@ -731,7 +596,7 @@ describe('TaskFiltersCloudComponent', () => { }); it('should emit the filters whose counter changed', async () => { - getFilterCountersSpy.and.returnValue(of({ counters: { 'fake-involved-tasks': 9 }, batched: true })); + getFilterCountersSpy.and.returnValue(of({ 'fake-involved-tasks': 9 })); const updatedFilterSpy = spyOn(component.updatedFilter, 'emit'); await bindAppName(); @@ -739,19 +604,20 @@ describe('TaskFiltersCloudComponent', () => { expect(updatedFilterSpy).toHaveBeenCalledWith('fake-involved-tasks'); }); - it('should resolve the counter of a filter the batch left out on its own', async () => { - getFilterCountersSpy.and.returnValue(of({ counters: {}, batched: true })); + it('should keep the counter of a filter the request left out', async () => { + getFilterCountersSpy.and.returnValue(of({ 'fake-involved-tasks': 9 })); + await bindAppName(); + expect(component.counters['fake-involved-tasks']).toBe(9); + getFilterCountersSpy.and.returnValue(of({})); await bindAppName(); - expect(getTaskFilterCounterSpy).toHaveBeenCalledWith(fakeGlobalFilter[0]); - expect(component.counters['fake-involved-tasks']).toBe(11); + expect(component.counters['fake-involved-tasks']).toBe(9); }); it('should keep the counters of the other filters when one counter cannot be resolved', async () => { getTaskListFiltersSpy.and.returnValue(of([fakeGlobalFilter[0], { ...fakeGlobalFilter[1], showCounter: true }])); - getFilterCountersSpy.and.returnValue(of({ counters: { 'fake-involved-tasks': 4 }, batched: true })); - getTaskFilterCounterSpy.and.throwError('the query of the filter cannot be built'); + getFilterCountersSpy.and.returnValue(of({ 'fake-involved-tasks': 4 })); await bindAppName(); @@ -759,15 +625,6 @@ describe('TaskFiltersCloudComponent', () => { expect(component.counters['fake-my-task1']).toBe(0); }); - it('should resolve the counters one filter at a time when the batched endpoint is not available', async () => { - getFilterCountersSpy.and.returnValue(of({ counters: {}, batched: false })); - - await bindAppName(); - - expect(getTaskFilterCounterSpy).toHaveBeenCalled(); - expect(component.counters['fake-involved-tasks']).toBe(11); - }); - it('should refresh the counters of every filter when a filter is clicked', async () => { await bindAppName(); @@ -775,17 +632,6 @@ describe('TaskFiltersCloudComponent', () => { expect(refreshFilterCountersSpy).toHaveBeenCalledWith('my-app-1'); }); - - it('should refresh the counter of the clicked filter alone when the batched endpoint is not available', async () => { - getFilterCountersSpy.and.returnValue(of({ counters: {}, batched: false })); - await bindAppName(); - getTaskFilterCounterSpy.calls.reset(); - - component.onFilterClick(fakeGlobalFilter[0]); - - expect(refreshFilterCountersSpy).not.toHaveBeenCalled(); - expect(getTaskFilterCounterSpy).toHaveBeenCalledTimes(1); - }); }); describe('Highlight Selected Filter', () => { diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters/task-filters-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters/task-filters-cloud.component.ts index 885148e62c..f1c2389acb 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters/task-filters-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters/task-filters-cloud.component.ts @@ -15,8 +15,8 @@ * limitations under the License. */ -import { Component, EventEmitter, inject, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; -import { combineLatest, defer, EMPTY, Observable, of, Subscription } from 'rxjs'; +import { Component, EventEmitter, inject, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core'; +import { combineLatest, Observable, of, Subscription } from 'rxjs'; import { TaskFilterCloudService } from '../../services/task-filter-cloud.service'; import { FilterParamsModel, TaskFilterCloudModel } from '../../models/filter-cloud.model'; import { AppConfigService, IconModule, TranslationService } from '@alfresco/adf-core'; @@ -24,10 +24,8 @@ import { catchError, map } from 'rxjs/operators'; import { BaseTaskFiltersCloudComponent } from '../base-task-filters-cloud.component'; import { TaskDetailsCloudModel } from '../../../models/task-details-cloud.model'; import { TaskCloudEngineEvent } from '../../../../models/engine-event-cloud.model'; -import { TaskListCloudService } from '../../../task-list/services/task-list-cloud.service'; -import { TaskFilterCloudAdapter } from '../../../../models/filter-cloud-model'; import { FilterCountersCloudService } from '../../../../services/filter-counters-cloud.service'; -import { FilterCounterEntityType } from '../../../../models/filter-counters-cloud.model'; +import { FilterCounterEntityType, FilterCountersResult } from '../../../../models/filter-counters-cloud.model'; import { takeUntilDestroyed, toSignal } from '@angular/core/rxjs-interop'; import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; import { TranslatePipe } from '@ngx-translate/core'; @@ -44,15 +42,6 @@ import { AsyncPipe } from '@angular/common'; export class TaskFiltersCloudComponent extends BaseTaskFiltersCloudComponent implements OnInit, OnChanges { protected readonly TASKS_ROUTE = '/task-list-cloud'; - /** - * (optional) From Activiti 8.7.0 forward, use the 'POST' method to get the task count. - * - * @deprecated only used by the backends without `POST /query/v1/count`. It will be removed, - * along with the 'GET' method, in ADF 10.0.0. - */ - @Input() - searchApiMethod: 'GET' | 'POST' = 'GET'; - /** Emitted when a filter is being selected based on the filterParam input. */ @Output() filterSelected = new EventEmitter(); @@ -77,10 +66,8 @@ export class TaskFiltersCloudComponent extends BaseTaskFiltersCloudComponent imp currentFiltersValues: { [key: string]: number } = {}; private filtersLoadedFor?: string; private countersSubscription?: Subscription; - private batchedCounters = true; private readonly taskFilterCloudService = inject(TaskFilterCloudService); - private readonly taskListCloudService = inject(TaskListCloudService); private readonly filterCountersCloudService = inject(FilterCountersCloudService); private readonly translationService = inject(TranslationService); private readonly appConfigService = inject(AppConfigService); @@ -142,40 +129,6 @@ export class TaskFiltersCloudComponent extends BaseTaskFiltersCloudComponent imp this.filters.forEach((filter) => (this.counters[filter.key] = 0)); } - /** - * Iterate over filters and update counters - * - * @deprecated resolves the counters one filter at a time, for the backends without the batched - * count endpoint. It will be removed in ADF 10.0.0. - */ - updateFilterCounters(): void { - this.filters.forEach((filter) => this.updateFilterCounter(filter)); - } - - /** - * Get current value for filter and check if value has changed - * - * @param filter filter - * @deprecated resolves the counter of one filter, for the backends without the batched count - * endpoint. It will be removed in ADF 10.0.0. - */ - updateFilterCounter(filter: TaskFilterCloudModel): void { - if (!filter?.showCounter) { - return; - } - - /* Building the query throws for a malformed filter: `defer` turns that into a stream error to catch. */ - defer(() => this.fetchTaskFilterCounter(filter)) - .pipe( - catchError(() => EMPTY), - takeUntilDestroyed(this.destroyRef) - ) - .subscribe((counter) => { - this.checkIfFilterValuesHasBeenUpdated(filter.key, counter); - this.counters = { ...this.counters, [filter.key]: counter }; - }); - } - initFilterCounterNotifications(): void { if (this.appName && this.enableNotifications) { this.filterCountersCloudService @@ -244,7 +197,7 @@ export class TaskFiltersCloudComponent extends BaseTaskFiltersCloudComponent imp onFilterClick(filter: FilterParamsModel) { if (filter) { this.selectFilter(filter); - this.refreshFilterCounter(this.currentFilter); + this.filterCountersCloudService.refreshFilterCounters(this.appName); this.filterClicked.emit(this.currentFilter); this.updatedCountersSet.delete(filter.key); } else { @@ -294,17 +247,10 @@ export class TaskFiltersCloudComponent extends BaseTaskFiltersCloudComponent imp this.filterCountersCloudService.getFilterCounters(appName, FilterCounterEntityType.TASK) ]) .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe(([, { counters, batched }]) => { - this.batchedCounters = batched; - if (batched) { - this.applyFilterCounters(counters); - } else { - this.updateFilterCounters(); - } - }); + .subscribe(([, counters]) => this.applyFilterCounters(counters)); } - private applyFilterCounters(counters: { [filterKey: string]: number }): void { + private applyFilterCounters(counters: FilterCountersResult): void { this.filters.forEach((filter) => { /* A filter without a key holds no request id. */ const filterKey = filter?.showCounter ? filter.key : undefined; @@ -313,8 +259,8 @@ export class TaskFiltersCloudComponent extends BaseTaskFiltersCloudComponent imp } const counter = counters[filterKey]; + /* A filter the request left out keeps the counter it holds, rather than showing a wrong one. */ if (counter === undefined) { - this.updateFilterCounter(filter); return; } @@ -323,12 +269,6 @@ export class TaskFiltersCloudComponent extends BaseTaskFiltersCloudComponent imp }); } - private fetchTaskFilterCounter(filter: TaskFilterCloudModel): Observable { - return this.searchApiMethod === 'POST' - ? this.taskListCloudService.getTaskListCount(new TaskFilterCloudAdapter(filter)) - : this.taskFilterCloudService.getTaskFilterCounter(filter); - } - /** * Reset the filters properties */ @@ -336,12 +276,4 @@ export class TaskFiltersCloudComponent extends BaseTaskFiltersCloudComponent imp this.filters = []; this.currentFilter = undefined; } - - private refreshFilterCounter(filter: TaskFilterCloudModel): void { - if (this.batchedCounters) { - this.filterCountersCloudService.refreshFilterCounters(this.appName); - } else { - this.updateFilterCounter(filter); - } - } } diff --git a/lib/process-services-cloud/src/lib/task/task-filters/services/task-filter-cloud.service.ts b/lib/process-services-cloud/src/lib/task/task-filters/services/task-filter-cloud.service.ts index e8a38a776b..371aa3a073 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/services/task-filter-cloud.service.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/services/task-filter-cloud.service.ts @@ -16,13 +16,12 @@ */ import { Injectable, inject } from '@angular/core'; -import { Observable, of, BehaviorSubject, throwError, Subject } from 'rxjs'; +import { Observable, of, BehaviorSubject, Subject } from 'rxjs'; import { TaskFilterCloudModel } from '../models/filter-cloud.model'; import { switchMap, map } from 'rxjs/operators'; import { BaseCloudService } from '../../../services/base-cloud.service'; import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface'; import { TASK_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service'; -import { TaskCloudNodePaging } from '../../../models/task-cloud.model'; import { NotificationCloudService } from '../../../services/notification-cloud.service'; import { TaskCloudEngineEvent } from '../../../models/engine-event-cloud.model'; import { IdentityUserService } from '../../../people/services/identity-user.service'; @@ -247,43 +246,6 @@ export class TaskFilterCloudService extends BaseCloudService { return defaultFilters.findIndex((filter) => filterName === filter.name) !== -1; } - /** - * Finds a task using an object with optional query properties. - * - * @returns Task information - * @param taskFilter task filter model - */ - getTaskFilterCounter(taskFilter: TaskFilterCloudModel): Observable { - if (taskFilter.appName || taskFilter.appName === '') { - const queryUrl = `${this.getBasePath(taskFilter.appName)}/query/v1/tasks`; - const queryParams = { - processInstanceId: taskFilter.processInstanceId, - processDefinitionId: taskFilter.processDefinitionId, - processDefinitionName: taskFilter.processDefinitionName, - assignee: taskFilter.assignee, - status: taskFilter.status, - taskName: taskFilter.taskName, - appName: taskFilter.appName, - assignmentType: taskFilter.assignmentType, - owner: taskFilter.owner, - taskId: taskFilter.taskId, - parentTaskId: taskFilter.parentTaskId, - priority: taskFilter.priority, - lastModifiedFrom: taskFilter.lastModifiedFrom, - lastModifiedTo: taskFilter.lastModifiedTo, - standalone: taskFilter.standalone, - createdDate: taskFilter.createdDate, - completedDate: taskFilter.completedDate, - completedBy: taskFilter.completedBy, - dueDate: taskFilter.dueDate, - maxItems: 1 - }; - return this.get(queryUrl, queryParams).pipe(map((tasks) => tasks.list.pagination.totalItems)); - } else { - return throwError('Appname not configured'); - } - } - /** * Calls update preference api to update task filter * diff --git a/lib/process-services-cloud/src/lib/task/task-list/services/task-list-cloud.service.spec.ts b/lib/process-services-cloud/src/lib/task/task-list/services/task-list-cloud.service.spec.ts index 06c214c13d..d59bbcb61e 100644 --- a/lib/process-services-cloud/src/lib/task/task-list/services/task-list-cloud.service.spec.ts +++ b/lib/process-services-cloud/src/lib/task/task-list/services/task-list-cloud.service.spec.ts @@ -238,40 +238,4 @@ describe('TaskListCloudService', () => { expect(res).toBe('Appname not configured'); }); }); - - describe('getTaskListCount', () => { - it('should concat the app name to the request url', async () => { - const taskRequest = { - appName: 'fakeName' - } as TaskListRequestModel; - requestSpy.and.callFake(returnCallUrl); - - const res = await firstValueFrom(service.getTaskListCount(taskRequest)); - - expect(res).toBeDefined(); - expect(res).not.toBeNull(); - expect(res).toContain('fakeName/query/v1/tasks/count'); - }); - - it('should return 0 if response is falsy for getTaskListCount', async () => { - const taskRequest = { - appName: 'fakeName', - pagination: { skipCount: 0, maxItems: 20 } - } as TaskListRequestModel; - requestSpy.and.callFake(() => Promise.resolve(null)); - - const res = await firstValueFrom(service.getTaskListCount(taskRequest)); - - expect(res).toBe(0); - }); - - it('should throw error if appName is not configured in getTaskListCount', async () => { - const taskRequest = { appName: null } as TaskListRequestModel; - requestSpy.and.callFake(returnCallUrl); - - const res = await firstValueFrom(service.getTaskListCount(taskRequest).pipe(catchError((error) => of(error.message)))); - - expect(res).toBe('Appname not configured'); - }); - }); }); diff --git a/lib/process-services-cloud/src/lib/task/task-list/services/task-list-cloud.service.ts b/lib/process-services-cloud/src/lib/task/task-list/services/task-list-cloud.service.ts index 6ee9c33fab..c986df3eef 100644 --- a/lib/process-services-cloud/src/lib/task/task-list/services/task-list-cloud.service.ts +++ b/lib/process-services-cloud/src/lib/task/task-list/services/task-list-cloud.service.ts @@ -124,17 +124,6 @@ export class TaskListCloudService extends BaseCloudService implements TaskListCl return this.fetchTaskList(requestNode).pipe(map((tasks) => tasks.list.pagination.totalItems)); } - getTaskListCount(requestNode: TaskListRequestModel): Observable { - if (!requestNode?.appName) { - return throwError(() => new Error('Appname not configured')); - } - - const queryUrl = `${this.getBasePath(requestNode.appName)}/query/v1/tasks/count`; - const queryData = this.buildQueryData(requestNode); - - return this.post(queryUrl, queryData).pipe(map((response) => response || 0)); - } - /** * Builds the body of a task query, with the empty properties of the request stripped out. *