diff --git a/demo-shell/resources/i18n/en.json b/demo-shell/resources/i18n/en.json index 5d6cd0772d..309aec7de9 100644 --- a/demo-shell/resources/i18n/en.json +++ b/demo-shell/resources/i18n/en.json @@ -214,6 +214,11 @@ "STORE": "Store", "RESTORE": "Restore" }, + "NOTIFICATIONS": { + "TASK_ASSIGNED": "{{taskName}} task has been assigned to {{assignee}}", + "PROCESS_STARTED": "{{processName}} process has been started", + "TASK_UPDATED": "{{taskName}} task details has been updated" + }, "FORM-LOADING": { "FORM_DATA": "Form Data", "FORM_DATA_MESSAGE": "Enter values to populate the form", diff --git a/demo-shell/src/app/components/cloud/cloud-layout.component.ts b/demo-shell/src/app/components/cloud/cloud-layout.component.ts index f3513c1629..d854a7787d 100644 --- a/demo-shell/src/app/components/cloud/cloud-layout.component.ts +++ b/demo-shell/src/app/components/cloud/cloud-layout.component.ts @@ -17,6 +17,23 @@ import { Component, OnInit, ViewEncapsulation } from '@angular/core'; import { Router, ActivatedRoute } from '@angular/router'; import { CloudLayoutService } from './services/cloud-layout.service'; +import { NotificationModel, NotificationService } from '@alfresco/adf-core'; +import { map } from 'rxjs/operators'; +import { NotificationCloudService } from '@alfresco/adf-process-services-cloud'; +import { TranslateService } from '@ngx-translate/core'; + +const SUBSCRIPTION_QUERY = ` + subscription { + engineEvents(eventType: [ + PROCESS_STARTED + TASK_ASSIGNED + TASK_UPDATED + ]) { + eventType + entity + } + } +`; @Component({ selector: 'app-cloud-layout', @@ -31,13 +48,26 @@ export class CloudLayoutComponent implements OnInit { constructor( private router: Router, private route: ActivatedRoute, - private cloudLayoutService: CloudLayoutService + private cloudLayoutService: CloudLayoutService, + private notificationCloudService: NotificationCloudService, + private notificationService: NotificationService, + private translateService: TranslateService ) { } ngOnInit() { let root: string = ''; this.route.params.subscribe((params) => { this.appName = params.appName; + this.notificationCloudService.makeGQLQuery( + this.appName, SUBSCRIPTION_QUERY + ) + .pipe(map((events: any) => events.data.engineEvents)) + .subscribe((result) => { + result.map((engineEvent) => { + this.notifyEvent(engineEvent); + + }); + }); }); if (this.route.snapshot && this.route.snapshot.firstChild) { @@ -62,4 +92,37 @@ export class CloudLayoutComponent implements OnInit { onStartProcess() { this.router.navigate([`/cloud/${this.appName}/start-process/`]); } + + notifyEvent(engineEvent) { + let message; + switch (engineEvent.eventType) { + case 'TASK_ASSIGNED': + message = this.translateService.instant('NOTIFICATIONS.TASK_ASSIGNED', + { taskName: engineEvent.entity.name || '', assignee: engineEvent.entity.assignee }); + this.pushNotification(engineEvent, message); + break; + case 'PROCESS_STARTED': + message = this.translateService.instant('NOTIFICATIONS.PROCESS_STARTED', + { processName: engineEvent.entity.name }); + this.pushNotification(engineEvent, message); + break; + case 'TASK_UPDATED': + message = this.translateService.instant('NOTIFICATIONS.TASK_UPDATED', + { taskName: engineEvent.entity.name || '' }); + this.pushNotification(engineEvent, message); + break; + default: + } + } + + pushNotification(engineEvent: any, message: string) { + const notification = { + messages: [message], + icon: 'info', + datetime: new Date(), + initiator: { displayName: engineEvent.entity.initiator || 'System' } + } as NotificationModel; + + this.notificationService.pushToNotificationHistory(notification); + } } diff --git a/lib/core/notifications/components/notification-history.component.html b/lib/core/notifications/components/notification-history.component.html index 679f583419..95812a7c72 100644 --- a/lib/core/notifications/components/notification-history.component.html +++ b/lib/core/notifications/components/notification-history.component.html @@ -46,7 +46,7 @@

{{ message }}

+ mat-line [matTooltip]="message" matTooltipShowDelay="1000">{{ message }}

{{notification.datetime | adfTimeAgo}}

diff --git a/lib/process-services-cloud/package.json b/lib/process-services-cloud/package.json index 098566458b..1643d7e624 100644 --- a/lib/process-services-cloud/package.json +++ b/lib/process-services-cloud/package.json @@ -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", diff --git a/lib/process-services-cloud/src/lib/models/engine-event-cloud.model.ts b/lib/process-services-cloud/src/lib/models/engine-event-cloud.model.ts new file mode 100644 index 0000000000..a81bf1611e --- /dev/null +++ b/lib/process-services-cloud/src/lib/models/engine-event-cloud.model.ts @@ -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; +} diff --git a/lib/process-services-cloud/src/lib/services/notification-cloud.service.spec.ts b/lib/process-services-cloud/src/lib/services/notification-cloud.service.spec.ts new file mode 100644 index 0000000000..20747cdc31 --- /dev/null +++ b/lib/process-services-cloud/src/lib/services/notification-cloud.service.spec.ts @@ -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); + }); +}); diff --git a/lib/process-services-cloud/src/lib/services/notification-cloud.service.ts b/lib/process-services-cloud/src/lib/services/notification-cloud.service.ts new file mode 100644 index 0000000000..5db39faf2f --- /dev/null +++ b/lib/process-services-cloud/src/lib/services/notification-cloud.service.ts @@ -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( { + link, + cache: new InMemoryCache({}) + }); + } + } + + makeGQLQuery(appName: string, gqlQuery: string) { + this.initNotificationsForApp(appName); + return this.apollo.subscribe({ query : gql(gqlQuery) }); + } +} diff --git a/lib/process-services-cloud/src/lib/services/public-api.ts b/lib/process-services-cloud/src/lib/services/public-api.ts index 8655ff1335..41e68d4527 100644 --- a/lib/process-services-cloud/src/lib/services/public-api.ts +++ b/lib/process-services-cloud/src/lib/services/public-api.ts @@ -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'; diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/base-task-filters-cloud.component.html b/lib/process-services-cloud/src/lib/task/task-filters/components/base-task-filters-cloud.component.html index b0eae32717..c234fad5b4 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/base-task-filters-cloud.component.html +++ b/lib/process-services-cloud/src/lib/task/task-filters/components/base-task-filters-cloud.component.html @@ -1,16 +1,25 @@ -
+
- + {{ counters$[filter.key] | async }}
diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/base-task-filters-cloud.component.scss b/lib/process-services-cloud/src/lib/task/task-filters/components/base-task-filters-cloud.component.scss index bf7d7fc6f1..f6f9371a13 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/base-task-filters-cloud.component.scss +++ b/lib/process-services-cloud/src/lib/task/task-filters/components/base-task-filters-cloud.component.scss @@ -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; } } diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/base-task-filters-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/base-task-filters-cloud.component.ts index f0d7ecbe52..82b7628421 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/base-task-filters-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/components/base-task-filters-cloud.component.ts @@ -46,6 +46,7 @@ export abstract class BaseTaskFiltersCloudComponent implements OnDestroy { error: EventEmitter = new EventEmitter(); counters$: {[key: string]: Observable} = {}; + updatedCounters: string[] = []; protected onDestroy$ = new Subject(); @@ -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); + } + } } diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters-cloud.component.spec.ts index c1a406fa34..1df66e2401 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters-cloud.component.spec.ts @@ -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); + }); + }); + })); }); diff --git a/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters-cloud.component.ts index b4845bed6a..4d6fb46aaa 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/components/task-filters-cloud.component.ts @@ -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(); + /** Emitted when filter counters are updated. */ + @Output() + filterCounterUpdated: EventEmitter = new EventEmitter(); + filters$: Observable; 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; } diff --git a/lib/process-services-cloud/src/lib/task/task-filters/mock/task-filters-cloud.mock.ts b/lib/process-services-cloud/src/lib/task/task-filters/mock/task-filters-cloud.mock.ts index e97b6f760b..c20ccdda76 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/mock/task-filters-cloud.mock.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/mock/task-filters-cloud.mock.ts @@ -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 + } +}; diff --git a/lib/process-services-cloud/src/lib/task/task-filters/services/task-filter-cloud.service.spec.ts b/lib/process-services-cloud/src/lib/task/task-filters/services/task-filter-cloud.service.spec.ts index 72d5a9fcd2..f379cc4a05 100644 --- a/lib/process-services-cloud/src/lib/task/task-filters/services/task-filter-cloud.service.spec.ts +++ b/lib/process-services-cloud/src/lib/task/task-filters/services/task-filter-cloud.service.spec.ts @@ -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 } ] 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 65e09222fa..2d981f5185 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 @@ -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 { + return this.notificationCloudService.makeGQLQuery(appName, TASK_EVENT_SUBSCRIPTION_QUERY) + .pipe(map((events: any) => events.data.engineEvents)); + } } diff --git a/lib/process-services-cloud/src/lib/task/task-header/mocks/fake-task-details-response.mock.ts b/lib/process-services-cloud/src/lib/task/task-header/mocks/fake-task-details-response.mock.ts index ba1dc59baa..d1b2566499 100644 --- a/lib/process-services-cloud/src/lib/task/task-header/mocks/fake-task-details-response.mock.ts +++ b/lib/process-services-cloud/src/lib/task/task-header/mocks/fake-task-details-response.mock.ts @@ -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, diff --git a/lib/process-services-cloud/src/lib/task/task-header/mocks/task-details-cloud.mock.ts b/lib/process-services-cloud/src/lib/task/task-header/mocks/task-details-cloud.mock.ts index 10bbadd0c9..e0d5f16553 100644 --- a/lib/process-services-cloud/src/lib/task/task-header/mocks/task-details-cloud.mock.ts +++ b/lib/process-services-cloud/src/lib/task/task-header/mocks/task-details-cloud.mock.ts @@ -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), diff --git a/lib/process-services-cloud/src/public-api.ts b/lib/process-services-cloud/src/public-api.ts index 684e20185b..c697d066e0 100644 --- a/lib/process-services-cloud/src/public-api.ts +++ b/lib/process-services-cloud/src/public-api.ts @@ -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'; diff --git a/lib/tsconfig.json b/lib/tsconfig.json index 2498afa0c3..9520b73a1b 100644 --- a/lib/tsconfig.json +++ b/lib/tsconfig.json @@ -29,7 +29,7 @@ "@alfresco/adf-core": ["./core/"], "@alfresco/adf-insights": ["./analytics"] }, - "lib": ["es2018", "esnext.array", "dom"], + "lib": ["es2018", "esnext.array", "esnext.asynciterable", "dom"], "suppressImplicitAnyIndexErrors": true }, "exclude": [ diff --git a/package-lock.json b/package-lock.json index 5f8dfb3297..f30ffd642e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1411,6 +1411,38 @@ "tslib": "^2.0.0" } }, + "@apollo/client": { + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@apollo/client/-/client-3.3.10.tgz", + "integrity": "sha512-OL5y8q5evSbvsqMAHKZJgfgB/MS9PLGF7f0MtHEkGGCDvmcmA81lsjYwk0xpDm/3XdnrYp/duN2tYUu2LnpJmw==", + "requires": { + "@graphql-typed-document-node/core": "^3.0.0", + "@types/zen-observable": "^0.8.0", + "@wry/context": "^0.5.2", + "@wry/equality": "^0.3.0", + "fast-json-stable-stringify": "^2.0.0", + "graphql-tag": "^2.12.0", + "hoist-non-react-statics": "^3.3.2", + "optimism": "^0.14.0", + "prop-types": "^15.7.2", + "symbol-observable": "^2.0.0", + "ts-invariant": "^0.6.0", + "tslib": "^1.10.0", + "zen-observable": "^0.8.14" + }, + "dependencies": { + "symbol-observable": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-2.0.3.tgz", + "integrity": "sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA==" + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, "@babel/code-frame": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", @@ -2936,6 +2968,11 @@ } } }, + "@graphql-typed-document-node/core": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.0.tgz", + "integrity": "sha512-wYn6r8zVZyQJ6rQaALBEln5B1pzxb9shV5Ef97kTvn6yVGrqyXVnDqnU24MXnFubR+rZjBY9NWuxX3FB2sTsjg==" + }, "@istanbuljs/schema": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", @@ -5862,6 +5899,11 @@ "integrity": "sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A==", "dev": true }, + "@types/ungap__global-this": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@types/ungap__global-this/-/ungap__global-this-0.3.1.tgz", + "integrity": "sha512-+/DsiV4CxXl6ZWefwHZDXSe1Slitz21tom38qPCaG0DYCS1NnDPIQDTKcmQ/tvK/edJUKkmuIDBJbmKDiB0r/g==" + }, "@types/unist": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.3.tgz", @@ -5902,6 +5944,16 @@ "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", "dev": true }, + "@types/zen-observable": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.2.tgz", + "integrity": "sha512-HrCIVMLjE1MOozVoD86622S7aunluLb2PJdPfb3nYiEtohm8mIB/vyv0Fd37AdeMFrTUQXEunw78YloMA3Qilg==" + }, + "@ungap/global-this": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@ungap/global-this/-/global-this-0.4.4.tgz", + "integrity": "sha512-mHkm6FvepJECMNthFuIgpAEFmPOk71UyXuIxYfjytvFTnSDBIz7jmViO+LfHI/AjrazWije0PnSP3+/NlwzqtA==" + }, "@webassemblyjs/ast": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.9.0.tgz", @@ -6077,6 +6129,51 @@ "@xtuc/long": "4.2.2" } }, + "@wry/context": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.5.4.tgz", + "integrity": "sha512-/pktJKHUXDr4D6TJqWgudOPJW2Z+Nb+bqk40jufA3uTkLbnCRKdJPiYDIa/c7mfcPH8Hr6O8zjCERpg5Sq04Zg==", + "requires": { + "tslib": "^1.14.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@wry/equality": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.3.2.tgz", + "integrity": "sha512-yi0VRqw+ygqM/WVZUze5meAhe2evOHBFXqK8onNVdNNB+Tyn8/07FZpeDklECBHeT9KN9DY2JpCVGNQY6RCRDg==", + "requires": { + "tslib": "^1.14.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "@wry/trie": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@wry/trie/-/trie-0.2.2.tgz", + "integrity": "sha512-OxqBB39x6MfHaa2HpMiRMfhuUnQTddD32Ko020eBeJXq87ivX6xnSSnzKHVbA21p7iqBASz8n/07b6W5wW1BVQ==", + "requires": { + "tslib": "^1.14.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, "@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -6370,6 +6467,16 @@ "picomatch": "^2.0.4" } }, + "apollo-angular": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/apollo-angular/-/apollo-angular-2.2.0.tgz", + "integrity": "sha512-/38ynj2gMJ1M/JlSDB6vv1fGYJy7WDukJn/3wQnoOYay5ZeU0rsAJdBaSZQiOPxqAJgVoGfU787/aXjIC1PWZg==", + "requires": { + "extract-files": "^9.0.0", + "semver": "^7.0.0", + "tslib": "^2.0.0" + } + }, "app-root-path": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/app-root-path/-/app-root-path-2.0.1.tgz", @@ -6645,8 +6752,7 @@ "async-limiter": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", - "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==", - "dev": true + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" }, "asynckit": { "version": "0.4.0", @@ -6852,8 +6958,7 @@ "backo2": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz", - "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=", - "dev": true + "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=" }, "bail": { "version": "1.0.5", @@ -11579,8 +11684,7 @@ "extract-files": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/extract-files/-/extract-files-9.0.0.tgz", - "integrity": "sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ==", - "dev": true + "integrity": "sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ==" }, "extsprintf": { "version": "1.3.0", @@ -11628,8 +11732,7 @@ "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, "fast-levenshtein": { "version": "2.0.6", @@ -12820,6 +12923,21 @@ "form-data": "^3.0.0" } }, + "graphql-tag": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.1.tgz", + "integrity": "sha512-LPewEE1vzGkHnCO8zdOGogKsHHBdtpGyihow1UuMwp6RnZa0lAS7NcbvltLOuo4pi5diQCPASAXZkQq44ffixA==", + "requires": { + "tslib": "^1.14.1" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, "graphviz": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/graphviz/-/graphviz-0.0.8.tgz", @@ -13076,6 +13194,14 @@ "minimalistic-crypto-utils": "^1.0.1" } }, + "hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "requires": { + "react-is": "^16.7.0" + } + }, "homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", @@ -14537,6 +14663,11 @@ "integrity": "sha512-O62gD5ADMUGtJoOoM9U6LQ7i4byPXUNoHJ6mqsmkQJcom331ZJGDApWgDESWyBMEHEJRjtHozgIiTzYo9RU4UA==", "dev": true }, + "iterall": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.3.0.tgz", + "integrity": "sha512-QZ9qOMdF+QLHxy1QIpUHUU1D5pS2CG2P69LF6L6CPjPYA/XMOmKV3PZpawHoAjHNyB0swdVTRxdYT4tbBbxqwg==" + }, "jake": { "version": "10.8.2", "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.2.tgz", @@ -14899,8 +15030,7 @@ "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, "js-yaml": { "version": "4.0.0", @@ -16439,7 +16569,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "requires": { "js-tokens": "^3.0.0 || ^4.0.0" } @@ -18531,8 +18660,7 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, "object-copy": { "version": "0.1.0", @@ -18775,6 +18903,15 @@ } } }, + "optimism": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.14.0.tgz", + "integrity": "sha512-ygbNt8n4DOCVpkwiLF+IrKKeNHOjtr9aXLWGP9HNJGoblSGsnVbJLstcH6/nE9Xy5ZQtlkSioFQNnthmENW6FQ==", + "requires": { + "@wry/context": "^0.5.2", + "@wry/trie": "^0.2.1" + } + }, "optimize-css-assets-webpack-plugin": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/optimize-css-assets-webpack-plugin/-/optimize-css-assets-webpack-plugin-5.0.4.tgz", @@ -20483,6 +20620,16 @@ "sisteransi": "^1.0.4" } }, + "prop-types": { + "version": "15.7.2", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", + "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", + "requires": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.8.1" + } + }, "proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", @@ -21386,6 +21533,11 @@ "strip-json-comments": "~2.0.1" } }, + "react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, "read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -25494,6 +25646,38 @@ } } }, + "subscriptions-transport-ws": { + "version": "0.9.18", + "resolved": "https://registry.npmjs.org/subscriptions-transport-ws/-/subscriptions-transport-ws-0.9.18.tgz", + "integrity": "sha512-tztzcBTNoEbuErsVQpTN2xUNN/efAZXyCyL5m3x4t6SKrEiTL2N8SaKWBFWM4u56pL79ULif3zjyeq+oV+nOaA==", + "requires": { + "backo2": "^1.0.2", + "eventemitter3": "^3.1.0", + "iterall": "^1.2.1", + "symbol-observable": "^1.0.4", + "ws": "^5.2.0" + }, + "dependencies": { + "eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" + }, + "ws": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, "sugarss": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/sugarss/-/sugarss-2.0.0.tgz", @@ -26200,6 +26384,23 @@ "glob": "^7.1.2" } }, + "ts-invariant": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.6.0.tgz", + "integrity": "sha512-caoafsfgb8QxdrKzFfjKt627m4i8KTtfAiji0DYJfWI4A/S9ORNNpzYuD9br64kyKFgxn9UNaLLbSupam84mCA==", + "requires": { + "@types/ungap__global-this": "^0.3.1", + "@ungap/global-this": "^0.4.2", + "tslib": "^1.9.3" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, "ts-loader": { "version": "5.4.5", "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-5.4.5.tgz", @@ -29053,6 +29254,11 @@ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true }, + "zen-observable": { + "version": "0.8.15", + "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz", + "integrity": "sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==" + }, "zone.js": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.10.3.tgz", diff --git a/package.json b/package.json index 70ff4ad76a..217db7fff0 100644 --- a/package.json +++ b/package.json @@ -84,10 +84,12 @@ "@angular/platform-browser": "^10.0.4", "@angular/platform-browser-dynamic": "^10.0.4", "@angular/router": "^10.0.4", + "@apollo/client": "^3.3.7", "@mat-datetimepicker/core": "^5.1.1", "@mat-datetimepicker/moment": "^5.1.1", "@ngx-translate/core": "^13.0.0", "adf-tslint-rules": "0.0.7", + "apollo-angular": "^2.2.0", "chart.js": "2.9.4", "classlist.js": "1.1.20150312", "custom-event-polyfill": "^1.0.7", @@ -101,6 +103,7 @@ "raphael": "2.3.0", "rxjs": "^6.6.3", "snyk": "^1.452.0", + "subscriptions-transport-ws": "^0.9.18", "tslib": "^2.0.3", "zone.js": "~0.10.2" }, diff --git a/tsconfig.json b/tsconfig.json index 8a0c69a6a0..12bbe4286b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -25,6 +25,7 @@ "lib": [ "es2018", "esnext.array", + "esnext.asynciterable", "dom" ], "paths": {