diff --git a/lib/content-services/src/lib/aspect-list/aspect-list-dialog.component.spec.ts b/lib/content-services/src/lib/aspect-list/aspect-list-dialog.component.spec.ts index dc816e9793..abb3b9ad0c 100644 --- a/lib/content-services/src/lib/aspect-list/aspect-list-dialog.component.spec.ts +++ b/lib/content-services/src/lib/aspect-list/aspect-list-dialog.component.spec.ts @@ -132,7 +132,9 @@ describe('AspectListDialogComponent', () => { describe('Without passing node id', () => { beforeEach(() => { 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(); }); @@ -247,9 +249,11 @@ describe('AspectListDialogComponent', () => { data.nodeId = 'fake-node-id'; aspectListService = TestBed.inject(AspectListService); 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, 'getCustomAspects').and.returnValue(of(customAspectListMock)); + spyOn(aspectListService, 'getAspects').and.returnValue(of({ list: { entries: customAspectListMock } })); spyOn(nodeService, 'getNode').and.returnValue( of(new Node({ id: 'fake-node-id', aspectNames: ['frs:AspectOne', 'cst:customAspect'] })).pipe(delay(0)) ); diff --git a/lib/content-services/src/lib/aspect-list/aspect-list.component.spec.ts b/lib/content-services/src/lib/aspect-list/aspect-list.component.spec.ts index fed44b4aa5..fff47994fb 100644 --- a/lib/content-services/src/lib/aspect-list/aspect-list.component.spec.ts +++ b/lib/content-services/src/lib/aspect-list/aspect-list.component.spec.ts @@ -19,15 +19,16 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { NodesApiService } from '../common/services/nodes-api.service'; import { ContentTestingModule } from '../testing/content.testing.module'; 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 { AspectEntry } from '@alfresco/js-api'; +import { AspectEntry, Pagination } from '@alfresco/js-api'; import { HarnessLoader } from '@angular/cdk/testing'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; import { MatExpansionPanelHarness } from '@angular/material/expansion/testing'; import { MatTableHarness } from '@angular/material/table/testing'; import { MatCheckboxHarness } from '@angular/material/checkbox/testing'; import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing'; +import { CustomAspectPaging } from './interfaces/custom-aspect-paging.interface'; const aspectListMock: AspectEntry[] = [ { @@ -110,6 +111,11 @@ const customAspectListMock: AspectEntry[] = [ } ]; +const allAspectsMock: CustomAspectPaging = { + standardAspectPaging: { list: { entries: aspectListMock } }, + customAspectPaging: { list: { entries: customAspectListMock } } +}; + describe('AspectListComponent', () => { let loader: HarnessLoader; let component: AspectListComponent; @@ -122,17 +128,15 @@ describe('AspectListComponent', () => { imports: [ContentTestingModule, AspectListComponent], providers: [AspectListService] }); + + fixture = TestBed.createComponent(AspectListComponent); + component = fixture.componentInstance; + nodeService = TestBed.inject(NodesApiService); + aspectListService = TestBed.inject(AspectListService); + loader = TestbedHarnessEnvironment.loader(fixture); }); 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 () => { spyOn(nodeService, 'getNode').and.returnValue(EMPTY); spyOn(aspectListService, 'getAspects').and.returnValue(EMPTY); @@ -144,16 +148,11 @@ describe('AspectListComponent', () => { describe('When passing a node id', () => { beforeEach(() => { - fixture = TestBed.createComponent(AspectListComponent); - component = fixture.componentInstance; - aspectListService = TestBed.inject(AspectListService); - spyOn(aspectListService, 'getAspects').and.returnValue(of([...aspectListMock, ...customAspectListMock])); - spyOn(aspectListService, 'getCustomAspects').and.returnValue(of(customAspectListMock)); + spyOn(aspectListService, 'getAllAspects').and.returnValue(of(allAspectsMock)); + spyOn(aspectListService, 'getAspects').and.returnValue(of({ list: { entries: customAspectListMock } })); 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)); component.nodeId = 'fake-node-id'; - loader = TestbedHarnessEnvironment.loader(fixture); }); afterEach(() => { @@ -251,6 +250,13 @@ describe('AspectListComponent', () => { expect(component.valueChanged.emit).toHaveBeenCalledWith(['frs:AspectOne', 'frs:SecondAspect', ...storedAspect]); 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', () => { @@ -265,11 +271,7 @@ describe('AspectListComponent', () => { describe('When no node id is passed', () => { beforeEach(() => { - fixture = TestBed.createComponent(AspectListComponent); - component = fixture.componentInstance; - aspectListService = TestBed.inject(AspectListService); - spyOn(aspectListService, 'getAspects').and.returnValue(of(aspectListMock)); - loader = TestbedHarnessEnvironment.loader(fixture); + spyOn(aspectListService, 'getAllAspects').and.returnValue(of(allAspectsMock)); }); 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-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(); }); }); diff --git a/lib/content-services/src/lib/aspect-list/aspect-list.component.ts b/lib/content-services/src/lib/aspect-list/aspect-list.component.ts index 3aa9b2af03..da7b56e2a0 100644 --- a/lib/content-services/src/lib/aspect-list/aspect-list.component.ts +++ b/lib/content-services/src/lib/aspect-list/aspect-list.component.ts @@ -17,11 +17,11 @@ import { Component, DestroyRef, EventEmitter, inject, Input, OnInit, Output, ViewEncapsulation } from '@angular/core'; import { NodesApiService } from '../common/services/nodes-api.service'; -import { Observable, zip } from 'rxjs'; -import { concatMap, map, tap } from 'rxjs/operators'; -import { AspectListService } from './services/aspect-list.service'; +import { EMPTY, Observable, zip } from 'rxjs'; +import { concatMap, expand, map, reduce, take, tap } from 'rxjs/operators'; +import { AspectListService, CustomAspectsWhere, StandardAspectsWhere } from './services/aspect-list.service'; 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 { MatExpansionModule } from '@angular/material/expansion'; import { MatTableModule } from '@angular/material/table'; @@ -63,6 +63,10 @@ export class AspectListComponent implements OnInit { private readonly destroyRef = inject(DestroyRef); + private customAspectsLoaded = 0; + private standardAspectsLoaded = 0; + private hasMoreAspects = false; + constructor(private aspectListService: AspectListService, private nodeApiService: NodesApiService) {} ngOnInit(): void { @@ -70,8 +74,13 @@ export class AspectListComponent implements OnInit { if (this.nodeId) { const node$ = this.nodeApiService.getNode(this.nodeId); const customAspect$ = this.aspectListService - .getCustomAspects(this.aspectListService.getVisibleAspects()) - .pipe(map((customAspects) => customAspects.flatMap((customAspect) => customAspect.entry.id))); + .getAspects(this.aspectListService.getVisibleAspects(), { + where: CustomAspectsWhere, + include: ['properties'], + skipCount: 0, + maxItems: 100 + }) + .pipe(map((customAspects) => customAspects?.list?.entries.flatMap((customAspect) => customAspect.entry.id))); aspects$ = zip(node$, customAspect$).pipe( tap(([node, customAspects]) => { this.nodeAspects = node.aspectNames.filter( @@ -84,13 +93,19 @@ export class AspectListComponent implements OnInit { this.valueChanged.emit([...this.nodeAspects, ...this.notDisplayedAspects]); this.updateCounter.emit(this.nodeAspects.length); }), - concatMap(() => this.aspectListService.getAspects()), + concatMap(() => this.loadAspects({ skipCount: this.standardAspectsLoaded }, { skipCount: this.customAspectsLoaded })), takeUntilDestroyed(this.destroyRef) ); } 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) { @@ -141,4 +156,33 @@ export class AspectListComponent implements OnInit { this.hasEqualAspect = this.nodeAspects.every((aspect) => this.nodeAspectStatus.includes(aspect)); } } + + private loadAspects(standardAspectsPagination?: ContentPagingQuery, customAspectsPagination?: ContentPagingQuery): Observable { + 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 ?? []) + ]) + ); + } } diff --git a/lib/content-services/src/lib/aspect-list/interfaces/custom-aspect-paging.interface.ts b/lib/content-services/src/lib/aspect-list/interfaces/custom-aspect-paging.interface.ts new file mode 100644 index 0000000000..c36c6582ff --- /dev/null +++ b/lib/content-services/src/lib/aspect-list/interfaces/custom-aspect-paging.interface.ts @@ -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; +} diff --git a/lib/content-services/src/lib/aspect-list/public-api.ts b/lib/content-services/src/lib/aspect-list/public-api.ts index 4a9abc400c..b5735a528b 100644 --- a/lib/content-services/src/lib/aspect-list/public-api.ts +++ b/lib/content-services/src/lib/aspect-list/public-api.ts @@ -22,5 +22,6 @@ export * from './services/node-aspect.service'; export * from './services/dialog-aspect-list.service'; export * from './aspect-list-dialog-data.interface'; +export * from './interfaces/custom-aspect-paging.interface'; export * from './aspect-list.module'; diff --git a/lib/content-services/src/lib/aspect-list/services/aspect-list.service.spec.ts b/lib/content-services/src/lib/aspect-list/services/aspect-list.service.spec.ts index 90f5a5ee1f..4b6d09f45c 100644 --- a/lib/content-services/src/lib/aspect-list/services/aspect-list.service.spec.ts +++ b/lib/content-services/src/lib/aspect-list/services/aspect-list.service.spec.ts @@ -16,96 +16,172 @@ */ import { TestBed } from '@angular/core/testing'; -import { AspectListService } from './aspect-list.service'; -import { AspectPaging, AspectsApi, AspectEntry } from '@alfresco/js-api'; -import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { AspectListService, CustomAspectsWhere, StandardAspectsWhere } from './aspect-list.service'; +import { AspectPaging, AspectsApi, AspectEntry, ListAspectsOpts } from '@alfresco/js-api'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; 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 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 standardAspectPagingMock: AspectPaging = { list: { entries: [stdAspect1, stdAspect2, stdAspect3] } }; 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 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', () => { let aspectListService: AspectListService; let apiService: AlfrescoApiService; let aspectsApi: AspectsApi; + let appConfigService: AppConfigService; beforeEach(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule] + providers: [provideHttpClient(), provideHttpClientTesting()] }); aspectListService = TestBed.inject(AspectListService); + appConfigService = TestBed.inject(AppConfigService); apiService = TestBed.inject(AlfrescoApiService); aspectsApi = new AspectsApi(apiService.getInstance()); 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) => { - const visibleAspects = ['std:standardAspectOne']; - spyOn(aspectsApi, 'listAspects').and.returnValue(Promise.resolve(standardAspectPagingMock)); - aspectListService.getStandardAspects(visibleAspects).subscribe((response) => { - expect(response).toEqual([stdAspect1]); - done(); + describe('When API returns error', () => { + const visibleAspects: string[] = []; + + beforeEach(() => { + spyOn(aspectsApi, 'listAspects').and.returnValue(Promise.reject(new Error('API error'))); + }); + + 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) => { - const visibleAspects = ['std:standardAspectTwo', 'std:standardAspectThree']; - spyOn(aspectsApi, 'listAspects').and.returnValue(Promise.resolve(standardAspectPagingMock)); - aspectListService.getStandardAspects(visibleAspects).subscribe((response) => { - expect(response).toEqual([stdAspect2, stdAspect3]); - done(); + describe('When aspects are returned', () => { + beforeEach(() => { + spyOn(aspectsApi, 'listAspects').and.returnValue(Promise.resolve(standardAspectPagingMock)); + }); + + 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) => { - const visibleAspects = ['cst:customAspectTwo']; - spyOn(aspectsApi, 'listAspects').and.returnValue(Promise.resolve(customAspectPagingMock)); - aspectListService.getCustomAspects(visibleAspects).subscribe((response) => { - expect(response).toEqual([cstAspect2]); - done(); + describe('getAllAspects', () => { + beforeEach(() => { + spyOn(aspectsApi, 'listAspects').and.returnValues(Promise.resolve(standardAspectPagingMock), Promise.resolve(customAspectPagingMock)); + }); + + 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) => { - const visibleAspects = ['cst:customAspectOne', 'cst:customAspectThree']; - spyOn(aspectsApi, 'listAspects').and.returnValue(Promise.resolve(customAspectPagingMock)); - aspectListService.getCustomAspects(visibleAspects).subscribe((response) => { - expect(response).toEqual([cstAspect1, cstAspect3]); - done(); + describe('getVisibleAspects', () => { + it('should return empty array when visible aspects are not provided in the config', () => { + spyOn(appConfigService, 'get').and.returnValue(undefined); + const visibleAspects = aspectListService.getVisibleAspects(); + expect(visibleAspects).toEqual([]); }); - }); - it('should get all custom aspects (visible aspects as undefined)', (done) => { - const visibleAspects = undefined; - 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 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(); + it('should return visible aspects from config when provided', () => { + const visibleAspectConfig = { + standard: ['std:standardAspectOne', 'std:standardAspectTwo'], + custom: ['cst:customAspectOne'] + }; + spyOn(appConfigService, 'get').and.returnValue(visibleAspectConfig); + const visibleAspects = aspectListService.getVisibleAspects(); + expect(visibleAspects).toEqual(['std:standardAspectOne', 'std:standardAspectTwo', 'cst:customAspectOne']); }); }); }); diff --git a/lib/content-services/src/lib/aspect-list/services/aspect-list.service.ts b/lib/content-services/src/lib/aspect-list/services/aspect-list.service.ts index 8e59f3e859..a64742cc31 100644 --- a/lib/content-services/src/lib/aspect-list/services/aspect-list.service.ts +++ b/lib/content-services/src/lib/aspect-list/services/aspect-list.service.ts @@ -20,7 +20,11 @@ import { AlfrescoApiService } from '../../services/alfresco-api.service'; import { AppConfigService } from '@alfresco/adf-core'; import { from, Observable, of, zip } from 'rxjs'; 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({ providedIn: 'root' @@ -34,37 +38,24 @@ export class AspectListService { constructor(private alfrescoApiService: AlfrescoApiService, private appConfigService: AppConfigService) {} - getAspects(): Observable { + getAllAspects(standardOpts?: ListAspectsOpts, customOpts?: ListAspectsOpts): Observable { const visibleAspectList = this.getVisibleAspects(); - const standardAspects$ = this.getStandardAspects(visibleAspectList); - const customAspects$ = this.getCustomAspects(visibleAspectList); + const standardAspects$ = this.getAspects(visibleAspectList, standardOpts); + const customAspects$ = this.getAspects(visibleAspectList, customOpts); return zip(standardAspects$, customAspects$).pipe( - map(([standardAspectList, customAspectList]) => [...standardAspectList, ...customAspectList]) + map(([standardAspectPaging, customAspectPaging]) => ({ standardAspectPaging, customAspectPaging })) ); } - getStandardAspects(whiteList: string[]): Observable { - const where = `(modelId in ('cm:contentmodel', 'emailserver:emailserverModel', 'smf:smartFolder', 'app:applicationmodel' ))`; - const opts: any = { - where, - include: ['properties'] - }; - + getAspects(whiteList: string[], opts?: ListAspectsOpts): Observable { return from(this.aspectsApi.listAspects(opts)).pipe( - map((result: AspectPaging) => this.filterAspectByConfig(whiteList, result?.list?.entries)), - catchError(() => of([])) - ); - } - - getCustomAspects(whiteList?: string[]): Observable { - const where = `(not namespaceUri matches('http://www.alfresco.*'))`; - const opts: any = { - where, - include: ['properties'] - }; - return from(this.aspectsApi.listAspects(opts)).pipe( - map((result: AspectPaging) => this.filterAspectByConfig(whiteList, result?.list?.entries)), - catchError(() => of([])) + map((result) => { + if (result?.list?.entries) { + result.list.entries = this.filterAspectByConfig(whiteList, result.list.entries); + } + return result; + }), + catchError(() => of({ list: { entries: [] } })) ); }