Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AAE-4966] Extensible app config #6914

Merged
merged 9 commits into from Apr 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -142,16 +142,25 @@ describe('AspectListService', () => {
describe('should fetch the list of the aspects', () => {

let service: AspectListService;
const appConfigService: AppConfigService = new AppConfigService(null);

const aspectTypesApi = jasmine.createSpyObj('AspectsApi', ['listAspects']);
const apiService: AlfrescoApiService = new AlfrescoApiService(null, null);
const logService: LogService = new LogService(appConfigService);
let appConfigService: AppConfigService;
let apiService: AlfrescoApiService;
let logService: LogService;
let aspectTypesApi: any;

beforeEach(() => {
TestBed.configureTestingModule({
imports: [ContentTestingModule]
});

aspectTypesApi = jasmine.createSpyObj('AspectsApi', ['listAspects']);
appConfigService = TestBed.inject(AppConfigService);
apiService = TestBed.inject(AlfrescoApiService);
logService = TestBed.inject(LogService);

spyOn(appConfigService, 'get').and.returnValue({ 'default': ['frs:AspectOne'] });
spyOnProperty(apiService, 'aspectsApi').and.returnValue(aspectTypesApi);
service = new AspectListService(apiService, appConfigService, null, logService);

service = TestBed.inject(AspectListService);
});

it('should get the list of only available aspects', async(() => {
Expand Down
Expand Up @@ -16,16 +16,19 @@
*/

import { CustomResourcesService } from './custom-resources.service';
import { PaginationModel, AlfrescoApiServiceMock, AppConfigService, LogService, AppConfigServiceMock, StorageService } from '@alfresco/adf-core';
import { PaginationModel } from '@alfresco/adf-core';
import { TestBed } from '@angular/core/testing';
import { ContentTestingModule } from '../../testing/content.testing.module';

describe('CustomResourcesService', () => {
let customResourcesService: CustomResourcesService;

beforeEach(() => {
const logService = new LogService(new AppConfigServiceMock(null));
const alfrescoApiService = new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService());
TestBed.configureTestingModule({
imports: [ContentTestingModule]
});

customResourcesService = new CustomResourcesService(alfrescoApiService, logService);
customResourcesService = TestBed.inject(CustomResourcesService);
});

describe('loadFavorites', () => {
Expand Down
Expand Up @@ -15,9 +15,7 @@
* limitations under the License.
*/

import { AlfrescoApiServiceMock, AppConfigService, ContentService,
setupTestBed, TranslationMock, AlfrescoApiService, StorageService
} from '@alfresco/adf-core';
import { setupTestBed } from '@alfresco/adf-core';
import { FileNode, FolderNode } from '../../mock';
import { ContentActionHandler } from '../models/content-action.model';
import { DocumentActionsService } from './document-actions.service';
Expand All @@ -36,21 +34,12 @@ describe('DocumentActionsService', () => {
imports: [
TranslateModule.forRoot(),
ContentTestingModule
],
providers: [
{
provide: AlfrescoApiService,
useValue: new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService())
}
]
});

beforeEach(() => {
const alfrescoApiService = new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService());
const contentService = TestBed.inject(ContentService);

documentListService = new DocumentListService(contentService, alfrescoApiService, null, null);
service = new DocumentActionsService(null, null, new TranslationMock(), documentListService, contentService);
documentListService = TestBed.inject(DocumentListService);
service = TestBed.inject(DocumentActionsService);
});

it('should register default download action', () => {
Expand Down
Expand Up @@ -15,10 +15,8 @@
* limitations under the License.
*/

import { AlfrescoApiServiceMock, AlfrescoApiService,
AppConfigService, ContentService, setupTestBed, LogService, AppConfigServiceMock, StorageService } from '@alfresco/adf-core';
import { AlfrescoApiService, setupTestBed } from '@alfresco/adf-core';
import { DocumentListService } from './document-list.service';
import { CustomResourcesService } from './custom-resources.service';
import { TestBed } from '@angular/core/testing';
import { ContentTestingModule } from '../../testing/content.testing.module';
import { TranslateModule } from '@ngx-translate/core';
Expand Down Expand Up @@ -72,11 +70,8 @@ describe('DocumentListService', () => {
});

beforeEach(() => {
const logService = new LogService(new AppConfigServiceMock(null));
const contentService = TestBed.inject(ContentService);
alfrescoApiService = new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService());
const customActionService = new CustomResourcesService(alfrescoApiService, logService);
service = new DocumentListService(contentService, alfrescoApiService, logService, customActionService);
alfrescoApiService = TestBed.inject(AlfrescoApiService);
service = TestBed.inject(DocumentListService);
jasmine.Ajax.install();
});

Expand Down
Expand Up @@ -16,7 +16,7 @@
*/

import { TestBed } from '@angular/core/testing';
import { AlfrescoApiServiceMock, AppConfigService, ContentService, setupTestBed, TranslationMock, StorageService } from '@alfresco/adf-core';
import { AppConfigService, setupTestBed } from '@alfresco/adf-core';
import { Observable } from 'rxjs';
import { FileNode, FolderNode } from '../../mock';
import { ContentActionHandler } from '../models/content-action.model';
Expand All @@ -41,10 +41,8 @@ describe('FolderActionsService', () => {
const appConfig: AppConfigService = TestBed.inject(AppConfigService);
appConfig.config.ecmHost = 'http://localhost:9876/ecm';

const contentService = TestBed.inject(ContentService);
const alfrescoApiService = new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService());
documentListService = new DocumentListService(contentService, alfrescoApiService, null, null);
service = new FolderActionsService(null, documentListService, contentService, new TranslationMock());
documentListService = TestBed.inject(DocumentListService);
service = TestBed.inject(FolderActionsService);
});

it('should register custom action handler', () => {
Expand Down
Expand Up @@ -19,19 +19,25 @@ import { SearchSortingPickerComponent } from './search-sorting-picker.component'
import { SearchQueryBuilderService } from '../../search-query-builder.service';
import { AppConfigService } from '@alfresco/adf-core';
import { SearchConfiguration } from '../../search-configuration.interface';
import { TestBed } from '@angular/core/testing';
import { ContentTestingModule } from '../../../testing/content.testing.module';

describe('SearchSortingPickerComponent', () => {

let queryBuilder: SearchQueryBuilderService;
let component: SearchSortingPickerComponent;

const buildConfig = (searchSettings): AppConfigService => {
const config = new AppConfigService(null);
const config = TestBed.inject(AppConfigService);
config.config.search = searchSettings;
return config;
};

beforeEach(() => {
TestBed.configureTestingModule({
imports: [ContentTestingModule]
});

const config: SearchConfiguration = {
sorting: {
options: [
Expand Down
Expand Up @@ -18,11 +18,19 @@
import { SearchConfiguration } from './search-configuration.interface';
import { AppConfigService } from '@alfresco/adf-core';
import { SearchHeaderQueryBuilderService } from './search-header-query-builder.service';
import { TestBed } from '@angular/core/testing';
import { ContentTestingModule } from '../testing/content.testing.module';

describe('SearchHeaderQueryBuilderService', () => {

beforeEach(() => {
TestBed.configureTestingModule({
imports: [ContentTestingModule]
});
});

const buildConfig = (searchSettings): AppConfigService => {
const config = new AppConfigService(null);
const config = TestBed.inject(AppConfigService);
config.config['search-headers'] = searchSettings;
return config;
};
Expand Down
Expand Up @@ -19,11 +19,19 @@ import { SearchQueryBuilderService } from './search-query-builder.service';
import { SearchConfiguration } from './search-configuration.interface';
import { AppConfigService } from '@alfresco/adf-core';
import { FacetField } from './facet-field.interface';
import { TestBed } from '@angular/core/testing';
import { ContentTestingModule } from '../testing/content.testing.module';

describe('SearchQueryBuilder', () => {

beforeEach(() => {
TestBed.configureTestingModule({
imports: [ContentTestingModule]
});
});

const buildConfig = (searchSettings): AppConfigService => {
const config = new AppConfigService(null);
const config = TestBed.inject(AppConfigService);
config.config.search = searchSettings;
return config;
};
Expand Down
81 changes: 59 additions & 22 deletions lib/core/app-config/app-config.service.spec.ts
Expand Up @@ -15,16 +15,25 @@
* limitations under the License.
*/

import { HttpClientModule } from '@angular/common/http';
import { async, inject, TestBed } from '@angular/core/testing';
import { HttpClient, HttpClientModule } from '@angular/common/http';
import { async, TestBed } from '@angular/core/testing';
import { AppConfigService } from './app-config.service';
import { AppConfigModule } from './app-config.module';
import { ExtensionConfig, ExtensionService } from '@alfresco/adf-extensions';
import { of } from 'rxjs';

declare let jasmine: any;
class TestExtensionService extends ExtensionService {

onSetup(config: ExtensionConfig) {
this.onSetup$.next(config);
}
}

describe('AppConfigService', () => {

let appConfigService: AppConfigService;
let extensionService: ExtensionService;
let httpClient: HttpClient;

const mockResponse = {
ecmHost: 'http://localhost:4000/ecm',
Expand All @@ -46,32 +55,60 @@ describe('AppConfigService', () => {
AppConfigModule
],
providers: [
{ provide: AppConfigService, useClass: AppConfigService }
{ provide: ExtensionService, useClass: TestExtensionService }
]
});
});

beforeEach(() => {
httpClient = TestBed.inject(HttpClient);
spyOn(httpClient, 'get').and.returnValue(of(mockResponse));

extensionService = TestBed.inject(ExtensionService);

jasmine.Ajax.install();
appConfigService = TestBed.inject(AppConfigService);
appConfigService.load();
});

beforeEach(
inject([AppConfigService], (appConfig: AppConfigService) => {
appConfigService = appConfig;
appConfigService.load();

jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200,
contentType: 'application/json',
responseText: JSON.stringify(mockResponse)
});
})
);

afterEach(() => {
jasmine.Ajax.uninstall();
it('should merge the configs from extensions', () => {
appConfigService.config = {
application: {
name: 'application name'
}
};

(extensionService as TestExtensionService).onSetup({
appConfig: {
application: {
name: 'custom name'
}
}
} as any);

expect(appConfigService.get('application.name')).toEqual('custom name');
});

it('should export service in the module', () => {
expect(appConfigService).toBeDefined();
it('should merge the configs upon new data loaded', async (done) => {
appConfigService.config = {
application: {
name: 'application name'
}
};

(extensionService as TestExtensionService).onSetup({
appConfig: {
application: {
name: 'custom name'
}
}
} as any);

expect(appConfigService.get('application.name')).toEqual('custom name');

await appConfigService.load();

expect(appConfigService.get('application.name')).toEqual('custom name');
done();
});

it('should stream only the selected attribute changes when using select', async(() => {
Expand Down
37 changes: 32 additions & 5 deletions lib/core/app-config/app-config.service.ts
Expand Up @@ -19,7 +19,8 @@ import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { ObjectUtils } from '../utils/object-utils';
import { Observable, Subject } from 'rxjs';
import { map, distinctUntilChanged } from 'rxjs/operators';
import { map, distinctUntilChanged, take } from 'rxjs/operators';
import { ExtensionConfig, ExtensionService, mergeObjects } from '@alfresco/adf-extensions';

/* spellchecker: disable */
export enum AppConfigValues {
Expand Down Expand Up @@ -69,9 +70,13 @@ export class AppConfigService {
protected onLoadSubject: Subject<any>;
onLoad: Observable<any>;

constructor(private http: HttpClient) {
constructor(protected http: HttpClient, protected extensionService: ExtensionService) {
this.onLoadSubject = new Subject();
this.onLoad = this.onLoadSubject.asObservable();

extensionService.setup$.subscribe((config) => {
DenysVuika marked this conversation as resolved.
Show resolved Hide resolved
this.onExtensionsLoaded(config);
});
}

/**
Expand Down Expand Up @@ -142,6 +147,29 @@ export class AppConfigService {
return location.port ? prefix + location.port : '';
}

protected onLoaded() {
this.onLoadSubject.next(this.config);
}

protected onDataLoaded(data: any) {
this.config = Object.assign({}, this.config, data || {});
this.onLoadSubject.next(this.config);

this.extensionService.setup$
.pipe(take(1))
.subscribe((config) => this.onExtensionsLoaded(config));
}

protected onExtensionsLoaded(config: ExtensionConfig) {
if (config) {
const customConfig = config.appConfig;

if (customConfig) {
this.config = mergeObjects(this.config, customConfig);
}
}
}

/**
* Loads the config file.
* @returns Notification when loading is complete
Expand All @@ -152,11 +180,10 @@ export class AppConfigService {

if (this.status === Status.INIT) {
this.status = Status.LOADING;
await this.http.get(configUrl).subscribe(
this.http.get(configUrl).subscribe(
(data: any) => {
this.status = Status.LOADED;
this.config = Object.assign({}, this.config, data || {});
this.onLoadSubject.next(this.config);
this.onDataLoaded(data);
resolve(this.config);
},
() => {
Expand Down