mirror of
https://github.com/goharbor/harbor.git
synced 2024-11-27 04:35:16 +01:00
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:
commit
3978def928
@ -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">
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
);
|
||||
|
@ -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) {
|
||||
|
@ -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",
|
||||
|
@ -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:
|
||||
|
@ -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())
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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": "新建目标",
|
||||
|
Loading…
Reference in New Issue
Block a user