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>
<div class="form-select">
<div class="select endpointSelect pull-left">
<select id="src_registry_id" (change)="sourceChange($event)" formControlName="src_registry_id">
<option *ngFor="let source of sourceList" [ngValue]="source.id">{{source.name}}-{{source.url}}</option>
<select id="src_registry_id" (change)="sourceChange($event)" formControlName="src_registry">
<option *ngFor="let source of sourceList" [ngValue]="source">{{source.name}}-{{source.url}}</option>
</select>
</div>
</div>
@ -63,8 +63,8 @@
<div formArrayName="filters">
<div class="filterSelect" *ngFor="let filter of filters.controls; let i=index">
<div [formGroupName]="i">
<div class="floatSetPar">
<label>{{supportedFilters[i].type}}:</label>
<div class="width-70">
<label>{{"REPLICATION." + supportedFilters[i].type.toUpperCase() | translate}}:</label>
</div>
<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'>
@ -88,8 +88,8 @@
<label class="form-group-label-override required">{{'REPLICATION.DEST_REGISTRY' | translate}}</label>
<div class="form-select">
<div class="select endpointSelect pull-left">
<select id="dest_registry_id" (change)="targetChange($event)" formControlName="dest_registry_id">
<option *ngFor="let target of targetList" [ngValue]="target.id">{{target.name}}-{{target.url}}</option>
<select id="dest_registry_id" (change)="targetChange($event)" formControlName="dest_registry">
<option *ngFor="let target of targetList" [ngValue]="target">{{target.name}}-{{target.url}}</option>
</select>
</div>
</div>
@ -112,13 +112,13 @@
<label class="form-group-label-override">{{'REPLICATION.TRIGGER_MODE' | translate}}</label>
<div formGroupName="trigger">
<!--on trigger-->
<div class="select floatSetPar">
<select id="ruleTrigger" formControlName="kind" (change)="selectTrigger($event)">
<option *ngFor="let trigger of supportedTriggers" [value]="trigger">{{trigger }}</option>
<div class="select width-115">
<select id="ruleTrigger" formControlName="type" (change)="selectTrigger($event)">
<option *ngFor="let trigger of supportedTriggers" [value]="trigger">{{'REPLICATION.' + trigger.toUpperCase() | translate }}</option>
</select>
</div>
<!--on push-->
<div formGroupName="schedule_param">
<div formGroupName="trigger_settings">
<div [hidden]="isNotSchedule()">
<label>Cron String</label>
<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;
}
.floatSetPar {
.width-70 {
display: inline-block;
width: 70px;
margin-right: 10px;
}
.width-115 {
display: inline-block;
width: 115px;
margin-right: 10px;
}
.schedule-style {
display: inline-block;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -432,9 +432,9 @@
"NO_ENDPOINT_INFO": "Please add an endpoint first",
"NO_PROJECT_INFO": "This project is not exist",
"SOURCE_IMAGES_FILTER": "Source images filter",
"SCHEDULE": "Scheduled",
"SCHEDULED": "Scheduled",
"MANUAL": "Manual",
"IMMEDIATE": "Immediate",
"EVENT_BASED":"Event Based",
"DAILY": "Daily",
"WEEKLY": "Weekly",
"SETTING": "Options",
@ -455,7 +455,10 @@
"SOURCE_NAMESPACES":"Source namespaces",
"DEST_REGISTRY":"Destination registry",
"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": {
"NEW_ENDPOINT": "New Endpoint",

View File

@ -433,9 +433,9 @@
"NO_ENDPOINT_INFO": "Please add an endpoint first",
"NO_PROJECT_INFO": "This project is not exist",
"SOURCE_IMAGES_FILTER": "Source images filter",
"SCHEDULE": "Scheduled",
"SCHEDULED": "Scheduled",
"MANUAL": "Manual",
"IMMEDIATE": "Immediate",
"EVENT_BASED":"Event Based",
"DAILY": "Daily",
"WEEKLY": "Weekly",
"SETTING": "Options",
@ -456,7 +456,10 @@
"SOURCE_NAMESPACES":"Source namespaces",
"DEST_REGISTRY":"Destination registry",
"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": {
"NEW_ENDPOINT": "Nuevo Endpoint",

View File

@ -414,9 +414,9 @@
"NO_ENDPOINT_INFO": "Please add an endpoint first",
"NO_PROJECT_INFO": "This project is not exist",
"SOURCE_IMAGES_FILTER": "Source images filter",
"SCHEDULE": "Scheduled",
"SCHEDULED": "Scheduled",
"MANUAL": "Manual",
"IMMEDIATE": "Immediate",
"EVENT_BASED":"Event Based",
"DAILY": "Daily",
"WEEKLY": "Weekly",
"SETTING": "Options",
@ -437,7 +437,10 @@
"SOURCE_NAMESPACES":"Source namespaces",
"DEST_REGISTRY":"Destination registry",
"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": {
"NEW_ENDPOINT": "Nouveau Point Final",

View File

@ -432,9 +432,9 @@
"NO_ENDPOINT_INFO": "Por favor adicione antes um endpoint",
"NO_PROJECT_INFO": "Esse projeto não existe",
"SOURCE_IMAGES_FILTER": "Filtro de imagens de origem",
"SCHEDULE": "Agendado",
"SCHEDULED": "Agendado",
"MANUAL": "Manual",
"IMMEDIATE": "Imediato",
"EVENT_BASED":"Event Based",
"DAILY": "Diário",
"WEEKLY": "Semanal",
"SETTING":"Opções",
@ -455,7 +455,10 @@
"SOURCE_NAMESPACES":"Source namespaces",
"DEST_REGISTRY":"Destination registry",
"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": {
"NEW_ENDPOINT": "Novo Endpoint",

View File

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