diff --git a/lib/content-services/src/lib/aspect-list/aspect-list.component.html b/lib/content-services/src/lib/aspect-list/aspect-list.component.html
index e90a4fd387..dd39db15ae 100644
--- a/lib/content-services/src/lib/aspect-list/aspect-list.component.html
+++ b/lib/content-services/src/lib/aspect-list/aspect-list.component.html
@@ -1,6 +1,6 @@
-
+
-
+
+
+
+
+
+
+
diff --git a/lib/content-services/src/lib/aspect-list/aspect-list.component.scss b/lib/content-services/src/lib/aspect-list/aspect-list.component.scss
index 5632bd12f8..e9a06f4677 100644
--- a/lib/content-services/src/lib/aspect-list/aspect-list.component.scss
+++ b/lib/content-services/src/lib/aspect-list/aspect-list.component.scss
@@ -8,10 +8,17 @@
.adf {
+ &-aspect-list-spinner {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 400px;
+ }
+
&-aspect-list-container {
padding-top: 3px;
- max-height: 400px;
+ height: 400px;
overflow: auto;
.adf-aspect-list-check-button {
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 c0ee0c2b60..7c528ef776 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
@@ -23,6 +23,7 @@ import { AspectListComponent } from './aspect-list.component';
import { AspectListService } from './aspect-list.service';
import { of } from 'rxjs';
import { AspectEntry } from '@alfresco/js-api';
+import { delay } from 'rxjs/operators';
const aspectListMock: AspectEntry[] = [{
entry: {
@@ -80,6 +81,26 @@ describe('AspectListComponent', () => {
providers: [AspectListService]
});
+ describe('Loading', () => {
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(AspectListComponent);
+ component = fixture.componentInstance;
+ nodeService = TestBed.inject(NodesApiService);
+ aspectListService = TestBed.inject(AspectListService);
+ });
+
+ it('should show the loading spinner when result is loading', () => {
+ const delayReusult = of([]).pipe(delay(0));
+ spyOn(nodeService, 'getNode').and.returnValue(delayReusult);
+ spyOn(aspectListService, 'getAspects').and.returnValue(delayReusult);
+ fixture.detectChanges();
+ const spinner = fixture.nativeElement.querySelector('#adf-aspect-spinner');
+ expect(spinner).toBeDefined();
+ expect(spinner).not.toBeNull();
+ });
+ });
+
describe('When passing a node id', () => {
beforeEach(() => {
diff --git a/lib/content-services/src/lib/aspect-list/aspect-list.module.ts b/lib/content-services/src/lib/aspect-list/aspect-list.module.ts
index dce4bab26a..9b448d49e8 100644
--- a/lib/content-services/src/lib/aspect-list/aspect-list.module.ts
+++ b/lib/content-services/src/lib/aspect-list/aspect-list.module.ts
@@ -27,6 +27,7 @@ import { MatDialogModule } from '@angular/material/dialog';
import { AspectListDialogComponent } from './aspect-list-dialog.component';
import { MatButtonModule } from '@angular/material/button';
import { MatTooltipModule } from '@angular/material/tooltip';
+import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
@NgModule({
imports: [
@@ -38,7 +39,8 @@ import { MatTooltipModule } from '@angular/material/tooltip';
TranslateModule,
MatDialogModule,
MatButtonModule,
- MatTooltipModule
+ MatTooltipModule,
+ MatProgressSpinnerModule
],
exports: [
AspectListComponent,
diff --git a/lib/content-services/src/lib/aspect-list/aspect-list.service.spec.ts b/lib/content-services/src/lib/aspect-list/aspect-list.service.spec.ts
index 437eb5cf44..1db6c5aa47 100644
--- a/lib/content-services/src/lib/aspect-list/aspect-list.service.spec.ts
+++ b/lib/content-services/src/lib/aspect-list/aspect-list.service.spec.ts
@@ -19,8 +19,8 @@ import { AspectEntry, AspectPaging } from '@alfresco/js-api';
import { async, TestBed } from '@angular/core/testing';
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
import { TranslateModule } from '@ngx-translate/core';
-import { AlfrescoApiService, AppConfigService, setupTestBed } from 'core';
-import { of, Subject } from 'rxjs';
+import { AlfrescoApiService, AppConfigService, LogService, setupTestBed } from 'core';
+import { of, Subject, throwError } from 'rxjs';
import { ContentTestingModule } from '../testing/content.testing.module';
import { AspectListService } from './aspect-list.service';
@@ -146,11 +146,12 @@ describe('AspectListService', () => {
const aspectTypesApi = jasmine.createSpyObj('AspectsApi', ['listAspects']);
const apiService: AlfrescoApiService = new AlfrescoApiService(null, null);
+ const logService: LogService = new LogService(appConfigService);
beforeEach(() => {
spyOn(appConfigService, 'get').and.returnValue({ 'default': ['frs:AspectOne'] });
spyOnProperty(apiService, 'aspectsApi').and.returnValue(aspectTypesApi);
- service = new AspectListService(apiService, appConfigService, null);
+ service = new AspectListService(apiService, appConfigService, null, logService);
});
it('should get the list of only available aspects', async(() => {
@@ -161,6 +162,26 @@ describe('AspectListService', () => {
expect(list[1].entry.id).toBe('frs:AspectCustom');
});
}));
+
+ it('should return a value when the standard aspect call fails', async(() => {
+ spyOn(logService, 'error').and.stub();
+ aspectTypesApi.listAspects.and.returnValues(throwError('Insert Coin'), of(customListAspectResp));
+ service.getAspects().subscribe((list) => {
+ expect(list.length).toBe(1);
+ expect(list[0].entry.id).toBe('frs:AspectCustom');
+ expect(logService.error).toHaveBeenCalled();
+ });
+ }));
+
+ it('should return a value when the custom aspect call fails', async(() => {
+ spyOn(logService, 'error').and.stub();
+ aspectTypesApi.listAspects.and.returnValues(of(listAspectResp), throwError('Insert Coin'));
+ service.getAspects().subscribe((list) => {
+ expect(list.length).toBe(1);
+ expect(list[0].entry.id).toBe('frs:AspectOne');
+ expect(logService.error).toHaveBeenCalled();
+ });
+ }));
});
});
diff --git a/lib/content-services/src/lib/aspect-list/aspect-list.service.ts b/lib/content-services/src/lib/aspect-list/aspect-list.service.ts
index 79d9a3852a..cf9dcb9f15 100644
--- a/lib/content-services/src/lib/aspect-list/aspect-list.service.ts
+++ b/lib/content-services/src/lib/aspect-list/aspect-list.service.ts
@@ -17,11 +17,11 @@
import { Injectable } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
-import { AlfrescoApiService, AppConfigService } from '@alfresco/adf-core';
-import { from, Observable, Subject, zip } from 'rxjs';
+import { AlfrescoApiService, AppConfigService, LogService } from '@alfresco/adf-core';
+import { from, Observable, of, Subject, zip } from 'rxjs';
import { AspectListDialogComponentData } from './aspect-list-dialog-data.interface';
import { AspectListDialogComponent } from './aspect-list-dialog.component';
-import { map } from 'rxjs/operators';
+import { catchError, map } from 'rxjs/operators';
import { AspectEntry, AspectPaging } from '@alfresco/js-api';
@Injectable({
@@ -30,7 +30,9 @@ import { AspectEntry, AspectPaging } from '@alfresco/js-api';
export class AspectListService {
constructor(private alfrescoApiService: AlfrescoApiService,
- private appConfigService: AppConfigService, private dialog: MatDialog) {
+ private appConfigService: AppConfigService,
+ private dialog: MatDialog,
+ private logService: LogService) {
}
getAspects(): Observable
{
@@ -44,17 +46,25 @@ export class AspectListService {
getStandardAspects(whiteList: string[]): Observable {
const where = `(modelIds in ('cm:contentmodel', 'emailserver:emailserverModel', 'smf:smartFolder', 'app:applicationmodel' ))`;
- return from(this.alfrescoApiService.aspectsApi.listAspects(where))
+ return from(this.alfrescoApiService.aspectsApi.listAspects({where}))
.pipe(
- map((result: AspectPaging) => this.filterAspectByConfig(whiteList, result?.list?.entries))
+ map((result: AspectPaging) => this.filterAspectByConfig(whiteList, result?.list?.entries)),
+ catchError((error) => {
+ this.logService.error(error);
+ return of([]);
+ })
);
}
getCustomAspects(): Observable {
- const where = `(not namespaceUri matches('http://www.alfresco.*')`;
- return from(this.alfrescoApiService.aspectsApi.listAspects(where))
+ const where = `(not namespaceUri matches('http://www.alfresco.*'))`;
+ return from(this.alfrescoApiService.aspectsApi.listAspects({where}))
.pipe(
- map((result: AspectPaging) => result?.list?.entries)
+ map((result: AspectPaging) => result?.list?.entries),
+ catchError((error) => {
+ this.logService.error(error);
+ return of([]);
+ })
);
}
diff --git a/lib/content-services/src/lib/aspect-list/node-aspect.service.spec.ts b/lib/content-services/src/lib/aspect-list/node-aspect.service.spec.ts
index b60c6e1471..31acf9561e 100644
--- a/lib/content-services/src/lib/aspect-list/node-aspect.service.spec.ts
+++ b/lib/content-services/src/lib/aspect-list/node-aspect.service.spec.ts
@@ -17,7 +17,7 @@
import { TestBed } from '@angular/core/testing';
import { TranslateModule } from '@ngx-translate/core';
-import { AlfrescoApiService, NodesApiService, setupTestBed } from 'core';
+import { AlfrescoApiService, CardViewUpdateService, NodesApiService, setupTestBed } from 'core';
import { of } from 'rxjs';
import { ContentTestingModule } from '../testing/content.testing.module';
import { AspectListService } from './aspect-list.service';
@@ -29,6 +29,7 @@ describe('NodeAspectService', () => {
let nodeAspectService: NodeAspectService;
let nodeApiService: NodesApiService;
let alfrescoApiService: AlfrescoApiService;
+ let cardViewUpdateService: CardViewUpdateService;
setupTestBed({
imports: [
@@ -42,6 +43,7 @@ describe('NodeAspectService', () => {
nodeAspectService = TestBed.inject(NodeAspectService);
nodeApiService = TestBed.inject(NodesApiService);
alfrescoApiService = TestBed.inject(AlfrescoApiService);
+ cardViewUpdateService = TestBed.inject(CardViewUpdateService);
});
it('should open the aspect list dialog', () => {
@@ -71,4 +73,16 @@ describe('NodeAspectService', () => {
nodeAspectService.updateNodeAspects('fake-node-id');
});
+ it('should send and update node aspect once the node has been updated', (done) => {
+ cardViewUpdateService.updatedAspect$.subscribe((nodeUpdated) => {
+ expect(nodeUpdated.id).toBe('fake-node-id');
+ expect(nodeUpdated.aspectNames).toEqual(['a', 'b', 'c']);
+ done();
+ });
+ const fakeNode = { id: 'fake-node-id', aspectNames: ['a', 'b', 'c'] };
+ spyOn(aspectListService, 'openAspectListDialog').and.returnValue(of(['a', 'b', 'c']));
+ spyOn(nodeApiService, 'updateNode').and.returnValue(of(fakeNode));
+ nodeAspectService.updateNodeAspects('fake-node-id');
+ });
+
});
diff --git a/lib/content-services/src/lib/aspect-list/node-aspect.service.ts b/lib/content-services/src/lib/aspect-list/node-aspect.service.ts
index 32fa1ca6be..cc4a1e9b40 100644
--- a/lib/content-services/src/lib/aspect-list/node-aspect.service.ts
+++ b/lib/content-services/src/lib/aspect-list/node-aspect.service.ts
@@ -16,7 +16,7 @@
*/
import { Injectable } from '@angular/core';
-import { AlfrescoApiService, NodesApiService } from '@alfresco/adf-core';
+import { AlfrescoApiService, CardViewUpdateService, NodesApiService } from '@alfresco/adf-core';
import { AspectListService } from './aspect-list.service';
@Injectable({
@@ -26,13 +26,15 @@ export class NodeAspectService {
constructor(private alfrescoApiService: AlfrescoApiService,
private nodesApiService: NodesApiService,
- private aspectListService: AspectListService) {
+ private aspectListService: AspectListService,
+ private cardViewUpdateService: CardViewUpdateService) {
}
updateNodeAspects(nodeId: string) {
this.aspectListService.openAspectListDialog(nodeId).subscribe((aspectList) => {
this.nodesApiService.updateNode(nodeId, { aspectNames: [...aspectList] }).subscribe((updatedNode) => {
this.alfrescoApiService.nodeUpdated.next(updatedNode);
+ this.cardViewUpdateService.updateNodeAspect(updatedNode);
});
});
}
diff --git a/lib/content-services/src/lib/content-metadata/components/content-metadata/content-metadata.component.spec.ts b/lib/content-services/src/lib/content-metadata/components/content-metadata/content-metadata.component.spec.ts
index ff61a79ca7..9289ca1f83 100644
--- a/lib/content-services/src/lib/content-metadata/components/content-metadata/content-metadata.component.spec.ts
+++ b/lib/content-services/src/lib/content-metadata/components/content-metadata/content-metadata.component.spec.ts
@@ -18,7 +18,7 @@
import { async, ComponentFixture, TestBed, tick, fakeAsync } from '@angular/core/testing';
import { SimpleChange } from '@angular/core';
import { By } from '@angular/platform-browser';
-import { Node } from '@alfresco/js-api';
+import { MinimalNode, Node } from '@alfresco/js-api';
import { ContentMetadataComponent } from './content-metadata.component';
import { ContentMetadataService } from '../../services/content-metadata.service';
import {
@@ -124,6 +124,17 @@ describe('ContentMetadataComponent', () => {
expect(component.changedProperties).toEqual({ properties: { 'property-key': 'updated-value' } });
}));
+ it('nodeAspectUpdate', fakeAsync(() => {
+ const fakeNode: MinimalNode = { id: 'fake-minimal-node', aspectNames: ['ft:a', 'ft:b', 'ft:c'], name: 'fake-node'};
+ spyOn(contentMetadataService, 'getGroupedProperties').and.stub();
+ spyOn(contentMetadataService, 'getBasicProperties').and.stub();
+ updateService.updateNodeAspect(fakeNode);
+
+ tick(600);
+ expect(contentMetadataService.getBasicProperties).toHaveBeenCalled();
+ expect(contentMetadataService.getGroupedProperties).toHaveBeenCalled();
+ }));
+
it('should save changedProperties on save click', fakeAsync(async () => {
component.editable = true;
const property = { key: 'properties.property-key', value: 'original-value' };
diff --git a/lib/content-services/src/lib/content-metadata/components/content-metadata/content-metadata.component.ts b/lib/content-services/src/lib/content-metadata/components/content-metadata/content-metadata.component.ts
index 2d47266e83..8b72b5b255 100644
--- a/lib/content-services/src/lib/content-metadata/components/content-metadata/content-metadata.component.ts
+++ b/lib/content-services/src/lib/content-metadata/components/content-metadata/content-metadata.component.ts
@@ -26,7 +26,8 @@ import {
AlfrescoApiService,
TranslationService,
AppConfigService,
- CardViewBaseItemModel
+ CardViewBaseItemModel,
+ UpdateNotification
} from '@alfresco/adf-core';
import { ContentMetadataService } from '../../services/content-metadata.service';
import { CardViewGroup } from '../../interfaces/content-metadata.interfaces';
@@ -115,13 +116,18 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
debounceTime(200),
takeUntil(this.onDestroy$))
.subscribe(
- (updatedNode) => {
+ (updatedNode: UpdateNotification) => {
this.hasMetadataChanged = true;
this.targetProperty = updatedNode.target;
this.updateChanges(updatedNode.changed);
}
);
+ this.cardViewUpdateService.updatedAspect$.pipe(
+ debounceTime(200),
+ takeUntil(this.onDestroy$))
+ .subscribe((node) => this.loadProperties(node));
+
this.loadProperties(this.node);
}
diff --git a/lib/core/card-view/services/card-view-update.service.spec.ts b/lib/core/card-view/services/card-view-update.service.spec.ts
index 2723cc0bf2..b0c368f5da 100644
--- a/lib/core/card-view/services/card-view-update.service.spec.ts
+++ b/lib/core/card-view/services/card-view-update.service.spec.ts
@@ -15,6 +15,7 @@
* limitations under the License.
*/
+import { MinimalNode } from '@alfresco/js-api';
import { async, TestBed } from '@angular/core/testing';
import { CardViewBaseItemModel } from '../models/card-view-baseitem.model';
import { CardViewUpdateService, transformKeyToObject } from './card-view-update.service';
@@ -82,5 +83,13 @@ describe('CardViewUpdateService', () => {
);
cardViewUpdateService.clicked(property);
}));
+
+ it('should send updated node when aspect changed', async(() => {
+ const fakeNode: MinimalNode = { id: 'Bigfoot'};
+ cardViewUpdateService.updatedAspect$.subscribe((node: MinimalNode) => {
+ expect(node.id).toBe('Bigfoot');
+ });
+ cardViewUpdateService.updateNodeAspect(fakeNode);
+ }));
});
});
diff --git a/lib/core/card-view/services/card-view-update.service.ts b/lib/core/card-view/services/card-view-update.service.ts
index c64b19300e..20156a7493 100644
--- a/lib/core/card-view/services/card-view-update.service.ts
+++ b/lib/core/card-view/services/card-view-update.service.ts
@@ -15,6 +15,7 @@
* limitations under the License.
*/
+import { MinimalNode } from '@alfresco/js-api';
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
import { CardViewBaseItemModel } from '../models/card-view-baseitem.model';
@@ -44,6 +45,7 @@ export class CardViewUpdateService {
itemUpdated$ = new Subject();
itemClicked$ = new Subject();
updateItem$ = new Subject();
+ updatedAspect$ = new Subject();
update(property: CardViewBaseItemModel, newValue: any) {
this.itemUpdated$.next({
@@ -66,4 +68,8 @@ export class CardViewUpdateService {
this.updateItem$.next(notification);
}
+ updateNodeAspect(node: MinimalNode) {
+ this.updatedAspect$.next(node);
+ }
+
}