From 584245ec4dbe98660ea4567a4969b3fdd61dcb74 Mon Sep 17 00:00:00 2001 From: Shijun Sun <30999793+AllForNothing@users.noreply.github.com> Date: Thu, 5 May 2022 17:36:11 +0800 Subject: [PATCH] Improve replication policy datagrid (#16806) Signed-off-by: AllForNothing --- .../left-side-nav/replication/replication.ts | 4 + .../create-edit-rule.component.spec.ts | 10 +- .../create-edit-rule.component.ts | 15 +- .../list-replication-rule.component.html | 4 +- .../list-replication-rule.component.spec.ts | 18 +- .../list-replication-rule.component.ts | 165 ++-- .../replication/replication.component.html | 2 +- .../replication/replication.component.spec.ts | 15 +- .../replication/replication.component.ts | 927 +++++++++--------- .../total-replication-page.component.html | 3 +- .../total-replication-page.component.ts | 6 - .../app/shared/services/endpoint.service.ts | 5 +- .../src/app/shared/services/interface.ts | 26 - .../shared/services/replication.service.ts | 32 +- 14 files changed, 614 insertions(+), 618 deletions(-) diff --git a/src/portal/src/app/base/left-side-nav/replication/replication.ts b/src/portal/src/app/base/left-side-nav/replication/replication.ts index a46d6b38f..1128352cb 100644 --- a/src/portal/src/app/base/left-side-nav/replication/replication.ts +++ b/src/portal/src/app/base/left-side-nav/replication/replication.ts @@ -22,3 +22,7 @@ export enum BandwidthUnit { MB = 'Mbps', KB = 'Kbps' } +export enum ReplicationExecutionFilter { + TRIGGER = 'trigger', + STATUS = 'status' +} diff --git a/src/portal/src/app/base/left-side-nav/replication/replication/create-edit-rule/create-edit-rule.component.spec.ts b/src/portal/src/app/base/left-side-nav/replication/replication/create-edit-rule/create-edit-rule.component.spec.ts index 3b59b9ab9..e73aed485 100644 --- a/src/portal/src/app/base/left-side-nav/replication/replication/create-edit-rule/create-edit-rule.component.spec.ts +++ b/src/portal/src/app/base/left-side-nav/replication/replication/create-edit-rule/create-edit-rule.component.spec.ts @@ -6,7 +6,6 @@ import { DatePickerComponent } from "../../../../../shared/components/datetime-p import { FilterComponent } from "../../../../../shared/components/filter/filter.component"; import { InlineAlertComponent } from "../../../../../shared/components/inline-alert/inline-alert.component"; import { - ReplicationRule, ReplicationJob, ReplicationJobItem } from "../../../../../shared/services"; @@ -21,15 +20,16 @@ import {delay} from "rxjs/operators"; import { SharedTestingModule } from "../../../../../shared/shared.module"; import { RegistryService } from "../../../../../../../ng-swagger-gen/services/registry.service"; import { Registry } from "../../../../../../../ng-swagger-gen/models/registry"; +import { ReplicationPolicy } from '../../../../../../../ng-swagger-gen/models/replication-policy'; describe("CreateEditRuleComponent (inline template)", () => { - let mockRules: ReplicationRule[] = [ + let mockRules: ReplicationPolicy[] = [ { id: 1, name: "sync_01", description: "", src_registry: {id: 2}, - src_namespaces: ["name1", "name2"], + dest_namespace: "", trigger: { type: "Manual", trigger_settings: {} @@ -137,11 +137,11 @@ describe("CreateEditRuleComponent (inline template)", () => { } ]; - let mockRule: ReplicationRule = { + let mockRule: ReplicationPolicy = { id: 1, name: "sync_01", description: "", - src_namespaces: ["namespace1", "namespace2"], + dest_namespace: "", src_registry: {id: 10 }, dest_registry: {id: 0 }, trigger: { diff --git a/src/portal/src/app/base/left-side-nav/replication/replication/create-edit-rule/create-edit-rule.component.ts b/src/portal/src/app/base/left-side-nav/replication/replication/create-edit-rule/create-edit-rule.component.ts index d4e986c9e..8b7d1af15 100644 --- a/src/portal/src/app/base/left-side-nav/replication/replication/create-edit-rule/create-edit-rule.component.ts +++ b/src/portal/src/app/base/left-side-nav/replication/replication/create-edit-rule/create-edit-rule.component.ts @@ -20,7 +20,7 @@ import { EventEmitter, Output } from "@angular/core"; -import { Filter, ReplicationRule } from "../../../../../shared/services"; +import { Filter } from "../../../../../shared/services"; import { forkJoin, Observable, Subject, Subscription } from "rxjs"; import { debounceTime, distinctUntilChanged, finalize } from "rxjs/operators"; import { FormArray, FormBuilder, FormGroup, Validators, FormControl } from "@angular/forms"; @@ -37,6 +37,7 @@ import { Label } from "../../../../../../../ng-swagger-gen/models/label"; import { LabelService } from "../../../../../../../ng-swagger-gen/services/label.service"; import { BandwidthUnit, Decoration, Flatten_I18n_MAP, Flatten_Level } from "../../replication"; import { errorHandler as errorHandlerFn} from '../../../../../shared/units/shared.utils'; +import { ReplicationPolicy } from '../../../../../../../ng-swagger-gen/models/replication-policy'; const PREFIX: string = '0 '; const PAGE_SIZE: number = 100; @@ -71,7 +72,7 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy { policyId: number; confirmSub: Subscription; ruleForm: FormGroup; - copyUpdateForm: ReplicationRule; + copyUpdateForm: ReplicationPolicy; cronString: string; supportedTriggers: string[]; supportedFilters: Filter[]; @@ -292,7 +293,7 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy { } - updateRuleFormAndCopyUpdateForm(rule: ReplicationRule): void { + updateRuleFormAndCopyUpdateForm(rule: ReplicationPolicy): void { this.isPushMode = rule.dest_registry.id !== 0; setTimeout(() => { // convert speed unit to KB or MB @@ -407,7 +408,7 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy { } // add new Replication rule this.inProgress = true; - let copyRuleForm: ReplicationRule = this.ruleForm.value; + let copyRuleForm: ReplicationPolicy = this.ruleForm.value; // need to convert unit to KB for speed property copyRuleForm.speed = this.convertToKB(copyRuleForm.speed); copyRuleForm.dest_namespace_replace_count = copyRuleForm.dest_namespace_replace_count @@ -457,7 +458,7 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy { }); } } - openCreateEditRule(rule?: ReplicationRule): void { + openCreateEditRule(rule?: ReplicationPolicy): void { this.formReset(); this.copyUpdateForm = clone(this.ruleForm.value); this.inlineAlert.close(); @@ -474,8 +475,8 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy { this.supportedFilterLabels.forEach((label, index) => { if (rule.filters && rule.filters.length) { rule.filters.forEach(f => { - if (f.type === FilterType.LABEL && f.value && f.value.length) { - f.value.forEach(name => { + if (f.type === FilterType.LABEL && f.value && (f.value as any).length) { + (f.value as any).forEach(name => { if (label.name === name) { this.supportedFilterLabels[index].select = true; } diff --git a/src/portal/src/app/base/left-side-nav/replication/replication/list-replication-rule/list-replication-rule.component.html b/src/portal/src/app/base/left-side-nav/replication/replication/list-replication-rule/list-replication-rule.component.html index 1c20803af..5581afda2 100644 --- a/src/portal/src/app/base/left-side-nav/replication/replication/list-replication-rule/list-replication-rule.component.html +++ b/src/portal/src/app/base/left-side-nav/replication/replication/list-replication-rule/list-replication-rule.component.html @@ -2,7 +2,7 @@ - + {{'REPLICATION.REPLICATION_MODE' | translate}} {{'REPLICATION.DESTINATION_NAMESPACE' | translate}} {{'REPLICATION.DES_REPO_FLATTENING' | translate}} - {{'REPLICATION.REPLICATION_TRIGGER' | translate}} + {{'REPLICATION.REPLICATION_TRIGGER' | translate}} {{'REPLICATION.BANDWIDTH' | translate}} {{'REPLICATION.DESCRIPTION' | translate}} {{'REPLICATION.PLACEHOLDER' | translate }} diff --git a/src/portal/src/app/base/left-side-nav/replication/replication/list-replication-rule/list-replication-rule.component.spec.ts b/src/portal/src/app/base/left-side-nav/replication/replication/list-replication-rule/list-replication-rule.component.spec.ts index 248988c0a..6873ae776 100644 --- a/src/portal/src/app/base/left-side-nav/replication/replication/list-replication-rule/list-replication-rule.component.spec.ts +++ b/src/portal/src/app/base/left-side-nav/replication/replication/list-replication-rule/list-replication-rule.component.spec.ts @@ -1,27 +1,26 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ConfirmationDialogComponent } from '../../../../../shared/components/confirmation-dialog'; import { ListReplicationRuleComponent } from './list-replication-rule.component'; -import { ReplicationRule } from '../../../../../shared/services'; import { ErrorHandler } from '../../../../../shared/units/error-handler'; -import { ReplicationService } from '../../../../../shared/services'; import { OperationService } from "../../../../../shared/components/operation/operation.service"; import { of } from 'rxjs'; import { delay } from "rxjs/operators"; import {HttpHeaders, HttpResponse} from "@angular/common/http"; import { SharedTestingModule } from "../../../../../shared/shared.module"; +import { ReplicationPolicy } from '../../../../../../../ng-swagger-gen/models/replication-policy'; +import { ReplicationService } from 'ng-swagger-gen/services/replication.service'; describe('ListReplicationRuleComponent (inline template)', () => { - let mockRules: ReplicationRule[] = [ + let mockRules: ReplicationPolicy[] = [ { "id": 1, "name": "sync_01", "description": "", "filters": null, "trigger": {"type": "Manual", "trigger_settings": null}, - "error_job_count": 2, "deletion": false, - "src_namespaces": ["name1", "name2"], + "dest_namespace": "", "src_registry": {id: 3}, "enabled": true, "override": true, @@ -33,9 +32,8 @@ describe('ListReplicationRuleComponent (inline template)', () => { "description": "", "filters": null, "trigger": {"type": "Manual", "trigger_settings": null}, - "error_job_count": 2, "deletion": false, - "src_namespaces": ["name1", "name2"], + "dest_namespace": "", "dest_registry": {id: 3}, "enabled": true, "override": true, @@ -47,13 +45,13 @@ describe('ListReplicationRuleComponent (inline template)', () => { let comp: ListReplicationRuleComponent; const fakedReplicationService = { - updateReplicationRule() { + updateReplicationPolicy() { return of(true).pipe(delay(0)); }, - deleteReplicationRule() { + deleteReplicationPolicy() { return of(true).pipe(delay(0)); }, - getReplicationRulesResponse() { + listReplicationPoliciesResponse() { return of(new HttpResponse({ body: mockRules, headers: new HttpHeaders({ diff --git a/src/portal/src/app/base/left-side-nav/replication/replication/list-replication-rule/list-replication-rule.component.ts b/src/portal/src/app/base/left-side-nav/replication/replication/list-replication-rule/list-replication-rule.component.ts index ae9ea1daa..97025c573 100644 --- a/src/portal/src/app/base/left-side-nav/replication/replication/list-replication-rule/list-replication-rule.component.ts +++ b/src/portal/src/app/base/left-side-nav/replication/replication/list-replication-rule/list-replication-rule.component.ts @@ -11,28 +11,20 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -import { - Component, - Input, - Output, - EventEmitter, - ViewChild, -} from "@angular/core"; +import { Component, EventEmitter, Input, Output, ViewChild, } from "@angular/core"; import { TranslateService } from "@ngx-translate/core"; -import { map, catchError, finalize } from "rxjs/operators"; -import { Observable, forkJoin, throwError as observableThrowError } from "rxjs"; -import { ReplicationService } from "../../../../../shared/services"; -import { - ReplicationRule -} from "../../../../../shared/services"; +import { catchError, finalize, map } from "rxjs/operators"; +import { forkJoin, Observable, throwError as observableThrowError } from "rxjs"; import { ConfirmationDialogComponent } from "../../../../../shared/components/confirmation-dialog"; -import { - ConfirmationState, - ConfirmationTargets, - ConfirmationButtons -} from "../../../../../shared/entities/shared.const"; +import { ConfirmationButtons, ConfirmationState, ConfirmationTargets } from "../../../../../shared/entities/shared.const"; import { ErrorHandler } from "../../../../../shared/units/error-handler"; -import { clone, getPageSizeFromLocalStorage, PageSizeMapKeys, setPageSizeToLocalStorage } from "../../../../../shared/units/utils"; +import { + clone, + getPageSizeFromLocalStorage, getQueryString, + getSortingString, + PageSizeMapKeys, + setPageSizeToLocalStorage +} from "../../../../../shared/units/utils"; import { operateChanges, OperateInfo, OperationState } from "../../../../../shared/components/operation/operate"; import { OperationService } from "../../../../../shared/components/operation/operation.service"; import { ClrDatagridStateInterface } from '@clr/angular'; @@ -42,12 +34,15 @@ import { ConfirmationMessage } from "../../../../global-confirmation-dialog/conf import { HELM_HUB } from "../../../../../shared/services/endpoint.service"; import { BandwidthUnit, Flatten_I18n_MAP } from "../../replication"; import { KB_TO_MB } from "../create-edit-rule/create-edit-rule.component"; +import { ReplicationService } from "ng-swagger-gen/services/replication.service"; +import { ReplicationPolicy } from '../../../../../../../ng-swagger-gen/models/replication-policy'; + @Component({ selector: "hbr-list-replication-rule", templateUrl: "./list-replication-rule.component.html", styleUrls: ["./list-replication-rule.component.scss"], }) -export class ListReplicationRuleComponent { +export class ListReplicationRuleComponent { @Input() selectedId: number | string; @Input() withReplicationJob: boolean; @Input() hasCreateReplicationPermission: boolean; @@ -55,15 +50,15 @@ export class ListReplicationRuleComponent { @Input() hasDeleteReplicationPermission: boolean; @Input() hasExecuteReplicationPermission: boolean; @Input() searchString: string; - @Output() selectOne = new EventEmitter(); - @Output() editOne = new EventEmitter(); - @Output() toggleOne = new EventEmitter(); + @Output() selectOne = new EventEmitter(); + @Output() editOne = new EventEmitter(); + @Output() toggleOne = new EventEmitter(); @Output() hideJobs = new EventEmitter(); - @Output() redirect = new EventEmitter(); + @Output() redirect = new EventEmitter(); @Output() openNewRule = new EventEmitter(); - @Output() replicateManual = new EventEmitter(); - rules: ReplicationRule[] = []; - selectedRow: ReplicationRule; + @Output() replicateManual = new EventEmitter(); + rules: ReplicationPolicy[] = []; + selectedRow: ReplicationPolicy; @ViewChild("toggleConfirmDialog") toggleConfirmDialog: ConfirmationDialogComponent; @ViewChild("deletionConfirmDialog") @@ -74,9 +69,9 @@ export class ListReplicationRuleComponent { loading: boolean = true; constructor(private replicationService: ReplicationService, - private translateService: TranslateService, - private errorHandlerEntity: ErrorHandler, - private operationService: OperationService) { + private translateService: TranslateService, + private errorHandlerEntity: ErrorHandler, + private operationService: OperationService) { } trancatedDescription(desc: string): string { @@ -86,9 +81,11 @@ export class ListReplicationRuleComponent { return desc; } } - replicateRule(rule: ReplicationRule): void { + + replicateRule(rule: ReplicationPolicy): void { this.replicateManual.emit(rule); } + deletionConfirm(message: ConfirmationAcknowledgement) { if ( message && @@ -97,10 +94,10 @@ export class ListReplicationRuleComponent { ) { this.deleteOpe(message.data); } - if ( message && + if (message && message.source === ConfirmationTargets.REPLICATION && message.state === ConfirmationState.CONFIRMED) { - const rule: ReplicationRule = clone(message.data); + const rule: ReplicationPolicy = clone(message.data); rule.enabled = !message.data.enabled; const opeMessage = new OperateInfo(); opeMessage.name = rule.enabled ? 'REPLICATION.ENABLE_TITLE' : 'REPLICATION.DISABLE_TITLE'; @@ -108,34 +105,38 @@ export class ListReplicationRuleComponent { opeMessage.state = OperationState.progressing; opeMessage.data.name = rule.name; this.operationService.publishInfo(opeMessage); - this.replicationService.updateReplicationRule(rule.id, rule).subscribe( - res => { + this.replicationService.updateReplicationPolicy({ + id: rule.id, + policy: rule + }).subscribe({ + next: () => { this.translateService.get(rule.enabled ? 'REPLICATION.ENABLE_SUCCESS' : 'REPLICATION.DISABLE_SUCCESS') .subscribe(msg => { - operateChanges(opeMessage, OperationState.success); - this.errorHandlerEntity.info(msg); - this.refreshRule(); - }); - }, error => { + operateChanges(opeMessage, OperationState.success); + this.errorHandlerEntity.info(msg); + this.refreshRule(); + }); + }, + error: error => { const errMessage = errorHandler(error); this.translateService.get(rule.enabled ? 'REPLICATION.ENABLE_FAILED' : 'REPLICATION.DISABLE_FAILED') .subscribe(msg => { - operateChanges(opeMessage, OperationState.failure, msg); - this.errorHandlerEntity.error(errMessage); - }); + operateChanges(opeMessage, OperationState.failure, msg); + this.errorHandlerEntity.error(errMessage); + }); } - ); + }); } } - selectRule(rule: ReplicationRule): void { + selectRule(rule: ReplicationPolicy): void { if (rule) { this.selectedId = rule.id || ""; this.selectOne.emit(rule); } } - redirectTo(rule: ReplicationRule): void { + redirectTo(rule: ReplicationPolicy): void { this.redirect.emit(rule); } @@ -143,11 +144,11 @@ export class ListReplicationRuleComponent { this.openNewRule.emit(); } - editRule(rule: ReplicationRule) { + editRule(rule: ReplicationPolicy) { this.editOne.emit(rule); } - deleteRule(rule: ReplicationRule) { + deleteRule(rule: ReplicationPolicy) { if (rule) { let deletionMessage = new ConfirmationMessage( "REPLICATION.DELETION_TITLE", @@ -161,7 +162,7 @@ export class ListReplicationRuleComponent { } } - deleteOpe(rule: ReplicationRule) { + deleteOpe(rule: ReplicationPolicy) { if (rule) { let observableLists: any[] = []; observableLists.push(this.delOperate(rule)); @@ -175,7 +176,7 @@ export class ListReplicationRuleComponent { } } - delOperate(rule: ReplicationRule): Observable { + delOperate(rule: ReplicationPolicy): Observable { // init operation info let operMessage = new OperateInfo(); operMessage.name = 'OPERATION.DELETE_REPLICATION'; @@ -185,11 +186,13 @@ export class ListReplicationRuleComponent { this.operationService.publishInfo(operMessage); return this.replicationService - .deleteReplicationRule(+rule.id) - .pipe(map(() => { - this.translateService.get('BATCH.DELETED_SUCCESS') - .subscribe(res => operateChanges(operMessage, OperationState.success)); + .deleteReplicationPolicy({ + id: rule.id }) + .pipe(map(() => { + this.translateService.get('BATCH.DELETED_SUCCESS') + .subscribe(res => operateChanges(operMessage, OperationState.success)); + }) , catchError(error => { const message = errorHandler(error); this.translateService.get(message).subscribe(res => @@ -198,7 +201,8 @@ export class ListReplicationRuleComponent { return observableThrowError(error); })); } - operateRule(operation: string, rule: ReplicationRule): void { + + operateRule(operation: string, rule: ReplicationPolicy): void { let title: string; let summary: string; let buttons: ConfirmationButtons; @@ -228,32 +232,44 @@ export class ListReplicationRuleComponent { ); this.deletionConfirmDialog.open(msg); } + clrLoad(state?: ClrDatagridStateInterface) { if (state && state.page) { this.pageSize = state.page.size; setPageSizeToLocalStorage(PageSizeMapKeys.LIST_REPLICATION_RULE_COMPONENT, this.pageSize); } this.loading = true; - this.replicationService.getReplicationRulesResponse( - this.searchString, - this.page, - this.pageSize) + const param: ReplicationService.ListReplicationPoliciesParams = { + page: this.page, + pageSize: this.pageSize, + sort: getSortingString(state), + }; + if (this.searchString) { + param.q = encodeURIComponent(`name=~${this.searchString}`); + } else { + param.q = getQueryString(state); + } + this.replicationService.listReplicationPoliciesResponse(param) .pipe(finalize(() => this.loading = false)) - .subscribe(response => { - // job list hidden - this.hideJobs.emit(); - // Get total count - if (response.headers) { - let xHeader: string = response.headers.get("x-total-count"); - if (xHeader) { - this.totalCount = parseInt(xHeader, 0); - } - } - this.rules = response.body as ReplicationRule[]; - }, error => { - this.errorHandlerEntity.error(error); + .subscribe({ + next: response => { + // job list hidden + this.hideJobs.emit(); + // Get total count + if (response.headers) { + let xHeader: string = response.headers.get("x-total-count"); + if (xHeader) { + this.totalCount = parseInt(xHeader, 0); + } + } + this.rules = response.body as ReplicationPolicy[]; + }, + error: error => { + this.errorHandlerEntity.error(error); + } }); } + refreshRule() { this.page = 1; this.totalCount = 0; @@ -261,15 +277,18 @@ export class ListReplicationRuleComponent { this.searchString = null; this.clrLoad(); } + isHelmHub(srcRegistry: any): boolean { - return srcRegistry && srcRegistry.type === HELM_HUB; + return srcRegistry && srcRegistry.type === HELM_HUB; } + getFlattenLevelString(level: number) { if (level !== null && Flatten_I18n_MAP[level]) { return Flatten_I18n_MAP[level]; } return level; } + getBandwidthStr(speed: number): string { if (speed >= KB_TO_MB) { return '' + (speed / KB_TO_MB).toFixed(2) + BandwidthUnit.MB; diff --git a/src/portal/src/app/base/left-side-nav/replication/replication/replication.component.html b/src/portal/src/app/base/left-side-nav/replication/replication/replication.component.html index 1d3c7d342..4a876c77f 100644 --- a/src/portal/src/app/base/left-side-nav/replication/replication/replication.component.html +++ b/src/portal/src/app/base/left-side-nav/replication/replication/replication.component.html @@ -12,7 +12,7 @@
{ - let mockRules: ReplicationRule[] = [ + let mockRules: ReplicationPolicy[] = [ { "id": 1, "name": "sync_01", "description": "", "filters": null, "trigger": {"type": "Manual", "trigger_settings": null}, - "error_job_count": 2, "deletion": false, "src_registry": {id: 3}, - "src_namespaces": ["name1"], + "dest_namespace": "", "enabled": true, "override": true, "speed": -1 @@ -40,10 +40,9 @@ describe('Replication Component (inline template)', () => { "description": "", "filters": null, "trigger": {"type": "Manual", "trigger_settings": null}, - "error_job_count": 2, "deletion": false, "dest_registry": {id: 5}, - "src_namespaces": ["name1"], + "dest_namespace": "", "enabled": true, "override": true, "speed": -1 @@ -127,7 +126,7 @@ describe('Replication Component (inline template)', () => { } }; const fakedReplicationService = { - getReplicationRulesResponse() { + listReplicationPoliciesResponse() { return of(new HttpResponse({ body: mockRules, headers: new HttpHeaders({ diff --git a/src/portal/src/app/base/left-side-nav/replication/replication/replication.component.ts b/src/portal/src/app/base/left-side-nav/replication/replication/replication.component.ts index d06802e82..807d8d383 100644 --- a/src/portal/src/app/base/left-side-nav/replication/replication/replication.component.ts +++ b/src/portal/src/app/base/left-side-nav/replication/replication/replication.component.ts @@ -11,48 +11,33 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -import { - Component, - OnInit, - ViewChild, - Input, - Output, - OnDestroy, - EventEmitter -} from "@angular/core"; -import { finalize, catchError, map, debounceTime, distinctUntilChanged, switchMap } from "rxjs/operators"; -import { Subscription, forkJoin, timer, Observable, throwError as observableThrowError } from "rxjs"; +import { Component, EventEmitter, Input, OnDestroy, OnInit, Output, ViewChild } from "@angular/core"; +import { catchError, debounceTime, distinctUntilChanged, finalize, map, switchMap } from "rxjs/operators"; +import { forkJoin, Observable, Subscription, throwError as observableThrowError, timer } from "rxjs"; import { TranslateService } from "@ngx-translate/core"; import { ListReplicationRuleComponent } from "./list-replication-rule/list-replication-rule.component"; import { CreateEditRuleComponent } from "./create-edit-rule/create-edit-rule.component"; import { ErrorHandler } from "../../../../shared/units/error-handler"; -import { Comparator, ReplicationService } from "../../../../shared/services"; -import { RequestQueryParams } from "../../../../shared/services"; -import { - ReplicationRule, - ReplicationJob, - ReplicationJobItem -} from "../../../../shared/services"; +import { Comparator, ReplicationJob, ReplicationJobItem } from "../../../../shared/services"; import { - CustomComparator, - doFiltering, - doSorting, - calculatePage, getPageSizeFromLocalStorage, PageSizeMapKeys, setPageSizeToLocalStorage + calculatePage, + CustomComparator, + doFiltering, + doSorting, + getPageSizeFromLocalStorage, getSortingString, + PageSizeMapKeys, + setPageSizeToLocalStorage } from "../../../../shared/units/utils"; import { - ConfirmationTargets, - ConfirmationButtons, - ConfirmationState, - REFRESH_TIME_DIFFERENCE + ConfirmationButtons, + ConfirmationState, + ConfirmationTargets, + REFRESH_TIME_DIFFERENCE } from "../../../../shared/entities/shared.const"; import { ConfirmationDialogComponent } from "../../../../shared/components/confirmation-dialog"; -import { - operateChanges, - OperationState, - OperateInfo -} from "../../../../shared/components/operation/operate"; +import { operateChanges, OperateInfo, OperationState } from "../../../../shared/components/operation/operate"; import { OperationService } from "../../../../shared/components/operation/operation.service"; import { Router } from "@angular/router"; import { FilterComponent } from "../../../../shared/components/filter/filter.component"; @@ -60,6 +45,9 @@ import { ClrDatagridStateInterface } from '@clr/angular'; import { errorHandler } from "../../../../shared/units/shared.utils"; import { ConfirmationMessage } from "../../../global-confirmation-dialog/confirmation-message"; import { ConfirmationAcknowledgement } from "../../../global-confirmation-dialog/confirmation-state-message"; +import { ReplicationService } from "ng-swagger-gen/services/replication.service"; +import { ReplicationPolicy } from '../../../../../../ng-swagger-gen/models/replication-policy'; +import { ReplicationExecutionFilter } from '../replication'; const ONE_HOUR_SECONDS: number = 3600; const ONE_MINUTE_SECONDS: number = 60; @@ -67,481 +55,500 @@ const ONE_DAY_SECONDS: number = 24 * ONE_HOUR_SECONDS; const IN_PROCESS: string = 'InProgress'; const ruleStatus: { [key: string]: any } = [ - { key: "all", description: "REPLICATION.ALL_STATUS" }, - { key: "1", description: "REPLICATION.ENABLED" }, - { key: "0", description: "REPLICATION.DISABLED" } + {key: "all", description: "REPLICATION.ALL_STATUS"}, + {key: "1", description: "REPLICATION.ENABLED"}, + {key: "0", description: "REPLICATION.DISABLED"} ]; export class SearchOption { - ruleId: number | string; - ruleName: string = ""; - trigger: string = ""; - status: string = ""; - page: number = 1; + ruleId: number | string; + ruleName: string = ""; + trigger: string = ""; + status: string = ""; + page: number = 1; } const STATUS_MAP = { - "Succeed": "Succeeded" + "Succeed": "Succeeded" }; @Component({ - selector: "hbr-replication", - templateUrl: "./replication.component.html", - styleUrls: ["./replication.component.scss"] + selector: "hbr-replication", + templateUrl: "./replication.component.html", + styleUrls: ["./replication.component.scss"] }) export class ReplicationComponent implements OnInit, OnDestroy { - @Input() projectId: number | string; - @Input() projectName: string; - @Input() isSystemAdmin: boolean; - @Input() withAdmiral: boolean; - @Input() withReplicationJob: boolean; - @Input() hasCreateReplicationPermission: boolean; - @Input() hasUpdateReplicationPermission: boolean; - @Input() hasDeleteReplicationPermission: boolean; - @Input() hasExecuteReplicationPermission: boolean; + @Input() projectId: number | string; + @Input() projectName: string; + @Input() isSystemAdmin: boolean; + @Input() withAdmiral: boolean; + @Input() withReplicationJob: boolean; + @Input() hasCreateReplicationPermission: boolean; + @Input() hasUpdateReplicationPermission: boolean; + @Input() hasDeleteReplicationPermission: boolean; + @Input() hasExecuteReplicationPermission: boolean; + @Output() openCreateRule = new EventEmitter(); + @Output() openEdit = new EventEmitter(); + @Output() goToRegistry = new EventEmitter(); - @Output() redirect = new EventEmitter(); - @Output() openCreateRule = new EventEmitter(); - @Output() openEdit = new EventEmitter(); - @Output() goToRegistry = new EventEmitter(); + search: SearchOption = new SearchOption(); + isOpenFilterTag: boolean; + ruleStatus = ruleStatus; + currentRuleStatus: { key: string; description: string }; + currentTerm: string; + defaultFilter = "trigger"; + selectedRow: ReplicationJobItem[] = []; + isStopOnGoing: boolean; + hiddenJobList = true; - search: SearchOption = new SearchOption(); - isOpenFilterTag: boolean; - ruleStatus = ruleStatus; - currentRuleStatus: { key: string; description: string }; - currentTerm: string; - defaultFilter = "trigger"; - selectedRow: ReplicationJobItem[] = []; - isStopOnGoing: boolean; - hiddenJobList = true; + jobs: ReplicationJobItem[]; - jobs: ReplicationJobItem[]; + @ViewChild(ListReplicationRuleComponent) + listReplicationRule: ListReplicationRuleComponent; - @ViewChild(ListReplicationRuleComponent) - listReplicationRule: ListReplicationRuleComponent; + @ViewChild(CreateEditRuleComponent) + createEditPolicyComponent: CreateEditRuleComponent; - @ViewChild(CreateEditRuleComponent) - createEditPolicyComponent: CreateEditRuleComponent; + @ViewChild("replicationConfirmDialog") + replicationConfirmDialog: ConfirmationDialogComponent; - @ViewChild("replicationConfirmDialog") - replicationConfirmDialog: ConfirmationDialogComponent; + @ViewChild("StopConfirmDialog") + StopConfirmDialog: ConfirmationDialogComponent; - @ViewChild("StopConfirmDialog") - StopConfirmDialog: ConfirmationDialogComponent; + creationTimeComparator: Comparator = new CustomComparator("start_time", "date"); + updateTimeComparator: Comparator = new CustomComparator("end_time", "date"); - creationTimeComparator: Comparator = new CustomComparator< - ReplicationJob - >("start_time", "date"); - updateTimeComparator: Comparator = new CustomComparator< - ReplicationJob - >("end_time", "date"); + // Server driven pagination + currentPage: number = 1; + totalCount: number = 0; + pageSize: number = getPageSizeFromLocalStorage(PageSizeMapKeys.LIST_REPLICATION_RULE_COMPONENT_EXECUTIONS); + currentState: ClrDatagridStateInterface; + jobsLoading: boolean = false; + timerDelay: Subscription; + @ViewChild(FilterComponent, {static: true}) + filterComponent: FilterComponent; + searchSub: Subscription; - // Server driven pagination - currentPage: number = 1; - totalCount: number = 0; - pageSize: number = getPageSizeFromLocalStorage(PageSizeMapKeys.LIST_REPLICATION_RULE_COMPONENT_EXECUTIONS); - currentState: ClrDatagridStateInterface; - jobsLoading: boolean = false; - timerDelay: Subscription; - @ViewChild(FilterComponent, {static: true}) - filterComponent: FilterComponent; - searchSub: Subscription; + constructor( + private router: Router, + private errorHandlerEntity: ErrorHandler, + private replicationService: ReplicationService, + private operationService: OperationService, + private translateService: TranslateService + ) { + } - constructor( - private router: Router, - private errorHandlerEntity: ErrorHandler, - private replicationService: ReplicationService, - private operationService: OperationService, - private translateService: TranslateService - ) { } + public get showPaginationIndex(): boolean { + return this.totalCount > 0; + } - public get showPaginationIndex(): boolean { - return this.totalCount > 0; - } - - ngOnInit() { - if (!this.searchSub) { - this.searchSub = this.filterComponent.filterTerms.pipe( - debounceTime(500), - distinctUntilChanged(), - switchMap( ruleName => { - this.listReplicationRule.loading = true; - this.listReplicationRule.page = 1; - return this.replicationService - .getReplicationRulesResponse(ruleName, this.listReplicationRule.page, this.listReplicationRule.pageSize); - }) - ).subscribe(response => { - this.hideJobs(); - // Get total count - if (response.headers) { - let xHeader: string = response.headers.get("x-total-count"); - if (xHeader) { - this.listReplicationRule.totalCount = parseInt(xHeader, 0); - } - } - this.listReplicationRule.selectedRow = null; // Clear selection - this.listReplicationRule.rules = response.body as ReplicationRule[]; - this.listReplicationRule.loading = false; - }, error => { - this.errorHandlerEntity.error(error); - this.listReplicationRule.loading = false; + ngOnInit() { + if (!this.searchSub) { + this.searchSub = this.filterComponent.filterTerms.pipe( + debounceTime(500), + distinctUntilChanged(), + switchMap(ruleName => { + this.listReplicationRule.loading = true; + this.listReplicationRule.page = 1; + return this.replicationService + .listReplicationPoliciesResponse({ + page: this.listReplicationRule.page, + pageSize: this.listReplicationRule.pageSize, + q: ruleName ? encodeURIComponent(`name=~${ruleName}`) : null + }); + }) + ).subscribe({ + next: response => { + this.hideJobs(); + // Get total count + if (response.headers) { + let xHeader: string = response.headers.get("x-total-count"); + if (xHeader) { + this.listReplicationRule.totalCount = parseInt(xHeader, 0); + } + } + this.listReplicationRule.selectedRow = null; // Clear selection + this.listReplicationRule.rules = response.body as ReplicationPolicy[]; + this.listReplicationRule.loading = false; + }, + error: error => { + this.errorHandlerEntity.error(error); + this.listReplicationRule.loading = false; + } }); - } - this.currentRuleStatus = this.ruleStatus[0]; - } - - ngOnDestroy() { - if (this.timerDelay) { - this.timerDelay.unsubscribe(); - } - if (this.searchSub) { - this.searchSub.unsubscribe(); - this.searchSub = null; - } - } - - // open replication rule - openModal(): void { - this.createEditPolicyComponent.openCreateEditRule(); - } - - // edit replication rule - openEditRule(rule: ReplicationRule) { - if (rule) { - this.createEditPolicyComponent.openCreateEditRule(rule); - } - } - - goRegistry(): void { - this.goToRegistry.emit(); - } - - goToLink(exeId: number): void { - let linkUrl = ["harbor", "replications", exeId, "tasks"]; - this.router.navigate(linkUrl); - } - - // Server driven data loading - clrLoadJobs(withLoading: boolean, state: ClrDatagridStateInterface): void { - if (!state || !state.page || !this.search.ruleId) { - return; - } - this.pageSize = state.page.size; - setPageSizeToLocalStorage(PageSizeMapKeys.LIST_REPLICATION_RULE_COMPONENT_EXECUTIONS, this.pageSize); - this.currentState = state; - - let pageNumber: number = calculatePage(state); - if (pageNumber <= 0) { - pageNumber = 1; + } + this.currentRuleStatus = this.ruleStatus[0]; } - let params: RequestQueryParams = new RequestQueryParams().set("page", "" + pageNumber).set("page_size", "" + this.pageSize); + ngOnDestroy() { + if (this.timerDelay) { + this.timerDelay.unsubscribe(); + } + if (this.searchSub) { + this.searchSub.unsubscribe(); + this.searchSub = null; + } + } - if (this.currentTerm && this.currentTerm !== "") { - params = params.set(this.defaultFilter, this.currentTerm); + // open replication rule + openModal(): void { + this.createEditPolicyComponent.openCreateEditRule(); } - if (withLoading) { - this.jobsLoading = true; + + // edit replication rule + openEditRule(rule: ReplicationPolicy) { + if (rule) { + this.createEditPolicyComponent.openCreateEditRule(rule); + } } - this.selectedRow = []; - this.replicationService.getExecutions(this.search.ruleId, params).subscribe( - response => { - this.totalCount = response.metadata.xTotalCount; - this.jobs = response.data; - if (!this.timerDelay) { - this.timerDelay = timer(REFRESH_TIME_DIFFERENCE, REFRESH_TIME_DIFFERENCE).subscribe(() => { - let count: number = 0; - this.jobs.forEach(job => { - if ( - job.status === IN_PROCESS - ) { - count++; - } - }); - if (count > 0) { - this.clrLoadJobs(false, this.currentState); - } else { - this.timerDelay.unsubscribe(); - this.timerDelay = null; + + goRegistry(): void { + this.goToRegistry.emit(); + } + + goToLink(exeId: number): void { + let linkUrl = ["harbor", "replications", exeId, "tasks"]; + this.router.navigate(linkUrl); + } + + // Server driven data loading + clrLoadJobs(withLoading: boolean, state: ClrDatagridStateInterface): void { + if (!state || !state.page || !this.search.ruleId) { + return; + } + this.pageSize = state.page.size; + setPageSizeToLocalStorage(PageSizeMapKeys.LIST_REPLICATION_RULE_COMPONENT_EXECUTIONS, this.pageSize); + this.currentState = state; + + let pageNumber: number = calculatePage(state); + if (pageNumber <= 0) { + pageNumber = 1; + } + const params: ReplicationService.ListReplicationExecutionsParams = { + policyId: +this.search.ruleId, + page: pageNumber, + pageSize: this.pageSize, + sort: getSortingString(state), + }; + if (this.defaultFilter === ReplicationExecutionFilter.TRIGGER && this.currentTerm) { + params.trigger = this.currentTerm; + } + if (this.defaultFilter === ReplicationExecutionFilter.STATUS && this.currentTerm) { + params.status = this.currentTerm; + } + if (withLoading) { + this.jobsLoading = true; + } + this.selectedRow = []; + this.replicationService.listReplicationExecutionsResponse(params).subscribe( + response => { + this.totalCount = Number.parseInt( + response.headers.get('x-total-count'), 10 + ); + this.jobs = response.body as ReplicationJobItem[]; + if (!this.timerDelay) { + this.timerDelay = timer(REFRESH_TIME_DIFFERENCE, REFRESH_TIME_DIFFERENCE).subscribe(() => { + let count: number = 0; + this.jobs.forEach(job => { + if ( + job.status === IN_PROCESS + ) { + count++; + } + }); + if (count > 0) { + this.clrLoadJobs(false, this.currentState); + } else { + this.timerDelay.unsubscribe(); + this.timerDelay = null; + } + }); + } + // Do filtering and sorting + this.jobs = doFiltering(this.jobs, state); + this.jobs = doSorting(this.jobs, state); + + this.jobsLoading = false; + }, + error => { + this.jobsLoading = false; + this.errorHandlerEntity.error(error); } - }); - } - // Do filtering and sorting - this.jobs = doFiltering(this.jobs, state); - this.jobs = doSorting(this.jobs, state); - - this.jobsLoading = false; - }, - error => { - this.jobsLoading = false; - this.errorHandlerEntity.error(error); - } - ); - } - public doSearchExecutions(terms: string): void { - this.currentTerm = terms.trim(); - // Trigger data loading and start from first page - this.jobsLoading = true; - this.currentPage = 1; - this.jobsLoading = true; - // Force reloading - this.loadFirstPage(); - } - loadFirstPage(): void { - let st: ClrDatagridStateInterface = this.currentState; - if (!st) { - st = { - page: {} - }; - } - st.page.size = this.pageSize; - st.page.from = 0; - st.page.to = this.pageSize - 1; - - this.clrLoadJobs(true, st); - } - - selectOneRule(rule: ReplicationRule) { - if (rule && rule.id) { - this.hiddenJobList = false; - this.search.ruleId = rule.id || ""; - this.loadFirstPage(); - } - } - - replicateManualRule(rule: ReplicationRule) { - if (rule) { - let replicationMessage = new ConfirmationMessage( - "REPLICATION.REPLICATION_TITLE", - "REPLICATION.REPLICATION_SUMMARY", - rule.name, - rule, - ConfirmationTargets.TARGET, - ConfirmationButtons.REPLICATE_CANCEL - ); - this.replicationConfirmDialog.open(replicationMessage); - } - } - - confirmReplication(message: ConfirmationAcknowledgement) { - if ( - message && - message.source === ConfirmationTargets.TARGET && - message.state === ConfirmationState.CONFIRMED - ) { - let rule: ReplicationRule = message.data; - - if (rule) { - forkJoin(this.replicationOperate(rule)).subscribe(item => { - this.selectOneRule(rule); - }, error => { - this.errorHandlerEntity.error(error); - }); - } - } - } - - replicationOperate(rule: ReplicationRule): Observable { - // init operation info - let operMessage = new OperateInfo(); - operMessage.name = "OPERATION.REPLICATION"; - operMessage.data.id = rule.id; - operMessage.state = OperationState.progressing; - operMessage.data.name = rule.name; - this.operationService.publishInfo(operMessage); - - return this.replicationService.replicateRule(+rule.id).pipe( - map(response => { - this.translateService - .get("BATCH.REPLICATE_SUCCESS") - .subscribe(res => - operateChanges(operMessage, OperationState.success) - ); - }), - catchError(error => { - const message = errorHandler(error); - this.translateService.get(message).subscribe(res => - operateChanges(operMessage, OperationState.failure, res) ); - return observableThrowError(error); - }) - ); - } - - customRedirect(rule: ReplicationRule) { - this.redirect.emit(rule); - } - doFilterJob($event: any): void { - this.defaultFilter = $event["target"].value; - this.doSearchJobs(this.currentTerm); - } - - doSearchJobs(terms: string) { - if (!terms) { - return; } - this.currentTerm = terms.trim(); - this.currentPage = 1; - this.loadFirstPage(); - } - hideJobs() { - this.search.ruleId = 0; - this.jobs = []; - this.hiddenJobList = true; - } + public doSearchExecutions(terms: string): void { + this.currentTerm = terms.trim(); + // Trigger data loading and start from first page + this.jobsLoading = true; + this.currentPage = 1; + this.jobsLoading = true; + // Force reloading + this.loadFirstPage(); + } - openStopExecutionsDialog(targets: ReplicationJobItem[]) { - let ExecutionId = targets.map(robot => robot.id).join(","); - let StopExecutionsMessage = new ConfirmationMessage( - "REPLICATION.STOP_TITLE", - "REPLICATION.STOP_SUMMARY", - ExecutionId, - targets, - ConfirmationTargets.STOP_EXECUTIONS, - ConfirmationButtons.STOP_CANCEL - ); - this.StopConfirmDialog.open(StopExecutionsMessage); - } - canStop() { - if (this.selectedRow?.length) { - let flag: boolean = true; - this.selectedRow.forEach(item => { - if (item.status !== IN_PROCESS) { - flag = false; + loadFirstPage(): void { + let st: ClrDatagridStateInterface = this.currentState; + if (!st) { + st = { + page: {} + }; } - }); - return flag; - } - return false; - } + st.page.size = this.pageSize; + st.page.from = 0; + st.page.to = this.pageSize - 1; - confirmStop(message: ConfirmationAcknowledgement) { - if ( - message && - message.state === ConfirmationState.CONFIRMED && - message.source === ConfirmationTargets.STOP_EXECUTIONS - ) { - this.StopExecutions(message.data); - } - } - - StopExecutions(targets: ReplicationJobItem[]): void { - if (targets && targets.length < 1) { - return; + this.clrLoadJobs(true, st); } - this.isStopOnGoing = true; - if (this.jobs && this.jobs.length) { - let ExecutionsStop$ = targets.map(target => this.StopOperate(target)); - forkJoin(ExecutionsStop$) - .pipe( - finalize(() => { - this.refreshJobs(); - this.isStopOnGoing = false; - }) - ) - .subscribe(() => { } - , error => { - this.errorHandlerEntity.error(error); - }); + selectOneRule(rule: ReplicationPolicy) { + if (rule && rule.id) { + this.hiddenJobList = false; + this.search.ruleId = rule.id || ""; + this.loadFirstPage(); + } } - } - StopOperate(targets: ReplicationJobItem): any { - let operMessage = new OperateInfo(); - operMessage.name = "OPERATION.STOP_EXECUTIONS"; - operMessage.data.id = targets.id; - operMessage.state = OperationState.progressing; - operMessage.data.name = targets.id; - this.operationService.publishInfo(operMessage); - - return this.replicationService - .stopJobs(targets.id) - .pipe( - map(response => { - this.translateService - .get("BATCH.STOP_SUCCESS") - .subscribe(res => - operateChanges(operMessage, OperationState.success) + replicateManualRule(rule: ReplicationPolicy) { + if (rule) { + let replicationMessage = new ConfirmationMessage( + "REPLICATION.REPLICATION_TITLE", + "REPLICATION.REPLICATION_SUMMARY", + rule.name, + rule, + ConfirmationTargets.TARGET, + ConfirmationButtons.REPLICATE_CANCEL ); - }), - catchError(error => { - const message = errorHandler(error); - this.translateService.get(message).subscribe(res => - operateChanges(operMessage, OperationState.failure, res) - ); - return observableThrowError(error); - }) - ); - } - - reloadRules(isReady: boolean) { - if (isReady) { - this.search.ruleName = ""; - this.filterComponent.currentValue = ""; - this.listReplicationRule.refreshRule(); - } - } - - refreshRules() { - this.search.ruleName = ""; - if (this.filterComponent.currentValue) { - this.filterComponent.currentValue = ""; - this.filterComponent.filterTerms.next(''); // will trigger refreshing - } else { - this.listReplicationRule.refreshRule(); // manually refresh - } - } - - refreshJobs() { - this.currentTerm = ""; - this.currentPage = 1; - let st: ClrDatagridStateInterface = { - page: { - from: 0, - to: this.pageSize - 1, - size: this.pageSize - } - }; - this.clrLoadJobs(true, st); - } - - openFilter(isOpen: boolean): void { - this.isOpenFilterTag = isOpen; - } - getDuration(j: ReplicationJobItem) { - if (!j) { - return; + this.replicationConfirmDialog.open(replicationMessage); + } } - let start_time = new Date(j.start_time).getTime(); - let end_time = new Date(j.end_time).getTime(); - let timesDiff = end_time - start_time; - let timesDiffSeconds = timesDiff / 1000; - let minutes = Math.floor(timesDiffSeconds / ONE_MINUTE_SECONDS); - let seconds = Math.floor(timesDiffSeconds % ONE_MINUTE_SECONDS); - if (minutes > 0) { - if (seconds === 0) { - return minutes + "m"; - } - return minutes + "m" + seconds + "s"; + confirmReplication(message: ConfirmationAcknowledgement) { + if ( + message && + message.source === ConfirmationTargets.TARGET && + message.state === ConfirmationState.CONFIRMED + ) { + let rule: ReplicationPolicy = message.data; + + if (rule) { + forkJoin(this.replicationOperate(rule)).subscribe(item => { + this.selectOneRule(rule); + }, error => { + this.errorHandlerEntity.error(error); + }); + } + } } - if (seconds > 0) { - return seconds + "s"; + replicationOperate(rule: ReplicationPolicy): Observable { + // init operation info + let operMessage = new OperateInfo(); + operMessage.name = "OPERATION.REPLICATION"; + operMessage.data.id = rule.id; + operMessage.state = OperationState.progressing; + operMessage.data.name = rule.name; + this.operationService.publishInfo(operMessage); + + return this.replicationService.startReplication({ + execution: { + policy_id: rule.id + } + }).pipe( + map(response => { + this.translateService + .get("BATCH.REPLICATE_SUCCESS") + .subscribe(res => + operateChanges(operMessage, OperationState.success) + ); + }), + catchError(error => { + const message = errorHandler(error); + this.translateService.get(message).subscribe(res => + operateChanges(operMessage, OperationState.failure, res) + ); + return observableThrowError(error); + }) + ); } - if (seconds <= 0 && timesDiff > 0) { - return timesDiff + 'ms'; - } else { - return '-'; + doFilterJob($event: any): void { + this.defaultFilter = $event["target"].value; + this.doSearchJobs(this.currentTerm); } - } - getStatusStr(status: string): string { - if (STATUS_MAP && STATUS_MAP[status]) { - return STATUS_MAP[status]; + + doSearchJobs(terms: string) { + if (!terms) { + return; + } + this.currentTerm = terms.trim(); + this.currentPage = 1; + this.loadFirstPage(); + } + + hideJobs() { + this.search.ruleId = 0; + this.jobs = []; + this.hiddenJobList = true; + } + + openStopExecutionsDialog(targets: ReplicationJobItem[]) { + let ExecutionId = targets.map(robot => robot.id).join(","); + let StopExecutionsMessage = new ConfirmationMessage( + "REPLICATION.STOP_TITLE", + "REPLICATION.STOP_SUMMARY", + ExecutionId, + targets, + ConfirmationTargets.STOP_EXECUTIONS, + ConfirmationButtons.STOP_CANCEL + ); + this.StopConfirmDialog.open(StopExecutionsMessage); + } + + canStop() { + if (this.selectedRow?.length) { + let flag: boolean = true; + this.selectedRow.forEach(item => { + if (item.status !== IN_PROCESS) { + flag = false; + } + }); + return flag; + } + return false; + } + + confirmStop(message: ConfirmationAcknowledgement) { + if ( + message && + message.state === ConfirmationState.CONFIRMED && + message.source === ConfirmationTargets.STOP_EXECUTIONS + ) { + this.StopExecutions(message.data); + } + } + + StopExecutions(targets: ReplicationJobItem[]): void { + if (targets && targets.length < 1) { + return; + } + + this.isStopOnGoing = true; + if (this.jobs && this.jobs.length) { + let ExecutionsStop$ = targets.map(target => this.StopOperate(target)); + forkJoin(ExecutionsStop$) + .pipe( + finalize(() => { + this.refreshJobs(); + this.isStopOnGoing = false; + }) + ) + .subscribe(() => { + } + , error => { + this.errorHandlerEntity.error(error); + }); + } + } + + StopOperate(targets: ReplicationJobItem): any { + let operMessage = new OperateInfo(); + operMessage.name = "OPERATION.STOP_EXECUTIONS"; + operMessage.data.id = targets.id; + operMessage.state = OperationState.progressing; + operMessage.data.name = targets.id; + this.operationService.publishInfo(operMessage); + + return this.replicationService + .stopReplication({ + id: targets.id + }) + .pipe( + map(response => { + this.translateService + .get("BATCH.STOP_SUCCESS") + .subscribe(res => + operateChanges(operMessage, OperationState.success) + ); + }), + catchError(error => { + const message = errorHandler(error); + this.translateService.get(message).subscribe(res => + operateChanges(operMessage, OperationState.failure, res) + ); + return observableThrowError(error); + }) + ); + } + + reloadRules(isReady: boolean) { + if (isReady) { + this.search.ruleName = ""; + this.filterComponent.currentValue = ""; + this.listReplicationRule.refreshRule(); + } + } + + refreshRules() { + this.search.ruleName = ""; + if (this.filterComponent.currentValue) { + this.filterComponent.currentValue = ""; + this.filterComponent.filterTerms.next(''); // will trigger refreshing + } else { + this.listReplicationRule.refreshRule(); // manually refresh + } + } + + refreshJobs() { + this.currentTerm = ""; + this.currentPage = 1; + let st: ClrDatagridStateInterface = { + page: { + from: 0, + to: this.pageSize - 1, + size: this.pageSize + } + }; + this.clrLoadJobs(true, st); + } + + openFilter(isOpen: boolean): void { + this.isOpenFilterTag = isOpen; + } + + getDuration(j: ReplicationJobItem) { + if (!j) { + return; + } + + let start_time = new Date(j.start_time).getTime(); + let end_time = new Date(j.end_time).getTime(); + let timesDiff = end_time - start_time; + let timesDiffSeconds = timesDiff / 1000; + let minutes = Math.floor(timesDiffSeconds / ONE_MINUTE_SECONDS); + let seconds = Math.floor(timesDiffSeconds % ONE_MINUTE_SECONDS); + if (minutes > 0) { + if (seconds === 0) { + return minutes + "m"; + } + return minutes + "m" + seconds + "s"; + } + + if (seconds > 0) { + return seconds + "s"; + } + + if (seconds <= 0 && timesDiff > 0) { + return timesDiff + 'ms'; + } else { + return '-'; + } + } + + getStatusStr(status: string): string { + if (STATUS_MAP && STATUS_MAP[status]) { + return STATUS_MAP[status]; + } + return status; } - return status; - } } diff --git a/src/portal/src/app/base/left-side-nav/replication/total-replication-page.component.html b/src/portal/src/app/base/left-side-nav/replication/total-replication-page.component.html index a823aac15..4ac8b8587 100644 --- a/src/portal/src/app/base/left-side-nav/replication/total-replication-page.component.html +++ b/src/portal/src/app/base/left-side-nav/replication/total-replication-page.component.html @@ -5,9 +5,8 @@ [isSystemAdmin]="isSystemAdmin" [withAdmiral]="withAdmiral" (goToRegistry)="goRegistry()" - (redirect)="customRedirect($event)" [hasCreateReplicationPermission]="true" [hasUpdateReplicationPermission]="true" [hasDeleteReplicationPermission]="true" [hasExecuteReplicationPermission]="true"> -
\ No newline at end of file + diff --git a/src/portal/src/app/base/left-side-nav/replication/total-replication-page.component.ts b/src/portal/src/app/base/left-side-nav/replication/total-replication-page.component.ts index 13e8294ea..ffc0fb696 100644 --- a/src/portal/src/app/base/left-side-nav/replication/total-replication-page.component.ts +++ b/src/portal/src/app/base/left-side-nav/replication/total-replication-page.component.ts @@ -15,7 +15,6 @@ import { Component, OnDestroy, OnInit } from '@angular/core'; import { Router, ActivatedRoute, NavigationEnd } from "@angular/router"; import {SessionService} from "../../../shared/services/session.service"; import {AppConfigService} from "../../../services/app-config.service"; -import { ReplicationRule } from "../../../shared/services"; import { Subscription } from "rxjs"; import { EventService, HarborEvent } from "../../../services/event-service/event.service"; // The route path which will display this component @@ -64,11 +63,6 @@ export class TotalReplicationPageComponent implements OnInit, OnDestroy { this.scrollSub = null; } } - customRedirect(rule: ReplicationRule): void { - if (rule) { - this.router.navigate(['../projects', rule.projects[0].project_id, 'replications'], { relativeTo: this.activeRoute }); - } - } goRegistry(): void { this.router.navigate(['harbor', 'registries']); } diff --git a/src/portal/src/app/shared/services/endpoint.service.ts b/src/portal/src/app/shared/services/endpoint.service.ts index 8bea83b9d..808529858 100644 --- a/src/portal/src/app/shared/services/endpoint.service.ts +++ b/src/portal/src/app/shared/services/endpoint.service.ts @@ -7,8 +7,9 @@ import { HTTP_GET_OPTIONS, CURRENT_BASE_HREF } from "../units/utils"; import { RequestQueryParams } from "./index"; -import { Endpoint, ReplicationRule, PingEndpoint } from "./index"; +import { Endpoint, PingEndpoint } from "./index"; import { catchError, map } from "rxjs/operators"; +import { ReplicationPolicy } from '../../../../ng-swagger-gen/models/replication-policy'; export const ADAPTERS_MAP = { "ali-acr": "Alibaba ACR", @@ -264,7 +265,7 @@ export class EndpointDefaultService extends EndpointService { let requestUrl: string = `${this._endpointUrl}/${endpointId}/policies`; return this.http .get(requestUrl, HTTP_GET_OPTIONS) - .pipe(map(response => response as ReplicationRule[]) + .pipe(map(response => response as ReplicationPolicy[]) , catchError(error => observableThrowError(error))); } diff --git a/src/portal/src/app/shared/services/interface.ts b/src/portal/src/app/shared/services/interface.ts index 2ab0f0229..47f621a29 100644 --- a/src/portal/src/app/shared/services/interface.ts +++ b/src/portal/src/app/shared/services/interface.ts @@ -53,32 +53,6 @@ export interface Filter { values?: string[]; } -/** - * Interface for replication rule. - * - ** - * interface ReplicationRule - * interface Filter - * interface Trigger - */ -export interface ReplicationRule extends Base { - [key: string]: any; - id?: number; - name: string; - description: string; - trigger: Trigger; - filters: Filter[]; - deletion?: boolean; - src_registry?: any; - dest_registry?: any; - src_namespaces: string[]; - dest_namespace?: string; - dest_namespace_replace_count?: number; - enabled: boolean; - override: boolean; - speed: number; -} - export class Filter { type: string; value?: any; diff --git a/src/portal/src/app/shared/services/replication.service.ts b/src/portal/src/app/shared/services/replication.service.ts index 87c9e5608..f021d9885 100644 --- a/src/portal/src/app/shared/services/replication.service.ts +++ b/src/portal/src/app/shared/services/replication.service.ts @@ -9,13 +9,13 @@ import { } from "../units/utils"; import { ReplicationJob, - ReplicationRule, ReplicationJobItem, ReplicationTasks } from "./interface"; import { RequestQueryParams } from "./RequestQueryParams"; import { map, catchError } from "rxjs/operators"; import { Observable, throwError as observableThrowError } from "rxjs"; +import { ReplicationPolicy } from '../../../../ng-swagger-gen/models/replication-policy'; /** * Define the service methods to handle the replication (rule and job) related things. @@ -44,13 +44,13 @@ export abstract class ReplicationService { ruleName?: string, queryParams?: RequestQueryParams ): - | Observable; + | Observable; abstract getReplicationRulesResponse( ruleName?: string, page?: number , pageSize?: number, queryParams?: RequestQueryParams - ): Observable>; + ): Observable>; /** * Get the specified replication rule. @@ -63,7 +63,7 @@ export abstract class ReplicationService { */ abstract getReplicationRule( ruleId: number | string - ): Observable; + ): Observable; /** @@ -88,7 +88,7 @@ export abstract class ReplicationService { * @memberOf ReplicationService */ abstract createReplicationRule( - replicationRule: ReplicationRule + replicationRule: ReplicationPolicy ): Observable; /** @@ -102,7 +102,7 @@ export abstract class ReplicationService { */ abstract updateReplicationRule( id: number, - rep: ReplicationRule + rep: ReplicationPolicy ): Observable; /** @@ -229,7 +229,7 @@ export class ReplicationDefaultService extends ReplicationService { // Private methods // Check if the rule object is valid - _isValidRule(rule: ReplicationRule): boolean { + _isValidRule(rule: ReplicationPolicy): boolean { return ( rule !== undefined && rule != null && @@ -255,7 +255,7 @@ export class ReplicationDefaultService extends ReplicationService { ruleName?: string, queryParams?: RequestQueryParams ): - | Observable { + | Observable { if (!queryParams) { queryParams = new RequestQueryParams(); } @@ -269,14 +269,14 @@ export class ReplicationDefaultService extends ReplicationService { } return this.http .get(this._ruleBaseUrl, buildHttpRequestOptions(queryParams)) - .pipe(map(response => response as ReplicationRule[]) + .pipe(map(response => response as ReplicationPolicy[]) , catchError(error => observableThrowError(error))); } public getReplicationRulesResponse( ruleName?: string, page?: number, pageSize?: number, - queryParams?: RequestQueryParams): Observable> { + queryParams?: RequestQueryParams): Observable> { if (!queryParams) { queryParams = new RequestQueryParams(); } @@ -290,14 +290,14 @@ export class ReplicationDefaultService extends ReplicationService { queryParams = queryParams.set("page_size", pageSize + ""); } return this.http - .get>(this._ruleBaseUrl, buildHttpRequestOptionsWithObserveResponse(queryParams)) - .pipe(map(response => response as HttpResponse) + .get>(this._ruleBaseUrl, buildHttpRequestOptionsWithObserveResponse(queryParams)) + .pipe(map(response => response as HttpResponse) , catchError(error => observableThrowError(error))); } public getReplicationRule( ruleId: number | string - ): Observable { + ): Observable { if (!ruleId) { return observableThrowError("Bad argument"); } @@ -305,7 +305,7 @@ export class ReplicationDefaultService extends ReplicationService { let url: string = `${this._ruleBaseUrl}/${ruleId}`; return this.http .get(url, HTTP_GET_OPTIONS) - .pipe(map(response => response as ReplicationRule) + .pipe(map(response => response as ReplicationPolicy) , catchError(error => observableThrowError(error))); } @@ -324,7 +324,7 @@ export class ReplicationDefaultService extends ReplicationService { } public createReplicationRule( - replicationRule: ReplicationRule + replicationRule: ReplicationPolicy ): Observable { if (!this._isValidRule(replicationRule)) { return observableThrowError("Bad argument"); @@ -342,7 +342,7 @@ export class ReplicationDefaultService extends ReplicationService { public updateReplicationRule( id: number, - rep: ReplicationRule + rep: ReplicationPolicy ): Observable { if (!this._isValidRule(rep)) { return observableThrowError("Bad argument");