Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Upload form in the submission view #102

Merged
merged 14 commits into from
Jul 21, 2021
26 changes: 14 additions & 12 deletions client/src/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import {AuthService} from './shared/services/auth.service';
import {UuidComponent} from './shared/components/uuid/uuid.component';
import {AutofillProjectService} from './project-registration/services/autofill-project.service';
import {ProjectCacheService} from './project-registration/services/project-cache.service';
import {MatMenuModule} from '@angular/material/menu';

const BROWSER_LOCALE = navigator.language;

Expand Down Expand Up @@ -103,24 +104,25 @@ const BROWSER_LOCALE = navigator.language;
UuidComponent
],
imports: [
AaiSecurity,
BrowserModule,
BrowserAnimationsModule,
FlexLayoutModule,
FormsModule,
HttpClientModule,
RouterModule.forRoot(ROUTES),
SharedModule,
ReactiveFormsModule,
NoopAnimationsModule,
NgxDatatableModule,
FlexLayoutModule,
BrowserAnimationsModule,
MatDialogModule,
MaterialModule,
AaiSecurity,
MatMenuModule,
MetadataSchemaFormModule,
ProjectRegistrationModule,
TemplateQuestionnaireModule,
MatDialogModule,
NgxGraphModule,
NgxChartsModule
NgxChartsModule,
NgxDatatableModule,
NoopAnimationsModule,
ProjectRegistrationModule,
ReactiveFormsModule,
RouterModule.forRoot(ROUTES),
SharedModule,
TemplateQuestionnaireModule
],
providers: [
{
Expand Down
3 changes: 2 additions & 1 deletion client/src/app/http-interceptors/http-error-interceptor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { Router} from '@angular/router';
/** Handle http error response in one place. */
@Injectable()
export class HttpErrorInterceptor implements HttpInterceptor {
MAX_RETRIES = 5;

constructor(private router: Router) {}

Expand All @@ -21,7 +22,7 @@ export class HttpErrorInterceptor implements HttpInterceptor {
next: HttpHandler
): Observable<HttpEvent<any>> {

return next.handle(req).pipe(retry(5), catchError(
return next.handle(req).pipe(retry(this.MAX_RETRIES), catchError(
(error: HttpErrorResponse) => {

if (this.router.url.startsWith('/error')) {
Expand Down
1 change: 0 additions & 1 deletion client/src/app/project/project.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,6 @@ export class ProjectComponent implements OnInit {

onSwitchUpload() {
this.upload = !this.upload;

}

canSubmit(project: Project) {
Expand Down
6 changes: 0 additions & 6 deletions client/src/app/shared/components/upload/upload.component.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,8 @@ <h4 class="card-title">Upload a Spreadsheet</h4>
<br/>
<p>
<input type="file" #fileInput placeholder="Upload file..." />
<input type="hidden" #projectUuidInput name="projectUuid" value="{{projectUuid}}" />
</p>

<mat-radio-group [(ngModel)]="isUpdate">
<mat-radio-button [value]="false" [checked]="!isUpdate" ><span>Create</span></mat-radio-button>
<mat-radio-button [value]="true" [checked]="isUpdate" ><span>Update</span></mat-radio-button>
</mat-radio-group>

<p>
<button type="button" (click)="upload()" class="vf-button vf-button--primary vf-button--sm" >Upload</button>
</p>
Expand Down
127 changes: 127 additions & 0 deletions client/src/app/shared/components/upload/upload.component.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import {ProjectFormComponent} from '../../../project-form/project-form.component';
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {of, throwError} from 'rxjs';
import {RouterTestingModule} from '@angular/router/testing';
import {ROUTES} from '../../../app.routes';
import {HttpClientTestingModule} from '@angular/common/http/testing';
import {Router} from '@angular/router';
import {AlertService} from '../../services/alert.service';
import {LoaderService} from '../../services/loader.service';
import SpyObj = jasmine.SpyObj;
import {UploadComponent} from './upload.component';
import {BrokerService} from '../../services/broker.service';
import {ElementRef} from '@angular/core';


describe('UploadComponent', () => {
let component: UploadComponent;
let fixture: ComponentFixture<UploadComponent>;

let brokerSvc: SpyObj<BrokerService>;
let alertSvc: SpyObj<AlertService>;
let loaderSvc: SpyObj<LoaderService>;
let router: SpyObj<Router>;

beforeEach(async(() => {
brokerSvc = jasmine.createSpyObj('BrokerService', [
'uploadSpreadsheet'
]);
alertSvc = jasmine.createSpyObj('AlertService', [
'success',
'clear',
'error'
]);
loaderSvc = jasmine.createSpyObj('LoaderService', [
'display'
]);
router = jasmine.createSpyObj('Router', [
'navigate'
]);

brokerSvc.uploadSpreadsheet.and.returnValue(of({message: '', details: {submission_uuid: 'submission-uuid'}}));

TestBed.configureTestingModule({
imports: [
RouterTestingModule.withRoutes(ROUTES),
HttpClientTestingModule
],
providers: [
{provide: AlertService, useValue: alertSvc},
{provide: BrokerService, useValue: brokerSvc},
{provide: LoaderService, useValue: loaderSvc},
{provide: Router, useValue: router}
],
declarations: [ProjectFormComponent]
})
.compileComponents();
}));

function makeSpreadsheetBlob() {
const blob = new Blob([''], {type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
blob['lastModifiedDate'] = '';
blob['name'] = 'filename.xlsx';
return blob;
}

beforeEach(() => {
fixture = TestBed.createComponent(UploadComponent);
component = fixture.componentInstance;
const mockNativeElement = {
get files() {
const blob = makeSpreadsheetBlob();
const file = <File>blob;
const fileList = {
0: file
};
return fileList;
}
};

component.fileInput = new ElementRef(mockNativeElement);
component.projectUuid = 'project-uuid';
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});

it('should display successful when upload successful', () => {
component.upload();
expect(brokerSvc.uploadSpreadsheet).toHaveBeenCalled();
expect(alertSvc.success).toHaveBeenCalled();
expect(loaderSvc.display.calls.allArgs()).toEqual([[true], [false]]);
expect(router.navigate).toHaveBeenCalledWith(['/submissions/detail'], Object({
queryParams: Object({
uuid: 'submission-uuid',
project: 'project-uuid'
})
}));
});

it('should display error when no file', () => {
const mockNativeElement = {
get files() {
return {};
}
};
component.fileInput = new ElementRef(mockNativeElement);
component.upload();

expect(brokerSvc.uploadSpreadsheet).toHaveBeenCalledTimes(0);
expect(alertSvc.clear).toHaveBeenCalled();
expect(alertSvc.error).toHaveBeenCalled();
expect(router.navigate).toHaveBeenCalledTimes(0);

});

it('should display error when broker svc upload has error', () => {
brokerSvc.uploadSpreadsheet.and.returnValue(throwError({status: 500}));
component.upload();

expect(brokerSvc.uploadSpreadsheet).toHaveBeenCalled();
expect(alertSvc.error).toHaveBeenCalled();
expect(loaderSvc.display.calls.allArgs()).toEqual([[true], [false]]);
expect(router.navigate).toHaveBeenCalledTimes(0);
});
});
23 changes: 12 additions & 11 deletions client/src/app/shared/components/upload/upload.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,26 @@ import {LoaderService} from '../../services/loader.service';
})
export class UploadComponent implements OnInit {

@ViewChild('fileInput', { static: true }) fileInput;
@ViewChild('projectUuidInput', { static: true }) projectIdInput;
@ViewChild('fileInput', {static: true}) fileInput;

error$: Observable<String>;

uploadResults$: Observable<UploadResults>;

@Input() projectUuid;
@Input() submissionUuid;
@Input() isUpdate = false;

@Output() fileUpload = new EventEmitter();

isUpdate = false;

constructor(private brokerService: BrokerService,
private router: Router,
private alertService: AlertService,
private loaderService: LoaderService) {
this.isUpdate = false;
}

ngOnInit() {}
ngOnInit() {
ke4 marked this conversation as resolved.
Show resolved Hide resolved
}

upload() {
const fileBrowser = this.fileInput.nativeElement;
Expand All @@ -43,19 +42,21 @@ export class UploadComponent implements OnInit {
const formData = new FormData();
formData.append('file', fileBrowser.files[0]);

const projectUuid = this.projectIdInput.nativeElement.value;
if (this.projectUuid) {
formData.append('projectUuid', this.projectUuid);
}

if (projectUuid) {
formData.append('projectUuid', projectUuid );
if (this.submissionUuid) {
formData.append('submissionUuid', this.submissionUuid);
}

this.brokerService.uploadSpreadsheet(formData, this.isUpdate).subscribe({
next: data => {
this.uploadResults$ = <any>data;
const submissionId = this.uploadResults$['details']['submission_id'];
const submissionUuid = this.uploadResults$['details']['submission_uuid'];
this.loaderService.display(false);
this.alertService.success('Upload Success', this.uploadResults$['message'], true, true);
this.router.navigate(['/submissions/detail'], { queryParams: { id: submissionId, project: this.projectUuid } } );
this.router.navigate(['/submissions/detail'], {queryParams: {uuid: submissionUuid, project: this.projectUuid}});
},
error: err => {
this.error$ = <any>err;
Expand Down
Loading