From 226a6548a1fbaecad9f340e0ac688ef334ab4049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miko=C5=82aj=20Serwicki?= <55290524+mserwicki@users.noreply.github.com> Date: Tue, 17 May 2022 11:35:52 +0200 Subject: [PATCH] [AAE-8713] feat: create api registry with factories support (#7634) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Andras Popovics Co-authored-by: Michal Dobkiewicz Co-authored-by: MichaƂ Dobkiewicz --- lib/core/api/api-client.factory.ts | 25 +++++++ lib/core/api/api-clients.service.spec.ts | 66 +++++++++++++++++++ lib/core/api/api-clients.service.ts | 54 +++++++++++++++ lib/core/api/api.module.ts | 29 ++++++++ .../activiti/activiti-client.module.ts | 38 +++++++++++ .../api/clients/alfresco-js-clients.module.ts | 28 ++++++++ .../discovery/discovery-client.module.ts | 36 ++++++++++ lib/core/api/clients/index.ts | 2 + lib/core/api/index.ts | 5 ++ lib/core/api/legacy-api-client.factory.ts | 30 +++++++++ lib/core/api/types.ts | 22 +++++++ lib/core/core.module.ts | 7 +- lib/core/index.ts | 1 + lib/core/services/discovery-api.service.ts | 14 ++-- 14 files changed, 351 insertions(+), 6 deletions(-) create mode 100644 lib/core/api/api-client.factory.ts create mode 100644 lib/core/api/api-clients.service.spec.ts create mode 100644 lib/core/api/api-clients.service.ts create mode 100644 lib/core/api/api.module.ts create mode 100644 lib/core/api/clients/activiti/activiti-client.module.ts create mode 100644 lib/core/api/clients/alfresco-js-clients.module.ts create mode 100644 lib/core/api/clients/discovery/discovery-client.module.ts create mode 100644 lib/core/api/clients/index.ts create mode 100644 lib/core/api/index.ts create mode 100644 lib/core/api/legacy-api-client.factory.ts create mode 100644 lib/core/api/types.ts diff --git a/lib/core/api/api-client.factory.ts b/lib/core/api/api-client.factory.ts new file mode 100644 index 0000000000..0d9af01f6a --- /dev/null +++ b/lib/core/api/api-client.factory.ts @@ -0,0 +1,25 @@ +/*! + * @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 { InjectionToken } from '@angular/core'; +import { Constructor } from './types'; + +export interface ApiClientFactory { + create(apiClass: Constructor): T; +} + +export const API_CLIENT_FACTORY_TOKEN = new InjectionToken('api-client-factory'); diff --git a/lib/core/api/api-clients.service.spec.ts b/lib/core/api/api-clients.service.spec.ts new file mode 100644 index 0000000000..df23b75349 --- /dev/null +++ b/lib/core/api/api-clients.service.spec.ts @@ -0,0 +1,66 @@ +/*! + * @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 { AboutApi } from '@alfresco/js-api'; +import { TestBed } from '@angular/core/testing'; +import { ApiClientFactory, API_CLIENT_FACTORY_TOKEN } from './api-client.factory'; +import { ApiClientsService } from './api-clients.service'; +import { Constructor } from './types'; + +class MockApiClientFactory implements ApiClientFactory { + create(apiClass: Constructor): T { + return new apiClass(); + } +} + +describe('ApiService', () => { + let apiService: ApiClientsService; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ + ApiClientsService, + { provide: API_CLIENT_FACTORY_TOKEN, useClass: MockApiClientFactory } + ] + }); + apiService = TestBed.inject(ApiClientsService); + }); + + it('should add api to registry', () => { + apiService.register('ActivitiClient.about', AboutApi); + + expect(apiService.get('ActivitiClient.about') instanceof AboutApi).toBeTruthy(); + }); + + it('should throw error if we try to get unregisterd API', () => { + expect(() => apiService.get('ActivitiClient.about')).toThrowError(); + + apiService.register('ActivitiClient.about', AboutApi); + + expect(() => apiService.get('ActivitiClient.about')).not.toThrowError(); + }); + + it('should create only single instance of API', () => { + apiService.register('ActivitiClient.about', AboutApi); + + const a = apiService.get('ActivitiClient.about'); + const b = apiService.get('ActivitiClient.about'); + + expect(a).toBe(b); + }); + +}); diff --git a/lib/core/api/api-clients.service.ts b/lib/core/api/api-clients.service.ts new file mode 100644 index 0000000000..a1147484c4 --- /dev/null +++ b/lib/core/api/api-clients.service.ts @@ -0,0 +1,54 @@ +/*! + * @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 { Inject, Injectable } from '@angular/core'; +import { ApiClientFactory, API_CLIENT_FACTORY_TOKEN } from './api-client.factory'; +import { Constructor, Dictionary } from './types'; + +@Injectable() +export class ApiClientsService { + + constructor(@Inject(API_CLIENT_FACTORY_TOKEN) private apiCreateFactory: ApiClientFactory) { } + + private registry: Dictionary> = {}; + private instances: Partial = {}; + + get(apiName: T): Api.ApiRegistry[T] { + + const ApiClass = this.registry[apiName]; + + if (!ApiClass) { + throw new Error(`Api not registred: ${apiName}`); + } + + return this.instances[apiName] as Api.ApiRegistry[T] ?? this.instantiateApi(apiName); + } + + + register(apiName: T, api: Constructor): void { + this.registry[apiName] = api; + } + + private instantiateApi(apiName: T): Api.ApiRegistry[T] { + const ApiClass = this.registry[apiName]; + const instance = this.apiCreateFactory.create(ApiClass); + this.instances[apiName] = instance; + + return instance; + } +} + diff --git a/lib/core/api/api.module.ts b/lib/core/api/api.module.ts new file mode 100644 index 0000000000..e4f9b3ab77 --- /dev/null +++ b/lib/core/api/api.module.ts @@ -0,0 +1,29 @@ +/*! + * @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 { NgModule } from '@angular/core'; +import { API_CLIENT_FACTORY_TOKEN } from './api-client.factory'; +import { ApiClientsService } from './api-clients.service'; +import { LegacyClientFactory } from './legacy-api-client.factory'; + +@NgModule({ + providers: [ + ApiClientsService, + { provide: API_CLIENT_FACTORY_TOKEN, useClass: LegacyClientFactory } + ] +}) +export class ApiModule { } diff --git a/lib/core/api/clients/activiti/activiti-client.module.ts b/lib/core/api/clients/activiti/activiti-client.module.ts new file mode 100644 index 0000000000..19270158dc --- /dev/null +++ b/lib/core/api/clients/activiti/activiti-client.module.ts @@ -0,0 +1,38 @@ +/*! + * @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 { AboutApi, SystemPropertiesApi } from '@alfresco/js-api'; +import { NgModule } from '@angular/core'; +import { ApiClientsService } from '../../api-clients.service'; + +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace Api { + interface ApiRegistry { + ['ActivitiClient.about']: AboutApi; + ['ActivitiClient.system-properties']: SystemPropertiesApi; + } + } +} + +@NgModule({}) +export class ActivitiClientModule { + constructor(private apiClientsService: ApiClientsService) { + this.apiClientsService.register('ActivitiClient.about', AboutApi); + this.apiClientsService.register('ActivitiClient.system-properties', SystemPropertiesApi); + } +} diff --git a/lib/core/api/clients/alfresco-js-clients.module.ts b/lib/core/api/clients/alfresco-js-clients.module.ts new file mode 100644 index 0000000000..61d4446ac1 --- /dev/null +++ b/lib/core/api/clients/alfresco-js-clients.module.ts @@ -0,0 +1,28 @@ +/*! + * @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 { NgModule } from '@angular/core'; +import { ActivitiClientModule } from './activiti/activiti-client.module'; +import { DiscoveryClientModule } from './discovery/discovery-client.module'; + +@NgModule({ + imports: [ + ActivitiClientModule, + DiscoveryClientModule + ] +}) +export class AlfrescoJsClientsModule { } diff --git a/lib/core/api/clients/discovery/discovery-client.module.ts b/lib/core/api/clients/discovery/discovery-client.module.ts new file mode 100644 index 0000000000..ce1c9412b2 --- /dev/null +++ b/lib/core/api/clients/discovery/discovery-client.module.ts @@ -0,0 +1,36 @@ +/*! + * @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 { DiscoveryApi } from '@alfresco/js-api'; +import { NgModule } from '@angular/core'; +import { ApiClientsService } from '../../api-clients.service'; + +declare global { + // eslint-disable-next-line @typescript-eslint/no-namespace + namespace Api { + interface ApiRegistry { + ['DiscoveryClient.discovery']: DiscoveryApi; + } + } +} + +@NgModule({}) +export class DiscoveryClientModule { + constructor(private apiClientsService: ApiClientsService) { + this.apiClientsService.register('DiscoveryClient.discovery', DiscoveryApi); + } +} diff --git a/lib/core/api/clients/index.ts b/lib/core/api/clients/index.ts new file mode 100644 index 0000000000..1ed1d0bf2d --- /dev/null +++ b/lib/core/api/clients/index.ts @@ -0,0 +1,2 @@ +export * from './activiti/activiti-client.module'; +export * from './alfresco-js-clients.module'; diff --git a/lib/core/api/index.ts b/lib/core/api/index.ts new file mode 100644 index 0000000000..6b687d926a --- /dev/null +++ b/lib/core/api/index.ts @@ -0,0 +1,5 @@ +export * from './api-client.factory'; +export * from './api-clients.service'; +export * from './api.module'; +export * from './clients'; + diff --git a/lib/core/api/legacy-api-client.factory.ts b/lib/core/api/legacy-api-client.factory.ts new file mode 100644 index 0000000000..cdda0e7219 --- /dev/null +++ b/lib/core/api/legacy-api-client.factory.ts @@ -0,0 +1,30 @@ +/*! + * @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 { Injectable } from '@angular/core'; +import { AlfrescoApiService } from '../services/alfresco-api.service'; +import { ApiClientFactory } from './api-client.factory'; +import { Constructor } from './types'; + +@Injectable() +export class LegacyClientFactory implements ApiClientFactory { + constructor(private alfrescoApiService: AlfrescoApiService) { } + + create(apiClass: Constructor): T { + return new apiClass(this.alfrescoApiService.getInstance()); + } +} diff --git a/lib/core/api/types.ts b/lib/core/api/types.ts new file mode 100644 index 0000000000..356d4ceb4f --- /dev/null +++ b/lib/core/api/types.ts @@ -0,0 +1,22 @@ +/*! + * @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. + */ + +export interface Dictionary { + [key: string]: T; +}; + +export type Constructor = new (...args: any[]) => T; diff --git a/lib/core/core.module.ts b/lib/core/core.module.ts index 3f466dd708..bba15a0a1c 100644 --- a/lib/core/core.module.ts +++ b/lib/core/core.module.ts @@ -60,6 +60,9 @@ import { DirectionalityConfigService } from './services/directionality-config.se import { SearchTextModule } from './search-text/search-text-input.module'; import { versionCompatibilityFactory } from './services/version-compatibility-factory'; import { VersionCompatibilityService } from './services/version-compatibility.service'; +import { ApiModule } from './api/api.module'; +import { AlfrescoJsClientsModule } from './api/clients/alfresco-js-clients.module'; + @NgModule({ imports: [ TranslateModule, @@ -94,7 +97,9 @@ import { VersionCompatibilityService } from './services/version-compatibility.se SortingPickerModule, NotificationHistoryModule, SearchTextModule, - BlankPageModule + BlankPageModule, + ApiModule, + AlfrescoJsClientsModule ], exports: [ AboutModule, diff --git a/lib/core/index.ts b/lib/core/index.ts index 679146759a..d3c602cbcf 100644 --- a/lib/core/index.ts +++ b/lib/core/index.ts @@ -53,4 +53,5 @@ export * from './mock/index'; export * from './testing'; export * from './material.module'; +export * from './api'; export * from './core.module'; diff --git a/lib/core/services/discovery-api.service.ts b/lib/core/services/discovery-api.service.ts index be9310ec9b..70a0680e5e 100644 --- a/lib/core/services/discovery-api.service.ts +++ b/lib/core/services/discovery-api.service.ts @@ -18,11 +18,13 @@ import { Injectable } from '@angular/core'; import { from, Observable, throwError, Subject } from 'rxjs'; import { catchError, map, switchMap, filter, take } from 'rxjs/operators'; -import { AboutApi, DiscoveryApi, RepositoryInfo, SystemPropertiesApi, SystemPropertiesRepresentation } from '@alfresco/js-api'; +import { RepositoryInfo, SystemPropertiesRepresentation } from '@alfresco/js-api'; import { BpmProductVersionModel } from '../models/product-version.model'; import { AlfrescoApiService } from './alfresco-api.service'; import { AuthenticationService } from './authentication.service'; +import { ApiClientsService } from '../api'; + @Injectable({ providedIn: 'root' @@ -36,7 +38,8 @@ export class DiscoveryApiService { constructor( private apiService: AlfrescoApiService, - private authenticationService: AuthenticationService) { + private authenticationService: AuthenticationService, + private apiClientsService: ApiClientsService) { this.authenticationService.onLogin .pipe( @@ -47,13 +50,14 @@ export class DiscoveryApiService { .subscribe((info) => this.ecmProductInfo$.next(info)); } + /** * Gets product information for Content Services. * * @returns ProductVersionModel containing product details */ getEcmProductInfo(): Observable { - const discoveryApi = new DiscoveryApi(this.apiService.getInstance()); + const discoveryApi = this.apiClientsService.get('DiscoveryClient.discovery'); return from(discoveryApi.getRepositoryInformation()) .pipe( @@ -68,7 +72,7 @@ export class DiscoveryApiService { * @returns ProductVersionModel containing product details */ getBpmProductInfo(): Observable { - const aboutApi = new AboutApi(this.apiService.getInstance()); + const aboutApi = this.apiClientsService.get('ActivitiClient.about'); return from(aboutApi.getAppVersion()) .pipe( @@ -78,7 +82,7 @@ export class DiscoveryApiService { } getBPMSystemProperties(): Observable { - const systemPropertiesApi = new SystemPropertiesApi(this.apiService.getInstance()); + const systemPropertiesApi = this.apiClientsService.get('ActivitiClient.system-properties'); return from(systemPropertiesApi.getProperties()) .pipe(