Revert AAE-20794 - new websocket refactoring (#10436)

* Checking different way to build url

* Checking different way to build url

* Maybe

* Maybe

* Revert "AAE-20808 Fixed websocket protocol (#10433)"

This reverts commit 48e9d56453b2908ab7dd1737b128aaf8b53b564c.

* Revert "AAE-20808 Adding support for ws protocol in websocket service (#10432)"

This reverts commit 6723cd1802183489b7ce38925e4d3fed83edc374.

* Revert websocket
This commit is contained in:
Vito Albano 2024-11-27 18:06:08 +00:00 committed by GitHub
parent 5461bedb3e
commit b09f4cacf9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 148 additions and 441 deletions

View File

@ -6,5 +6,5 @@
}, },
"exclude": ["../**/*.spec.ts" ], "exclude": ["../**/*.spec.ts" ],
"include": ["../src/**/*", "*.js", "../../core/feature-flags"] "include": ["../src/**/*", "*.js"]
} }

View File

@ -32,7 +32,6 @@ import {
import { ProcessFilterCloudModel } from '../models/process-filter-cloud.model'; import { ProcessFilterCloudModel } from '../models/process-filter-cloud.model';
import { IdentityUserService } from '../../../people/services/identity-user.service'; import { IdentityUserService } from '../../../people/services/identity-user.service';
import { NotificationCloudService } from '../../../services/notification-cloud.service'; import { NotificationCloudService } from '../../../services/notification-cloud.service';
import { provideMockFeatureFlags } from '@alfresco/adf-core/feature-flags';
describe('ProcessFilterCloudService', () => { describe('ProcessFilterCloudService', () => {
let service: ProcessFilterCloudService; let service: ProcessFilterCloudService;
@ -53,10 +52,7 @@ describe('ProcessFilterCloudService', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule], imports: [ProcessServiceCloudTestingModule],
providers: [ providers: [{ provide: PROCESS_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }]
{ provide: PROCESS_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService },
provideMockFeatureFlags({ ['studio-ws-graphql-subprotocol']: false })
]
}); });
service = TestBed.inject(ProcessFilterCloudService); service = TestBed.inject(ProcessFilterCloudService);
@ -72,7 +68,7 @@ describe('ProcessFilterCloudService', () => {
}); });
it('should create processfilter key by using appName and the username', (done) => { it('should create processfilter key by using appName and the username', (done) => {
service.getProcessFilters('mock-appName').subscribe((res: ProcessFilterCloudModel[]) => { service.getProcessFilters('mock-appName').subscribe((res: any) => {
expect(res).toBeDefined(); expect(res).toBeDefined();
expect(getCurrentUserInfoSpy).toHaveBeenCalled(); expect(getCurrentUserInfoSpy).toHaveBeenCalled();
done(); done();
@ -141,7 +137,7 @@ describe('ProcessFilterCloudService', () => {
it('should create the process filters in case the filters are not exist in the user preferences', (done) => { it('should create the process filters in case the filters are not exist in the user preferences', (done) => {
getPreferencesSpy.and.returnValue(of(fakeProcessCloudFilterWithDifferentEntries)); getPreferencesSpy.and.returnValue(of(fakeProcessCloudFilterWithDifferentEntries));
service.getProcessFilters('mock-appName').subscribe((res: ProcessFilterCloudModel[]) => { service.getProcessFilters('mock-appName').subscribe((res: any) => {
expect(res).toBeDefined(); expect(res).toBeDefined();
expect(res).not.toBeNull(); expect(res).not.toBeNull();
expect(res.length).toBe(3); expect(res.length).toBe(3);
@ -247,7 +243,6 @@ describe('ProcessFilterCloudService', () => {
it('should reset filters to default values', async () => { it('should reset filters to default values', async () => {
const changedFilter = new ProcessFilterCloudModel(fakeProcessCloudFilters[0]); const changedFilter = new ProcessFilterCloudModel(fakeProcessCloudFilters[0]);
changedFilter.processDefinitionKey = 'modifiedProcessDefinitionKey'; changedFilter.processDefinitionKey = 'modifiedProcessDefinitionKey';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
spyOn<any>(service, 'defaultProcessFilters').and.returnValue(fakeProcessCloudFilters); spyOn<any>(service, 'defaultProcessFilters').and.returnValue(fakeProcessCloudFilters);
await service.resetProcessFilterToDefaults('mock-appName', changedFilter).toPromise(); await service.resetProcessFilterToDefaults('mock-appName', changedFilter).toPromise();

View File

@ -18,12 +18,17 @@
import { TestBed } from '@angular/core/testing'; import { TestBed } from '@angular/core/testing';
import { ProcessServiceCloudTestingModule } from '../testing/process-service-cloud.testing.module'; import { ProcessServiceCloudTestingModule } from '../testing/process-service-cloud.testing.module';
import { NotificationCloudService } from './notification-cloud.service'; import { NotificationCloudService } from './notification-cloud.service';
import { WebSocketService } from './web-socket.service'; import { Apollo } from 'apollo-angular';
import { provideMockFeatureFlags } from '@alfresco/adf-core/feature-flags';
describe('NotificationCloudService', () => { describe('NotificationCloudService', () => {
let service: NotificationCloudService; let service: NotificationCloudService;
let wsService: WebSocketService; let apollo: Apollo;
let apolloCreateSpy: jasmine.Spy;
let apolloSubscribeSpy: jasmine.Spy;
const useMock: any = {
subscribe: () => {}
};
const queryMock = ` const queryMock = `
subscription { subscription {
@ -38,25 +43,39 @@ describe('NotificationCloudService', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule], imports: [ProcessServiceCloudTestingModule]
providers: [WebSocketService, provideMockFeatureFlags({ ['studio-ws-graphql-subprotocol']: false })]
}); });
service = TestBed.inject(NotificationCloudService); service = TestBed.inject(NotificationCloudService);
wsService = TestBed.inject(WebSocketService); apollo = TestBed.inject(Apollo);
service.appsListening = [];
apolloCreateSpy = spyOn(apollo, 'createNamed');
apolloSubscribeSpy = spyOn(apollo, 'use').and.returnValue(useMock);
}); });
it('should call getSubscription with the correct parameters', () => { it('should not create more than one websocket per app if it was already created', () => {
const getSubscriptionSpy = spyOn(wsService, 'getSubscription').and.callThrough(); service.makeGQLQuery('myAppName', queryMock);
expect(service.appsListening.length).toBe(1);
expect(service.appsListening[0]).toBe('myAppName');
service.makeGQLQuery('myAppName', queryMock); service.makeGQLQuery('myAppName', queryMock);
expect(service.appsListening.length).toBe(1);
expect(service.appsListening[0]).toBe('myAppName');
expect(getSubscriptionSpy).toHaveBeenCalledWith({ expect(apolloCreateSpy).toHaveBeenCalledTimes(1);
apolloClientName: 'myAppName', expect(apolloSubscribeSpy).toHaveBeenCalledTimes(2);
wsUrl: 'myAppName/notifications', });
httpUrl: 'myAppName/notifications/graphql',
subscriptionOptions: { it('should create new websocket if it is subscribing to new app', () => {
query: jasmine.any(Object) service.makeGQLQuery('myAppName', queryMock);
} expect(service.appsListening.length).toBe(1);
}); expect(service.appsListening[0]).toBe('myAppName');
service.makeGQLQuery('myOtherAppName', queryMock);
expect(service.appsListening.length).toBe(2);
expect(service.appsListening[1]).toBe('myOtherAppName');
expect(apolloCreateSpy).toHaveBeenCalledTimes(2);
expect(apolloSubscribeSpy).toHaveBeenCalledTimes(2);
}); });
}); });

View File

@ -15,24 +15,101 @@
* limitations under the License. * limitations under the License.
*/ */
import { gql } from '@apollo/client/core'; import { Apollo } from 'apollo-angular';
import { HttpLink } from 'apollo-angular/http';
import { split, gql, InMemoryCache, ApolloLink, InMemoryCacheConfig } from '@apollo/client/core';
import { WebSocketLink } from '@apollo/client/link/ws';
import { onError } from '@apollo/client/link/error';
import { getMainDefinition } from '@apollo/client/utilities';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { WebSocketService } from './web-socket.service'; import { AuthenticationService } from '@alfresco/adf-core';
import { BaseCloudService } from './base-cloud.service';
import { AdfHttpClient } from '@alfresco/adf-core/api';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
}) })
export class NotificationCloudService { export class NotificationCloudService extends BaseCloudService {
constructor(private readonly webSocketService: WebSocketService) {} appsListening = [];
constructor(public apollo: Apollo, private http: HttpLink, private authService: AuthenticationService, protected adfHttpClient: AdfHttpClient) {
super(adfHttpClient);
}
private get webSocketHost() {
return this.contextRoot.split('://')[1];
}
private get protocol() {
return this.contextRoot.split('://')[0] === 'https' ? 'wss' : 'ws';
}
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: `${this.protocol}://${this.webSocketHost}/${appName}/notifications/ws/graphql`,
options: {
reconnect: true,
lazy: true,
connectionParams: {
kaInterval: 2000,
// eslint-disable-next-line @typescript-eslint/naming-convention
'X-Authorization': 'Bearer ' + this.authService.getToken()
}
}
});
const link = split(
({ query }) => {
const definition = getMainDefinition(query);
return definition.kind === 'OperationDefinition' && definition.operation === 'subscription';
},
webSocketLink,
httpLink
);
const errorLink = onError(({ graphQLErrors, operation, forward }) => {
if (graphQLErrors) {
for (const err of graphQLErrors) {
switch (err.extensions.code) {
case 'UNAUTHENTICATED': {
const oldHeaders = operation.getContext().headers;
operation.setContext({
headers: {
...oldHeaders,
// eslint-disable-next-line @typescript-eslint/naming-convention
'X-Authorization': 'Bearer ' + this.authService.getToken()
}
});
forward(operation);
break;
}
default:
break;
}
}
}
});
this.apollo.createNamed(appName, {
link: ApolloLink.from([errorLink, link]),
cache: new InMemoryCache({ merge: true } as InMemoryCacheConfig),
defaultOptions: {
watchQuery: {
errorPolicy: 'all'
}
}
});
}
}
makeGQLQuery(appName: string, gqlQuery: string) { makeGQLQuery(appName: string, gqlQuery: string) {
return this.webSocketService.getSubscription({ this.initNotificationsForApp(appName);
apolloClientName: appName, return this.apollo.use(appName).subscribe({ query: gql(gqlQuery) });
wsUrl: `${appName}/notifications`,
httpUrl: `${appName}/notifications/graphql`,
subscriptionOptions: {
query: gql(gqlQuery)
}
});
} }
} }

View File

@ -24,4 +24,3 @@ export * from './form-fields.interfaces';
export * from './base-cloud.service'; export * from './base-cloud.service';
export * from './task-list-cloud.service.interface'; export * from './task-list-cloud.service.interface';
export * from './variable-mapper.sevice'; export * from './variable-mapper.sevice';
export * from './web-socket.service';

View File

@ -1,135 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* 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 { AppConfigService, AuthenticationService } from '@alfresco/adf-core';
import { TestBed } from '@angular/core/testing';
import { Apollo, gql } from 'apollo-angular';
import { of, Subject } from 'rxjs';
import { WebSocketService } from './web-socket.service';
import { SubscriptionOptions } from '@apollo/client/core';
import { FeaturesServiceToken, IFeaturesService, provideMockFeatureFlags } from '@alfresco/adf-core/feature-flags';
import { HttpClientTestingModule } from '@angular/common/http/testing';
describe('WebSocketService', () => {
let service: WebSocketService;
let featureService: IFeaturesService;
const onLogoutSubject: Subject<void> = new Subject<void>();
const apolloMock = jasmine.createSpyObj('Apollo', ['use', 'createNamed']);
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [
{
provide: Apollo,
useValue: apolloMock
},
{
provide: AppConfigService,
useValue: {
get: () => 'wss://testHost'
}
},
{
provide: AuthenticationService,
useValue: {
getToken: () => 'testToken',
onLogout: onLogoutSubject.asObservable()
}
},
provideMockFeatureFlags({ ['studio-ws-graphql-subprotocol']: true })
]
});
service = TestBed.inject(WebSocketService);
featureService = TestBed.inject(FeaturesServiceToken);
spyOn(featureService, 'isOn$').and.returnValue(of(true));
apolloMock.use.and.returnValues(undefined, { subscribe: () => of({}) });
});
afterEach(() => {
apolloMock.use.calls.reset();
apolloMock.createNamed.calls.reset();
});
it('should not create a new Apollo client if it is already in use', (done) => {
const apolloClientName = 'testClient';
const subscriptionOptions: SubscriptionOptions = { query: gql(`subscription {testQuery}`) };
const wsOptions = { apolloClientName, wsUrl: 'testUrl', subscriptionOptions };
apolloMock.use.and.returnValues(true, { subscribe: () => of({}) });
service.getSubscription(wsOptions).subscribe(() => {
expect(apolloMock.use).toHaveBeenCalledTimes(2);
expect(apolloMock.use).toHaveBeenCalledWith(apolloClientName);
expect(apolloMock.createNamed).not.toHaveBeenCalled();
done();
});
});
it('should subscribe to Apollo client if not already in use', (done) => {
const apolloClientName = 'testClient';
const expectedApolloClientName = 'testClient';
const subscriptionOptions: SubscriptionOptions = { query: gql(`subscription {testQuery}`) };
const wsOptions = { apolloClientName, wsUrl: 'testUrl', subscriptionOptions };
service.getSubscription(wsOptions).subscribe(() => {
expect(apolloMock.use).toHaveBeenCalledWith(expectedApolloClientName);
expect(apolloMock.use).toHaveBeenCalledTimes(2);
expect(apolloMock.createNamed).toHaveBeenCalledTimes(1);
expect(apolloMock.createNamed).toHaveBeenCalledWith(expectedApolloClientName, jasmine.any(Object));
done();
});
});
it('should create named client with the right authentication token when FF is on', (done) => {
let headers = {};
const expectedHeaders = { Authorization: 'Bearer testToken' };
const apolloClientName = 'testClient';
const subscriptionOptions: SubscriptionOptions = { query: gql(`subscription {testQuery}`) };
const wsOptions = { apolloClientName, wsUrl: 'testUrl', subscriptionOptions };
apolloMock.createNamed.and.callFake((_, options) => {
headers = options.headers;
});
service.getSubscription(wsOptions).subscribe(() => {
expect(apolloMock.use).toHaveBeenCalledTimes(2);
expect(apolloMock.createNamed).toHaveBeenCalled();
expect(headers).toEqual(expectedHeaders);
done();
});
});
it('should create named client with the right authentication token when FF is off', (done) => {
featureService.isOn$ = jasmine.createSpy().and.returnValue(of(false));
let headers = {};
const expectedHeaders = { 'X-Authorization': 'Bearer testToken' };
const apolloClientName = 'testClient';
const subscriptionOptions: SubscriptionOptions = { query: gql(`subscription {testQuery}`) };
const wsOptions = { apolloClientName, wsUrl: 'testUrl', subscriptionOptions };
apolloMock.createNamed.and.callFake((_, options) => {
headers = options.headers;
});
service.getSubscription(wsOptions).subscribe(() => {
expect(apolloMock.use).toHaveBeenCalledTimes(2);
expect(apolloMock.createNamed).toHaveBeenCalled();
expect(headers).toEqual(expectedHeaders);
done();
});
});
});

View File

@ -1,211 +0,0 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* 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 { createClient } from 'graphql-ws';
import { Inject, Injectable } from '@angular/core';
import { AppConfigService, AuthenticationService } from '@alfresco/adf-core';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { WebSocketLink } from '@apollo/client/link/ws';
import {
DefaultContext,
FetchResult,
from,
HttpLink,
InMemoryCache,
InMemoryCacheConfig,
NextLink,
Operation,
split,
SubscriptionOptions
} from '@apollo/client/core';
import { getMainDefinition } from '@apollo/client/utilities';
import { Kind, OperationTypeNode } from 'graphql';
import { Apollo } from 'apollo-angular';
import { onError } from '@apollo/client/link/error';
import { RetryLink } from '@apollo/client/link/retry';
import { Observable } from 'rxjs';
import { switchMap, take, tap } from 'rxjs/operators';
import { FeaturesServiceToken, IFeaturesService } from '@alfresco/adf-core/feature-flags';
interface serviceOptions {
apolloClientName: string;
wsUrl: string;
httpUrl?: string;
subscriptionOptions: SubscriptionOptions;
}
@Injectable({
providedIn: 'root'
})
export class WebSocketService {
private host = '';
private subscriptionProtocol: 'graphql-ws' | 'transport-ws' = 'transport-ws';
private wsLink: GraphQLWsLink | WebSocketLink;
private httpLink: HttpLink;
constructor(
private readonly apollo: Apollo,
private readonly appConfigService: AppConfigService,
private readonly authService: AuthenticationService,
@Inject(FeaturesServiceToken) private featuresService: IFeaturesService
) {
this.host = this.appConfigService.get('bpmHost', '');
}
public getSubscription<T>(options: serviceOptions): Observable<FetchResult<T>> {
const { apolloClientName, subscriptionOptions } = options;
this.authService.onLogout.pipe(take(1)).subscribe(() => {
if (this.apollo.use(apolloClientName)) {
this.apollo.removeClient(apolloClientName);
}
});
return this.featuresService.isOn$('studio-ws-graphql-subprotocol').pipe(
tap((isOn) => {
if (isOn) {
this.subscriptionProtocol = 'graphql-ws';
}
}),
switchMap(() => {
if (this.apollo.use(apolloClientName) === undefined) {
this.initSubscriptions(options);
}
return this.apollo.use(apolloClientName).subscribe<T>({ errorPolicy: 'all', ...subscriptionOptions });
})
);
}
private createWsUrl(serviceUrl: string): string {
const url = new URL(serviceUrl, this.host);
const protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';
url.protocol = protocol;
return url.href;
}
private createHttpUrl(serviceUrl: string): string {
const url = new URL(serviceUrl, this.host);
return url.href;
}
private initSubscriptions(options: serviceOptions): void {
switch (this.subscriptionProtocol) {
case 'graphql-ws':
this.createGraphQLWsLink(options);
break;
case 'transport-ws':
this.createTransportWsLink(options);
break;
default:
throw new Error('Unknown subscription protocol');
}
this.httpLink = options.httpUrl
? new HttpLink({
uri: this.createHttpUrl(options.httpUrl)
})
: undefined;
const link = split(
({ query }) => {
const definition = getMainDefinition(query);
return definition.kind === Kind.OPERATION_DEFINITION && definition.operation === OperationTypeNode.SUBSCRIPTION;
},
this.wsLink,
this.httpLink
);
const authLink = (operation: Operation, forward: NextLink) => {
operation.setContext(({ headers }: DefaultContext) => ({
headers: {
...headers,
...(this.subscriptionProtocol === 'graphql-ws' && { Authorization: `Bearer ${this.authService.getToken()}` }),
...(this.subscriptionProtocol === 'transport-ws' && { 'X-Authorization': `Bearer ${this.authService.getToken()}` })
}
}));
return forward(operation);
};
const errorLink = onError(({ graphQLErrors, networkError, operation, forward }) => {
if (graphQLErrors) {
for (const error of graphQLErrors) {
if (error.extensions && error.extensions['code'] === 'UNAUTHENTICATED') {
authLink(operation, forward);
}
}
}
if (networkError) {
console.error(`[Network error]: ${networkError}`);
}
});
const retryLink = new RetryLink({
delay: {
initial: 300,
max: Number.POSITIVE_INFINITY,
jitter: true
},
attempts: {
max: 5,
retryIf: (error) => !!error
}
});
this.apollo.createNamed(options.apolloClientName, {
headers: {
...(this.subscriptionProtocol === 'graphql-ws' && { Authorization: `Bearer ${this.authService.getToken()}` }),
...(this.subscriptionProtocol === 'transport-ws' && { 'X-Authorization': `Bearer ${this.authService.getToken()}` })
},
link: from([authLink, retryLink, errorLink, link]),
cache: new InMemoryCache({ merge: true } as InMemoryCacheConfig)
});
}
private createTransportWsLink(options: serviceOptions) {
this.wsLink = new WebSocketLink({
uri: this.createWsUrl(options.wsUrl) + '/ws/graphql',
options: {
reconnect: true,
lazy: true,
connectionParams: {
kaInterval: 2000,
'X-Authorization': 'Bearer ' + this.authService.getToken()
}
}
});
}
private createGraphQLWsLink(options: serviceOptions) {
this.wsLink = new GraphQLWsLink(
createClient({
url: this.createWsUrl(options.wsUrl) + '/v2/ws/graphql',
connectionParams: {
Authorization: 'Bearer ' + this.authService.getToken()
},
on: {
error: () => {
this.apollo.removeClient(options.apolloClientName);
this.initSubscriptions(options);
}
},
lazy: true
})
);
}
}

View File

@ -39,7 +39,6 @@ import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatExpansionPanelHarness } from '@angular/material/expansion/testing'; import { MatExpansionPanelHarness } from '@angular/material/expansion/testing';
import { MatSelectHarness } from '@angular/material/select/testing'; import { MatSelectHarness } from '@angular/material/select/testing';
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing'; import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
import { provideMockFeatureFlags } from '@alfresco/adf-core/feature-flags';
describe('EditServiceTaskFilterCloudComponent', () => { describe('EditServiceTaskFilterCloudComponent', () => {
let loader: HarnessLoader; let loader: HarnessLoader;
@ -51,16 +50,12 @@ describe('EditServiceTaskFilterCloudComponent', () => {
let getTaskFilterSpy: jasmine.Spy; let getTaskFilterSpy: jasmine.Spy;
let getDeployedApplicationsSpy: jasmine.Spy; let getDeployedApplicationsSpy: jasmine.Spy;
let taskService: TaskCloudService; let taskService: TaskCloudService;
const afterClosedSubject = new Subject<unknown>(); const afterClosedSubject = new Subject<any>();
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule, TaskFiltersCloudModule, MatIconTestingModule], imports: [ProcessServiceCloudTestingModule, TaskFiltersCloudModule, MatIconTestingModule],
providers: [ providers: [MatDialog, { provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }]
MatDialog,
{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService },
provideMockFeatureFlags({ ['studio-ws-graphql-subprotocol']: false })
]
}); });
fixture = TestBed.createComponent(EditServiceTaskFilterCloudComponent); fixture = TestBed.createComponent(EditServiceTaskFilterCloudComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
@ -68,8 +63,9 @@ describe('EditServiceTaskFilterCloudComponent', () => {
appsService = TestBed.inject(AppsProcessCloudService); appsService = TestBed.inject(AppsProcessCloudService);
taskService = TestBed.inject(TaskCloudService); taskService = TestBed.inject(TaskCloudService);
dialog = TestBed.inject(MatDialog); dialog = TestBed.inject(MatDialog);
const dialogRefMock = jasmine.createSpyObj('MatDialogRef', ['afterClosed']); const dialogRefMock: any = {
dialogRefMock.afterClosed.and.returnValue(afterClosedSubject); afterClosed: () => afterClosedSubject
};
spyOn(dialog, 'open').and.returnValue(dialogRefMock); spyOn(dialog, 'open').and.returnValue(dialogRefMock);
getTaskFilterSpy = spyOn(service, 'getTaskFilterById').and.returnValue(of(fakeServiceFilter)); getTaskFilterSpy = spyOn(service, 'getTaskFilterById').and.returnValue(of(fakeServiceFilter));
getDeployedApplicationsSpy = spyOn(appsService, 'getDeployedApplicationsByStatus').and.returnValue(of(fakeApplicationInstance)); getDeployedApplicationsSpy = spyOn(appsService, 'getDeployedApplicationsByStatus').and.returnValue(of(fakeApplicationInstance));

View File

@ -57,7 +57,6 @@ import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatSelectHarness } from '@angular/material/select/testing'; import { MatSelectHarness } from '@angular/material/select/testing';
import { MatExpansionPanelHarness } from '@angular/material/expansion/testing'; import { MatExpansionPanelHarness } from '@angular/material/expansion/testing';
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing'; import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
import { provideMockFeatureFlags } from '@alfresco/adf-core/feature-flags';
describe('EditTaskFilterCloudComponent', () => { describe('EditTaskFilterCloudComponent', () => {
let loader: HarnessLoader; let loader: HarnessLoader;
@ -71,16 +70,12 @@ describe('EditTaskFilterCloudComponent', () => {
let getTaskFilterSpy: jasmine.Spy; let getTaskFilterSpy: jasmine.Spy;
let getDeployedApplicationsSpy: jasmine.Spy; let getDeployedApplicationsSpy: jasmine.Spy;
let taskService: TaskCloudService; let taskService: TaskCloudService;
const afterClosedSubject = new Subject<unknown>(); const afterClosedSubject = new Subject<any>();
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule, TaskFiltersCloudModule, PeopleCloudModule, MatIconTestingModule], imports: [ProcessServiceCloudTestingModule, TaskFiltersCloudModule, PeopleCloudModule, MatIconTestingModule],
providers: [ providers: [MatDialog, { provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }]
MatDialog,
{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService },
provideMockFeatureFlags({ ['studio-ws-graphql-subprotocol']: false })
]
}); });
fixture = TestBed.createComponent(EditTaskFilterCloudComponent); fixture = TestBed.createComponent(EditTaskFilterCloudComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
@ -90,8 +85,9 @@ describe('EditTaskFilterCloudComponent', () => {
taskService = TestBed.inject(TaskCloudService); taskService = TestBed.inject(TaskCloudService);
alfrescoApiService = TestBed.inject(AlfrescoApiService); alfrescoApiService = TestBed.inject(AlfrescoApiService);
dialog = TestBed.inject(MatDialog); dialog = TestBed.inject(MatDialog);
const dialogRefMock = jasmine.createSpyObj('MatDialogRef', ['afterClosed']); const dialogRefMock: any = {
dialogRefMock.afterClosed.and.returnValue(afterClosedSubject); afterClosed: () => afterClosedSubject
};
spyOn(dialog, 'open').and.returnValue(dialogRefMock); spyOn(dialog, 'open').and.returnValue(dialogRefMock);
spyOn(alfrescoApiService, 'getInstance').and.returnValue(mockAlfrescoApi); spyOn(alfrescoApiService, 'getInstance').and.returnValue(mockAlfrescoApi);
getTaskFilterSpy = spyOn(service, 'getTaskFilterById').and.returnValue(of(fakeFilter)); getTaskFilterSpy = spyOn(service, 'getTaskFilterById').and.returnValue(of(fakeFilter));

View File

@ -16,7 +16,7 @@
*/ */
import { AppConfigService } from '@alfresco/adf-core'; import { AppConfigService } from '@alfresco/adf-core';
import { DebugElement, SimpleChange } from '@angular/core'; import { SimpleChange } from '@angular/core';
import { ComponentFixture, TestBed, fakeAsync } from '@angular/core/testing'; import { ComponentFixture, TestBed, fakeAsync } from '@angular/core/testing';
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
import { first, of, throwError } from 'rxjs'; import { first, of, throwError } from 'rxjs';
@ -32,7 +32,6 @@ import { HarnessLoader } from '@angular/cdk/testing';
import { MatActionListItemHarness } from '@angular/material/list/testing'; import { MatActionListItemHarness } from '@angular/material/list/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { TaskFilterCloudAdapter } from '../../../models/filter-cloud-model'; import { TaskFilterCloudAdapter } from '../../../models/filter-cloud-model';
import { provideMockFeatureFlags } from '@alfresco/adf-core/feature-flags';
describe('TaskFiltersCloudComponent', () => { describe('TaskFiltersCloudComponent', () => {
let loader: HarnessLoader; let loader: HarnessLoader;
@ -46,14 +45,10 @@ describe('TaskFiltersCloudComponent', () => {
let getTaskListFiltersSpy: jasmine.Spy; let getTaskListFiltersSpy: jasmine.Spy;
let getTaskListCounterSpy: jasmine.Spy; let getTaskListCounterSpy: jasmine.Spy;
const configureTestingModule = (providers: unknown[]) => { const configureTestingModule = (providers: any[]) => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule, TaskFiltersCloudModule], imports: [ProcessServiceCloudTestingModule, TaskFiltersCloudModule],
providers: [ providers: [{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }, ...providers]
{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService },
provideMockFeatureFlags({ ['studio-ws-graphql-subprotocol']: false }),
...providers
]
}); });
taskFilterService = TestBed.inject(TaskFilterCloudService); taskFilterService = TestBed.inject(TaskFilterCloudService);
taskListService = TestBed.inject(TaskListCloudService); taskListService = TestBed.inject(TaskListCloudService);
@ -107,7 +102,7 @@ describe('TaskFiltersCloudComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable(); await fixture.whenStable();
const filters: DebugElement[] = fixture.debugElement.queryAll(By.css('.adf-icon')); const filters: any = fixture.debugElement.queryAll(By.css('.adf-icon'));
expect(filters.length).toBe(0); expect(filters.length).toBe(0);
}); });
@ -270,7 +265,7 @@ describe('TaskFiltersCloudComponent', () => {
component.showIcons = false; component.showIcons = false;
fixture.detectChanges(); fixture.detectChanges();
const filters: DebugElement[] = fixture.debugElement.queryAll(By.css('.adf-icon')); const filters: any = fixture.debugElement.queryAll(By.css('.adf-icon'));
expect(filters.length).toBe(0); expect(filters.length).toBe(0);
}); });

View File

@ -37,7 +37,6 @@ import { IdentityUserService } from '../../../people/services/identity-user.serv
import { ApolloModule } from 'apollo-angular'; import { ApolloModule } from 'apollo-angular';
import { StorageService } from '@alfresco/adf-core'; import { StorageService } from '@alfresco/adf-core';
import { TaskStatusFilter } from '../public-api'; import { TaskStatusFilter } from '../public-api';
import { provideMockFeatureFlags } from '@alfresco/adf-core/feature-flags';
describe('TaskFilterCloudService', () => { describe('TaskFilterCloudService', () => {
let service: TaskFilterCloudService; let service: TaskFilterCloudService;
@ -58,10 +57,7 @@ describe('TaskFilterCloudService', () => {
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [HttpClientTestingModule, ProcessServiceCloudTestingModule, ApolloModule], imports: [HttpClientTestingModule, ProcessServiceCloudTestingModule, ApolloModule],
providers: [ providers: [{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: UserPreferenceCloudService }]
{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: UserPreferenceCloudService },
provideMockFeatureFlags({ ['studio-ws-graphql-subprotocol']: false })
]
}); });
service = TestBed.inject(TaskFilterCloudService); service = TestBed.inject(TaskFilterCloudService);
notificationCloudService = TestBed.inject(NotificationCloudService); notificationCloudService = TestBed.inject(NotificationCloudService);
@ -270,10 +266,7 @@ describe('Inject [LocalPreferenceCloudService] into the TaskFilterCloudService',
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [HttpClientTestingModule, ProcessServiceCloudTestingModule, ApolloModule], imports: [HttpClientTestingModule, ProcessServiceCloudTestingModule, ApolloModule],
providers: [ providers: [{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }]
{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService },
provideMockFeatureFlags({ ['studio-ws-graphql-subprotocol']: false })
]
}); });
service = TestBed.inject(TaskFilterCloudService); service = TestBed.inject(TaskFilterCloudService);
preferenceCloudService = service.preferenceService; preferenceCloudService = service.preferenceService;

20
package-lock.json generated
View File

@ -35,7 +35,6 @@
"date-fns": "^2.30.0", "date-fns": "^2.30.0",
"dotenv-expand": "^5.1.0", "dotenv-expand": "^5.1.0",
"event-emitter": "^0.3.5", "event-emitter": "^0.3.5",
"graphql-ws": "^5.16.0",
"material-icons": "^1.13.12", "material-icons": "^1.13.12",
"minimatch-browser": "1.0.0", "minimatch-browser": "1.0.0",
"ng2-charts": "^4.1.1", "ng2-charts": "^4.1.1",
@ -116,7 +115,7 @@
"eslint-plugin-rxjs": "^5.0.3", "eslint-plugin-rxjs": "^5.0.3",
"eslint-plugin-storybook": "^0.11.1", "eslint-plugin-storybook": "^0.11.1",
"eslint-plugin-unicorn": "^49.0.0", "eslint-plugin-unicorn": "^49.0.0",
"graphql": "^16.9.0", "graphql": "^16.8.1",
"husky": "^7.0.4", "husky": "^7.0.4",
"jasmine-ajax": "4.0.0", "jasmine-ajax": "4.0.0",
"jasmine-core": "5.4.0", "jasmine-core": "5.4.0",
@ -19157,8 +19156,7 @@
}, },
"node_modules/graphql": { "node_modules/graphql": {
"version": "16.9.0", "version": "16.9.0",
"resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz", "license": "MIT",
"integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==",
"engines": { "engines": {
"node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0"
} }
@ -19176,20 +19174,6 @@
"graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0" "graphql": "^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0"
} }
}, },
"node_modules/graphql-ws": {
"version": "5.16.0",
"resolved": "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.16.0.tgz",
"integrity": "sha512-Ju2RCU2dQMgSKtArPbEtsK5gNLnsQyTNIo/T7cZNp96niC1x0KdJNZV0TIoilceBPQwfb5itrGl8pkFeOUMl4A==",
"workspaces": [
"website"
],
"engines": {
"node": ">=10"
},
"peerDependencies": {
"graphql": ">=0.11 <=16"
}
},
"node_modules/guess-parser": { "node_modules/guess-parser": {
"version": "0.4.22", "version": "0.4.22",
"dev": true, "dev": true,

View File

@ -55,7 +55,6 @@
"date-fns": "^2.30.0", "date-fns": "^2.30.0",
"dotenv-expand": "^5.1.0", "dotenv-expand": "^5.1.0",
"event-emitter": "^0.3.5", "event-emitter": "^0.3.5",
"graphql-ws": "^5.16.0",
"material-icons": "^1.13.12", "material-icons": "^1.13.12",
"minimatch-browser": "1.0.0", "minimatch-browser": "1.0.0",
"ng2-charts": "^4.1.1", "ng2-charts": "^4.1.1",
@ -136,7 +135,7 @@
"eslint-plugin-rxjs": "^5.0.3", "eslint-plugin-rxjs": "^5.0.3",
"eslint-plugin-storybook": "^0.11.1", "eslint-plugin-storybook": "^0.11.1",
"eslint-plugin-unicorn": "^49.0.0", "eslint-plugin-unicorn": "^49.0.0",
"graphql": "^16.9.0", "graphql": "^16.8.1",
"husky": "^7.0.4", "husky": "^7.0.4",
"jasmine-ajax": "4.0.0", "jasmine-ajax": "4.0.0",
"jasmine-core": "5.4.0", "jasmine-core": "5.4.0",