Add disable/enable function to replication rules

Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
AllForNothing 2020-09-16 18:09:10 +08:00
parent 2db1974081
commit abdbd79ed4
13 changed files with 299 additions and 95 deletions

View File

@ -616,7 +616,15 @@
"NAMESPACE_TOOLTIP": "Namespace name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.", "NAMESPACE_TOOLTIP": "Namespace name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
"TAG": "Tag", "TAG": "Tag",
"LABEL": "Label", "LABEL": "Label",
"RESOURCE": "Resource" "RESOURCE": "Resource",
"ENABLE_TITLE": "Enable rule",
"ENABLE_SUMMARY": "Do you want to enable rule {{param}}?",
"DISABLE_TITLE": "Disable rule",
"DISABLE_SUMMARY": "Do you want to disable rule {{param}}?",
"ENABLE_SUCCESS": "Enabled rule successfully",
"ENABLE_FAILED": "Enabling rule failed",
"DISABLE_SUCCESS": "Disabled rule successfully",
"DISABLE_FAILED": "Disabling rule failed"
}, },
"DESTINATION": { "DESTINATION": {
"NEW_ENDPOINT": "New Endpoint", "NEW_ENDPOINT": "New Endpoint",

View File

@ -617,7 +617,15 @@
"NAMESPACE_TOOLTIP": "Namespace name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.", "NAMESPACE_TOOLTIP": "Namespace name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
"TAG": "Tag", "TAG": "Tag",
"LABEL": "Label", "LABEL": "Label",
"RESOURCE": "Resource" "RESOURCE": "Resource",
"ENABLE_TITLE": "Enable rule",
"ENABLE_SUMMARY": "Do you want to enable rule {{param}}?",
"DISABLE_TITLE": "Disable rule",
"DISABLE_SUMMARY": "Do you want to disable rule {{param}}?",
"ENABLE_SUCCESS": "Enabled rule successfully",
"ENABLE_FAILED": "Enabling rule failed",
"DISABLE_SUCCESS": "Disabled rule successfully",
"DISABLE_FAILED": "Disabling rule failed"
}, },
"DESTINATION": { "DESTINATION": {
"NEW_ENDPOINT": "Nuevo Endpoint", "NEW_ENDPOINT": "Nuevo Endpoint",

View File

@ -606,7 +606,15 @@
"NAMESPACE_TOOLTIP": "Namespace name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.", "NAMESPACE_TOOLTIP": "Namespace name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
"TAG": "Tag", "TAG": "Tag",
"LABEL": "Label", "LABEL": "Label",
"RESOURCE": "Resource" "RESOURCE": "Resource",
"ENABLE_TITLE": "Enable rule",
"ENABLE_SUMMARY": "Do you want to enable rule {{param}}?",
"DISABLE_TITLE": "Disable rule",
"DISABLE_SUMMARY": "Do you want to disable rule {{param}}?",
"ENABLE_SUCCESS": "Enabled rule successfully",
"ENABLE_FAILED": "Enabling rule failed",
"DISABLE_SUCCESS": "Disabled rule successfully",
"DISABLE_FAILED": "Disabling rule failed"
}, },
"DESTINATION": { "DESTINATION": {
"NEW_ENDPOINT": "Nouveau Point Final", "NEW_ENDPOINT": "Nouveau Point Final",

View File

@ -616,7 +616,15 @@
"NAMESPACE_TOOLTIP": "Namespace name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.", "NAMESPACE_TOOLTIP": "Namespace name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
"TAG": "Tag", "TAG": "Tag",
"LABEL": "Label", "LABEL": "Label",
"RESOURCE": "Resource" "RESOURCE": "Resource",
"ENABLE_TITLE": "Enable rule",
"ENABLE_SUMMARY": "Do you want to enable rule {{param}}?",
"DISABLE_TITLE": "Disable rule",
"DISABLE_SUMMARY": "Do you want to disable rule {{param}}?",
"ENABLE_SUCCESS": "Enabled rule successfully",
"ENABLE_FAILED": "Enabling rule failed",
"DISABLE_SUCCESS": "Disabled rule successfully",
"DISABLE_FAILED": "Disabling rule failed"
}, },
"DESTINATION": { "DESTINATION": {
"NEW_ENDPOINT": "Novo Endpoint", "NEW_ENDPOINT": "Novo Endpoint",

View File

@ -616,7 +616,15 @@
"NAMESPACE_TOOLTIP": "İsim alanı ismi en az 2 karakter uzunluğunda, küçük harfli karakterler, sayılar ve ._- ile başlamalı ve karakter veya sayılarla başlamalıdır. Ad alanı adı en az 2 karakter uzunluğunda, küçük harf, rakam ve ._- ile başlamalı ve karakter veya rakamlarla başlamalıdır.", "NAMESPACE_TOOLTIP": "İsim alanı ismi en az 2 karakter uzunluğunda, küçük harfli karakterler, sayılar ve ._- ile başlamalı ve karakter veya sayılarla başlamalıdır. Ad alanı adı en az 2 karakter uzunluğunda, küçük harf, rakam ve ._- ile başlamalı ve karakter veya rakamlarla başlamalıdır.",
"TAG": "Etiketlemek", "TAG": "Etiketlemek",
"LABEL": "Etiket", "LABEL": "Etiket",
"RESOURCE": "Kaynak" "RESOURCE": "Kaynak",
"ENABLE_TITLE": "Enable rule",
"ENABLE_SUMMARY": "Do you want to enable rule {{param}}?",
"DISABLE_TITLE": "Disable rule",
"DISABLE_SUMMARY": "Do you want to disable rule {{param}}?",
"ENABLE_SUCCESS": "Enabled rule successfully",
"ENABLE_FAILED": "Enabling rule failed",
"DISABLE_SUCCESS": "Disabled rule successfully",
"DISABLE_FAILED": "Disabling rule failed"
}, },
"DESTINATION": { "DESTINATION": {
"NEW_ENDPOINT": "Yeni Uç Nokta", "NEW_ENDPOINT": "Yeni Uç Nokta",

View File

@ -617,7 +617,15 @@
"NAMESPACE_TOOLTIP": "Namespace名称由小写字符、数字和._-组成且至少2个字符并以字符或者数字开头。", "NAMESPACE_TOOLTIP": "Namespace名称由小写字符、数字和._-组成且至少2个字符并以字符或者数字开头。",
"TAG": "Tag", "TAG": "Tag",
"LABEL": "标签", "LABEL": "标签",
"RESOURCE": "资源" "RESOURCE": "资源",
"ENABLE_TITLE": "启用规则",
"ENABLE_SUMMARY": "确定启用规则 {{param}}?",
"DISABLE_TITLE": "禁用规则",
"DISABLE_SUMMARY": "确认禁用规则 {{param}}?",
"ENABLE_SUCCESS": "启用规则成功",
"ENABLE_FAILED": "启用规则失败",
"DISABLE_SUCCESS": "禁用规则成功",
"DISABLE_FAILED": "禁用规则失败"
}, },
"DESTINATION": { "DESTINATION": {
"NEW_ENDPOINT": "新建目标", "NEW_ENDPOINT": "新建目标",

View File

@ -613,7 +613,15 @@
"NAMESPACE_TOOLTIP": "Namespace名稱由小寫字符、數字和._-組成且至少2個字符並以字符或者數字開頭。", "NAMESPACE_TOOLTIP": "Namespace名稱由小寫字符、數字和._-組成且至少2個字符並以字符或者數字開頭。",
"TAG":"標籤", "TAG":"標籤",
"LABEL": "標籤", "LABEL": "標籤",
"RESOURCE": "資源" "RESOURCE": "資源",
"ENABLE_TITLE": "Enable rule",
"ENABLE_SUMMARY": "Do you want to enable rule {{param}}?",
"DISABLE_TITLE": "Disable rule",
"DISABLE_SUMMARY": "Do you want to disable rule {{param}}?",
"ENABLE_SUCCESS": "Enabled rule successfully",
"ENABLE_FAILED": "Enabling rule failed",
"DISABLE_SUCCESS": "Disabled rule successfully",
"DISABLE_FAILED": "Disabling rule failed"
}, },
"DESTINATION":{ "DESTINATION":{
"NEW_ENDPOINT": "新建目標", "NEW_ENDPOINT": "新建目標",

View File

@ -2,9 +2,44 @@
<clr-datagrid [clrDgLoading]="loading" [(clrDgSingleSelected)]="selectedRow" (clrDgSingleSelectedChange)="selectRule($event)" [clrDgRowSelection]="true"> <clr-datagrid [clrDgLoading]="loading" [(clrDgSingleSelected)]="selectedRow" (clrDgSingleSelectedChange)="selectRule($event)" [clrDgRowSelection]="true">
<clr-dg-action-bar> <clr-dg-action-bar>
<button type="button" id="new_replication_rule_id" class="btn btn-secondary" *ngIf="hasCreateReplicationPermission" (click)="openModal()"><clr-icon shape="plus" size="16"></clr-icon>&nbsp;{{'REPLICATION.NEW_REPLICATION_RULE' | translate}}</button> <button type="button" id="new_replication_rule_id" class="btn btn-secondary" *ngIf="hasCreateReplicationPermission" (click)="openModal()"><clr-icon shape="plus" size="16"></clr-icon>&nbsp;{{'REPLICATION.NEW_REPLICATION_RULE' | translate}}</button>
<button type="button" id="edit_replication_rule_id" class="btn btn-secondary" *ngIf="hasUpdateReplicationPermission" [disabled]="!selectedRow" (click)="editRule(selectedRow)"><clr-icon shape="pencil" size="16"></clr-icon>&nbsp;{{'REPLICATION.EDIT_POLICY' | translate}}</button>
<button type="button" id="delete_replication_rule_id" class="btn btn-secondary" *ngIf="hasDeleteReplicationPermission" [disabled]="!selectedRow" (click)="deleteRule(selectedRow)"><clr-icon shape="times" size="16"></clr-icon>&nbsp;{{'REPLICATION.DELETE_POLICY' | translate}}</button>
<button type="button" id="replication_exe_id" class="btn btn-secondary" *ngIf="hasExecuteReplicationPermission" [disabled]="!selectedRow" (click)="replicateRule(selectedRow)"><clr-icon shape="export" size="16"></clr-icon>&nbsp;{{'REPLICATION.REPLICATE' | translate}}</button> <button type="button" id="replication_exe_id" class="btn btn-secondary" *ngIf="hasExecuteReplicationPermission" [disabled]="!selectedRow" (click)="replicateRule(selectedRow)"><clr-icon shape="export" size="16"></clr-icon>&nbsp;{{'REPLICATION.REPLICATE' | translate}}</button>
<clr-dropdown
[clrCloseMenuOnItemClick]="false"
class="btn btn-link"
clrDropdownTrigger>
<span id="rule-action">{{ 'BUTTON.ACTIONS' | translate}}<clr-icon shape="caret down"></clr-icon></span>
<clr-dropdown-menu *clrIfOpen>
<clr-dropdown>
<button type="button" class="btn btn-secondary" (click)="editRule(selectedRow)"
[disabled]="!selectedRow">
<clr-icon shape="edit" size="16"></clr-icon>&nbsp;
<span id="edit_replication_rule_id">{{'REPLICATION.EDIT_POLICY' | translate}}</span>
</button>
<button type="button" class="btn btn-secondary" (click)="operateRule('enable', selectedRow)"
[disabled]="!(selectedRow && !selectedRow.enabled)">
<clr-icon shape="connect" size="16"></clr-icon>&nbsp;
<span id="rule-enable">{{'DISTRIBUTION.ENABLE_ACTION' | translate}}</span>
</button>
<button
type="button"
class="btn btn-secondary"
(click)="operateRule('disable', selectedRow)"
[disabled]="!(selectedRow && selectedRow.enabled)">
<clr-icon shape="disconnect" size="16"></clr-icon>&nbsp;
<span id="rule-disable">{{'DISTRIBUTION.DISABLE_ACTION' | translate}}</span>
</button>
<div class="dropdown-divider"></div>
<button
type="button"
class="btn btn-secondary"
(click)="deleteRule(selectedRow)"
[disabled]="!selectedRow">
<clr-icon shape="window-close" size="16"></clr-icon>&nbsp;
<span id="delete_replication_rule_id">{{'REPLICATION.DELETE_POLICY' | translate}}</span>
</button>
</clr-dropdown>
</clr-dropdown-menu>
</clr-dropdown>
</clr-dg-action-bar> </clr-dg-action-bar>
<clr-dg-column>{{'REPLICATION.NAME' | translate}}</clr-dg-column> <clr-dg-column>{{'REPLICATION.NAME' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'status'" class="status-width">{{'REPLICATION.STATUS' | translate}}</clr-dg-column> <clr-dg-column [clrDgField]="'status'" class="status-width">{{'REPLICATION.STATUS' | translate}}</clr-dg-column>

View File

@ -12,10 +12,11 @@ import { ReplicationRule } from '../../services/interface';
import { ErrorHandler } from '../../utils/error-handler/error-handler'; import { ErrorHandler } from '../../utils/error-handler/error-handler';
import { SERVICE_CONFIG, IServiceConfig } from '../../entities/service.config'; import { SERVICE_CONFIG, IServiceConfig } from '../../entities/service.config';
import { ReplicationService, ReplicationDefaultService } from '../../services/replication.service'; import { ReplicationService } from '../../services/replication.service';
import { OperationService } from "../operation/operation.service"; import { OperationService } from "../operation/operation.service";
import { of } from 'rxjs'; import { of } from 'rxjs';
import { CURRENT_BASE_HREF } from "../../utils/utils"; import { CURRENT_BASE_HREF } from "../../utils/utils";
import { delay } from "rxjs/operators";
describe('ListReplicationRuleComponent (inline template)', () => { describe('ListReplicationRuleComponent (inline template)', () => {
@ -59,6 +60,24 @@ describe('ListReplicationRuleComponent (inline template)', () => {
let config: IServiceConfig = { let config: IServiceConfig = {
replicationRuleEndpoint: CURRENT_BASE_HREF + '/policies/replication/testing' replicationRuleEndpoint: CURRENT_BASE_HREF + '/policies/replication/testing'
}; };
const fakedReplicationService = {
getReplicationRules() {
return of(mockRules).pipe(delay(0));
},
updateReplicationRule() {
return of(true).pipe(delay(0));
}
};
const fakedOperationService = {
publishInfo() {
return undefined;
}
};
const fakedErrorHandler = {
info() {
return undefined;
}
};
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
@ -71,10 +90,10 @@ describe('ListReplicationRuleComponent (inline template)', () => {
ConfirmationDialogComponent ConfirmationDialogComponent
], ],
providers: [ providers: [
ErrorHandler, {provide: ErrorHandler, useValue: fakedErrorHandler},
{ provide: SERVICE_CONFIG, useValue: config }, {provide: SERVICE_CONFIG, useValue: config},
{ provide: ReplicationService, useClass: ReplicationDefaultService }, {provide: ReplicationService, useValue: fakedReplicationService},
{ provide: OperationService } {provide: OperationService, useValue: fakedOperationService}
] ]
}); });
})); }));
@ -99,5 +118,38 @@ describe('ListReplicationRuleComponent (inline template)', () => {
expect(el.textContent.trim()).toEqual('sync_01'); expect(el.textContent.trim()).toEqual('sync_01');
}); });
})); }));
it('should disable rule', () => {
fixture.detectChanges();
comp.selectedRow = comp.rules[0];
comp.selectedRow.enabled = true;
fixture.detectChanges();
const action: HTMLElement = fixture.nativeElement.querySelector("#rule-action");
action.click();
fixture.detectChanges();
const disable: HTMLElement = fixture.nativeElement.querySelector("#rule-disable");
disable.click();
fixture.detectChanges();
const button: HTMLElement = fixture.nativeElement.querySelector("#dialog-action-disable");
button.click();
fixture.detectChanges();
const body: HTMLElement = fixture.nativeElement.querySelector(".modal-body");
expect(body).toBeFalsy();
});
it('should enable rule', () => {
fixture.detectChanges();
comp.selectedRow = comp.rules[0];
comp.selectedRow.enabled = false;
fixture.detectChanges();
const action: HTMLElement = fixture.nativeElement.querySelector("#rule-action");
action.click();
fixture.detectChanges();
const enable: HTMLElement = fixture.nativeElement.querySelector("#rule-enable");
enable.click();
fixture.detectChanges();
const button: HTMLElement = fixture.nativeElement.querySelector("#dialog-action-enable");
button.click();
fixture.detectChanges();
const body: HTMLElement = fixture.nativeElement.querySelector(".modal-body");
expect(body).toBeFalsy();
});
}); });

View File

@ -24,29 +24,28 @@ import {
SimpleChange, SimpleChange,
SimpleChanges SimpleChanges
} from "@angular/core"; } from "@angular/core";
import { Comparator } from "../../services/interface"; import { Comparator } from "../../services";
import { TranslateService } from "@ngx-translate/core"; import { TranslateService } from "@ngx-translate/core";
import { map, catchError } from "rxjs/operators"; import { map, catchError } from "rxjs/operators";
import { Observable, forkJoin, of, throwError as observableThrowError } from "rxjs"; import { Observable, forkJoin, throwError as observableThrowError } from "rxjs";
import { ReplicationService } from "../../services/replication.service"; import { ReplicationService } from "../../services";
import { import {
ReplicationJob,
ReplicationJobItem,
ReplicationRule ReplicationRule
} from "../../services/interface"; } from "../../services";
import { ConfirmationDialogComponent } from "../confirmation-dialog/confirmation-dialog.component"; import { ConfirmationDialogComponent } from "../confirmation-dialog";
import { ConfirmationMessage } from "../confirmation-dialog/confirmation-message"; import { ConfirmationMessage } from "../confirmation-dialog";
import { ConfirmationAcknowledgement } from "../confirmation-dialog/confirmation-state-message"; import { ConfirmationAcknowledgement } from "../confirmation-dialog";
import { import {
ConfirmationState, ConfirmationState,
ConfirmationTargets, ConfirmationTargets,
ConfirmationButtons ConfirmationButtons
} from "../../entities/shared.const"; } from "../../entities/shared.const";
import { ErrorHandler } from "../../utils/error-handler/error-handler"; import { ErrorHandler } from "../../utils/error-handler";
import { CustomComparator } from "../../utils/utils"; import { clone, CustomComparator } from "../../utils/utils";
import { operateChanges, OperateInfo, OperationState } from "../operation/operate"; import { operateChanges, OperateInfo, OperationState } from "../operation/operate";
import { OperationService } from "../operation/operation.service"; import { OperationService } from "../operation/operation.service";
import { errorHandler as errorHandFn } from "../../utils/shared/shared.utils"; import { errorHandler as errorHandFn} from "../../utils/shared/shared.utils";
const jobstatus = "InProgress"; const jobstatus = "InProgress";
@ -158,6 +157,35 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
) { ) {
this.deleteOpe(message.data); this.deleteOpe(message.data);
} }
if ( message &&
message.source === ConfirmationTargets.REPLICATION &&
message.state === ConfirmationState.CONFIRMED) {
const rule: ReplicationRule = clone(message.data);
rule.enabled = !message.data.enabled;
const opeMessage = new OperateInfo();
opeMessage.name = rule.enabled ? 'REPLICATION.ENABLE_TITLE' : 'REPLICATION.DISABLE_TITLE';
opeMessage.data.id = rule.id;
opeMessage.state = OperationState.progressing;
opeMessage.data.name = rule.name;
this.operationService.publishInfo(opeMessage);
this.replicationService.updateReplicationRule(rule.id, rule).subscribe(
res => {
this.translateService.get(rule.enabled ? 'REPLICATION.ENABLE_SUCCESS' : 'REPLICATION.DISABLE_SUCCESS')
.subscribe(msg => {
operateChanges(opeMessage, OperationState.success);
this.errorHandler.info(msg);
this.retrieveRules('');
});
}, error => {
const errMessage = errorHandFn(error);
this.translateService.get(rule.enabled ? 'REPLICATION.ENABLE_FAILED' : 'REPLICATION.DISABLE_FAILED')
.subscribe(msg => {
operateChanges(opeMessage, OperationState.failure, msg);
this.errorHandler.error(errMessage);
});
}
);
}
} }
selectRule(rule: ReplicationRule): void { selectRule(rule: ReplicationRule): void {
@ -232,4 +260,34 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
return observableThrowError(error); return observableThrowError(error);
})); }));
} }
operateRule(operation: string, rule: ReplicationRule): void {
let title: string;
let summary: string;
let buttons: ConfirmationButtons;
switch (operation) {
case 'enable':
title = 'REPLICATION.ENABLE_TITLE';
summary = 'REPLICATION.ENABLE_SUMMARY';
buttons = ConfirmationButtons.ENABLE_CANCEL;
break;
case 'disable':
title = 'REPLICATION.DISABLE_TITLE';
summary = 'REPLICATION.DISABLE_SUMMARY';
buttons = ConfirmationButtons.DISABLE_CANCEL;
break;
default:
return;
}
// Confirm
const msg: ConfirmationMessage = new ConfirmationMessage(
title,
summary,
rule.name,
rule,
ConfirmationTargets.REPLICATION,
buttons
);
this.deletionConfirmDialog.open(msg);
}
} }

View File

@ -37,7 +37,8 @@ export const enum ConfirmationTargets {
CONFIG_TAB, CONFIG_TAB,
HELM_CHART, HELM_CHART,
HELM_CHART_VERSION, HELM_CHART_VERSION,
STOP_EXECUTIONS STOP_EXECUTIONS,
REPLICATION
} }
export const enum ActionType { export const enum ActionType {

View File

@ -237,6 +237,7 @@ Select Rule And Replicate
Select Rule And Click Edit Button Select Rule And Click Edit Button
[Arguments] ${rule_name} [Arguments] ${rule_name}
Retry Element Click //clr-dg-row[contains(.,'${rule_name}')]//clr-radio-wrapper/label Retry Element Click //clr-dg-row[contains(.,'${rule_name}')]//clr-radio-wrapper/label
Retry Element Click ${replication_action}
Retry Element Click ${edit_replication_rule_id} Retry Element Click ${edit_replication_rule_id}
Delete Replication Rule Delete Replication Rule

View File

@ -69,6 +69,7 @@ ${trigger_mode_selector} //*[@id='ruleTrigger']
${dest_namespace_xpath} //*[@id='dest_namespace'] ${dest_namespace_xpath} //*[@id='dest_namespace']
${new_replication_rule_id} //*[@id='new_replication_rule_id'] ${new_replication_rule_id} //*[@id='new_replication_rule_id']
${edit_replication_rule_id} //*[@id='edit_replication_rule_id'] ${edit_replication_rule_id} //*[@id='edit_replication_rule_id']
${replication_action} //*[@id='rule-action']
${delete_replication_rule_id} //*[@id='delete_replication_rule_id'] ${delete_replication_rule_id} //*[@id='delete_replication_rule_id']
${replication_exec_id} //*[@id='replication_exe_id'] ${replication_exec_id} //*[@id='replication_exe_id']
${replication_task_line_1} //clr-datagrid//clr-dg-row/div/div[2]//clr-checkbox-wrapper/label[1] ${replication_task_line_1} //clr-datagrid//clr-dg-row/div/div[2]//clr-checkbox-wrapper/label[1]