mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[ACA-4259] Task filter counter notifications (#6607)
* [ACA-4259] Task Filter Counter Notifications * Improve Apollo connection for multiple apps * Improve Notification Cloud Service * Rebase branch * Rebase branch * Add peer dependencies * Rebase * Remove apollo-angular-link-http * Fix linting * Rebase branch * Fix package lock * Push package lock
This commit is contained in:
@@ -21,8 +21,11 @@
|
||||
"@alfresco/js-api": "4.2.0",
|
||||
"@alfresco/adf-core": "4.2.0",
|
||||
"@alfresco/adf-content-services": "4.2.0",
|
||||
"@apollo/client": "^3.3.7",
|
||||
"@ngx-translate/core": ">=13.0.0",
|
||||
"moment": ">=2.22.2"
|
||||
"apollo-angular": "^2.2.0",
|
||||
"moment": ">=2.22.2",
|
||||
"subscriptions-transport-ws": "^0.9.18"
|
||||
},
|
||||
"keywords": [
|
||||
"process-services-cloud",
|
||||
|
@@ -0,0 +1,23 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { TaskDetailsCloudModel } from '../task/start-task/models/task-details-cloud.model';
|
||||
|
||||
export interface TaskCloudEngineEvent {
|
||||
eventType: string;
|
||||
entity: TaskDetailsCloudModel;
|
||||
}
|
@@ -0,0 +1,72 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { TestBed, async } from '@angular/core/testing';
|
||||
import { setupTestBed } from '@alfresco/adf-core';
|
||||
import { ProcessServiceCloudTestingModule } from '../testing/process-service-cloud.testing.module';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { NotificationCloudService } from './notification-cloud.service';
|
||||
import { Apollo } from 'apollo-angular';
|
||||
|
||||
describe('NotificationCloudService', () => {
|
||||
let service: NotificationCloudService;
|
||||
let apollo: Apollo;
|
||||
|
||||
const queryMock = `
|
||||
subscription {
|
||||
engineEvents(eventType: [
|
||||
MY_EVENT
|
||||
]) {
|
||||
eventType
|
||||
entity
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
TranslateModule.forRoot(),
|
||||
ProcessServiceCloudTestingModule
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(async(() => {
|
||||
service = TestBed.inject(NotificationCloudService);
|
||||
apollo = TestBed.inject(Apollo);
|
||||
}));
|
||||
|
||||
it('should not create more than one websocket per app if it was already created', () => {
|
||||
const apolloCreateSpy = spyOn(apollo, 'create');
|
||||
const apolloSubscribeSpy = spyOn(apollo, 'subscribe');
|
||||
|
||||
service.makeGQLQuery('myAppName', queryMock);
|
||||
expect(service.appsListening.length).toBe(1);
|
||||
expect(service.appsListening[0]).toBe('myAppName');
|
||||
|
||||
service.makeGQLQuery('myAppName', queryMock);
|
||||
expect(service.appsListening.length).toBe(1);
|
||||
expect(service.appsListening[0]).toBe('myAppName');
|
||||
|
||||
service.makeGQLQuery('myAppName2', queryMock);
|
||||
expect(service.appsListening.length).toBe(2);
|
||||
expect(service.appsListening[0]).toBe('myAppName');
|
||||
expect(service.appsListening[1]).toBe('myAppName2');
|
||||
|
||||
expect(apolloCreateSpy).toHaveBeenCalledTimes(2);
|
||||
expect(apolloSubscribeSpy).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
});
|
@@ -0,0 +1,85 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Apollo } from 'apollo-angular';
|
||||
import { HttpLink } from 'apollo-angular/http';
|
||||
import { split, gql, InMemoryCache } from '@apollo/client/core';
|
||||
import { WebSocketLink } from '@apollo/client/link/ws';
|
||||
import { getMainDefinition } from '@apollo/client/utilities';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { StorageService, AppConfigService, AlfrescoApiService } from '@alfresco/adf-core';
|
||||
import { BaseCloudService } from './base-cloud.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class NotificationCloudService extends BaseCloudService {
|
||||
|
||||
appsListening = [];
|
||||
|
||||
constructor(apiService: AlfrescoApiService,
|
||||
appConfigService: AppConfigService,
|
||||
public apollo: Apollo,
|
||||
private http: HttpLink,
|
||||
private storageService: StorageService) {
|
||||
super(apiService, appConfigService);
|
||||
}
|
||||
|
||||
private getUrlDomain(appName: string) {
|
||||
return this.getBasePath(appName).split('://')[1];
|
||||
}
|
||||
|
||||
initNotificationsForApp(appName: string) {
|
||||
if (!this.appsListening.includes(appName)) {
|
||||
this.appsListening.push(appName);
|
||||
const httpLink = this.http.create({
|
||||
uri: `${this.getBasePath(appName)}/notifications/graphql`
|
||||
});
|
||||
|
||||
const webSocketLink = new WebSocketLink({
|
||||
uri: `wss://${this.getUrlDomain(appName)}/notifications/ws/graphql`,
|
||||
options: {
|
||||
reconnect: true,
|
||||
lazy: true,
|
||||
connectionParams: {
|
||||
kaInterval: 2000,
|
||||
'X-Authorization': 'Bearer ' + this.storageService.getItem('access_token')
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const link = split(
|
||||
({ query }) => {
|
||||
const definition = getMainDefinition(query);
|
||||
return definition.kind === 'OperationDefinition' && definition.operation === 'subscription';
|
||||
},
|
||||
webSocketLink,
|
||||
httpLink
|
||||
);
|
||||
|
||||
this.apollo.create(<any> {
|
||||
link,
|
||||
cache: new InMemoryCache({})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
makeGQLQuery(appName: string, gqlQuery: string) {
|
||||
this.initNotificationsForApp(appName);
|
||||
return this.apollo.subscribe({ query : gql(gqlQuery) });
|
||||
}
|
||||
}
|
@@ -18,5 +18,6 @@
|
||||
export * from './user-preference-cloud.service';
|
||||
export * from './local-preference-cloud.service';
|
||||
export * from './cloud-token.service';
|
||||
export * from './notification-cloud.service';
|
||||
export * from './preference-cloud.interface';
|
||||
export * from './form-fields.interfaces';
|
||||
|
@@ -1,16 +1,25 @@
|
||||
<ng-container *ngIf="filters$ | async as filterList; else loading">
|
||||
<div *ngFor="let filter of filterList" class="adf-task-filters__entry">
|
||||
<div *ngFor="let filter of filterList"
|
||||
class="adf-task-filters__entry">
|
||||
<button (click)="onFilterClick(filter)"
|
||||
[attr.aria-label]="filter.name | translate"
|
||||
[id]="filter.id"
|
||||
[attr.data-automation-id]="filter.key + '_filter'"
|
||||
mat-button
|
||||
[class.adf-active]="currentFilter === filter"
|
||||
class="adf-filter-action-button adf-full-width" fxLayout="row" fxLayoutAlign="space-between center">
|
||||
<adf-icon data-automation-id="adf-filter-icon" *ngIf="showIcons" [value]="filter.icon"></adf-icon>
|
||||
<span data-automation-id="adf-filter-label" class="adf-filter-action-button__label">{{ filter.name | translate }}</span>
|
||||
[attr.aria-label]="filter.name | translate"
|
||||
[id]="filter.id"
|
||||
[attr.data-automation-id]="filter.key + '_filter'"
|
||||
mat-button
|
||||
[class.adf-active]="currentFilter === filter"
|
||||
class="adf-filter-action-button adf-full-width"
|
||||
fxLayout="row"
|
||||
fxLayoutAlign="space-between center">
|
||||
<adf-icon data-automation-id="adf-filter-icon"
|
||||
*ngIf="showIcons"
|
||||
[value]="filter.icon"></adf-icon>
|
||||
<span data-automation-id="adf-filter-label"
|
||||
class="adf-filter-action-button__label">{{ filter.name | translate }}</span>
|
||||
</button>
|
||||
<span *ngIf="counters$[filter.key]" class="adf-filter-action-button__counter">
|
||||
<span *ngIf="counters$[filter.key]"
|
||||
[attr.data-automation-id]="filter.key + '_filter-counter'"
|
||||
class="adf-filter-action-button__counter"
|
||||
[class.adf-active]=wasFilterUpdated(filter.key)>
|
||||
{{ counters$[filter.key] | async }}
|
||||
</span>
|
||||
</div>
|
||||
|
@@ -1,5 +1,7 @@
|
||||
@mixin adf-cloud-task-filters-theme($theme) {
|
||||
$primary: map-get($theme, primary);
|
||||
$accent: map-get($theme, accent);
|
||||
$background: map-get($theme, background);
|
||||
|
||||
.adf {
|
||||
&-task-filters__entry {
|
||||
@@ -7,7 +9,6 @@
|
||||
padding: 12px 0 !important;
|
||||
height: 24px;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
font-size: 14px !important;
|
||||
font-weight: bold;
|
||||
opacity: 1;
|
||||
@@ -20,6 +21,7 @@
|
||||
.adf-filter-action-button {
|
||||
opacity: 0.54;
|
||||
padding: 16px;
|
||||
cursor: pointer;
|
||||
|
||||
.adf-filter-action-button__label {
|
||||
padding-left: 20px;
|
||||
@@ -29,13 +31,26 @@
|
||||
|
||||
.adf-filter-action-button__counter {
|
||||
opacity: 0.54;
|
||||
padding-left: 10px;
|
||||
padding-top: 6px;
|
||||
margin-left: 10px;
|
||||
margin-top: 6px;
|
||||
|
||||
&.adf-active {
|
||||
margin-left: 8px;
|
||||
margin-top: 5px;
|
||||
padding: 0 5px;
|
||||
border-radius: 15px;
|
||||
background-color: mat-color($accent);
|
||||
color: mat-color($mat-grey, 50);
|
||||
font-size: smaller;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&:hover {
|
||||
color: mat-color($primary);
|
||||
.adf-filter-action-button__counter, .adf-filter-action-button {
|
||||
|
||||
.adf-filter-action-button__counter,
|
||||
.adf-filter-action-button {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
@@ -46,6 +46,7 @@ export abstract class BaseTaskFiltersCloudComponent implements OnDestroy {
|
||||
error: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
counters$: {[key: string]: Observable<number>} = {};
|
||||
updatedCounters: string[] = [];
|
||||
|
||||
protected onDestroy$ = new Subject<boolean>();
|
||||
|
||||
@@ -53,4 +54,21 @@ export abstract class BaseTaskFiltersCloudComponent implements OnDestroy {
|
||||
this.onDestroy$.next(true);
|
||||
this.onDestroy$.complete();
|
||||
}
|
||||
|
||||
wasFilterUpdated(filterKey: string): boolean {
|
||||
return this.updatedCounters.includes(filterKey);
|
||||
}
|
||||
|
||||
addToUpdatedCounters(filterKey: string) {
|
||||
if (!this.updatedCounters.includes(filterKey)) {
|
||||
this.updatedCounters.push(filterKey);
|
||||
}
|
||||
}
|
||||
|
||||
resetFilterCounter(filterKey: string) {
|
||||
const filterIndex = this.updatedCounters.indexOf(filterKey);
|
||||
if (filterIndex > -1) {
|
||||
this.updatedCounters.splice(filterIndex, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -26,7 +26,7 @@ import { TaskFiltersCloudComponent } from './task-filters-cloud.component';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
|
||||
import { TaskFiltersCloudModule } from '../task-filters-cloud.module';
|
||||
import { fakeGlobalFilter } from '../mock/task-filters-cloud.mock';
|
||||
import { fakeGlobalFilter, taskNotifications } from '../mock/task-filters-cloud.mock';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
|
||||
describe('TaskFiltersCloudComponent', () => {
|
||||
@@ -34,7 +34,7 @@ describe('TaskFiltersCloudComponent', () => {
|
||||
let taskFilterService: TaskFilterCloudService;
|
||||
|
||||
const fakeGlobalFilterObservable =
|
||||
new Observable(function(observer) {
|
||||
new Observable(function (observer) {
|
||||
observer.next(fakeGlobalFilter);
|
||||
observer.complete();
|
||||
});
|
||||
@@ -69,12 +69,13 @@ describe('TaskFiltersCloudComponent', () => {
|
||||
|
||||
taskFilterService = TestBed.inject(TaskFilterCloudService);
|
||||
spyOn(taskFilterService, 'getTaskFilterCounter').and.returnValue(of(11));
|
||||
spyOn(taskFilterService, 'getTaskNotificationSubscription').and.returnValue(of(taskNotifications));
|
||||
});
|
||||
|
||||
it('should attach specific icon for each filter if hasIcon is true', async(() => {
|
||||
spyOn(taskFilterService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
|
||||
const change = new SimpleChange(undefined, 'my-app-1', true);
|
||||
component.ngOnChanges({'appName': change});
|
||||
component.ngOnChanges({ 'appName': change });
|
||||
fixture.detectChanges();
|
||||
component.showIcons = true;
|
||||
fixture.whenStable().then(() => {
|
||||
@@ -93,7 +94,7 @@ describe('TaskFiltersCloudComponent', () => {
|
||||
|
||||
component.showIcons = false;
|
||||
const change = new SimpleChange(undefined, 'my-app-1', true);
|
||||
component.ngOnChanges({'appName': change});
|
||||
component.ngOnChanges({ 'appName': change });
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
@@ -107,7 +108,7 @@ describe('TaskFiltersCloudComponent', () => {
|
||||
it('should display the filters', async(() => {
|
||||
spyOn(taskFilterService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
|
||||
const change = new SimpleChange(undefined, 'my-app-1', true);
|
||||
component.ngOnChanges({'appName': change});
|
||||
component.ngOnChanges({ 'appName': change });
|
||||
fixture.detectChanges();
|
||||
component.showIcons = true;
|
||||
fixture.whenStable().then(() => {
|
||||
@@ -126,7 +127,7 @@ describe('TaskFiltersCloudComponent', () => {
|
||||
|
||||
const appName = 'my-app-1';
|
||||
const change = new SimpleChange(null, appName, true);
|
||||
component.ngOnChanges({'appName': change});
|
||||
component.ngOnChanges({ 'appName': change });
|
||||
|
||||
component.error.subscribe((err) => {
|
||||
expect(err).toBeDefined();
|
||||
@@ -368,7 +369,7 @@ describe('TaskFiltersCloudComponent', () => {
|
||||
it('should display filter counter if property set to true', async(() => {
|
||||
spyOn(taskFilterService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
|
||||
const change = new SimpleChange(undefined, 'my-app-1', true);
|
||||
component.ngOnChanges({'appName': change});
|
||||
component.ngOnChanges({ 'appName': change });
|
||||
fixture.detectChanges();
|
||||
component.showIcons = true;
|
||||
fixture.whenStable().then(() => {
|
||||
@@ -379,4 +380,44 @@ describe('TaskFiltersCloudComponent', () => {
|
||||
expect(filterCounters[0].nativeElement.innerText).toContain('11');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should update filter counter when notification received', async(() => {
|
||||
spyOn(taskFilterService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
|
||||
const change = new SimpleChange(undefined, 'my-app-1', true);
|
||||
component.ngOnChanges({ 'appName': change });
|
||||
fixture.detectChanges();
|
||||
component.showIcons = true;
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
const updatedFilterCounters = fixture.debugElement.queryAll(By.css('span.adf-active'));
|
||||
expect(updatedFilterCounters.length).toBe(1);
|
||||
expect(Object.keys(component.counters$).length).toBe(1);
|
||||
expect(component.counters$['fake-involved-tasks']).toBeDefined();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should reset filter counter notification when filter is selected', async(() => {
|
||||
spyOn(taskFilterService, 'getTaskListFilters').and.returnValue(fakeGlobalFilterObservable);
|
||||
let change = new SimpleChange(undefined, 'my-app-1', true);
|
||||
component.ngOnChanges({ 'appName': change });
|
||||
fixture.detectChanges();
|
||||
component.showIcons = true;
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
let updatedFilterCounters = fixture.debugElement.queryAll(By.css('span.adf-active'));
|
||||
expect(updatedFilterCounters.length).toBe(1);
|
||||
|
||||
component.filters = fakeGlobalFilter;
|
||||
component.currentFilter = null;
|
||||
|
||||
change = new SimpleChange(null, { key: fakeGlobalFilter[0].key }, true);
|
||||
component.ngOnChanges({ 'filterParam': change });
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
updatedFilterCounters = fixture.debugElement.queryAll(By.css('span.adf-active'));
|
||||
expect(updatedFilterCounters.length).toBe(0);
|
||||
});
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
@@ -20,8 +20,10 @@ import { Observable } from 'rxjs';
|
||||
import { TaskFilterCloudService } from '../services/task-filter-cloud.service';
|
||||
import { TaskFilterCloudModel, FilterParamsModel } from '../models/filter-cloud.model';
|
||||
import { TranslationService } from '@alfresco/adf-core';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { map, takeUntil } from 'rxjs/operators';
|
||||
import { BaseTaskFiltersCloudComponent } from './base-task-filters-cloud.component';
|
||||
import { TaskDetailsCloudModel } from '../../start-task/models/task-details-cloud.model';
|
||||
import { TaskCloudEngineEvent } from '../../../models/engine-event-cloud.model';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-cloud-task-filters',
|
||||
@@ -37,10 +39,13 @@ export class TaskFiltersCloudComponent extends BaseTaskFiltersCloudComponent imp
|
||||
@Output()
|
||||
filterClicked = new EventEmitter<TaskFilterCloudModel>();
|
||||
|
||||
/** Emitted when filter counters are updated. */
|
||||
@Output()
|
||||
filterCounterUpdated: EventEmitter<TaskCloudEngineEvent[]> = new EventEmitter<TaskCloudEngineEvent[]>();
|
||||
|
||||
filters$: Observable<TaskFilterCloudModel[]>;
|
||||
filters: TaskFilterCloudModel[] = [];
|
||||
currentFilter: TaskFilterCloudModel;
|
||||
counters = {};
|
||||
|
||||
constructor(private taskFilterCloudService: TaskFilterCloudService,
|
||||
private translationService: TranslationService) {
|
||||
@@ -48,6 +53,7 @@ export class TaskFiltersCloudComponent extends BaseTaskFiltersCloudComponent imp
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.initFilterCounterNotifications();
|
||||
this.getFilters(this.appName);
|
||||
}
|
||||
|
||||
@@ -89,6 +95,30 @@ export class TaskFiltersCloudComponent extends BaseTaskFiltersCloudComponent imp
|
||||
});
|
||||
}
|
||||
|
||||
initFilterCounterNotifications() {
|
||||
this.taskFilterCloudService.getTaskNotificationSubscription(this.appName)
|
||||
.subscribe((result: TaskCloudEngineEvent[]) => {
|
||||
result.map((taskEvent: TaskCloudEngineEvent) => {
|
||||
this.updateFilterCounter(taskEvent.entity);
|
||||
});
|
||||
this.filterCounterUpdated.emit(result);
|
||||
});
|
||||
}
|
||||
|
||||
updateFilterCounter(filterNotification: TaskDetailsCloudModel) {
|
||||
this.filters.map((filter) => {
|
||||
if (this.isFilterPresent(filter, filterNotification)) {
|
||||
this.counters$[filter.key] = this.counters$[filter.key].pipe(map((counter) => counter + 1));
|
||||
this.addToUpdatedCounters(filter.key);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
isFilterPresent(filter: TaskFilterCloudModel, filterNotification: TaskDetailsCloudModel): boolean {
|
||||
return filter.status === filterNotification.status
|
||||
&& filter.assignee === filterNotification.assignee;
|
||||
}
|
||||
|
||||
public selectFilter(paramFilter: FilterParamsModel) {
|
||||
if (paramFilter) {
|
||||
this.currentFilter = this.filters.find((filter, index) =>
|
||||
@@ -104,7 +134,10 @@ export class TaskFiltersCloudComponent extends BaseTaskFiltersCloudComponent imp
|
||||
public selectFilterAndEmit(newParamFilter: FilterParamsModel) {
|
||||
if (newParamFilter) {
|
||||
this.selectFilter(newParamFilter);
|
||||
this.filterSelected.emit(this.currentFilter);
|
||||
if (this.currentFilter) {
|
||||
this.resetFilterCounter(this.currentFilter.key);
|
||||
this.filterSelected.emit(this.currentFilter);
|
||||
}
|
||||
} else {
|
||||
this.currentFilter = undefined;
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { assignedTaskDetailsCloudMock } from '../../task-header/mocks/task-details-cloud.mock';
|
||||
import { TaskFilterCloudModel, ServiceTaskFilterCloudModel } from '../models/filter-cloud.model';
|
||||
|
||||
export const fakeGlobalFilter = [
|
||||
@@ -23,8 +24,8 @@ export const fakeGlobalFilter = [
|
||||
key: 'fake-involved-tasks',
|
||||
icon: 'adjust',
|
||||
id: '10',
|
||||
status: 'open',
|
||||
assignee: 'fake-involved',
|
||||
status: 'ASSIGNED',
|
||||
assignee: 'AssignedTaskUser',
|
||||
showCounter: true
|
||||
}),
|
||||
new TaskFilterCloudModel({
|
||||
@@ -273,3 +274,16 @@ export const fakeTaskCloudFilters = [
|
||||
order: 'DESC'
|
||||
}
|
||||
];
|
||||
|
||||
export const taskNotifications = [
|
||||
{
|
||||
eventType: 'TASK_ASSIGNED',
|
||||
entity: assignedTaskDetailsCloudMock
|
||||
}
|
||||
];
|
||||
|
||||
export const taskCloudEngineEventsMock = {
|
||||
data: {
|
||||
engineEvents: taskNotifications
|
||||
}
|
||||
};
|
||||
|
@@ -33,15 +33,19 @@ import {
|
||||
fakePreferenceWithNoTaskFilterPreference,
|
||||
fakeTaskCloudFilters,
|
||||
fakeTaskCloudPreferenceList,
|
||||
fakeTaskFilter
|
||||
fakeTaskFilter,
|
||||
taskCloudEngineEventsMock
|
||||
} from '../mock/task-filters-cloud.mock';
|
||||
import { UserPreferenceCloudService } from '../../../services/user-preference-cloud.service';
|
||||
import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { TaskFilterCloudModel } from '../models/filter-cloud.model';
|
||||
import { NotificationCloudService } from '../../../services/notification-cloud.service';
|
||||
import { TaskCloudEngineEvent } from './../../../models/engine-event-cloud.model';
|
||||
|
||||
describe('TaskFilterCloudService', () => {
|
||||
let service: TaskFilterCloudService;
|
||||
let notificationCloudService: NotificationCloudService;
|
||||
|
||||
let getPreferencesSpy: jasmine.Spy;
|
||||
let getPreferenceByKeySpy: jasmine.Spy;
|
||||
@@ -53,7 +57,7 @@ describe('TaskFilterCloudService', () => {
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
HttpClientTestingModule
|
||||
HttpClientTestingModule
|
||||
],
|
||||
providers: [
|
||||
{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: UserPreferenceCloudService },
|
||||
@@ -64,6 +68,7 @@ describe('TaskFilterCloudService', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
service = TestBed.inject(TaskFilterCloudService);
|
||||
notificationCloudService = TestBed.inject(NotificationCloudService);
|
||||
|
||||
const preferenceCloudService = service.preferenceService;
|
||||
createPreferenceSpy = spyOn(preferenceCloudService, 'createPreference').and.returnValue(of(fakeTaskCloudFilters));
|
||||
@@ -229,6 +234,17 @@ describe('TaskFilterCloudService', () => {
|
||||
expect(service.isDefaultFilter(defaultFilterName)).toBe(true);
|
||||
expect(service.isDefaultFilter(fakeFilterName)).toBe(false);
|
||||
});
|
||||
|
||||
it('should return engine event task subscription', (done) => {
|
||||
spyOn(notificationCloudService, 'makeGQLQuery').and.returnValue(of(taskCloudEngineEventsMock));
|
||||
|
||||
service.getTaskNotificationSubscription('myAppName').subscribe((res: TaskCloudEngineEvent[]) => {
|
||||
expect(res.length).toBe(1);
|
||||
expect(res[0].eventType).toBe('TASK_ASSIGNED');
|
||||
expect(res[0].entity.name).toBe('This is a new task');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Inject [LocalPreferenceCloudService] into the TaskFilterCloudService', () => {
|
||||
@@ -240,9 +256,7 @@ describe('Inject [LocalPreferenceCloudService] into the TaskFilterCloudService',
|
||||
const identityUserMock = { username: 'fakeusername', firstName: 'fake-identity-first-name', lastName: 'fake-identity-last-name', email: 'fakeIdentity@email.com' };
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
HttpClientTestingModule
|
||||
],
|
||||
imports: [ HttpClientTestingModule ],
|
||||
providers: [
|
||||
{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }
|
||||
]
|
||||
|
@@ -24,6 +24,24 @@ 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 '../../task-list/models/task-cloud.model';
|
||||
import { NotificationCloudService } from '../../../services/notification-cloud.service';
|
||||
import { TaskCloudEngineEvent } from '../../../models/engine-event-cloud.model';
|
||||
|
||||
const TASK_EVENT_SUBSCRIPTION_QUERY = `
|
||||
subscription {
|
||||
engineEvents(eventType: [
|
||||
TASK_COMPLETED
|
||||
TASK_ASSIGNED
|
||||
TASK_ACTIVATED
|
||||
TASK_SUSPENDED
|
||||
TASK_CANCELLED
|
||||
TASK_UPDATED
|
||||
]) {
|
||||
eventType
|
||||
entity
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
@@ -37,7 +55,8 @@ export class TaskFilterCloudService extends BaseCloudService {
|
||||
@Inject(TASK_FILTERS_SERVICE_TOKEN)
|
||||
public preferenceService: PreferenceCloudServiceInterface,
|
||||
apiService: AlfrescoApiService,
|
||||
appConfigService: AppConfigService) {
|
||||
appConfigService: AppConfigService,
|
||||
private notificationCloudService: NotificationCloudService) {
|
||||
super(apiService, appConfigService);
|
||||
this.filtersSubject = new BehaviorSubject([]);
|
||||
this.filters$ = this.filtersSubject.asObservable();
|
||||
@@ -315,4 +334,9 @@ export class TaskFilterCloudService extends BaseCloudService {
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
getTaskNotificationSubscription(appName: string): Observable<TaskCloudEngineEvent[]> {
|
||||
return this.notificationCloudService.makeGQLQuery(appName, TASK_EVENT_SUBSCRIPTION_QUERY)
|
||||
.pipe(map((events: any) => events.data.engineEvents));
|
||||
}
|
||||
}
|
||||
|
@@ -21,7 +21,7 @@ export const fakeTaskDetailsCloud = {
|
||||
'appVersion': '',
|
||||
'id': '68d54a8f',
|
||||
'assignee': 'Phil Woods',
|
||||
'name': 'This is a new task ',
|
||||
'name': 'This is a new task',
|
||||
'description': 'This is the description ',
|
||||
'createdDate': 1545048055900,
|
||||
'dueDate': 1545091200000,
|
||||
|
@@ -46,7 +46,7 @@ export const assignedTaskDetailsCloudMock: TaskDetailsCloudModel = {
|
||||
'appVersion': 1,
|
||||
'id': '68d54a8f-01f3-11e9-8e36-0a58646002ad',
|
||||
'assignee': 'AssignedTaskUser',
|
||||
'name': 'This is a new task ',
|
||||
'name': 'This is a new task',
|
||||
'description': 'This is the description ',
|
||||
'createdDate': new Date(1545048055900),
|
||||
'dueDate': new Date(),
|
||||
@@ -70,7 +70,7 @@ export const createdTaskDetailsCloudMock: TaskDetailsCloudModel = {
|
||||
'appVersion': 1,
|
||||
'id': '68d54a8f-01f3-11e9-8e36-0a58646002ad',
|
||||
'assignee': 'CreatedTaskUser',
|
||||
'name': 'This is a new task ',
|
||||
'name': 'This is a new task',
|
||||
'description': 'This is the description ',
|
||||
'createdDate': new Date(1545048055900),
|
||||
'dueDate': new Date(1545091200000),
|
||||
@@ -94,7 +94,7 @@ export const emptyOwnerTaskDetailsCloudMock: TaskDetailsCloudModel = {
|
||||
'appVersion': 1,
|
||||
'id': '68d54a8f-01f3-11e9-8e36-0a58646002ad',
|
||||
'assignee': 'AssignedTaskUser',
|
||||
'name': 'This is a new task ',
|
||||
'name': 'This is a new task',
|
||||
'description': 'This is the description ',
|
||||
'createdDate': new Date(1545048055900),
|
||||
'dueDate': new Date(1545091200000),
|
||||
@@ -118,7 +118,7 @@ export const createdStateTaskDetailsCloudMock: TaskDetailsCloudModel = {
|
||||
'appVersion': 1,
|
||||
'id': 'mock-task-id',
|
||||
'assignee': '',
|
||||
'name': 'This is a new task ',
|
||||
'name': 'This is a new task',
|
||||
'description': 'This is the description ',
|
||||
'createdDate': new Date(1545048055900),
|
||||
'dueDate': new Date(1545091200000),
|
||||
@@ -142,7 +142,7 @@ export const completedTaskDetailsCloudMock: TaskDetailsCloudModel = {
|
||||
'appVersion': 1,
|
||||
'id': 'mock-task-id',
|
||||
'assignee': 'CompletedTaskAssignee',
|
||||
'name': 'This is a new task ',
|
||||
'name': 'This is a new task',
|
||||
'description': 'This is the description ',
|
||||
'createdDate': new Date(1545048055900),
|
||||
'dueDate': new Date(1545091200000),
|
||||
@@ -166,7 +166,7 @@ export const cancelledTaskDetailsCloudMock: TaskDetailsCloudModel = {
|
||||
'appVersion': 1,
|
||||
'id': 'mock-task-id',
|
||||
'assignee': 'CancelledTaskAssignee',
|
||||
'name': 'This is a new task ',
|
||||
'name': 'This is a new task',
|
||||
'description': 'This is the description ',
|
||||
'createdDate': new Date(1545048055900),
|
||||
'dueDate': new Date(1545091200000),
|
||||
@@ -190,7 +190,7 @@ export const suspendedTaskDetailsCloudMock: TaskDetailsCloudModel = {
|
||||
'appVersion': 1,
|
||||
'id': 'mock-task-id',
|
||||
'assignee': 'SuspendedTaskAssignee',
|
||||
'name': 'This is a new task ',
|
||||
'name': 'This is a new task',
|
||||
'description': 'This is the description ',
|
||||
'createdDate': new Date(1545048055900),
|
||||
'dueDate': new Date(1545091200000),
|
||||
|
@@ -29,3 +29,4 @@ export * from './lib/pipes/process-services-cloud-pipe.module';
|
||||
export * from './lib/models/process-definition-cloud.model';
|
||||
export * from './lib/models/date-cloud-filter.model';
|
||||
export * from './lib/models/application-version.model';
|
||||
export * from './lib/models/engine-event-cloud.model';
|
||||
|
Reference in New Issue
Block a user