Merge pull request #7224 from pureshine/replication_ng

Refactor replication-task page according to the new design
This commit is contained in:
Wenkai Yin 2019-03-29 17:19:31 +08:00 committed by GitHub
commit 1491cf1846
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 358 additions and 164 deletions

View File

@ -42,6 +42,19 @@ import { RouterTestingModule } from '@angular/router/testing';
import { of } from "rxjs"; import { of } from "rxjs";
describe("CreateEditRuleComponent (inline template)", () => { describe("CreateEditRuleComponent (inline template)", () => {
let mockEndpoint: Endpoint = {
id: 1,
credential: {
access_key: "admin",
access_secret: "",
type: "basic"
},
description: "test",
insecure: false,
name: "target_01",
type: "Harbor",
url: "https://10.117.4.151"
};
let mockRules: ReplicationRule[] = [ let mockRules: ReplicationRule[] = [
{ {
id: 1, id: 1,
@ -205,6 +218,8 @@ describe("CreateEditRuleComponent (inline template)", () => {
let spyJobs: jasmine.Spy; let spyJobs: jasmine.Spy;
let spyAdapter: jasmine.Spy; let spyAdapter: jasmine.Spy;
let spyEndpoint: jasmine.Spy; let spyEndpoint: jasmine.Spy;
let spyEndpoints: jasmine.Spy;
let config: IServiceConfig = { let config: IServiceConfig = {
replicationBaseEndpoint: "/api/replication/testing", replicationBaseEndpoint: "/api/replication/testing",
@ -262,10 +277,14 @@ describe("CreateEditRuleComponent (inline template)", () => {
spyAdapter = spyOn(replicationService, "getReplicationAdapter").and.returnValues( spyAdapter = spyOn(replicationService, "getReplicationAdapter").and.returnValues(
of(mockAdapter)); of(mockAdapter));
spyEndpoint = spyOn(endpointService, "getEndpoints").and.returnValues( spyEndpoints = spyOn(endpointService, "getEndpoints").and.returnValues(
of(mockEndpoints) of(mockEndpoints)
); );
spyEndpoint = spyOn(endpointService, "getEndpoint").and.returnValue(
of(mockEndpoint)
);
fixture.detectChanges(); fixture.detectChanges();
}); });

View File

@ -10,14 +10,13 @@
<clr-dg-column class="min-width">{{'REPLICATION.SRC_NAMESPACE' | translate}}</clr-dg-column> <clr-dg-column class="min-width">{{'REPLICATION.SRC_NAMESPACE' | translate}}</clr-dg-column>
<clr-dg-column>{{'REPLICATION.REPLICATION_MODE' | translate}}</clr-dg-column> <clr-dg-column>{{'REPLICATION.REPLICATION_MODE' | translate}}</clr-dg-column>
<clr-dg-column class="min-width">{{'REPLICATION.DESTINATION_NAMESPACE' | translate}}</clr-dg-column> <clr-dg-column class="min-width">{{'REPLICATION.DESTINATION_NAMESPACE' | translate}}</clr-dg-column>
<clr-dg-column>{{'REPLICATION.LAST_REPLICATION' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'trigger'">{{'REPLICATION.REPLICATION_TRIGGER' | translate}}</clr-dg-column> <clr-dg-column [clrDgField]="'trigger'">{{'REPLICATION.REPLICATION_TRIGGER' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'description'">{{'REPLICATION.DESCRIPTION' | translate}}</clr-dg-column> <clr-dg-column [clrDgField]="'description'">{{'REPLICATION.DESCRIPTION' | translate}}</clr-dg-column>
<clr-dg-placeholder>{{'REPLICATION.PLACEHOLDER' | translate }}</clr-dg-placeholder> <clr-dg-placeholder>{{'REPLICATION.PLACEHOLDER' | translate }}</clr-dg-placeholder>
<clr-dg-row *clrDgItems="let p of changedRules" [clrDgItem]="p" [style.backgroundColor]="(projectScope && withReplicationJob && selectedId === p.id) ? '#eee' : ''"> <clr-dg-row *clrDgItems="let p of changedRules; let i=index" [clrDgItem]="p" [style.backgroundColor]="(projectScope && withReplicationJob && selectedId === p.id) ? '#eee' : ''">
<clr-dg-cell>{{p.name}}</clr-dg-cell> <clr-dg-cell>{{p.name}}</clr-dg-cell>
<clr-dg-cell class="min-width"> <clr-dg-cell class="min-width">
{{p.src_registry_id>0? srcRegistry : 'current'}} : {{p.src_namespaces?.length>0 ? p.src_namespaces[0]: ''}} {{p.src_registry_id? registryName[i]:'current'}} : {{p.src_namespaces?.length>0 ? p.src_namespaces[0]: ''}}
<clr-tooltip> <clr-tooltip>
<clr-icon *ngIf="p.src_namespaces && p.src_namespaces.length > 1" clrTooltipTrigger shape="ellipsis-horizontal" size="18"></clr-icon> <clr-icon *ngIf="p.src_namespaces && p.src_namespaces.length > 1" clrTooltipTrigger shape="ellipsis-horizontal" size="18"></clr-icon>
<clr-tooltip-content clrPosition="top-right" clrSize="md" *clrIfOpen> <clr-tooltip-content clrPosition="top-right" clrSize="md" *clrIfOpen>
@ -29,9 +28,8 @@
{{p.src_registry_id>0? 'pull-based' : 'push-based'}} {{p.src_registry_id>0? 'pull-based' : 'push-based'}}
</clr-dg-cell> </clr-dg-cell>
<clr-dg-cell class="min-width"> <clr-dg-cell class="min-width">
{{p.dest_registry_id>0? srcRegistry : 'current'}} : {{p.dest_namespace? p.dest_namespace: ''}} {{p.dest_registry_id? registryName[i]: 'current'}} : {{p.dest_namespace? p.dest_namespace: ''}}
</clr-dg-cell> </clr-dg-cell>
<clr-dg-cell>{{p.update_time | date: 'short'}}</clr-dg-cell>
<clr-dg-cell>{{p.trigger ? p.trigger.type : ''}}</clr-dg-cell> <clr-dg-cell>{{p.trigger ? p.trigger.type : ''}}</clr-dg-cell>
<clr-dg-cell> <clr-dg-cell>
{{p.description ? trancatedDescription(p.description) : '-'}} {{p.description ? trancatedDescription(p.description) : '-'}}

View File

@ -15,8 +15,23 @@ import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
import { ReplicationService, ReplicationDefaultService } from '../service/replication.service'; import { ReplicationService, ReplicationDefaultService } from '../service/replication.service';
import { OperationService } from "../operation/operation.service"; import { OperationService } from "../operation/operation.service";
import { of } from 'rxjs'; import { of } from 'rxjs';
import { EndpointService, EndpointDefaultService } from "../service/endpoint.service";
import { Endpoint } from "../service/interface";
describe('ListReplicationRuleComponent (inline template)', () => { describe('ListReplicationRuleComponent (inline template)', () => {
let mockEndpoint: Endpoint = {
id: 1,
credential: {
access_key: "admin",
access_secret: "",
type: "basic"
},
description: "test",
insecure: false,
name: "target_01",
type: "Harbor",
url: "https://10.117.4.151"
};
let mockRules: ReplicationRule[] = [ let mockRules: ReplicationRule[] = [
{ {
@ -49,10 +64,16 @@ describe('ListReplicationRuleComponent (inline template)', () => {
let replicationService: ReplicationService; let replicationService: ReplicationService;
let endpointService: EndpointService;
let spyRules: jasmine.Spy; let spyRules: jasmine.Spy;
let spyEndpoint: jasmine.Spy;
let config: IServiceConfig = { let config: IServiceConfig = {
replicationRuleEndpoint: '/api/policies/replication/testing' replicationRuleEndpoint: '/api/policies/replication/testing',
systemInfoEndpoint: "/api/endpoints/testing"
}; };
beforeEach(async(() => { beforeEach(async(() => {
@ -69,7 +90,8 @@ describe('ListReplicationRuleComponent (inline template)', () => {
ErrorHandler, ErrorHandler,
{ provide: SERVICE_CONFIG, useValue: config }, { provide: SERVICE_CONFIG, useValue: config },
{ provide: ReplicationService, useClass: ReplicationDefaultService }, { provide: ReplicationService, useClass: ReplicationDefaultService },
{ provide: OperationService } { provide: OperationService },
{ provide: EndpointService, useClass: EndpointDefaultService }
] ]
}); });
})); }));
@ -79,6 +101,11 @@ describe('ListReplicationRuleComponent (inline template)', () => {
comp = fixture.componentInstance; comp = fixture.componentInstance;
replicationService = fixture.debugElement.injector.get(ReplicationService); replicationService = fixture.debugElement.injector.get(ReplicationService);
spyRules = spyOn(replicationService, 'getReplicationRules').and.returnValues(of(mockRules)); spyRules = spyOn(replicationService, 'getReplicationRules').and.returnValues(of(mockRules));
endpointService = fixture.debugElement.injector.get(EndpointService);
spyEndpoint = spyOn(endpointService, "getEndpoint").and.returnValue(
of(mockEndpoint)
);
fixture.detectChanges(); fixture.detectChanges();
}); });

View File

@ -27,9 +27,9 @@ import {
import { Comparator } from "../service/interface"; import { Comparator } from "../service/interface";
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, throwError as observableThrowError } from "rxjs"; import { Observable, forkJoin, of, throwError as observableThrowError } from "rxjs";
import { ReplicationService } from "../service/replication.service"; import { ReplicationService } from "../service/replication.service";
import { EndpointService } from "../service/endpoint.service";
import { import {
ReplicationJob, ReplicationJob,
ReplicationJobItem, ReplicationJobItem,
@ -82,7 +82,9 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
changedRules: ReplicationRule[]; changedRules: ReplicationRule[];
ruleName: string; ruleName: string;
canDeleteRule: boolean; canDeleteRule: boolean;
srcRegistry: string = 'docker hub'; registryName: [] = [];
desRegistry: [] = [];
currentRegistry: string;
selectedRow: ReplicationRule; selectedRow: ReplicationRule;
@ -96,6 +98,7 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
enabledComparator: Comparator<ReplicationRule> = new CustomComparator<ReplicationRule>("enabled", "number"); enabledComparator: Comparator<ReplicationRule> = new CustomComparator<ReplicationRule>("enabled", "number");
constructor(private replicationService: ReplicationService, constructor(private replicationService: ReplicationService,
private endpointService: EndpointService,
private translateService: TranslateService, private translateService: TranslateService,
private errorHandler: ErrorHandler, private errorHandler: ErrorHandler,
private operationService: OperationService, private operationService: OperationService,
@ -116,6 +119,9 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
if (!this.projectScope) { if (!this.projectScope) {
this.retrieveRules(); this.retrieveRules();
} }
this.translateService.get("REPLICATION.CURRENT").subscribe((res: string) => {
this.currentRegistry = res;
});
} }
ngOnChanges(changes: SimpleChanges): void { ngOnChanges(changes: SimpleChanges): void {
let proIdChange: SimpleChange = changes["projectId"]; let proIdChange: SimpleChange = changes["projectId"];
@ -140,13 +146,36 @@ export class ListReplicationRuleComponent implements OnInit, OnChanges {
// job list hidden // job list hidden
this.hideJobs.emit(); this.hideJobs.emit();
this.changedRules = this.rules; this.changedRules = this.rules;
this.loading = false; // get registry name
let targetLists: ReplicationRule[] = rules;
if (targetLists && targetLists.length) {
let registryList: any[] = [];
targetLists.forEach(target => {
let tartId: number;
if (target.src_registry_id > 0) {
tartId = target.src_registry_id;
} else {
tartId = target.dest_registry_id;
}
registryList.push(this.getRegistry(tartId));
});
forkJoin(...registryList).subscribe((item) => {
this.selectedRow = null;
this.registryName = item.map(target => target.name);
this.loading = false;
});
}
}, error => { }, error => {
this.errorHandler.error(error); this.errorHandler.error(error);
this.loading = false; this.loading = false;
}); });
} }
getRegistry(endpointId) {
return this.endpointService.getEndpoint(endpointId);
}
replicateRule(rule: ReplicationRule): void { replicateRule(rule: ReplicationRule): void {
this.replicateManual.emit(rule); this.replicateManual.emit(rule);
} }

View File

@ -1,117 +1,135 @@
<div class="replication-tasks"> <div class="replication-tasks">
<section class="overview-section"> <section class="overview-section">
<div class="title-wrapper clr-row"> <div class="title-wrapper">
<div class="clr-col-lg-9"> <div>
<div> <div>
<a (click)="onBack()" class="onback">< {{'PROJECT_DETAIL.REPLICATION'| translate}}</a> <a (click)="onBack()" class="onback"><{{'PROJECT_DETAIL.REPLICATION'|
</div> translate}}</a>
<div class="title-block clr-row">
<div class="clr-col-2">
<h2 class="custom-h2">{{'REPLICATION.REPLICATION_EXECUTION'| translate}}</h2>
<h4>{{executionId}}</h4>
</div>
<div class="clr-col-3">
<span class="label label-info" *ngIf="executions === 'InProgress'">In Progress</span>
<span class="label label-success" *ngIf="executions === 'success'">Success</span>
<span class="label label-danger" *ngIf="executions=== 'failture'">Failture</span>
</div>
</div>
</div>
<div class="clr-col-3">
<button class="btn btn-outline" (click)="stopJob()" [disabled]="stopOnGoing">{{'REPLICATION.STOPJOB' | translate}}</button>
</div>
</div> </div>
<div class="execution-block row"> <div class="title-block">
<div class="clr-col-sm-12 clr-col-md-6"> <div>
<div class="flex-block"> <h2 class="custom-h2 h2-style">{{'REPLICATION.REPLICATION_EXECUTION'|
<section class="execution-detail-label"> translate}}</h2>
<section class="detail-row"> <span class="id-divider"></span>
<span class="label label-purple detail-span">{{'REPLICATION.SUCCESS'| translate}}</span> <h2 class="custom-h2 h2-style">{{executionId}}</h2>
<div class="execution-details">{{'1'}}</div> </div>
</section> <div>
<section class="detail-row"> <div class="status-progress" *ngIf="executions === 'InProgress'">
<span class="label label-red detail-span">{{'REPLICATION.FAILTURE'| translate}}</span> <span class="spinner spinner-inline"></span>
<div class="execution-details">{{'2'}}</div> <span>{{'REPLICATION.IN_PROGRESS'| translate}}</span>
</section>
<section class="detail-row">
<span class="label label-light-blue detail-span">{{'REPLICATION.IN_PROGRESS'| translate}}</span>
<div class="execution-details">{{'3'}}</div>
</section>
</section>
</div>
</div> </div>
<div class="clr-col-sm-12 clr-col-md-6"> <div class="status-success" *ngIf="executions === 'success'">
<div class="executions-detail"> <clr-icon size="18" shape="success-standard" class="color-green"></clr-icon>
<div class="policy"> <span>{{'REPLICATION.SUCCESS'| translate}}</span>
<label>{{'REPLICATION.POLICY' | translate}} :</label>
<span>{{'My 1st policy'}}</span>
</div>
<div class="trigger">
<label for="">{{'REPLICATION.TRIGGER_MODE' | translate}} :</label>
<span>{{'Schedule'}}</span>
</div>
<div class="start-time">
<label for="">{{'REPLICATION.CREATION_TIME' | translate}} :</label>
<span>{{'3/14/19, 2:26 PM'}}</span>
</div>
</div>
</div> </div>
<div class="status-failed" *ngIf="executions === 'failed'">
<clr-icon size="18" shape="error-standard" class="color-red"></clr-icon>
<span>{{'REPLICATION.FAILTURE'| translate}}</span>
</div>
</div>
<div>
<button class="btn btn-primary btn-sm" (click)="stopJob()"
[disabled]="stopOnGoing">{{'REPLICATION.STOPJOB' | translate}}</button>
</div>
</div> </div>
</section> </div>
<div class="tasks-detail">
<h3 class="modal-title">Tasks</h3>
<div class="row flex-items-xs-between flex-items-xs-bottom">
<div class="action-select">
<div class="select filterTag" [hidden]="!isOpenFilterTag">
<select>
<option value="username">{{"AUDIT_LOG.USERNAME" | translate | lowercase}}</option>
<option value="repository">{{"CONFIG.REPOSITORY" | translate | lowercase}}</option>
<option value="tag">{{"REPOSITORY.TAG" | translate | lowercase}}</option>
<option value="operation">{{"AUDIT_LOG.OPERATION" | translate | lowercase}}</option>
</select>
</div>
<hbr-filter [withDivider]="true" (openFlag)="openFilter($event)" filterPlaceholder='{{"AUDIT_LOG.FILTER_PLACEHOLDER" | translate}}'></hbr-filter>
<span class="refresh-btn">
<clr-icon shape="refresh"></clr-icon>
<span class="spinner spinner-inline" hidden></span>
</span>
</div>
</div>
<clr-datagrid [(clrDgSelected)]="selectedRow">
<clr-dg-column>{{'REPLICATION.TASK_ID'| translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'resource_type'">{{'REPLICATION.RECOURCE_TYPE' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'src_resource'">{{'REPLICATION.RECOURCE' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'dst_resource'">{{'REPLICATION.DESTINATION' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'status'">{{'REPLICATION.STATUS' | translate}}</clr-dg-column>
<clr-dg-column [clrDgSortBy]="startTimeComparator">{{'REPLICATION.CREATION_TIME' | translate}}</clr-dg-column>
<clr-dg-column [clrDgSortBy]="endTimeComparator">{{'REPLICATION.END_TIME' | translate}}</clr-dg-column>
<clr-dg-column>{{'REPLICATION.LOGS' | translate}}</clr-dg-column>
<clr-dg-row *clrDgItems="let t of tasks">
<clr-dg-cell>{{t.id}}</clr-dg-cell>
<clr-dg-cell>{{t.resource_type}}</clr-dg-cell>
<clr-dg-cell>{{t.src_resource}}</clr-dg-cell>
<clr-dg-cell>{{t.dst_resource}}</clr-dg-cell>
<clr-dg-cell>{{t.status}}</clr-dg-cell>
<clr-dg-cell>{{t.start_time | date: 'short'}}</clr-dg-cell>
<clr-dg-cell>{{t.end_time | date: 'short'}}</clr-dg-cell>
<clr-dg-cell>
<span *ngIf="t.status=='InProgress'; else elseBlock" class="label">{{'REPLICATION.NO_LOGS' | translate}}</span>
<ng-template #elseBlock>
<a target="_blank" [href]="viewLog(t.id)">
<clr-icon shape="list"></clr-icon>
</a>
</ng-template>
</clr-dg-cell>
</clr-dg-row>
<clr-dg-footer>
<span *ngIf="pagination.totalItems">{{pagination.firstItem + 1}}
-
{{pagination.lastItem +1 }} {{'ROBOT_ACCOUNT.OF' |
translate}} </span>
{{pagination.totalItems }} {{'ROBOT_ACCOUNT.ITEMS' | translate}}
<clr-dg-pagination #pagination [clrDgPageSize]="15"></clr-dg-pagination>
</clr-dg-footer>
</clr-datagrid>
</div> </div>
<div class="execution-block">
<div class="executions-detail">
<div class="policy">
<label>{{'REPLICATION.POLICY' | translate}} :</label>
<span>{{'My 1st policy'}}</span>
</div>
<div class="trigger">
<label for="">{{'REPLICATION.TRIGGER_MODE' | translate}} :</label>
<span>{{'Schedule'}}</span>
</div>
<div class="start-time">
<label for="">{{'REPLICATION.CREATION_TIME' | translate}} :</label>
<span>{{'3/14/19, 2:26 PM'}}</span>
</div>
</div>
<div class="flex-block">
<section class="execution-detail-label">
<section class="detail-row">
<span class="label label-purple detail-span">{{'REPLICATION.SUCCESS'| translate}}</span>
<div class="execution-details">{{'1'}}</div>
</section>
<section class="detail-row">
<span class="label label-red detail-span">{{'REPLICATION.FAILTURE'| translate}}</span>
<div class="execution-details">{{'2'}}</div>
</section>
<section class="detail-row">
<span class="label label-light-blue detail-span">{{'REPLICATION.IN_PROGRESS'| translate}}</span>
<div class="execution-details">{{'3'}}</div>
</section>
</section>
</div>
</div>
</section>
<div class="tasks-detail">
<h3 class="modal-title">Tasks</h3>
<div class="row flex-items-xs-between flex-items-xs-bottom">
<div class="action-select">
<div class="select filterTag" [hidden]="!isOpenFilterTag">
<select>
<option value="recourceType">{{'REPLICATION.RECOURCE_TYPE' |translate}}</option>
<option value="recource">{{'REPLICATION.RECOURCE' | translate}}</option>
<option value="destination">{{'REPLICATION.DESTINATION' | translate}}</option>
<option value="status">{{'REPLICATION.STATUS' | translate}}</option>
</select>
</div>
<hbr-filter [withDivider]="true" (openFlag)="openFilter($event)"
filterPlaceholder='{{"REPLICATION.FILTER_PLACEHOLDER" | translate}}'></hbr-filter>
<span class="refresh-btn">
<clr-icon shape="refresh"></clr-icon>
<span class="spinner spinner-inline" hidden></span>
</span>
</div>
</div>
<clr-datagrid [(clrDgSelected)]="selectedRow" [clrDgLoading]="loading">
<clr-dg-column>{{'REPLICATION.TASK_ID'| translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'resource_type'">{{'REPLICATION.RECOURCE_TYPE'
| translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'src_resource'">{{'REPLICATION.RECOURCE' |
translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'dst_resource'">{{'REPLICATION.DESTINATION' |
translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'status'">{{'REPLICATION.STATUS' |
translate}}</clr-dg-column>
<clr-dg-column [clrDgSortBy]="startTimeComparator">{{'REPLICATION.CREATION_TIME'
| translate}}</clr-dg-column>
<clr-dg-column [clrDgSortBy]="endTimeComparator">{{'REPLICATION.END_TIME'
| translate}}</clr-dg-column>
<clr-dg-column>{{'REPLICATION.LOGS' | translate}}</clr-dg-column>
<clr-dg-row *clrDgItems="let t of tasks">
<clr-dg-cell>{{t.id}}</clr-dg-cell>
<clr-dg-cell>{{t.resource_type}}</clr-dg-cell>
<clr-dg-cell>{{t.src_resource}}</clr-dg-cell>
<clr-dg-cell>{{t.dst_resource}}</clr-dg-cell>
<clr-dg-cell>{{t.status}}</clr-dg-cell>
<clr-dg-cell>{{t.start_time | date: 'short'}}</clr-dg-cell>
<clr-dg-cell>{{t.end_time | date: 'short'}}</clr-dg-cell>
<clr-dg-cell>
<span *ngIf="t.status=='InProgress'; else elseBlock" class="label">{{'REPLICATION.NO_LOGS'
| translate}}</span>
<ng-template #elseBlock>
<a target="_blank" [href]="viewLog(t.id)">
<clr-icon shape="list"></clr-icon>
</a>
</ng-template>
</clr-dg-cell>
</clr-dg-row>
<clr-dg-footer>
<span *ngIf="pagination.totalItems">{{pagination.firstItem + 1}}
-
{{pagination.lastItem +1 }} {{'ROBOT_ACCOUNT.OF' |
translate}} </span>
{{pagination.totalItems }} {{'ROBOT_ACCOUNT.ITEMS' | translate}}
<clr-dg-pagination #pagination [clrDgPageSize]="15"></clr-dg-pagination>
</clr-dg-footer>
</clr-datagrid>
</div>
</div> </div>

View File

@ -3,11 +3,33 @@
.title-wrapper { .title-wrapper {
.onback{ .onback{
color: #007cbb; color: #007cbb;
font-size: 12px; font-size: 14px;
cursor: pointer; cursor: pointer;
} }
h4 { .title-block {
margin-top: 8px; display: flex;
align-items: center;
>div:first-child {
width: 210px;
}
>div:nth-child(2) {
width: 140px;
span {
color: #007cbb;
font-size: 12px;
margin-left: 10px;
}
}
.id-divider {
display: inline-block;
height: 25px;
width: 2px;
background-color: #cccccc;
margin: 0 20px;
}
.h2-style {
display: inline-block;
}
} }
} }
.execution-block { .execution-block {
@ -21,7 +43,6 @@
display: flex; display: flex;
.detail-span { .detail-span {
flex:0 0 100px; flex:0 0 100px;
font-weight: 600;
margin-top: 10px; margin-top: 10px;
} }
.execution-details { .execution-details {
@ -31,9 +52,14 @@
} }
} }
.executions-detail { .executions-detail {
font-weight: 600; width: 400px;
span { label {
margin-left: 5px; display: inline-block;
color: black;
width: 120px;
}
>div {
margin-top: 10px;
} }
} }
} }

View File

@ -1,8 +1,8 @@
import { Component, OnInit, Input } from '@angular/core'; import { Component, OnInit, Input } from '@angular/core';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { ReplicationService } from "../../service/replication.service"; import { ReplicationService } from "../../service/replication.service";
import { map, catchError } from "rxjs/operators"; import { TranslateService } from '@ngx-translate/core';
import { Observable, forkJoin, throwError as observableThrowError } from "rxjs"; import { finalize } from "rxjs/operators";
import { ErrorHandler } from "../../error-handler/error-handler"; import { ErrorHandler } from "../../error-handler/error-handler";
import { ReplicationJob, ReplicationTasks, Comparator, ReplicationJobItem } from "../../service/interface"; import { ReplicationJob, ReplicationTasks, Comparator, ReplicationJobItem } from "../../service/interface";
import { CustomComparator } from "../../utils"; import { CustomComparator } from "../../utils";
@ -14,7 +14,11 @@ import { CustomComparator } from "../../utils";
export class ReplicationTasksComponent implements OnInit { export class ReplicationTasksComponent implements OnInit {
isOpenFilterTag: boolean; isOpenFilterTag: boolean;
selectedRow: []; selectedRow: [];
loading = false;
searchTask: string;
defaultFilter = "recourceType";
tasks: ReplicationTasks[] = []; tasks: ReplicationTasks[] = [];
tasksCopy: ReplicationTasks[] = [];
stopOnGoing: boolean; stopOnGoing: boolean;
executions: string = 'InProgress'; executions: string = 'InProgress';
@Input() executionId: string; @Input() executionId: string;
@ -26,15 +30,16 @@ export class ReplicationTasksComponent implements OnInit {
>("end_time", "date"); >("end_time", "date");
constructor( constructor(
private translate: TranslateService,
private router: Router, private router: Router,
private replicationService: ReplicationService, private replicationService: ReplicationService,
private errorHandler: ErrorHandler, private errorHandler: ErrorHandler,
) { } ) { }
ngOnInit(): void { ngOnInit(): void {
// this.getExecutions(); this.clrLoadTasks();
this.getTasks();
// this.executions.status = 'success'; // this.executions.status = 'success';
this.searchTask = '';
} }
// getExecutions(): void { // getExecutions(): void {
@ -54,9 +59,12 @@ export class ReplicationTasksComponent implements OnInit {
stopJob() { stopJob() {
this.stopOnGoing = true; this.stopOnGoing = true;
this.replicationService.stopJobs(this.executionId) this.replicationService.stopJobs(this.executionId)
.subscribe(res => { .subscribe(response => {
this.stopOnGoing = false; this.stopOnGoing = false;
// this.getExecutions(); // this.getExecutions();
this.translate.get("REPLICATION.STOP_SUCCESS", { param: this.executionId }).subscribe((res: string) => {
this.errorHandler.info(res);
});
}, },
error => { error => {
this.errorHandler.error(error); this.errorHandler.error(error);
@ -64,13 +72,33 @@ export class ReplicationTasksComponent implements OnInit {
} }
viewLog(taskId: number | string): string { viewLog(taskId: number | string): string {
return this.replicationService.getJobBaseUrl() + "/" + this.executionId + "/tasks/" + taskId + "/log"; return this.replicationService.getJobBaseUrl() + "/executions/" + this.executionId + "/tasks/" + taskId + "/log";
} }
getTasks(): void { clrLoadTasks(): void {
this.loading = true;
this.replicationService.getReplicationTasks(this.executionId) this.replicationService.getReplicationTasks(this.executionId)
.pipe(finalize(() => (this.loading = false)))
.subscribe(tasks => { .subscribe(tasks => {
this.tasks = tasks.map(x => Object.assign({}, x)); if (this.defaultFilter === 'recourceType') {
this.tasks = tasks.filter(x =>
x.resource_type.includes(this.searchTask)
);
} else if (this.defaultFilter === 'recource') {
this.tasks = tasks.filter(x =>
x.src_resource.includes(this.searchTask)
);
} else if (this.defaultFilter === 'destination') {
this.tasks = tasks.filter(x =>
x.dst_resource.includes(this.searchTask)
);
} else {
this.tasks = tasks.filter(x =>
x.status.includes(this.searchTask)
);
}
this.tasksCopy = tasks.map(x => Object.assign({}, x));
}, },
error => { error => {
this.errorHandler.error(error); this.errorHandler.error(error);
@ -80,12 +108,31 @@ export class ReplicationTasksComponent implements OnInit {
this.router.navigate(["harbor", "replications"]); this.router.navigate(["harbor", "replications"]);
} }
selectFilter($event: any): void {
this.defaultFilter = $event['target'].value;
this.doSearch(this.searchTask);
}
// refresh icon
refreshTasks(): void {
this.searchTask = '';
this.clrLoadTasks();
}
doSearch(value: string): void {
if (!value) {
return;
}
this.searchTask = value.trim();
this.clrLoadTasks();
}
openFilter(isOpen: boolean): void { openFilter(isOpen: boolean): void {
if (isOpen) { if (isOpen) {
this.isOpenFilterTag = true; this.isOpenFilterTag = true;
} else { } else {
this.isOpenFilterTag = false; this.isOpenFilterTag = false;
} }
} }
} }

View File

@ -26,7 +26,7 @@
<h5 class="flex-items-xs-bottom option-left-down">{{'REPLICATION.REPLICATION_EXECUTIONS' | translate}}</h5> <h5 class="flex-items-xs-bottom option-left-down">{{'REPLICATION.REPLICATION_EXECUTIONS' | translate}}</h5>
<div class="flex-items-xs-bottom option-right-down"> <div class="flex-items-xs-bottom option-right-down">
<button class="btn btn-link" (click)="toggleSearchJobOptionalName(currentJobSearchOption)">{{toggleJobSearchOption[currentJobSearchOption] | translate}}</button> <button class="btn btn-link" (click)="toggleSearchJobOptionalName(currentJobSearchOption)">{{toggleJobSearchOption[currentJobSearchOption] | translate}}</button>
<hbr-filter [withDivider]="true" filterPlaceholder='{{"REPLICATION.FILTER_JOBS_PLACEHOLDER" | translate}}' (filterEvt)="doSearchJobs($event)" <hbr-filter [withDivider]="true" filterPlaceholder='{{"REPLICATION.FILTER_EXECUTIONS_PLACEHOLDER" | translate}}' (filterEvt)="doSearchJobs($event)"
[currentValue]="search.repoName"></hbr-filter> [currentValue]="search.repoName"></hbr-filter>
<span class="refresh-btn" (click)="refreshJobs()"> <span class="refresh-btn" (click)="refreshJobs()">
<clr-icon shape="refresh"></clr-icon> <clr-icon shape="refresh"></clr-icon>

View File

@ -28,7 +28,7 @@
} }
.row-right { .row-right {
padding-right: 50px; margin-left: 564px;
} }
.replication-row { .replication-row {

View File

@ -27,6 +27,20 @@ import { of } from 'rxjs';
describe('Replication Component (inline template)', () => { describe('Replication Component (inline template)', () => {
let mockEndpoint: Endpoint = {
id: 1,
credential: {
access_key: "admin",
access_secret: "",
type: "basic"
},
description: "test",
insecure: false,
name: "target_01",
type: "Harbor",
url: "https://10.117.4.151"
};
let mockRules: ReplicationRule[] = [ let mockRules: ReplicationRule[] = [
{ {
@ -128,6 +142,7 @@ describe('Replication Component (inline template)', () => {
let spyRules: jasmine.Spy; let spyRules: jasmine.Spy;
let spyJobs: jasmine.Spy; let spyJobs: jasmine.Spy;
let spyEndpoint: jasmine.Spy; let spyEndpoint: jasmine.Spy;
let spyEndpoints: jasmine.Spy;
let deGrids: DebugElement[]; let deGrids: DebugElement[];
let deRules: DebugElement; let deRules: DebugElement;
@ -138,7 +153,7 @@ describe('Replication Component (inline template)', () => {
let config: IServiceConfig = { let config: IServiceConfig = {
replicationRuleEndpoint: '/api/policies/replication/testing', replicationRuleEndpoint: '/api/policies/replication/testing',
replicationBaseEndpoint: '/api/replication/testing' systemInfoEndpoint: "/api/endpoints/testing"
}; };
beforeEach(async(() => { beforeEach(async(() => {
@ -187,8 +202,8 @@ describe('Replication Component (inline template)', () => {
spyJobs = spyOn(replicationService, 'getExecutions').and.returnValues(of(mockJob)); spyJobs = spyOn(replicationService, 'getExecutions').and.returnValues(of(mockJob));
spyEndpoint = spyOn(endpointService, 'getEndpoints').and.returnValues(of(mockEndpoints)); spyEndpoints = spyOn(endpointService, 'getEndpoints').and.returnValues(of(mockEndpoints));
spyEndpoint = spyOn(endpointService, "getEndpoint").and.returnValue(of(mockEndpoint));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();

View File

@ -278,7 +278,7 @@ export class ReplicationDefaultService extends ReplicationService {
if (!executionId) { if (!executionId) {
return observableThrowError("Bad argument"); return observableThrowError("Bad argument");
} }
let url: string = `${this._replicateUrl}/${executionId}/tasks`; let url: string = `${this._replicateUrl}/executions/${executionId}/tasks`;
return this.http return this.http
.get(url, HTTP_GET_OPTIONS) .get(url, HTTP_GET_OPTIONS)
.pipe(map(response => response.json() as ReplicationTasks) .pipe(map(response => response.json() as ReplicationTasks)
@ -385,10 +385,10 @@ export class ReplicationDefaultService extends ReplicationService {
if (!queryParams) { if (!queryParams) {
queryParams = new RequestQueryParams(); queryParams = new RequestQueryParams();
} }
let url: string = `${this._replicateUrl}/executions`;
queryParams.set("policy_id", "" + ruleId); queryParams.set("policy_id", "" + ruleId);
return this.http return this.http
.get(this._replicateUrl, buildHttpRequestOptions(queryParams)) .get(url, buildHttpRequestOptions(queryParams))
.pipe(map(response => { .pipe(map(response => {
let result: ReplicationJob = { let result: ReplicationJob = {
metadata: { metadata: {
@ -435,7 +435,7 @@ export class ReplicationDefaultService extends ReplicationService {
if (!jobId || jobId <= 0) { if (!jobId || jobId <= 0) {
return observableThrowError("Bad request argument."); return observableThrowError("Bad request argument.");
} }
let requestUrl: string = `${this._replicateUrl}/${jobId}`; let requestUrl: string = `${this._replicateUrl}/executions/${jobId}`;
return this.http return this.http
.put( .put(

View File

@ -331,7 +331,10 @@
"OF": "of" "OF": "of"
}, },
"REPLICATION": { "REPLICATION": {
"CURRENT": "current",
"FILTER_PLACEHOLDER": "Filter Tasks",
"STOP_TITLE": "Confirm Stop Executions", "STOP_TITLE": "Confirm Stop Executions",
"STOP_SUCCESS": "Stop Execution {{param}} Successful",
"STOP_SUMMARY": "Do you want to stop the executions {{param}}?", "STOP_SUMMARY": "Do you want to stop the executions {{param}}?",
"TASK_ID":"Task ID", "TASK_ID":"Task ID",
"RECOURCE_TYPE": "Recource Type", "RECOURCE_TYPE": "Recource Type",
@ -340,14 +343,14 @@
"POLICY": "Policy", "POLICY": "Policy",
"DURATION": "Duration", "DURATION": "Duration",
"SUCCESS_RATE": "Success Rate", "SUCCESS_RATE": "Success Rate",
"SUCCESS": "Success", "SUCCESS": "SUCCESS",
"FAILTURE": "Failture", "FAILTURE": "FAILTURE",
"IN_PROGRESS": "In Progress", "IN_PROGRESS": "IN PROGRESS",
"REPLICATION_RULE": "Replication Rule", "REPLICATION_RULE": "Replication Rule",
"NEW_REPLICATION_RULE": "New Replication Rule", "NEW_REPLICATION_RULE": "New Replication Rule",
"ENDPOINTS": "Endpoints", "ENDPOINTS": "Endpoints",
"FILTER_POLICIES_PLACEHOLDER": "Filter Rules", "FILTER_POLICIES_PLACEHOLDER": "Filter Rules",
"FILTER_JOBS_PLACEHOLDER": "Filter Jobs", "FILTER_EXECUTIONS_PLACEHOLDER": "Filter Executions",
"DELETION_TITLE": "Confirm Rules Deletion", "DELETION_TITLE": "Confirm Rules Deletion",
"DELETION_SUMMARY": "Do you want to delete rules {{param}}?", "DELETION_SUMMARY": "Do you want to delete rules {{param}}?",
"REPLICATION_TITLE": "Confirm Rules replication", "REPLICATION_TITLE": "Confirm Rules replication",

View File

@ -330,7 +330,10 @@
"OF": "of" "OF": "of"
}, },
"REPLICATION": { "REPLICATION": {
"CURRENT": "current",
"FILTER_PLACEHOLDER": "Filter Tasks",
"STOP_TITLE": "Confirme Stop Executions", "STOP_TITLE": "Confirme Stop Executions",
"STOP_SUCCESS": "Stop Execution {{param}} Successful",
"STOP_SUMMARY": "De que desea detener las ejecuciones {{param}}?", "STOP_SUMMARY": "De que desea detener las ejecuciones {{param}}?",
"TASK_ID":"Task ID", "TASK_ID":"Task ID",
"RECOURCE_TYPE": "Recource Type", "RECOURCE_TYPE": "Recource Type",
@ -339,16 +342,16 @@
"POLICY": "Policy", "POLICY": "Policy",
"DURATION": "Duration", "DURATION": "Duration",
"SUCCESS_RATE": "Success Rate", "SUCCESS_RATE": "Success Rate",
"SUCCESS": "Success", "SUCCESS": "SUCCESS",
"FAILTURE": "Failture", "FAILTURE": "FAILTURE",
"IN_PROGRESS": "In Progress", "IN_PROGRESS": "IN PROGRESS",
"STOP_EXECUTIONS": "Stop Execution", "STOP_EXECUTIONS": "Stop Execution",
"ID":"ID", "ID":"ID",
"REPLICATION_RULE": "Reglas de Replicación", "REPLICATION_RULE": "Reglas de Replicación",
"NEW_REPLICATION_RULE": "Nueva Regla de Replicación", "NEW_REPLICATION_RULE": "Nueva Regla de Replicación",
"ENDPOINTS": "Endpoints", "ENDPOINTS": "Endpoints",
"FILTER_POLICIES_PLACEHOLDER": "Filtrar Reglas", "FILTER_POLICIES_PLACEHOLDER": "Filtrar Reglas",
"FILTER_JOBS_PLACEHOLDER": "Filtrar Trabajos", "FILTER_EXECUTIONS_PLACEHOLDER": "Filter Ejecuciones",
"DELETION_TITLE": "Confirmar Eliminación de Regla", "DELETION_TITLE": "Confirmar Eliminación de Regla",
"DELETION_SUMMARY": "¿Quiere eliminar la regla {{param}}?", "DELETION_SUMMARY": "¿Quiere eliminar la regla {{param}}?",
"DELETION_TITLE_FAILURE": "failed to delete Rule Deletion", "DELETION_TITLE_FAILURE": "failed to delete Rule Deletion",

View File

@ -315,7 +315,10 @@
"OF": "de" "OF": "de"
}, },
"REPLICATION": { "REPLICATION": {
"CURRENT": "current",
"FILTER_PLACEHOLDER": "Filter Tasks",
"STOP_TITLE": "Confirmer arrêter les exécutions", "STOP_TITLE": "Confirmer arrêter les exécutions",
"STOP_SUCCESS": "Stop Execution {{param}} Successful",
"STOP_SUMMARY": "Voulez-vous arrêter les exécutions {{param}}?", "STOP_SUMMARY": "Voulez-vous arrêter les exécutions {{param}}?",
"TASK_ID":"Task ID", "TASK_ID":"Task ID",
"RECOURCE_TYPE": "Recource Type", "RECOURCE_TYPE": "Recource Type",
@ -324,16 +327,16 @@
"POLICY": "Policy", "POLICY": "Policy",
"DURATION": "Duration", "DURATION": "Duration",
"SUCCESS_RATE": "Success Rate", "SUCCESS_RATE": "Success Rate",
"SUCCESS": "Success", "SUCCESS": "SUCCESS",
"FAILTURE": "Failture", "FAILTURE": "FAILTURE",
"IN_PROGRESS": "In Progress", "IN_PROGRESS": "IN PROGRESS",
"STOP_EXECUTIONS": "Stop Execution", "STOP_EXECUTIONS": "Stop Execution",
"ID":"ID", "ID":"ID",
"REPLICATION_RULE": "Règle de Réplication", "REPLICATION_RULE": "Règle de Réplication",
"NEW_REPLICATION_RULE": "Nouvelle Règle de Réplication", "NEW_REPLICATION_RULE": "Nouvelle Règle de Réplication",
"ENDPOINTS": "Points finaux", "ENDPOINTS": "Points finaux",
"FILTER_POLICIES_PLACEHOLDER": "Filtrer les Règles", "FILTER_POLICIES_PLACEHOLDER": "Filtrer les Règles",
"FILTER_JOBS_PLACEHOLDER": "Filtrer les Travaux", "FILTER_EXECUTIONS_PLACEHOLDER": "Filter Exécutions",
"DELETION_TITLE": "Confirmer la suppression de la Règle", "DELETION_TITLE": "Confirmer la suppression de la Règle",
"DELETION_SUMMARY": "Voulez-vous supprimer la règle {{param}} ?", "DELETION_SUMMARY": "Voulez-vous supprimer la règle {{param}} ?",
"DELETION_TITLE_FAILURE": "n'a pas supprimé la règle", "DELETION_TITLE_FAILURE": "n'a pas supprimé la règle",

View File

@ -329,7 +329,10 @@
"OF": "de" "OF": "de"
}, },
"REPLICATION": { "REPLICATION": {
"CURRENT": "current",
"FILTER_PLACEHOLDER": "Filter Tasks",
"STOP_TITLE": "Confirme as execuções de parada", "STOP_TITLE": "Confirme as execuções de parada",
"STOP_SUCCESS": "Stop Execution {{param}} Successful",
"STOP_SUMMARY": "Você quer parar as execuções? {{param}}?", "STOP_SUMMARY": "Você quer parar as execuções? {{param}}?",
"TASK_ID":"Task ID", "TASK_ID":"Task ID",
"RECOURCE_TYPE": "Recource Type", "RECOURCE_TYPE": "Recource Type",
@ -338,16 +341,16 @@
"POLICY": "Policy", "POLICY": "Policy",
"DURATION": "Duration", "DURATION": "Duration",
"SUCCESS_RATE": "Success Rate", "SUCCESS_RATE": "Success Rate",
"SUCCESS": "Success", "SUCCESS": "SUCCESS",
"FAILTURE": "Failture", "FAILTURE": "FAILTURE",
"IN_PROGRESS": "In Progress", "IN_PROGRESS": "IN PROGRESS",
"STOP_EXECUTIONS": "Stop Execution", "STOP_EXECUTIONS": "Stop Execution",
"ID":"ID", "ID":"ID",
"REPLICATION_RULE": "Regra de replicação", "REPLICATION_RULE": "Regra de replicação",
"NEW_REPLICATION_RULE": "Nova regra de replicação", "NEW_REPLICATION_RULE": "Nova regra de replicação",
"ENDPOINTS": "Endpoints", "ENDPOINTS": "Endpoints",
"FILTER_POLICIES_PLACEHOLDER": "Filtrar regras", "FILTER_POLICIES_PLACEHOLDER": "Filtrar regras",
"FILTER_JOBS_PLACEHOLDER": "Filtrar tarefas", "FILTER_EXECUTIONS_PLACEHOLDER": "Execuções de Filtro",
"DELETION_TITLE": "Confirmar remoção de regras", "DELETION_TITLE": "Confirmar remoção de regras",
"DELETION_SUMMARY": "Você quer remover a regra {{param}}?", "DELETION_SUMMARY": "Você quer remover a regra {{param}}?",
"REPLICATION_TITLE": "Confirmar regras de replicação", "REPLICATION_TITLE": "Confirmar regras de replicação",

View File

@ -330,7 +330,10 @@
"OF": "共计" "OF": "共计"
}, },
"REPLICATION": { "REPLICATION": {
"CURRENT": "当前仓库",
"FILTER_PLACEHOLDER": "过滤任务",
"STOP_TITLE": "确认停止任务", "STOP_TITLE": "确认停止任务",
"STOP_SUCCESS": "停止任务 {{param}} 成功",
"STOP_SUMMARY": "确认停止任务{{param}}?", "STOP_SUMMARY": "确认停止任务{{param}}?",
"TASK_ID":"任务ID", "TASK_ID":"任务ID",
"RECOURCE_TYPE": "源类型", "RECOURCE_TYPE": "源类型",
@ -348,7 +351,7 @@
"NEW_REPLICATION_RULE": "新建规则", "NEW_REPLICATION_RULE": "新建规则",
"ENDPOINTS": "目标", "ENDPOINTS": "目标",
"FILTER_POLICIES_PLACEHOLDER": "过滤规则", "FILTER_POLICIES_PLACEHOLDER": "过滤规则",
"FILTER_JOBS_PLACEHOLDER": "过滤任务", "FILTER_EXECUTIONS_PLACEHOLDER": "过滤任务",
"DELETION_TITLE": "删除规则确认", "DELETION_TITLE": "删除规则确认",
"DELETION_SUMMARY": "确认删除规则 {{param}}?", "DELETION_SUMMARY": "确认删除规则 {{param}}?",
"DELETION_TITLE_FAILURE": "规则确认删除失败", "DELETION_TITLE_FAILURE": "规则确认删除失败",