[ADF-5369] HTTP 500 response in adf-upload-button is emitted as a success event (#7087)

* [ADF-5369] HTTP 500 response in adf-upload-button is emitted as a success event

* [ci:force] unit test fixed
This commit is contained in:
Dharan
2021-06-08 13:30:04 +05:30
committed by GitHub
parent acf4b26d74
commit 9a2a62255f
8 changed files with 190 additions and 50 deletions

View File

@@ -404,8 +404,8 @@ export class FilesComponent implements OnInit, OnChanges, OnDestroy {
}); });
} }
openSnackMessageError(message: string) { openSnackMessageError(error: any) {
this.notificationService.showError(message); this.notificationService.showError(error.value || error);
} }
openSnackMessageInfo(message: string) { openSnackMessageInfo(message: string) {

View File

@@ -0,0 +1,58 @@
/*!
* @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.
*/
export const mockUploadSuccessPromise = {
on: function (event, callback) {
if (event === 'success') {
callback();
}
return this;
},
catch: function (callback) {
callback();
return this;
},
then: function (callback) {
callback();
return this;
},
next: function (callback) {
callback();
return this;
}
};
export const mockUploadErrorPromise = {
on: function (event, callback) {
if (event === 'error') {
callback();
}
return this;
},
catch: function (callback) {
callback();
return this;
},
then: function (callback) {
callback();
return this;
},
next: function (callback) {
callback();
return this;
}
};

View File

@@ -126,14 +126,15 @@ export abstract class UploadBase implements OnInit, OnDestroy {
const event = new UploadFilesEvent( const event = new UploadFilesEvent(
[...filteredFiles], [...filteredFiles],
this.uploadService, this.uploadService,
this.success this.success,
this.error
); );
this.beginUpload.emit(event); this.beginUpload.emit(event);
if (!event.defaultPrevented) { if (!event.defaultPrevented) {
if (filteredFiles.length > 0) { if (filteredFiles.length > 0) {
this.uploadService.addToQueue(...filteredFiles); this.uploadService.addToQueue(...filteredFiles);
this.uploadService.uploadFilesInTheQueue(this.success); this.uploadService.uploadFilesInTheQueue(this.success, this.error);
} }
} }
}); });

View File

@@ -23,6 +23,7 @@ import { UploadButtonComponent } from './upload-button.component';
import { NodeEntry } from '@alfresco/js-api'; import { NodeEntry } from '@alfresco/js-api';
import { ContentTestingModule } from '../../testing/content.testing.module'; import { ContentTestingModule } from '../../testing/content.testing.module';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { mockUploadErrorPromise, mockUploadSuccessPromise } from '../../mock/upload.service.mock';
describe('UploadButtonComponent', () => { describe('UploadButtonComponent', () => {
@@ -124,40 +125,32 @@ describe('UploadButtonComponent', () => {
it('should call uploadFile with the default root folder', () => { it('should call uploadFile with the default root folder', () => {
component.rootFolderId = '-root-'; component.rootFolderId = '-root-';
component.success = null;
spyOn(contentService, 'getNode').and.returnValue(of(fakeFolderNodeWithPermission)); spyOn(contentService, 'getNode').and.returnValue(of(fakeFolderNodeWithPermission));
spyOn(uploadService, 'uploadFilesInTheQueue').and.stub();
component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) }); component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) });
uploadService.uploadFilesInTheQueue = jasmine.createSpy('uploadFilesInTheQueue');
fixture.detectChanges(); fixture.detectChanges();
component.onFilesAdded(fakeEvent); component.onFilesAdded(fakeEvent);
expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith(null); expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalled();
}); });
it('should call uploadFile with a custom root folder', () => { it('should call uploadFile with a custom root folder', () => {
component.rootFolderId = '-my-'; component.rootFolderId = '-my-';
component.success = null;
spyOn(contentService, 'getNode').and.returnValue(of(fakeFolderNodeWithPermission)); spyOn(contentService, 'getNode').and.returnValue(of(fakeFolderNodeWithPermission));
spyOn(uploadService, 'uploadFilesInTheQueue').and.stub();
component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) }); component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) });
uploadService.uploadFilesInTheQueue = jasmine.createSpy('uploadFilesInTheQueue');
fixture.detectChanges(); fixture.detectChanges();
component.onFilesAdded(fakeEvent); component.onFilesAdded(fakeEvent);
expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith(null); expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalled();
}); });
it('should not call uploadFiles if rootFolderId is null', () => { it('should not call uploadFiles if rootFolderId is null', () => {
component.rootFolderId = null; component.rootFolderId = null;
spyOn(uploadService, 'uploadFilesInTheQueue').and.stub();
component.ngOnChanges({ rootFolderId: new SimpleChange(null, null, true) }); component.ngOnChanges({ rootFolderId: new SimpleChange(null, null, true) });
uploadService.uploadFilesInTheQueue = jasmine.createSpy('uploadFilesInTheQueue');
fixture.detectChanges(); fixture.detectChanges();
component.onFilesAdded(fakeEvent); component.onFilesAdded(fakeEvent);
@@ -351,6 +344,7 @@ describe('UploadButtonComponent', () => {
let fakeNodeWithNoPermission; let fakeNodeWithNoPermission;
beforeEach(() => { beforeEach(() => {
spyOn(uploadService, 'uploadFilesInTheQueue').and.stub();
fakeNodeWithNoPermission = { fakeNodeWithNoPermission = {
entry: {} entry: {}
}; };
@@ -361,9 +355,6 @@ describe('UploadButtonComponent', () => {
spyOn(contentService, 'getNode').and.returnValue(of(fakeNodeWithNoPermission)); spyOn(contentService, 'getNode').and.returnValue(of(fakeNodeWithNoPermission));
component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) }); component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) });
uploadService.uploadFilesInTheQueue = jasmine.createSpy('uploadFilesInTheQueue');
fixture.detectChanges(); fixture.detectChanges();
component.onFilesAdded(fakeEvent); component.onFilesAdded(fakeEvent);
@@ -375,9 +366,6 @@ describe('UploadButtonComponent', () => {
spyOn(contentService, 'getNode').and.returnValue(throwError('error')); spyOn(contentService, 'getNode').and.returnValue(throwError('error'));
component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) }); component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) });
uploadService.uploadFilesInTheQueue = jasmine.createSpy('uploadFilesInTheQueue');
fixture.detectChanges(); fixture.detectChanges();
component.onFilesAdded(fakeEvent); component.onFilesAdded(fakeEvent);
@@ -407,9 +395,6 @@ describe('UploadButtonComponent', () => {
spyOn(contentService, 'getNode').and.returnValue(of(fakeNodeWithNoPermission)); spyOn(contentService, 'getNode').and.returnValue(of(fakeNodeWithNoPermission));
component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) }); component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) });
uploadService.uploadFilesInTheQueue = jasmine.createSpy('uploadFilesInTheQueue');
fixture.detectChanges(); fixture.detectChanges();
component.onFilesAdded(fakeEvent); component.onFilesAdded(fakeEvent);
@@ -421,13 +406,40 @@ describe('UploadButtonComponent', () => {
spyOn(contentService, 'getNode').and.returnValue(of(fakeFolderNodeWithPermission)); spyOn(contentService, 'getNode').and.returnValue(of(fakeFolderNodeWithPermission));
component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) }); component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) });
uploadService.uploadFilesInTheQueue = jasmine.createSpy('uploadFilesInTheQueue');
fixture.detectChanges(); fixture.detectChanges();
component.onFilesAdded(fakeEvent); component.onFilesAdded(fakeEvent);
expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalled(); expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalled();
}); });
}); });
describe('Events', () => {
beforeEach(() => {
spyOn(contentService, 'getNode').and.returnValue(of(fakeFolderNodeWithPermission));
component.rootFolderId = 'nodeId';
component.ngOnChanges({ rootFolderId: new SimpleChange(null, component.rootFolderId, true) });
fixture.detectChanges();
});
it('should emit an success for successful upload of a file', (done) => {
spyOn(uploadService, 'getUploadPromise').and.returnValue(mockUploadSuccessPromise);
component.success.subscribe((success) => {
expect(success).not.toBeNull();
done();
});
component.onFilesAdded(fakeEvent);
});
it('should emit error if upload errored', (done) => {
spyOn(uploadService, 'getUploadPromise').and.returnValue(mockUploadErrorPromise);
component.error.subscribe((error) => {
expect(error).not.toBeNull();
done();
});
component.onFilesAdded(fakeEvent);
});
});
}); });

View File

@@ -20,6 +20,7 @@ import { FileModel, UploadService, setupTestBed } from '@alfresco/adf-core';
import { UploadDragAreaComponent } from './upload-drag-area.component'; import { UploadDragAreaComponent } from './upload-drag-area.component';
import { ContentTestingModule } from '../../testing/content.testing.module'; import { ContentTestingModule } from '../../testing/content.testing.module';
import { TranslateModule } from '@ngx-translate/core'; import { TranslateModule } from '@ngx-translate/core';
import { mockUploadSuccessPromise, mockUploadErrorPromise } from '../../mock/upload.service.mock';
function getFakeShareDataRow(allowableOperations = ['delete', 'update', 'create']) { function getFakeShareDataRow(allowableOperations = ['delete', 'update', 'create']) {
return { return {
@@ -222,6 +223,7 @@ describe('UploadDragAreaComponent', () => {
it('should only upload those files whose fileTypes are in acceptedFilesType', async(() => { it('should only upload those files whose fileTypes are in acceptedFilesType', async(() => {
spyOn(uploadService, 'uploadFilesInTheQueue'); spyOn(uploadService, 'uploadFilesInTheQueue');
component.success = null; component.success = null;
component.error = null;
component.acceptedFilesType = '.jpg,.pdf'; component.acceptedFilesType = '.jpg,.pdf';
fixture.detectChanges(); fixture.detectChanges();
const files: File[] = [ const files: File[] = [
@@ -231,7 +233,7 @@ describe('UploadDragAreaComponent', () => {
]; ];
component.onFilesDropped(files); component.onFilesDropped(files);
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith(null); expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith(null, null);
const filesCalledWith = addToQueueSpy.calls.mostRecent().args; const filesCalledWith = addToQueueSpy.calls.mostRecent().args;
expect(filesCalledWith.length).toBe(2, 'Files should contain two elements'); expect(filesCalledWith.length).toBe(2, 'Files should contain two elements');
expect(filesCalledWith[0].name).toBe('phobos.jpg'); expect(filesCalledWith[0].name).toBe('phobos.jpg');
@@ -242,12 +244,13 @@ describe('UploadDragAreaComponent', () => {
it('should upload a file if fileType is in acceptedFilesType', async(() => { it('should upload a file if fileType is in acceptedFilesType', async(() => {
spyOn(uploadService, 'uploadFilesInTheQueue'); spyOn(uploadService, 'uploadFilesInTheQueue');
component.success = null; component.success = null;
component.error = null;
component.acceptedFilesType = '.png'; component.acceptedFilesType = '.png';
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
component.onFilesDropped([new File(['fakefake'], 'file-fake.png', { type: 'image/png' })]); component.onFilesDropped([new File(['fakefake'], 'file-fake.png', { type: 'image/png' })]);
expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith(null); expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith(null, null);
}); });
})); }));
@@ -281,23 +284,25 @@ describe('UploadDragAreaComponent', () => {
it('should not upload a file if fileType is not in acceptedFilesType', async(() => { it('should not upload a file if fileType is not in acceptedFilesType', async(() => {
component.success = null; component.success = null;
component.error = null;
component.acceptedFilesType = '.pdf'; component.acceptedFilesType = '.pdf';
fixture.detectChanges(); fixture.detectChanges();
spyOn(uploadService, 'uploadFilesInTheQueue'); spyOn(uploadService, 'uploadFilesInTheQueue');
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
component.onFilesDropped([new File(['fakefake'], 'file-fake.png', { type: 'image/png' })]); component.onFilesDropped([new File(['fakefake'], 'file-fake.png', { type: 'image/png' })]);
expect(uploadService.uploadFilesInTheQueue).not.toHaveBeenCalledWith(null); expect(uploadService.uploadFilesInTheQueue).not.toHaveBeenCalledWith(null, null);
}); });
})); }));
it('should upload a file with a custom root folder ID when dropped', async(() => { it('should upload a file with a custom root folder ID when dropped', async(() => {
component.success = null; component.success = null;
component.error = null;
fixture.detectChanges(); fixture.detectChanges();
spyOn(uploadService, 'uploadFilesInTheQueue'); spyOn(uploadService, 'uploadFilesInTheQueue');
component.onFilesDropped([new File(['fakefake'], 'file-fake.png', { type: 'image/png' })]); component.onFilesDropped([new File(['fakefake'], 'file-fake.png', { type: 'image/png' })]);
expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith(null); expect(uploadService.uploadFilesInTheQueue).toHaveBeenCalledWith(null, null);
})); }));
it('should upload a file when user has create permission on target folder', async(() => { it('should upload a file when user has create permission on target folder', async(() => {
@@ -446,5 +451,67 @@ describe('UploadDragAreaComponent', () => {
component.onUploadFiles(fakeCustomEvent); component.onUploadFiles(fakeCustomEvent);
}); });
it('should emit success if successful of upload a file', (done) => {
spyOn(uploadService, 'getUploadPromise').and.returnValue(mockUploadSuccessPromise);
const fakeItem = {
fullPath: '/folder-fake/file-fake.png',
isDirectory: false,
isFile: true,
relativeFolder: '/',
name: 'file-fake.png',
file: (callbackFile) => {
const fileFake = new File(['fakefake'], 'file-fake.png', { type: 'image/png' });
callbackFile(fileFake);
}
};
fixture.detectChanges();
component.success.subscribe((success) => {
expect(success).not.toBeNull();
done();
});
const fakeCustomEvent: CustomEvent = new CustomEvent('CustomEvent', {
detail: {
data: getFakeShareDataRow(),
files: [fakeItem]
}
});
component.onUploadFiles(fakeCustomEvent);
});
it('should emit error if upload errored', (done) => {
spyOn(uploadService, 'getUploadPromise').and.returnValue(mockUploadErrorPromise);
const fakeItem = {
fullPath: '/folder-fake/file-fake.png',
isDirectory: false,
isFile: true,
relativeFolder: '/',
name: 'file-fake.png',
file: (callbackFile) => {
const fileFake = new File(['fakefake'], 'file-fake.png', { type: 'image/png' });
callbackFile(fileFake);
}
};
fixture.detectChanges();
component.error.subscribe((error) => {
expect(error).not.toBeNull();
done();
});
const fakeCustomEvent: CustomEvent = new CustomEvent('CustomEvent', {
detail: {
data: getFakeShareDataRow(),
files: [fakeItem]
}
});
component.onUploadFiles(fakeCustomEvent);
});
}); });
}); });

View File

@@ -32,7 +32,8 @@ export class UploadFilesEvent {
constructor( constructor(
public files: Array<FileModel>, public files: Array<FileModel>,
private uploadService: UploadService, private uploadService: UploadService,
private callback: EventEmitter<any> private successEmitter: EventEmitter<any>,
private errorEmitter: EventEmitter<any>
) {} ) {}
pauseUpload() { pauseUpload() {
@@ -42,7 +43,7 @@ export class UploadFilesEvent {
resumeUpload() { resumeUpload() {
if (this.files && this.files.length > 0) { if (this.files && this.files.length > 0) {
this.uploadService.addToQueue(...this.files); this.uploadService.addToQueue(...this.files);
this.uploadService.uploadFilesInTheQueue(this.callback); this.uploadService.uploadFilesInTheQueue(this.successEmitter, this.errorEmitter);
} }
} }
} }

View File

@@ -195,7 +195,7 @@ describe('UploadService', () => {
<FileUploadOptions> { parentId: '-root-' } <FileUploadOptions> { parentId: '-root-' }
); );
service.addToQueue(fileFake); service.addToQueue(fileFake);
service.uploadFilesInTheQueue(emitter); service.uploadFilesInTheQueue(null, emitter);
expect(jasmine.Ajax.requests.mostRecent().url) expect(jasmine.Ajax.requests.mostRecent().url)
.toBe('http://localhost:9876/ecm/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children?autoRename=true&include=allowableOperations'); .toBe('http://localhost:9876/ecm/alfresco/api/-default-/public/alfresco/versions/1/nodes/-root-/children?autoRename=true&include=allowableOperations');

View File

@@ -166,9 +166,10 @@ export class UploadService {
/** /**
* Finds all the files in the queue that are not yet uploaded and uploads them into the directory folder. * Finds all the files in the queue that are not yet uploaded and uploads them into the directory folder.
* @param emitter Emitter to invoke on file status change * @param successEmitter Emitter to invoke on file success status change
* @param errorEmitter Emitter to invoke on file error status change
*/ */
uploadFilesInTheQueue(emitter?: EventEmitter<any>): void { uploadFilesInTheQueue(successEmitter?: EventEmitter<any>, errorEmitter?: EventEmitter<any>): void {
if (!this.activeTask) { if (!this.activeTask) {
const file = this.queue.find( const file = this.queue.find(
(currentFile) => currentFile.status === FileUploadStatus.Pending (currentFile) => currentFile.status === FileUploadStatus.Pending
@@ -176,13 +177,13 @@ export class UploadService {
if (file) { if (file) {
this.onUploadStarting(file); this.onUploadStarting(file);
const promise = this.beginUpload(file, emitter); const promise = this.beginUpload(file, successEmitter, errorEmitter);
this.activeTask = promise; this.activeTask = promise;
this.cache[file.name] = promise; this.cache[file.name] = promise;
const next = () => { const next = () => {
this.activeTask = null; this.activeTask = null;
setTimeout(() => this.uploadFilesInTheQueue(emitter), 100); setTimeout(() => this.uploadFilesInTheQueue(successEmitter, errorEmitter), 100);
}; };
promise.next = next; promise.next = next;
@@ -270,7 +271,7 @@ export class UploadService {
} }
} }
private beginUpload(file: FileModel, emitter: EventEmitter<any>): any { private beginUpload(file: FileModel, successEmitter?: EventEmitter<any>, errorEmitter?: EventEmitter<any>): any {
const promise = this.getUploadPromise(file); const promise = this.getUploadPromise(file);
promise promise
.on('progress', (progress: FileUploadProgress) => { .on('progress', (progress: FileUploadProgress) => {
@@ -278,14 +279,14 @@ export class UploadService {
}) })
.on('abort', () => { .on('abort', () => {
this.onUploadAborted(file); this.onUploadAborted(file);
if (emitter) { if (successEmitter) {
emitter.emit({ value: 'File aborted' }); successEmitter.emit({ value: 'File aborted' });
} }
}) })
.on('error', (err) => { .on('error', (err) => {
this.onUploadError(file, err); this.onUploadError(file, err);
if (emitter) { if (errorEmitter) {
emitter.emit({ value: 'Error file uploaded' }); errorEmitter.emit({ value: 'Error file uploaded' });
} }
}) })
.on('success', (data) => { .on('success', (data) => {
@@ -296,13 +297,13 @@ export class UploadService {
} else { } else {
this.deleteAbortedNodeVersion(data.entry.id, data.entry.properties['cm:versionLabel']); this.deleteAbortedNodeVersion(data.entry.id, data.entry.properties['cm:versionLabel']);
} }
if (emitter) { if (successEmitter) {
emitter.emit({ value: 'File deleted' }); successEmitter.emit({ value: 'File deleted' });
} }
} else { } else {
this.onUploadComplete(file, data); this.onUploadComplete(file, data);
if (emitter) { if (successEmitter) {
emitter.emit({ value: data }); successEmitter.emit({ value: data });
} }
} }
}) })