mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-05-12 17:04:57 +00:00
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:
parent
5461bedb3e
commit
b09f4cacf9
@ -6,5 +6,5 @@
|
||||
},
|
||||
|
||||
"exclude": ["../**/*.spec.ts" ],
|
||||
"include": ["../src/**/*", "*.js", "../../core/feature-flags"]
|
||||
"include": ["../src/**/*", "*.js"]
|
||||
}
|
||||
|
@ -32,7 +32,6 @@ import {
|
||||
import { ProcessFilterCloudModel } from '../models/process-filter-cloud.model';
|
||||
import { IdentityUserService } from '../../../people/services/identity-user.service';
|
||||
import { NotificationCloudService } from '../../../services/notification-cloud.service';
|
||||
import { provideMockFeatureFlags } from '@alfresco/adf-core/feature-flags';
|
||||
|
||||
describe('ProcessFilterCloudService', () => {
|
||||
let service: ProcessFilterCloudService;
|
||||
@ -53,10 +52,7 @@ describe('ProcessFilterCloudService', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [ProcessServiceCloudTestingModule],
|
||||
providers: [
|
||||
{ provide: PROCESS_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService },
|
||||
provideMockFeatureFlags({ ['studio-ws-graphql-subprotocol']: false })
|
||||
]
|
||||
providers: [{ provide: PROCESS_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }]
|
||||
});
|
||||
service = TestBed.inject(ProcessFilterCloudService);
|
||||
|
||||
@ -72,7 +68,7 @@ describe('ProcessFilterCloudService', () => {
|
||||
});
|
||||
|
||||
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(getCurrentUserInfoSpy).toHaveBeenCalled();
|
||||
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) => {
|
||||
getPreferencesSpy.and.returnValue(of(fakeProcessCloudFilterWithDifferentEntries));
|
||||
|
||||
service.getProcessFilters('mock-appName').subscribe((res: ProcessFilterCloudModel[]) => {
|
||||
service.getProcessFilters('mock-appName').subscribe((res: any) => {
|
||||
expect(res).toBeDefined();
|
||||
expect(res).not.toBeNull();
|
||||
expect(res.length).toBe(3);
|
||||
@ -247,7 +243,6 @@ describe('ProcessFilterCloudService', () => {
|
||||
it('should reset filters to default values', async () => {
|
||||
const changedFilter = new ProcessFilterCloudModel(fakeProcessCloudFilters[0]);
|
||||
changedFilter.processDefinitionKey = 'modifiedProcessDefinitionKey';
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
spyOn<any>(service, 'defaultProcessFilters').and.returnValue(fakeProcessCloudFilters);
|
||||
|
||||
await service.resetProcessFilterToDefaults('mock-appName', changedFilter).toPromise();
|
||||
|
@ -18,12 +18,17 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { ProcessServiceCloudTestingModule } from '../testing/process-service-cloud.testing.module';
|
||||
import { NotificationCloudService } from './notification-cloud.service';
|
||||
import { WebSocketService } from './web-socket.service';
|
||||
import { provideMockFeatureFlags } from '@alfresco/adf-core/feature-flags';
|
||||
import { Apollo } from 'apollo-angular';
|
||||
|
||||
describe('NotificationCloudService', () => {
|
||||
let service: NotificationCloudService;
|
||||
let wsService: WebSocketService;
|
||||
let apollo: Apollo;
|
||||
let apolloCreateSpy: jasmine.Spy;
|
||||
let apolloSubscribeSpy: jasmine.Spy;
|
||||
|
||||
const useMock: any = {
|
||||
subscribe: () => {}
|
||||
};
|
||||
|
||||
const queryMock = `
|
||||
subscription {
|
||||
@ -38,25 +43,39 @@ describe('NotificationCloudService', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [ProcessServiceCloudTestingModule],
|
||||
providers: [WebSocketService, provideMockFeatureFlags({ ['studio-ws-graphql-subprotocol']: false })]
|
||||
imports: [ProcessServiceCloudTestingModule]
|
||||
});
|
||||
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', () => {
|
||||
const getSubscriptionSpy = spyOn(wsService, 'getSubscription').and.callThrough();
|
||||
it('should not create more than one websocket per app if it was already created', () => {
|
||||
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');
|
||||
|
||||
expect(getSubscriptionSpy).toHaveBeenCalledWith({
|
||||
apolloClientName: 'myAppName',
|
||||
wsUrl: 'myAppName/notifications',
|
||||
httpUrl: 'myAppName/notifications/graphql',
|
||||
subscriptionOptions: {
|
||||
query: jasmine.any(Object)
|
||||
}
|
||||
});
|
||||
expect(apolloCreateSpy).toHaveBeenCalledTimes(1);
|
||||
expect(apolloSubscribeSpy).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('should create new websocket if it is subscribing to new app', () => {
|
||||
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);
|
||||
});
|
||||
});
|
||||
|
@ -15,24 +15,101 @@
|
||||
* 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 { 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({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class NotificationCloudService {
|
||||
constructor(private readonly webSocketService: WebSocketService) {}
|
||||
export class NotificationCloudService extends BaseCloudService {
|
||||
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) {
|
||||
return this.webSocketService.getSubscription({
|
||||
apolloClientName: appName,
|
||||
wsUrl: `${appName}/notifications`,
|
||||
httpUrl: `${appName}/notifications/graphql`,
|
||||
subscriptionOptions: {
|
||||
query: gql(gqlQuery)
|
||||
}
|
||||
});
|
||||
this.initNotificationsForApp(appName);
|
||||
return this.apollo.use(appName).subscribe({ query: gql(gqlQuery) });
|
||||
}
|
||||
}
|
||||
|
@ -24,4 +24,3 @@ export * from './form-fields.interfaces';
|
||||
export * from './base-cloud.service';
|
||||
export * from './task-list-cloud.service.interface';
|
||||
export * from './variable-mapper.sevice';
|
||||
export * from './web-socket.service';
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
@ -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
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
@ -39,7 +39,6 @@ import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
||||
import { MatExpansionPanelHarness } from '@angular/material/expansion/testing';
|
||||
import { MatSelectHarness } from '@angular/material/select/testing';
|
||||
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
|
||||
import { provideMockFeatureFlags } from '@alfresco/adf-core/feature-flags';
|
||||
|
||||
describe('EditServiceTaskFilterCloudComponent', () => {
|
||||
let loader: HarnessLoader;
|
||||
@ -51,16 +50,12 @@ describe('EditServiceTaskFilterCloudComponent', () => {
|
||||
let getTaskFilterSpy: jasmine.Spy;
|
||||
let getDeployedApplicationsSpy: jasmine.Spy;
|
||||
let taskService: TaskCloudService;
|
||||
const afterClosedSubject = new Subject<unknown>();
|
||||
const afterClosedSubject = new Subject<any>();
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [ProcessServiceCloudTestingModule, TaskFiltersCloudModule, MatIconTestingModule],
|
||||
providers: [
|
||||
MatDialog,
|
||||
{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService },
|
||||
provideMockFeatureFlags({ ['studio-ws-graphql-subprotocol']: false })
|
||||
]
|
||||
providers: [MatDialog, { provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }]
|
||||
});
|
||||
fixture = TestBed.createComponent(EditServiceTaskFilterCloudComponent);
|
||||
component = fixture.componentInstance;
|
||||
@ -68,8 +63,9 @@ describe('EditServiceTaskFilterCloudComponent', () => {
|
||||
appsService = TestBed.inject(AppsProcessCloudService);
|
||||
taskService = TestBed.inject(TaskCloudService);
|
||||
dialog = TestBed.inject(MatDialog);
|
||||
const dialogRefMock = jasmine.createSpyObj('MatDialogRef', ['afterClosed']);
|
||||
dialogRefMock.afterClosed.and.returnValue(afterClosedSubject);
|
||||
const dialogRefMock: any = {
|
||||
afterClosed: () => afterClosedSubject
|
||||
};
|
||||
spyOn(dialog, 'open').and.returnValue(dialogRefMock);
|
||||
getTaskFilterSpy = spyOn(service, 'getTaskFilterById').and.returnValue(of(fakeServiceFilter));
|
||||
getDeployedApplicationsSpy = spyOn(appsService, 'getDeployedApplicationsByStatus').and.returnValue(of(fakeApplicationInstance));
|
||||
|
@ -57,7 +57,6 @@ import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
||||
import { MatSelectHarness } from '@angular/material/select/testing';
|
||||
import { MatExpansionPanelHarness } from '@angular/material/expansion/testing';
|
||||
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
|
||||
import { provideMockFeatureFlags } from '@alfresco/adf-core/feature-flags';
|
||||
|
||||
describe('EditTaskFilterCloudComponent', () => {
|
||||
let loader: HarnessLoader;
|
||||
@ -71,16 +70,12 @@ describe('EditTaskFilterCloudComponent', () => {
|
||||
let getTaskFilterSpy: jasmine.Spy;
|
||||
let getDeployedApplicationsSpy: jasmine.Spy;
|
||||
let taskService: TaskCloudService;
|
||||
const afterClosedSubject = new Subject<unknown>();
|
||||
const afterClosedSubject = new Subject<any>();
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [ProcessServiceCloudTestingModule, TaskFiltersCloudModule, PeopleCloudModule, MatIconTestingModule],
|
||||
providers: [
|
||||
MatDialog,
|
||||
{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService },
|
||||
provideMockFeatureFlags({ ['studio-ws-graphql-subprotocol']: false })
|
||||
]
|
||||
providers: [MatDialog, { provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }]
|
||||
});
|
||||
fixture = TestBed.createComponent(EditTaskFilterCloudComponent);
|
||||
component = fixture.componentInstance;
|
||||
@ -90,8 +85,9 @@ describe('EditTaskFilterCloudComponent', () => {
|
||||
taskService = TestBed.inject(TaskCloudService);
|
||||
alfrescoApiService = TestBed.inject(AlfrescoApiService);
|
||||
dialog = TestBed.inject(MatDialog);
|
||||
const dialogRefMock = jasmine.createSpyObj('MatDialogRef', ['afterClosed']);
|
||||
dialogRefMock.afterClosed.and.returnValue(afterClosedSubject);
|
||||
const dialogRefMock: any = {
|
||||
afterClosed: () => afterClosedSubject
|
||||
};
|
||||
spyOn(dialog, 'open').and.returnValue(dialogRefMock);
|
||||
spyOn(alfrescoApiService, 'getInstance').and.returnValue(mockAlfrescoApi);
|
||||
getTaskFilterSpy = spyOn(service, 'getTaskFilterById').and.returnValue(of(fakeFilter));
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
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 { By } from '@angular/platform-browser';
|
||||
import { first, of, throwError } from 'rxjs';
|
||||
@ -32,7 +32,6 @@ import { HarnessLoader } from '@angular/cdk/testing';
|
||||
import { MatActionListItemHarness } from '@angular/material/list/testing';
|
||||
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
||||
import { TaskFilterCloudAdapter } from '../../../models/filter-cloud-model';
|
||||
import { provideMockFeatureFlags } from '@alfresco/adf-core/feature-flags';
|
||||
|
||||
describe('TaskFiltersCloudComponent', () => {
|
||||
let loader: HarnessLoader;
|
||||
@ -46,14 +45,10 @@ describe('TaskFiltersCloudComponent', () => {
|
||||
let getTaskListFiltersSpy: jasmine.Spy;
|
||||
let getTaskListCounterSpy: jasmine.Spy;
|
||||
|
||||
const configureTestingModule = (providers: unknown[]) => {
|
||||
const configureTestingModule = (providers: any[]) => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [ProcessServiceCloudTestingModule, TaskFiltersCloudModule],
|
||||
providers: [
|
||||
{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService },
|
||||
provideMockFeatureFlags({ ['studio-ws-graphql-subprotocol']: false }),
|
||||
...providers
|
||||
]
|
||||
providers: [{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }, ...providers]
|
||||
});
|
||||
taskFilterService = TestBed.inject(TaskFilterCloudService);
|
||||
taskListService = TestBed.inject(TaskListCloudService);
|
||||
@ -107,7 +102,7 @@ describe('TaskFiltersCloudComponent', () => {
|
||||
fixture.detectChanges();
|
||||
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);
|
||||
});
|
||||
|
||||
@ -270,7 +265,7 @@ describe('TaskFiltersCloudComponent', () => {
|
||||
component.showIcons = false;
|
||||
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);
|
||||
});
|
||||
|
@ -37,7 +37,6 @@ import { IdentityUserService } from '../../../people/services/identity-user.serv
|
||||
import { ApolloModule } from 'apollo-angular';
|
||||
import { StorageService } from '@alfresco/adf-core';
|
||||
import { TaskStatusFilter } from '../public-api';
|
||||
import { provideMockFeatureFlags } from '@alfresco/adf-core/feature-flags';
|
||||
|
||||
describe('TaskFilterCloudService', () => {
|
||||
let service: TaskFilterCloudService;
|
||||
@ -58,10 +57,7 @@ describe('TaskFilterCloudService', () => {
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [HttpClientTestingModule, ProcessServiceCloudTestingModule, ApolloModule],
|
||||
providers: [
|
||||
{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: UserPreferenceCloudService },
|
||||
provideMockFeatureFlags({ ['studio-ws-graphql-subprotocol']: false })
|
||||
]
|
||||
providers: [{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: UserPreferenceCloudService }]
|
||||
});
|
||||
service = TestBed.inject(TaskFilterCloudService);
|
||||
notificationCloudService = TestBed.inject(NotificationCloudService);
|
||||
@ -270,10 +266,7 @@ describe('Inject [LocalPreferenceCloudService] into the TaskFilterCloudService',
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [HttpClientTestingModule, ProcessServiceCloudTestingModule, ApolloModule],
|
||||
providers: [
|
||||
{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService },
|
||||
provideMockFeatureFlags({ ['studio-ws-graphql-subprotocol']: false })
|
||||
]
|
||||
providers: [{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }]
|
||||
});
|
||||
service = TestBed.inject(TaskFilterCloudService);
|
||||
preferenceCloudService = service.preferenceService;
|
||||
|
20
package-lock.json
generated
20
package-lock.json
generated
@ -35,7 +35,6 @@
|
||||
"date-fns": "^2.30.0",
|
||||
"dotenv-expand": "^5.1.0",
|
||||
"event-emitter": "^0.3.5",
|
||||
"graphql-ws": "^5.16.0",
|
||||
"material-icons": "^1.13.12",
|
||||
"minimatch-browser": "1.0.0",
|
||||
"ng2-charts": "^4.1.1",
|
||||
@ -116,7 +115,7 @@
|
||||
"eslint-plugin-rxjs": "^5.0.3",
|
||||
"eslint-plugin-storybook": "^0.11.1",
|
||||
"eslint-plugin-unicorn": "^49.0.0",
|
||||
"graphql": "^16.9.0",
|
||||
"graphql": "^16.8.1",
|
||||
"husky": "^7.0.4",
|
||||
"jasmine-ajax": "4.0.0",
|
||||
"jasmine-core": "5.4.0",
|
||||
@ -19157,8 +19156,7 @@
|
||||
},
|
||||
"node_modules/graphql": {
|
||||
"version": "16.9.0",
|
||||
"resolved": "https://registry.npmjs.org/graphql/-/graphql-16.9.0.tgz",
|
||||
"integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
"version": "0.4.22",
|
||||
"dev": true,
|
||||
|
@ -55,7 +55,6 @@
|
||||
"date-fns": "^2.30.0",
|
||||
"dotenv-expand": "^5.1.0",
|
||||
"event-emitter": "^0.3.5",
|
||||
"graphql-ws": "^5.16.0",
|
||||
"material-icons": "^1.13.12",
|
||||
"minimatch-browser": "1.0.0",
|
||||
"ng2-charts": "^4.1.1",
|
||||
@ -136,7 +135,7 @@
|
||||
"eslint-plugin-rxjs": "^5.0.3",
|
||||
"eslint-plugin-storybook": "^0.11.1",
|
||||
"eslint-plugin-unicorn": "^49.0.0",
|
||||
"graphql": "^16.9.0",
|
||||
"graphql": "^16.8.1",
|
||||
"husky": "^7.0.4",
|
||||
"jasmine-ajax": "4.0.0",
|
||||
"jasmine-core": "5.4.0",
|
||||
|
Loading…
x
Reference in New Issue
Block a user