import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
import { PoolEditPeerModalComponent } from '../pool-edit-peer-modal/pool-edit-peer-modal.component';
import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
+import { DeletionImpact } from '~/app/shared/enum/critical-confirmation-modal-impact.enum';
const BASE_URL = '/block/mirroring';
@Component({
const peerUUID = this.getPeerUUID();
this.modalService.show(CriticalConfirmationModalComponent, {
+ impact: DeletionImpact.high,
itemDescription: $localize`mirror peer`,
itemNames: [`${poolName} (${peerUUID})`],
submitActionObservable: () =>
import { RBDImageFormat, RbdModel } from './rbd-model';
import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
import { RBDActionHelpers } from '../rbd-contants';
+import { DeletionImpact } from '~/app/shared/enum/critical-confirmation-modal-impact.enum';
const BASE_URL = 'block/rbd';
@Component({
const imageSpec = new ImageSpec(poolName, namespace, imageName);
this.cdsModalService.show(CriticalConfirmationModalComponent, {
+ impact: DeletionImpact.high,
itemDescription: 'RBD',
- itemNames: [imageSpec],
+ itemNames: [imageSpec.imageName],
bodyTemplate: this.deleteTpl,
bodyContext: {
hasSnapshots: this.hasSnapshots(),
import { RbdSnapshotActionsModel } from './rbd-snapshot-actions.model';
import { RbdSnapshotModel } from './rbd-snapshot.model';
import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
+import { DeletionImpact } from '~/app/shared/enum/critical-confirmation-modal-impact.enum';
@Component({
selector: 'cd-rbd-snapshot-list',
deleteSnapshotModal() {
const snapshotName = this.selection.selected[0].name;
this.modalRef = this.cdsModalService.show(CriticalConfirmationModalComponent, {
+ impact: DeletionImpact.high,
itemDescription: $localize`RBD snapshot`,
itemNames: [snapshotName],
submitAction: () => this._asyncTask('deleteSnapshot', 'rbd/snap/delete', snapshotName)
[columns]="snapshot.columns"
identifier="name"
forceIdentifier="true"
- selectionType="multiClick"
+ selectionType="single"
(updateSelection)="snapshot.updateSelection($event)">
<cd-table-actions class="table-actions"
[permission]="permission"
import { FormModalComponent } from '~/app/shared/components/form-modal/form-modal.component';
import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
import { CellTemplate } from '~/app/shared/enum/cell-template.enum';
+import { DeletionImpact } from '~/app/shared/enum/critical-confirmation-modal-impact.enum';
import { Icons } from '~/app/shared/enum/icons.enum';
import { NotificationType } from '~/app/shared/enum/notification-type.enum';
import { CdValidators } from '~/app/shared/forms/cd-validators';
name: this.actionLabels.DELETE,
icon: Icons.destroy,
permission: 'delete',
- click: () => this.deleteSnapshotModal(),
- canBePrimary: (selection) => selection.hasSelection,
- disable: (selection) => !selection.hasSelection
+ click: () => this.deleteSnapshotModal()
}
]
};
deleteSnapshotModal() {
this.modalService.show(CriticalConfirmationModalComponent, {
+ impact: DeletionImpact.high,
itemDescription: $localize`CephFs Snapshot`,
itemNames: this.snapshot.selection.selected.map((snapshot: CephfsSnapshot) => snapshot.name),
submitAction: () => this.deleteSnapshot()
import { HealthService } from '~/app/shared/api/health.service';
import { CephfsAuthModalComponent } from '~/app/ceph/cephfs/cephfs-auth-modal/cephfs-auth-modal.component';
import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
+import { DeletionImpact } from '~/app/shared/enum/critical-confirmation-modal-impact.enum';
const BASE_URL = 'cephfs/fs';
removeVolumeModal() {
const volName = this.selection.first().mdsmap['fs_name'];
this.cdsModalService.show(CriticalConfirmationModalComponent, {
+ impact: DeletionImpact.high,
itemDescription: 'File System',
itemNames: [volName],
actionDescription: 'remove',
import { NgbModalRef } from '@ng-bootstrap/ng-bootstrap';
import _ from 'lodash';
import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
+import { DeletionImpact } from '~/app/shared/enum/critical-confirmation-modal-impact.enum';
@Component({
selector: 'cd-cephfs-subvolume-group',
removeSubVolumeModal() {
const name = this.selection.first().name;
this.cdsModalService.show(CriticalConfirmationModalComponent, {
+ impact: DeletionImpact.high,
itemDescription: 'subvolume group',
itemNames: [name],
actionDescription: 'remove',
import _ from 'lodash';
import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
import { DEFAULT_SUBVOLUME_GROUP } from '~/app/shared/constants/cephfs.constant';
+import { DeletionImpact } from '~/app/shared/enum/critical-confirmation-modal-impact.enum';
@Component({
selector: 'cd-cephfs-subvolume-list',
this.errorMessage = '';
this.selectedName = this.selection.first().name;
this.modalService.show(CriticalConfirmationModalComponent, {
- actionDescription: 'Remove',
+ impact: DeletionImpact.high,
+ actionDescription: 'remove',
itemNames: [this.selectedName],
itemDescription: 'Subvolume',
childFormGroup: this.removeForm,
import { CdValidators } from '~/app/shared/forms/cd-validators';
import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
import { DEFAULT_SUBVOLUME_GROUP } from '~/app/shared/constants/cephfs.constant';
+import { DeletionImpact } from '~/app/shared/enum/critical-confirmation-modal-impact.enum';
@Component({
selector: 'cd-cephfs-subvolume-snapshots-list',
const subVolumeGroupName = this.activeGroupName;
const fsName = this.fsName;
this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
- actionDescription: this.actionLabels.REMOVE,
+ impact: DeletionImpact.high,
+ actionDescription: 'remove',
itemNames: [snapshotName],
itemDescription: 'Snapshot',
submitAction: () =>
import { URLBuilderService } from '~/app/shared/services/url-builder.service';
import { HostFormComponent } from './host-form/host-form.component';
import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
+import { DeletionImpact } from '~/app/shared/enum/critical-confirmation-modal-impact.enum';
const BASE_URL = 'hosts';
deleteAction() {
const hostname = this.selection.first().hostname;
this.modalRef = this.cdsModalService.show(CriticalConfirmationModalComponent, {
+ impact: DeletionImpact.high,
itemDescription: 'Host',
itemNames: [hostname],
actionDescription: 'remove',
<cd-table [data]="osds"
(fetchData)="getOsdList($event)"
[columns]="columns"
- selectionType="multiClick"
+ selectionType="single"
[hasDetails]="true"
(setExpandedRow)="setExpandedRow($event)"
(updateSelection)="updateSelection($event)"
import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
import { CdTableFetchDataContext } from '~/app/shared/models/cd-table-fetch-data-context';
import { Osd } from '~/app/shared/models/osd.model';
+import { DeletionImpact } from '~/app/shared/enum/critical-confirmation-modal-impact.enum';
const BASE_URL = 'osd';
childFormGroupTemplate?: TemplateRef<any>
): void {
check(this.getSelectedOsdIds()).subscribe((result) => {
+ const osdIds = this.getSelectedOsdIds();
this.cdsModalService.show(CriticalConfirmationModalComponent, {
+ impact: DeletionImpact.high,
+ itemNames: osdIds,
actionDescription: actionDescription,
itemDescription: itemDescription,
bodyTemplate: this.criticalConfirmationTpl,
missingStats: result.missing_stats,
storedPgs: result.stored_pgs,
actionDescription: templateItemDescription,
- osdIds: this.getSelectedOsdIds()
+ osdIds
},
childFormGroup: childFormGroup,
childFormGroupTemplate: childFormGroupTemplate,
import { SettingsService } from '~/app/shared/api/settings.service';
import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
import { CellTemplate } from '~/app/shared/enum/cell-template.enum';
+import { DeletionImpact } from '~/app/shared/enum/critical-confirmation-modal-impact.enum';
const BASE_URL = 'services';
deleteAction() {
const service = this.selection.first();
this.cdsModalService.show(CriticalConfirmationModalComponent, {
+ impact: DeletionImpact.high,
itemDescription: $localize`Service`,
itemNames: [service.service_name],
actionDescription: 'delete',
import { getFsalFromRoute, getPathfromFsal } from '../utils';
import { SUPPORTED_FSAL } from '../models/nfs.fsal';
import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
+import { DeletionImpact } from '~/app/shared/enum/critical-confirmation-modal-impact.enum';
export enum RgwExportType {
BUCKET = 'bucket',
const export_id = this.selection.first().export_id;
this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
+ impact: DeletionImpact.high,
itemDescription: $localize`NFS export`,
itemNames: [`${cluster_id}:${export_id}`],
submitActionObservable: () =>
import { Pool } from '../pool';
import { PoolStat, PoolStats } from '../pool-stat';
import { ModalCdsService } from '~/app/shared/services/modal-cds.service';
+import { DeletionImpact } from '~/app/shared/enum/critical-confirmation-modal-impact.enum';
const BASE_URL = 'pool';
deletePoolModal() {
const name = this.selection.first().pool_name;
this.modalService.show(CriticalConfirmationModalComponent, {
+ impact: DeletionImpact.high,
itemDescription: 'Pool',
itemNames: [name],
submitActionObservable: () =>
[data]="buckets"
[columns]="columns"
columnMode="flex"
- selectionType="multiClick"
+ selectionType="single"
[hasDetails]="true"
(setExpandedRow)="setExpandedRow($event)"
(updateSelection)="updateSelection($event)"
import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
import { URLBuilderService } from '~/app/shared/services/url-builder.service';
import { Bucket } from '../models/rgw-bucket';
+import { DeletionImpact } from '~/app/shared/enum/critical-confirmation-modal-impact.enum';
const BASE_URL = 'rgw/bucket';
permission: 'delete',
icon: Icons.destroy,
click: () => this.deleteAction(),
- disable: () => !this.selection.hasSelection,
- name: this.actionLabels.DELETE,
- canBePrimary: (selection: CdTableSelection) => selection.hasMultiSelection
+ name: this.actionLabels.DELETE
};
this.tableActions = [addAction, editAction, deleteAction];
this.setTableRefreshTimeout();
deleteAction() {
const itemNames = this.selection.selected.map((bucket: any) => bucket['bid']);
this.modalService.show(CriticalConfirmationModalComponent, {
- itemDescription: this.selection.hasSingleSelection ? $localize`bucket` : $localize`buckets`,
+ itemDescription: $localize`bucket`,
+ impact: DeletionImpact.high,
itemNames: itemNames,
bodyTemplate: this.deleteTpl,
submitActionObservable: () => {
[data]="users"
[columns]="columns"
columnMode="flex"
- selectionType="multiClick"
+ selectionType="single"
[hasDetails]="true"
(setExpandedRow)="setExpandedRow($event)"
(updateSelection)="updateSelection($event)"
import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
import { TableComponent } from '~/app/shared/datatable/table/table.component';
import { CellTemplate } from '~/app/shared/enum/cell-template.enum';
+import { DeletionImpact } from '~/app/shared/enum/critical-confirmation-modal-impact.enum';
import { Icons } from '~/app/shared/enum/icons.enum';
import { CdTableAction } from '~/app/shared/models/cd-table-action';
import { CdTableColumn } from '~/app/shared/models/cd-table-column';
icon: Icons.destroy,
click: () => this.deleteAction(),
disable: () => !this.selection.hasSelection,
- name: this.actionLabels.DELETE,
- canBePrimary: (selection: CdTableSelection) => selection.hasMultiSelection
+ name: this.actionLabels.DELETE
};
this.tableActions = [addAction, editAction, deleteAction];
this.setTableRefreshTimeout();
deleteAction() {
this.modalService.show(CriticalConfirmationModalComponent, {
+ impact: DeletionImpact.high,
itemDescription: this.selection.hasSingleSelection ? $localize`user` : $localize`users`,
itemNames: this.selection.selected.map((user: any) => user['uid']),
submitActionObservable: (): Observable<any> => {
import { CriticalConfirmationModalComponent } from '~/app/shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
import { ActionLabelsI18n } from '~/app/shared/constants/app.constants';
import { CellTemplate } from '~/app/shared/enum/cell-template.enum';
+import { DeletionImpact } from '~/app/shared/enum/critical-confirmation-modal-impact.enum';
import { Icons } from '~/app/shared/enum/icons.enum';
import { NotificationType } from '~/app/shared/enum/notification-type.enum';
import { CdTableAction } from '~/app/shared/models/cd-table-action';
}
this.modalRef = this.modalService.show(CriticalConfirmationModalComponent, {
+ impact: DeletionImpact.high,
itemDescription: 'User',
itemNames: [username],
submitAction: () => this.deleteUser(username)
<ng-container *ngTemplateOutlet="deletionHeading"></ng-container>
</cds-modal-header>
- <section cdsModalContent>
- <form name="deletionForm"
- #formDir="ngForm"
- [formGroup]="deletionForm"
- novalidate>
+ <section cdsModalContent>
+ <form name="deletionForm"
+ #formDir="ngForm"
+ [formGroup]="deletionForm"
+ novalidate>
<cd-alert-panel *ngIf="infoMessage"
type="info"
spacingClass="mb-3"
</ng-template>
<ng-container *ngTemplateOutlet="childFormGroupTemplate; context:{form:deletionForm}"></ng-container>
<div class="form-item">
- <cds-checkbox id="confirmation"
- name="confirmation"
- formControlName="confirmation"
- autofocus
- [required]="true"
- modal-primary-focus
- i18n>Yes, I am sure.</cds-checkbox>
+ <ng-container *ngIf="impact == impactEnum.normal; else highImpactDeletion">
+ <cds-checkbox id="confirmation"
+ name="confirmation"
+ formControlName="confirmation"
+ autofocus
+ [required]="true"
+ modal-primary-focus
+ i18n>Yes, I am sure.</cds-checkbox>
+ </ng-container>
+ <ng-template #highImpactDeletion>
+ <cds-text-label label="Resource Name"
+ for="resource_name"
+ cdRequiredField="Resource Name"
+ [invalid]="!deletionForm.controls.confirmInput.valid && deletionForm.controls.confirmInput.dirty"
+ [invalidText]="ResourceError"
+ i18n>Resource Name
+ <input cdsText
+ type="text"
+ placeholder="Enter resource name to delete"
+ id="resource_name"
+ formControlName="confirmInput"/>
+ </cds-text-label>
+ <ng-template #ResourceError>
+ <span *ngIf="deletionForm.showError('confirmInput', formDir, 'required')"
+ class="invalid-feedback">
+ <ng-container i18n>This field is required.</ng-container>
+ </span>
+ <span *ngIf="deletionForm.showError('confirmInput', formDir, 'matchResource')"
+ class="invalid-feedback">
+ <ng-container i18n>Enter the correct resource name.</ng-container>
+ </span>
+ </ng-template>
+ </ng-template>
</div>
</div>
</form>
import { CriticalConfirmationModalComponent } from './critical-confirmation-modal.component';
import { CheckboxModule, ModalService, PlaceholderService } from 'carbon-components-angular';
import { ModalCdsService } from '../../services/modal-cds.service';
+import { DeletionImpact } from '../../enum/critical-confirmation-modal-impact.enum';
@NgModule({})
export class MockModule {}
});
describe('component functions', () => {
- const changeValue = (value: boolean) => {
- const ctrl = component.deletionForm.get('confirmation');
+ const changeValue = (formControl: string, value: any) => {
+ const ctrl = component.deletionForm.get(formControl);
ctrl.setValue(value);
ctrl.markAsDirty();
ctrl.updateValueAndValidity();
beforeEach(() => {
component.deletionForm.reset();
+ const ctrl = component.deletionForm.get('impact');
+ ctrl.setValue(DeletionImpact.normal);
+ ctrl.markAsDirty();
+ ctrl.updateValueAndValidity();
+ component.deletionForm.get('confirmation').updateValueAndValidity();
});
it('should test empty values', () => {
- component.deletionForm.reset();
testValidation(true, 'required', true);
+ changeValue('confirmation', true);
+ changeValue('confirmation', false);
+ testValidation(true, 'required', true);
+ });
+ });
+
+ describe('validate confirmInput', () => {
+ const testValidation = (submitted: boolean, error: string, expected: boolean) => {
+ expect(
+ component.deletionForm.showError('confirmInput', <NgForm>{ submitted: submitted }, error)
+ ).toBe(expected);
+ };
+
+ beforeEach(() => {
component.deletionForm.reset();
- changeValue(true);
- changeValue(false);
+ const ctrl = component.deletionForm.get('impact');
+ ctrl.setValue(DeletionImpact.high);
+ ctrl.markAsDirty();
+ ctrl.updateValueAndValidity();
+ component.deletionForm.get('confirmInput').updateValueAndValidity();
+ });
+
+ it('should test empty values', () => {
+ testValidation(true, 'required', true);
+ changeValue('confirmInput', 'dummytext');
+ changeValue('confirmInput', '');
testValidation(true, 'required', true);
});
+
+ it('should give error, if entered resource name is wrong', () => {
+ component.itemNames = ['resource1'];
+ changeValue('confirmInput', 'dummytext');
+ testValidation(true, 'matchResource', true);
+ });
+
+ it('should give error, if entered resource name is correct', () => {
+ component.itemNames = ['resource1'];
+ changeValue('confirmInput', 'resource1');
+ testValidation(true, 'matchResource', false);
+ });
});
describe('deletion call', () => {
import { Component, Inject, OnInit, Optional, TemplateRef, ViewChild } from '@angular/core';
-import { UntypedFormControl, Validators } from '@angular/forms';
-
+import { UntypedFormControl, AbstractControl, ValidationErrors, Validators } from '@angular/forms';
import { Observable } from 'rxjs';
import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
import { SubmitButtonComponent } from '../submit-button/submit-button.component';
import { BaseModal } from 'carbon-components-angular';
+import { CdValidators } from '../../forms/cd-validators';
+import { DeletionImpact } from '../../enum/critical-confirmation-modal-impact.enum';
@Component({
selector: 'cd-deletion-modal',
@ViewChild(SubmitButtonComponent, { static: true })
submitButton: SubmitButtonComponent;
deletionForm: CdFormGroup;
-
+ impactEnum = DeletionImpact;
childFormGroup: CdFormGroup;
childFormGroupTemplate: TemplateRef<any>;
constructor(
+ @Optional() @Inject('impact') public impact: DeletionImpact,
@Optional() @Inject('itemDescription') public itemDescription: 'entry',
@Optional() @Inject('itemNames') public itemNames: string[],
@Optional() @Inject('actionDescription') public actionDescription = 'delete',
) {
super();
this.actionDescription = actionDescription || 'delete';
+ this.impact = this.impact || DeletionImpact.normal;
}
ngOnInit() {
const controls = {
- confirmation: new UntypedFormControl(false, [Validators.requiredTrue])
+ impact: new UntypedFormControl(this.impact),
+ confirmation: new UntypedFormControl(false, {
+ validators: [
+ CdValidators.composeIf(
+ {
+ impact: DeletionImpact.normal
+ },
+ [Validators.requiredTrue]
+ )
+ ]
+ }),
+ confirmInput: new UntypedFormControl('', [
+ CdValidators.composeIf({ impact: this.impactEnum.high }, [
+ this.matchResourceName.bind(this),
+ Validators.required
+ ])
+ ])
};
if (this.childFormGroup) {
controls['child'] = this.childFormGroup;
}
}
+ matchResourceName(control: AbstractControl): ValidationErrors | null {
+ if (this.itemNames && control.value != this.itemNames[0]) {
+ return { matchResource: true };
+ }
+ return null;
+ }
+
callSubmitAction() {
if (this.submitActionObservable) {
this.submitActionObservable().subscribe({
--- /dev/null
+export enum DeletionImpact {
+ normal = 'normal',
+ high = 'high' // NOTE: User should be able to select only single resource while deleting
+}
),
// RGW operations
'rgw/bucket/delete': this.newTaskMessage(this.commonOperations.delete, (metadata) => {
- return $localize`${
- metadata.bucket_names.length > 1 ? 'selected buckets' : metadata.bucket_names[0]
- }`;
+ return $localize`${metadata.bucket_names[0]}`;
}),
'rgw/accounts': this.newTaskMessage(this.commonOperations.delete, (metadata) => {
return $localize`${`account '${metadata.account_names[0]}'`}`;