import { RbdSnapshotListComponent } from './rbd-snapshot-list/rbd-snapshot-list.component';
import { RbdTrashListComponent } from './rbd-trash-list/rbd-trash-list.component';
import { RbdTrashMoveModalComponent } from './rbd-trash-move-modal/rbd-trash-move-modal.component';
+import { RbdTrashPurgeModalComponent } from './rbd-trash-purge-modal/rbd-trash-purge-modal.component';
import { RbdTrashRestoreModalComponent } from './rbd-trash-restore-modal/rbd-trash-restore-modal.component';
@NgModule({
RbdDetailsComponent,
RbdSnapshotFormComponent,
RbdTrashMoveModalComponent,
- RbdTrashRestoreModalComponent
+ RbdTrashRestoreModalComponent,
+ RbdTrashPurgeModalComponent
],
imports: [
CommonModule,
RbdTrashListComponent,
RbdTrashMoveModalComponent,
RbdImagesComponent,
- RbdTrashRestoreModalComponent
+ RbdTrashRestoreModalComponent,
+ RbdTrashPurgeModalComponent
]
})
export class BlockModule {}
[selection]="selection"
[tableActions]="tableActions">
</cd-table-actions>
+
+ <button class="btn btn-sm btn-default btn-label"
+ type="button"
+ (click)="purgeModal()">
+ <i class="fa fa-fw fa-times"
+ aria-hidden="true"></i>
+ <ng-container i18n>Purge Trash</ng-container>
+ </button>
</div>
</cd-table>
import { AuthStorageService } from '../../../shared/services/auth-storage.service';
import { TaskListService } from '../../../shared/services/task-list.service';
import { TaskWrapperService } from '../../../shared/services/task-wrapper.service';
+import { RbdTrashPurgeModalComponent } from '../rbd-trash-purge-modal/rbd-trash-purge-modal.component';
import { RbdTrashRestoreModalComponent } from '../rbd-trash-restore-modal/rbd-trash-restore-modal.component';
@Component({
isExpired(expiresAt): boolean {
return moment().isAfter(expiresAt);
}
+
+ purgeModal() {
+ this.modalService.show(RbdTrashPurgeModalComponent);
+ }
}
--- /dev/null
+<cd-modal>
+ <ng-container i18n
+ class="modal-title">Purge Trash</ng-container>
+
+ <ng-container class="modal-content">
+ <form name="purgeForm"
+ class="form"
+ #formDir="ngForm"
+ [formGroup]="purgeForm"
+ novalidate>
+ <div class="modal-body">
+ <p>
+ <ng-container i18n>To purge, select one or All images and click</ng-container>
+ <kbd i18n>Purge Trash</kbd>.
+ </p>
+
+ <div class="form-group">
+ <label class="center-block"
+ i18n>Pool:
+ </label>
+ <input class="form-control"
+ type="text"
+ placeholder="Pool name..."
+ i18n-placeholder
+ formControlName="poolName"
+ *ngIf="!poolPermission.read">
+ <select class="form-control"
+ formControlName="poolName"
+ *ngIf="poolPermission.read">
+ <option value=""
+ i18n>All</option>
+ <option *ngFor="let pool of pools"
+ [value]="pool">{{ pool }}</option>
+ </select>
+ </div>
+ </div>
+
+ <div class="modal-footer">
+ <div class="button-group text-right">
+ <cd-submit-button i18n
+ [form]="purgeForm"
+ (submitAction)="purge()">
+ Purge Trash
+ </cd-submit-button>
+ <button i18n
+ type="button"
+ class="btn btn-sm btn-default"
+ (click)="modalRef.hide()">Cancel</button>
+ </div>
+ </div>
+ </form>
+ </ng-container>
+</cd-modal>
--- /dev/null
+import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
+import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
+import { ReactiveFormsModule } from '@angular/forms';
+import { RouterTestingModule } from '@angular/router/testing';
+
+import { ToastModule } from 'ng2-toastr';
+import { BsModalRef } from 'ngx-bootstrap';
+
+import { configureTestBed } from '../../../../testing/unit-test-helper';
+import { Permission } from '../../../shared/models/permissions';
+import { NotificationService } from '../../../shared/services/notification.service';
+import { SharedModule } from '../../../shared/shared.module';
+import { RbdTrashPurgeModalComponent } from './rbd-trash-purge-modal.component';
+
+describe('RbdTrashPurgeModalComponent', () => {
+ let component: RbdTrashPurgeModalComponent;
+ let fixture: ComponentFixture<RbdTrashPurgeModalComponent>;
+ let httpTesting: HttpTestingController;
+
+ configureTestBed({
+ imports: [
+ HttpClientTestingModule,
+ ReactiveFormsModule,
+ SharedModule,
+ ToastModule.forRoot(),
+ RouterTestingModule
+ ],
+ declarations: [RbdTrashPurgeModalComponent],
+ providers: [BsModalRef]
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(RbdTrashPurgeModalComponent);
+ httpTesting = TestBed.get(HttpTestingController);
+ component = fixture.componentInstance;
+ });
+
+ it('should create', () => {
+ fixture.detectChanges();
+ expect(component).toBeTruthy();
+ });
+
+ it(
+ 'should finish ngOnInit',
+ fakeAsync(() => {
+ component.poolPermission = new Permission(['read', 'create', 'update', 'delete']);
+ fixture.detectChanges();
+ const req = httpTesting.expectOne('api/pool?attrs=pool_name,application_metadata');
+ req.flush([
+ {
+ application_metadata: ['foo'],
+ pool_name: 'bar'
+ },
+ {
+ application_metadata: ['rbd'],
+ pool_name: 'baz'
+ }
+ ]);
+ tick();
+ expect(component.pools).toEqual(['baz']);
+ expect(component.purgeForm).toBeTruthy();
+ })
+ );
+
+ it('should call ngOnInit without pool permissions', () => {
+ component.poolPermission = new Permission([]);
+ component.ngOnInit();
+ httpTesting.expectOne('api/summary');
+ httpTesting.verify();
+ });
+
+ describe('should call purge', () => {
+ let notificationService: NotificationService;
+ let modalRef: BsModalRef;
+ let req;
+
+ beforeEach(() => {
+ fixture.detectChanges();
+ notificationService = TestBed.get(NotificationService);
+ modalRef = TestBed.get(BsModalRef);
+
+ component.purgeForm.patchValue({ poolName: 'foo' });
+
+ spyOn(modalRef, 'hide').and.stub();
+ spyOn(component.purgeForm, 'setErrors').and.stub();
+ spyOn(notificationService, 'show').and.stub();
+
+ component.purge();
+
+ req = httpTesting.expectOne('api/block/image/trash/purge/?pool_name=foo');
+ });
+
+ it('with success', () => {
+ req.flush(null);
+ expect(component.purgeForm.setErrors).toHaveBeenCalledTimes(0);
+ expect(component.modalRef.hide).toHaveBeenCalledTimes(1);
+ });
+
+ it('with failure', () => {
+ req.flush(null, { status: 500, statusText: 'failure' });
+ expect(component.purgeForm.setErrors).toHaveBeenCalledTimes(1);
+ expect(component.modalRef.hide).toHaveBeenCalledTimes(0);
+ });
+ });
+});
--- /dev/null
+import { Component, OnInit } from '@angular/core';
+
+import { BsModalRef } from 'ngx-bootstrap';
+
+import { PoolService } from '../../../shared/api/pool.service';
+import { RbdService } from '../../../shared/api/rbd.service';
+import { CdFormBuilder } from '../../../shared/forms/cd-form-builder';
+import { CdFormGroup } from '../../../shared/forms/cd-form-group';
+import { FinishedTask } from '../../../shared/models/finished-task';
+import { Permission } from '../../../shared/models/permissions';
+import { AuthStorageService } from '../../../shared/services/auth-storage.service';
+import { TaskWrapperService } from '../../../shared/services/task-wrapper.service';
+
+@Component({
+ selector: 'cd-rbd-trash-purge-modal',
+ templateUrl: './rbd-trash-purge-modal.component.html',
+ styleUrls: ['./rbd-trash-purge-modal.component.scss']
+})
+export class RbdTrashPurgeModalComponent implements OnInit {
+ poolPermission: Permission;
+ purgeForm: CdFormGroup;
+ pools: any[];
+
+ constructor(
+ private authStorageService: AuthStorageService,
+ private rbdService: RbdService,
+ public modalRef: BsModalRef,
+ private fb: CdFormBuilder,
+ private poolService: PoolService,
+ private taskWrapper: TaskWrapperService
+ ) {
+ this.poolPermission = this.authStorageService.getPermissions().pool;
+ }
+
+ createForm() {
+ this.purgeForm = this.fb.group({
+ poolName: ''
+ });
+ }
+
+ ngOnInit() {
+ if (this.poolPermission.read) {
+ this.poolService.list(['pool_name', 'application_metadata']).then((resp) => {
+ this.pools = resp
+ .filter((pool) => pool.application_metadata.includes('rbd'))
+ .map((pool) => pool.pool_name);
+ });
+ }
+
+ this.createForm();
+ }
+
+ purge() {
+ const poolName = this.purgeForm.getValue('poolName') || '';
+ this.taskWrapper
+ .wrapTaskAroundCall({
+ task: new FinishedTask('rbd/trash/purge', {
+ pool_name: poolName
+ }),
+ call: this.rbdService.purgeTrash(poolName)
+ })
+ .subscribe(
+ undefined,
+ () => {
+ this.purgeForm.setErrors({ cdSubmitButton: true });
+ },
+ () => {
+ this.modalRef.hide();
+ }
+ );
+ }
+}
);
}
+ purgeTrash(poolName) {
+ return this.http.post(`api/block/image/trash/purge/?pool_name=${poolName}`, null, {
+ observe: 'response'
+ });
+ }
+
restoreTrash(poolName, imageId, newImageName) {
return this.http.post(
`api/block/image/trash/${poolName}/${imageId}/restore`,