@PageHelper.restrictTo(pages.create.url)
create(group_id: string, status: string, bucket_name: string) {
// Enter in group_id
- cy.get('#group_id').type(group_id);
+ cy.get('#group_id').type(group_id, { force: true });
// Show Status
this.selectOption('status', status);
cy.get('#status').should('have.class', 'ng-valid');
// Enter the bucket_name
cy.get('#bucket_name').type(bucket_name);
+ cy.get('#bucket_name').should('have.class', 'ng-valid');
// Click the create button and wait for policy to be made
- cy.contains('button', 'Create Sync Policy Group').wait(WAIT_TIMER).click();
+ cy.contains('cd-submit-button button', 'Create').click();
this.getFirstTableCell(group_id).should('exist');
}
// Change the status field
this.selectOption('status', status);
- cy.contains('button', 'Edit Sync Policy Group').click();
+ cy.contains('cd-submit-button button', 'Edit').click();
this.searchTable(group_id);
cy.get(`[cdstabledata]:nth-child(${this.columnIndex.status})`)
-<cd-modal [modalRef]="activeModal">
- <ng-container i18n="form title"
- class="modal-title">{{ action | titlecase }} {{ groupType | upperFirst }} Flow</ng-container>
-
- <ng-container class="modal-content">
- <form name="flowForm"
- #frm="ngForm"
- [formGroup]="currentFormGroupContext"
- novalidate>
- <div class="modal-body">
- <div class="form-group row">
- <label class="cd-col-form-label required"
- for="flow_id"
- i18n>Name</label>
- <div class="cd-col-form-input">
- <input class="form-control"
- type="text"
- placeholder="Flow Name..."
- id="flow_id"
- name="flow_id"
- formControlName="flow_id"/>
- </div>
- </div>
- <div class="form-group row">
- <label class="cd-col-form-label"
- for="bucket"
- i18n>Bucket Name</label>
- <div class="cd-col-form-input">
- <input id="bucket"
- name="bucket"
- class="form-control"
- type="text"
- i18n-placeholder
- placeholder="Bucket Name..."
- formControlName="bucket_name"/>
- <span class="invalid-feedback"
- *ngIf="currentFormGroupContext.showError('bucket_name', frm, 'bucketNameNotAllowed')"
- i18n>The bucket with chosen name does not exist.</span>
- </div>
- </div>
- <ng-container *ngIf="groupType == flowType.symmetrical; else directionalFlow">
- <div class="form-group row">
- <label class="cd-col-form-label required"
- for="zones">
- <ng-container i18n>Zones</ng-container>
- <cd-helper>
- <span i18n>Flow need to be associated with atleast one zone</span>
- </cd-helper>
- </label>
- <div class="cd-col-form-input">
- <ng-container *ngTemplateOutlet="zoneMultiSelect;context: { name: 'zones', zone: zones }"></ng-container>
- </div>
- </div>
- </ng-container>
- <ng-template #directionalFlow>
- <div class="form-group row">
- <label class="cd-col-form-label required"
- for="source_zone"
- i18n>Source Zone
- </label>
- <div class="cd-col-form-input">
- <ng-container *ngTemplateOutlet="sourceAndDestZone;context: { name: 'source_zone', zones: zones }"></ng-container>
- </div>
- </div>
- <div class="form-group row">
- <label class="cd-col-form-label required"
- for="destination_zone"
- i18n>Destination Zone</label>
- <div class="cd-col-form-input">
- <ng-container *ngTemplateOutlet="sourceAndDestZone;context: { name: 'destination_zone', zones: zones }"></ng-container>
- </div>
- </div>
+<cds-modal size="sm"
+ [open]="open">
+ <cds-modal-header (closeSelect)="closeModal()">
+ <h3 cdsModalHeaderHeading
+ i18n>{{ action | titlecase }} {{ groupType }} flow</h3>
+ <cd-help-text [formAllFieldsRequired]="true"></cd-help-text>
+ </cds-modal-header>
+ <form name="flowForm"
+ #frm="ngForm"
+ [formGroup]="currentFormGroupContext"
+ novalidate>
+ <div cdsModalContent>
+ <!-- Flow Name-->
+ <div class="form-item">
+ <cds-text-label for="flow_id"
+ [invalid]="!currentFormGroupContext.controls.flow_id.valid && currentFormGroupContext.controls.flow_id.dirty"
+ [invalidText]="flowIdError">
+ <ng-container i18n>Name</ng-container>
+ <input cdsText
+ type="text"
+ id="flow_id"
+ formControlName="flow_id"
+ autofocus
+ [invalid]="!currentFormGroupContext.controls.flow_id.valid && currentFormGroupContext.controls.flow_id.dirty">
+ </cds-text-label>
+ <ng-template #flowIdError>
+ @if (currentFormGroupContext.showError('flow_id', formDir, 'required')) {
+ <span class="invalid-feedback">
+ <ng-container i18n> This field is required. </ng-container>
+ </span>
+ }
</ng-template>
</div>
- <div class="modal-footer">
- <cd-form-button-panel (submitActionEvent)="submit()"
- [form]="currentFormGroupContext"
- [submitText]="(action | titlecase) + ' ' + (groupType | upperFirst) + ' ' + 'Flow'"></cd-form-button-panel>
+ <!-- Bucket Name-->
+ @if (currentFormGroupContext.controls.bucket_name.value) {
+ <div class="form-item">
+ <cds-text-label for="bucket_name"
+ cdOptionalField="Bucket name">
+ <ng-container i18n>Bucket name</ng-container>
+ <input cdsText
+ type="text"
+ id="bucket_name"
+ formControlName="bucket_name"
+ [readOnly]="true">
+ </cds-text-label>
</div>
- </form>
- </ng-container>
-</cd-modal>
-
-<ng-template #zoneMultiSelect
- let-name="name"
- let-zone="zone">
- <cd-select-badges [id]="name"
- [name]="name"
- [customBadges]="zone.customBadges"
- [customBadgeValidators]="zone.data.validators"
- [messages]="zone.data.messages"
- [data]="zone.data.selected"
- [options]="zone.data.available"
- (selection)="zoneSelection()">
- </cd-select-badges>
- <i *ngIf="zone.data.selected.length <= 0"
- i18n-title
- title="Flow should be associated with {{name?.split('_').join(' ')}}"
- class="{{ icons.warning }} icon-warning-color">
- </i>
- <span class="invalid-feedback"
- *ngIf="currentFormGroupContext.showError(name, frm, 'required')"
- i18n>{{name?.split('_').join(' ')}} selection is required!
- </span>
-</ng-template>
+ }
+ <!-- Symmetrical flow zones -->
+ @if (groupType == flowType.symmetrical) {
+ <div class="form-item">
+ <cds-combo-box label="Zones"
+ type="multi"
+ selectionFeedback="top-after-reopen"
+ for="zones"
+ formControlName="zones"
+ [helperText]="'Flow need to be associated with atleast one zone'"
+ i18n-helperText
+ [appendInline]="true"
+ [items]="zones"
+ itemValueKey="content"
+ id="zones"
+ cdDynamicInputCombobox
+ [invalid]="currentFormGroupContext.controls?.zones?.invalid && currentFormGroupContext.controls?.zones?.dirty"
+ [invalidText]="'Zone selection is required!'"
+ i18n>
+ <cds-dropdown-list></cds-dropdown-list>
+ </cds-combo-box>
+ </div>
+ } @else {
+ <div class="form-item">
+ <ng-container *ngTemplateOutlet="sourceAndDestZone;context: { formControl: 'source_zone', zones: zones, name: 'Source zone' }"></ng-container>
+ </div>
+ <div class="form-item">
+ <ng-container *ngTemplateOutlet="sourceAndDestZone;context: { formControl: 'destination_zone', zones: zones, name: 'Destination zone' }"></ng-container>
+ </div>
+ }
+ <!-- Directional flow zones -->
+ </div>
+ <cd-form-button-panel (submitActionEvent)="submit()"
+ [form]="currentFormGroupContext"
+ [modalForm]="true"
+ [submitText]="(action | titlecase)"></cd-form-button-panel>
+ </form>
+</cds-modal>
<ng-template #sourceAndDestZone
let-name="name"
- let-zones="zones">
- <select [id]="name"
- [name]="name"
- class="form-select"
- (change)="onChangeZoneDropdown(name, $event)"
- [autofocus]="editing">
- <option i18n
- *ngIf="zones.data.available.length == 0"
- [ngValue]="null">Loading...</option>
- <option i18n
- *ngIf="zones.data.available.length > 0"
- [ngValue]="null">-- Select {{name.split('_').join(' ')}} --</option>
- <option *ngFor="let destinationZone of zones.data.available"
- [value]="destinationZone.name">{{ destinationZone.name }}</option>
- </select>
- <span class="invalid-feedback"
- *ngIf="currentFormGroupContext.showError(name, frm, 'required')"
- i18n>This field is required.</span>
+ let-zones="zones"
+ let-formControl="formControl"
+ [formGroup]="currentFormGroupContext">
+ <cds-select [label]="name"
+ [formControlName]="formControl"
+ [id]="formControl"
+ [invalid]="currentFormGroupContext.controls[formControl].invalid && (currentFormGroupContext.controls[formControl].dirty)"
+ [invalidText]="zoneError"
+ i18n>
+ @if (zones.length == 0) {
+ <option [ngValue]="null">Loading...</option>
+ }
+ @if (zones.length > 0) {
+ <option [ngValue]="null">-- Select {{name}} --</option>
+ }
+ @for (zone of zones; track zone) {
+ <option [value]="zone.name">{{ zone.name }}</option>
+ }
+ </cds-select>
+ <ng-template #zoneError>
+ @if (currentFormGroupContext.showError(formControl, frm, 'required')) {
+ <span class="invalid-feedback"
+ i18n>This field is required.</span>
+ }
+ </ng-template>
</ng-template>
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RgwMultisiteSyncFlowModalComponent } from './rgw-multisite-sync-flow-modal.component';
-import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { ToastrModule } from 'ngx-toastr';
import { PipesModule } from '~/app/shared/pipes/pipes.module';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { RgwMultisiteService } from '~/app/shared/api/rgw-multisite.service';
import { of } from 'rxjs';
import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core';
+import { ComboBoxModule, ModalModule, SelectModule } from 'carbon-components-angular';
enum FlowType {
symmetrical = 'symmetrical',
ToastrModule.forRoot(),
PipesModule,
ReactiveFormsModule,
- CommonModule
+ CommonModule,
+ SelectModule,
+ ModalModule,
+ ComboBoxModule
],
schemas: [NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA],
- providers: [NgbActiveModal, { provide: RgwMultisiteService, useClass: MultisiteServiceMock }]
+ providers: [
+ { provide: RgwMultisiteService, useClass: MultisiteServiceMock },
+ { provide: 'groupType', useValue: FlowType.symmetrical },
+ { provide: 'groupExpandedRow', useValue: { groupName: 'new', bucket: 'bucket1' } },
+ {
+ provide: 'flowSelectedRow',
+ useValue: { id: 'symmetrical', zones: ['zone1-zg1-realm1'] }
+ },
+ { provide: 'action', useValue: 'create' }
+ ]
}).compileComponents();
fixture = TestBed.createComponent(RgwMultisiteSyncFlowModalComponent);
multisiteServiceMock = (TestBed.inject(RgwMultisiteService) as unknown) as MultisiteServiceMock;
component = fixture.componentInstance;
component.groupType = FlowType.symmetrical;
+ component.groupExpandedRow = { groupName: 'new', bucket: 'bucket1' };
+ component.flowSelectedRow = { id: 'symmetrical', zones: ['zone1-zg1-realm1'] };
+ component.action = 'create';
fixture.detectChanges();
});
it('should call createEditSyncFlow for creating/editing symmetrical sync flow', () => {
component.editing = false;
+ component.ngOnInit();
component.currentFormGroupContext.patchValue({
flow_id: 'symmetrical',
group_id: 'new',
- zones: { added: ['zone1-zg1-realm1'], removed: [] }
+ zones: ['zone1-zg1-realm1']
});
- component.zones.data.selected = ['zone1-zg1-realm1'];
const spy = jest.spyOn(component, 'submit');
const putDataSpy = jest
.spyOn(multisiteServiceMock, 'createEditSyncFlow')
component.submit();
expect(spy).toHaveBeenCalled();
expect(putDataSpy).toHaveBeenCalled();
- expect(putDataSpy).toHaveBeenCalledWith(component.currentFormGroupContext.getRawValue());
+ expect(putDataSpy).toHaveBeenCalledWith({
+ ...component.currentFormGroupContext.getRawValue(),
+ zones: { added: ['zone1-zg1-realm1'], removed: [] }
+ });
});
it('should call createEditSyncFlow for creating/editing directional sync flow', () => {
component.editing = false;
component.groupType = FlowType.directional;
component.ngOnInit();
- fixture.detectChanges();
component.currentFormGroupContext.patchValue({
flow_id: 'directional',
group_id: 'new',
- source_zone: { added: ['zone1-zg1-realm1'], removed: [] },
- destination_zone: { added: ['zone2-zg1-realm1'], removed: [] }
+ source_zone: ['zone1-zg1-realm1'],
+ destination_zone: ['zone2-zg1-realm1']
});
const spy = jest.spyOn(component, 'submit');
const putDataSpy = jest
-import { Component, OnInit } from '@angular/core';
+import { Component, Inject, OnInit } from '@angular/core';
import { UntypedFormControl, Validators } from '@angular/forms';
-import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import { RgwDaemonService } from '~/app/shared/api/rgw-daemon.service';
import { ActionLabelsI18n, SucceededActionLabelsI18n } from '~/app/shared/constants/app.constants';
import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
import { Icons } from '~/app/shared/enum/icons.enum';
import { RgwMultisiteService } from '~/app/shared/api/rgw-multisite.service';
import { NotificationType } from '~/app/shared/enum/notification-type.enum';
-import { ZoneData } from '../models/rgw-multisite-zone-selector';
+import { BaseModal } from 'carbon-components-angular';
+import { ComboBoxItem } from '~/app/shared/models/combo-box.model';
@Component({
selector: 'cd-rgw-multisite-sync-flow-modal',
templateUrl: './rgw-multisite-sync-flow-modal.component.html',
styleUrls: ['./rgw-multisite-sync-flow-modal.component.scss']
})
-export class RgwMultisiteSyncFlowModalComponent implements OnInit {
- action: string;
+export class RgwMultisiteSyncFlowModalComponent extends BaseModal implements OnInit {
editing: boolean = false;
- groupType: FlowType;
- groupExpandedRow: any;
- flowSelectedRow: any;
syncPolicyDirectionalFlowForm: CdFormGroup;
syncPolicySymmetricalFlowForm: CdFormGroup;
syncPolicyPipeForm: CdFormGroup;
currentFormGroupContext: CdFormGroup;
flowType = FlowType;
icons = Icons;
- zones = new ZoneData(false, 'Filter Zones');
+ zones: ComboBoxItem[] = [];
constructor(
- public activeModal: NgbActiveModal,
+ @Inject('groupType') public groupType: FlowType,
+ @Inject('groupExpandedRow') public groupExpandedRow: { groupName: string; bucket: string },
+ @Inject('flowSelectedRow') public flowSelectedRow: { id: string; zones: string[] },
+ @Inject('action') public action: string,
public actionLabels: ActionLabelsI18n,
public notificationService: NotificationService,
private rgwDaemonService: RgwDaemonService,
private rgwZonegroupService: RgwZonegroupService,
private rgwMultisiteService: RgwMultisiteService,
private succeededLabels: SucceededActionLabelsI18n
- ) {}
+ ) {
+ super();
+ }
ngOnInit(): void {
if (this.action === 'edit') {
this.createDirectionalFlowForm();
this.currentFormGroupContext = _.cloneDeep(this.syncPolicyDirectionalFlowForm);
}
- this.currentFormGroupContext.get('bucket_name').disable();
if (this.editing) {
this.currentFormGroupContext.patchValue({
flow_id: this.flowSelectedRow.id,
}
})
)
- .subscribe((zonegroupData: any) => {
+ .subscribe((zonegroupData: { zones: { name: string }[] }) => {
if (zonegroupData && zonegroupData?.zones?.length > 0) {
- const zones: any = [];
- zonegroupData.zones.forEach((zone: any) => {
+ const zones: SelectOption[] = [];
+ zonegroupData.zones.forEach((zone: { name: string }) => {
zones.push(new SelectOption(false, zone.name, ''));
});
- this.zones.data.available = [...zones];
+ this.zones = [...zones].map((zone: { name: string }) => {
+ return { name: zone.name, content: zone.name };
+ });
if (this.editing) {
+ // @TODO: Editing/deletion of directional flow not supported yet.
+ // Integrate it once the backend supports it.
if (this.groupType === FlowType.symmetrical) {
- this.zones.data.selected = [...this.flowSelectedRow.zones];
+ this.zones = [...zones].map((zone: { name: string }) => {
+ if (this.flowSelectedRow.zones.includes(zone.name)) {
+ return { name: zone.name, content: zone.name, selected: true };
+ }
+ return { name: zone.name, content: zone.name };
+ });
+ this.currentFormGroupContext.patchValue({ zones: this.zones });
}
- this.zoneSelection();
}
}
});
});
}
- onChangeZoneDropdown(zoneType: string, event: Event) {
- const selectedVal = (event.target as HTMLSelectElement).value;
- this.currentFormGroupContext.get(zoneType).setValue(selectedVal);
- }
-
commonFormControls(flowType: FlowType) {
return {
bucket_name: new UntypedFormControl(this.groupExpandedRow?.bucket),
};
}
- zoneSelection() {
- if (this.groupType === FlowType.symmetrical) {
- this.currentFormGroupContext.patchValue({
- zones: this.zones.data.selected
- });
- }
- }
-
getZoneData(zoneDataToFilter: string[], zoneDataForCondition: string[]) {
return zoneDataToFilter.filter((zone: string) => !zoneDataForCondition.includes(zone));
}
}
if (this.groupType == FlowType.symmetrical) {
+ const selectedZones = this.currentFormGroupContext.get('zones').value;
if (this.editing) {
- zones.removed = this.getZoneData(this.flowSelectedRow.zones, this.zones.data.selected);
- zones.added = this.getZoneData(this.zones.data.selected, this.flowSelectedRow.zones);
+ zones.removed = this.getZoneData(this.flowSelectedRow.zones, selectedZones);
+ zones.added = this.getZoneData(selectedZones, this.flowSelectedRow.zones);
}
- zones.added = this.assignZoneValue(zones.added, this.zones.data.selected);
+ zones.added = this.assignZoneValue(zones.added, selectedZones);
}
this.rgwMultisiteService
.createEditSyncFlow({ ...this.currentFormGroupContext.getRawValue(), zones: zones })
- .subscribe(
- () => {
+ .subscribe({
+ next: () => {
const action = this.editing ? this.succeededLabels.EDITED : this.succeededLabels.CREATED;
this.notificationService.show(
NotificationType.success,
$localize`${action} Sync Flow '${this.currentFormGroupContext.getValue('flow_id')}'`
);
- this.activeModal.close(NotificationType.success);
},
- () => {
+ error: () => {
// Reset the 'Submit' button.
this.currentFormGroupContext.setErrors({ cdSubmitButton: true });
- this.activeModal.dismiss();
+ },
+ complete: () => {
+ this.closeModal();
}
- );
+ });
}
}
-<cd-modal [modalRef]="activeModal">
- <ng-container i18n="form title"
- class="modal-title">{{ action | titlecase }} Pipe</ng-container>
-
- <ng-container class="modal-content">
- <form name="pipeForm"
- #frm="ngForm"
- [formGroup]="pipeForm"
- novalidate>
- <div class="modal-body">
- <div class="form-group row">
- <label class="cd-col-form-label required"
- for="pipe_id"
- i18n>Name</label>
- <div class="cd-col-form-input">
- <input class="form-control"
- type="text"
- placeholder="Pipe Name..."
- id="pipe_id"
- name="pipe_id"
- formControlName="pipe_id"
- [readonly]="editing"/>
- </div>
- </div>
- <div class="form-group row">
- <label class="cd-col-form-label required"
- for="source_zone"
- i18n>Source Zone </label>
- <div class="cd-col-form-input">
- <ng-container *ngTemplateOutlet="zoneMultiSelect;context: { name: 'source_zones', zone: sourceZones }"></ng-container>
- </div>
- </div>
- <div class="form-group row">
- <label class="cd-col-form-label required"
- for="destination_zone"
- i18n>Destination Zone</label>
- <div class="cd-col-form-input">
- <ng-container *ngTemplateOutlet="zoneMultiSelect;context: { name: 'destination_zones', zone: destZones }"></ng-container>
- </div>
- </div>
- <div class="form-group row">
- <label class="cd-col-form-label"
- for="bucket"
- i18n>Bucket Name</label>
- <div class="cd-col-form-input">
- <input id="bucket"
- name="bucket"
- class="form-control"
- type="text"
- i18n-placeholder
- placeholder="Bucket Name..."
- formControlName="bucket_name"/>
- </div>
- </div>
- <div class="form-group row">
- <label class="cd-col-form-label"
- for="source_bucket"
- i18n>Source Bucket</label>
- <div class="cd-col-form-input">
- <input id="source_bucket"
- name="source_bucket"
- class="form-control"
- type="text"
- i18n-placeholder
- placeholder="Source Bucket Name..."
- formControlName="source_bucket"/>
- <cd-help-text>
- <span i18n>{{ allBucketSelectedHelpText }}</span>
- </cd-help-text>
- </div>
- </div>
- <div class="form-group row">
- <label class="cd-col-form-label"
- for="dest_bucket"
- i18n>Destination Bucket</label>
- <div class="cd-col-form-input">
- <input id="dest_bucket"
- name="dest_bucket"
- class="form-control"
- type="text"
- i18n-placeholder
- placeholder="Destination Bucket Name..."
- formControlName="destination_bucket"/>
- <cd-help-text>
- <span i18n>{{ allBucketSelectedHelpText }}</span>
- </cd-help-text>
- </div>
- </div>
+<cds-modal [open]="open"
+ size="md">
+ <cds-modal-header (closeSelect)="closeModal()">
+ <h3 cdsModalHeaderHeading
+ i18n>{{ action | titlecase }} pipe</h3>
+ <cd-help-text [formAllFieldsRequired]="true"></cd-help-text>
+ </cds-modal-header>
+ <form name="pipeForm"
+ #frm="ngForm"
+ [formGroup]="pipeForm"
+ novalidate>
+ <div cdsModalContent>
+ <!-- Pipe Name -->
+ <div class="form-item">
+ <cds-text-label for="pipe_id"
+ [invalid]="!pipeForm.controls.pipe_id.valid && pipeForm.controls.pipe_id.dirty"
+ [invalidText]="pipeIdError">
+ <ng-container i18n>Name</ng-container>
+ <input cdsText
+ type="text"
+ id="pipe_id"
+ formControlName="pipe_id"
+ autofocus
+ [invalid]="!pipeForm.controls.pipe_id.valid && pipeForm.controls.pipe_id.dirty"
+ [readonly]="editing">
+ </cds-text-label>
+ <ng-template #pipeIdError>
+ @if (pipeForm.showError('pipe_id', formDir, 'required')) {
+ <span class="invalid-feedback">
+ <ng-container i18n> This field is required. </ng-container>
+ </span>
+ }
+ </ng-template>
+ </div>
+ <!-- Source and Destination Zones -->
+ <div class="form-item">
+ <ng-container *ngTemplateOutlet="zoneMultiSelect;context: { formControl: 'source_zones', zone: sourceZones, name: 'Source zone' }"></ng-container>
+ </div>
+ <div class="form-item">
+ <ng-container *ngTemplateOutlet="zoneMultiSelect;context: { formControl: 'destination_zones', zone: destZones, name: 'Destination zone' }"></ng-container>
+ </div>
+ <!-- Bucket Name -->
+ @if (pipeForm.controls.bucket_name.value) {
+ <div class="form-item">
+ <cds-text-label for="bucket_name"
+ cdOptionalField="Bucket name">
+ <ng-container i18n>Bucket name</ng-container>
+ <input cdsText
+ type="text"
+ id="bucket_name"
+ formControlName="bucket_name"
+ autofocus
+ [readonly]="true">
+ </cds-text-label>
+ </div>
+ }
+ <!-- Source Bucket -->
+ <div class="form-item">
+ <cds-text-label for="source_bucket"
+ [helperText]="allBucketSelectedHelpText"
+ i18n-helperText
+ cdOptionalField="Source bucket">
+ <ng-container i18n>Source bucket</ng-container>
+ <input cdsText
+ type="text"
+ id="source_bucket"
+ formControlName="source_bucket">
+ </cds-text-label>
</div>
- <div class="modal-footer">
- <cd-form-button-panel (submitActionEvent)="submit()"
- [form]="pipeForm"
- [submitText]="(action | titlecase) + ' ' + 'Pipe'">
- </cd-form-button-panel>
+ <!-- Destination Bucket -->
+ <div class="form-item">
+ <cds-text-label for="destination_bucket"
+ [helperText]="allBucketSelectedHelpText"
+ i18n-helperText
+ cdOptionalField="Destination bucket">
+ <ng-container i18n>Destination bucket</ng-container>
+ <input cdsText
+ type="text"
+ id="destination_bucket"
+ formControlName="destination_bucket">
+ </cds-text-label>
</div>
- </form>
- </ng-container>
-</cd-modal>
+ </div>
+ <cd-form-button-panel (submitActionEvent)="submit()"
+ [form]="pipeForm"
+ [modalForm]="true"
+ [submitText]="(action | titlecase) + ' ' + 'Pipe'">
+ </cd-form-button-panel>
+ </form>
+</cds-modal>
<ng-template #zoneMultiSelect
let-name="name"
- let-zone="zone">
- <cd-select-badges id="{{ name }}"
- name="{{ name }}"
- [customBadges]="zone.customBadges"
- [customBadgeValidators]="zone.data.validators"
- [messages]="zone.data.messages"
- [data]="zone.data.selected"
- [options]="zone.data.available"
- (selection)="onZoneSelection(name)">
- </cd-select-badges>
- <i *ngIf="zone.data.selected.length <= 0"
- i18n-title
- title="Pipe should be associated with {{ name?.split('_').join(' ') }}"
- class="{{ icons.warning }} icon-warning-color">
- </i>
- <span class="invalid-feedback"
- *ngIf="pipeForm.showError(name, frm, 'required')"
- i18n>{{ name?.split('_').join(' ') }} selection is required!
- </span>
+ let-zone="zone"
+ let-formControl="formControl"
+ [formGroup]="pipeForm">
+ <cds-combo-box [label]="name"
+ type="multi"
+ selectionFeedback="top-after-reopen"
+ [for]="formControl"
+ [formControlName]="formControl"
+ [helperText]="'Pipe need to be associated with atleast one zone'"
+ i18n-helperText
+ [items]="zone"
+ itemValueKey="content"
+ [id]="formControl"
+ [invalid]="pipeForm.controls[formControl].invalid && pipeForm.controls[formControl].dirty"
+ [invalidText]="'Zone selection is required!'"
+ i18n>
+ <cds-dropdown-list></cds-dropdown-list>
+ </cds-combo-box>
</ng-template>
import { of } from 'rxjs';
import { RgwMultisiteService } from '~/app/shared/api/rgw-multisite.service';
import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core';
+import { USER } from '~/app/shared/constants/app.constants';
+import { FlowType } from '../models/rgw-multisite';
+import { ComboBoxModule } from 'carbon-components-angular';
class MultisiteServiceMock {
createEditSyncPipe = jest.fn().mockReturnValue(of(null));
ToastrModule.forRoot(),
PipesModule,
ReactiveFormsModule,
- CommonModule
+ CommonModule,
+ ComboBoxModule
],
schemas: [NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA],
- providers: [NgbActiveModal, { provide: RgwMultisiteService, useClass: MultisiteServiceMock }]
+ providers: [
+ NgbActiveModal,
+ { provide: RgwMultisiteService, useClass: MultisiteServiceMock },
+ { provide: 'groupType', useValue: FlowType.symmetrical },
+ { provide: 'groupExpandedRow', useValue: { groupName: 'new', bucket: 'bucket1' } },
+ {
+ provide: 'pipeSelectedRow',
+ useValue: {
+ source: { zones: ['zone1-zg1-realm1'], bucket: 'bucket1' },
+ dest: { zones: ['zone2-zg1-realm1'], bucket: 'bucket1' },
+ id: 'pipe1',
+ params: { user: 'dashboard', mode: USER }
+ }
+ },
+ { provide: 'action', useValue: 'create' }
+ ]
}).compileComponents();
fixture = TestBed.createComponent(RgwMultisiteSyncPipeModalComponent);
it('should call createEditSyncPipe for creating/editing sync pipe', () => {
component.editing = false;
+ component.ngOnInit();
component.pipeForm.patchValue({
pipe_id: 'pipe1',
group_id: 'new',
source_bucket: '',
- source_zones: { added: ['zone1-zg1-realm1'], removed: [] },
+ source_zones: ['zone1-zg1-realm1'],
destination_bucket: '',
- destination_zones: { added: ['zone2-zg1-realm1'], removed: [] }
+ destination_zones: ['zone2-zg1-realm1']
});
- component.sourceZones.data.selected = ['zone1-zg1-realm1'];
- component.destZones.data.selected = ['zone2-zg1-realm1'];
+ component.sourceZones = [
+ { name: 'zone1-zg1-realm1', content: 'zone1-zg1-realm1', selected: true }
+ ];
+ component.destZones = [
+ { name: 'zone2-zg1-realm1', content: 'zone2-zg1-realm1', selected: true }
+ ];
const spy = jest.spyOn(component, 'submit');
const putDataSpy = jest.spyOn(multisiteServiceMock, 'createEditSyncPipe');
component.submit();
expect(putDataSpy).toHaveBeenCalled();
expect(putDataSpy).toHaveBeenCalledWith({
...component.pipeForm.getRawValue(),
+ source_zones: { added: ['zone1-zg1-realm1'], removed: [] },
+ destination_zones: { added: ['zone2-zg1-realm1'], removed: [] },
mode: '',
user: ''
});
pipe_id: 'pipe1',
group_id: 's3-bucket-replication:enabled',
source_bucket: '',
- source_zones: { added: ['zone1-zg1-realm1'], removed: [] },
+ source_zones: ['zone1-zg1-realm1'],
destination_bucket: '',
- destination_zones: { added: ['zone2-zg1-realm1'], removed: [] }
+ destination_zones: ['zone2-zg1-realm1']
});
component.pipeSelectedRow = {
dest: { bucket: '*', zones: ['zone2-zg1-realm1'] },
id: 'pipi1',
params: {
- dest: {},
- mode: 'user',
- priority: 0,
- source: { filter: { tags: [] } },
+ mode: USER,
user: 'dashboard'
},
source: { bucket: '*', zones: ['zone1-zg1-realm1'] }
};
- component.sourceZones.data.selected = ['zone1-zg1-realm1'];
- component.destZones.data.selected = ['zone2-zg1-realm1'];
+ component.sourceZones = [
+ { name: 'zone1-zg1-realm1', content: 'zone1-zg1-realm1', selected: true }
+ ];
+ component.destZones = [
+ { name: 'zone2-zg1-realm1', content: 'zone2-zg1-realm1', selected: true }
+ ];
const spy = jest.spyOn(component, 'submit');
const putDataSpy = jest.spyOn(multisiteServiceMock, 'createEditSyncPipe');
component.submit();
expect(putDataSpy).toHaveBeenCalled();
expect(putDataSpy).toHaveBeenCalledWith({
...component.pipeForm.getRawValue(),
- mode: 'user',
+ source_zones: { added: ['zone1-zg1-realm1'], removed: [] },
+ destination_zones: { added: ['zone2-zg1-realm1'], removed: [] },
+ mode: USER,
user: 'dashboard'
});
});
-import { Component, OnInit } from '@angular/core';
+import { Component, Inject, OnInit } from '@angular/core';
import { UntypedFormControl, Validators } from '@angular/forms';
import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
import { RgwZonegroup, Zone } from '../models/rgw-multisite';
import { RgwDaemon } from '../models/rgw-daemon';
import { RgwDaemonService } from '~/app/shared/api/rgw-daemon.service';
import { RgwZonegroupService } from '~/app/shared/api/rgw-zonegroup.service';
-import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';
import _ from 'lodash';
import { Icons } from '~/app/shared/enum/icons.enum';
import { RgwMultisiteService } from '~/app/shared/api/rgw-multisite.service';
import { NotificationType } from '~/app/shared/enum/notification-type.enum';
import { NotificationService } from '~/app/shared/services/notification.service';
-import { ZoneData } from '../models/rgw-multisite-zone-selector';
import { SucceededActionLabelsI18n } from '~/app/shared/constants/app.constants';
+import { BaseModal } from 'carbon-components-angular';
+import { ComboBoxItem } from '~/app/shared/models/combo-box.model';
const ALL_ZONES = $localize`All zones (*)`;
const ALL_BUCKET_SELECTED_HELP_TEXT =
'If no value is provided, all the buckets in the zone group will be selected.';
+interface PipeParams {
+ source: { zones: string[]; bucket: string };
+ dest: { zones: string[]; bucket: string };
+ id: string;
+ params: { user: string; mode: string };
+}
+
+interface GroupParams {
+ groupName: string;
+ bucket: string;
+}
+
@Component({
selector: 'cd-rgw-multisite-sync-pipe-modal',
templateUrl: './rgw-multisite-sync-pipe-modal.component.html',
styleUrls: ['./rgw-multisite-sync-pipe-modal.component.scss']
})
-export class RgwMultisiteSyncPipeModalComponent implements OnInit {
- groupExpandedRow: any;
- pipeSelectedRow: any;
+export class RgwMultisiteSyncPipeModalComponent extends BaseModal implements OnInit {
pipeForm: CdFormGroup;
- action: string;
editing: boolean;
- sourceZones = new ZoneData(false, 'Filter Zones');
- destZones = new ZoneData(false, 'Filter Zones');
+ sourceZones: ComboBoxItem[] = [];
+ destZones: ComboBoxItem[] = [];
icons = Icons;
allBucketSelectedHelpText = ALL_BUCKET_SELECTED_HELP_TEXT;
constructor(
- public activeModal: NgbActiveModal,
+ @Inject('groupExpandedRow') public groupExpandedRow: GroupParams,
+ @Inject('pipeSelectedRow') public pipeSelectedRow: PipeParams,
+ @Inject('action') public action: string,
private rgwDaemonService: RgwDaemonService,
private rgwZonegroupService: RgwZonegroupService,
private rgwMultisiteService: RgwMultisiteService,
private notificationService: NotificationService,
private succeededLabels: SucceededActionLabelsI18n
- ) {}
+ ) {
+ super();
+ }
ngOnInit(): void {
if (this.pipeSelectedRow) {
validators: [Validators.required]
})
});
- this.pipeForm.get('bucket_name').disable();
this.rgwDaemonService.selectedDaemon$
.pipe(
switchMap((daemon: RgwDaemon) => {
}
})
)
- .subscribe((zonegroupData: any) => {
+ .subscribe((zonegroupData: { zones: { name: string }[] }) => {
if (zonegroupData && zonegroupData?.zones?.length > 0) {
- let zones: any[] = [];
+ let zones: SelectOption[] = [];
zones.push(new SelectOption(false, ALL_ZONES, ''));
- zonegroupData.zones.forEach((zone: any) => {
+ zonegroupData.zones.forEach((zone: { name: string }) => {
zones.push(new SelectOption(false, zone.name, ''));
});
- this.sourceZones.data.available = JSON.parse(JSON.stringify(zones));
- this.destZones.data.available = JSON.parse(JSON.stringify(zones));
+ this.sourceZones = JSON.parse(JSON.stringify(zones)).map((zone: { name: string }) => {
+ return { name: zone.name, content: zone.name };
+ });
+ this.destZones = JSON.parse(JSON.stringify(zones)).map((zone: { name: string }) => {
+ return { name: zone.name, content: zone.name };
+ });
if (this.editing) {
this.pipeForm.get('pipe_id').disable();
- this.sourceZones.data.selected = [...this.pipeSelectedRow.source.zones];
- this.destZones.data.selected = [...this.pipeSelectedRow.dest.zones];
+ this.sourceZones = [...this.sourceZones].map((zone: { name: string }) => {
+ if (this.pipeSelectedRow.source.zones.includes(zone.name)) {
+ return { name: zone.name, content: zone.name, selected: true };
+ }
+ return { name: zone.name, content: zone.name };
+ });
+ this.destZones = [...this.destZones].map((zone: { name: string }) => {
+ if (this.pipeSelectedRow.dest.zones.includes(zone.name)) {
+ return { name: zone.name, content: zone.name, selected: true };
+ }
+ return { name: zone.name, content: zone.name };
+ });
const availableDestZone: SelectOption[] = [];
this.pipeSelectedRow.dest.zones.forEach((zone: string) => {
availableDestZone.push(new SelectOption(true, zone, ''));
return zones.map((str) => str.replace('*', ALL_ZONES));
}
- onZoneSelection(zoneType: string) {
- if (zoneType === 'source_zones') {
- this.pipeForm.patchValue({
- source_zones: this.sourceZones.data.selected
- });
- } else {
- this.pipeForm.patchValue({
- destination_zones: this.destZones.data.selected
- });
- }
- }
-
getZoneData(zoneDataToFilter: string[], zoneDataForCondition: string[]) {
return zoneDataToFilter.filter((zone: string) => !zoneDataForCondition.includes(zone));
}
return;
}
+ const selectedSourceZones = this.pipeForm.getValue('source_zones');
+ const selectedDestZones = this.pipeForm.getValue('destination_zones');
if (this.editing) {
- destZones.removed = this.getZoneData(
- this.pipeSelectedRow.dest.zones,
- this.destZones.data.selected
- );
- destZones.added = this.getZoneData(
- this.destZones.data.selected,
- this.pipeSelectedRow.dest.zones
- );
+ destZones.removed = this.getZoneData(this.pipeSelectedRow.dest.zones, selectedDestZones);
+ destZones.added = this.getZoneData(selectedDestZones, this.pipeSelectedRow.dest.zones);
sourceZones.removed = this.getZoneData(
this.pipeSelectedRow.source.zones,
- this.sourceZones.data.selected
- );
- sourceZones.added = this.getZoneData(
- this.sourceZones.data.selected,
- this.pipeSelectedRow.source.zones
+ selectedSourceZones
);
+ sourceZones.added = this.getZoneData(selectedSourceZones, this.pipeSelectedRow.source.zones);
}
- sourceZones.added = this.assignZoneValue(sourceZones.added, this.sourceZones.data.selected);
- destZones.added = this.assignZoneValue(destZones.added, this.destZones.data.selected);
+ sourceZones.added = this.assignZoneValue(sourceZones.added, selectedSourceZones);
+ destZones.added = this.assignZoneValue(destZones.added, selectedDestZones);
sourceZones.removed = this.replaceWithAsterisk(sourceZones.removed);
destZones.removed = this.replaceWithAsterisk(destZones.removed);
user: this.editing ? this.pipeSelectedRow?.params?.user : '',
mode: this.editing ? this.pipeSelectedRow?.params?.mode : ''
})
- .subscribe(
- () => {
+ .subscribe({
+ next: () => {
const action = this.editing ? this.succeededLabels.EDITED : this.succeededLabels.CREATED;
this.notificationService.show(
NotificationType.success,
$localize`${action} Sync Pipe '${this.pipeForm.getValue('pipe_id')}'`
);
- this.activeModal.close(NotificationType.success);
},
- () => {
+ error: () => {
// Reset the 'Submit' button.
this.pipeForm.setErrors({ cdSubmitButton: true });
- this.activeModal.dismiss();
+ },
+ complete: () => {
+ this.closeModal();
}
- );
+ });
}
}
-<ng-container *ngIf="expandedRow">
+@if (expandedRow) {
<nav
ngbNav
#nav="ngbNav"
<ng-container ngbNavItem="flow">
<a
ngbNavLink
- i18n>Flow</a>
+ i18n>Flow</a>
<ng-template ngbNavContent>
<legend>
Symmetrical
</cd-help-text>
</legend>
<cd-table
- #table
- [data]="pipeData"
- [columns]="pipeCols"
- selectionType="multiClick"
- [searchableObjects]="true"
- [hasDetails]="false"
- [serverSide]="false"
- [toolHeader]="true"
- (updateSelection)="pipeSelection = $event"
- (fetchData)="loadData($event)">
- <div class="table-actions">
- <cd-table-actions
- [permission]="permission"
- [selection]="pipeSelection"
- class="btn-group"
- [tableActions]="pipeTableActions">
- </cd-table-actions>
- </div>
+ #table
+ [data]="pipeData"
+ [columns]="pipeCols"
+ selectionType="multiClick"
+ [searchableObjects]="true"
+ [hasDetails]="false"
+ [serverSide]="false"
+ [toolHeader]="true"
+ (updateSelection)="pipeSelection = $event"
+ (fetchData)="loadData($event)">
+ <div class="table-actions">
+ <cd-table-actions
+ [permission]="permission"
+ [selection]="pipeSelection"
+ class="btn-group"
+ [tableActions]="pipeTableActions">
+ </cd-table-actions>
+ </div>
</cd-table>
</ng-template>
</ng-container>
</nav>
<div [ngbNavOutlet]="nav"></div>
-</ng-container>
+}
<ng-template #deleteTpl>
<cd-alert-panel type="danger"
import { CdTableColumn } from '~/app/shared/models/cd-table-column';
import { CdTableSelection } from '~/app/shared/models/cd-table-selection';
import { Permission } from '~/app/shared/models/permissions';
-import { ModalService } from '~/app/shared/services/modal.service';
import { RgwMultisiteService } from '~/app/shared/api/rgw-multisite.service';
import { DeleteConfirmationModalComponent } from '~/app/shared/components/delete-confirmation-modal/delete-confirmation-modal.component';
import { FinishedTask } from '~/app/shared/models/finished-task';
constructor(
private actionLabels: ActionLabelsI18n,
- private modalService: ModalService,
+ private modalService: ModalCdsService,
private rgwMultisiteService: RgwMultisiteService,
private taskWrapper: TaskWrapperService,
private cdsModalService: ModalCdsService
action: action
};
- this.modalRef = this.modalService.show(RgwMultisiteSyncFlowModalComponent, initialState, {
- size: 'lg'
- });
+ this.modalRef = this.modalService.show(RgwMultisiteSyncFlowModalComponent, initialState);
try {
const res = await this.modalRef.result;
action: action
};
- this.modalRef = this.modalService.show(RgwMultisiteSyncPipeModalComponent, initialState, {
- size: 'lg'
- });
+ this.modalRef = this.modalService.show(RgwMultisiteSyncPipeModalComponent, initialState);
try {
const res = await this.modalRef.result;
-<cd-modal [pageURL]="pageURL">
- <span class="modal-title"
- i18n>{{ action | titlecase }} {{ resource | upperFirst }}</span>
- <ng-container class="modal-content">
- <form
- #frm="ngForm"
- [formGroup]="syncPolicyForm"
- *cdFormLoading="loading"
- novalidate>
- <div class="modal-body">
+<cds-modal size="sm"
+ [open]="open">
+ <cds-modal-header (closeSelect)="closeModal()">
+ <h3 cdsModalHeaderHeading
+ i18n>{{ action | titlecase }} {{ resource }}</h3>
+ <cd-help-text [formAllFieldsRequired]="true"></cd-help-text>
+ </cds-modal-header>
+ <ng-container *cdFormLoading="loading">
+ <form #frm="ngForm"
+ [formGroup]="syncPolicyForm"
+ *cdFormLoading="loading"
+ novalidate>
+ <div cdsModalContent>
<!-- Group Id -->
- <div class="form-group row">
- <label
- class="cd-col-form-label required"
- for="group_id"
- i18n>Group Name</label>
- <div class="cd-col-form-input">
- <input
- id="group_id"
- name="group_id"
- class="form-control"
- type="text"
- i18n-placeholder
- placeholder="Group Name..."
- formControlName="group_id"
- [readonly]="editing"/>
- <span
- class="invalid-feedback"
- *ngIf="syncPolicyForm.showError('group_id', frm, 'required')"
- i18n>This field is required.</span>
- </div>
+ <div class="form-item">
+ <cds-text-label for="group_id"
+ [invalid]="!syncPolicyForm.controls.group_id.valid && syncPolicyForm.controls.group_id.dirty"
+ [invalidText]="groupIdError">
+ <ng-container i18n>Group name</ng-container>
+ <input cdsText
+ type="text"
+ id="group_id"
+ formControlName="group_id"
+ autofocus
+ [invalid]="!syncPolicyForm.controls.group_id.valid && syncPolicyForm.controls.group_id.dirty"
+ [readonly]="editing"/>
+ </cds-text-label>
+ <ng-template #groupIdError>
+ @if (syncPolicyForm.showError('group_id', frm, 'required')) {
+ <span class="invalid-feedback">
+ <ng-container i18n> This field is required. </ng-container>
+ </span>
+ }
+ </ng-template>
</div>
<!-- Status -->
- <div class="form-group row">
- <label
- class="cd-col-form-label required"
- for="status"
- i18n>Status</label>
- <div class="cd-col-form-input">
- <select
- id="status"
- name="status"
- class="form-select"
- formControlName="status">
- <option
- i18n
- value="{{syncPolicyStatus.ENABLED}}">{{syncPolicyStatus.ENABLED | upperFirst }}</option>
- <option
- i18n
- value="{{syncPolicyStatus.ALLOWED}}">{{syncPolicyStatus.ALLOWED | upperFirst }}</option>
- <option
- i18n
- value="{{syncPolicyStatus.FORBIDDEN}}">{{syncPolicyStatus.FORBIDDEN | upperFirst }}</option>
- </select>
- <span
- class="invalid-feedback"
- *ngIf="syncPolicyForm.showError('status', frm, 'required')"
- i18n>This field is required.</span>
- </div>
+ <div class="form-item">
+ <cds-select label="Status"
+ for="status"
+ formControlName="status"
+ id="status"
+ [invalid]="syncPolicyForm.controls['status'].invalid && (syncPolicyForm.controls['status'].dirty)"
+ [invalidText]="statusError"
+ i18n>
+ <ng-container>Status</ng-container>
+ <option value="{{syncPolicyStatus.ENABLED}}">{{syncPolicyStatus.ENABLED | upperFirst }}</option>
+ <option value="{{syncPolicyStatus.ALLOWED}}">{{syncPolicyStatus.ALLOWED | upperFirst }}</option>
+ <option value="{{syncPolicyStatus.FORBIDDEN}}">{{syncPolicyStatus.FORBIDDEN | upperFirst }}</option>
+ </cds-select>
+ <ng-template #statusError>
+ @if (syncPolicyForm.showError('status', frm, 'required')) {
+ <span class="invalid-feedback"
+ i18n>This field is required.</span>
+ }
+ </ng-template>
</div>
<!-- Bucket Name -->
- <div class="form-group row">
- <label
- class="cd-col-form-label"
- for="bucket_name"
- i18n>Bucket Name</label>
- <div class="cd-col-form-input">
- <input
- id="bucket_name"
- name="bucket_name"
- class="form-control"
- type="text"
- i18n-placeholder
- placeholder="Bucket Name..."
- formControlName="bucket_name"
- [ngbTypeahead]="bucketDataSource"/>
- <span
- class="invalid-feedback"
- *ngIf="syncPolicyForm.showError('bucket_name', frm, 'bucketNameNotAllowed')"
- i18n>The bucket with chosen name does not exist.</span>
- </div>
- </div>
- </div>
-
- <div class="modal-footer">
- <div class="text-right">
- <cd-form-button-panel (submitActionEvent)="submit()"
- [form]="syncPolicyForm"
- [disabled]="syncPolicyForm.pending || syncPolicyForm.pristine || syncPolicyForm.invalid"
- [submitText]="(action | titlecase) + ' ' + (resource | upperFirst)"></cd-form-button-panel>
+ <div class="form-item">
+ <cds-text-label for="bucket_name"
+ [invalid]="!syncPolicyForm.controls.bucket_name.valid && syncPolicyForm.controls.bucket_name.dirty"
+ [invalidText]="bucketNameError"
+ cdOptionalField="Bucket name">
+ <ng-container i18n>Bucket name</ng-container>
+ <input cdsText
+ type="text"
+ id="bucket_name"
+ formControlName="bucket_name"
+ [invalid]="!syncPolicyForm.controls.bucket_name.valid && syncPolicyForm.controls.bucket_name.dirty"
+ [readonly]="editing"/>
+ </cds-text-label>
+ <ng-template #bucketNameError>
+ @if (syncPolicyForm.showError('bucket_name', frm, 'bucketNameNotAllowed')) {
+ <span class="invalid-feedback"
+ i18n>The bucket with chosen name does not exist.</span>
+ }
+ </ng-template>
</div>
</div>
+ <cd-form-button-panel (submitActionEvent)="submit()"
+ [form]="syncPolicyForm"
+ [modalForm]="true"
+ [submitText]="(action | titlecase)"></cd-form-button-panel>
</form>
</ng-container>
-</cd-modal>
+</cds-modal>
import { RouterTestingModule } from '@angular/router/testing';
import { CUSTOM_ELEMENTS_SCHEMA, NO_ERRORS_SCHEMA } from '@angular/core';
import { SharedModule } from '~/app/shared/shared.module';
+import { ModalModule, SelectModule } from 'carbon-components-angular';
describe('RgwMultisiteSyncPolicyFormComponent', () => {
let component: RgwMultisiteSyncPolicyFormComponent;
PipesModule,
ComponentsModule,
SharedModule,
- RouterTestingModule
+ RouterTestingModule,
+ ModalModule,
+ SelectModule
],
schemas: [NO_ERRORS_SCHEMA, CUSTOM_ELEMENTS_SCHEMA],
providers: []
resource: string;
syncPolicyStatus = RgwMultisiteSyncPolicyStatus;
pageURL: string;
+ open: boolean = false;
bucketDataSource = (text$: Observable<string>) => {
return text$.pipe(
debounceTime(200),
super();
this.editing = this.router.url.includes('(modal:edit');
this.action = this.editing ? this.actionLabels.EDIT : this.actionLabels.CREATE;
- this.resource = $localize`Sync Policy Group`;
+ this.resource = $localize`sync policy group`;
this.createForm();
this.loadingReady();
this.pageURL = 'rgw/multisite/sync-policy';
}
ngOnInit(): void {
+ this.open = this.route.outlet === 'modal';
if (this.editing) {
this.route.paramMap.subscribe((params: any) => {
const groupName = params.get('groupName');
.subscribe((syncPolicy: any) => {
this.loadingReady();
if (syncPolicy) {
- this.syncPolicyForm.get('bucket_name').disable();
this.syncPolicyForm.patchValue({
group_id: syncPolicy.id,
status: syncPolicy.status,
bucketExistence(requiredExistenceResult: boolean): AsyncValidatorFn {
return (control: AbstractControl): Observable<ValidationErrors | null> => {
- if (control.dirty) {
+ if (control.dirty && control.value) {
return observableTimer(500).pipe(
switchMapTo(this.rgwBucketService.exists(control.value)),
map((existenceResult: boolean) =>
return of([]);
}
}
+
+ closeModal(): void {
+ this.router.navigate([this.pageURL, { outlets: { modal: null } }]);
+ }
}