From da72765d8eda4d24c265658295f959423d2c657e Mon Sep 17 00:00:00 2001 From: =?utf8?q?Alfonso=20Mart=C3=ADnez?= Date: Wed, 30 Oct 2019 12:31:13 +0100 Subject: [PATCH] mgr/dashboard: edit/clone/copy rbd image after its data is received MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Fixes: https://tracker.ceph.com/issues/42561 Signed-off-by: Alfonso Martínez --- .../block/rbd-form/rbd-form.component.spec.ts | 115 +++++++++++++++++- .../ceph/block/rbd-form/rbd-form.component.ts | 43 ++++--- 2 files changed, 140 insertions(+), 18 deletions(-) diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form.component.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form.component.spec.ts index 3c8fbf2a8239..78926ba7521b 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form.component.spec.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form.component.spec.ts @@ -1,5 +1,5 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, discardPeriodicTasks, fakeAsync, TestBed } from '@angular/core/testing'; import { ReactiveFormsModule } from '@angular/forms'; import { ActivatedRoute, Router } from '@angular/router'; import { RouterTestingModule } from '@angular/router/testing'; @@ -9,6 +9,8 @@ import { ToastrModule } from 'ngx-toastr'; import { By } from '@angular/platform-browser'; import { of } from 'rxjs'; +import { delay } from 'rxjs/operators'; + import { ActivatedRouteStub } from '../../../../testing/activated-route-stub'; import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper'; import { RbdService } from '../../../shared/api/rbd.service'; @@ -55,6 +57,117 @@ describe('RbdFormComponent', () => { expect(component).toBeTruthy(); }); + describe('create/edit/clone/copy image', () => { + let createAction; + let editAction; + let cloneAction; + let copyAction; + let rbdServiceGetSpy; + + beforeEach(() => { + createAction = spyOn(component, 'createAction').and.stub(); + editAction = spyOn(component, 'editAction').and.stub(); + cloneAction = spyOn(component, 'cloneAction').and.stub(); + copyAction = spyOn(component, 'copyAction').and.stub(); + spyOn(component, 'setResponse').and.stub(); + spyOn(TestBed.get(Router), 'navigate').and.stub(); + rbdServiceGetSpy = spyOn(TestBed.get(RbdService), 'get'); + rbdServiceGetSpy.and.returnValue(of({ pool_name: 'foo', pool_image: 'bar' })); + component.mode = undefined; + }); + + it('should create image', () => { + component.ngOnInit(); + component.submit(); + + expect(createAction).toHaveBeenCalledTimes(1); + expect(editAction).toHaveBeenCalledTimes(0); + expect(cloneAction).toHaveBeenCalledTimes(0); + expect(copyAction).toHaveBeenCalledTimes(0); + }); + + it('should not edit image if no image data is received', fakeAsync(() => { + component.mode = RbdFormMode.editing; + rbdServiceGetSpy.and.returnValue( + of({ pool_name: 'foo', pool_image: 'bar' }).pipe(delay(100)) + ); + component.ngOnInit(); + component.submit(); + + expect(createAction).toHaveBeenCalledTimes(0); + expect(editAction).toHaveBeenCalledTimes(0); + expect(cloneAction).toHaveBeenCalledTimes(0); + expect(copyAction).toHaveBeenCalledTimes(0); + + discardPeriodicTasks(); + })); + + it('should edit image after image data is received', () => { + component.mode = RbdFormMode.editing; + component.ngOnInit(); + component.submit(); + + expect(createAction).toHaveBeenCalledTimes(0); + expect(editAction).toHaveBeenCalledTimes(1); + expect(cloneAction).toHaveBeenCalledTimes(0); + expect(copyAction).toHaveBeenCalledTimes(0); + }); + + it('should not clone image if no image data is received', fakeAsync(() => { + component.mode = RbdFormMode.cloning; + rbdServiceGetSpy.and.returnValue( + of({ pool_name: 'foo', pool_image: 'bar' }).pipe(delay(100)) + ); + component.ngOnInit(); + component.submit(); + + expect(createAction).toHaveBeenCalledTimes(0); + expect(editAction).toHaveBeenCalledTimes(0); + expect(cloneAction).toHaveBeenCalledTimes(0); + expect(copyAction).toHaveBeenCalledTimes(0); + + discardPeriodicTasks(); + })); + + it('should clone image after image data is received', () => { + component.mode = RbdFormMode.cloning; + component.ngOnInit(); + component.submit(); + + expect(createAction).toHaveBeenCalledTimes(0); + expect(editAction).toHaveBeenCalledTimes(0); + expect(cloneAction).toHaveBeenCalledTimes(1); + expect(copyAction).toHaveBeenCalledTimes(0); + }); + + it('should not copy image if no image data is received', fakeAsync(() => { + component.mode = RbdFormMode.copying; + rbdServiceGetSpy.and.returnValue( + of({ pool_name: 'foo', pool_image: 'bar' }).pipe(delay(100)) + ); + component.ngOnInit(); + component.submit(); + + expect(createAction).toHaveBeenCalledTimes(0); + expect(editAction).toHaveBeenCalledTimes(0); + expect(cloneAction).toHaveBeenCalledTimes(0); + expect(copyAction).toHaveBeenCalledTimes(0); + + discardPeriodicTasks(); + })); + + it('should copy image after image data is received', () => { + component.mode = RbdFormMode.copying; + component.ngOnInit(); + component.submit(); + + expect(createAction).toHaveBeenCalledTimes(0); + expect(editAction).toHaveBeenCalledTimes(0); + expect(cloneAction).toHaveBeenCalledTimes(0); + expect(copyAction).toHaveBeenCalledTimes(1); + }); + }); + describe('should test decodeURIComponent of params', () => { let rbdService: RbdService; diff --git a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form.component.ts b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form.component.ts index ba22359761fa..6e9bb294ce1d 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form.component.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-form/rbd-form.component.ts @@ -4,7 +4,8 @@ import { ActivatedRoute, Router } from '@angular/router'; import { I18n } from '@ngx-translate/i18n-polyfill'; import * as _ from 'lodash'; -import { Observable } from 'rxjs'; +import { AsyncSubject, Observable } from 'rxjs'; +import { switchMap } from 'rxjs/operators'; import { PoolService } from '../../../shared/api/pool.service'; import { RbdService } from '../../../shared/api/rbd.service'; @@ -82,6 +83,7 @@ export class RbdFormComponent implements OnInit { ]; action: string; resource: string; + private rbdImage = new AsyncSubject(); constructor( private authStorageService: AuthStorageService, @@ -227,6 +229,7 @@ export class RbdFormComponent implements OnInit { } this.rbdService.get(poolName, rbdName).subscribe((resp: RbdFormResponseModel) => { this.setResponse(resp, this.snapName); + this.rbdImage.next(resp); }); }); } else { @@ -638,22 +641,28 @@ export class RbdFormComponent implements OnInit { } submit() { - let action: Observable; - - if (this.mode === this.rbdFormMode.editing) { - action = this.editAction(); - } else if (this.mode === this.rbdFormMode.cloning) { - action = this.cloneAction(); - } else if (this.mode === this.rbdFormMode.copying) { - action = this.copyAction(); - } else { - action = this.createAction(); + if (!this.mode) { + this.rbdImage.next('create'); } - - action.subscribe( - undefined, - () => this.rbdForm.setErrors({ cdSubmitButton: true }), - () => this.router.navigate(['/block/rbd']) - ); + this.rbdImage.complete(); + this.rbdImage + .pipe( + switchMap(() => { + if (this.mode === this.rbdFormMode.editing) { + return this.editAction(); + } else if (this.mode === this.rbdFormMode.cloning) { + return this.cloneAction(); + } else if (this.mode === this.rbdFormMode.copying) { + return this.copyAction(); + } else { + return this.createAction(); + } + }) + ) + .subscribe( + () => {}, + () => this.rbdForm.setErrors({ cdSubmitButton: true }), + () => this.router.navigate(['/block/rbd']) + ); } } -- 2.47.3