mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[MNT-24459] Aspect list get all aspects when not all were fetched by first call (#10980)
* [MNT-24459] Aspect list get all aspects when not all were fetched by first call * [MNT-24459] CR fixes * [MNT-24459] Sonar issues fixed
This commit is contained in:
@@ -132,7 +132,9 @@ describe('AspectListDialogComponent', () => {
|
|||||||
describe('Without passing node id', () => {
|
describe('Without passing node id', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
aspectListService = TestBed.inject(AspectListService);
|
aspectListService = TestBed.inject(AspectListService);
|
||||||
spyOn(aspectListService, 'getAspects').and.returnValue(of(aspectListMock));
|
spyOn(aspectListService, 'getAllAspects').and.returnValue(
|
||||||
|
of({ standardAspectPaging: { list: { entries: aspectListMock } }, customAspectPaging: { list: { entries: customAspectListMock } } })
|
||||||
|
);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -247,9 +249,11 @@ describe('AspectListDialogComponent', () => {
|
|||||||
data.nodeId = 'fake-node-id';
|
data.nodeId = 'fake-node-id';
|
||||||
aspectListService = TestBed.inject(AspectListService);
|
aspectListService = TestBed.inject(AspectListService);
|
||||||
nodeService = TestBed.inject(NodesApiService);
|
nodeService = TestBed.inject(NodesApiService);
|
||||||
spyOn(aspectListService, 'getAspects').and.returnValue(of([...aspectListMock, ...customAspectListMock]));
|
spyOn(aspectListService, 'getAllAspects').and.returnValue(
|
||||||
|
of({ standardAspectPaging: { list: { entries: aspectListMock } }, customAspectPaging: { list: { entries: customAspectListMock } } })
|
||||||
|
);
|
||||||
spyOn(aspectListService, 'getVisibleAspects').and.returnValue(['frs:AspectOne']);
|
spyOn(aspectListService, 'getVisibleAspects').and.returnValue(['frs:AspectOne']);
|
||||||
spyOn(aspectListService, 'getCustomAspects').and.returnValue(of(customAspectListMock));
|
spyOn(aspectListService, 'getAspects').and.returnValue(of({ list: { entries: customAspectListMock } }));
|
||||||
spyOn(nodeService, 'getNode').and.returnValue(
|
spyOn(nodeService, 'getNode').and.returnValue(
|
||||||
of(new Node({ id: 'fake-node-id', aspectNames: ['frs:AspectOne', 'cst:customAspect'] })).pipe(delay(0))
|
of(new Node({ id: 'fake-node-id', aspectNames: ['frs:AspectOne', 'cst:customAspect'] })).pipe(delay(0))
|
||||||
);
|
);
|
||||||
|
@@ -19,15 +19,16 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|||||||
import { NodesApiService } from '../common/services/nodes-api.service';
|
import { NodesApiService } from '../common/services/nodes-api.service';
|
||||||
import { ContentTestingModule } from '../testing/content.testing.module';
|
import { ContentTestingModule } from '../testing/content.testing.module';
|
||||||
import { AspectListComponent } from './aspect-list.component';
|
import { AspectListComponent } from './aspect-list.component';
|
||||||
import { AspectListService } from './services/aspect-list.service';
|
import { AspectListService, CustomAspectsWhere, StandardAspectsWhere } from './services/aspect-list.service';
|
||||||
import { EMPTY, of } from 'rxjs';
|
import { EMPTY, of } from 'rxjs';
|
||||||
import { AspectEntry } from '@alfresco/js-api';
|
import { AspectEntry, Pagination } from '@alfresco/js-api';
|
||||||
import { HarnessLoader } from '@angular/cdk/testing';
|
import { HarnessLoader } from '@angular/cdk/testing';
|
||||||
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
||||||
import { MatExpansionPanelHarness } from '@angular/material/expansion/testing';
|
import { MatExpansionPanelHarness } from '@angular/material/expansion/testing';
|
||||||
import { MatTableHarness } from '@angular/material/table/testing';
|
import { MatTableHarness } from '@angular/material/table/testing';
|
||||||
import { MatCheckboxHarness } from '@angular/material/checkbox/testing';
|
import { MatCheckboxHarness } from '@angular/material/checkbox/testing';
|
||||||
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
|
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
|
||||||
|
import { CustomAspectPaging } from './interfaces/custom-aspect-paging.interface';
|
||||||
|
|
||||||
const aspectListMock: AspectEntry[] = [
|
const aspectListMock: AspectEntry[] = [
|
||||||
{
|
{
|
||||||
@@ -110,6 +111,11 @@ const customAspectListMock: AspectEntry[] = [
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const allAspectsMock: CustomAspectPaging = {
|
||||||
|
standardAspectPaging: { list: { entries: aspectListMock } },
|
||||||
|
customAspectPaging: { list: { entries: customAspectListMock } }
|
||||||
|
};
|
||||||
|
|
||||||
describe('AspectListComponent', () => {
|
describe('AspectListComponent', () => {
|
||||||
let loader: HarnessLoader;
|
let loader: HarnessLoader;
|
||||||
let component: AspectListComponent;
|
let component: AspectListComponent;
|
||||||
@@ -122,17 +128,15 @@ describe('AspectListComponent', () => {
|
|||||||
imports: [ContentTestingModule, AspectListComponent],
|
imports: [ContentTestingModule, AspectListComponent],
|
||||||
providers: [AspectListService]
|
providers: [AspectListService]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
fixture = TestBed.createComponent(AspectListComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
nodeService = TestBed.inject(NodesApiService);
|
||||||
|
aspectListService = TestBed.inject(AspectListService);
|
||||||
|
loader = TestbedHarnessEnvironment.loader(fixture);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Loading', () => {
|
describe('Loading', () => {
|
||||||
beforeEach(() => {
|
|
||||||
fixture = TestBed.createComponent(AspectListComponent);
|
|
||||||
component = fixture.componentInstance;
|
|
||||||
nodeService = TestBed.inject(NodesApiService);
|
|
||||||
aspectListService = TestBed.inject(AspectListService);
|
|
||||||
loader = TestbedHarnessEnvironment.loader(fixture);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should show the loading spinner when result is loading', async () => {
|
it('should show the loading spinner when result is loading', async () => {
|
||||||
spyOn(nodeService, 'getNode').and.returnValue(EMPTY);
|
spyOn(nodeService, 'getNode').and.returnValue(EMPTY);
|
||||||
spyOn(aspectListService, 'getAspects').and.returnValue(EMPTY);
|
spyOn(aspectListService, 'getAspects').and.returnValue(EMPTY);
|
||||||
@@ -144,16 +148,11 @@ describe('AspectListComponent', () => {
|
|||||||
|
|
||||||
describe('When passing a node id', () => {
|
describe('When passing a node id', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(AspectListComponent);
|
spyOn(aspectListService, 'getAllAspects').and.returnValue(of(allAspectsMock));
|
||||||
component = fixture.componentInstance;
|
spyOn(aspectListService, 'getAspects').and.returnValue(of({ list: { entries: customAspectListMock } }));
|
||||||
aspectListService = TestBed.inject(AspectListService);
|
|
||||||
spyOn(aspectListService, 'getAspects').and.returnValue(of([...aspectListMock, ...customAspectListMock]));
|
|
||||||
spyOn(aspectListService, 'getCustomAspects').and.returnValue(of(customAspectListMock));
|
|
||||||
spyOn(aspectListService, 'getVisibleAspects').and.returnValue(['frs:AspectOne']);
|
spyOn(aspectListService, 'getVisibleAspects').and.returnValue(['frs:AspectOne']);
|
||||||
nodeService = TestBed.inject(NodesApiService);
|
|
||||||
spyOn(nodeService, 'getNode').and.returnValue(of({ id: 'fake-node-id', aspectNames: ['frs:AspectOne', 'stored:aspect'] } as any));
|
spyOn(nodeService, 'getNode').and.returnValue(of({ id: 'fake-node-id', aspectNames: ['frs:AspectOne', 'stored:aspect'] } as any));
|
||||||
component.nodeId = 'fake-node-id';
|
component.nodeId = 'fake-node-id';
|
||||||
loader = TestbedHarnessEnvironment.loader(fixture);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -251,6 +250,13 @@ describe('AspectListComponent', () => {
|
|||||||
expect(component.valueChanged.emit).toHaveBeenCalledWith(['frs:AspectOne', 'frs:SecondAspect', ...storedAspect]);
|
expect(component.valueChanged.emit).toHaveBeenCalledWith(['frs:AspectOne', 'frs:SecondAspect', ...storedAspect]);
|
||||||
expect(component.updateCounter.emit).toHaveBeenCalledWith(2);
|
expect(component.updateCounter.emit).toHaveBeenCalledWith(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should load aspects with 0 skip count as pagination by default', () => {
|
||||||
|
expect(aspectListService.getAllAspects).toHaveBeenCalledWith(
|
||||||
|
{ where: StandardAspectsWhere, include: ['properties'], skipCount: 0, maxItems: 100 },
|
||||||
|
{ where: CustomAspectsWhere, include: ['properties'], skipCount: 0, maxItems: 100 }
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('with excluded aspects', () => {
|
describe('with excluded aspects', () => {
|
||||||
@@ -265,11 +271,7 @@ describe('AspectListComponent', () => {
|
|||||||
|
|
||||||
describe('When no node id is passed', () => {
|
describe('When no node id is passed', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(AspectListComponent);
|
spyOn(aspectListService, 'getAllAspects').and.returnValue(of(allAspectsMock));
|
||||||
component = fixture.componentInstance;
|
|
||||||
aspectListService = TestBed.inject(AspectListService);
|
|
||||||
spyOn(aspectListService, 'getAspects').and.returnValue(of(aspectListMock));
|
|
||||||
loader = TestbedHarnessEnvironment.loader(fixture);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
@@ -289,5 +291,38 @@ describe('AspectListComponent', () => {
|
|||||||
expect(await loader.hasHarness(MatExpansionPanelHarness.with({ selector: '#aspect-list-FirstAspect' }))).toBeFalse();
|
expect(await loader.hasHarness(MatExpansionPanelHarness.with({ selector: '#aspect-list-FirstAspect' }))).toBeFalse();
|
||||||
expect(await loader.hasHarness(MatExpansionPanelHarness.with({ selector: '#aspect-list-SecondAspect' }))).toBeFalse();
|
expect(await loader.hasHarness(MatExpansionPanelHarness.with({ selector: '#aspect-list-SecondAspect' }))).toBeFalse();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should load aspects with 0 skip count as pagination by default', () => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(aspectListService.getAllAspects).toHaveBeenCalledWith(
|
||||||
|
{ where: StandardAspectsWhere, include: ['properties'], skipCount: 0, maxItems: 100 },
|
||||||
|
{ where: CustomAspectsWhere, include: ['properties'], skipCount: 0, maxItems: 100 }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load next batch of aspects if not all items were returned by first call', (done) => {
|
||||||
|
fixture.detectChanges();
|
||||||
|
const moreItemsPagination: Pagination = { count: 2, hasMoreItems: true };
|
||||||
|
const allAspectsWithMoreItems: CustomAspectPaging = {
|
||||||
|
standardAspectPaging: { list: { entries: aspectListMock, pagination: moreItemsPagination } },
|
||||||
|
customAspectPaging: { list: { entries: customAspectListMock, pagination: moreItemsPagination } }
|
||||||
|
};
|
||||||
|
const getAspectsSpy = spyOn(aspectListService, 'getAllAspects').and.returnValues(of(allAspectsWithMoreItems), of(allAspectsMock));
|
||||||
|
spyOn(aspectListService, 'getAspects').and.returnValues(
|
||||||
|
of({ list: { entries: aspectListMock } }),
|
||||||
|
of({ list: { entries: customAspectListMock } })
|
||||||
|
);
|
||||||
|
|
||||||
|
component.aspects$.subscribe(() => {
|
||||||
|
expect(getAspectsSpy.calls.argsFor(0)[0]).toEqual({ where: StandardAspectsWhere, include: ['properties'], skipCount: 0, maxItems: 100 });
|
||||||
|
expect(getAspectsSpy.calls.argsFor(0)[1]).toEqual({ where: CustomAspectsWhere, include: ['properties'], skipCount: 0, maxItems: 100 });
|
||||||
|
expect(getAspectsSpy.calls.argsFor(1)[0]).toEqual({ where: StandardAspectsWhere, include: ['properties'], skipCount: 2, maxItems: 100 });
|
||||||
|
expect(getAspectsSpy.calls.argsFor(1)[1]).toEqual({ where: CustomAspectsWhere, include: ['properties'], skipCount: 2, maxItems: 100 });
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
component.ngOnInit();
|
||||||
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -17,11 +17,11 @@
|
|||||||
|
|
||||||
import { Component, DestroyRef, EventEmitter, inject, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';
|
import { Component, DestroyRef, EventEmitter, inject, Input, OnInit, Output, ViewEncapsulation } from '@angular/core';
|
||||||
import { NodesApiService } from '../common/services/nodes-api.service';
|
import { NodesApiService } from '../common/services/nodes-api.service';
|
||||||
import { Observable, zip } from 'rxjs';
|
import { EMPTY, Observable, zip } from 'rxjs';
|
||||||
import { concatMap, map, tap } from 'rxjs/operators';
|
import { concatMap, expand, map, reduce, take, tap } from 'rxjs/operators';
|
||||||
import { AspectListService } from './services/aspect-list.service';
|
import { AspectListService, CustomAspectsWhere, StandardAspectsWhere } from './services/aspect-list.service';
|
||||||
import { MatCheckboxChange, MatCheckboxModule } from '@angular/material/checkbox';
|
import { MatCheckboxChange, MatCheckboxModule } from '@angular/material/checkbox';
|
||||||
import { AspectEntry } from '@alfresco/js-api';
|
import { AspectEntry, ContentPagingQuery, ListAspectsOpts } from '@alfresco/js-api';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { MatExpansionModule } from '@angular/material/expansion';
|
import { MatExpansionModule } from '@angular/material/expansion';
|
||||||
import { MatTableModule } from '@angular/material/table';
|
import { MatTableModule } from '@angular/material/table';
|
||||||
@@ -63,6 +63,10 @@ export class AspectListComponent implements OnInit {
|
|||||||
|
|
||||||
private readonly destroyRef = inject(DestroyRef);
|
private readonly destroyRef = inject(DestroyRef);
|
||||||
|
|
||||||
|
private customAspectsLoaded = 0;
|
||||||
|
private standardAspectsLoaded = 0;
|
||||||
|
private hasMoreAspects = false;
|
||||||
|
|
||||||
constructor(private aspectListService: AspectListService, private nodeApiService: NodesApiService) {}
|
constructor(private aspectListService: AspectListService, private nodeApiService: NodesApiService) {}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
@@ -70,8 +74,13 @@ export class AspectListComponent implements OnInit {
|
|||||||
if (this.nodeId) {
|
if (this.nodeId) {
|
||||||
const node$ = this.nodeApiService.getNode(this.nodeId);
|
const node$ = this.nodeApiService.getNode(this.nodeId);
|
||||||
const customAspect$ = this.aspectListService
|
const customAspect$ = this.aspectListService
|
||||||
.getCustomAspects(this.aspectListService.getVisibleAspects())
|
.getAspects(this.aspectListService.getVisibleAspects(), {
|
||||||
.pipe(map((customAspects) => customAspects.flatMap((customAspect) => customAspect.entry.id)));
|
where: CustomAspectsWhere,
|
||||||
|
include: ['properties'],
|
||||||
|
skipCount: 0,
|
||||||
|
maxItems: 100
|
||||||
|
})
|
||||||
|
.pipe(map((customAspects) => customAspects?.list?.entries.flatMap((customAspect) => customAspect.entry.id)));
|
||||||
aspects$ = zip(node$, customAspect$).pipe(
|
aspects$ = zip(node$, customAspect$).pipe(
|
||||||
tap(([node, customAspects]) => {
|
tap(([node, customAspects]) => {
|
||||||
this.nodeAspects = node.aspectNames.filter(
|
this.nodeAspects = node.aspectNames.filter(
|
||||||
@@ -84,13 +93,19 @@ export class AspectListComponent implements OnInit {
|
|||||||
this.valueChanged.emit([...this.nodeAspects, ...this.notDisplayedAspects]);
|
this.valueChanged.emit([...this.nodeAspects, ...this.notDisplayedAspects]);
|
||||||
this.updateCounter.emit(this.nodeAspects.length);
|
this.updateCounter.emit(this.nodeAspects.length);
|
||||||
}),
|
}),
|
||||||
concatMap(() => this.aspectListService.getAspects()),
|
concatMap(() => this.loadAspects({ skipCount: this.standardAspectsLoaded }, { skipCount: this.customAspectsLoaded })),
|
||||||
takeUntilDestroyed(this.destroyRef)
|
takeUntilDestroyed(this.destroyRef)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
aspects$ = this.aspectListService.getAspects().pipe(takeUntilDestroyed(this.destroyRef));
|
aspects$ = this.loadAspects({ skipCount: this.standardAspectsLoaded }, { skipCount: this.customAspectsLoaded });
|
||||||
}
|
}
|
||||||
this.aspects$ = aspects$.pipe(map((aspects) => aspects.filter((aspect) => !this.excludedAspects.includes(aspect.entry.id))));
|
this.aspects$ = aspects$.pipe(
|
||||||
|
expand(() =>
|
||||||
|
this.hasMoreAspects ? this.loadAspects({ skipCount: this.standardAspectsLoaded }, { skipCount: this.customAspectsLoaded }) : EMPTY
|
||||||
|
),
|
||||||
|
map((aspects) => aspects.filter((aspect) => !this.excludedAspects.includes(aspect.entry.id))),
|
||||||
|
reduce((acc, aspects) => [...acc, ...aspects])
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onCheckBoxClick(event: Event) {
|
onCheckBoxClick(event: Event) {
|
||||||
@@ -141,4 +156,33 @@ export class AspectListComponent implements OnInit {
|
|||||||
this.hasEqualAspect = this.nodeAspects.every((aspect) => this.nodeAspectStatus.includes(aspect));
|
this.hasEqualAspect = this.nodeAspects.every((aspect) => this.nodeAspectStatus.includes(aspect));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private loadAspects(standardAspectsPagination?: ContentPagingQuery, customAspectsPagination?: ContentPagingQuery): Observable<AspectEntry[]> {
|
||||||
|
const standardAspectOpts: ListAspectsOpts = {
|
||||||
|
where: StandardAspectsWhere,
|
||||||
|
include: ['properties'],
|
||||||
|
skipCount: standardAspectsPagination?.skipCount ?? 0,
|
||||||
|
maxItems: 100
|
||||||
|
};
|
||||||
|
const customAspectOpts: ListAspectsOpts = {
|
||||||
|
where: CustomAspectsWhere,
|
||||||
|
include: ['properties'],
|
||||||
|
skipCount: customAspectsPagination?.skipCount ?? 0,
|
||||||
|
maxItems: 100
|
||||||
|
};
|
||||||
|
return this.aspectListService.getAllAspects(standardAspectOpts, customAspectOpts).pipe(
|
||||||
|
take(1),
|
||||||
|
tap((aspectsPaging) => {
|
||||||
|
this.customAspectsLoaded += aspectsPaging.customAspectPaging?.list?.pagination?.count ?? 0;
|
||||||
|
this.standardAspectsLoaded += aspectsPaging.standardAspectPaging?.list?.pagination?.count ?? 0;
|
||||||
|
this.hasMoreAspects =
|
||||||
|
aspectsPaging.customAspectPaging?.list?.pagination?.hasMoreItems ||
|
||||||
|
aspectsPaging.standardAspectPaging?.list?.pagination?.hasMoreItems;
|
||||||
|
}),
|
||||||
|
map((aspectsPaging) => [
|
||||||
|
...(aspectsPaging.standardAspectPaging?.list?.entries ?? []),
|
||||||
|
...(aspectsPaging.customAspectPaging?.list?.entries ?? [])
|
||||||
|
])
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,23 @@
|
|||||||
|
/*!
|
||||||
|
* @license
|
||||||
|
* Copyright © 2005-2025 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 { AspectPaging } from '@alfresco/js-api';
|
||||||
|
|
||||||
|
export interface CustomAspectPaging {
|
||||||
|
standardAspectPaging: AspectPaging;
|
||||||
|
customAspectPaging: AspectPaging;
|
||||||
|
}
|
@@ -22,5 +22,6 @@ export * from './services/node-aspect.service';
|
|||||||
export * from './services/dialog-aspect-list.service';
|
export * from './services/dialog-aspect-list.service';
|
||||||
|
|
||||||
export * from './aspect-list-dialog-data.interface';
|
export * from './aspect-list-dialog-data.interface';
|
||||||
|
export * from './interfaces/custom-aspect-paging.interface';
|
||||||
|
|
||||||
export * from './aspect-list.module';
|
export * from './aspect-list.module';
|
||||||
|
@@ -16,96 +16,172 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
import { AspectListService } from './aspect-list.service';
|
import { AspectListService, CustomAspectsWhere, StandardAspectsWhere } from './aspect-list.service';
|
||||||
import { AspectPaging, AspectsApi, AspectEntry } from '@alfresco/js-api';
|
import { AspectPaging, AspectsApi, AspectEntry, ListAspectsOpts } from '@alfresco/js-api';
|
||||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
import { provideHttpClientTesting } from '@angular/common/http/testing';
|
||||||
import { AlfrescoApiService } from '../../services';
|
import { AlfrescoApiService } from '../../services';
|
||||||
|
import { provideHttpClient } from '@angular/common/http';
|
||||||
|
import { AppConfigService } from '@alfresco/adf-core';
|
||||||
|
|
||||||
const stdAspect1: AspectEntry = { entry: { id: 'std:standardAspectOne', description: 'Standard Aspect One', title: 'StandardAspectOne' } };
|
const stdAspect1: AspectEntry = { entry: { id: 'std:standardAspectOne', description: 'Standard Aspect One', title: 'StandardAspectOne' } };
|
||||||
const stdAspect2: AspectEntry = { entry: { id: 'std:standardAspectTwo', description: 'Standard Aspect Two', title: 'StandardAspectTwo' } };
|
const stdAspect2: AspectEntry = { entry: { id: 'std:standardAspectTwo', description: 'Standard Aspect Two', title: 'StandardAspectTwo' } };
|
||||||
const stdAspect3: AspectEntry = { entry: { id: 'std:standardAspectThree', description: 'Standard Aspect Three', title: 'StandardAspectThree' } };
|
const stdAspect3: AspectEntry = { entry: { id: 'std:standardAspectThree', description: 'Standard Aspect Three', title: 'StandardAspectThree' } };
|
||||||
const standardAspectPagingMock: AspectPaging = { list: { entries: [stdAspect1, stdAspect2, stdAspect3] } };
|
|
||||||
|
|
||||||
const cstAspect1: AspectEntry = { entry: { id: 'cst:customAspectOne', description: 'Custom Aspect One', title: 'CustomAspectOne' } };
|
const cstAspect1: AspectEntry = { entry: { id: 'cst:customAspectOne', description: 'Custom Aspect One', title: 'CustomAspectOne' } };
|
||||||
const cstAspect2: AspectEntry = { entry: { id: 'cst:customAspectTwo', description: 'Custom Aspect Two', title: 'CustomAspectTwo' } };
|
const cstAspect2: AspectEntry = { entry: { id: 'cst:customAspectTwo', description: 'Custom Aspect Two', title: 'CustomAspectTwo' } };
|
||||||
const cstAspect3: AspectEntry = { entry: { id: 'cst:customAspectThree', description: 'Custom Aspect Three', title: 'CustomAspectThree' } };
|
const cstAspect3: AspectEntry = { entry: { id: 'cst:customAspectThree', description: 'Custom Aspect Three', title: 'CustomAspectThree' } };
|
||||||
const customAspectPagingMock: AspectPaging = { list: { entries: [cstAspect1, cstAspect2, cstAspect3] } };
|
|
||||||
|
const aspectsOpts: ListAspectsOpts = {
|
||||||
|
where: StandardAspectsWhere,
|
||||||
|
include: ['properties'],
|
||||||
|
skipCount: 0,
|
||||||
|
maxItems: 100
|
||||||
|
};
|
||||||
|
|
||||||
|
const customAspectsOpts: ListAspectsOpts = {
|
||||||
|
where: CustomAspectsWhere,
|
||||||
|
include: ['properties'],
|
||||||
|
skipCount: 0,
|
||||||
|
maxItems: 100
|
||||||
|
};
|
||||||
|
|
||||||
|
let standardAspectPagingMock: AspectPaging;
|
||||||
|
let customAspectPagingMock: AspectPaging;
|
||||||
|
|
||||||
describe('AspectListService', () => {
|
describe('AspectListService', () => {
|
||||||
let aspectListService: AspectListService;
|
let aspectListService: AspectListService;
|
||||||
let apiService: AlfrescoApiService;
|
let apiService: AlfrescoApiService;
|
||||||
let aspectsApi: AspectsApi;
|
let aspectsApi: AspectsApi;
|
||||||
|
let appConfigService: AppConfigService;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [HttpClientTestingModule]
|
providers: [provideHttpClient(), provideHttpClientTesting()]
|
||||||
});
|
});
|
||||||
|
|
||||||
aspectListService = TestBed.inject(AspectListService);
|
aspectListService = TestBed.inject(AspectListService);
|
||||||
|
appConfigService = TestBed.inject(AppConfigService);
|
||||||
apiService = TestBed.inject(AlfrescoApiService);
|
apiService = TestBed.inject(AlfrescoApiService);
|
||||||
aspectsApi = new AspectsApi(apiService.getInstance());
|
aspectsApi = new AspectsApi(apiService.getInstance());
|
||||||
spyOnProperty(aspectListService, 'aspectsApi', 'get').and.returnValue(aspectsApi);
|
spyOnProperty(aspectListService, 'aspectsApi', 'get').and.returnValue(aspectsApi);
|
||||||
|
standardAspectPagingMock = { list: { entries: [stdAspect1, stdAspect2, stdAspect3] } };
|
||||||
|
customAspectPagingMock = { list: { entries: [cstAspect1, cstAspect2, cstAspect3] } };
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get one standard aspect', (done) => {
|
describe('When API returns error', () => {
|
||||||
const visibleAspects = ['std:standardAspectOne'];
|
const visibleAspects: string[] = [];
|
||||||
spyOn(aspectsApi, 'listAspects').and.returnValue(Promise.resolve(standardAspectPagingMock));
|
|
||||||
aspectListService.getStandardAspects(visibleAspects).subscribe((response) => {
|
beforeEach(() => {
|
||||||
expect(response).toEqual([stdAspect1]);
|
spyOn(aspectsApi, 'listAspects').and.returnValue(Promise.reject(new Error('API error')));
|
||||||
done();
|
});
|
||||||
|
|
||||||
|
it('should return empty paging list for standard aspects when api returns error', (done) => {
|
||||||
|
aspectListService.getAspects(visibleAspects, aspectsOpts).subscribe((response) => {
|
||||||
|
expect(response.list.entries).toEqual([]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return empty paging list for custom aspects when api returns error', (done) => {
|
||||||
|
aspectListService.getAspects(visibleAspects, aspectsOpts).subscribe((response) => {
|
||||||
|
expect(response.list.entries).toEqual([]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get two standard aspects', (done) => {
|
describe('When aspects are returned', () => {
|
||||||
const visibleAspects = ['std:standardAspectTwo', 'std:standardAspectThree'];
|
beforeEach(() => {
|
||||||
spyOn(aspectsApi, 'listAspects').and.returnValue(Promise.resolve(standardAspectPagingMock));
|
spyOn(aspectsApi, 'listAspects').and.returnValue(Promise.resolve(standardAspectPagingMock));
|
||||||
aspectListService.getStandardAspects(visibleAspects).subscribe((response) => {
|
});
|
||||||
expect(response).toEqual([stdAspect2, stdAspect3]);
|
|
||||||
done();
|
it('should add custom pagination for aspects list request when provided', (done) => {
|
||||||
|
const visibleAspects: string[] = [];
|
||||||
|
const customPagination: ListAspectsOpts = { skipCount: 10, maxItems: 20 };
|
||||||
|
aspectListService.getAspects(visibleAspects, customPagination).subscribe(() => {
|
||||||
|
expect(aspectsApi.listAspects).toHaveBeenCalledWith(customPagination);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get one aspect', (done) => {
|
||||||
|
const visibleAspects = ['std:standardAspectOne'];
|
||||||
|
aspectListService.getAspects(visibleAspects, aspectsOpts).subscribe((response) => {
|
||||||
|
expect(response.list.entries).toEqual([stdAspect1]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get two aspects', (done) => {
|
||||||
|
const visibleAspects = ['std:standardAspectTwo', 'std:standardAspectThree'];
|
||||||
|
aspectListService.getAspects(visibleAspects, aspectsOpts).subscribe((response) => {
|
||||||
|
expect(response.list.entries).toEqual([stdAspect2, stdAspect3]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get all aspects (visible aspects as undefined)', (done) => {
|
||||||
|
const visibleAspects: string[] = undefined;
|
||||||
|
aspectListService.getAspects(visibleAspects, aspectsOpts).subscribe((response) => {
|
||||||
|
expect(response.list.entries).toEqual([stdAspect1, stdAspect2, stdAspect3]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get all aspects (visible aspects as empty array)', (done) => {
|
||||||
|
const visibleAspects: string[] = [];
|
||||||
|
aspectListService.getAspects(visibleAspects, aspectsOpts).subscribe((response) => {
|
||||||
|
expect(response.list.entries).toEqual([stdAspect1, stdAspect2, stdAspect3]);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get one custom aspect', (done) => {
|
describe('getAllAspects', () => {
|
||||||
const visibleAspects = ['cst:customAspectTwo'];
|
beforeEach(() => {
|
||||||
spyOn(aspectsApi, 'listAspects').and.returnValue(Promise.resolve(customAspectPagingMock));
|
spyOn(aspectsApi, 'listAspects').and.returnValues(Promise.resolve(standardAspectPagingMock), Promise.resolve(customAspectPagingMock));
|
||||||
aspectListService.getCustomAspects(visibleAspects).subscribe((response) => {
|
});
|
||||||
expect(response).toEqual([cstAspect2]);
|
|
||||||
done();
|
it('should get all aspects (standard and custom) when visible aspects are empty', (done) => {
|
||||||
|
spyOn(appConfigService, 'get').and.returnValue(undefined);
|
||||||
|
aspectListService.getAllAspects(aspectsOpts, customAspectsOpts).subscribe((aspects) => {
|
||||||
|
expect(aspects.standardAspectPaging.list.entries).toEqual([stdAspect1, stdAspect2, stdAspect3]);
|
||||||
|
expect(aspects.customAspectPaging.list.entries).toEqual([cstAspect1, cstAspect2, cstAspect3]);
|
||||||
|
expect(aspectsApi.listAspects).toHaveBeenCalledTimes(2);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should get all aspects (standard and custom) filtered by visible aspects', (done) => {
|
||||||
|
const visibleAspectConfig = {
|
||||||
|
standard: ['std:standardAspectOne', 'std:standardAspectTwo'],
|
||||||
|
custom: ['cst:customAspectOne']
|
||||||
|
};
|
||||||
|
spyOn(appConfigService, 'get').and.returnValue(visibleAspectConfig);
|
||||||
|
aspectListService.getAllAspects(aspectsOpts, customAspectsOpts).subscribe((aspects) => {
|
||||||
|
expect(aspects.standardAspectPaging.list.entries).toEqual([stdAspect1, stdAspect2]);
|
||||||
|
expect(aspects.customAspectPaging.list.entries).toEqual([cstAspect1]);
|
||||||
|
expect(aspectsApi.listAspects).toHaveBeenCalledTimes(2);
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should get two custom aspects', (done) => {
|
describe('getVisibleAspects', () => {
|
||||||
const visibleAspects = ['cst:customAspectOne', 'cst:customAspectThree'];
|
it('should return empty array when visible aspects are not provided in the config', () => {
|
||||||
spyOn(aspectsApi, 'listAspects').and.returnValue(Promise.resolve(customAspectPagingMock));
|
spyOn(appConfigService, 'get').and.returnValue(undefined);
|
||||||
aspectListService.getCustomAspects(visibleAspects).subscribe((response) => {
|
const visibleAspects = aspectListService.getVisibleAspects();
|
||||||
expect(response).toEqual([cstAspect1, cstAspect3]);
|
expect(visibleAspects).toEqual([]);
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
it('should get all custom aspects (visible aspects as undefined)', (done) => {
|
it('should return visible aspects from config when provided', () => {
|
||||||
const visibleAspects = undefined;
|
const visibleAspectConfig = {
|
||||||
spyOn(aspectsApi, 'listAspects').and.returnValue(Promise.resolve(customAspectPagingMock));
|
standard: ['std:standardAspectOne', 'std:standardAspectTwo'],
|
||||||
aspectListService.getCustomAspects(visibleAspects).subscribe((response) => {
|
custom: ['cst:customAspectOne']
|
||||||
expect(response).toEqual([cstAspect1, cstAspect2, cstAspect3]);
|
};
|
||||||
done();
|
spyOn(appConfigService, 'get').and.returnValue(visibleAspectConfig);
|
||||||
});
|
const visibleAspects = aspectListService.getVisibleAspects();
|
||||||
});
|
expect(visibleAspects).toEqual(['std:standardAspectOne', 'std:standardAspectTwo', 'cst:customAspectOne']);
|
||||||
|
|
||||||
it('should get all custom aspects (visible aspects as empty array)', (done) => {
|
|
||||||
const visibleAspects = [];
|
|
||||||
spyOn(aspectsApi, 'listAspects').and.returnValue(Promise.resolve(customAspectPagingMock));
|
|
||||||
aspectListService.getCustomAspects(visibleAspects).subscribe((response) => {
|
|
||||||
expect(response).toEqual([cstAspect1, cstAspect2, cstAspect3]);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should get all custom aspects (visible aspects not supplied)', (done) => {
|
|
||||||
spyOn(aspectsApi, 'listAspects').and.returnValue(Promise.resolve(customAspectPagingMock));
|
|
||||||
aspectListService.getCustomAspects().subscribe((response) => {
|
|
||||||
expect(response).toEqual([cstAspect1, cstAspect2, cstAspect3]);
|
|
||||||
done();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@@ -20,7 +20,11 @@ import { AlfrescoApiService } from '../../services/alfresco-api.service';
|
|||||||
import { AppConfigService } from '@alfresco/adf-core';
|
import { AppConfigService } from '@alfresco/adf-core';
|
||||||
import { from, Observable, of, zip } from 'rxjs';
|
import { from, Observable, of, zip } from 'rxjs';
|
||||||
import { catchError, map } from 'rxjs/operators';
|
import { catchError, map } from 'rxjs/operators';
|
||||||
import { AspectEntry, AspectPaging, AspectsApi } from '@alfresco/js-api';
|
import { AspectEntry, AspectPaging, AspectsApi, ListAspectsOpts } from '@alfresco/js-api';
|
||||||
|
import { CustomAspectPaging } from '../interfaces/custom-aspect-paging.interface';
|
||||||
|
|
||||||
|
export const StandardAspectsWhere = `(modelId in ('cm:contentmodel', 'emailserver:emailserverModel', 'smf:smartFolder', 'app:applicationmodel' ))`;
|
||||||
|
export const CustomAspectsWhere = `(not namespaceUri matches('http://www.alfresco.*'))`;
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@@ -34,37 +38,24 @@ export class AspectListService {
|
|||||||
|
|
||||||
constructor(private alfrescoApiService: AlfrescoApiService, private appConfigService: AppConfigService) {}
|
constructor(private alfrescoApiService: AlfrescoApiService, private appConfigService: AppConfigService) {}
|
||||||
|
|
||||||
getAspects(): Observable<AspectEntry[]> {
|
getAllAspects(standardOpts?: ListAspectsOpts, customOpts?: ListAspectsOpts): Observable<CustomAspectPaging> {
|
||||||
const visibleAspectList = this.getVisibleAspects();
|
const visibleAspectList = this.getVisibleAspects();
|
||||||
const standardAspects$ = this.getStandardAspects(visibleAspectList);
|
const standardAspects$ = this.getAspects(visibleAspectList, standardOpts);
|
||||||
const customAspects$ = this.getCustomAspects(visibleAspectList);
|
const customAspects$ = this.getAspects(visibleAspectList, customOpts);
|
||||||
return zip(standardAspects$, customAspects$).pipe(
|
return zip(standardAspects$, customAspects$).pipe(
|
||||||
map(([standardAspectList, customAspectList]) => [...standardAspectList, ...customAspectList])
|
map(([standardAspectPaging, customAspectPaging]) => ({ standardAspectPaging, customAspectPaging }))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getStandardAspects(whiteList: string[]): Observable<AspectEntry[]> {
|
getAspects(whiteList: string[], opts?: ListAspectsOpts): Observable<AspectPaging> {
|
||||||
const where = `(modelId in ('cm:contentmodel', 'emailserver:emailserverModel', 'smf:smartFolder', 'app:applicationmodel' ))`;
|
|
||||||
const opts: any = {
|
|
||||||
where,
|
|
||||||
include: ['properties']
|
|
||||||
};
|
|
||||||
|
|
||||||
return from(this.aspectsApi.listAspects(opts)).pipe(
|
return from(this.aspectsApi.listAspects(opts)).pipe(
|
||||||
map((result: AspectPaging) => this.filterAspectByConfig(whiteList, result?.list?.entries)),
|
map((result) => {
|
||||||
catchError(() => of([]))
|
if (result?.list?.entries) {
|
||||||
);
|
result.list.entries = this.filterAspectByConfig(whiteList, result.list.entries);
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
getCustomAspects(whiteList?: string[]): Observable<AspectEntry[]> {
|
}),
|
||||||
const where = `(not namespaceUri matches('http://www.alfresco.*'))`;
|
catchError(() => of({ list: { entries: [] } }))
|
||||||
const opts: any = {
|
|
||||||
where,
|
|
||||||
include: ['properties']
|
|
||||||
};
|
|
||||||
return from(this.aspectsApi.listAspects(opts)).pipe(
|
|
||||||
map((result: AspectPaging) => this.filterAspectByConfig(whiteList, result?.list?.entries)),
|
|
||||||
catchError(() => of([]))
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user