diff --git a/lib/process-services-cloud/src/lib/form/services/content-cloud-node-selector.service.spec.ts b/lib/process-services-cloud/src/lib/form/services/content-cloud-node-selector.service.spec.ts new file mode 100644 index 0000000000..2ab49800d5 --- /dev/null +++ b/lib/process-services-cloud/src/lib/form/services/content-cloud-node-selector.service.spec.ts @@ -0,0 +1,143 @@ +/*! + * @license + * Copyright 2019 Alfresco Software, Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TestBed } from '@angular/core/testing'; +import { AlfrescoApiService, NotificationService, setupTestBed } from '@alfresco/adf-core'; +import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module'; +import { TranslateModule } from '@ngx-translate/core'; +import { ContentCloudNodeSelectorService } from 'process-services-cloud'; +import { MatDialog } from '@angular/material/dialog'; +import { of, Subject } from 'rxjs'; +import { ContentNodeSelectorComponent, ContentNodeSelectorComponentData } from '@alfresco/adf-content-services'; + +describe('ContentCloudNodeSelectorService', () => { + let service: ContentCloudNodeSelectorService; + let apiService: AlfrescoApiService; + let notificationService: NotificationService; + let getNodeSpy: jasmine.Spy; + let dialog: MatDialog; + let openDialogSpy: jasmine.Spy; + let showWarningSpy: jasmine.Spy; + + const relativePathNodeResponseBody = { + entry: { + id: 'mock-relative-path-node-id' + } + }; + + const aliasNodeResponseBody = { + entry: { + id: 'mock-alias-node-id' + } + }; + + setupTestBed({ + imports: [TranslateModule.forRoot(), ProcessServiceCloudTestingModule] + }); + + beforeEach(() => { + service = TestBed.inject(ContentCloudNodeSelectorService); + notificationService = TestBed.inject(NotificationService); + apiService = TestBed.inject(AlfrescoApiService); + dialog = TestBed.inject(MatDialog); + + showWarningSpy = spyOn(notificationService, 'showWarning'); + openDialogSpy = spyOn(dialog, 'open').and.callFake(() => ({ + afterOpened: () => of({}), + afterClosed: () => of({}), + componentInstance: { + body: '', + error: new Subject() + } + })); + + getNodeSpy = spyOn(apiService.nodesApi, 'getNode'); + }); + + it('should be able to open the content node select panel dialog', () => { + const mockData = { + title: 'Select a file', + actionName: 'ATTACH', + currentFolderId: 'nodeId', + selectionMode: 'single' + }; + + service.openUploadFileDialog('nodeId', 'single', true, true); + + const args = openDialogSpy.calls.allArgs()[0]; + + expect(openDialogSpy).toHaveBeenCalled(); + expect(args[0]).toEqual(ContentNodeSelectorComponent); + expect(args[1].data.title).toEqual(mockData.title); + expect(args[1].data.actionName).toEqual(mockData.actionName); + expect(args[1].panelClass).toEqual('adf-content-node-selector-dialog'); + expect(args[1].data.selectionMode).toEqual(mockData.selectionMode); + expect(args[1].data.currentFolderId).toEqual('nodeId'); + }); + + it('should be able to set sourceNodeNotFound value to true if the relative path is invalid/deleted', async () => { + expect(service.sourceNodeNotFound).toBe(false); + + getNodeSpy.and.returnValue(Promise.reject('Not exists')); + await service.fetchNodeIdFromRelativePath('mock-alias', { relativePath: 'mock-wrong-relativePath' }); + + expect(getNodeSpy).toHaveBeenCalledWith('mock-alias', { + relativePath: 'mock-wrong-relativePath' + }); + expect(service.sourceNodeNotFound).toBe(true); + }); + + it('should be able to set sourceNodeNotFound value to false after the dialog close', async () => { + service.sourceNodeNotFound = true; + service.openUploadFileDialog('nodeId', 'single', true, true); + + service.close(); + + expect(service.sourceNodeNotFound).toBe(false); + }); + + it('should be able to show a notification if the relative path is invalid/deleted', () => { + service.sourceNodeNotFound = true; + service.openUploadFileDialog('nodeId', 'single', true, true); + + expect(showWarningSpy).toHaveBeenCalledWith('ADF_CLOUD_TASK_FORM.ERROR.DESTINATION_FOLDER_PATH_ERROR'); + }); + + it('should not show a notification if the relative path is valid', () => { + service.sourceNodeNotFound = false; + service.openUploadFileDialog('nodeId', 'single', true, true); + + expect(showWarningSpy).not.toHaveBeenCalled(); + }); + + it('should be able to fetch given relative path node id', async () => { + getNodeSpy.and.returnValue(Promise.resolve(relativePathNodeResponseBody)); + await service.fetchNodeIdFromRelativePath('mock-alias', { relativePath: 'mock-relativePath' }); + + expect(getNodeSpy).toHaveBeenCalledWith('mock-alias', { + relativePath: 'mock-relativePath' + }); + expect(service.sourceNodeNotFound).toBe(false); + }); + + it('should be able to fetch given alias node id', async () => { + getNodeSpy.and.returnValue(Promise.resolve(aliasNodeResponseBody)); + await service.fetchAliasNodeId('mock-alias'); + + expect(getNodeSpy).toHaveBeenCalledWith('mock-alias'); + }); +}); diff --git a/lib/process-services-cloud/src/lib/form/services/content-cloud-node-selector.service.ts b/lib/process-services-cloud/src/lib/form/services/content-cloud-node-selector.service.ts index 5c79643c66..dcfb8cfc1a 100644 --- a/lib/process-services-cloud/src/lib/form/services/content-cloud-node-selector.service.ts +++ b/lib/process-services-cloud/src/lib/form/services/content-cloud-node-selector.service.ts @@ -16,7 +16,7 @@ */ import { Injectable } from '@angular/core'; -import { AlfrescoApiService } from '@alfresco/adf-core'; +import { AlfrescoApiService, NotificationService } from '@alfresco/adf-core'; import { MatDialog } from '@angular/material/dialog'; import { ContentNodeSelectorComponent, ContentNodeSelectorComponentData, NodeAction } from '@alfresco/adf-content-services'; import { Node } from '@alfresco/js-api'; @@ -27,55 +27,67 @@ import { Observable, Subject, throwError } from 'rxjs'; }) export class ContentCloudNodeSelectorService { - constructor( - private apiService: AlfrescoApiService, - private dialog: MatDialog) { - } + sourceNodeNotFound = false; - openUploadFileDialog(currentFolderId?: string, selectionMode?: string, isAllFileSources?: boolean, restrictRootToCurrentFolderId?: boolean): Observable { - const select = new Subject(); - select.subscribe({ - complete: this.close.bind(this) - }); - const data = { - title: 'Select a file', - actionName: NodeAction.ATTACH, - currentFolderId, - restrictRootToCurrentFolderId, - select, - selectionMode, - isSelectionValid: (entry: Node) => entry.isFile, - showFilesInResult: true, - showDropdownSiteList: false, - showLocalUploadButton: isAllFileSources - }; - this.openContentNodeDialog(data, 'adf-content-node-selector-dialog', '66%'); - return select; - } + constructor( + private apiService: AlfrescoApiService, + private notificationService: NotificationService, + private dialog: MatDialog) { + } + + openUploadFileDialog(currentFolderId?: string, selectionMode?: string, isAllFileSources?: boolean, restrictRootToCurrentFolderId?: boolean): Observable { + const select = new Subject(); + select.subscribe({ complete: this.close.bind(this) }); + const data = { + title: 'Select a file', + actionName: NodeAction.ATTACH, + currentFolderId, + restrictRootToCurrentFolderId, + select, + selectionMode, + isSelectionValid: (entry: Node) => entry.isFile, + showFilesInResult: true, + showDropdownSiteList: false, + showLocalUploadButton: isAllFileSources + }; + this.openContentNodeDialog(data, 'adf-content-node-selector-dialog', '66%'); + return select; + } async fetchNodeIdFromRelativePath(alias: string, opts: { relativePath: string }): Promise { - const relativePathNodeEntry: any = await this.apiService.getInstance().node + const relativePathNodeEntry: any = await this.apiService.nodesApi .getNode(alias, opts) - .catch((err) => this.handleError(err)); + .catch((err) => { + this.sourceNodeNotFound = true; + return this.handleError(err); + }); return relativePathNodeEntry?.entry?.id; } async fetchAliasNodeId(alias: string): Promise { - const aliasNodeEntry: any = await this.apiService.getInstance().node + const aliasNodeEntry: any = await this.apiService.nodesApi .getNode(alias) .catch((err) => this.handleError(err)); return aliasNodeEntry?.entry?.id; } - private openContentNodeDialog(data: ContentNodeSelectorComponentData, currentPanelClass: string, chosenWidth: string) { - this.dialog.open(ContentNodeSelectorComponent, { data, panelClass: currentPanelClass, width: chosenWidth }); - } + private openContentNodeDialog(data: ContentNodeSelectorComponentData, currentPanelClass: string, chosenWidth: string) { + const contentNodeDialog = this.dialog.open(ContentNodeSelectorComponent, { data, panelClass: currentPanelClass, width: chosenWidth }); + contentNodeDialog.afterOpened().subscribe(() => { + if (this.sourceNodeNotFound) { + this.notificationService.showWarning('ADF_CLOUD_TASK_FORM.ERROR.DESTINATION_FOLDER_PATH_ERROR'); + } + }); + contentNodeDialog.afterClosed().subscribe(() => { + this.sourceNodeNotFound = false; + }); + } - close() { - this.dialog.closeAll(); - } + close() { + this.dialog.closeAll(); + } - private handleError(error: any): Observable { - return throwError(error || 'Server error'); - } + private handleError(error: any): Observable { + return throwError(error || 'Server error'); + } } diff --git a/lib/process-services-cloud/src/lib/i18n/en.json b/lib/process-services-cloud/src/lib/i18n/en.json index f4563a2a65..98c4b72c24 100644 --- a/lib/process-services-cloud/src/lib/i18n/en.json +++ b/lib/process-services-cloud/src/lib/i18n/en.json @@ -371,7 +371,8 @@ } }, "ERROR": { - "INVALID_DESTINATION_FOLDER_PATH": "Invalid destination folder path" + "INVALID_DESTINATION_FOLDER_PATH": "Invalid destination folder path", + "DESTINATION_FOLDER_PATH_ERROR": "The destination path is incorrect or does not exist, rollback to -my- location" } }, "ADF_CLOUD_FORM_COMPONENT": {