Merge pull request #7282 from zhoumeina/replication_ng

modify frontend replication UI to apply api changes. use dest registry as object
This commit is contained in:
Wenkai Yin 2019-04-03 16:08:07 +08:00 committed by GitHub
commit 3978def928
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 78 additions and 58 deletions

View File

@ -33,8 +33,8 @@
<label class="form-group-label-override required">{{'REPLICATION.SOURCE_REGISTRY' | translate}}</label> <label class="form-group-label-override required">{{'REPLICATION.SOURCE_REGISTRY' | translate}}</label>
<div class="form-select"> <div class="form-select">
<div class="select endpointSelect pull-left"> <div class="select endpointSelect pull-left">
<select id="src_registry_id" (change)="sourceChange($event)" formControlName="src_registry_id"> <select id="src_registry_id" (change)="sourceChange($event)" formControlName="src_registry">
<option *ngFor="let source of sourceList" [ngValue]="source.id">{{source.name}}-{{source.url}}</option> <option *ngFor="let source of sourceList" [ngValue]="source">{{source.name}}-{{source.url}}</option>
</select> </select>
</div> </div>
</div> </div>
@ -63,8 +63,8 @@
<div formArrayName="filters"> <div formArrayName="filters">
<div class="filterSelect" *ngFor="let filter of filters.controls; let i=index"> <div class="filterSelect" *ngFor="let filter of filters.controls; let i=index">
<div [formGroupName]="i"> <div [formGroupName]="i">
<div class="floatSetPar"> <div class="width-70">
<label>{{supportedFilters[i].type}}:</label> <label>{{"REPLICATION." + supportedFilters[i].type.toUpperCase() | translate}}:</label>
</div> </div>
<label *ngIf="supportedFilters[i]?.style==='input'" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left" <label *ngIf="supportedFilters[i]?.style==='input'" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-bottom-left"
[class.invalid]='(filter.value.dirty || filter.value.touched) && filter.value.invalid'> [class.invalid]='(filter.value.dirty || filter.value.touched) && filter.value.invalid'>
@ -88,8 +88,8 @@
<label class="form-group-label-override required">{{'REPLICATION.DEST_REGISTRY' | translate}}</label> <label class="form-group-label-override required">{{'REPLICATION.DEST_REGISTRY' | translate}}</label>
<div class="form-select"> <div class="form-select">
<div class="select endpointSelect pull-left"> <div class="select endpointSelect pull-left">
<select id="dest_registry_id" (change)="targetChange($event)" formControlName="dest_registry_id"> <select id="dest_registry_id" (change)="targetChange($event)" formControlName="dest_registry">
<option *ngFor="let target of targetList" [ngValue]="target.id">{{target.name}}-{{target.url}}</option> <option *ngFor="let target of targetList" [ngValue]="target">{{target.name}}-{{target.url}}</option>
</select> </select>
</div> </div>
</div> </div>
@ -112,13 +112,13 @@
<label class="form-group-label-override">{{'REPLICATION.TRIGGER_MODE' | translate}}</label> <label class="form-group-label-override">{{'REPLICATION.TRIGGER_MODE' | translate}}</label>
<div formGroupName="trigger"> <div formGroupName="trigger">
<!--on trigger--> <!--on trigger-->
<div class="select floatSetPar"> <div class="select width-115">
<select id="ruleTrigger" formControlName="kind" (change)="selectTrigger($event)"> <select id="ruleTrigger" formControlName="type" (change)="selectTrigger($event)">
<option *ngFor="let trigger of supportedTriggers" [value]="trigger">{{trigger }}</option> <option *ngFor="let trigger of supportedTriggers" [value]="trigger">{{'REPLICATION.' + trigger.toUpperCase() | translate }}</option>
</select> </select>
</div> </div>
<!--on push--> <!--on push-->
<div formGroupName="schedule_param"> <div formGroupName="trigger_settings">
<div [hidden]="isNotSchedule()"> <div [hidden]="isNotSchedule()">
<label>Cron String</label> <label>Cron String</label>
<label for="targetCron" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-right"> <label for="targetCron" aria-haspopup="true" role="tooltip" class="tooltip tooltip-validation tooltip-md tooltip-top-right">

View File

@ -68,12 +68,18 @@ h4 {
padding-left: 0; padding-left: 0;
} }
.floatSetPar { .width-70 {
display: inline-block; display: inline-block;
width: 70px; width: 70px;
margin-right: 10px; margin-right: 10px;
} }
.width-115 {
display: inline-block;
width: 115px;
margin-right: 10px;
}
.schedule-style { .schedule-style {
display: inline-block; display: inline-block;
} }

View File

@ -166,7 +166,7 @@ describe("CreateEditRuleComponent (inline template)", () => {
deletion: false deletion: false
}; };
let mockAdapter = { let mockRegistryInfo = {
"type": "harbor", "type": "harbor",
"description": "", "description": "",
"supported_resource_filters": [ "supported_resource_filters": [
@ -269,8 +269,8 @@ describe("CreateEditRuleComponent (inline template)", () => {
spyJobs = spyOn(replicationService, "getExecutions").and.returnValues( spyJobs = spyOn(replicationService, "getExecutions").and.returnValues(
of(mockJob)); of(mockJob));
spyAdapter = spyOn(replicationService, "getReplicationAdapter").and.returnValues( spyAdapter = spyOn(replicationService, "getRegistryInfo").and.returnValues(
of(mockAdapter)); of(mockRegistryInfo));
spyEndpoint = spyOn(endpointService, "getEndpoints").and.returnValues( spyEndpoint = spyOn(endpointService, "getEndpoints").and.returnValues(
of(mockEndpoints) of(mockEndpoints)
); );

View File

@ -52,9 +52,9 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
filterCount = 0; filterCount = 0;
alertClosed = false; alertClosed = false;
TRIGGER_TYPES = { TRIGGER_TYPES = {
MANUAL: "Manual", MANUAL: "manual",
SCHEDULED: "Scheduled", SCHEDULED: "scheduled",
EVENT_BASED: "EventBased" EVENT_BASED: "eventBased"
}; };
ruleNameTooltip = "REPLICATION.NAME_TOOLTIP"; ruleNameTooltip = "REPLICATION.NAME_TOOLTIP";
@ -91,11 +91,11 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
this.createForm(); this.createForm();
} }
initAdapter(type: string): void { initRegistryInfo(id: number): void {
this.repService.getReplicationAdapter(type).subscribe(adapter => { this.repService.getRegistryInfo(id).subscribe(adapter => {
this.supportedFilters = adapter.supported_resource_filters; this.supportedFilters = adapter.supported_resource_filters;
this.supportedTriggers = adapter.supported_triggers; this.supportedTriggers = adapter.supported_triggers;
this.ruleForm.get("trigger").get("kind").setValue(this.supportedTriggers[0]); this.ruleForm.get("trigger").get("type").setValue(this.supportedTriggers[0]);
}); });
} }
@ -134,20 +134,15 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
modeChange(): void { modeChange(): void {
if (this.isPushMode) { if (this.isPushMode) {
this.initAdapter("harbor"); this.initRegistryInfo(0);
} }
} }
sourceChange($event): void { sourceChange($event): void {
this.noSelectedEndpoint = false; this.noSelectedEndpoint = false;
let selectId = this.ruleForm.get('src_registry_id').value; let selectId = this.ruleForm.get('src_registry').value;
let selecedTarget: Endpoint = this.sourceList.find( this.initRegistryInfo(selectId.id);
source => source.id === selectId
);
this.initAdapter(selecedTarget.type);
} }
ngOnDestroy(): void { ngOnDestroy(): void {
@ -172,13 +167,13 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
this.ruleForm = this.fb.group({ this.ruleForm = this.fb.group({
name: ["", Validators.required], name: ["", Validators.required],
description: "", description: "",
src_registry_id: new FormControl(), src_registry: new FormControl(),
src_namespaces: new FormArray([new FormControl('')], Validators.required), src_namespaces: new FormArray([new FormControl('')], Validators.required),
dest_registry_id: new FormControl(), dest_registry: new FormControl(),
dest_namespace: "", dest_namespace: "",
trigger: this.fb.group({ trigger: this.fb.group({
kind: '', type: '',
schedule_param: this.fb.group({ trigger_settings: this.fb.group({
cron: "" cron: ""
}) })
}), }),
@ -192,11 +187,11 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
} }
isNotSchedule(): boolean { isNotSchedule(): boolean {
return this.ruleForm.get("trigger").get("kind").value !== this.TRIGGER_TYPES.SCHEDULED; return this.ruleForm.get("trigger").get("type").value !== this.TRIGGER_TYPES.SCHEDULED;
} }
isNotEventBased(): boolean { isNotEventBased(): boolean {
return this.ruleForm.get("trigger").get("kind").value !== this.TRIGGER_TYPES.EVENT_BASED; return this.ruleForm.get("trigger").get("type").value !== this.TRIGGER_TYPES.EVENT_BASED;
} }
initForm(): void { initForm(): void {
@ -204,15 +199,15 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
name: "", name: "",
description: "", description: "",
trigger: { trigger: {
kind: '', type: '',
schedule_param: { trigger_settings: {
cron: "" cron: ""
} }
}, },
deletion: false deletion: false
}); });
this.setFilter([]); this.setFilter([]);
this.initAdapter("harbor"); this.initRegistryInfo(0);
this.copyUpdateForm = clone(this.ruleForm.value); this.copyUpdateForm = clone(this.ruleForm.value);
} }
@ -324,9 +319,9 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
this.inProgress = true; this.inProgress = true;
let copyRuleForm: ReplicationRule = this.ruleForm.value; let copyRuleForm: ReplicationRule = this.ruleForm.value;
if (this.isPushMode) { if (this.isPushMode) {
copyRuleForm.src_registry_id = null; copyRuleForm.src_registry = null;
} else { } else {
copyRuleForm.dest_registry_id = null; copyRuleForm.dest_registry = null;
} }
if (this.policyId < 0) { if (this.policyId < 0) {

View File

@ -79,6 +79,7 @@ import { OperationService } from './operation/operation.service';
* this default configuration. * this default configuration.
*/ */
export const DefaultServiceConfig: IServiceConfig = { export const DefaultServiceConfig: IServiceConfig = {
baseEndpoint: "/api",
systemInfoEndpoint: "/api/systeminfo", systemInfoEndpoint: "/api/systeminfo",
repositoryBaseEndpoint: "/api/repositories", repositoryBaseEndpoint: "/api/repositories",
logBaseEndpoint: "/api/logs", logBaseEndpoint: "/api/logs",

View File

@ -2,6 +2,7 @@ import { InjectionToken } from '@angular/core';
export let SERVICE_CONFIG = new InjectionToken("service.config"); export let SERVICE_CONFIG = new InjectionToken("service.config");
export interface IServiceConfig { export interface IServiceConfig {
baseEndpoint?: string;
/** /**
* The base endpoint of service used to retrieve the system configuration information. * The base endpoint of service used to retrieve the system configuration information.
* The configurations may include but not limit: * The configurations may include but not limit:

View File

@ -141,7 +141,7 @@ export abstract class ReplicationService {
): Observable<any>; ): Observable<any>;
abstract getReplicationAdapter(type: string): Observable<any>; abstract getRegistryInfo(id: number): Observable<any>;
/** /**
* Get the jobs for the specified replication rule. * Get the jobs for the specified replication rule.
@ -207,6 +207,7 @@ export abstract class ReplicationService {
export class ReplicationDefaultService extends ReplicationService { export class ReplicationDefaultService extends ReplicationService {
_ruleBaseUrl: string; _ruleBaseUrl: string;
_replicateUrl: string; _replicateUrl: string;
_baseUrl: string;
constructor( constructor(
private http: Http, private http: Http,
@ -219,6 +220,7 @@ export class ReplicationDefaultService extends ReplicationService {
this._replicateUrl = config.replicationBaseEndpoint this._replicateUrl = config.replicationBaseEndpoint
? config.replicationBaseEndpoint ? config.replicationBaseEndpoint
: "/api/replication"; : "/api/replication";
this._baseUrl = config.baseEndpoint ? config.baseEndpoint : "/api";
} }
// Private methods // Private methods
@ -230,12 +232,12 @@ export class ReplicationDefaultService extends ReplicationService {
rule.name !== undefined && rule.name !== undefined &&
rule.name.trim() !== "" && rule.name.trim() !== "" &&
rule.src_namespaces && rule.src_namespaces.length > 0 && rule.src_namespaces && rule.src_namespaces.length > 0 &&
(!!rule.dest_registry_id || !!rule.src_registry_id) (!!rule.dest_registry || !!rule.src_registry)
); );
} }
public getReplicationAdapter(type): Observable<any> { public getRegistryInfo(id): Observable<any> {
let requestUrl: string = `${this._replicateUrl}/adapters/${type}`; let requestUrl: string = `${this._baseUrl}/registries/${id}/info`;
return this.http return this.http
.get(requestUrl) .get(requestUrl)
.pipe(map(response => response.json()) .pipe(map(response => response.json())

View File

@ -432,9 +432,9 @@
"NO_ENDPOINT_INFO": "Please add an endpoint first", "NO_ENDPOINT_INFO": "Please add an endpoint first",
"NO_PROJECT_INFO": "This project is not exist", "NO_PROJECT_INFO": "This project is not exist",
"SOURCE_IMAGES_FILTER": "Source images filter", "SOURCE_IMAGES_FILTER": "Source images filter",
"SCHEDULE": "Scheduled", "SCHEDULED": "Scheduled",
"MANUAL": "Manual", "MANUAL": "Manual",
"IMMEDIATE": "Immediate", "EVENT_BASED":"Event Based",
"DAILY": "Daily", "DAILY": "Daily",
"WEEKLY": "Weekly", "WEEKLY": "Weekly",
"SETTING": "Options", "SETTING": "Options",
@ -455,7 +455,10 @@
"SOURCE_NAMESPACES":"Source namespaces", "SOURCE_NAMESPACES":"Source namespaces",
"DEST_REGISTRY":"Destination registry", "DEST_REGISTRY":"Destination registry",
"DEST_NAMESPACE":"Destination namespace", "DEST_NAMESPACE":"Destination namespace",
"NAMESPACE_TOOLTIP": "Namespace name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers." "NAMESPACE_TOOLTIP": "Namespace name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
"TAG":"Tag",
"LABEL":"Label",
"RESOURCE":"Resource"
}, },
"DESTINATION": { "DESTINATION": {
"NEW_ENDPOINT": "New Endpoint", "NEW_ENDPOINT": "New Endpoint",

View File

@ -433,9 +433,9 @@
"NO_ENDPOINT_INFO": "Please add an endpoint first", "NO_ENDPOINT_INFO": "Please add an endpoint first",
"NO_PROJECT_INFO": "This project is not exist", "NO_PROJECT_INFO": "This project is not exist",
"SOURCE_IMAGES_FILTER": "Source images filter", "SOURCE_IMAGES_FILTER": "Source images filter",
"SCHEDULE": "Scheduled", "SCHEDULED": "Scheduled",
"MANUAL": "Manual", "MANUAL": "Manual",
"IMMEDIATE": "Immediate", "EVENT_BASED":"Event Based",
"DAILY": "Daily", "DAILY": "Daily",
"WEEKLY": "Weekly", "WEEKLY": "Weekly",
"SETTING": "Options", "SETTING": "Options",
@ -456,7 +456,10 @@
"SOURCE_NAMESPACES":"Source namespaces", "SOURCE_NAMESPACES":"Source namespaces",
"DEST_REGISTRY":"Destination registry", "DEST_REGISTRY":"Destination registry",
"DEST_NAMESPACE":"Destination namespace", "DEST_NAMESPACE":"Destination namespace",
"NAMESPACE_TOOLTIP": "Namespace name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers." "NAMESPACE_TOOLTIP": "Namespace name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
"TAG":"Tag",
"LABEL":"Label",
"RESOURCE":"Resource"
}, },
"DESTINATION": { "DESTINATION": {
"NEW_ENDPOINT": "Nuevo Endpoint", "NEW_ENDPOINT": "Nuevo Endpoint",

View File

@ -414,9 +414,9 @@
"NO_ENDPOINT_INFO": "Please add an endpoint first", "NO_ENDPOINT_INFO": "Please add an endpoint first",
"NO_PROJECT_INFO": "This project is not exist", "NO_PROJECT_INFO": "This project is not exist",
"SOURCE_IMAGES_FILTER": "Source images filter", "SOURCE_IMAGES_FILTER": "Source images filter",
"SCHEDULE": "Scheduled", "SCHEDULED": "Scheduled",
"MANUAL": "Manual", "MANUAL": "Manual",
"IMMEDIATE": "Immediate", "EVENT_BASED":"Event Based",
"DAILY": "Daily", "DAILY": "Daily",
"WEEKLY": "Weekly", "WEEKLY": "Weekly",
"SETTING": "Options", "SETTING": "Options",
@ -437,7 +437,10 @@
"SOURCE_NAMESPACES":"Source namespaces", "SOURCE_NAMESPACES":"Source namespaces",
"DEST_REGISTRY":"Destination registry", "DEST_REGISTRY":"Destination registry",
"DEST_NAMESPACE":"Destination namespace", "DEST_NAMESPACE":"Destination namespace",
"NAMESPACE_TOOLTIP": "Namespace name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers." "NAMESPACE_TOOLTIP": "Namespace name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
"TAG":"Tag",
"LABEL":"Label",
"RESOURCE":"Resource"
}, },
"DESTINATION": { "DESTINATION": {
"NEW_ENDPOINT": "Nouveau Point Final", "NEW_ENDPOINT": "Nouveau Point Final",

View File

@ -432,9 +432,9 @@
"NO_ENDPOINT_INFO": "Por favor adicione antes um endpoint", "NO_ENDPOINT_INFO": "Por favor adicione antes um endpoint",
"NO_PROJECT_INFO": "Esse projeto não existe", "NO_PROJECT_INFO": "Esse projeto não existe",
"SOURCE_IMAGES_FILTER": "Filtro de imagens de origem", "SOURCE_IMAGES_FILTER": "Filtro de imagens de origem",
"SCHEDULE": "Agendado", "SCHEDULED": "Agendado",
"MANUAL": "Manual", "MANUAL": "Manual",
"IMMEDIATE": "Imediato", "EVENT_BASED":"Event Based",
"DAILY": "Diário", "DAILY": "Diário",
"WEEKLY": "Semanal", "WEEKLY": "Semanal",
"SETTING":"Opções", "SETTING":"Opções",
@ -455,7 +455,10 @@
"SOURCE_NAMESPACES":"Source namespaces", "SOURCE_NAMESPACES":"Source namespaces",
"DEST_REGISTRY":"Destination registry", "DEST_REGISTRY":"Destination registry",
"DEST_NAMESPACE":"Destination namespace", "DEST_NAMESPACE":"Destination namespace",
"NAMESPACE_TOOLTIP": "Namespace name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers." "NAMESPACE_TOOLTIP": "Namespace name should be at least 2 characters long with lower case characters, numbers and ._- and must be start with characters or numbers.",
"TAG":"Tag",
"LABEL":"Label",
"RESOURCE":"Resource"
}, },
"DESTINATION": { "DESTINATION": {
"NEW_ENDPOINT": "Novo Endpoint", "NEW_ENDPOINT": "Novo Endpoint",

View File

@ -433,9 +433,9 @@
"NO_ENDPOINT_INFO": "请先添加一个目标", "NO_ENDPOINT_INFO": "请先添加一个目标",
"NO_PROJECT_INFO": "此项目不存在", "NO_PROJECT_INFO": "此项目不存在",
"SOURCE_IMAGES_FILTER": "源镜像过滤器", "SOURCE_IMAGES_FILTER": "源镜像过滤器",
"SCHEDULE": "定时", "SCHEDULED": "定时",
"MANUAL": "手动", "MANUAL": "手动",
"IMMEDIATE": "即刻", "EVENT_BASED":"事件驱动",
"DAILY": "每天", "DAILY": "每天",
"WEEKLY": "每周", "WEEKLY": "每周",
"SETTING": "设置", "SETTING": "设置",
@ -456,7 +456,10 @@
"SOURCE_NAMESPACES":"源Namespace", "SOURCE_NAMESPACES":"源Namespace",
"DEST_REGISTRY":"目的Registry", "DEST_REGISTRY":"目的Registry",
"DEST_NAMESPACE":"目的Namespace", "DEST_NAMESPACE":"目的Namespace",
"NAMESPACE_TOOLTIP": "Namespace名称由小写字符、数字和._-组成且至少2个字符并以字符或者数字开头。" "NAMESPACE_TOOLTIP": "Namespace名称由小写字符、数字和._-组成且至少2个字符并以字符或者数字开头。",
"TAG":"Tag",
"LABEL":"标签",
"RESOURCE":"资源"
}, },
"DESTINATION": { "DESTINATION": {
"NEW_ENDPOINT": "新建目标", "NEW_ENDPOINT": "新建目标",