[ACS-5839] migrate to latest JS-API types (#8859)

* [ci:force] migrate Minimal Node to Node

* [ci:force] remove js-api wrappers and use real types

* [ci:force] remove js-api wrappers and use real types

* [ci:force] fix linting errors

* [ci:force] fix linting errors

* [ci:force] security fixes

* [ci:force] sonarcloud bug fixes

* [ci:force] dead code elimination, sonar suggested fixes
This commit is contained in:
Denys Vuika
2023-08-29 20:56:40 +01:00
committed by GitHub
parent a5b05b3e5f
commit 3b4ce3b857
51 changed files with 1337 additions and 1984 deletions

View File

@@ -24,70 +24,74 @@ import { ContentTestingModule } from '../testing/content.testing.module';
import { AspectListDialogComponentData } from './aspect-list-dialog-data.interface';
import { AspectListService } from './services/aspect-list.service';
import { delay } from 'rxjs/operators';
import { AspectEntry, MinimalNode } from '@alfresco/js-api';
import { AspectEntry, Node } from '@alfresco/js-api';
import { NodesApiService } from '../common/services/nodes-api.service';
const aspectListMock: AspectEntry[] = [{
entry: {
parentId: 'frs:aspectZero',
id: 'frs:AspectOne',
description: 'First Aspect with random description',
title: 'FirstAspect',
properties: [
{
id: 'channelPassword',
title: 'The authenticated channel password',
dataType: 'd:encrypted'
},
{
id: 'channelUsername',
title: 'The authenticated channel username',
dataType: 'd:encrypted'
}
]
const aspectListMock: AspectEntry[] = [
{
entry: {
parentId: 'frs:aspectZero',
id: 'frs:AspectOne',
description: 'First Aspect with random description',
title: 'FirstAspect',
properties: [
{
id: 'channelPassword',
title: 'The authenticated channel password',
dataType: 'd:encrypted'
},
{
id: 'channelUsername',
title: 'The authenticated channel username',
dataType: 'd:encrypted'
}
]
}
},
{
entry: {
parentId: 'frs:AspectZer',
id: 'frs:SecondAspect',
description: 'Second Aspect description',
title: 'SecondAspect',
properties: [
{
id: 'assetId',
title: 'Published Asset Id',
dataType: 'd:text'
},
{
id: 'assetUrl',
title: 'Published Asset URL',
dataType: 'd:text'
}
]
}
}
},
{
entry: {
parentId: 'frs:AspectZer',
id: 'frs:SecondAspect',
description: 'Second Aspect description',
title: 'SecondAspect',
properties: [
{
id: 'assetId',
title: 'Published Asset Id',
dataType: 'd:text'
},
{
id: 'assetUrl',
title: 'Published Asset URL',
dataType: 'd:text'
}
]
}
}];
];
const customAspectListMock: AspectEntry[] = [{
entry: {
parentId: 'cst:customAspect',
id: 'cst:customAspect',
description: 'Custom Aspect with random description',
title: 'CustomAspect',
properties: [
{
id: 'channelPassword',
title: 'The authenticated channel password',
dataType: 'd:propA'
},
{
id: 'channelUsername',
title: 'The authenticated channel username',
dataType: 'd:propB'
}
]
const customAspectListMock: AspectEntry[] = [
{
entry: {
parentId: 'cst:customAspect',
id: 'cst:customAspect',
description: 'Custom Aspect with random description',
title: 'CustomAspect',
properties: [
{
id: 'channelPassword',
title: 'The authenticated channel password',
dataType: 'd:propA'
},
{
id: 'channelUsername',
title: 'The authenticated channel username',
dataType: 'd:propB'
}
]
}
}
}];
];
describe('AspectListDialogComponent', () => {
let fixture: ComponentFixture<AspectListDialogComponent>;
@@ -97,10 +101,9 @@ describe('AspectListDialogComponent', () => {
const event = new KeyboardEvent('keydown', {
bubbles: true,
keyCode: 27
} as KeyboardEventInit );
} as KeyboardEventInit);
describe('Without passing node id', () => {
beforeEach(async () => {
data = {
title: 'Title',
@@ -110,11 +113,7 @@ describe('AspectListDialogComponent', () => {
};
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ContentTestingModule,
MatDialogModule
],
imports: [TranslateModule.forRoot(), ContentTestingModule, MatDialogModule],
providers: [
{ provide: MAT_DIALOG_DATA, useValue: data },
{
@@ -156,7 +155,9 @@ describe('AspectListDialogComponent', () => {
expect(dialogTitle).not.toBeNull();
expect(dialogTitle.innerText).toBe(data.title);
const dialogDescription = fixture.nativeElement.querySelector('[data-automation-id="aspect-list-dialog-title"] .adf-aspect-list-dialog-description');
const dialogDescription = fixture.nativeElement.querySelector(
'[data-automation-id="aspect-list-dialog-title"] .adf-aspect-list-dialog-description'
);
expect(dialogDescription).not.toBeNull();
expect(dialogDescription.innerText).toBe(data.description);
@@ -228,7 +229,11 @@ describe('AspectListDialogComponent', () => {
});
it('should complete the select stream Cancel button is clicked', (done) => {
data.select.subscribe(() => { }, () => { }, () => done());
data.select.subscribe(
() => {},
() => {},
() => done()
);
const cancelButton: HTMLButtonElement = fixture.nativeElement.querySelector('#aspect-list-dialog-actions-cancel');
expect(cancelButton).toBeDefined();
cancelButton.click();
@@ -237,7 +242,6 @@ describe('AspectListDialogComponent', () => {
});
describe('Passing the node id', () => {
beforeEach(async () => {
data = {
title: 'Title',
@@ -248,11 +252,7 @@ describe('AspectListDialogComponent', () => {
};
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ContentTestingModule,
MatDialogModule
],
imports: [TranslateModule.forRoot(), ContentTestingModule, MatDialogModule],
providers: [
{ provide: MAT_DIALOG_DATA, useValue: data },
{
@@ -274,7 +274,9 @@ describe('AspectListDialogComponent', () => {
spyOn(aspectListService, 'getAspects').and.returnValue(of([...aspectListMock, ...customAspectListMock]));
spyOn(aspectListService, 'getVisibleAspects').and.returnValue(['frs:AspectOne']);
spyOn(aspectListService, 'getCustomAspects').and.returnValue(of(customAspectListMock));
spyOn(nodeService, 'getNode').and.returnValue(of(new MinimalNode({ id: 'fake-node-id', aspectNames: ['frs:AspectOne', 'cst:customAspect'] })).pipe(delay(0)));
spyOn(nodeService, 'getNode').and.returnValue(
of(new Node({ id: 'fake-node-id', aspectNames: ['frs:AspectOne', 'cst:customAspect'] })).pipe(delay(0))
);
fixture = TestBed.createComponent(AspectListDialogComponent);
fixture.componentInstance.data.select = new Subject<string[]>();
fixture.detectChanges();
@@ -326,5 +328,4 @@ describe('AspectListDialogComponent', () => {
expect(applyButton.disabled).toBe(false);
});
});
});

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { MinimalNode } from '@alfresco/js-api';
import { Node } from '@alfresco/js-api';
import { TestBed } from '@angular/core/testing';
import { TranslateModule } from '@ngx-translate/core';
import { NodesApiService } from '../../common/services/nodes-api.service';
@@ -27,7 +27,6 @@ import { CardViewContentUpdateService } from '../../common/services/card-view-co
import { TagService } from '@alfresco/adf-content-services';
describe('NodeAspectService', () => {
let dialogAspectListService: DialogAspectListService;
let nodeAspectService: NodeAspectService;
let nodeApiService: NodesApiService;
@@ -35,10 +34,7 @@ describe('NodeAspectService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ContentTestingModule
]
imports: [TranslateModule.forRoot(), ContentTestingModule]
});
dialogAspectListService = TestBed.inject(DialogAspectListService);
nodeAspectService = TestBed.inject(NodeAspectService);
@@ -81,7 +77,7 @@ describe('NodeAspectService', () => {
expect(nodeUpdated.id).toBe('fake-node-id');
expect(nodeUpdated.aspectNames).toEqual(['a', 'b', 'c']);
});
const fakeNode = new MinimalNode({ id: 'fake-node-id', aspectNames: ['a', 'b', 'c'] });
const fakeNode = new Node({ id: 'fake-node-id', aspectNames: ['a', 'b', 'c'] });
spyOn(dialogAspectListService, 'openAspectListDialog').and.returnValue(of(['a', 'b', 'c']));
spyOn(nodeApiService, 'updateNode').and.returnValue(of(fakeNode));
nodeAspectService.updateNodeAspects('fake-node-id');
@@ -92,7 +88,7 @@ describe('NodeAspectService', () => {
expect(nodeUpdated.id).toBe('fake-node-id');
expect(nodeUpdated.aspectNames).toEqual(['a', 'b', 'c']);
});
const fakeNode = new MinimalNode({ id: 'fake-node-id', aspectNames: ['a', 'b', 'c'] });
const fakeNode = new Node({ id: 'fake-node-id', aspectNames: ['a', 'b', 'c'] });
spyOn(dialogAspectListService, 'openAspectListDialog').and.returnValue(of(['a', 'b', 'c']));
spyOn(nodeApiService, 'updateNode').and.returnValue(of(fakeNode));
nodeAspectService.updateNodeAspects('fake-node-id');
@@ -101,7 +97,7 @@ describe('NodeAspectService', () => {
it('should call emit on refresh from TagService', () => {
const tagService = TestBed.inject(TagService);
spyOn(dialogAspectListService, 'openAspectListDialog').and.returnValue(of([]));
const node = new MinimalNode({ id: 'fake-node-id', aspectNames: ['a', 'b', 'c'] });
const node = new Node({ id: 'fake-node-id', aspectNames: ['a', 'b', 'c'] });
spyOn(nodeApiService, 'updateNode').and.returnValue(of(node));
spyOn(tagService.refresh, 'emit');
nodeAspectService.updateNodeAspects('some node id', 'some-selector');

View File

@@ -15,19 +15,9 @@
* limitations under the License.
*/
import {
Component,
EventEmitter,
Input,
OnChanges,
OnInit,
Output,
ViewChild,
ViewEncapsulation,
OnDestroy
} from '@angular/core';
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, ViewChild, ViewEncapsulation, OnDestroy } from '@angular/core';
import { MatSelect } from '@angular/material/select';
import { Node, PathElementEntity } from '@alfresco/js-api';
import { Node, PathElement } from '@alfresco/js-api';
import { DocumentListComponent } from '../document-list/components/document-list.component';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
@@ -40,7 +30,6 @@ import { takeUntil } from 'rxjs/operators';
host: { class: 'adf-breadcrumb' }
})
export class BreadcrumbComponent implements OnInit, OnChanges, OnDestroy {
/** Active node, builds UI based on folderNode.path.elements collection. */
@Input()
folderNode: Node = null;
@@ -79,10 +68,10 @@ export class BreadcrumbComponent implements OnInit, OnChanges, OnDestroy {
@Input()
maxItems: number;
previousNodes: PathElementEntity[];
lastNodes: PathElementEntity[];
previousNodes: PathElement[];
lastNodes: PathElement[];
route: PathElementEntity[] = [];
route: PathElement[] = [];
private onDestroy$ = new Subject<boolean>();
@@ -96,18 +85,16 @@ export class BreadcrumbComponent implements OnInit, OnChanges, OnDestroy {
/** Emitted when the user clicks on a breadcrumb. */
@Output()
navigate = new EventEmitter<PathElementEntity>();
navigate = new EventEmitter<PathElement>();
ngOnInit() {
this.transform = this.transform ? this.transform : null;
if (this.target) {
this.target.$folderNode
.pipe(takeUntil(this.onDestroy$))
.subscribe((folderNode: Node) => {
this.folderNode = folderNode;
this.recalculateNodes();
});
this.target.$folderNode.pipe(takeUntil(this.onDestroy$)).subscribe((folderNode: Node) => {
this.folderNode = folderNode;
this.recalculateNodes();
});
}
}
@@ -141,7 +128,7 @@ export class BreadcrumbComponent implements OnInit, OnChanges, OnDestroy {
return !!this.previousNodes;
}
parseRoute(node: Node): PathElementEntity[] {
parseRoute(node: Node): PathElement[] {
if (node && node.path) {
const route = (node.path.elements || []).slice();
@@ -149,7 +136,7 @@ export class BreadcrumbComponent implements OnInit, OnChanges, OnDestroy {
id: node.id,
name: node.name,
node
} as PathElementEntity);
} as PathElement);
const rootPos = this.getElementPosition(route, this.rootId);
if (rootPos > 0) {
@@ -170,7 +157,7 @@ export class BreadcrumbComponent implements OnInit, OnChanges, OnDestroy {
return [];
}
private getElementPosition(route: PathElementEntity[], nodeId: string): number {
private getElementPosition(route: PathElement[], nodeId: string): number {
let position: number = -1;
if (route && route.length > 0 && nodeId) {
@@ -184,7 +171,7 @@ export class BreadcrumbComponent implements OnInit, OnChanges, OnDestroy {
return !this.readOnly && !lastItem;
}
onRoutePathClick(route: PathElementEntity, event?: Event): void {
onRoutePathClick(route: PathElement, event?: Event): void {
if (event && event.type === 'click') {
event.preventDefault();
}
@@ -192,7 +179,7 @@ export class BreadcrumbComponent implements OnInit, OnChanges, OnDestroy {
this.onRouteClick(route);
}
onRouteClick(route: PathElementEntity) {
onRouteClick(route: PathElement) {
if (route && !this.readOnly) {
this.navigate.emit(route);

View File

@@ -17,7 +17,7 @@
import { Component, OnChanges, ViewChild, ViewEncapsulation } from '@angular/core';
import { MatSelect } from '@angular/material/select';
import { PathElementEntity, Node } from '@alfresco/js-api';
import { PathElement, Node } from '@alfresco/js-api';
import { BreadcrumbComponent } from './breadcrumb.component';
@Component({
@@ -28,12 +28,11 @@ import { BreadcrumbComponent } from './breadcrumb.component';
host: { class: 'adf-dropdown-breadcrumb' }
})
export class DropdownBreadcrumbComponent extends BreadcrumbComponent implements OnChanges {
@ViewChild('dropdown')
dropdown: MatSelect;
currentNode: PathElementEntity;
previousNodes: PathElementEntity[];
currentNode: PathElement;
previousNodes: PathElement[];
/**
* Calculate the current and previous nodes from the route array

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { AssocChildBody, AssociationBody } from '@alfresco/js-api';
import { ChildAssociationBody, AssociationBody } from '@alfresco/js-api';
export interface FileUploadProgress {
loaded: number;
@@ -63,7 +63,7 @@ export class FileUploadOptions {
* You can optionally specify an array of **secondaryChildren** to create one or more secondary child associations,
* such that the newly created node acts as a parent node.
*/
secondaryChildren?: AssocChildBody[];
secondaryChildren?: ChildAssociationBody[];
/**
* You can optionally specify an array of **targets** to create one or more peer associations such that the newly created node acts as a source node.
*/
@@ -112,9 +112,13 @@ export class FileModel {
percent: 0
};
this.options = Object.assign({}, {
newVersion: false
}, options);
this.options = Object.assign(
{},
{
newVersion: false
},
options
);
}
get extension(): string {

View File

@@ -15,24 +15,23 @@
* limitations under the License.
*/
import { MinimalNode } from '@alfresco/js-api';
import { Node } from '@alfresco/js-api';
import { fakeAsync, TestBed } from '@angular/core/testing';
import { CardViewContentUpdateService } from './card-view-content-update.service';
describe('CardViewContentUpdateService', () => {
let cardViewContentUpdateService: CardViewContentUpdateService;
let cardViewContentUpdateService: CardViewContentUpdateService;
beforeEach(() => {
cardViewContentUpdateService = TestBed.inject(CardViewContentUpdateService);
});
beforeEach(() => {
cardViewContentUpdateService = TestBed.inject(CardViewContentUpdateService);
it('should send updated node when aspect changed', fakeAsync(() => {
const fakeNode = { id: 'Bigfoot' } as Node;
cardViewContentUpdateService.updatedAspect$.subscribe((node) => {
expect(node.id).toBe('Bigfoot');
});
it('should send updated node when aspect changed', fakeAsync(() => {
const fakeNode: MinimalNode = { id: 'Bigfoot'} as MinimalNode;
cardViewContentUpdateService.updatedAspect$.subscribe((node: MinimalNode) => {
expect(node.id).toBe('Bigfoot');
});
cardViewContentUpdateService.updateNodeAspect(fakeNode);
}));
cardViewContentUpdateService.updateNodeAspect(fakeNode);
}));
});

View File

@@ -16,43 +16,41 @@
*/
import { UpdateNotification, CardViewBaseItemModel, CardViewUpdateService } from '@alfresco/adf-core';
import { MinimalNode } from '@alfresco/js-api';
import { Node } from '@alfresco/js-api';
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
import { BaseCardViewContentUpdate } from '../../interfaces/base-card-view-content-update.interface';
@Injectable({
providedIn: 'root'
providedIn: 'root'
})
export class CardViewContentUpdateService implements BaseCardViewContentUpdate {
itemUpdated$ = new Subject<UpdateNotification>();
updatedAspect$ = new Subject<Node>();
itemUpdated$ = new Subject<UpdateNotification>();
constructor(private cardViewUpdateService: CardViewUpdateService) {
this.linkVariables();
}
updatedAspect$ = new Subject<MinimalNode>();
update(property: CardViewBaseItemModel, newValue: any) {
this.cardViewUpdateService.update(property, newValue);
}
constructor(private cardViewUpdateService: CardViewUpdateService) {
this.linkVariables();
}
updateElement(notification: CardViewBaseItemModel) {
this.cardViewUpdateService.updateElement(notification);
}
update(property: CardViewBaseItemModel, newValue: any) {
this.cardViewUpdateService.update(property, newValue);
}
updateNodeAspect(node: Node) {
this.updatedAspect$.next(node);
}
updateElement(notification: CardViewBaseItemModel) {
this.cardViewUpdateService.updateElement(notification);
}
private linkVariables() {
this.linkItemUpdated();
}
updateNodeAspect(node: MinimalNode) {
this.updatedAspect$.next(node);
}
private linkVariables() {
this.linkItemUpdated();
}
private linkItemUpdated() {
this.cardViewUpdateService.itemUpdated$.subscribe(res => {
this.itemUpdated$.next(res);
});
}
private linkItemUpdated() {
this.cardViewUpdateService.itemUpdated$.subscribe((res) => {
this.itemUpdated$.next(res);
});
}
}

View File

@@ -16,7 +16,7 @@
*/
import { Injectable } from '@angular/core';
import { ContentApi, MinimalNode, Node, NodeEntry } from '@alfresco/js-api';
import { ContentApi, Node, NodeEntry } from '@alfresco/js-api';
import { Subject } from 'rxjs';
import { AlfrescoApiService, AuthenticationService } from '@alfresco/adf-core';
import { PermissionsEnum } from '../models/permissions.enum';
@@ -33,10 +33,9 @@ export interface FolderCreatedEvent {
providedIn: 'root'
})
export class ContentService {
folderCreated: Subject<FolderCreatedEvent> = new Subject<FolderCreatedEvent>();
folderCreate: Subject<MinimalNode> = new Subject<MinimalNode>();
folderEdit: Subject<MinimalNode> = new Subject<MinimalNode>();
folderCreated = new Subject<FolderCreatedEvent>();
folderCreate = new Subject<Node>();
folderEdit = new Subject<Node>();
private _contentApi: ContentApi;
get contentApi(): ContentApi {
@@ -44,10 +43,7 @@ export class ContentService {
return this._contentApi;
}
constructor(public authService: AuthenticationService,
public apiService: AlfrescoApiService) {
}
constructor(public authService: AuthenticationService, public apiService: AlfrescoApiService) {}
/**
* Gets a content URL for the given node.
@@ -89,17 +85,16 @@ export class ContentService {
let hasPermissions = false;
userId = userId ?? this.authService.getEcmUsername();
const permissions = [...(node.permissions?.locallySet || []), ...(node.permissions?.inherited || [])]
.filter((currentPermission) => currentPermission.authorityId === userId);
const permissions = [...(node.permissions?.locallySet || []), ...(node.permissions?.inherited || [])].filter(
(currentPermission) => currentPermission.authorityId === userId
);
if (permissions.length) {
if (permission && permission.startsWith('!')) {
hasPermissions = !permissions.find((currentPermission) => currentPermission.name === permission.replace('!', ''));
} else {
hasPermissions = !!permissions.find((currentPermission) => currentPermission.name === permission);
}
} else {
if (permission === PermissionsEnum.CONSUMER) {
hasPermissions = true;
} else if (permission === PermissionsEnum.NOT_CONSUMER) {
@@ -124,11 +119,12 @@ export class ContentService {
if (node && node.allowableOperations) {
if (allowableOperation && allowableOperation.startsWith('!')) {
hasAllowableOperations = !node.allowableOperations.find((currentOperation) => currentOperation === allowableOperation.replace('!', ''));
hasAllowableOperations = !node.allowableOperations.find(
(currentOperation) => currentOperation === allowableOperation.replace('!', '')
);
} else {
hasAllowableOperations = !!node.allowableOperations.find((currentOperation) => currentOperation === allowableOperation);
}
} else {
if (allowableOperation && allowableOperation.startsWith('!')) {
hasAllowableOperations = true;
@@ -149,5 +145,4 @@ export class ContentService {
return hasAllowableOperations;
}
}

View File

@@ -16,7 +16,7 @@
*/
import { Injectable } from '@angular/core';
import { MinimalNode, NodeEntry, NodePaging, NodesApi, TrashcanApi, Node } from '@alfresco/js-api';
import { NodeEntry, NodePaging, NodesApi, TrashcanApi, Node } from '@alfresco/js-api';
import { Subject, from, Observable, throwError } from 'rxjs';
import { AlfrescoApiService, UserPreferencesService } from '@alfresco/adf-core';
import { catchError, map } from 'rxjs/operators';
@@ -26,7 +26,6 @@ import { NodeMetadata } from '../models/node-metadata.model';
providedIn: 'root'
})
export class NodesApiService {
/**
* Publish/subscribe to events related to node updates.
*/
@@ -44,11 +43,9 @@ export class NodesApiService {
return this._nodesApi;
}
constructor(private apiService: AlfrescoApiService,
private preferences: UserPreferencesService) {
}
constructor(private apiService: AlfrescoApiService, private preferences: UserPreferencesService) {}
private getEntryFromEntity(entity: NodeEntry) {
private getEntryFromEntity(entity: NodeEntry): Node {
return entity.entry;
}
@@ -59,7 +56,7 @@ export class NodesApiService {
* @param options Optional parameters supported by JS-API
* @returns Node information
*/
getNode(nodeId: string, options: any = {}): Observable<MinimalNode> {
getNode(nodeId: string, options: any = {}): Observable<Node> {
const defaults = {
include: ['path', 'properties', 'allowableOperations', 'permissions']
};
@@ -86,9 +83,7 @@ export class NodesApiService {
};
const queryOptions = Object.assign(defaults, options);
return from(this.nodesApi.listNodeChildren(nodeId, queryOptions)).pipe(
catchError((err) => throwError(err))
);
return from(this.nodesApi.listNodeChildren(nodeId, queryOptions)).pipe(catchError((err) => throwError(err)));
}
/**
@@ -99,7 +94,7 @@ export class NodesApiService {
* @param options Optional parameters supported by JS-API
* @returns Details of the new node
*/
createNode(parentNodeId: string, nodeBody: any, options: any = {}): Observable<MinimalNode> {
createNode(parentNodeId: string, nodeBody: any, options: any = {}): Observable<Node> {
return from(this.nodesApi.createNode(parentNodeId, nodeBody, options)).pipe(
map(this.getEntryFromEntity),
catchError((err) => throwError(err))
@@ -114,7 +109,7 @@ export class NodesApiService {
* @param options Optional parameters supported by JS-API
* @returns Details of the new folder
*/
createFolder(parentNodeId: string, nodeBody: any, options: any = {}): Observable<MinimalNode> {
createFolder(parentNodeId: string, nodeBody: any, options: any = {}): Observable<Node> {
const body = Object.assign({ nodeType: 'cm:folder' }, nodeBody);
return this.createNode(parentNodeId, body, options);
}
@@ -127,7 +122,7 @@ export class NodesApiService {
* @param options Optional parameters supported by JS-API
* @returns Updated node information
*/
updateNode(nodeId: string, nodeBody: any, options: any = {}): Observable<MinimalNode> {
updateNode(nodeId: string, nodeBody: any, options: any = {}): Observable<Node> {
const defaults = {
include: ['path', 'properties', 'allowableOperations', 'permissions', 'definition']
};
@@ -147,9 +142,7 @@ export class NodesApiService {
* @returns Empty result that notifies when the deletion is complete
*/
deleteNode(nodeId: string, options: any = {}): Observable<any> {
return from(this.nodesApi.deleteNode(nodeId, options)).pipe(
catchError((err) => throwError(err))
);
return from(this.nodesApi.deleteNode(nodeId, options)).pipe(catchError((err) => throwError(err)));
}
/**
@@ -158,7 +151,7 @@ export class NodesApiService {
* @param nodeId ID of the node to restore
* @returns Details of the restored node
*/
restoreNode(nodeId: string): Observable<MinimalNode> {
restoreNode(nodeId: string): Observable<Node> {
return from(this.trashcanApi.restoreDeletedNode(nodeId)).pipe(
map(this.getEntryFromEntity),
catchError((err) => throwError(err))
@@ -172,8 +165,7 @@ export class NodesApiService {
* @returns Node metadata
*/
public getNodeMetadata(nodeId: string): Observable<NodeMetadata> {
return from(this.nodesApi.getNode(nodeId))
.pipe(map(this.cleanMetadataFromSemicolon));
return from(this.nodesApi.getNode(nodeId)).pipe(map(this.cleanMetadataFromSemicolon));
}
/**
@@ -194,7 +186,7 @@ export class NodesApiService {
}
}
return this.createNodeInsideRoot(name || this.generateUuid(), nodeType, properties, path);
return this.createNodeInsideRoot(name || window.crypto.randomUUID(), nodeType, properties, path);
}
/**
@@ -204,10 +196,7 @@ export class NodesApiService {
* @returns Content data
*/
getNodeContent(nodeId: string): Observable<any> {
return from(this.nodesApi.getNodeContent(nodeId))
.pipe(
catchError((err) => throwError(err))
);
return from(this.nodesApi.getNodeContent(nodeId)).pipe(catchError((err) => throwError(err)));
}
/**
@@ -229,14 +218,6 @@ export class NodesApiService {
return from(this.nodesApi.createNode('-root-', body, {}));
}
private generateUuid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = Math.random() * 16 | 0;
const v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
private cleanMetadataFromSemicolon(nodeEntry: NodeEntry): NodeMetadata {
const metadata = {};
@@ -244,9 +225,9 @@ export class NodesApiService {
for (const key in nodeEntry.entry.properties) {
if (key) {
if (key.indexOf(':') !== -1) {
metadata [key.split(':')[1]] = nodeEntry.entry.properties[key];
metadata[key.split(':')[1]] = nodeEntry.entry.properties[key];
} else {
metadata [key] = nodeEntry.entry.properties[key];
metadata[key] = nodeEntry.entry.properties[key];
}
}
}
@@ -254,5 +235,4 @@ export class NodesApiService {
return new NodeMetadata(metadata, nodeEntry.entry.nodeType);
}
}

View File

@@ -19,7 +19,7 @@ import { Injectable } from '@angular/core';
import { from, Observable, throwError } from 'rxjs';
import { AlfrescoApiService, LogService } from '@alfresco/adf-core';
import {
MinimalNode,
Node,
SiteBodyCreate,
SiteEntry,
SiteGroupEntry,
@@ -38,16 +38,13 @@ import { catchError } from 'rxjs/operators';
providedIn: 'root'
})
export class SitesService {
private _sitesApi: SitesApi;
get sitesApi(): SitesApi {
this._sitesApi = this._sitesApi ?? new SitesApi(this.apiService.getInstance());
return this._sitesApi;
}
constructor(private apiService: AlfrescoApiService,
private logService: LogService) {
}
constructor(private apiService: AlfrescoApiService, private logService: LogService) {}
/**
* Create a site
@@ -56,10 +53,7 @@ export class SitesService {
* @returns site SiteEntry
*/
createSite(siteBody: SiteBodyCreate): Observable<SiteEntry> {
return from(this.sitesApi.createSite(siteBody))
.pipe(
catchError((err: any) => this.handleError(err))
);
return from(this.sitesApi.createSite(siteBody)).pipe(catchError((err: any) => this.handleError(err)));
}
/**
@@ -74,10 +68,7 @@ export class SitesService {
include: ['properties']
};
const queryOptions = Object.assign({}, defaultOptions, opts);
return from(this.sitesApi.listSites(queryOptions))
.pipe(
catchError((err: any) => this.handleError(err))
);
return from(this.sitesApi.listSites(queryOptions)).pipe(catchError((err: any) => this.handleError(err)));
}
/**
@@ -88,10 +79,7 @@ export class SitesService {
* @returns Information about the site
*/
getSite(siteId: string, opts?: any): Observable<SiteEntry | any> {
return from(this.sitesApi.getSite(siteId, opts))
.pipe(
catchError((err: any) => this.handleError(err))
);
return from(this.sitesApi.getSite(siteId, opts)).pipe(catchError((err: any) => this.handleError(err)));
}
/**
@@ -104,10 +92,7 @@ export class SitesService {
deleteSite(siteId: string, permanentFlag: boolean = true): Observable<any> {
const options: any = {};
options.permanent = permanentFlag;
return from(this.sitesApi.deleteSite(siteId, options))
.pipe(
catchError((err: any) => this.handleError(err))
);
return from(this.sitesApi.deleteSite(siteId, options)).pipe(catchError((err: any) => this.handleError(err)));
}
/**
@@ -148,13 +133,10 @@ export class SitesService {
* @param node Node to look for parent site
* @returns Site guid
*/
getSiteNameFromNodePath(node: MinimalNode): string {
getSiteNameFromNodePath(node: Node): string {
let siteName = '';
if (node.path && node.path.elements) {
const foundNode = node.path
.elements.find((pathNode: MinimalNode) =>
pathNode.nodeType === 'st:site' &&
pathNode.name !== 'Sites');
const foundNode = node.path.elements.find((pathNode) => pathNode.nodeType === 'st:site' && pathNode.name !== 'Sites');
siteName = foundNode ? foundNode.name : '';
}
return siteName.toLocaleLowerCase();
@@ -167,10 +149,7 @@ export class SitesService {
* @returns Site membership requests
*/
getSiteMembershipRequests(opts?: any): Observable<SiteMembershipRequestWithPersonPaging> {
return from(this.sitesApi.getSiteMembershipRequests(opts))
.pipe(
catchError((err: any) => this.handleError(err))
);
return from(this.sitesApi.getSiteMembershipRequests(opts)).pipe(catchError((err: any) => this.handleError(err)));
}
/**
@@ -182,10 +161,7 @@ export class SitesService {
* @return Observable<SiteMemberEntry>
*/
createSiteMembership(siteId: string, siteMembershipBodyCreate: SiteMembershipBodyCreate, opts?: any): Observable<SiteMemberEntry> {
return from(this.sitesApi.createSiteMembership(siteId, siteMembershipBodyCreate, opts))
.pipe(
catchError((err: any) => this.handleError(err))
);
return from(this.sitesApi.createSiteMembership(siteId, siteMembershipBodyCreate, opts)).pipe(catchError((err: any) => this.handleError(err)));
}
/**
@@ -197,11 +173,15 @@ export class SitesService {
* @param opts Optional parameters
* @return Observable<SiteMemberEntry>
*/
updateSiteMembership(siteId: string, personId: string, siteMembershipBodyUpdate: SiteMembershipBodyUpdate, opts?: any): Observable<SiteMemberEntry> {
return from(this.sitesApi.updateSiteMembership(siteId, personId, siteMembershipBodyUpdate, opts))
.pipe(
catchError((err: any) => this.handleError(err))
);
updateSiteMembership(
siteId: string,
personId: string,
siteMembershipBodyUpdate: SiteMembershipBodyUpdate,
opts?: any
): Observable<SiteMemberEntry> {
return from(this.sitesApi.updateSiteMembership(siteId, personId, siteMembershipBodyUpdate, opts)).pipe(
catchError((err: any) => this.handleError(err))
);
}
/**
@@ -212,10 +192,7 @@ export class SitesService {
* @return Null response notifying when the operation is complete
*/
deleteSiteMembership(siteId: string, personId: string): Observable<void> {
return from(this.sitesApi.deleteSiteMembership(siteId, personId))
.pipe(
catchError((err: any) => this.handleError(err))
);
return from(this.sitesApi.deleteSiteMembership(siteId, personId)).pipe(catchError((err: any) => this.handleError(err)));
}
/**
@@ -227,10 +204,7 @@ export class SitesService {
* @returns Null response notifying when the operation is complete
*/
approveSiteMembershipRequest(siteId: string, inviteeId: string, opts?: any): Observable<SiteMembershipRequestWithPersonPaging> {
return from(this.sitesApi.approveSiteMembershipRequest(siteId, inviteeId, opts))
.pipe(
catchError((err: any) => this.handleError(err))
);
return from(this.sitesApi.approveSiteMembershipRequest(siteId, inviteeId, opts)).pipe(catchError((err: any) => this.handleError(err)));
}
/**
@@ -242,10 +216,7 @@ export class SitesService {
* @returns Null response notifying when the operation is complete
*/
rejectSiteMembershipRequest(siteId: string, inviteeId: string, opts?: any): Observable<SiteMembershipRequestWithPersonPaging> {
return from(this.sitesApi.rejectSiteMembershipRequest(siteId, inviteeId, opts))
.pipe(
catchError((err: any) => this.handleError(err))
);
return from(this.sitesApi.rejectSiteMembershipRequest(siteId, inviteeId, opts)).pipe(catchError((err: any) => this.handleError(err)));
}
/**
@@ -256,10 +227,7 @@ export class SitesService {
* @returns Observable<SiteGroupPaging>
*/
listSiteGroups(siteId: string, opts?: any): Observable<SiteGroupPaging> {
return from(this.sitesApi.listSiteGroups(siteId, opts))
.pipe(
catchError((err: any) => this.handleError(err))
);
return from(this.sitesApi.listSiteGroups(siteId, opts)).pipe(catchError((err: any) => this.handleError(err)));
}
/**
@@ -270,10 +238,7 @@ export class SitesService {
* @returns Observable<SiteGroupEntry>
*/
createSiteGroupMembership(siteId: string, siteMembershipBodyCreate: SiteMembershipBodyCreate): Observable<SiteGroupEntry> {
return from(this.sitesApi.createSiteGroupMembership(siteId, siteMembershipBodyCreate))
.pipe(
catchError((err: any) => this.handleError(err))
);
return from(this.sitesApi.createSiteGroupMembership(siteId, siteMembershipBodyCreate)).pipe(catchError((err: any) => this.handleError(err)));
}
/**
@@ -284,10 +249,7 @@ export class SitesService {
* @return Observable<SiteGroupEntry>
*/
getSiteGroupMembership(siteId: string, groupId: string): Observable<SiteGroupEntry> {
return from(this.sitesApi.getSiteGroupMembership(siteId, groupId))
.pipe(
catchError((err: any) => this.handleError(err))
);
return from(this.sitesApi.getSiteGroupMembership(siteId, groupId)).pipe(catchError((err: any) => this.handleError(err)));
}
/**
@@ -299,10 +261,9 @@ export class SitesService {
* @return Observable<SiteGroupEntry>
*/
updateSiteGroupMembership(siteId: string, groupId: string, siteMembershipBodyUpdate: SiteMembershipBodyUpdate): Observable<SiteGroupEntry> {
return from(this.sitesApi.updateSiteGroupMembership(siteId, groupId, siteMembershipBodyUpdate))
.pipe(
catchError((err: any) => this.handleError(err))
);
return from(this.sitesApi.updateSiteGroupMembership(siteId, groupId, siteMembershipBodyUpdate)).pipe(
catchError((err: any) => this.handleError(err))
);
}
/**
@@ -313,10 +274,7 @@ export class SitesService {
* @return Observable<void>
*/
deleteSiteGroupMembership(siteId: string, groupId: string): Observable<void> {
return from(this.sitesApi.deleteSiteGroupMembership(siteId, groupId))
.pipe(
catchError((err: any) => this.handleError(err))
);
return from(this.sitesApi.deleteSiteGroupMembership(siteId, groupId)).pipe(catchError((err: any) => this.handleError(err)));
}
private handleError(error: any): Observable<never> {

View File

@@ -18,27 +18,10 @@
import { ComponentFixture, discardPeriodicTasks, fakeAsync, flush, TestBed, tick } from '@angular/core/testing';
import { DebugElement, SimpleChange } from '@angular/core';
import { By } from '@angular/platform-browser';
import {
Category,
CategoryPaging,
ClassesApi,
MinimalNode,
Node,
Tag,
TagBody,
TagEntry,
TagPaging,
TagPagingList
} from '@alfresco/js-api';
import { Category, CategoryPaging, ClassesApi, Node, Tag, TagBody, TagEntry, TagPaging, TagPagingList } from '@alfresco/js-api';
import { ContentMetadataComponent } from './content-metadata.component';
import { ContentMetadataService } from '../../services/content-metadata.service';
import {
AppConfigService,
CardViewBaseItemModel,
CardViewComponent,
LogService,
UpdateNotification
} from '@alfresco/adf-core';
import { AppConfigService, CardViewBaseItemModel, CardViewComponent, LogService, UpdateNotification } from '@alfresco/adf-core';
import { NodesApiService } from '../../../common/services/nodes-api.service';
import { EMPTY, of, throwError } from 'rxjs';
import { ContentTestingModule } from '../../../testing/content.testing.module';
@@ -86,7 +69,7 @@ describe('ContentMetadataComponent', () => {
const category1 = new Category({ id: 'test', name: 'testCat' });
const category2 = new Category({ id: 'test2', name: 'testCat2' });
const categoryPagingResponse: CategoryPaging = { list: { pagination: {}, entries: [ { entry: category1 }, { entry: category2 }]}};
const categoryPagingResponse: CategoryPaging = { list: { pagination: {}, entries: [{ entry: category1 }, { entry: category2 }] } };
const findTagElements = (): DebugElement[] => fixture.debugElement.queryAll(By.css('.adf-metadata-properties-tag'));
@@ -123,8 +106,8 @@ describe('ContentMetadataComponent', () => {
async function updateAspectProperty(newValue: string): Promise<void> {
component.editable = true;
const property = {key: 'properties.property-key', value: 'original-value'} as CardViewBaseItemModel;
const expectedNode = {...node, name: 'some-modified-value'};
const property = { key: 'properties.property-key', value: 'original-value' } as CardViewBaseItemModel;
const expectedNode = { ...node, name: 'some-modified-value' };
spyOn(nodesApiService, 'updateNode').and.returnValue(of(expectedNode));
updateService.update(property, newValue);
@@ -139,10 +122,7 @@ describe('ContentMetadataComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ContentTestingModule
],
imports: [TranslateModule.forRoot(), ContentTestingModule],
providers: [
{
provide: LogService,
@@ -247,7 +227,7 @@ describe('ContentMetadataComponent', () => {
}));
it('nodeAspectUpdate', fakeAsync(() => {
const fakeNode = { id: 'fake-minimal-node', aspectNames: ['ft:a', 'ft:b', 'ft:c'], name: 'fake-node'} as MinimalNode;
const fakeNode = { id: 'fake-minimal-node', aspectNames: ['ft:a', 'ft:b', 'ft:c'], name: 'fake-node' } as Node;
spyOn(contentMetadataService, 'getGroupedProperties').and.stub();
spyOn(contentMetadataService, 'getBasicProperties').and.stub();
updateService.updateNodeAspect(fakeNode);
@@ -265,13 +245,13 @@ describe('ContentMetadataComponent', () => {
}));
it('should save changedProperties which delete property and update node on save click', fakeAsync(async () => {
const expectedNode = {...node, name: 'some-modified-value'};
const expectedNode = { ...node, name: 'some-modified-value' };
await updateAspectProperty('');
expect(component.node).toEqual({...expectedNode, properties: {}});
expect(component.node).toEqual({ ...expectedNode, properties: {} });
expect(nodesApiService.updateNode).toHaveBeenCalled();
}));
it('should call removeTag and assignTagsToNode on TagService on save click', fakeAsync( () => {
it('should call removeTag and assignTagsToNode on TagService on save click', fakeAsync(() => {
component.editable = true;
component.displayTags = true;
const property = { key: 'properties.property-key', value: 'original-value' } as CardViewBaseItemModel;
@@ -300,7 +280,7 @@ describe('ContentMetadataComponent', () => {
expect(tagService.assignTagsToNode).toHaveBeenCalledWith(node.id, [tag1, tag2]);
}));
it('should call getTagsByNodeId on TagService on save click', fakeAsync( () => {
it('should call getTagsByNodeId on TagService on save click', fakeAsync(() => {
component.editable = true;
component.displayTags = true;
const property = { key: 'properties.property-key', value: 'original-value' } as CardViewBaseItemModel;
@@ -361,7 +341,7 @@ describe('ContentMetadataComponent', () => {
tick(100);
expect(component.node).toEqual(expectedNode);
expect(contentMetadataService.openConfirmDialog).toHaveBeenCalledWith({nodeType: 'ft:poppoli'});
expect(contentMetadataService.openConfirmDialog).toHaveBeenCalledWith({ nodeType: 'ft:poppoli' });
expect(nodesApiService.updateNode).toHaveBeenCalled();
discardPeriodicTasks();
}));
@@ -445,7 +425,7 @@ describe('ContentMetadataComponent', () => {
});
describe('Properties loading', () => {
let expectedNode: MinimalNode;
let expectedNode: Node;
beforeEach(() => {
expectedNode = { ...node, name: 'some-modified-value' };
@@ -537,7 +517,9 @@ describe('ContentMetadataComponent', () => {
fixture.detectChanges();
await fixture.whenStable();
const firstGroupedPropertiesComponent = fixture.debugElement.query(By.css('.adf-metadata-grouped-properties-container adf-card-view')).componentInstance;
const firstGroupedPropertiesComponent = fixture.debugElement.query(
By.css('.adf-metadata-grouped-properties-container adf-card-view')
).componentInstance;
expect(firstGroupedPropertiesComponent.properties).toBe(expectedProperties);
});
@@ -553,7 +535,9 @@ describe('ContentMetadataComponent', () => {
fixture.detectChanges();
await fixture.whenStable();
const basicPropertiesComponent = fixture.debugElement.query(By.css('.adf-metadata-grouped-properties-container adf-card-view')).componentInstance;
const basicPropertiesComponent = fixture.debugElement.query(
By.css('.adf-metadata-grouped-properties-container adf-card-view')
).componentInstance;
expect(basicPropertiesComponent.displayEmpty).toBe(false);
});
@@ -575,14 +559,17 @@ describe('ContentMetadataComponent', () => {
component.expanded = true;
const cardViewGroup = {
title: 'Group 1', properties: [{
data: null,
default: null,
displayValue: 'DefaultName',
icon: '',
key: 'properties.cm:default',
label: 'To'
}]
title: 'Group 1',
properties: [
{
data: null,
default: null,
displayValue: 'DefaultName',
icon: '',
key: 'properties.cm:default',
label: 'To'
}
]
};
spyOn(contentMetadataService, 'getGroupedProperties').and.returnValue(of([{ properties: [cardViewGroup] } as any]));
@@ -618,11 +605,10 @@ describe('ContentMetadataComponent', () => {
});
});
describe('Display properties with aspect oriented config', () => {
let appConfig: AppConfigService;
let classesApi: ClassesApi;
let expectedNode: MinimalNode;
let expectedNode: Node;
const versionableResponse: PropertyGroup = {
name: 'cm:versionable',
@@ -697,21 +683,11 @@ describe('ContentMetadataComponent', () => {
beforeEach(() => {
appConfig = TestBed.inject(AppConfigService);
const propertyDescriptorsService = TestBed.inject(
PropertyDescriptorsService
);
const propertyDescriptorsService = TestBed.inject(PropertyDescriptorsService);
classesApi = propertyDescriptorsService['classesApi'];
expectedNode = {
...node,
aspectNames: [
'rn:renditioned',
'cm:versionable',
'cm:titled',
'cm:auditable',
'cm:author',
'cm:thumbnailModification',
'exif:exif'
],
aspectNames: ['rn:renditioned', 'cm:versionable', 'cm:titled', 'cm:auditable', 'cm:author', 'cm:thumbnailModification', 'exif:exif'],
name: 'some-modified-value',
properties: {
'exif:pixelXDimension': 1024,
@@ -849,11 +825,15 @@ describe('ContentMetadataComponent', () => {
exifProp.nativeElement.click();
const pixelXDimentionElement = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-label-properties.exif:pixelXDimension"]'));
const pixelXDimentionElement = fixture.debugElement.query(
By.css('[data-automation-id="card-textitem-label-properties.exif:pixelXDimension"]')
);
expect(pixelXDimentionElement).toBeTruthy();
expect(pixelXDimentionElement.nativeElement.textContent.trim()).toEqual('Image Width');
const pixelYDimentionElement = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-label-properties.exif:pixelYDimension"]'));
const pixelYDimentionElement = fixture.debugElement.query(
By.css('[data-automation-id="card-textitem-label-properties.exif:pixelYDimension"]')
);
expect(pixelYDimentionElement).toBeTruthy();
expect(pixelYDimentionElement.nativeElement.textContent.trim()).toEqual('Image Height');
});
@@ -880,7 +860,7 @@ describe('ContentMetadataComponent', () => {
});
describe('Expand the panel', () => {
let expectedNode: MinimalNode;
let expectedNode: Node;
beforeEach(() => {
expectedNode = { ...node, name: 'some-modified-value' };
@@ -948,7 +928,7 @@ describe('ContentMetadataComponent', () => {
describe('events', () => {
it('should not propagate the event on left arrows press', () => {
fixture.detectChanges();
const event = { keyCode: 37, stopPropagation: () => { } };
const event = { keyCode: 37, stopPropagation: () => {} };
spyOn(event, 'stopPropagation').and.stub();
const element = fixture.debugElement.query(By.css('adf-card-view'));
element.triggerEventHandler('keydown', event);
@@ -957,7 +937,7 @@ describe('ContentMetadataComponent', () => {
it('should not propagate the event on right arrows press', () => {
fixture.detectChanges();
const event = { keyCode: 39, stopPropagation: () => { } };
const event = { keyCode: 39, stopPropagation: () => {} };
spyOn(event, 'stopPropagation').and.stub();
const element = fixture.debugElement.query(By.css('adf-card-view'));
element.triggerEventHandler('keydown', event);
@@ -966,7 +946,7 @@ describe('ContentMetadataComponent', () => {
it('should propagate the event on other keys press', () => {
fixture.detectChanges();
const event = { keyCode: 40, stopPropagation: () => { } };
const event = { keyCode: 40, stopPropagation: () => {} };
spyOn(event, 'stopPropagation').and.stub();
const element = fixture.debugElement.query(By.css('adf-card-view'));
element.triggerEventHandler('keydown', event);
@@ -1141,7 +1121,7 @@ describe('ContentMetadataComponent', () => {
expect(tagsCreator.disabledTagsRemoving).toBeTrue();
});
it('should have assigned false to disabledTagsRemoving if forkJoin fails', fakeAsync( () => {
it('should have assigned false to disabledTagsRemoving if forkJoin fails', fakeAsync(() => {
const property = { key: 'properties.property-key', value: 'original-value' } as CardViewBaseItemModel;
const expectedNode = { ...node, name: 'some-modified-value' };
spyOn(nodesApiService, 'updateNode').and.returnValue(of(expectedNode));
@@ -1234,7 +1214,7 @@ describe('ContentMetadataComponent', () => {
});
it('should render categories when ngOnChanges', () => {
component.ngOnChanges({ node: new SimpleChange(undefined, node, false)});
component.ngOnChanges({ node: new SimpleChange(undefined, node, false) });
fixture.detectChanges();
const categories = getCategories();
@@ -1256,7 +1236,6 @@ describe('ContentMetadataComponent', () => {
});
it('should not reload categories in ngOnChanges if node is not changed', () => {
component.ngOnChanges({});
fixture.detectChanges();
@@ -1330,12 +1309,12 @@ describe('ContentMetadataComponent', () => {
it('should clear categories and emit event when classifiable changes', (done) => {
component.node.aspectNames = [];
component.ngOnChanges({ node: new SimpleChange(undefined, node, false)});
component.ngOnChanges({ node: new SimpleChange(undefined, node, false) });
component.classifiableChanged.subscribe(() => {
expect(component.categories).toEqual([]);
done();
});
component.ngOnChanges({ node: new SimpleChange(undefined, node, false)});
component.ngOnChanges({ node: new SimpleChange(undefined, node, false) });
});
it('should enable discard and save buttons after emitting categories change event', () => {
@@ -1357,7 +1336,7 @@ describe('ContentMetadataComponent', () => {
expect(categoriesManagementComponent.disableRemoval).toBeTrue();
});
it('should not disable removal if forkJoin fails', fakeAsync( () => {
it('should not disable removal if forkJoin fails', fakeAsync(() => {
const property = { key: 'properties.property-key', value: 'original-value' } as CardViewBaseItemModel;
const expectedNode = { ...node, name: 'some-modified-value' };
spyOn(nodesApiService, 'updateNode').and.returnValue(of(expectedNode));
@@ -1393,15 +1372,15 @@ describe('ContentMetadataComponent', () => {
component.ngOnInit();
fixture.detectChanges();
expect(categoriesManagementComponent.categories).toEqual([ category1, category2 ]);
expect(categoriesManagementComponent.categories).toEqual([category1, category2]);
expect(categoryService.getCategoryLinksForNode).toHaveBeenCalledWith(node.id);
});
it('should set correct tags after ngOnChanges', () => {
component.ngOnChanges({ node: new SimpleChange(undefined, node, false)});
component.ngOnChanges({ node: new SimpleChange(undefined, node, false) });
fixture.detectChanges();
expect(categoriesManagementComponent.categories).toEqual([ category1, category2 ]);
expect(categoriesManagementComponent.categories).toEqual([category1, category2]);
expect(categoryService.getCategoryLinksForNode).toHaveBeenCalledWith(node.id);
});
});

View File

@@ -18,16 +18,7 @@
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ComponentFixture, fakeAsync, flush, TestBed, tick } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import {
MinimalNode,
Node,
NodeEntry,
NodePaging,
RequestScope,
ResultSetPaging,
SiteEntry,
SitePaging
} from '@alfresco/js-api';
import { Node, NodeEntry, NodePaging, RequestScope, ResultSetPaging, SiteEntry, SitePaging } from '@alfresco/js-api';
import { of } from 'rxjs';
import { ContentNodeSelectorPanelComponent } from './content-node-selector-panel.component';
import { ContentTestingModule } from '../testing/content.testing.module';
@@ -83,17 +74,13 @@ describe('ContentNodeSelectorPanelComponent', () => {
};
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ContentTestingModule
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
});
TestBed.configureTestingModule({
imports: [TranslateModule.forRoot(), ContentTestingModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
});
});
describe('General component features', () => {
beforeEach(async () => {
fixture = TestBed.createComponent(ContentNodeSelectorPanelComponent);
component = fixture.componentInstance;
@@ -105,10 +92,14 @@ describe('ContentNodeSelectorPanelComponent', () => {
searchQueryBuilderService = component.queryBuilderService;
component.queryBuilderService.resetToDefaults();
spyOn(nodeService, 'getNode').and.returnValue(of(new MinimalNode({
id: 'fake-node',
path: { elements: [{ nodeType: 'st:site', name: 'fake-site' }] }
})));
spyOn(nodeService, 'getNode').and.returnValue(
of(
new Node({
id: 'fake-node',
path: { elements: [{ nodeType: 'st:site', name: 'fake-site' }] }
})
)
);
searchSpy = spyOn(searchQueryBuilderService, 'execute');
const fakeSite = new SiteEntry({
entry: {
@@ -127,7 +118,7 @@ describe('ContentNodeSelectorPanelComponent', () => {
});
describe('Search functionality', () => {
let getCorrespondingNodeIdsSpy;
let getCorrespondingNodeIdsSpy: jasmine.Spy;
let customResourcesService: CustomResourcesService;
const entry: Node = { id: 'fakeid' } as Node;
@@ -137,24 +128,27 @@ describe('ContentNodeSelectorPanelComponent', () => {
component.isSelectionValid = (node: Node) => node.isFile;
spyOn(documentListService, 'getFolderNode').and.returnValue(of(expectedDefaultFolderNode));
spyOn(documentListService, 'getFolder').and.returnValue(of(new NodePaging({
list: {
pagination: {},
entries: [],
source: {}
}
})));
spyOn(documentListService, 'getFolder').and.returnValue(
of(
new NodePaging({
list: {
pagination: {},
entries: [],
source: {}
}
})
)
);
spyOn(sitesService, 'getSites').and.returnValue(of(new SitePaging({ list: { entries: [] } })));
customResourcesService = TestBed.inject(CustomResourcesService);
getCorrespondingNodeIdsSpy = spyOn(customResourcesService, 'getCorrespondingNodeIds').and
.callFake((id) => {
if (id === '-sites-') {
return of(['123456testId', '09876543testId']);
}
return of([id]);
});
getCorrespondingNodeIdsSpy = spyOn(customResourcesService, 'getCorrespondingNodeIds').and.callFake((id) => {
if (id === '-sites-') {
return of(['123456testId', '09876543testId']);
}
return of([id]);
});
component.currentFolderId = 'cat-girl-nuku-nuku';
component.documentList.ngOnInit();
@@ -259,14 +253,14 @@ describe('ContentNodeSelectorPanelComponent', () => {
tick(debounceSearch);
expect(searchSpy.calls.count()).toBe(1, 'Search count should be one after only one search');
expect(searchSpy.calls.count()).toBe(1);
component.siteChanged({ entry: { guid: 'namek' } } as SiteEntry);
const expectedQueryBody = mockQueryBody;
expectedQueryBody.filterQueries = [{ query: `ANCESTOR:'workspace://SpacesStore/namek'` }];
expect(searchSpy.calls.count()).toBe(2, 'Search count should be two after the site change');
expect(searchSpy.calls.count()).toBe(2);
expect(searchSpy).toHaveBeenCalledWith(expectedQueryBody);
}));
@@ -285,7 +279,9 @@ describe('ContentNodeSelectorPanelComponent', () => {
const expectedQueryBodyWithSiteChange = mockQueryBody;
expectedQueryBodyWithSiteChange.filterQueries = [
{ query: `ANCESTOR:'workspace://SpacesStore/-sites-' OR ANCESTOR:'workspace://SpacesStore/123456testId' OR ANCESTOR:'workspace://SpacesStore/09876543testId'` }
{
query: `ANCESTOR:'workspace://SpacesStore/-sites-' OR ANCESTOR:'workspace://SpacesStore/123456testId' OR ANCESTOR:'workspace://SpacesStore/09876543testId'`
}
];
expect(searchSpy).toHaveBeenCalled();
@@ -302,7 +298,7 @@ describe('ContentNodeSelectorPanelComponent', () => {
tick(debounceSearch);
component.siteChanged({ entry: { guid: '-sites-' } } as SiteEntry);
expect(getCorrespondingNodeIdsSpy.calls.count()).toBe(1, 'getCorrespondingNodeIdsSpy calls count should be one after the site changes to known alias \'-sites\-');
expect(getCorrespondingNodeIdsSpy.calls.count()).toBe(1);
expect(getCorrespondingNodeIdsSpy.calls.mostRecent().args[0]).toEqual('-sites-');
}));
@@ -329,7 +325,7 @@ describe('ContentNodeSelectorPanelComponent', () => {
tick(debounceSearch);
expect(getCorrespondingNodeIdsSpy.calls.count()).toBe(0, 'getCorrespondingNodeIdsSpy calls count should be 0 when no site is selected');
expect(getCorrespondingNodeIdsSpy.calls.count()).toBe(0);
}));
it('should NOT get the corresponding node ids on search when NO known alias is selected from dropdown', fakeAsync(() => {
@@ -350,7 +346,7 @@ describe('ContentNodeSelectorPanelComponent', () => {
typeToSearchBox('vegeta');
tick(debounceSearch);
expect(getCorrespondingNodeIdsSpy.calls.count()).toBe(0, 'getCorrespondingNodeIdsSpy should not be called');
expect(getCorrespondingNodeIdsSpy.calls.count()).toBe(0);
component.siteChanged({ entry: { guid: 'namek' } } as SiteEntry);
@@ -364,8 +360,8 @@ describe('ContentNodeSelectorPanelComponent', () => {
const searchIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-icon"]'));
const clearIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-clear"]'));
expect(searchIcon).not.toBeNull('Search icon should be in the DOM');
expect(clearIcon).toBeNull('Clear icon should NOT be in the DOM');
expect(searchIcon).not.toBeNull();
expect(clearIcon).toBeNull();
}));
it('should show the X (clear) icon without the search icon when the search contains at least one character', fakeAsync(() => {
@@ -378,8 +374,8 @@ describe('ContentNodeSelectorPanelComponent', () => {
const searchIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-icon"]'));
const clearIcon = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-clear"]'));
expect(searchIcon).toBeNull('Search icon should NOT be in the DOM');
expect(clearIcon).not.toBeNull('Clear icon should be in the DOM');
expect(searchIcon).toBeNull();
expect(clearIcon).not.toBeNull();
}));
it('should clear the search field, nodes and chosenNode when clicking on the X (clear) icon', async () => {
@@ -420,7 +416,7 @@ describe('ContentNodeSelectorPanelComponent', () => {
spyOn(customResourcesService, 'hasCorrespondingNodeIds').and.returnValue(true);
const showingSearchSpy = spyOn(component.showingSearch, 'emit');
component.queryBuilderService.execute({ query: { query: 'search' } });
await component.queryBuilderService.execute({ query: { query: 'search' } });
triggerSearchResults(fakeResultSetPaging);
fixture.detectChanges();
@@ -464,7 +460,7 @@ describe('ContentNodeSelectorPanelComponent', () => {
searchQueryBuilderService.update();
getCorrespondingNodeIdsSpy.and.throwError('Failed');
const showingSearchSpy = spyOn(component.showingSearch, 'emit');
component.queryBuilderService.execute({ query: { query: 'search' } });
await component.queryBuilderService.execute({ query: { query: 'search' } });
triggerSearchResults(fakeResultSetPaging);
fixture.detectChanges();
@@ -481,9 +477,7 @@ describe('ContentNodeSelectorPanelComponent', () => {
component.siteChanged({ entry: { guid: 'my-site-id' } } as SiteEntry);
const expectedQueryBodyWithSiteChange = mockQueryBody;
expectedQueryBodyWithSiteChange.filterQueries = [
{ query: `ANCESTOR:'workspace://SpacesStore/my-site-id'` }
];
expectedQueryBodyWithSiteChange.filterQueries = [{ query: `ANCESTOR:'workspace://SpacesStore/my-site-id'` }];
expect(searchSpy).toHaveBeenCalledWith(expectedQueryBodyWithSiteChange);
});
@@ -516,9 +510,9 @@ describe('ContentNodeSelectorPanelComponent', () => {
tick(debounceSearch);
fixture.detectChanges();
expect(searchSpy.calls.count()).toBe(1, 'no other search has been performed');
expect(searchSpy.calls.count()).toBe(1);
expect(component.clearSearch).toHaveBeenCalled();
expect(component.folderIdToShow).toBe('cat-girl-nuku-nuku', 'back to the folder in which the search was performed');
expect(component.folderIdToShow).toBe('cat-girl-nuku-nuku');
flush();
}));
@@ -558,38 +552,40 @@ describe('ContentNodeSelectorPanelComponent', () => {
expect(component.folderIdToShow).toBe('namek');
}));
it('should show the current folder\'s content instead of search results if search was not performed', async () => {
it('should show the current folder content instead of search results if search was not performed', async () => {
const documentList = fixture.debugElement.query(By.directive(DocumentListComponent));
expect(documentList).not.toBeNull('Document list should be shown');
expect(documentList).not.toBeNull();
expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku');
});
it('should pass through the rowFilter to the documentList', async () => {
const filter = (shareDataRow: ShareDataRow) =>
shareDataRow.node.entry.name === 'impossible-name';
const filter = (shareDataRow: ShareDataRow) => shareDataRow.node.entry.name === 'impossible-name';
component.rowFilter = filter;
fixture.detectChanges();
const documentList = fixture.debugElement.query(By.directive(DocumentListComponent));
expect(documentList).not.toBeNull('Document list should be shown');
expect(documentList.componentInstance.rowFilter({
node: {
entry: new Node({
name: 'impossible-name',
id: 'name'
})
}
}))
.toBe(filter({
expect(documentList).not.toBeNull();
expect(
documentList.componentInstance.rowFilter({
node: {
entry: new Node({
name: 'impossible-name',
id: 'name'
})
}
} as ShareDataRow));
})
).toBe(
filter({
node: {
entry: new Node({
name: 'impossible-name',
id: 'name'
})
}
} as ShareDataRow)
);
});
it('should pass through the excludeSiteContent to the rowFilter of the documentList', async () => {
@@ -598,12 +594,11 @@ describe('ContentNodeSelectorPanelComponent', () => {
fixture.detectChanges();
const documentList = fixture.debugElement.query(By.directive(DocumentListComponent));
expect(documentList).not.toBeNull('Document list should be shown');
expect(documentList.componentInstance.rowFilter).toBeTruthy('Document list should have had a rowFilter');
expect(documentList).not.toBeNull();
expect(documentList.componentInstance.rowFilter).toBeTruthy();
const testSiteContent = new Node({ id: 'blog-id', properties: { 'st:componentId': 'blog' } });
expect(documentList.componentInstance.rowFilter({ node: { entry: testSiteContent } }, null, null))
.toBe(false);
expect(documentList.componentInstance.rowFilter({ node: { entry: testSiteContent } }, null, null)).toBe(false);
});
it('should pass through the imageResolver to the documentList', async () => {
@@ -613,7 +608,7 @@ describe('ContentNodeSelectorPanelComponent', () => {
fixture.detectChanges();
const documentList = fixture.debugElement.query(By.directive(DocumentListComponent));
expect(documentList).not.toBeNull('Document list should be shown');
expect(documentList).not.toBeNull();
expect(documentList.componentInstance.imageResolver).toBe(resolver);
});
@@ -625,7 +620,7 @@ describe('ContentNodeSelectorPanelComponent', () => {
fixture.detectChanges();
const documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]'));
expect(documentList).not.toBeNull('Document list should be shown');
expect(documentList).not.toBeNull();
expect(component.hasValidQuery).toEqual(true);
expect(documentList.componentInstance.currentFolderId).toBeNull();
done();
@@ -670,12 +665,12 @@ describe('ContentNodeSelectorPanelComponent', () => {
fixture.whenStable().then(() => {
const clearButton = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-search-clear"]'));
expect(clearButton).not.toBeNull('Clear button should be in DOM');
expect(clearButton).not.toBeNull();
clearButton.triggerEventHandler('click', {});
fixture.detectChanges();
const documentList = fixture.debugElement.query(By.css('[data-automation-id="content-node-selector-document-list"]'));
expect(documentList).not.toBeNull('Document list should be shown');
expect(documentList).not.toBeNull();
expect(documentList.componentInstance.currentFolderId).toBe('cat-girl-nuku-nuku');
done();
});
@@ -714,7 +709,6 @@ describe('ContentNodeSelectorPanelComponent', () => {
});
describe('Pagination "Load more" button', () => {
it('should NOT be shown by default', () => {
fixture.detectChanges();
const pagination = fixture.debugElement.query(By.css('[data-automation-id="adf-infinite-pagination-button"]'));
@@ -745,7 +739,9 @@ describe('ContentNodeSelectorPanelComponent', () => {
fixture.detectChanges();
await fixture.whenStable();
const spinnerSelector = By.css('[data-automation-id="content-node-selector-search-pagination"] [data-automation-id="adf-infinite-pagination-spinner"]');
const spinnerSelector = By.css(
'[data-automation-id="content-node-selector-search-pagination"] [data-automation-id="adf-infinite-pagination-spinner"]'
);
const paginationLoading = fixture.debugElement.query(spinnerSelector);
expect(paginationLoading).not.toBeNull();
@@ -782,6 +778,5 @@ describe('ContentNodeSelectorPanelComponent', () => {
});
});
});
});
});

View File

@@ -18,22 +18,8 @@
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import {
MinimalNode,
Node,
NodeEntry,
NodePaging,
ResultSetPaging,
SiteEntry,
SitePaging,
UserInfo
} from '@alfresco/js-api';
import {
AppConfigService,
DataRow,
ThumbnailService,
DataColumn
} from '@alfresco/adf-core';
import { Node, NodeEntry, NodePaging, ResultSetPaging, SiteEntry, SitePaging, UserInfo } from '@alfresco/js-api';
import { AppConfigService, DataRow, ThumbnailService, DataColumn } from '@alfresco/adf-core';
import { ContentService } from '../common/services/content.service';
import { UploadService } from '../common/services/upload.service';
import { NodesApiService } from '../common/services/nodes-api.service';
@@ -90,17 +76,13 @@ describe('ContentNodeSelectorPanelComponent', () => {
};
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ContentTestingModule
],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
});
TestBed.configureTestingModule({
imports: [TranslateModule.forRoot(), ContentTestingModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
});
});
describe('General component features', () => {
beforeEach(() => {
fixture = TestBed.createComponent(ContentNodeSelectorPanelComponent);
component = fixture.componentInstance;
@@ -116,10 +98,14 @@ describe('ContentNodeSelectorPanelComponent', () => {
searchQueryBuilderService = component.queryBuilderService;
component.queryBuilderService.resetToDefaults();
spyOn(nodeService, 'getNode').and.returnValue(of(new MinimalNode({
id: 'fake-node',
path: { elements: [{ nodeType: 'st:site', name: 'fake-site' }] }
})));
spyOn(nodeService, 'getNode').and.returnValue(
of(
new Node({
id: 'fake-node',
path: { elements: [{ nodeType: 'st:site', name: 'fake-site' }] }
})
)
);
const fakeSite = new SiteEntry({
entry: {
id: 'fake-site',
@@ -136,7 +122,6 @@ describe('ContentNodeSelectorPanelComponent', () => {
});
describe('Site selection', () => {
beforeEach(() => {
spyOn(sitesService, 'getSites').and.returnValue(of(new SitePaging({ list: { entries: [] } })));
component.currentFolderId = 'fake-starting-folder';
@@ -167,7 +152,6 @@ describe('ContentNodeSelectorPanelComponent', () => {
});
describe('Parameters', () => {
let documentListService: DocumentListService;
beforeEach(() => {
@@ -175,14 +159,15 @@ describe('ContentNodeSelectorPanelComponent', () => {
spyOn(documentListService, 'getFolderNode').and.returnValue(of(new NodeEntry()));
spyOn(documentListService, 'getFolder').and.returnValue(throwError('No results for test'));
spyOn(sitesService, 'getSites').and.returnValue(of(new SitePaging({
list: {
entries: [
{ entry: { guid: 'namek', id: 'namek' } },
{ entry: { guid: 'blog', id: 'blog' } }
]
}
})));
spyOn(sitesService, 'getSites').and.returnValue(
of(
new SitePaging({
list: {
entries: [{ entry: { guid: 'namek', id: 'namek' } }, { entry: { guid: 'blog', id: 'blog' } }]
}
})
)
);
component.currentFolderId = 'cat-girl-nuku-nuku';
fixture.detectChanges();
@@ -207,8 +192,7 @@ describe('ContentNodeSelectorPanelComponent', () => {
fixture.detectChanges();
const testSiteContent = new Node({ id: 'blog-id', properties: { 'st:componentId': 'blog' } });
expect(component.rowFilter({ node: { entry: testSiteContent } } as any, null, null))
.toBe(false, 'did not filter out blog');
expect(component.rowFilter({ node: { entry: testSiteContent } } as any, null, null)).toBe(false, 'did not filter out blog');
});
it('should still be able to filter out the exclude site content after rowFilter changes', () => {
@@ -227,13 +211,17 @@ describe('ContentNodeSelectorPanelComponent', () => {
properties: { 'st:componentId': 'blog' },
isFile: true
});
expect(component.rowFilter({ node: { entry: testSiteContent } } as any, null, null))
.toBe(false, 'did not filter out blog with filterFunction1');
expect(component.rowFilter({ node: { entry: testSiteContent } } as any, null, null)).toBe(
false,
'did not filter out blog with filterFunction1'
);
component.rowFilter = filterFunction2;
fixture.detectChanges();
expect(component.rowFilter({ node: { entry: testSiteContent } } as any, null, null))
.toBe(false, 'did not filter out blog with filterFunction2');
expect(component.rowFilter({ node: { entry: testSiteContent } } as any, null, null)).toBe(
false,
'did not filter out blog with filterFunction2'
);
});
it('should NOT filter out any site content by default', () => {
@@ -245,33 +233,28 @@ describe('ContentNodeSelectorPanelComponent', () => {
it('should render search input by default', () => {
fixture.detectChanges();
expect(fixture.debugElement.nativeElement.querySelector('.adf-content-node-selector-content-input'))
.not.toBe(null);
expect(fixture.debugElement.nativeElement.querySelector('.adf-content-node-selector-content-input')).not.toBe(null);
});
it('should not render search input if `showSearch` is false', () => {
component.showSearch = false;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement.querySelector('.adf-content-node-selector-content-input'))
.toBe(null);
expect(fixture.debugElement.nativeElement.querySelector('.adf-content-node-selector-content-input')).toBe(null);
});
it('should render sites list dropdown by default', () => {
fixture.detectChanges();
expect(fixture.debugElement.nativeElement.querySelector('adf-sites-dropdown'))
.not.toBe(null);
expect(fixture.debugElement.nativeElement.querySelector('adf-sites-dropdown')).not.toBe(null);
});
it('should not render sites list dropdown if `showDropdownSiteList` is false', () => {
component.showDropdownSiteList = false;
fixture.detectChanges();
expect(fixture.debugElement.nativeElement.querySelector('adf-sites-dropdown'))
.toBe(null);
expect(fixture.debugElement.nativeElement.querySelector('adf-sites-dropdown')).toBe(null);
});
});
describe('Breadcrumbs', () => {
let documentListService: DocumentListService;
beforeEach(() => {
@@ -292,7 +275,6 @@ describe('ContentNodeSelectorPanelComponent', () => {
const breadcrumb = fixture.debugElement.query(By.directive(DropdownBreadcrumbComponent));
expect(breadcrumb).not.toBeNull();
expect(breadcrumb.componentInstance.folderNode).toEqual(undefined);
});
it('should not show the breadcrumb if search was performed as last action', async () => {
@@ -314,7 +296,6 @@ describe('ContentNodeSelectorPanelComponent', () => {
fixture.detectChanges();
const breadcrumb = fixture.debugElement.query(By.directive(DropdownBreadcrumbComponent));
expect(breadcrumb).not.toBeNull();
});
it('should show the breadcrumb in search results for a valid node selection', async () => {
@@ -377,7 +358,7 @@ describe('ContentNodeSelectorPanelComponent', () => {
name: 'trans-node-name',
path: { elements: [{ id: 'testId', name: 'testName' }] }
};
component.breadcrumbTransform = (() => transformedFolderNode);
component.breadcrumbTransform = () => transformedFolderNode;
fixture.detectChanges();
await fixture.whenStable();
@@ -391,7 +372,6 @@ describe('ContentNodeSelectorPanelComponent', () => {
});
describe('Chosen node', () => {
const entry: Node = { id: 'fakeid' } as Node;
const nodePage: NodePaging = { list: { pagination: {} } };
let hasAllowableOperations;
@@ -464,7 +444,6 @@ describe('ContentNodeSelectorPanelComponent', () => {
});
describe('in the case when isSelectionValid is a custom function for checking permissions,', () => {
beforeEach(() => {
component.isSelectionValid = returnHasPermission;
});
@@ -528,7 +507,6 @@ describe('ContentNodeSelectorPanelComponent', () => {
});
describe('in the case when isSelectionValid is null', () => {
beforeEach(() => {
component.isSelectionValid = null;
});
@@ -579,7 +557,6 @@ describe('ContentNodeSelectorPanelComponent', () => {
});
describe('in the case when isSelectionValid is not defined', () => {
beforeEach(() => {
component.isSelectionValid = undefined;
});
@@ -632,10 +609,13 @@ describe('ContentNodeSelectorPanelComponent', () => {
const isUploadingSpy = spyOn(uploadService, 'isUploading').and.returnValue(true);
const documentListReloadSpy = spyOn(component.documentList, 'reloadWithoutResettingSelection');
const fakeFileModels = [new FileModel({
name: 'fake-name',
size: 100
} as File), new FileModel({ name: 'fake-name-2', size: 200 } as File)];
const fakeFileModels = [
new FileModel({
name: 'fake-name',
size: 100
} as File),
new FileModel({ name: 'fake-name-2', size: 200 } as File)
];
const fileUploadCompleteEvent = new FileUploadCompleteEvent(fakeFileModels[0], 1, fakeFileModels[0], 0);
uploadService.fileUploadComplete.next(fileUploadCompleteEvent);
@@ -698,11 +678,9 @@ describe('ContentNodeSelectorPanelComponent', () => {
expect(component.getPreselectNodesBasedOnSelectionMode()).toEqual([]);
});
});
});
describe('Search panel', () => {
beforeEach(() => {
contentNodeSelectorPanelService.customModels = undefined;
});

View File

@@ -17,7 +17,7 @@
import { Component, Inject, OnInit, ViewEncapsulation } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { DownloadEntry, MinimalNode } from '@alfresco/js-api';
import { DownloadEntry } from '@alfresco/js-api';
import { LogService } from '@alfresco/adf-core';
import { NodesApiService } from '../../common/services/nodes-api.service';
import { DownloadZipService } from './services/download-zip.service';
@@ -31,19 +31,19 @@ import { ContentService } from '../../common/services/content.service';
encapsulation: ViewEncapsulation.None
})
export class DownloadZipDialogComponent implements OnInit {
// flag for async threads
cancelled = false;
downloadId: string;
constructor(private dialogRef: MatDialogRef<DownloadZipDialogComponent>,
@Inject(MAT_DIALOG_DATA)
public data: any,
private logService: LogService,
private downloadZipService: DownloadZipService,
private nodeService: NodesApiService,
private contentService: ContentService) {
}
constructor(
private dialogRef: MatDialogRef<DownloadZipDialogComponent>,
@Inject(MAT_DIALOG_DATA)
public data: any,
private logService: LogService,
private downloadZipService: DownloadZipService,
private nodeService: NodesApiService,
private contentService: ContentService
) {}
ngOnInit() {
if (this.data && this.data.nodeIds && this.data.nodeIds.length > 0) {
@@ -63,12 +63,11 @@ export class DownloadZipDialogComponent implements OnInit {
downloadZip(nodeIds: string[]) {
if (nodeIds && nodeIds.length > 0) {
this.downloadZipService.createDownload({ nodeIds }).subscribe((data: DownloadEntry) => {
if (data && data.entry && data.entry.id) {
const url = this.contentService.getContentUrl(data.entry.id, true);
this.nodeService.getNode(data.entry.id).subscribe((downloadNode: MinimalNode) => {
this.nodeService.getNode(data.entry.id).subscribe((downloadNode) => {
this.logService.log(downloadNode);
const fileName = downloadNode.name;
this.downloadId = data.entry.id;

View File

@@ -16,7 +16,7 @@
*/
import { Directive, HostListener, Input, OnChanges, Output, EventEmitter } from '@angular/core';
import { SiteBody, FavoriteBody, FavoriteEntry, FavoritesApi } from '@alfresco/js-api';
import { FavoriteBodyCreate, FavoritesApi } from '@alfresco/js-api';
import { AlfrescoApiService } from '@alfresco/adf-core';
import { LibraryEntity } from '../interfaces/library-entity.interface';
@@ -34,7 +34,7 @@ export class LibraryFavoriteDirective implements OnChanges {
private targetLibrary = null;
_favoritesApi: FavoritesApi;
private _favoritesApi: FavoritesApi;
get favoritesApi(): FavoritesApi {
this._favoritesApi = this._favoritesApi ?? new FavoritesApi(this.alfrescoApiService.getInstance());
return this._favoritesApi;
@@ -57,8 +57,7 @@ export class LibraryFavoriteDirective implements OnChanges {
}
}
constructor(private alfrescoApiService: AlfrescoApiService) {
}
constructor(private alfrescoApiService: AlfrescoApiService) {}
ngOnChanges(changes) {
if (!changes.library.currentValue) {
@@ -87,10 +86,10 @@ export class LibraryFavoriteDirective implements OnChanges {
}
}
private addFavorite(favoriteBody: FavoriteBody) {
private addFavorite(favoriteBody: FavoriteBodyCreate) {
this.favoritesApi
.createFavorite('-me-', favoriteBody)
.then((libraryEntry: FavoriteEntry) => {
.then((libraryEntry) => {
this.targetLibrary.isFavorite = true;
this.toggle.emit(libraryEntry);
})
@@ -100,7 +99,7 @@ export class LibraryFavoriteDirective implements OnChanges {
private removeFavorite(favoriteId: string) {
this.favoritesApi
.deleteFavorite('-me-', favoriteId)
.then((libraryBody: SiteBody) => {
.then((libraryBody) => {
this.targetLibrary.isFavorite = false;
this.toggle.emit(libraryBody);
})

View File

@@ -17,8 +17,8 @@
/* eslint-disable @angular-eslint/no-input-rename */
import { Directive, EventEmitter, HostListener, Input, OnChanges, Output } from '@angular/core';
import { FavoriteBody, NodeEntry, SharedLinkEntry, Node, SharedLink, FavoritesApi } from '@alfresco/js-api';
import { Directive, EventEmitter, HostListener, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { FavoriteBodyCreate, NodeEntry, SharedLinkEntry, Node, SharedLink, FavoritesApi } from '@alfresco/js-api';
import { Observable, from, forkJoin, of } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { AlfrescoApiService } from '@alfresco/adf-core';
@@ -30,7 +30,7 @@ import { AlfrescoApiService } from '@alfresco/adf-core';
export class NodeFavoriteDirective implements OnChanges {
favorites: any[] = [];
_favoritesApi: FavoritesApi;
private _favoritesApi: FavoritesApi;
get favoritesApi(): FavoritesApi {
this._favoritesApi = this._favoritesApi ?? new FavoritesApi(this.alfrescoApiService.getInstance());
return this._favoritesApi;
@@ -41,20 +41,19 @@ export class NodeFavoriteDirective implements OnChanges {
selection: NodeEntry[] = [];
/** Emitted when the favorite setting is complete. */
@Output() toggle: EventEmitter<any> = new EventEmitter();
@Output() toggle = new EventEmitter<any>();
/** Emitted when the favorite setting fails. */
@Output() error: EventEmitter<any> = new EventEmitter();
@Output() error = new EventEmitter<any>();
@HostListener('click')
onClick() {
this.toggleFavorite();
}
constructor(private alfrescoApiService: AlfrescoApiService) {
}
constructor(private alfrescoApiService: AlfrescoApiService) {}
ngOnChanges(changes) {
ngOnChanges(changes: SimpleChanges) {
if (!changes.selection.currentValue.length) {
this.favorites = [];
@@ -81,7 +80,7 @@ export class NodeFavoriteDirective implements OnChanges {
forkJoin(batch).subscribe(
() => {
this.favorites.map((selected) => selected.entry.isFavorite = false);
this.favorites.forEach((selected) => (selected.entry.isFavorite = false));
this.toggle.emit();
},
(error) => this.error.emit(error)
@@ -90,23 +89,21 @@ export class NodeFavoriteDirective implements OnChanges {
if (!every) {
const notFavorite = this.favorites.filter((node) => !node.entry.isFavorite);
const body: FavoriteBody[] = notFavorite.map((node) => this.createFavoriteBody(node));
const body = notFavorite.map((node) => this.createFavoriteBody(node));
from(this.favoritesApi.createFavorite('-me-', body as any))
.subscribe(
() => {
notFavorite.map((selected) => selected.entry.isFavorite = true);
this.toggle.emit();
},
(error) => this.error.emit(error)
);
from(this.favoritesApi.createFavorite('-me-', body as any)).subscribe(
() => {
notFavorite.forEach((selected) => (selected.entry.isFavorite = true));
this.toggle.emit();
},
(error) => this.error.emit(error)
);
}
}
markFavoritesNodes(selection: NodeEntry[]) {
if (selection.length <= this.favorites.length) {
const newFavorites = this.reduce(this.favorites, selection);
this.favorites = newFavorites;
this.favorites = this.reduce(this.favorites, selection);
}
const result = this.diff(selection, this.favorites);
@@ -125,8 +122,8 @@ export class NodeFavoriteDirective implements OnChanges {
return this.favorites.every((selected) => selected.entry.isFavorite);
}
private getProcessBatch(selection): any[] {
return selection.map((selected: NodeEntry) => this.getFavorite(selected));
private getProcessBatch(selection: NodeEntry[]): Observable<any>[] {
return selection.map((selected) => this.getFavorite(selected));
}
private getFavorite(selected: NodeEntry | SharedLinkEntry): Observable<any> {
@@ -153,22 +150,24 @@ export class NodeFavoriteDirective implements OnChanges {
isFavorite: true
}
})),
catchError(() => of({
entry: {
id,
isFolder,
isFile,
name,
isFavorite: false
}
}))
catchError(() =>
of({
entry: {
id,
isFolder,
isFile,
name,
isFavorite: false
}
})
)
);
}
private createFavoriteBody(node): FavoriteBody {
private createFavoriteBody(node: NodeEntry): FavoriteBodyCreate {
const type = this.getNodeType(node);
// shared files have nodeId
const id = node.entry.nodeId || node.entry.id;
const id = node.entry['nodeId'] || node.entry.id;
return {
target: {
@@ -179,7 +178,7 @@ export class NodeFavoriteDirective implements OnChanges {
};
}
private getNodeType(node): string {
private getNodeType(node: NodeEntry): string {
// shared could only be files
if (!node.entry.isFile && !node.entry.isFolder) {
return 'file';
@@ -188,15 +187,15 @@ export class NodeFavoriteDirective implements OnChanges {
return node.entry.isFile ? 'file' : 'folder';
}
private diff(list, patch): any[] {
private diff(list: NodeEntry[], patch: any[]): NodeEntry[] {
const ids = patch.map((item) => item.entry.id);
return list.filter((item) => ids.includes(item.entry.id) ? null : item);
return list.filter((item) => (ids.includes(item.entry.id) ? null : item));
}
private reduce(patch, comparator): any[] {
private reduce(patch: any[], comparator: NodeEntry[]): any[] {
const ids = comparator.map((item) => item.entry.id);
return patch.filter((item) => ids.includes(item.entry.id) ? item : null);
return patch.filter((item) => (ids.includes(item.entry.id) ? item : null));
}
}

View File

@@ -26,10 +26,10 @@ import {
DataTableModule,
ObjectDataTableAdapter,
ShowHeaderMode,
ThumbnailService, AppConfigService
ThumbnailService,
AppConfigService
} from '@alfresco/adf-core';
import { ContentService } from '../../common/services/content.service';
import { Subject, of, throwError } from 'rxjs';
import {
FileNode,
@@ -45,15 +45,13 @@ import {
mockNode3
} from '../../mock';
import { ContentActionModel } from '../models/content-action.model';
import { NodeMinimal, NodeMinimalEntry, NodePaging } from '../models/document-library.model';
import { ImageResolver } from './../data/image-resolver.model';
import { RowFilter } from './../data/row-filter.model';
import { DocumentListService } from './../services/document-list.service';
import { CustomResourcesService } from './../services/custom-resources.service';
import { DocumentListComponent } from './document-list.component';
import { ContentTestingModule } from '../../testing/content.testing.module';
import { FavoritePaging, NodeEntry } from '@alfresco/js-api';
import { FavoritePaging, NodeEntry, NodePaging, Node } from '@alfresco/js-api';
import { By } from '@angular/platform-browser';
import { DocumentListModule } from '../document-list.module';
import { TranslateModule } from '@ngx-translate/core';
@@ -69,7 +67,6 @@ const mockDialog = {
};
describe('DocumentList', () => {
let documentList: DocumentListComponent;
let documentListService: DocumentListService;
let apiService: AlfrescoApiService;
@@ -87,14 +84,9 @@ describe('DocumentList', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ContentTestingModule
],
imports: [TranslateModule.forRoot(), ContentTestingModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
providers: [
{ provide: MatDialog, useValue: mockDialog }
]
providers: [{ provide: MatDialog, useValue: mockDialog }]
});
eventMock = {
preventDefault: () => {}
@@ -120,7 +112,9 @@ describe('DocumentList', () => {
documentList.currentFolderId = 'no-node';
spyGetSites = spyOn(customResourcesService.sitesApi, 'listSites').and.returnValue(Promise.resolve(fakeGetSitesAnswer));
spyFavorite = spyOn(customResourcesService.favoritesApi, 'listFavorites').and.returnValue(Promise.resolve(new FavoritePaging({ list: { entries: [] } })));
spyFavorite = spyOn(customResourcesService.favoritesApi, 'listFavorites').and.returnValue(
Promise.resolve(new FavoritePaging({ list: { entries: [] } }))
);
});
afterEach(() => {
@@ -128,7 +122,6 @@ describe('DocumentList', () => {
});
describe('presets', () => {
const validatePreset = (keys: string[]) => {
const columns = documentList.data.getColumns();
expect(columns.length).toBe(keys.length);
@@ -144,14 +137,7 @@ describe('DocumentList', () => {
fixture.detectChanges();
await fixture.whenStable();
validatePreset([
'$thumbnail',
'name',
'path',
'content.sizeInBytes',
'archivedAt',
'archivedByUser.displayName'
]);
validatePreset(['$thumbnail', 'name', 'path', 'content.sizeInBytes', 'archivedAt', 'archivedByUser.displayName']);
});
it('should load -sites- preset', async () => {
@@ -160,11 +146,7 @@ describe('DocumentList', () => {
fixture.detectChanges();
await fixture.whenStable();
validatePreset([
'$thumbnail',
'title',
'visibility'
]);
validatePreset(['$thumbnail', 'title', 'visibility']);
});
it('shuld load -mysites- preset', async () => {
@@ -173,11 +155,7 @@ describe('DocumentList', () => {
fixture.detectChanges();
await fixture.whenStable();
validatePreset([
'$thumbnail',
'title',
'visibility'
]);
validatePreset(['$thumbnail', 'title', 'visibility']);
});
it('should load -favorites- preset', async () => {
@@ -186,14 +164,7 @@ describe('DocumentList', () => {
fixture.detectChanges();
await fixture.whenStable();
validatePreset([
'$thumbnail',
'name',
'path',
'content.sizeInBytes',
'modifiedAt',
'modifiedByUser.displayName'
]);
validatePreset(['$thumbnail', 'name', 'path', 'content.sizeInBytes', 'modifiedAt', 'modifiedByUser.displayName']);
});
it('should load -recent- preset', async () => {
@@ -202,13 +173,7 @@ describe('DocumentList', () => {
fixture.detectChanges();
await fixture.whenStable();
validatePreset([
'$thumbnail',
'name',
'path',
'content.sizeInBytes',
'modifiedAt'
]);
validatePreset(['$thumbnail', 'name', 'path', 'content.sizeInBytes', 'modifiedAt']);
});
it('should load -sharedlinks- preset', async () => {
@@ -234,13 +199,7 @@ describe('DocumentList', () => {
fixture.detectChanges();
await fixture.whenStable();
validatePreset([
'$thumbnail',
'name',
'content.sizeInBytes',
'modifiedAt',
'modifiedByUser.displayName'
]);
validatePreset(['$thumbnail', 'name', 'content.sizeInBytes', 'modifiedAt', 'modifiedByUser.displayName']);
});
});
@@ -330,8 +289,7 @@ describe('DocumentList', () => {
it('should call action handler with node', () => {
const node = new FileNode();
const action = new ContentActionModel();
action.handler = () => {
};
action.handler = () => {};
spyOn(action, 'handler').and.stub();
@@ -342,8 +300,7 @@ describe('DocumentList', () => {
it('should call action handler with node and permission', () => {
const node = new FileNode();
const action = new ContentActionModel();
action.handler = () => {
};
action.handler = () => {};
action.permission = 'fake-permission';
spyOn(action, 'handler').and.stub();
@@ -355,8 +312,7 @@ describe('DocumentList', () => {
it('should call action execute with node if it is defined', () => {
const node = new FileNode();
const action = new ContentActionModel();
action.execute = () => {
};
action.execute = () => {};
spyOn(action, 'execute').and.stub();
documentList.executeContentAction(node, action);
@@ -369,8 +325,7 @@ describe('DocumentList', () => {
const node = new FileNode();
const action = new ContentActionModel();
action.handler = () => deleteObservable;
action.execute = () => {
};
action.execute = () => {};
spyOn(action, 'execute').and.stub();
documentList.executeContentAction(node, action);
@@ -482,10 +437,7 @@ describe('DocumentList', () => {
const documentMenu = new ContentActionModel();
documentMenu.target = 'document';
documentList.actions = [
folderMenu,
documentMenu
];
documentList.actions = [folderMenu, documentMenu];
let actions = documentList.getNodeActions(new FolderNode());
expect(actions.length).toBe(1);
@@ -505,9 +457,7 @@ describe('DocumentList', () => {
title: 'FileAction'
});
documentList.actions = [
documentMenu
];
documentList.actions = [documentMenu];
const nodeFile = { entry: { isFile: true, name: 'xyz', allowableOperations: ['create', 'update'] } };
@@ -594,9 +544,7 @@ describe('DocumentList', () => {
title: 'FileAction'
});
documentList.actions = [
documentMenu
];
documentList.actions = [documentMenu];
const nodeFile = { entry: { isFile: true, name: 'xyz', allowableOperations: ['create', 'update'] } };
@@ -614,9 +562,7 @@ describe('DocumentList', () => {
title: 'FolderAction'
});
documentList.actions = [
documentMenu
];
documentList.actions = [documentMenu];
const nodeFile = { entry: { isFolder: true, name: 'xyz', allowableOperations: ['create', 'update'] } };
@@ -634,9 +580,7 @@ describe('DocumentList', () => {
title: 'FileAction'
});
documentList.actions = [
documentMenu
];
documentList.actions = [documentMenu];
const nodeFile = { entry: { isFile: true, name: 'xyz', allowableOperations: ['create', 'update', 'delete'] } };
@@ -653,9 +597,7 @@ describe('DocumentList', () => {
title: 'FileAction'
});
documentList.actions = [
documentMenu
];
documentList.actions = [documentMenu];
const nodeFile = {
entry: {
@@ -682,9 +624,7 @@ describe('DocumentList', () => {
spyOn(apiService.getInstance(), 'getEcmUsername').and.returnValue('lockOwner');
documentList.actions = [
documentMenu
];
documentList.actions = [documentMenu];
const nodeFile = {
entry: {
@@ -715,9 +655,7 @@ describe('DocumentList', () => {
spyOn(apiService.getInstance(), 'getEcmUsername').and.returnValue('jerryTheKillerCow');
documentList.actions = [
documentMenu
];
documentList.actions = [documentMenu];
const nodeFile = {
entry: {
@@ -747,9 +685,7 @@ describe('DocumentList', () => {
title: 'FolderAction'
});
documentList.actions = [
documentMenu
];
documentList.actions = [documentMenu];
const nodeFile = {
entry: {
@@ -773,9 +709,7 @@ describe('DocumentList', () => {
disableWithNoPermission: false
});
documentList.actions = [
documentMenu
];
documentList.actions = [documentMenu];
const nodeFile = { entry: { isFile: true, name: 'xyz', allowableOperations: null } };
@@ -793,9 +727,7 @@ describe('DocumentList', () => {
disableWithNoPermission: false
});
documentList.actions = [
documentMenu
];
documentList.actions = [documentMenu];
const nodeFile = { entry: { isFolder: true, name: 'xyz', allowableOperations: null } };
@@ -813,9 +745,7 @@ describe('DocumentList', () => {
disableWithNoPermission: true
});
documentList.actions = [
documentMenu
];
documentList.actions = [documentMenu];
const nodeFile = { entry: { isFile: true, name: 'xyz', allowableOperations: null } };
@@ -834,9 +764,7 @@ describe('DocumentList', () => {
disableWithNoPermission: true
});
documentList.actions = [
documentMenu
];
documentList.actions = [documentMenu];
const nodeFile = { entry: { isFolder: true, name: 'xyz', allowableOperations: null } };
@@ -852,7 +780,7 @@ describe('DocumentList', () => {
documentButton.target = 'document';
documentList.actions = [documentButton];
let node = new NodeMinimalEntry();
let node = new NodeEntry();
expect(documentList.getNodeActions(node)).toEqual([]);
node = new FileNode();
@@ -1022,7 +950,7 @@ describe('DocumentList', () => {
expect(documentList.navigateTo(null)).toBeFalsy();
});
it('should perform navigation through corret linked folder', () => {
it('should perform navigation through correct linked folder', () => {
const linkFolder = new FolderNode();
linkFolder.entry.id = 'link-folder';
linkFolder.entry.nodeType = 'app:folderlink';
@@ -1040,7 +968,7 @@ describe('DocumentList', () => {
let called = false;
documentList.navigationMode = DocumentListComponent.SINGLE_CLICK_NAVIGATION;
documentList.preview.subscribe(() => called = true);
documentList.preview.subscribe(() => (called = true));
documentList.onNodeClick(file);
expect(called).toBeFalsy();
@@ -1065,7 +993,7 @@ describe('DocumentList', () => {
});
it('should display folder content from loadFolder on reload if folderNode defined', () => {
documentList.folderNode = new NodeMinimal();
documentList.folderNode = new Node();
spyOn(documentList, 'loadFolder').and.callThrough();
documentList.reload();
@@ -1203,7 +1131,7 @@ describe('DocumentList', () => {
});
it('should emit [nodeClick] event on row click', () => {
const node = new NodeMinimalEntry();
const node = new NodeEntry();
spyOn(documentList, 'onNodeClick').and.callThrough();
documentList.onNodeClick(node);
@@ -1211,7 +1139,7 @@ describe('DocumentList', () => {
});
it('should emit node-click DOM event', () => {
const node = new NodeMinimalEntry();
const node = new NodeEntry();
document.addEventListener('node-click', (res) => {
expect(res).toBeDefined();
@@ -1221,7 +1149,7 @@ describe('DocumentList', () => {
});
it('should emit [nodeDblClick] event on row double-click', () => {
const node = new NodeMinimalEntry();
const node = new NodeEntry();
spyOn(documentList, 'onNodeDblClick').and.callThrough();
documentList.onNodeDblClick(node);
@@ -1229,7 +1157,7 @@ describe('DocumentList', () => {
});
it('should emit node-dblclick DOM event', () => {
const node = new NodeMinimalEntry();
const node = new NodeEntry();
document.addEventListener('node-dblclick', (res) => {
expect(res).toBeDefined();
@@ -1316,7 +1244,7 @@ describe('DocumentList', () => {
const error = { message: '{ "error": { "statusCode": 403 } }' };
documentList.currentFolderId = '1d26e465-dea3-42f3-b415-faa8364b9692';
documentList.folderNode = new NodeMinimal();
documentList.folderNode = new Node();
documentList.folderNode.id = '1d26e465-dea3-42f3-b415-faa8364b9692';
spyFolderNode.and.returnValue(of({ entry: fakeNodeWithNoPermission }));
@@ -1330,7 +1258,9 @@ describe('DocumentList', () => {
});
it('should allow to perform navigation for virtual sources', () => {
spyFolderNode = spyOn(documentListService, 'loadFolderByNodeId').and.callFake(() => of(new DocumentLoaderNode(null, { list: { pagination: {} } })));
spyFolderNode = spyOn(documentListService, 'loadFolderByNodeId').and.callFake(() =>
of(new DocumentLoaderNode(null, { list: { pagination: {} } }))
);
const sources = ['-trashcan-', '-sharedlinks-', '-sites-', '-mysites-', '-favorites-', '-recent-'];
const node = new FolderNode('folder');
@@ -1502,13 +1432,17 @@ describe('DocumentList', () => {
documentList.ngOnChanges({ currentFolderId: new SimpleChange(undefined, 'fake-id', true) });
expect(documentListService.getFolder).toHaveBeenCalledWith(null, {
where: undefined,
maxItems: 25,
skipCount: 0,
orderBy: ['isFolder desc', 'name asc'],
rootFolderId: 'fake-id'
}, ['test-include']);
expect(documentListService.getFolder).toHaveBeenCalledWith(
null,
{
where: undefined,
maxItems: 25,
skipCount: 0,
orderBy: ['isFolder desc', 'name asc'],
rootFolderId: 'fake-id'
},
['test-include']
);
});
it('should add where in the server request when present', () => {
@@ -1518,13 +1452,17 @@ describe('DocumentList', () => {
documentList.ngOnChanges({ currentFolderId: new SimpleChange(undefined, 'fake-id', true) });
expect(documentListService.getFolder).toHaveBeenCalledWith(null, {
where: '(isFolder=true)',
maxItems: 25,
skipCount: 0,
orderBy: ['isFolder desc', 'name asc'],
rootFolderId: 'fake-id'
}, ['test-include']);
expect(documentListService.getFolder).toHaveBeenCalledWith(
null,
{
where: '(isFolder=true)',
maxItems: 25,
skipCount: 0,
orderBy: ['isFolder desc', 'name asc'],
rootFolderId: 'fake-id'
},
['test-include']
);
});
it('should add orderBy in the server request', () => {
@@ -1535,13 +1473,17 @@ describe('DocumentList', () => {
documentList.ngOnChanges({ currentFolderId: new SimpleChange(undefined, 'fake-id', true) });
expect(documentListService.getFolder).toHaveBeenCalledWith(null, {
maxItems: 25,
skipCount: 0,
where: null,
orderBy: ['isFolder desc', 'size desc'],
rootFolderId: 'fake-id'
}, ['test-include']);
expect(documentListService.getFolder).toHaveBeenCalledWith(
null,
{
maxItems: 25,
skipCount: 0,
where: null,
orderBy: ['isFolder desc', 'size desc'],
rootFolderId: 'fake-id'
},
['test-include']
);
});
it('should reset the pagination when enter in a new folder', () => {
@@ -1553,23 +1495,31 @@ describe('DocumentList', () => {
skipCount: 10
});
expect(documentListService.getFolder).toHaveBeenCalledWith(null, Object({
maxItems: 10,
skipCount: 10,
orderBy: ['isFolder desc', 'name asc'],
rootFolderId: 'no-node',
where: undefined
}), undefined);
expect(documentListService.getFolder).toHaveBeenCalledWith(
null,
Object({
maxItems: 10,
skipCount: 10,
orderBy: ['isFolder desc', 'name asc'],
rootFolderId: 'no-node',
where: undefined
}),
undefined
);
documentList.onNodeClick(folder);
expect(documentListService.getFolder).toHaveBeenCalledWith(null, Object({
maxItems: 25,
skipCount: 0,
orderBy: ['isFolder desc', 'name asc'],
rootFolderId: 'folder-id',
where: undefined
}), undefined);
expect(documentListService.getFolder).toHaveBeenCalledWith(
null,
Object({
maxItems: 25,
skipCount: 0,
orderBy: ['isFolder desc', 'name asc'],
rootFolderId: 'folder-id',
where: undefined
}),
undefined
);
});
it('should display fileAutoDownload dialog if node size exceeds appConfig.viewer.fileAutoDownloadSizeThresholdInMB', async () => {
@@ -1581,13 +1531,15 @@ describe('DocumentList', () => {
}
};
documentList.navigationMode = DocumentListComponent.SINGLE_CLICK_NAVIGATION;
const node = { entry: {
const node = {
entry: {
...mockNode1,
content: {
...mockNode1.content,
sizeInBytes: 104857600
}
} };
}
};
documentList.onNodeClick(node);
fixture.detectChanges();
@@ -1597,7 +1549,6 @@ describe('DocumentList', () => {
});
describe('Preselect nodes', () => {
beforeEach(() => {
spyOn(thumbnailService, 'getMimeTypeIcon').and.returnValue(`assets/images/ft_ic_created.svg`);
});
@@ -1634,7 +1585,10 @@ describe('DocumentList', () => {
it('should call the datatable select row method for each preselected node', async () => {
const datatableSelectRowSpy = spyOn(documentList.dataTable, 'selectRow');
const fakeDatatableRows = [new ShareDataRow(mockPreselectedNodes[0], contentService, null), new ShareDataRow(mockPreselectedNodes[1], contentService, null)];
const fakeDatatableRows = [
new ShareDataRow(mockPreselectedNodes[0], contentService, null),
new ShareDataRow(mockPreselectedNodes[1], contentService, null)
];
spyOn(documentList, 'preselectRowsOfPreselectedNodes');
documentList.preselectedRows = fakeDatatableRows;
@@ -1665,7 +1619,10 @@ describe('DocumentList', () => {
it('should return only the first preselected row when selection mode is single', () => {
documentList.selectionMode = 'single';
const fakeDatatableRows = [new ShareDataRow(mockPreselectedNodes[0], contentService, null), new ShareDataRow(mockPreselectedNodes[1], contentService, null)];
const fakeDatatableRows = [
new ShareDataRow(mockPreselectedNodes[0], contentService, null),
new ShareDataRow(mockPreselectedNodes[1], contentService, null)
];
documentList.preselectedRows = fakeDatatableRows;
const preselectedRows = documentList.getPreselectedRowsBasedOnSelectionMode();
@@ -1675,7 +1632,10 @@ describe('DocumentList', () => {
it('should return all the preselected rows when selection mode is multiple', () => {
documentList.selectionMode = 'multiple';
const fakeDatatableRows = [new ShareDataRow(mockPreselectedNodes[0], contentService, null), new ShareDataRow(mockPreselectedNodes[1], contentService, null)];
const fakeDatatableRows = [
new ShareDataRow(mockPreselectedNodes[0], contentService, null),
new ShareDataRow(mockPreselectedNodes[1], contentService, null)
];
documentList.preselectedRows = fakeDatatableRows;
const preselectedRows = documentList.getPreselectedRowsBasedOnSelectionMode();
@@ -1692,7 +1652,10 @@ describe('DocumentList', () => {
it('should the combined selection be only the first preselected row when selection mode is single', () => {
const getSelectionFromAdapterSpy = spyOn(documentList.data, 'getSelectedRows');
const fakeDatatableRows = [new ShareDataRow(mockPreselectedNodes[0], contentService, null), new ShareDataRow(mockPreselectedNodes[1], contentService, null)];
const fakeDatatableRows = [
new ShareDataRow(mockPreselectedNodes[0], contentService, null),
new ShareDataRow(mockPreselectedNodes[1], contentService, null)
];
documentList.preselectedRows = fakeDatatableRows;
documentList.selectionMode = 'single';
const selection = documentList.getSelectionBasedOnSelectionMode();
@@ -1703,7 +1666,10 @@ describe('DocumentList', () => {
});
it('should get the selection from the adapter when selection mode is multiple', () => {
const fakeDatatableRows = [new ShareDataRow(mockPreselectedNodes[0], contentService, null), new ShareDataRow(mockPreselectedNodes[1], contentService, null)];
const fakeDatatableRows = [
new ShareDataRow(mockPreselectedNodes[0], contentService, null),
new ShareDataRow(mockPreselectedNodes[1], contentService, null)
];
fakeDatatableRows[0].isSelected = true;
fakeDatatableRows[1].isSelected = false;
documentList.data.setRows(fakeDatatableRows);
@@ -1747,7 +1713,10 @@ describe('DocumentList', () => {
const getRowByNodeIdSpy = spyOn(documentList.data, 'getRowByNodeId').and.callThrough();
const onNodeUnselectSpy = spyOn(documentList, 'onNodeUnselect');
const fakeDatatableRows = [new ShareDataRow(mockPreselectedNodes[0], contentService, null), new ShareDataRow(mockPreselectedNodes[1], contentService, null)];
const fakeDatatableRows = [
new ShareDataRow(mockPreselectedNodes[0], contentService, null),
new ShareDataRow(mockPreselectedNodes[1], contentService, null)
];
fakeDatatableRows[0].isSelected = true;
documentList.data.setRows(fakeDatatableRows);
let selection = documentList.data.getSelectedRows();
@@ -1766,7 +1735,10 @@ describe('DocumentList', () => {
it('should preselect the rows of the preselected nodes', () => {
const getRowByNodeIdSpy = spyOn(documentList.data, 'getRowByNodeId').and.callThrough();
const fakeDatatableRows = [new ShareDataRow(mockPreselectedNodes[0], contentService, null), new ShareDataRow(mockPreselectedNodes[1], contentService, null)];
const fakeDatatableRows = [
new ShareDataRow(mockPreselectedNodes[0], contentService, null),
new ShareDataRow(mockPreselectedNodes[1], contentService, null)
];
documentList.data.setRows(fakeDatatableRows);
documentList.selectionMode = 'multiple';
@@ -1814,7 +1786,7 @@ describe('DocumentList', () => {
@Component({
template: `
<adf-document-list #customDocumentList>
<adf-document-list #customDocumentList>
<adf-custom-loading-content-template>
<span id="custom-loading-template">This is a custom loading template</span>
</adf-custom-loading-content-template>
@@ -1840,12 +1812,7 @@ describe('DocumentListComponent rendering', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [CustomTemplateComponent],
imports: [
TranslateModule.forRoot(),
ContentTestingModule,
DataTableModule,
DocumentListModule
]
imports: [TranslateModule.forRoot(), ContentTestingModule, DataTableModule, DocumentListModule]
});
fixture = TestBed.createComponent(CustomTemplateComponent);
component = fixture.componentInstance;
@@ -1905,5 +1872,4 @@ describe('DocumentListComponent rendering', () => {
const cell3 = fixture.nativeElement.querySelector('div[title="Id"][data-automation-id="Name 3"]');
expect(cell3.innerText).toBe('3');
});
});

View File

@@ -22,16 +22,14 @@ import { SearchHeaderQueryBuilderService } from '../../../search/services/search
import { FilterSearch } from './../../../search/models/filter-search.interface';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { NodePaging, MinimalNode } from '@alfresco/js-api';
import { ADF_DOCUMENT_PARENT_COMPONENT } from '../document-list.token';
@Component({
selector: 'adf-filter-header',
templateUrl: './filter-header.component.html',
providers: [{ provide: SEARCH_QUERY_SERVICE_TOKEN, useClass: SearchHeaderQueryBuilderService}]
providers: [{ provide: SEARCH_QUERY_SERVICE_TOKEN, useClass: SearchHeaderQueryBuilderService }]
})
export class FilterHeaderComponent implements OnInit, OnChanges, OnDestroy {
/** (optional) Initial filter value to sort . */
@Input()
value: any = {};
@@ -47,18 +45,18 @@ export class FilterHeaderComponent implements OnInit, OnChanges, OnDestroy {
isFilterServiceActive: boolean;
private onDestroy$ = new Subject<boolean>();
constructor(@Inject(ADF_DOCUMENT_PARENT_COMPONENT) private documentList: any,
@Inject(SEARCH_QUERY_SERVICE_TOKEN) private searchFilterQueryBuilder: SearchHeaderQueryBuilderService) {
constructor(
@Inject(ADF_DOCUMENT_PARENT_COMPONENT) private documentList: any,
@Inject(SEARCH_QUERY_SERVICE_TOKEN) private searchFilterQueryBuilder: SearchHeaderQueryBuilderService
) {
this.isFilterServiceActive = this.searchFilterQueryBuilder.isFilterServiceActive();
}
ngOnInit() {
this.searchFilterQueryBuilder.executed
.pipe(takeUntil(this.onDestroy$))
.subscribe((newNodePaging: NodePaging) => {
this.documentList.node = newNodePaging;
this.documentList.reload();
});
this.searchFilterQueryBuilder.executed.pipe(takeUntil(this.onDestroy$)).subscribe((newNodePaging) => {
this.documentList.node = newNodePaging;
this.documentList.reload();
});
this.initDataPagination();
this.initDataSorting();
@@ -84,24 +82,20 @@ export class FilterHeaderComponent implements OnInit, OnChanges, OnDestroy {
}
initDataPagination() {
this.documentList.pagination
.pipe(takeUntil(this.onDestroy$))
.subscribe((newPagination: PaginationModel) => {
this.searchFilterQueryBuilder.setupCurrentPagination(newPagination.maxItems, newPagination.skipCount);
});
this.documentList.pagination.pipe(takeUntil(this.onDestroy$)).subscribe((newPagination: PaginationModel) => {
this.searchFilterQueryBuilder.setupCurrentPagination(newPagination.maxItems, newPagination.skipCount);
});
}
initDataSorting() {
this.documentList.sortingSubject
.pipe(takeUntil(this.onDestroy$))
.subscribe((sorting: DataSorting[]) => {
this.searchFilterQueryBuilder.setSorting(sorting);
});
this.documentList.sortingSubject.pipe(takeUntil(this.onDestroy$)).subscribe((sorting: DataSorting[]) => {
this.searchFilterQueryBuilder.setSorting(sorting);
});
}
private configureSearchParent(currentFolderId: string) {
if (this.searchFilterQueryBuilder.isCustomSourceNode(currentFolderId)) {
this.searchFilterQueryBuilder.getNodeIdForCustomSource(currentFolderId).subscribe((node: MinimalNode) => {
this.searchFilterQueryBuilder.getNodeIdForCustomSource(currentFolderId).subscribe((node) => {
this.initSearchHeader(node.id);
});
} else {
@@ -116,7 +110,6 @@ export class FilterHeaderComponent implements OnInit, OnChanges, OnDestroy {
this.searchFilterQueryBuilder.setActiveFilter(columnKey, this.value[columnKey]);
});
}
}
ngOnDestroy() {

View File

@@ -16,7 +16,7 @@
*/
import { DataRow, ObjectUtils, ThumbnailService } from '@alfresco/adf-core';
import { MinimalNode, NodeEntry } from '@alfresco/js-api';
import { Node, NodeEntry } from '@alfresco/js-api';
import { PermissionStyleModel } from './../models/permissions-style.model';
import { ContentService } from './../../common/services/content.service';
@@ -38,16 +38,19 @@ export class ShareDataRow implements DataRow {
this.cache = {};
}
constructor(private obj: NodeEntry,
private contentService: ContentService,
private permissionsStyle: PermissionStyleModel[],
private thumbnailService?: ThumbnailService,
private allowDropFiles?: boolean) {
constructor(
private obj: NodeEntry,
private contentService: ContentService,
private permissionsStyle: PermissionStyleModel[],
private thumbnailService?: ThumbnailService,
private allowDropFiles?: boolean
) {
if (!obj) {
throw new Error(ERR_OBJECT_NOT_FOUND);
}
this.isDropTarget = allowDropFiles !== undefined ? this.allowDropFiles && this.checkNodeTypeAndPermissions(obj) : this.checkNodeTypeAndPermissions(obj);
this.isDropTarget =
allowDropFiles !== undefined ? this.allowDropFiles && this.checkNodeTypeAndPermissions(obj) : this.checkNodeTypeAndPermissions(obj);
if (permissionsStyle) {
this.cssClass = this.getPermissionClass(obj);
}
@@ -62,25 +65,25 @@ export class ShareDataRow implements DataRow {
let permissionsClasses = '';
this.permissionsStyle.forEach((currentPermissionsStyle: PermissionStyleModel) => {
if (this.applyPermissionStyleToFolder(nodeEntity.entry, currentPermissionsStyle) || this.applyPermissionStyleToFile(nodeEntity.entry, currentPermissionsStyle)) {
if (
this.applyPermissionStyleToFolder(nodeEntity.entry, currentPermissionsStyle) ||
this.applyPermissionStyleToFile(nodeEntity.entry, currentPermissionsStyle)
) {
if (this.contentService.hasAllowableOperations(nodeEntity.entry, currentPermissionsStyle.permission)) {
permissionsClasses += ` ${currentPermissionsStyle.css}`;
}
}
});
return permissionsClasses;
}
private applyPermissionStyleToFile(node: MinimalNode, currentPermissionsStyle: PermissionStyleModel): boolean {
return (currentPermissionsStyle.isFile && node.isFile);
private applyPermissionStyleToFile(node: Node, currentPermissionsStyle: PermissionStyleModel): boolean {
return currentPermissionsStyle.isFile && node.isFile;
}
private applyPermissionStyleToFolder(node: MinimalNode, currentPermissionsStyle: PermissionStyleModel): boolean {
return (currentPermissionsStyle.isFolder && node.isFolder);
private applyPermissionStyleToFolder(node: Node, currentPermissionsStyle: PermissionStyleModel): boolean {
return currentPermissionsStyle.isFolder && node.isFolder;
}
isFolderAndHasPermissionToUpload(nodeEntry: NodeEntry): boolean {

View File

@@ -1,85 +0,0 @@
/*!
* @license
* Copyright © 2005-2023 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.
*/
// note: contains only limited subset of available fields
import { NodeEntry, Node } from '@alfresco/js-api';
export class NodePaging {
list: NodePagingList;
}
export class NodePagingList {
pagination: Pagination;
entries: NodeMinimalEntry[];
}
export class NodeMinimalEntry implements NodeEntry {
entry: NodeMinimal;
}
export class Pagination {
count: number;
hasMoreItems: boolean;
totalItems: number;
skipCount: number;
maxItems: number;
}
export class NodeMinimal implements Node {
id: string;
parentId: string;
name: string;
nodeType: string;
isFolder: boolean;
isFile: boolean;
modifiedAt: Date;
modifiedByUser: UserInfo;
createdAt: Date;
createdByUser: UserInfo;
content: ContentInfo;
path: PathInfoEntity;
properties: NodeProperties = {};
aspectNames: string[];
}
export class UserInfo {
displayName: string;
id: string;
}
export class ContentInfo {
mimeType: string;
mimeTypeName: string;
sizeInBytes: number;
encoding: string;
}
export class PathInfoEntity {
elements: PathElementEntity[];
isComplete: boolean;
name: string;
}
export class PathElementEntity {
id: string;
name: string;
}
export interface NodeProperties {
[key: string]: any;
}

View File

@@ -43,7 +43,6 @@ export * from './services/lock.service';
// models
export * from './models/content-action.model';
export * from './models/document-library.model';
export * from './models/permissions.model';
export * from './models/permissions-style.model';
export * from './models/node-action.enum';

View File

@@ -19,7 +19,7 @@ import { AlfrescoApiService, LogService, PaginationModel } from '@alfresco/adf-c
import { NodesApiService } from '../../common/services/nodes-api.service';
import { Injectable } from '@angular/core';
import { MinimalNode, NodeEntry, NodePaging, NodesApi } from '@alfresco/js-api';
import { Node, NodeEntry, NodePaging, NodesApi } from '@alfresco/js-api';
import { DocumentLoaderNode } from '../models/document-folder.model';
import { Observable, from, throwError, forkJoin } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
@@ -32,18 +32,18 @@ const ROOT_ID = '-root-';
providedIn: 'root'
})
export class DocumentListService implements DocumentListLoader {
private _nodesApi: NodesApi;
get nodes(): NodesApi {
this._nodesApi = this._nodesApi ?? new NodesApi(this.apiService.getInstance());
return this._nodesApi;
}
constructor(private nodesApiService: NodesApiService,
private apiService: AlfrescoApiService,
private logService: LogService,
private customResourcesService: CustomResourcesService) {
}
constructor(
private nodesApiService: NodesApiService,
private apiService: AlfrescoApiService,
private logService: LogService,
private customResourcesService: CustomResourcesService
) {}
/**
* Deletes a node.
@@ -63,9 +63,7 @@ export class DocumentListService implements DocumentListLoader {
* @returns NodeEntry for the copied node
*/
copyNode(nodeId: string, targetParentId: string): Observable<NodeEntry> {
return from(this.nodes.copyNode(nodeId, { targetParentId })).pipe(
catchError((err) => this.handleError(err))
);
return from(this.nodes.copyNode(nodeId, { targetParentId })).pipe(catchError((err) => this.handleError(err)));
}
/**
@@ -76,9 +74,7 @@ export class DocumentListService implements DocumentListLoader {
* @returns NodeEntry for the moved node
*/
moveNode(nodeId: string, targetParentId: string): Observable<NodeEntry> {
return from(this.nodes.moveNode(nodeId, { targetParentId })).pipe(
catchError((err) => this.handleError(err))
);
return from(this.nodes.moveNode(nodeId, { targetParentId })).pipe(catchError((err) => this.handleError(err)));
}
/**
@@ -95,8 +91,9 @@ export class DocumentListService implements DocumentListLoader {
rootNodeId = opts.rootFolderId;
}
const includeFieldsRequest = ['path', 'properties', 'allowableOperations', 'permissions', 'aspectNames', ...includeFields]
.filter((element, index, array) => index === array.indexOf(element));
const includeFieldsRequest = ['path', 'properties', 'allowableOperations', 'permissions', 'aspectNames', ...includeFields].filter(
(element, index, array) => index === array.indexOf(element)
);
const params: any = {
includeSource: true,
@@ -122,9 +119,7 @@ export class DocumentListService implements DocumentListLoader {
}
}
return from(this.nodes.listNodeChildren(rootNodeId, params)).pipe(
catchError((err) => this.handleError(err))
);
return from(this.nodes.listNodeChildren(rootNodeId, params)).pipe(catchError((err) => this.handleError(err)));
}
/**
@@ -134,9 +129,10 @@ export class DocumentListService implements DocumentListLoader {
* @param includeFields Extra information to include (available options are "aspectNames", "isLink" and "association")
* @returns Details of the folder
*/
getNode(nodeId: string, includeFields: string[] = []): Observable<MinimalNode> {
const includeFieldsRequest = ['path', 'properties', 'allowableOperations', 'permissions', 'definition', ...includeFields]
.filter((element, index, array) => index === array.indexOf(element));
getNode(nodeId: string, includeFields: string[] = []): Observable<Node> {
const includeFieldsRequest = ['path', 'properties', 'allowableOperations', 'permissions', 'definition', ...includeFields].filter(
(element, index, array) => index === array.indexOf(element)
);
const opts: any = {
includeSource: true,
@@ -154,17 +150,16 @@ export class DocumentListService implements DocumentListLoader {
* @returns Details of the folder
*/
getFolderNode(nodeId: string, includeFields: string[] = []): Observable<NodeEntry> {
const includeFieldsRequest = ['path', 'properties', 'allowableOperations', 'permissions', 'aspectNames', ...includeFields]
.filter((element, index, array) => index === array.indexOf(element));
const includeFieldsRequest = ['path', 'properties', 'allowableOperations', 'permissions', 'aspectNames', ...includeFields].filter(
(element, index, array) => index === array.indexOf(element)
);
const opts: any = {
includeSource: true,
include: includeFieldsRequest
};
return from(this.nodes.getNode(nodeId, opts)).pipe(
catchError((err) => this.handleError(err))
);
return from(this.nodes.getNode(nodeId, opts)).pipe(catchError((err) => this.handleError(err)));
}
isCustomSourceService(nodeId): boolean {
@@ -181,28 +176,43 @@ export class DocumentListService implements DocumentListLoader {
* @param orderBy order by node property
* @returns Details of the folder
*/
loadFolderByNodeId(nodeId: string, pagination: PaginationModel, includeFields: string[], where?: string, orderBy?: string[]): Observable<DocumentLoaderNode> {
loadFolderByNodeId(
nodeId: string,
pagination: PaginationModel,
includeFields: string[],
where?: string,
orderBy?: string[]
): Observable<DocumentLoaderNode> {
if (this.customResourcesService.isCustomSource(nodeId)) {
return this.customResourcesService.loadFolderByNodeId(nodeId, pagination, includeFields, where).pipe(
map((result: any) => new DocumentLoaderNode(null, result))
);
return this.customResourcesService
.loadFolderByNodeId(nodeId, pagination, includeFields, where)
.pipe(map((result: any) => new DocumentLoaderNode(null, result)));
} else {
return this.retrieveDocumentNode(nodeId, pagination, includeFields, where, orderBy);
}
}
private retrieveDocumentNode(nodeId: string, pagination: PaginationModel, includeFields: string[], where?: string, orderBy?: string[]): Observable<DocumentLoaderNode> {
private retrieveDocumentNode(
nodeId: string,
pagination: PaginationModel,
includeFields: string[],
where?: string,
orderBy?: string[]
): Observable<DocumentLoaderNode> {
return forkJoin([
this.getFolderNode(nodeId, includeFields),
this.getFolder(null, {
maxItems: pagination.maxItems,
skipCount: pagination.skipCount,
orderBy,
rootFolderId: nodeId,
where
}, includeFields)]).pipe(
map((results) => new DocumentLoaderNode(results[0], results[1]))
);
this.getFolder(
null,
{
maxItems: pagination.maxItems,
skipCount: pagination.skipCount,
orderBy,
rootFolderId: nodeId,
where
},
includeFields
)
]).pipe(map((results) => new DocumentLoaderNode(results[0], results[1])));
}
private handleError(error: any) {

View File

@@ -16,14 +16,14 @@
*/
import { CardViewBaseItemModel, UpdateNotification } from '@alfresco/adf-core';
import { MinimalNode } from '@alfresco/js-api';
import { Node } from '@alfresco/js-api';
import { Subject } from 'rxjs';
export interface BaseCardViewContentUpdate {
itemUpdated$: Subject<UpdateNotification>;
updatedAspect$: Subject<MinimalNode>;
updatedAspect$: Subject<Node>;
update(property: CardViewBaseItemModel, newValue: any);
updateElement(notification: CardViewBaseItemModel);
updateNodeAspect(node: MinimalNode);
updateNodeAspect(node: Node);
}

View File

@@ -15,85 +15,75 @@
* limitations under the License.
*/
import {
ContentInfo,
NodeMinimal,
NodeMinimalEntry,
NodePaging,
NodePagingList,
PathInfoEntity
} from '../document-list';
import { ContentInfo, Node, NodeEntry, PathInfo } from '@alfresco/js-api';
export class PageNode extends NodePaging {
constructor(entries?: NodeMinimalEntry[]) {
super();
this.list = new NodePagingList();
this.list.entries = entries || [];
}
}
export class FileNode extends NodeMinimalEntry {
export class FileNode extends NodeEntry {
constructor(name?: string, mimeType?: string, id?: string) {
super();
this.entry = new NodeMinimal();
this.entry = new Node();
this.entry.id = id || 'file-id';
this.entry.isFile = true;
this.entry.isFolder = false;
this.entry.name = name;
this.entry.path = new PathInfoEntity();
this.entry.path = new PathInfo();
this.entry.content = new ContentInfo();
this.entry.content.mimeType = mimeType || 'text/plain';
this.entry.properties = {};
}
}
export class FolderNode extends NodeMinimalEntry {
export class FolderNode extends NodeEntry {
constructor(name?: string) {
super();
this.entry = new NodeMinimal();
this.entry = new Node();
this.entry.id = 'folder-id';
this.entry.isFile = false;
this.entry.isFolder = true;
this.entry.name = name;
this.entry.path = new PathInfoEntity();
this.entry.path = new PathInfo();
this.entry.aspectNames = ['cm:folder'];
this.entry.properties = {};
}
}
export class SmartFolderNode extends NodeMinimalEntry {
export class SmartFolderNode extends NodeEntry {
constructor(name?: string) {
super();
this.entry = new NodeMinimal();
this.entry = new Node();
this.entry.id = 'smart-folder-id';
this.entry.isFile = false;
this.entry.isFolder = true;
this.entry.name = name;
this.entry.path = new PathInfoEntity();
this.entry.path = new PathInfo();
this.entry.aspectNames = ['smf:systemConfigSmartFolder'];
this.entry.properties = {};
}
}
export class RuleFolderNode extends NodeMinimalEntry {
export class RuleFolderNode extends NodeEntry {
constructor(name?: string) {
super();
this.entry = new NodeMinimal();
this.entry = new Node();
this.entry.id = 'rule-folder-id';
this.entry.isFile = false;
this.entry.isFolder = true;
this.entry.name = name;
this.entry.path = new PathInfoEntity();
this.entry.path = new PathInfo();
this.entry.aspectNames = ['rule:rules'];
this.entry.properties = {};
}
}
export class LinkFolderNode extends NodeMinimalEntry {
export class LinkFolderNode extends NodeEntry {
constructor(name?: string) {
super();
this.entry = new NodeMinimal();
this.entry = new Node();
this.entry.id = 'link-folder-id';
this.entry.isFile = false;
this.entry.isFolder = true;
this.entry.nodeType = 'app:folderlink';
this.entry.name = name;
this.entry.path = new PathInfoEntity();
this.entry.path = new PathInfo();
this.entry.properties = {};
}
}

View File

@@ -15,12 +15,12 @@
* limitations under the License.
*/
import { MinimalNodeEntryEntity, Version, NodeChildAssociation, Node } from '@alfresco/js-api';
import { Version, NodeChildAssociation, Node } from '@alfresco/js-api';
import { NodeEntityEvent } from '../../document-list';
export interface NewVersionUploaderDialogData {
title?: string;
node: MinimalNodeEntryEntity;
node: Node;
file?: File;
currentVersion?: Version;
showVersionsOnly?: boolean;

View File

@@ -34,11 +34,10 @@ import {
fakeSiteRoles
} from '../../../mock/permission-list.component.mock';
import { ContentTestingModule } from '../../../testing/content.testing.module';
import { MinimalNode } from '@alfresco/js-api';
import { Node } from '@alfresco/js-api';
import { NodesApiService } from '../../../common/services/nodes-api.service';
describe('PermissionListComponent', () => {
let fixture: ComponentFixture<PermissionListComponent>;
let component: PermissionListComponent;
let element: HTMLElement;
@@ -51,10 +50,7 @@ describe('PermissionListComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ContentTestingModule
]
imports: [TranslateModule.forRoot(), ContentTestingModule]
});
fixture = TestBed.createComponent(PermissionListComponent);
component = fixture.componentInstance;
@@ -114,7 +110,7 @@ describe('PermissionListComponent', () => {
});
describe('Inherited Permission', () => {
it('should show inherited details', async () => {
it('should show inherited details', async () => {
getNodeSpy.and.returnValue(of(fakeNodeInheritedOnly));
component.ngOnInit();
@@ -122,23 +118,23 @@ describe('PermissionListComponent', () => {
await fixture.whenStable();
expect(element.querySelector('.adf-inherit-container .mat-checked')).toBeDefined();
expect(element.querySelector('.adf-inherit-container h3').textContent.trim())
.toBe('PERMISSION_MANAGER.LABELS.INHERITED-PERMISSIONS PERMISSION_MANAGER.LABELS.ON');
expect(element.querySelector('span[title="total"]').textContent.trim())
.toBe('PERMISSION_MANAGER.LABELS.INHERITED-SUBTITLE');
expect(element.querySelector('.adf-inherit-container h3').textContent.trim()).toBe(
'PERMISSION_MANAGER.LABELS.INHERITED-PERMISSIONS PERMISSION_MANAGER.LABELS.ON'
);
expect(element.querySelector('span[title="total"]').textContent.trim()).toBe('PERMISSION_MANAGER.LABELS.INHERITED-SUBTITLE');
});
it('should toggle the inherited button', async () => {
it('should toggle the inherited button', async () => {
getNodeSpy.and.returnValue(of(fakeNodeInheritedOnly));
component.ngOnInit();
fixture.detectChanges();
expect(element.querySelector('.adf-inherit-container .mat-checked')).toBeDefined();
expect(element.querySelector('.adf-inherit-container h3').textContent.trim())
.toBe('PERMISSION_MANAGER.LABELS.INHERITED-PERMISSIONS PERMISSION_MANAGER.LABELS.ON');
expect(element.querySelector('span[title="total"]').textContent.trim())
.toBe('PERMISSION_MANAGER.LABELS.INHERITED-SUBTITLE');
expect(element.querySelector('.adf-inherit-container h3').textContent.trim()).toBe(
'PERMISSION_MANAGER.LABELS.INHERITED-PERMISSIONS PERMISSION_MANAGER.LABELS.ON'
);
expect(element.querySelector('span[title="total"]').textContent.trim()).toBe('PERMISSION_MANAGER.LABELS.INHERITED-SUBTITLE');
spyOn(nodeService, 'updateNode').and.returnValue(of(fakeLocalPermission));
@@ -148,13 +144,13 @@ describe('PermissionListComponent', () => {
fixture.detectChanges();
expect(element.querySelector('.adf-inherit-container .mat-checked')).toBe(null);
expect(element.querySelector('.adf-inherit-container h3').textContent.trim())
.toBe('PERMISSION_MANAGER.LABELS.INHERITED-PERMISSIONS PERMISSION_MANAGER.LABELS.OFF');
expect(element.querySelector('span[title="total"]').textContent.trim())
.toBe('PERMISSION_MANAGER.LABELS.INHERITED-SUBTITLE');
expect(element.querySelector('.adf-inherit-container h3').textContent.trim()).toBe(
'PERMISSION_MANAGER.LABELS.INHERITED-PERMISSIONS PERMISSION_MANAGER.LABELS.OFF'
);
expect(element.querySelector('span[title="total"]').textContent.trim()).toBe('PERMISSION_MANAGER.LABELS.INHERITED-SUBTITLE');
});
it('should not toggle inherited button for read only users', async () => {
it('should not toggle inherited button for read only users', async () => {
getNodeSpy.and.returnValue(of(fakeReadOnlyNodeInherited));
component.ngOnInit();
@@ -162,10 +158,10 @@ describe('PermissionListComponent', () => {
await fixture.whenStable();
expect(element.querySelector('.adf-inherit-container .mat-checked')).toBeDefined();
expect(element.querySelector('.adf-inherit-container h3').textContent.trim())
.toBe('PERMISSION_MANAGER.LABELS.INHERITED-PERMISSIONS PERMISSION_MANAGER.LABELS.ON');
expect(element.querySelector('span[title="total"]').textContent.trim())
.toBe('PERMISSION_MANAGER.LABELS.INHERITED-SUBTITLE');
expect(element.querySelector('.adf-inherit-container h3').textContent.trim()).toBe(
'PERMISSION_MANAGER.LABELS.INHERITED-PERMISSIONS PERMISSION_MANAGER.LABELS.ON'
);
expect(element.querySelector('span[title="total"]').textContent.trim()).toBe('PERMISSION_MANAGER.LABELS.INHERITED-SUBTITLE');
spyOn(nodeService, 'updateNode').and.returnValue(of(fakeLocalPermission));
@@ -176,22 +172,20 @@ describe('PermissionListComponent', () => {
await fixture.whenStable();
expect(element.querySelector('.adf-inherit-container .mat-checked')).toBeDefined();
expect(element.querySelector('.adf-inherit-container h3').textContent.trim())
.toBe('PERMISSION_MANAGER.LABELS.INHERITED-PERMISSIONS PERMISSION_MANAGER.LABELS.ON');
expect(element.querySelector('span[title="total"]').textContent.trim())
.toBe('PERMISSION_MANAGER.LABELS.INHERITED-SUBTITLE');
expect(document.querySelector('.adf-snackbar-message-content').textContent)
.toContain('PERMISSION_MANAGER.ERROR.NOT-ALLOWED');
expect(element.querySelector('.adf-inherit-container h3').textContent.trim()).toBe(
'PERMISSION_MANAGER.LABELS.INHERITED-PERMISSIONS PERMISSION_MANAGER.LABELS.ON'
);
expect(element.querySelector('span[title="total"]').textContent.trim()).toBe('PERMISSION_MANAGER.LABELS.INHERITED-SUBTITLE');
expect(document.querySelector('.adf-snackbar-message-content').textContent).toContain('PERMISSION_MANAGER.ERROR.NOT-ALLOWED');
});
});
describe('locally set permission', () => {
describe('locally set permission', () => {
beforeEach(() => {
getNodeSpy.and.returnValue(of(fakeLocalPermission));
});
it('should show locally set permissions', async () => {
it('should show locally set permissions', async () => {
searchQuerySpy.and.returnValue(of(fakeSiteNodeResponse));
component.ngOnInit();
@@ -212,7 +206,7 @@ describe('PermissionListComponent', () => {
expect(element.querySelector('adf-user-name-column').textContent).toContain('GROUP_EVERYONE');
expect(element.querySelector('#adf-select-role-permission').textContent).toContain('Contributor');
const selectBox = fixture.debugElement.query(By.css(('[id="adf-select-role-permission"] .mat-select-trigger')));
const selectBox = fixture.debugElement.query(By.css('[id="adf-select-role-permission"] .mat-select-trigger'));
selectBox.triggerEventHandler('click', null);
fixture.detectChanges();
@@ -234,14 +228,16 @@ describe('PermissionListComponent', () => {
expect(element.querySelector('adf-user-name-column').textContent).toContain('GROUP_site_testsite_SiteManager');
expect(element.querySelector('#adf-select-role-permission').textContent).toContain('ADF.ROLES.SITEMANAGER');
const deleteButton = element.querySelector<HTMLButtonElement>('[data-automation-id="adf-delete-permission-button-GROUP_site_testsite_SiteManager"]');
const deleteButton = element.querySelector<HTMLButtonElement>(
'[data-automation-id="adf-delete-permission-button-GROUP_site_testsite_SiteManager"]'
);
expect(deleteButton.disabled).toBe(true);
const otherDeleteButton = element.querySelector<HTMLButtonElement>('[data-automation-id="adf-delete-permission-button-superadminuser"]');
expect(otherDeleteButton.disabled).toBe(false);
});
it('should update the role when another value is chosen', async () => {
spyOn(nodeService, 'updateNode').and.returnValue(of(new MinimalNode({id: 'fake-uwpdated-node'})));
it('should update the role when another value is chosen', async () => {
spyOn(nodeService, 'updateNode').and.returnValue(of(new Node({ id: 'fake-uwpdated-node' })));
searchQuerySpy.and.returnValue(of(fakeEmptyResponse));
component.ngOnInit();
@@ -251,7 +247,7 @@ describe('PermissionListComponent', () => {
expect(element.querySelector('adf-user-name-column').textContent).toContain('GROUP_EVERYONE');
expect(element.querySelector('#adf-select-role-permission').textContent).toContain('Contributor');
const selectBox = fixture.debugElement.query(By.css(('[id="adf-select-role-permission"] .mat-select-trigger')));
const selectBox = fixture.debugElement.query(By.css('[id="adf-select-role-permission"] .mat-select-trigger'));
selectBox.triggerEventHandler('click', null);
fixture.detectChanges();
const options = fixture.debugElement.queryAll(By.css('mat-option'));
@@ -259,11 +255,13 @@ describe('PermissionListComponent', () => {
expect(options.length).toBe(5);
options[3].triggerEventHandler('click', {});
fixture.detectChanges();
expect(nodeService.updateNode).toHaveBeenCalledWith('f472543f-7218-403d-917b-7a5861257244', { permissions: { locallySet: [ { accessStatus: 'ALLOWED', name: 'Editor', authorityId: 'GROUP_EVERYONE' } ] } });
expect(nodeService.updateNode).toHaveBeenCalledWith('f472543f-7218-403d-917b-7a5861257244', {
permissions: { locallySet: [{ accessStatus: 'ALLOWED', name: 'Editor', authorityId: 'GROUP_EVERYONE' }] }
});
});
it('should delete the person', async () => {
spyOn(nodeService, 'updateNode').and.returnValue(of(new MinimalNode({id: 'fake-uwpdated-node'})));
it('should delete the person', async () => {
spyOn(nodeService, 'updateNode').and.returnValue(of(new Node({ id: 'fake-uwpdated-node' })));
searchQuerySpy.and.returnValue(of(fakeEmptyResponse));
component.ngOnInit();
@@ -277,8 +275,7 @@ describe('PermissionListComponent', () => {
deleteButton.click();
fixture.detectChanges();
expect(nodeService.updateNode).toHaveBeenCalledWith('f472543f-7218-403d-917b-7a5861257244', { permissions: { locallySet: [ ] } });
expect(nodeService.updateNode).toHaveBeenCalledWith('f472543f-7218-403d-917b-7a5861257244', { permissions: { locallySet: [] } });
});
});
});

View File

@@ -20,7 +20,7 @@ import { AlfrescoApiService, AppConfigService, DataSorting } from '@alfresco/adf
import { SearchConfiguration } from '../models/search-configuration.interface';
import { BaseQueryBuilderService } from './base-query-builder.service';
import { SearchCategory } from '../models/search-category.interface';
import { MinimalNode, QueryBody } from '@alfresco/js-api';
import { Node } from '@alfresco/js-api';
import { filter } from 'rxjs/operators';
import { Observable } from 'rxjs';
import { SearchSortingDefinition } from '../models/search-sorting-definition.interface';
@@ -31,18 +31,14 @@ import { NodesApiService } from '../../common/services/nodes-api.service';
providedIn: 'root'
})
export class SearchHeaderQueryBuilderService extends BaseQueryBuilderService {
private customSources = ['-trashcan-', '-sharedlinks-', '-sites-', '-mysites-', '-favorites-', '-recent-', '-my-'];
activeFilters: FilterSearch[] = [];
constructor(appConfig: AppConfigService,
alfrescoApiService: AlfrescoApiService,
private nodeApiService: NodesApiService) {
constructor(appConfig: AppConfigService, alfrescoApiService: AlfrescoApiService, private nodeApiService: NodesApiService) {
super(appConfig, alfrescoApiService);
this.updated.pipe(
filter((query: QueryBody) => !!query)).subscribe(() => {
this.updated.pipe(filter((query) => !!query)).subscribe(() => {
this.execute();
});
}
@@ -56,9 +52,7 @@ export class SearchHeaderQueryBuilderService extends BaseQueryBuilderService {
}
setupCurrentPagination(maxItems: number, skipCount: number) {
if (!this.paging ||
(this.paging &&
this.paging.maxItems !== maxItems || this.paging.skipCount !== skipCount)) {
if (!this.paging || (this.paging && this.paging.maxItems !== maxItems) || this.paging.skipCount !== skipCount) {
this.paging = { maxItems, skipCount };
this.execute();
}
@@ -118,7 +112,7 @@ export class SearchHeaderQueryBuilderService extends BaseQueryBuilderService {
private getSortingFieldFromColumnName(columnName: string) {
if (this.sortingOptions.length > 0) {
const sortOption: SearchSortingDefinition = this.sortingOptions.find((option: SearchSortingDefinition) => option.key === columnName);
const sortOption = this.sortingOptions.find((option) => option.key === columnName);
return sortOption ? sortOption.field : '';
}
return '';
@@ -127,25 +121,23 @@ export class SearchHeaderQueryBuilderService extends BaseQueryBuilderService {
getCategoryForColumn(columnKey: string): SearchCategory {
let foundCategory = null;
if (this.categories !== null) {
foundCategory = this.categories.find(
category => category.columnKey === columnKey
);
foundCategory = this.categories.find((category) => category.columnKey === columnKey);
}
return foundCategory;
}
setCurrentRootFolderId(currentFolderId: string) {
const alreadyAddedFilter = this.filterQueries.find(filterQueries =>
filterQueries.query.includes(currentFolderId)
);
const alreadyAddedFilter = this.filterQueries.find((filterQueries) => filterQueries.query.includes(currentFolderId));
if (alreadyAddedFilter !== undefined) {
this.filterQueries = [];
}
this.filterQueries = [{
query: `PARENT:"workspace://SpacesStore/${currentFolderId}"`
}];
this.filterQueries = [
{
query: `PARENT:"workspace://SpacesStore/${currentFolderId}"`
}
];
this.execute();
}
@@ -154,8 +146,7 @@ export class SearchHeaderQueryBuilderService extends BaseQueryBuilderService {
return this.customSources.includes(currentNodeId);
}
getNodeIdForCustomSource(customSourceId: string): Observable<MinimalNode> {
getNodeIdForCustomSource(customSourceId: string): Observable<Node> {
return this.nodeApiService.getNode(customSourceId);
}
}