new endpoint

Signed-off-by: FangyuanCheng <fangyuanc@vmware.com>
This commit is contained in:
FangyuanCheng 2019-02-25 16:00:31 +08:00
parent bb76a4d97d
commit 03c9bf8ceb
21 changed files with 293 additions and 112 deletions

View File

@ -11,6 +11,18 @@
</div> </div>
<form #targetForm="ngForm"> <form #targetForm="ngForm">
<section class="form-block"> <section class="form-block">
<!-- provider -->
<div class="form-group">
<label class="form-group-label-override required">{{'DESTINATION.PROVIDER' | translate}}</label>
<div class="form-select">
<div class="select providerSelect pull-left">
<select name="adapter" id="adapter" [(ngModel)]="target.type" [disabled]="testOngoing || controlEnabled">
<option *ngFor="let adapter of adapterList" [ngValue]="adapter" value="{{adapter.type}}">{{adapter.type}}</option>
</select>
</div>
</div>
</div>
<!-- Endpoint name -->
<div class="form-group"> <div class="form-group">
<label for="destination_name" class="col-md-4 form-group-label-override required">{{ 'DESTINATION.NAME' | <label for="destination_name" class="col-md-4 form-group-label-override required">{{ 'DESTINATION.NAME' |
translate }}</label> translate }}</label>
@ -23,30 +35,39 @@
</span> </span>
</label> </label>
</div> </div>
<!--Description-->
<div class="form-group">
<label class="form-group-label-override">{{'REPLICATION.DESCRIPTION' | translate}}</label>
<textarea type="text" class="inputWidth" row=3 name="description" [(ngModel)]="target.description"></textarea>
</div>
<!-- Endpoint Url -->
<div class="form-group"> <div class="form-group">
<label for="destination_url" class="col-md-4 form-group-label-override required">{{ 'DESTINATION.URL' | <label for="destination_url" class="col-md-4 form-group-label-override required">{{ 'DESTINATION.URL' |
translate }}</label> translate }}</label>
<label class="col-md-8" for="destination_url" aria-haspopup="true" role="tooltip" [class.invalid]="targetEndpoint.errors && (targetEndpoint.dirty || targetEndpoint.touched)" <label class="col-md-8" for="destination_url" aria-haspopup="true" role="tooltip" [class.invalid]="targetEndpoint.errors && (targetEndpoint.dirty || targetEndpoint.touched)"
[class.valid]="targetEndpoint.valid" class="tooltip tooltip-validation tooltip-sm tooltip-bottom-left"> [class.valid]="targetEndpoint.valid" class="tooltip tooltip-validation tooltip-sm tooltip-bottom-left">
<input type="text" id="destination_url" [disabled]="testOngoing" [readonly]="!editable" [(ngModel)]="target.endpoint" <input type="text" id="destination_url" [disabled]="testOngoing || controlEnabled" [readonly]="!editable" [(ngModel)]="target.url"
size="20" name="endpointUrl" #targetEndpoint="ngModel" required placeholder="http(s)://192.168.1.1"> size="20" name="endpointUrl" #targetEndpoint="ngModel" required placeholder="http(s)://192.168.1.1">
<span class="tooltip-content" *ngIf="targetEndpoint.errors && targetEndpoint.errors.required && (targetEndpoint.dirty || targetEndpoint.touched)"> <span class="tooltip-content" *ngIf="targetEndpoint.errors && targetEndpoint.errors.required && (targetEndpoint.dirty || targetEndpoint.touched)">
{{ 'DESTINATION.URL_IS_REQUIRED' | translate }} {{ 'DESTINATION.URL_IS_REQUIRED' | translate }}
</span> </span>
</label> </label>
</div> </div>
<!-- access_key -->
<div class="form-group"> <div class="form-group">
<label for="destination_username" class="col-md-4 form-group-label-override">{{ 'DESTINATION.USERNAME' | <label for="destination_access_key" class="col-md-4 form-group-label-override">{{ 'DESTINATION.ACCESS_ID' |
translate }}</label> translate }}</label>
<input type="text" class="col-md-8" id="destination_username" [disabled]="testOngoing" [readonly]="!editable" <input type="text" placeholder="Access ID" class="col-md-8" id="destination_access_key" [disabled]="testOngoing" [readonly]="!editable"
[(ngModel)]="target.username" size="20" name="username" #username="ngModel"> [(ngModel)]="target.credential.access_key" size="23" name="access_key" #access_key="ngModel">
</div> </div>
<!-- access_secret -->
<div class="form-group"> <div class="form-group">
<label for="destination_password" class="col-md-4 form-group-label-override">{{ 'DESTINATION.PASSWORD' | <label for="destination_password" class="col-md-4 form-group-label-override">{{ 'DESTINATION.ACCESS_SECRET' |
translate }}</label> translate }}</label>
<input type="password" class="col-md-8" id="destination_password" [disabled]="testOngoing" [readonly]="!editable" <input type="password" placeholder="Access Secret" class="col-md-8" id="destination_password" [disabled]="testOngoing" [readonly]="!editable"
[(ngModel)]="target.password" size="20" name="password" #password="ngModel"> [(ngModel)]="target.credential.access_secret" size="23" name="access_secret" #access_secret="ngModel">
</div> </div>
<!-- Verify Remote Cert -->
<div class="form-group"> <div class="form-group">
<label for="destination_insecure" id="destination_insecure_checkbox">{{'CONFIG.VERIFY_REMOTE_CERT' | <label for="destination_insecure" id="destination_insecure_checkbox">{{'CONFIG.VERIFY_REMOTE_CERT' |
translate }}</label> translate }}</label>

View File

@ -11,3 +11,11 @@
.form-height { .form-height {
height: 30px; height: 30px;
} }
.providerSelect {
width: 180px;
}
.inputWidth {
width: 182px;
}

View File

@ -12,7 +12,7 @@ import { FilterComponent } from "../filter/filter.component";
import { CreateEditEndpointComponent } from "../create-edit-endpoint/create-edit-endpoint.component"; import { CreateEditEndpointComponent } from "../create-edit-endpoint/create-edit-endpoint.component";
import { InlineAlertComponent } from "../inline-alert/inline-alert.component"; import { InlineAlertComponent } from "../inline-alert/inline-alert.component";
import { ErrorHandler } from "../error-handler/error-handler"; import { ErrorHandler } from "../error-handler/error-handler";
import { Endpoint } from "../service/interface"; import { Endpoint, Adapter } from "../service/interface";
import { import {
EndpointService, EndpointService,
EndpointDefaultService EndpointDefaultService
@ -21,14 +21,27 @@ import { IServiceConfig, SERVICE_CONFIG } from "../service.config";
describe("CreateEditEndpointComponent (inline template)", () => { describe("CreateEditEndpointComponent (inline template)", () => {
let mockData: Endpoint = { let mockData: Endpoint = {
id: 1, id: 1,
url: "https://10.117.4.151", credential: {
name: "target_01", access_key: "admin",
username: "admin", access_secret: "",
password: "", type: "basic"
},
description: "test",
insecure: false, insecure: false,
type: "harbor" name: "target_01",
type: "Harbor",
url: "https://10.117.4.151"
}; };
let mockAdapter: [Adapter] = [{
type: "Harbor",
description: "test",
supported_resource_types: [
"repository"
],
supported_resource_filters: null
}];
let comp: CreateEditEndpointComponent; let comp: CreateEditEndpointComponent;
let fixture: ComponentFixture<CreateEditEndpointComponent>; let fixture: ComponentFixture<CreateEditEndpointComponent>;
@ -39,6 +52,7 @@ describe("CreateEditEndpointComponent (inline template)", () => {
let endpointService: EndpointService; let endpointService: EndpointService;
let spy: jasmine.Spy; let spy: jasmine.Spy;
let spyAdapter: jasmine.Spy;
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
@ -61,6 +75,10 @@ describe("CreateEditEndpointComponent (inline template)", () => {
comp = fixture.componentInstance; comp = fixture.componentInstance;
endpointService = fixture.debugElement.injector.get(EndpointService); endpointService = fixture.debugElement.injector.get(EndpointService);
spyAdapter = spyOn(endpointService, "getAdapters").and.returnValue(
Promise.resolve(mockAdapter)
);
spy = spyOn(endpointService, "getEndpoint").and.returnValue( spy = spyOn(endpointService, "getEndpoint").and.returnValue(
Promise.resolve(mockData) Promise.resolve(mockData)
); );

View File

@ -18,7 +18,8 @@ import {
ViewChild, ViewChild,
AfterViewChecked, AfterViewChecked,
ChangeDetectorRef, ChangeDetectorRef,
OnDestroy OnDestroy,
OnInit
} from "@angular/core"; } from "@angular/core";
import { NgForm } from "@angular/forms"; import { NgForm } from "@angular/forms";
import { Subscription } from "rxjs"; import { Subscription } from "rxjs";
@ -27,7 +28,7 @@ import { TranslateService } from "@ngx-translate/core";
import { EndpointService } from "../service/endpoint.service"; import { EndpointService } from "../service/endpoint.service";
import { ErrorHandler } from "../error-handler/index"; import { ErrorHandler } from "../error-handler/index";
import { InlineAlertComponent } from "../inline-alert/inline-alert.component"; import { InlineAlertComponent } from "../inline-alert/inline-alert.component";
import { Endpoint } from "../service/interface"; import { Endpoint, Adapter } from "../service/interface";
import { toPromise, clone, compareValue, isEmptyObject } from "../utils"; import { toPromise, clone, compareValue, isEmptyObject } from "../utils";
@ -39,16 +40,17 @@ const FAKE_PASSWORD = "rjGcfuRu";
styleUrls: ["./create-edit-endpoint.component.scss"] styleUrls: ["./create-edit-endpoint.component.scss"]
}) })
export class CreateEditEndpointComponent export class CreateEditEndpointComponent
implements AfterViewChecked, OnDestroy { implements AfterViewChecked, OnDestroy, OnInit {
modalTitle: string; modalTitle: string;
controlEnabled: boolean = false;
createEditDestinationOpened: boolean; createEditDestinationOpened: boolean;
staticBackdrop: boolean = true; staticBackdrop: boolean = true;
closable: boolean = false; closable: boolean = false;
editable: boolean; editable: boolean;
adapterList: Adapter[] = [];
target: Endpoint = this.initEndpoint(); target: Endpoint = this.initEndpoint();
selectedType: string;
initVal: Endpoint; initVal: Endpoint;
targetForm: NgForm; targetForm: NgForm;
@ViewChild("targetForm") currentForm: NgForm; @ViewChild("targetForm") currentForm: NgForm;
@ -71,6 +73,14 @@ export class CreateEditEndpointComponent
private ref: ChangeDetectorRef private ref: ChangeDetectorRef
) {} ) {}
ngOnInit(): void {
toPromise<Adapter[]>(this.endpointService.getAdapters())
.then(adapters => {
this.adapterList = adapters || [];
})
.catch((error: any) => this.errorHandler.error(error));
}
public get isValid(): boolean { public get isValid(): boolean {
return ( return (
!this.testOngoing && !this.testOngoing &&
@ -98,12 +108,16 @@ export class CreateEditEndpointComponent
initEndpoint(): Endpoint { initEndpoint(): Endpoint {
return { return {
url: "", credential: {
name: "", access_key: "",
username: "", access_secret: "",
password: "", type: "basic"
},
description: "",
insecure: false, insecure: false,
type: "" name: "",
type: "Harbor",
url: "",
}; };
} }
@ -125,7 +139,6 @@ export class CreateEditEndpointComponent
this.initVal = this.initEndpoint(); this.initVal = this.initEndpoint();
this.formValues = null; this.formValues = null;
this.endpointId = ""; this.endpointId = "";
this.inlineAlert.close(); this.inlineAlert.close();
} }
@ -158,11 +171,12 @@ export class CreateEditEndpointComponent
this.target = target; this.target = target;
// Keep data cache // Keep data cache
this.initVal = clone(target); this.initVal = clone(target);
this.initVal.password = FAKE_PASSWORD; this.initVal.credential.access_secret = FAKE_PASSWORD;
this.target.password = FAKE_PASSWORD; this.target.credential.access_secret = FAKE_PASSWORD;
// Open the modal now // Open the modal now
this.open(); this.open();
this.controlEnabled = true;
this.forceRefreshView(2000); this.forceRefreshView(2000);
}) })
.catch(error => this.errorHandler.error(error)); .catch(error => this.errorHandler.error(error));
@ -173,15 +187,16 @@ export class CreateEditEndpointComponent
.subscribe(res => (this.modalTitle = res)); .subscribe(res => (this.modalTitle = res));
// Directly open the modal // Directly open the modal
this.open(); this.open();
this.controlEnabled = false;
} }
} }
testConnection() { testConnection() {
let payload: Endpoint = this.initEndpoint(); let payload: Endpoint = this.initEndpoint();
if (!this.endpointId) { if (!this.endpointId) {
payload.endpoint = this.target.endpoint; payload.url = this.target.url;
payload.username = this.target.username; payload.credential.access_key = this.target.credential.access_key;
payload.password = this.target.password; payload.credential.access_secret = this.target.credential.access_secret;
payload.insecure = this.target.insecure; payload.insecure = this.target.insecure;
} else { } else {
let changes: { [key: string]: any } = this.getChanges(); let changes: { [key: string]: any } = this.getChanges();
@ -225,7 +240,6 @@ export class CreateEditEndpointComponent
if (this.onGoing) { if (this.onGoing) {
return; // Avoid duplicated submitting return; // Avoid duplicated submitting
} }
this.onGoing = true; this.onGoing = true;
toPromise<number>(this.endpointService.createEndpoint(this.target)) toPromise<number>(this.endpointService.createEndpoint(this.target))
.then(response => { .then(response => {

View File

@ -176,7 +176,7 @@ describe("CreateEditRuleComponent (inline template)", () => {
let config: IServiceConfig = { let config: IServiceConfig = {
replicationBaseEndpoint: "/api/replication/executions/testing", replicationBaseEndpoint: "/api/replication/executions/testing",
targetBaseEndpoint: "/api/targets/testing" targetBaseEndpoint: "/api/registries/testing"
}; };
beforeEach(async(() => { beforeEach(async(() => {

View File

@ -105,12 +105,16 @@ export class CreateEditRuleComponent implements OnInit, OnDestroy {
}; };
emptyEndpoint = { emptyEndpoint = {
id: -1, id: -1,
endpoint: "", credential: {
access_key: "",
access_secret: "",
type: ""
},
description: "",
insecure: false,
name: "", name: "",
username: "", type: "",
password: "", url: "",
insecure: true,
type: 0
}; };
constructor( constructor(
private fb: FormBuilder, private fb: FormBuilder,

View File

@ -17,17 +17,27 @@
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!(selectedRow.length ===1)" (click)="editTargets(selectedRow)" ><clr-icon shape="pencil" size="16"></clr-icon>&nbsp;{{'DESTINATION.EDIT' | translate}}</button> <button type="button" class="btn btn-sm btn-secondary" [disabled]="!(selectedRow.length ===1)" (click)="editTargets(selectedRow)" ><clr-icon shape="pencil" size="16"></clr-icon>&nbsp;{{'DESTINATION.EDIT' | translate}}</button>
<button type="button" class="btn btn-sm btn-secondary" [disabled]="!selectedRow.length" (click)="deleteTargets(selectedRow)"><clr-icon shape="times" size="16"></clr-icon>&nbsp;{{'DESTINATION.DELETE' | translate}}</button> <button type="button" class="btn btn-sm btn-secondary" [disabled]="!selectedRow.length" (click)="deleteTargets(selectedRow)"><clr-icon shape="times" size="16"></clr-icon>&nbsp;{{'DESTINATION.DELETE' | translate}}</button>
</clr-dg-action-bar> </clr-dg-action-bar>
<clr-dg-column [clrDgField]="'name'">{{'DESTINATION.NAME' | translate}}</clr-dg-column> <clr-dg-column [clrDgField]="'name'" class="flex-min-width">{{'DESTINATION.NAME' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'endpoint'">{{'DESTINATION.URL' | translate}}</clr-dg-column> <clr-dg-column [clrDgField]="'status'">{{'DESTINATION.STATUS' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'url'" class="flex-min-width">{{'DESTINATION.URL' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'type'" class="flex-min-width">{{'DESTINATION.PROVIDER' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'insecure'">{{'CONFIG.VERIFY_REMOTE_CERT' | translate }}</clr-dg-column> <clr-dg-column [clrDgField]="'insecure'">{{'CONFIG.VERIFY_REMOTE_CERT' | translate }}</clr-dg-column>
<clr-dg-column [clrDgField]="'credential.type'">{{'DESTINATION.AUTHENTICATION' | translate }}</clr-dg-column>
<clr-dg-column [clrDgSortBy]="creationTimeComparator">{{'DESTINATION.CREATION_TIME' | translate}}</clr-dg-column> <clr-dg-column [clrDgSortBy]="creationTimeComparator">{{'DESTINATION.CREATION_TIME' | translate}}</clr-dg-column>
<clr-dg-placeholder>{{'DESTINATION.PLACEHOLDER' | translate }}</clr-dg-placeholder> <clr-dg-placeholder>{{'DESTINATION.PLACEHOLDER' | translate }}</clr-dg-placeholder>
<clr-dg-row *clrDgItems="let t of targets" [clrDgItem]='t'> <clr-dg-row *clrDgItems="let t of targets" [clrDgItem]='t'>
<clr-dg-cell>{{t.name}}</clr-dg-cell> <clr-dg-cell class="flex-min-width">{{t.name}}</clr-dg-cell>
<clr-dg-cell>{{t.endpoint}}</clr-dg-cell> <clr-dg-cell [ngSwitch]="t.status">
<div *ngSwitchCase="'unhealthy'"><clr-icon shape="exclamation-circle" class="is-error text-alignment" size="22"></clr-icon> Unhealthy</div>
<div *ngSwitchCase="'healthy'"><clr-icon shape="success-standard" class="is-success text-alignment" size="18"></clr-icon> Healthy</div>
<div *ngSwitchCase="'unknown' || ''"><clr-icon shape="exclamation-triangle" class="is-warning text-alignment" size="22"></clr-icon> Unknown</div>
</clr-dg-cell>
<clr-dg-cell class="flex-min-width">{{t.url}}</clr-dg-cell>
<clr-dg-cell class="flex-min-width">{{t.type}}</clr-dg-cell>
<clr-dg-cell> <clr-dg-cell>
{{!t.insecure}} {{!t.insecure}}
</clr-dg-cell> </clr-dg-cell>
<clr-dg-cell>{{t.credential.type}}</clr-dg-cell>
<clr-dg-cell>{{t.creation_time | date: 'short'}}</clr-dg-cell> <clr-dg-cell>{{t.creation_time | date: 'short'}}</clr-dg-cell>
</clr-dg-row> </clr-dg-row>
<clr-dg-footer> <clr-dg-footer>

View File

@ -26,3 +26,7 @@
.endpoint-view { .endpoint-view {
position: relative; position: relative;
} }
.flex-min-width {
min-width: 180px;
}

View File

@ -10,7 +10,7 @@ import { ConfirmationDialogComponent } from "../confirmation-dialog/confirmation
import { CreateEditEndpointComponent } from "../create-edit-endpoint/create-edit-endpoint.component"; import { CreateEditEndpointComponent } from "../create-edit-endpoint/create-edit-endpoint.component";
import { InlineAlertComponent } from "../inline-alert/inline-alert.component"; import { InlineAlertComponent } from "../inline-alert/inline-alert.component";
import { ErrorHandler } from "../error-handler/error-handler"; import { ErrorHandler } from "../error-handler/error-handler";
import { Endpoint } from "../service/interface"; import { Endpoint, Adapter } from "../service/interface";
import { import {
EndpointService, EndpointService,
EndpointDefaultService EndpointDefaultService
@ -24,54 +24,83 @@ describe("EndpointComponent (inline template)", () => {
let mockData: Endpoint[] = [ let mockData: Endpoint[] = [
{ {
id: 1, id: 1,
url: "https://10.117.4.151", credential: {
access_key: "admin",
access_secret: "",
type: "basic"
},
description: "test",
insecure: false,
name: "target_01", name: "target_01",
username: "admin", type: "Harbor",
password: "", url: "https://10.117.4.151"
insecure: true,
type: "Harbor"
}, },
{ {
id: 2, id: 2,
url: "https://10.117.5.142", credential: {
name: "target_02", access_key: "AAA",
username: "AAA", access_secret: "",
password: "", type: "basic"
},
description: "test",
insecure: false, insecure: false,
type: "Harbor" name: "target_02",
type: "Harbor",
url: "https://10.117.5.142"
}, },
{ {
id: 3, id: 3,
url: "https://101.1.11.111", credential: {
name: "target_03", access_key: "admin",
username: "admin", access_secret: "",
password: "", type: "basic"
},
description: "test",
insecure: false, insecure: false,
type: "Harbor" name: "target_03",
type: "Harbor",
url: "https://101.1.11.111"
}, },
{ {
id: 4, id: 4,
url: "http://4.4.4.4", credential: {
name: "target_04", access_key: "admin",
username: "", access_secret: "",
password: "", type: "basic"
},
description: "test",
insecure: false, insecure: false,
type: "Harbor" name: "target_04",
type: "Harbor",
url: "https://4.4.4.4"
} }
]; ];
let mockOne: Endpoint[] = [ let mockOne: Endpoint[] = [
{ {
id: 1, id: 1,
url: "https://10.117.4.151", credential: {
name: "target_01", access_key: "admin",
username: "admin", access_secret: "",
password: "", type: "basic"
},
description: "test",
insecure: false, insecure: false,
type: "Harbor" name: "target_01",
type: "Harbor",
url: "https://10.117.4.151"
} }
]; ];
let mockAdapter: [Adapter] = [{
type: "Harbor",
description: "test",
supported_resource_types: [
"repository"
],
supported_resource_filters: null
}];
let comp: EndpointComponent; let comp: EndpointComponent;
let fixture: ComponentFixture<EndpointComponent>; let fixture: ComponentFixture<EndpointComponent>;
let config: IServiceConfig = { let config: IServiceConfig = {
@ -80,6 +109,7 @@ describe("EndpointComponent (inline template)", () => {
let endpointService: EndpointService; let endpointService: EndpointService;
let spy: jasmine.Spy; let spy: jasmine.Spy;
let spyAdapter: jasmine.Spy;
let spyOnRules: jasmine.Spy; let spyOnRules: jasmine.Spy;
let spyOne: jasmine.Spy; let spyOne: jasmine.Spy;
beforeEach(async(() => { beforeEach(async(() => {
@ -110,6 +140,11 @@ describe("EndpointComponent (inline template)", () => {
spy = spyOn(endpointService, "getEndpoints").and.returnValues( spy = spyOn(endpointService, "getEndpoints").and.returnValues(
Promise.resolve(mockData) Promise.resolve(mockData)
); );
spyAdapter = spyOn(endpointService, "getAdapters").and.returnValue(
Promise.resolve(mockAdapter)
);
spyOnRules = spyOn( spyOnRules = spyOn(
endpointService, endpointService,
"getEndpointWithReplicationRules" "getEndpointWithReplicationRules"

View File

@ -77,12 +77,16 @@ export class EndpointComponent implements OnInit, OnDestroy {
get initEndpoint(): Endpoint { get initEndpoint(): Endpoint {
return { return {
url: "", credential: {
name: "", access_key: "",
username: "", access_secret: "",
password: "",
insecure: false,
type: "" type: ""
},
description: "",
insecure: false,
name: "",
type: "",
url: "",
}; };
} }

View File

@ -86,6 +86,7 @@ export const DefaultServiceConfig: IServiceConfig = {
targetBaseEndpoint: "/api/registries", targetBaseEndpoint: "/api/registries",
replicationBaseEndpoint: "/api/replication/executions", replicationBaseEndpoint: "/api/replication/executions",
replicationRuleEndpoint: "/api/replication/policies", replicationRuleEndpoint: "/api/replication/policies",
adapterEndpoint: "api/replication/adapters",
vulnerabilityScanningBaseEndpoint: "/api/repositories", vulnerabilityScanningBaseEndpoint: "/api/repositories",
projectPolicyEndpoint: "/api/projects/configs", projectPolicyEndpoint: "/api/projects/configs",
projectBaseEndpoint: "/api/projects", projectBaseEndpoint: "/api/projects",

View File

@ -84,21 +84,29 @@ describe('Replication Component (inline template)', () => {
let mockEndpoints: Endpoint[] = [ let mockEndpoints: Endpoint[] = [
{ {
"id": 1, "id": 1,
"url": "https://10.117.4.151", "credential": {
"name": "target_01", "access_key": "admin",
"username": "admin", "access_secret": "",
"password": "", "type": "basic"
},
"description": "test",
"insecure": false, "insecure": false,
"type": "Harbor" "name": "target_01",
"type": "Harbor",
"url": "https://10.117.4.151"
}, },
{ {
"id": 2, "id": 2,
"url": "https://10.117.5.142", "credential": {
"name": "target_02", "access_key": "admin",
"username": "AAA", "access_secret": "",
"password": "", "type": "basic"
},
"description": "test",
"insecure": false, "insecure": false,
"type": "Harbor" "name": "target_02",
"type": "Harbor",
"url": "https://10.117.5.142"
}, },
]; ];

View File

@ -54,6 +54,11 @@ export interface IServiceConfig {
*/ */
replicationBaseEndpoint?: string; replicationBaseEndpoint?: string;
/**
* The base endpoint of the service used to handle the adapters.
*/
adapterEndpoint?: string;
/** /**
* The base endpoint of the service used to handle the replication rules. * The base endpoint of the service used to handle the replication rules.
* Replication rule related endpoints will be built based on this endpoint. * Replication rule related endpoints will be built based on this endpoint.

View File

@ -9,7 +9,7 @@ import {
HTTP_GET_OPTIONS HTTP_GET_OPTIONS
} from "../utils"; } from "../utils";
import { RequestQueryParams } from "./RequestQueryParams"; import { RequestQueryParams } from "./RequestQueryParams";
import { Endpoint, ReplicationRule } from "./interface"; import { Endpoint, ReplicationRule, Adapter } from "./interface";
/** /**
* Define the service methods to handle the endpoint related things. * Define the service methods to handle the endpoint related things.
@ -57,6 +57,17 @@ export abstract class EndpointService {
* *
* @memberOf EndpointService * @memberOf EndpointService
*/ */
abstract getAdapters(): Observable<any> | Promise<any> | any;
/**
* Create new endpoint.
*
* @abstract
* ** deprecated param {Adapter} adapter
* returns {(Observable<any> | any)}
*
* @memberOf EndpointService
*/
abstract createEndpoint( abstract createEndpoint(
endpoint: Endpoint endpoint: Endpoint
): Observable<any> | Promise<any> | any; ): Observable<any> | Promise<any> | any;
@ -167,6 +178,14 @@ export class EndpointDefaultService extends EndpointService {
.catch(error => Promise.reject(error)); .catch(error => Promise.reject(error));
} }
public getAdapters(): Observable<any> | Promise<any> | any {
return this.http
.get(`/api/replication/adapters`)
.toPromise()
.then(response => response.json() as Adapter)
.catch(error => Promise.reject(error));
}
public createEndpoint( public createEndpoint(
endpoint: Endpoint endpoint: Endpoint
): Observable<any> | Promise<any> | any { ): Observable<any> | Promise<any> | any {

View File

@ -75,13 +75,27 @@ export interface Tag extends Base {
* extends {Base} * extends {Base}
*/ */
export interface Endpoint extends Base { export interface Endpoint extends Base {
url: string; credential: {
name: string; access_key?: string,
username?: string; access_secret?: string,
password?: string;
insecure: boolean;
type: string; type: string;
[key: string]: any; };
description: string;
insecure: boolean;
name: string;
type: string;
url: string;
}
export interface Adapter extends Base {
type: string;
description: string;
supported_resource_types: [
string
];
supported_resource_filters: [
string
];
} }
/** /**

View File

@ -65,6 +65,7 @@ const uiLibConfig: IServiceConfig = {
targetBaseEndpoint: "/api/registries", targetBaseEndpoint: "/api/registries",
replicationBaseEndpoint: "/api/replication/executions", replicationBaseEndpoint: "/api/replication/executions",
replicationRuleEndpoint: "/api/replication/policies", replicationRuleEndpoint: "/api/replication/policies",
adapterEndpoint: "api/replication/adapters",
vulnerabilityScanningBaseEndpoint: "/api/repositories", vulnerabilityScanningBaseEndpoint: "/api/repositories",
projectPolicyEndpoint: "/api/projects/configs", projectPolicyEndpoint: "/api/projects/configs",
projectBaseEndpoint: "/api/projects", projectBaseEndpoint: "/api/projects",

View File

@ -456,22 +456,25 @@
}, },
"DESTINATION": { "DESTINATION": {
"NEW_ENDPOINT": "New Endpoint", "NEW_ENDPOINT": "New Endpoint",
"PROVIDER": "Provider",
"ENDPOINT": "Endpoint", "ENDPOINT": "Endpoint",
"NAME": "Endpoint Name", "NAME": "Name",
"NAME_IS_REQUIRED": "Endpoint name is required.", "NAME_IS_REQUIRED": "Endpoint name is required.",
"URL": "Endpoint URL", "URL": "Endpoint URL",
"URL_IS_REQUIRED": "Endpoint URL is required.", "URL_IS_REQUIRED": "Endpoint URL is required.",
"USERNAME": "Username", "AUTHENTICATION":"Authentication",
"PASSWORD": "Password", "ACCESS_ID": "Access ID",
"ACCESS_SECRET": "Access Secret",
"STATUS": "Status",
"TEST_CONNECTION": "Test Connection", "TEST_CONNECTION": "Test Connection",
"TITLE_EDIT": "Edit Endpoint", "TITLE_EDIT": "Edit Endpoint",
"TITLE_ADD": "Create Endpoint", "TITLE_ADD": "New Registry Endpoint",
"EDIT": "Edit", "EDIT": "Edit",
"DELETE": "Delete", "DELETE": "Delete",
"TESTING_CONNECTION": "Testing Connection...", "TESTING_CONNECTION": "Testing Connection...",
"TEST_CONNECTION_SUCCESS": "Connection tested successfully.", "TEST_CONNECTION_SUCCESS": "Connection tested successfully.",
"TEST_CONNECTION_FAILURE": "Failed to ping endpoint.", "TEST_CONNECTION_FAILURE": "Failed to ping endpoint.",
"CONFLICT_NAME": "Endpoint name or URL already exists.", "CONFLICT_NAME": "Endpoint name already exists.",
"INVALID_NAME": "Invalid endpoint name.", "INVALID_NAME": "Invalid endpoint name.",
"FAILED_TO_GET_TARGET": "Failed to get endpoint.", "FAILED_TO_GET_TARGET": "Failed to get endpoint.",
"CREATION_TIME": "Creation Time", "CREATION_TIME": "Creation Time",

View File

@ -457,22 +457,25 @@
}, },
"DESTINATION": { "DESTINATION": {
"NEW_ENDPOINT": "Nuevo Endpoint", "NEW_ENDPOINT": "Nuevo Endpoint",
"PROVIDER": "Provider",
"ENDPOINT": "Endpoint", "ENDPOINT": "Endpoint",
"NAME": "Nombre del Endpoint", "NAME": "Nombre",
"NAME_IS_REQUIRED": "El nombre del endpoint es obligatorio.", "NAME_IS_REQUIRED": "El nombre del endpoint es obligatorio.",
"URL": "URL del Endpoint", "URL": "URL del Endpoint",
"URL_IS_REQUIRED": "La URL del endpoint es obligatoria.", "URL_IS_REQUIRED": "La URL del endpoint es obligatoria.",
"USERNAME": "Nombre de usuario", "AUTHENTICATION":"Autenticación",
"PASSWORD": "Contraseña", "ACCESS_ID": "ID de acceso",
"ACCESS_SECRET": "Secreto de acceso",
"STATUS": "Estado",
"TEST_CONNECTION": "Comprobar conexión", "TEST_CONNECTION": "Comprobar conexión",
"TITLE_EDIT": "Editar Endpoint", "TITLE_EDIT": "Editar Endpoint",
"TITLE_ADD": "Crear Endpoint", "TITLE_ADD": "Nuevo punto final de registro",
"EDIT": "Editar", "EDIT": "Editar",
"DELETE": "Eliminar", "DELETE": "Eliminar",
"TESTING_CONNECTION": "Comprobar conexión...", "TESTING_CONNECTION": "Comprobar conexión...",
"TEST_CONNECTION_SUCCESS": "Conexión comprobada satisfactoriamente.", "TEST_CONNECTION_SUCCESS": "Conexión comprobada satisfactoriamente.",
"TEST_CONNECTION_FAILURE": "Fallo al comprobar el endpoint.", "TEST_CONNECTION_FAILURE": "Fallo al comprobar el endpoint.",
"CONFLICT_NAME": "El nombre del endpoint ya existe.", "CONFLICT_NAME": "El nombre ya existe.",
"INVALID_NAME": "El nombre del endpoint no es válido.", "INVALID_NAME": "El nombre del endpoint no es válido.",
"FAILED_TO_GET_TARGET": "Fallo al obtener el endpoint.", "FAILED_TO_GET_TARGET": "Fallo al obtener el endpoint.",
"CREATION_TIME": "Fecha de creación", "CREATION_TIME": "Fecha de creación",

View File

@ -438,21 +438,24 @@
}, },
"DESTINATION": { "DESTINATION": {
"NEW_ENDPOINT": "Nouveau Point Final", "NEW_ENDPOINT": "Nouveau Point Final",
"PROVIDER": "Provider",
"ENDPOINT": "Point Final", "ENDPOINT": "Point Final",
"NAME": "Nom du Point Final", "NAME": "Alors",
"NAME_IS_REQUIRED": "Le nom du Point final est obligatoire.", "NAME_IS_REQUIRED": "Le nom du Point final est obligatoire.",
"URL": "URL du Point Final", "URL": "URL du Point Final",
"URL_IS_REQUIRED": "L'URL du Point Final est obligatoire.", "URL_IS_REQUIRED": "L'URL du Point Final est obligatoire.",
"USERNAME": "Nom d'utilisateur", "AUTHENTICATION":"Authentification",
"PASSWORD": "Mot de Passe", "ACCESS_ID": "ID d'accès",
"ACCESS_SECRET": "Secret d'accès",
"STATUS": "Statut",
"TEST_CONNECTION": "Test de Connexion", "TEST_CONNECTION": "Test de Connexion",
"TITLE_EDIT": "Editer le Point Final", "TITLE_EDIT": "Editer le Point Final",
"TITLE_ADD": "Créer le Point Final", "TITLE_ADD": "Nouveau point de terminaison de registre",
"DELETE": "Supprimer le Point Final", "DELETE": "Supprimer le Point Final",
"TESTING_CONNECTION": "En train de tester la Connexion...", "TESTING_CONNECTION": "En train de tester la Connexion...",
"TEST_CONNECTION_SUCCESS": "Connexion testée avec succès.", "TEST_CONNECTION_SUCCESS": "Connexion testée avec succès.",
"TEST_CONNECTION_FAILURE": "Echec du ping du Point Final.", "TEST_CONNECTION_FAILURE": "Echec du ping du Point Final.",
"CONFLICT_NAME": "Le nom ou l'URL du point final existe déjà.", "CONFLICT_NAME": "Le nom du noeud final existe déjà.",
"INVALID_NAME": "Nom du point final invalide.", "INVALID_NAME": "Nom du point final invalide.",
"FAILED_TO_GET_TARGET": "Echec de l'obtention du point final.", "FAILED_TO_GET_TARGET": "Echec de l'obtention du point final.",
"CREATION_TIME": "Temps de Création", "CREATION_TIME": "Temps de Création",

View File

@ -456,22 +456,25 @@
}, },
"DESTINATION": { "DESTINATION": {
"NEW_ENDPOINT": "Novo Endpoint", "NEW_ENDPOINT": "Novo Endpoint",
"PROVIDER": "Provider",
"ENDPOINT": "Endpoint", "ENDPOINT": "Endpoint",
"NAME": "Nome do Endpoint", "NAME": "Nome",
"NAME_IS_REQUIRED": "Nome do Endpoint é obrigatório.", "NAME_IS_REQUIRED": "Nome do Endpoint é obrigatório.",
"URL": "URL do Endpoint", "URL": "URL do Endpoint",
"URL_IS_REQUIRED": "URL do Endpoint URL é obrigatória.", "URL_IS_REQUIRED": "URL do Endpoint URL é obrigatória.",
"USERNAME": "Nome de usuário", "AUTHENTICATION":"Autenticação",
"PASSWORD": "Senha", "ACCESS_ID": "ID de acesso",
"ACCESS_SECRET": "Secreto de acceso",
"STATUS": "Segredo de acesso",
"TEST_CONNECTION": "Testar Conexão", "TEST_CONNECTION": "Testar Conexão",
"TITLE_EDIT": "Editar Endpoint", "TITLE_EDIT": "Editar Endpoint",
"TITLE_ADD": "Criar Endpoint", "TITLE_ADD": "Novo ponto final do registro",
"EDIT": "Editar", "EDIT": "Editar",
"DELETE": "Remover", "DELETE": "Remover",
"TESTING_CONNECTION": "Testando Conexão...", "TESTING_CONNECTION": "Testando Conexão...",
"TEST_CONNECTION_SUCCESS": "Conexão testada com sucesso.", "TEST_CONNECTION_SUCCESS": "Conexão testada com sucesso.",
"TEST_CONNECTION_FAILURE": "Falha ao pingar o endpoint.", "TEST_CONNECTION_FAILURE": "Falha ao pingar o endpoint.",
"CONFLICT_NAME": "Nome do Endpoint ou URL já existe.", "CONFLICT_NAME": "O nome do terminal já existe.",
"INVALID_NAME": "Nome do Endpoint inválido.", "INVALID_NAME": "Nome do Endpoint inválido.",
"FAILED_TO_GET_TARGET": "Falha ao obter endpoint.", "FAILED_TO_GET_TARGET": "Falha ao obter endpoint.",
"CREATION_TIME": "Data de criação", "CREATION_TIME": "Data de criação",

View File

@ -457,13 +457,16 @@
}, },
"DESTINATION": { "DESTINATION": {
"NEW_ENDPOINT": "新建目标", "NEW_ENDPOINT": "新建目标",
"PROVIDER": "提供者",
"ENDPOINT": "目标", "ENDPOINT": "目标",
"NAME": "目标名", "NAME": "目标名",
"NAME_IS_REQUIRED": "目标名为必填项。", "NAME_IS_REQUIRED": "目标名为必填项。",
"URL": "目标URL", "URL": "目标URL",
"URL_IS_REQUIRED": "目标URL为必填项。", "URL_IS_REQUIRED": "目标URL为必填项。",
"USERNAME": "用户名", "AUTHENTICATION":"认证",
"PASSWORD": "密码", "ACCESS_ID": "访问ID",
"ACCESS_SECRET": "访问密码",
"STATUS": "状态",
"TEST_CONNECTION": "测试连接", "TEST_CONNECTION": "测试连接",
"TITLE_EDIT": "编辑目标", "TITLE_EDIT": "编辑目标",
"TITLE_ADD": "新建目标", "TITLE_ADD": "新建目标",
@ -472,7 +475,7 @@
"TESTING_CONNECTION": "正在测试连接...", "TESTING_CONNECTION": "正在测试连接...",
"TEST_CONNECTION_SUCCESS": "测试连接成功。", "TEST_CONNECTION_SUCCESS": "测试连接成功。",
"TEST_CONNECTION_FAILURE": "测试连接失败。", "TEST_CONNECTION_FAILURE": "测试连接失败。",
"CONFLICT_NAME": "目标名或目标URL已存在。", "CONFLICT_NAME": "目标名已存在。",
"INVALID_NAME": "无效的目标名称。", "INVALID_NAME": "无效的目标名称。",
"FAILED_TO_GET_TARGET": "获取目标失败。", "FAILED_TO_GET_TARGET": "获取目标失败。",
"CREATION_TIME": "创建时间", "CREATION_TIME": "创建时间",