mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-18 22:57:38 +01:00
Add P2p preheat distribution instance UI
Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
parent
a78ab897d7
commit
d01ff31dc8
@ -21,4 +21,32 @@ const swaggerObj = yaml.load(fs.readFileSync(inputFile, {encoding: 'utf-8'}));
|
||||
if (swaggerObj.host) {
|
||||
delete swaggerObj.host;
|
||||
}
|
||||
// enhancement for property 'additionalProperties'
|
||||
traverseObject(swaggerObj);
|
||||
|
||||
fs.writeFileSync(outputDir + '/swagger.json', JSON.stringify(swaggerObj, null, 2));
|
||||
|
||||
|
||||
function traverseObject(obj) {
|
||||
if (obj) {
|
||||
if (Array.isArray(obj)) {
|
||||
for (let i = 0; i < obj.length; i++) {
|
||||
traverseObject(obj[i])
|
||||
}
|
||||
}
|
||||
if (typeof obj === 'object') {
|
||||
for (let name in obj) {
|
||||
if (obj.hasOwnProperty(name)) {
|
||||
if (name === 'additionalProperties'
|
||||
&& obj[name].type === 'object'
|
||||
&& obj[name].additionalProperties === true) {
|
||||
obj[name] = true;
|
||||
} else {
|
||||
traverseObject(obj[name])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,6 +46,7 @@ import { ProjectQuotasComponent } from './project-quotas/project-quotas.componen
|
||||
import { HarborLibraryModule } from "../lib/harbor-library.module";
|
||||
import { HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
import { AllPipesModule } from './all-pipes/all-pipes.module';
|
||||
import { DistributionModule } from './distribution/distribution.module';
|
||||
registerLocaleData(zh, 'zh-cn');
|
||||
registerLocaleData(es, 'es-es');
|
||||
registerLocaleData(localeFr, 'fr-fr');
|
||||
@ -85,7 +86,8 @@ export function getCurrentLanguage(translateService: TranslateService) {
|
||||
OidcOnboardModule,
|
||||
LicenseModule,
|
||||
HarborLibraryModule,
|
||||
AllPipesModule
|
||||
AllPipesModule,
|
||||
DistributionModule,
|
||||
],
|
||||
exports: [
|
||||
],
|
||||
|
@ -56,6 +56,10 @@
|
||||
<clr-icon shape="cloud-traffic" clrVerticalNavIcon></clr-icon>
|
||||
{{'SIDE_NAV.SYSTEM_MGMT.REPLICATION' | translate}}
|
||||
</a>
|
||||
<a clrVerticalNavLink routerLink="/harbor/distribution/instances" routerLinkActive="active">
|
||||
<clr-icon shape="share"></clr-icon>
|
||||
{{'SIDE_NAV.DISTRIBUTIONS.NAME' | translate}}
|
||||
</a>
|
||||
<a *ngIf="!withAdmiral" clrVerticalNavLink routerLink="/harbor/labels"
|
||||
routerLinkActive="active">
|
||||
<clr-icon shape="tag" clrVerticalNavIcon></clr-icon>
|
||||
|
20
src/portal/src/app/distribution/base.scss
Normal file
20
src/portal/src/app/distribution/base.scss
Normal file
@ -0,0 +1,20 @@
|
||||
@mixin refresh-button {
|
||||
cursor: pointer;
|
||||
margin-top: 7px;
|
||||
}
|
||||
|
||||
@mixin refresh-button-hover($color) {
|
||||
color: $color;
|
||||
}
|
||||
|
||||
@mixin float-right($right-margin) {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-right: $right-margin;
|
||||
}
|
||||
|
||||
@mixin square($edge) {
|
||||
width: $edge;
|
||||
height: $edge;
|
||||
min-height: $edge;
|
||||
}
|
@ -0,0 +1,137 @@
|
||||
<div class="row">
|
||||
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
|
||||
<h2 class="custom-h2">
|
||||
{{ 'SIDE_NAV.DISTRIBUTIONS.INSTANCES' | translate }}
|
||||
</h2>
|
||||
<div>
|
||||
<clr-datagrid (clrDgRefresh)="loadData()" [clrDgLoading]="inProgress" [(clrDgSelected)]="selectedRow">
|
||||
<clr-dg-action-bar>
|
||||
<div class="clr-row">
|
||||
<div class="clr-col-7">
|
||||
<button id="new-instance"
|
||||
type="button"
|
||||
class="btn btn-secondary"
|
||||
(click)="addInstance()"
|
||||
>
|
||||
<clr-icon shape="plus" size="16"></clr-icon> {{
|
||||
'DISTRIBUTION.ADD_ACTION' | translate
|
||||
}}
|
||||
</button>
|
||||
<button id="set-default"
|
||||
[disabled]="!(selectedRow && selectedRow.length === 1 && !selectedRow[0].default && !selectedRow[0].enabled)"
|
||||
class="btn btn-secondary"
|
||||
(click)="setAsDefault()">{{'SCANNER.SET_AS_DEFAULT' | translate}}</button>
|
||||
<clr-dropdown
|
||||
[clrCloseMenuOnItemClick]="false"
|
||||
class="btn btn-link"
|
||||
clrDropdownTrigger>
|
||||
<span id="member-action">{{ 'BUTTON.ACTIONS' | translate}}<clr-icon shape="caret down"></clr-icon></span>
|
||||
<clr-dropdown-menu *clrIfOpen>
|
||||
<clr-dropdown>
|
||||
<button type="button" class="btn btn-secondary" (click)="editInstance()"
|
||||
[disabled]="!(selectedRow && selectedRow.length === 1)">
|
||||
<clr-icon shape="edit" size="16"></clr-icon> {{'DISTRIBUTION.EDIT_ACTION' | translate}}
|
||||
</button>
|
||||
<button type="button" class="btn btn-secondary" (click)="operateInstances('enable', selectedRow)"
|
||||
[disabled]="!(selectedRow && selectedRow.length === 1 && !selectedRow[0].enabled)">
|
||||
<clr-icon shape="connect" size="16"></clr-icon> {{'DISTRIBUTION.ENABLE_ACTION' | translate}}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary"
|
||||
(click)="operateInstances('disable', selectedRow)"
|
||||
[disabled]="!(selectedRow && selectedRow.length === 1 && selectedRow[0].enabled)">
|
||||
<clr-icon shape="disconnect" size="16"></clr-icon> {{'DISTRIBUTION.DISABLE_ACTION' | translate}}
|
||||
</button>
|
||||
<div class="dropdown-divider"></div>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-secondary"
|
||||
(click)="operateInstances('delete', selectedRow)"
|
||||
[disabled]="selectedRow.length < 1">
|
||||
<clr-icon shape="window-close" size="16"></clr-icon> {{'DISTRIBUTION.DELETE_ACTION' | translate}}
|
||||
</button>
|
||||
</clr-dropdown>
|
||||
</clr-dropdown-menu>
|
||||
</clr-dropdown>
|
||||
</div>
|
||||
<div class="clr-col-5">
|
||||
<div class="action-head-pos">
|
||||
<hbr-filter [withDivider]="true" filterPlaceholder="{{'DISTRIBUTION.FILTER_INSTANCE_PLACEHOLDER' | translate}}" (filterEvt)="doFilter($event)"></hbr-filter>
|
||||
<span class="refresh-btn">
|
||||
<clr-icon shape="refresh" [hidden]="inProgress" ng-disabled="inProgress" (click)="refresh()"></clr-icon>
|
||||
<span class="spinner spinner-inline" [hidden]="inProgress === false"></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</clr-dg-action-bar>
|
||||
<clr-dg-column>{{ 'DISTRIBUTION.NAME' | translate }}</clr-dg-column>
|
||||
<clr-dg-column>{{ 'DISTRIBUTION.ENDPOINT' | translate }}</clr-dg-column>
|
||||
<clr-dg-column>{{ 'DISTRIBUTION.PROVIDER' | translate }}</clr-dg-column>
|
||||
<clr-dg-column>{{ 'DISTRIBUTION.STATUS' | translate }}</clr-dg-column>
|
||||
<clr-dg-column>{{ 'DISTRIBUTION.ENABLED' | translate }}</clr-dg-column>
|
||||
<clr-dg-column>{{'SCANNER.AUTH' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'DISTRIBUTION.SETUP_TIMESTAMP' | translate}}</clr-dg-column>
|
||||
<clr-dg-column>{{'DISTRIBUTION.DESCRIPTION' | translate}}</clr-dg-column>
|
||||
<clr-dg-placeholder>{{
|
||||
'DISTRIBUTION.NOT_FOUND' | translate
|
||||
}}</clr-dg-placeholder>
|
||||
<clr-dg-row *ngFor="let instance of instances" [clrDgItem]="instance">
|
||||
<clr-dg-cell>
|
||||
<span>{{ instance.name }}</span>
|
||||
<span *ngIf="instance.default" class="label label-info ml-1">{{'SCANNER.DEFAULT' | translate}}</span>
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell>{{ instance.endpoint }}</clr-dg-cell>
|
||||
<clr-dg-cell>
|
||||
<span>{{ instance.vendor }}</span>
|
||||
<clr-signpost *ngIf="providerMap[instance.vendor]">
|
||||
<clr-signpost-content *clrIfOpen>
|
||||
<div>
|
||||
<span>
|
||||
<img (error)="showDefaultIcon($event, instance.vendor)" class="height-24" [src]="providerMap[instance.vendor].icon">
|
||||
</span>
|
||||
</div>
|
||||
<div class="margin-top-5px">
|
||||
<span>{{'DISTRIBUTION.NAME' | translate}}:</span>
|
||||
<span class="ml-1">{{providerMap[instance.vendor].name}}</span>
|
||||
</div>
|
||||
<div class="margin-top-5px">
|
||||
<span class="no-wrapper">
|
||||
<span>{{'DISTRIBUTION.MAINTAINER' | translate}}:</span>
|
||||
<span class="ml-1">{{providerMap[instance.vendor].maintainers?.join(',')}}</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="margin-top-5px">
|
||||
<span>{{'DISTRIBUTION.SOURCE' | translate}}:</span>
|
||||
<a target="_blank" href="{{providerMap[instance.vendor].source}}" class="ml-1">{{providerMap[instance.vendor].source}}</a>
|
||||
</div>
|
||||
<div class="margin-top-5px">
|
||||
<span>{{'DISTRIBUTION.VERSION' | translate}}:</span>
|
||||
<span class="ml-1">{{providerMap[instance.vendor].version}}</span>
|
||||
</div>
|
||||
</clr-signpost-content>
|
||||
</clr-signpost>
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell [ngSwitch]="instance.status === 'Healthy'">
|
||||
<span *ngSwitchCase="true" class="label label-success">{{ instance.status }}</span>
|
||||
<span *ngSwitchDefault class="label label-danger">{{ instance.status }}</span>
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell>{{ instance.enabled || false }}</clr-dg-cell>
|
||||
<clr-dg-cell>{{ instance.auth_mode }}</clr-dg-cell>
|
||||
<clr-dg-cell>{{fmtTime(instance.setup_timestamp) | date: 'short'}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{ instance.description }}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
<clr-dg-pagination #pagination [clrDgPageSize]="pageSize" [clrDgTotalItems]="totalCount" [(clrDgPage)]="currentPage">
|
||||
<span *ngIf="pagination.totalItems">{{ pagination.firstItem + 1 }} - {{ pagination.lastItem + 1 }}{{ 'HELM_CHART.OF' | translate }}</span>
|
||||
<span>{{ pagination.totalItems }} {{ 'HELM_CHART.ITEMS' | translate }}</span>
|
||||
</clr-dg-pagination>
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<dist-setup-modal (refresh)="refresh()" [providers]="providers" #setupModal></dist-setup-modal>
|
||||
</div>
|
@ -0,0 +1,32 @@
|
||||
@import "../base.scss";
|
||||
$refrsh-btn-color: #007CBB;
|
||||
.refresh-btn {
|
||||
@include refresh-button
|
||||
}
|
||||
|
||||
.refresh-btn:hover {
|
||||
@include refresh-button-hover($refrsh-btn-color);
|
||||
}
|
||||
|
||||
.filter-pos {
|
||||
float: right;
|
||||
margin-right: 24px;
|
||||
position: relative;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
.action-head-pos {
|
||||
padding-right: 18px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
.no-wrapper {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.margin-top-5px {
|
||||
margin-top: 5px;
|
||||
}
|
||||
.height-24 {
|
||||
height: 24px;
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { TranslateModule, TranslateService } from '@ngx-translate/core';
|
||||
import { ClarityModule } from '@clr/angular';
|
||||
import { SharedModule } from '../../shared/shared.module';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { DistributionInstancesComponent } from './distribution-instances.component';
|
||||
import { PreheatService } from "../../../../ng-swagger-gen/services/preheat.service";
|
||||
import { Instance } from '../../../../ng-swagger-gen/models/instance';
|
||||
import { HttpHeaders, HttpResponse } from '@angular/common/http';
|
||||
import { of } from 'rxjs';
|
||||
import { delay } from 'rxjs/operators';
|
||||
import { Metadata } from '../../../../ng-swagger-gen/models/metadata';
|
||||
import { DistributionSetupModalComponent } from '../distribution-setup-modal/distribution-setup-modal.component';
|
||||
|
||||
describe('DistributionInstanceComponent', () => {
|
||||
let component: DistributionInstancesComponent;
|
||||
let fixture: ComponentFixture<DistributionInstancesComponent>;
|
||||
|
||||
const instance1: Instance = {
|
||||
name: 'Test1',
|
||||
default: true,
|
||||
enabled: true,
|
||||
description: 'Test1',
|
||||
endpoint: 'http://test.com',
|
||||
id: 1,
|
||||
setup_timestamp: new Date().getTime(),
|
||||
auth_mode: 'NONE',
|
||||
vendor: 'kraken',
|
||||
status: 'Healthy'
|
||||
};
|
||||
|
||||
const instance2: Instance = {
|
||||
name: 'Test2',
|
||||
default: false,
|
||||
enabled: false,
|
||||
description: 'Test2',
|
||||
endpoint: 'http://test2.com',
|
||||
id: 2,
|
||||
setup_timestamp: new Date().getTime() + 3600000,
|
||||
auth_mode: 'BASIC',
|
||||
auth_info: {
|
||||
password: '123',
|
||||
username: 'abc'
|
||||
},
|
||||
vendor: 'kraken',
|
||||
status: 'Healthy'
|
||||
};
|
||||
|
||||
const instance3: Instance = {
|
||||
name: 'Test3',
|
||||
default: false,
|
||||
enabled: true,
|
||||
description: 'Test3',
|
||||
endpoint: 'http://test3.com',
|
||||
id: 3,
|
||||
setup_timestamp: new Date().getTime() + 7200000,
|
||||
auth_mode: 'OAUTH',
|
||||
auth_info: {
|
||||
token: 'xxxxxxxxxxxxxxxxxxxx'
|
||||
},
|
||||
vendor: 'kraken',
|
||||
status: 'Unhealthy'
|
||||
};
|
||||
|
||||
const mockedProviders: Metadata[] = [{
|
||||
'icon': 'https://raw.githubusercontent.com/alibaba/Dragonfly/master/docs/images/logo.png',
|
||||
'id': 'dragonfly',
|
||||
'maintainers': ['Jin Zhang/taiyun.zj@alibaba-inc.com'],
|
||||
'name': 'Dragonfly',
|
||||
'source': 'https://github.com/alibaba/Dragonfly',
|
||||
'version': '0.10.1'
|
||||
}, {
|
||||
'icon': 'https://github.com/uber/kraken/blob/master/assets/kraken-logo-color.svg',
|
||||
'id': 'kraken',
|
||||
'maintainers': ['mmpei/peimingming@corp.netease.com'],
|
||||
'name': 'Kraken',
|
||||
'source': 'https://github.com/uber/kraken',
|
||||
'version': '0.1.3'
|
||||
}];
|
||||
|
||||
|
||||
const fakedPreheatService = {
|
||||
ListInstancesResponse() {
|
||||
const res: HttpResponse<Array<Instance>> = new HttpResponse<Array<Instance>>({
|
||||
headers: new HttpHeaders({'x-total-count': '3'}),
|
||||
body: [instance1, instance2, instance3]
|
||||
});
|
||||
return of(res).pipe(delay(10));
|
||||
},
|
||||
ListProviders() {
|
||||
return of(mockedProviders).pipe(delay(10));
|
||||
}
|
||||
};
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
ClarityModule,
|
||||
TranslateModule,
|
||||
SharedModule,
|
||||
HttpClientTestingModule
|
||||
],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
providers: [
|
||||
{ provide: PreheatService, useValue: fakedPreheatService }
|
||||
],
|
||||
declarations: [
|
||||
DistributionInstancesComponent,
|
||||
DistributionSetupModalComponent
|
||||
]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DistributionInstancesComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render list and get providers', async () => {
|
||||
fixture.autoDetectChanges(true);
|
||||
await fixture.whenStable();
|
||||
expect(component.providers.length).toEqual(2);
|
||||
const rows = fixture.nativeElement.getElementsByTagName('clr-dg-row');
|
||||
expect(rows.length).toEqual(3);
|
||||
});
|
||||
|
||||
it('should open modal', async () => {
|
||||
fixture.autoDetectChanges(true);
|
||||
await fixture.whenStable();
|
||||
const addButton: HTMLButtonElement = fixture.nativeElement.querySelector("#new-instance");
|
||||
addButton.click();
|
||||
await fixture.whenStable();
|
||||
const modal: HTMLElement = fixture.nativeElement.querySelector("clr-modal");
|
||||
expect(modal).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,377 @@
|
||||
import { MessageHandlerService } from '../../shared/message-handler/message-handler.service';
|
||||
import { Component, OnInit, ViewChild, OnDestroy } from '@angular/core';
|
||||
import {
|
||||
Subscription,
|
||||
Observable,
|
||||
forkJoin,
|
||||
throwError as observableThrowError
|
||||
} from 'rxjs';
|
||||
import { DistributionSetupModalComponent } from '../distribution-setup-modal/distribution-setup-modal.component';
|
||||
import { OperationService } from '../../../lib/components/operation/operation.service';
|
||||
import {
|
||||
ConfirmationState,
|
||||
ConfirmationTargets,
|
||||
ConfirmationButtons
|
||||
} from '../../shared/shared.const';
|
||||
import { ConfirmationDialogService } from '../../shared/confirmation-dialog/confirmation-dialog.service';
|
||||
import { ConfirmationMessage } from '../../shared/confirmation-dialog/confirmation-message';
|
||||
import {
|
||||
operateChanges,
|
||||
OperateInfo,
|
||||
OperationState
|
||||
} from '../../../lib/components/operation/operate';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { map, catchError, finalize } from 'rxjs/operators';
|
||||
import { errorHandler } from '../../../lib/utils/shared/shared.utils';
|
||||
import { clone, DEFAULT_PAGE_SIZE } from '../../../lib/utils/utils';
|
||||
import { Instance } from "../../../../ng-swagger-gen/models/instance";
|
||||
import { PreheatService } from "../../../../ng-swagger-gen/services/preheat.service";
|
||||
import { Metadata } from '../../../../ng-swagger-gen/models/metadata';
|
||||
|
||||
interface MultiOperateData {
|
||||
operation: string;
|
||||
instances: Instance[];
|
||||
}
|
||||
|
||||
const DEFAULT_ICON: string = 'images/harbor-logo.svg';
|
||||
const KRAKEN_ICON: string = 'images/kraken-logo-color.svg';
|
||||
const ONE_THOUSAND: number = 1000;
|
||||
const KRAKEN: string = 'kraken';
|
||||
|
||||
@Component({
|
||||
selector: 'dist-instances',
|
||||
templateUrl: './distribution-instances.component.html',
|
||||
styleUrls: ['./distribution-instances.component.scss']
|
||||
})
|
||||
export class DistributionInstancesComponent implements OnInit, OnDestroy {
|
||||
instances: Instance[] = [];
|
||||
selectedRow: Instance[] = [];
|
||||
|
||||
pageSize: number = DEFAULT_PAGE_SIZE;
|
||||
currentPage: number = 1;
|
||||
totalCount: number = 0;
|
||||
queryString: string;
|
||||
|
||||
chanSub: Subscription;
|
||||
|
||||
private loading: boolean = true;
|
||||
private operationSubscription: Subscription;
|
||||
|
||||
@ViewChild('setupModal', { static: false })
|
||||
setupModal: DistributionSetupModalComponent;
|
||||
providerMap: {[key: string]: Metadata} = {};
|
||||
providers: Metadata[] = [];
|
||||
constructor(
|
||||
private disService: PreheatService,
|
||||
private msgHandler: MessageHandlerService,
|
||||
private translate: TranslateService,
|
||||
private operationDialogService: ConfirmationDialogService,
|
||||
private operationService: OperationService
|
||||
) {
|
||||
// subscribe operation
|
||||
this.operationSubscription = operationDialogService.confirmationConfirm$.subscribe(
|
||||
confirmed => {
|
||||
if (
|
||||
confirmed &&
|
||||
confirmed.source === ConfirmationTargets.INSTANCE &&
|
||||
confirmed.state === ConfirmationState.CONFIRMED
|
||||
) {
|
||||
this.operateInstance(confirmed.data);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public get inProgress(): boolean {
|
||||
return this.loading;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.loadData();
|
||||
this.getProviders();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.operationSubscription) {
|
||||
this.operationSubscription.unsubscribe();
|
||||
}
|
||||
if (this.chanSub) {
|
||||
this.chanSub.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
||||
getProviders() {
|
||||
this.disService.ListProviders().subscribe(
|
||||
providers => {
|
||||
if (providers && providers.length) {
|
||||
this.providers = providers;
|
||||
providers.forEach(item => {
|
||||
this.providerMap[item.id] = item;
|
||||
});
|
||||
}
|
||||
},
|
||||
err => this.msgHandler.error(err)
|
||||
);
|
||||
}
|
||||
|
||||
loadData() {
|
||||
this.selectedRow = [];
|
||||
const queryParam: PreheatService.ListInstancesParams = {
|
||||
page: this.currentPage,
|
||||
pageSize: this.pageSize
|
||||
};
|
||||
if (this.queryString) {
|
||||
queryParam.q = encodeURIComponent(`name=~${this.queryString}`);
|
||||
}
|
||||
this.loading = true;
|
||||
this.disService.ListInstancesResponse(queryParam)
|
||||
.pipe(finalize(() => this.loading = false))
|
||||
.subscribe(
|
||||
response => {
|
||||
this.totalCount = Number.parseInt(
|
||||
response.headers.get('x-total-count')
|
||||
);
|
||||
this.instances = response.body as Instance[];
|
||||
},
|
||||
err => this.msgHandler.error(err)
|
||||
);
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.queryString = null;
|
||||
this.currentPage = 1;
|
||||
this.loadData();
|
||||
}
|
||||
|
||||
doFilter($evt: any) {
|
||||
this.currentPage = 1;
|
||||
this.queryString = $evt;
|
||||
this.loadData();
|
||||
}
|
||||
|
||||
addInstance() {
|
||||
this.setupModal.openSetupModal(false);
|
||||
}
|
||||
|
||||
editInstance() {
|
||||
if (this.selectedRow && this.selectedRow.length === 1) {
|
||||
this.setupModal.openSetupModal(true, clone(this.selectedRow[0]));
|
||||
}
|
||||
}
|
||||
|
||||
setAsDefault() {
|
||||
if (this.selectedRow && this.selectedRow.length === 1) {
|
||||
const operMessage = new OperateInfo();
|
||||
operMessage.name = 'DISTRIBUTION.SET_AS_DEFAULT';
|
||||
operMessage.data.id = this.selectedRow[0].id;
|
||||
operMessage.state = OperationState.progressing;
|
||||
operMessage.data.name = this.selectedRow[0].name;
|
||||
this.operationService.publishInfo(operMessage);
|
||||
this.disService.UpdateInstance({
|
||||
propertySet: {default: true},
|
||||
instanceId: this.selectedRow[0].id
|
||||
})
|
||||
.subscribe(
|
||||
() => {
|
||||
this.translate.get('DISTRIBUTION.SET_DEFAULT_SUCCESS').subscribe(msg => {
|
||||
operateChanges(operMessage, OperationState.success);
|
||||
this.msgHandler.info(msg);
|
||||
});
|
||||
this.refresh();
|
||||
},
|
||||
error => {
|
||||
const message = errorHandler(error);
|
||||
this.translate.get('DISTRIBUTION.SET_DEFAULT_FAILED').subscribe(msg => {
|
||||
operateChanges(operMessage, OperationState.failure, msg);
|
||||
this.translate.get(message).subscribe(errMsg => {
|
||||
this.msgHandler.error(msg + ': ' + errMsg);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
// Operate the specified Instance
|
||||
operateInstances(operation: string, instances: Instance[]): void {
|
||||
let arr: string[] = [];
|
||||
let title: string;
|
||||
let summary: string;
|
||||
let buttons: ConfirmationButtons;
|
||||
|
||||
switch (operation) {
|
||||
case 'delete':
|
||||
title = 'DISTRIBUTION.DELETION_TITLE';
|
||||
summary = 'DISTRIBUTION.DELETION_SUMMARY';
|
||||
buttons = ConfirmationButtons.DELETE_CANCEL;
|
||||
break;
|
||||
case 'enable':
|
||||
title = 'DISTRIBUTION.ENABLE_TITLE';
|
||||
summary = 'DISTRIBUTION.ENABLE_SUMMARY';
|
||||
buttons = ConfirmationButtons.ENABLE_CANCEL;
|
||||
break;
|
||||
case 'disable':
|
||||
title = 'DISTRIBUTION.DISABLE_TITLE';
|
||||
summary = 'DISTRIBUTION.DISABLE_SUMMARY';
|
||||
buttons = ConfirmationButtons.DISABLE_CANCEL;
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (instances && instances.length) {
|
||||
instances.forEach(instance => {
|
||||
arr.push(instance.name);
|
||||
});
|
||||
}
|
||||
// Confirm
|
||||
let msg: ConfirmationMessage = new ConfirmationMessage(
|
||||
title,
|
||||
summary,
|
||||
arr.join(','),
|
||||
{ operation: operation, instances: instances },
|
||||
ConfirmationTargets.INSTANCE,
|
||||
buttons
|
||||
);
|
||||
this.operationDialogService.openComfirmDialog(msg);
|
||||
}
|
||||
|
||||
operateInstance(data: MultiOperateData) {
|
||||
let observableLists: any[] = [];
|
||||
if (data.instances && data.instances.length) {
|
||||
switch (data.operation) {
|
||||
case 'delete':
|
||||
data.instances.forEach(instance => {
|
||||
observableLists.push(this.deleteInstance(instance));
|
||||
});
|
||||
break;
|
||||
|
||||
case 'enable':
|
||||
data.instances.forEach(instance => {
|
||||
observableLists.push(this.enableInstance(instance));
|
||||
});
|
||||
break;
|
||||
|
||||
case 'disable':
|
||||
data.instances.forEach(instance => {
|
||||
observableLists.push(this.disableInstance(instance));
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
forkJoin(...observableLists).subscribe(item => {
|
||||
this.selectedRow = [];
|
||||
this.refresh();
|
||||
});
|
||||
}
|
||||
}
|
||||
deleteInstance(instance: Instance): Observable<any> {
|
||||
let operMessage = new OperateInfo();
|
||||
operMessage.name = 'DISTRIBUTION.DELETE_INSTANCE';
|
||||
operMessage.data.id = instance.id;
|
||||
operMessage.state = OperationState.progressing;
|
||||
operMessage.data.name = instance.name;
|
||||
this.operationService.publishInfo(operMessage);
|
||||
|
||||
return this.disService.DeleteInstance({instanceId: instance.id}).pipe(
|
||||
map(() => {
|
||||
this.translate.get('DISTRIBUTION.DELETED_SUCCESS').subscribe(msg => {
|
||||
operateChanges(operMessage, OperationState.success);
|
||||
this.msgHandler.info(msg);
|
||||
});
|
||||
}),
|
||||
catchError(error => {
|
||||
const message = errorHandler(error);
|
||||
this.translate.get('DISTRIBUTION.DELETED_FAILED').subscribe(msg => {
|
||||
operateChanges(operMessage, OperationState.failure, msg);
|
||||
this.translate.get(message).subscribe(errMsg => {
|
||||
this.msgHandler.error(msg + ': ' + errMsg);
|
||||
});
|
||||
});
|
||||
return observableThrowError(message);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
enableInstance(instance: Instance) {
|
||||
let operMessage = new OperateInfo();
|
||||
operMessage.name = 'DISTRIBUTION.ENABLE_INSTANCE';
|
||||
operMessage.data.id = instance.id;
|
||||
operMessage.state = OperationState.progressing;
|
||||
operMessage.data.name = instance.name;
|
||||
this.operationService.publishInfo(operMessage);
|
||||
|
||||
instance.enabled = true;
|
||||
return this.disService
|
||||
.UpdateInstance({
|
||||
propertySet: {enabled: true},
|
||||
instanceId: instance.id
|
||||
})
|
||||
.pipe(
|
||||
map(() => {
|
||||
this.translate.get('DISTRIBUTION.ENABLE_SUCCESS').subscribe(msg => {
|
||||
operateChanges(operMessage, OperationState.success);
|
||||
this.msgHandler.info(msg);
|
||||
});
|
||||
}),
|
||||
catchError(error => {
|
||||
const message = errorHandler(error);
|
||||
this.translate.get('DISTRIBUTION.ENABLE_FAILED').subscribe(msg => {
|
||||
operateChanges(operMessage, OperationState.failure, msg);
|
||||
this.translate.get(message).subscribe(errMsg => {
|
||||
this.msgHandler.error(msg + ': ' + errMsg);
|
||||
});
|
||||
});
|
||||
return observableThrowError(message);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
disableInstance(instance: Instance) {
|
||||
let operMessage = new OperateInfo();
|
||||
operMessage.name = 'DISTRIBUTION.DISABLE_INSTANCE';
|
||||
operMessage.data.id = instance.id;
|
||||
operMessage.state = OperationState.progressing;
|
||||
operMessage.data.name = instance.name;
|
||||
this.operationService.publishInfo(operMessage);
|
||||
|
||||
instance.enabled = false;
|
||||
return this.disService
|
||||
.UpdateInstance({
|
||||
propertySet: {enabled: false},
|
||||
instanceId: instance.id
|
||||
})
|
||||
.pipe(
|
||||
map(() => {
|
||||
this.translate.get('DISTRIBUTION.DISABLE_SUCCESS').subscribe(msg => {
|
||||
operateChanges(operMessage, OperationState.success);
|
||||
this.msgHandler.info(msg);
|
||||
});
|
||||
}),
|
||||
catchError(error => {
|
||||
const message = errorHandler(error);
|
||||
this.translate.get('DISTRIBUTION.DISABLE_FAILED').subscribe(msg => {
|
||||
operateChanges(operMessage, OperationState.failure, msg);
|
||||
this.translate.get(message).subscribe(errMsg => {
|
||||
this.msgHandler.error(msg + ': ' + errMsg);
|
||||
});
|
||||
});
|
||||
return observableThrowError(message);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
fmtTime(time: number) {
|
||||
let date = new Date();
|
||||
return date.setTime(time * ONE_THOUSAND);
|
||||
}
|
||||
showDefaultIcon(event: any, vendor: string) {
|
||||
if (event && event.target) {
|
||||
if (KRAKEN === vendor) {
|
||||
event.target.src = KRAKEN_ICON;
|
||||
} else {
|
||||
event.target.src = DEFAULT_ICON;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
16
src/portal/src/app/distribution/distribution-interface.ts
Normal file
16
src/portal/src/app/distribution/distribution-interface.ts
Normal file
@ -0,0 +1,16 @@
|
||||
export class AuthMode {
|
||||
static NONE = 'NONE';
|
||||
static BASIC = 'BASIC';
|
||||
static OAUTH = 'OAUTH';
|
||||
static CUSTOM = 'CUSTOM';
|
||||
}
|
||||
|
||||
export enum PreheatingStatusEnum {
|
||||
// front status
|
||||
NOT_PREHEATED = 'NOT_PREHEATED',
|
||||
// back-end status
|
||||
PENDING = 'PENDING',
|
||||
RUNNING = 'RUNNING',
|
||||
SUCCESS = 'SUCCESS',
|
||||
FAIL = 'FAIL',
|
||||
}
|
@ -0,0 +1,238 @@
|
||||
<clr-modal
|
||||
[(clrModalOpen)]="opened"
|
||||
[clrModalClosable]="false"
|
||||
[clrModalStaticBackdrop]="true"
|
||||
>
|
||||
<h3 class="modal-title">{{ title | translate }}</h3>
|
||||
<div class="modal-body">
|
||||
<inline-alert class="modal-title"></inline-alert>
|
||||
<form #instanceForm="ngForm" class="clr-form clr-form-horizontal">
|
||||
<!-- 1. provider -->
|
||||
<clr-select-container>
|
||||
<label class="required">{{
|
||||
'DISTRIBUTION.PROVIDER' | translate
|
||||
}}</label>
|
||||
<select
|
||||
clrSelect
|
||||
name="provider"
|
||||
id="provider"
|
||||
[(ngModel)]="model.vendor"
|
||||
[disabled]="editingMode"
|
||||
required
|
||||
>
|
||||
<option class="display-none" value=""></option>
|
||||
<option
|
||||
*ngFor="let provider of providers"
|
||||
value="{{ provider.id }}"
|
||||
>{{ provider.name }}</option
|
||||
>
|
||||
</select>
|
||||
<clr-control-error>
|
||||
{{ 'TOOLTIP.ITEM_REQUIRED' | translate }}
|
||||
</clr-control-error>
|
||||
</clr-select-container>
|
||||
|
||||
<!-- 2. name -->
|
||||
<clr-input-container>
|
||||
<label class="required clr-control-label" for="name">{{
|
||||
'DISTRIBUTION.NAME' | translate
|
||||
}}</label>
|
||||
<input
|
||||
clrInput
|
||||
required
|
||||
type="text"
|
||||
id="name"
|
||||
autocomplete="off"
|
||||
placeholder="{{ 'DISTRIBUTION.SETUP.NAME_PLACEHOLDER' | translate }}"
|
||||
[(ngModel)]="model.name"
|
||||
name="name"
|
||||
[disabled]="editingMode"
|
||||
/>
|
||||
<clr-control-error>
|
||||
{{ 'TOOLTIP.ITEM_REQUIRED' | translate }}
|
||||
</clr-control-error>
|
||||
</clr-input-container>
|
||||
|
||||
<!-- 3. description -->
|
||||
<clr-textarea-container>
|
||||
<label>{{ 'DISTRIBUTION.DESCRIPTION' | translate }}</label>
|
||||
<textarea
|
||||
clrTextarea
|
||||
type="text"
|
||||
id="description"
|
||||
class="inputWidth"
|
||||
row="3"
|
||||
placeholder="{{
|
||||
'DISTRIBUTION.SETUP.DESCRIPTION_PLACEHOLDER' | translate
|
||||
}}"
|
||||
[(ngModel)]="model.description"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
></textarea>
|
||||
</clr-textarea-container>
|
||||
|
||||
<!-- 4. endpoint -->
|
||||
<clr-input-container>
|
||||
<label class="required clr-control-label" for="endpoint">{{
|
||||
'DISTRIBUTION.ENDPOINT' | translate
|
||||
}}</label>
|
||||
<input
|
||||
clrInput
|
||||
required
|
||||
pattern="^([hH][tT]{2}[pP]:\/\/|[hH][tT]{2}[pP][sS]:\/\/)(.*?)*$"
|
||||
type="text"
|
||||
id="endpoint"
|
||||
placeholder="{{
|
||||
'DISTRIBUTION.SETUP.ENDPOINT_PLACEHOLDER' | translate
|
||||
}}"
|
||||
[(ngModel)]="model.endpoint"
|
||||
name="endpoint"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<clr-control-error>{{
|
||||
'TOOLTIP.ENDPOINT_FORMAT' | translate
|
||||
}}</clr-control-error>
|
||||
</clr-input-container>
|
||||
|
||||
<!-- 5. enabled -->
|
||||
<clr-checkbox-container *ngIf="!editingMode">
|
||||
<label for="enabled">
|
||||
<span>{{ 'DISTRIBUTION.ENABLED' | translate }}</span>
|
||||
</label>
|
||||
<clr-checkbox-wrapper>
|
||||
<input
|
||||
clrCheckbox
|
||||
id="enabled"
|
||||
name="enabled"
|
||||
type="checkbox"
|
||||
[(ngModel)]="model.enabled"
|
||||
/>
|
||||
</clr-checkbox-wrapper>
|
||||
</clr-checkbox-container>
|
||||
|
||||
<!-- auth mode -->
|
||||
<clr-radio-container clrInline>
|
||||
<label>{{ 'DISTRIBUTION.AUTH_MODE' | translate }}</label>
|
||||
<clr-radio-wrapper>
|
||||
<input
|
||||
clrRadio
|
||||
type="radio"
|
||||
name="auth_mode"
|
||||
id="none_mode"
|
||||
value="NONE"
|
||||
[(ngModel)]="model.auth_mode"
|
||||
(change)="authModeChange()"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
/>
|
||||
<label for="none_mode">NONE</label>
|
||||
</clr-radio-wrapper>
|
||||
<clr-radio-wrapper>
|
||||
<input
|
||||
clrRadio
|
||||
type="radio"
|
||||
name="auth_mode"
|
||||
id="basic_mode"
|
||||
value="BASIC"
|
||||
[(ngModel)]="model.auth_mode"
|
||||
(change)="authModeChange()"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
/>
|
||||
<label for="basic_mode">Basic</label>
|
||||
</clr-radio-wrapper>
|
||||
<clr-radio-wrapper>
|
||||
<input
|
||||
clrRadio
|
||||
type="radio"
|
||||
name="auth_mode"
|
||||
id="token_mode"
|
||||
value="OAUTH"
|
||||
[(ngModel)]="model.auth_mode"
|
||||
(change)="authModeChange()"
|
||||
[ngModelOptions]="{ standalone: true }"
|
||||
/>
|
||||
<label for="token_mode">OAuth</label>
|
||||
</clr-radio-wrapper>
|
||||
</clr-radio-container>
|
||||
|
||||
<!-- auth data -->
|
||||
<span *ngIf="model.auth_mode == 'BASIC'">
|
||||
<clr-input-container>
|
||||
<label class="required clr-control-label" for="auth_data_username">{{
|
||||
'DISTRIBUTION.USERNAME' | translate
|
||||
}}</label>
|
||||
<input
|
||||
clrInput
|
||||
required
|
||||
type="text"
|
||||
id="auth_data_username"
|
||||
[(ngModel)]="authData['username']"
|
||||
placeholder="{{
|
||||
'DISTRIBUTION.SETUP.USERNAME_PLACEHOLDER' | translate
|
||||
}}"
|
||||
name="auth_data_username"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<clr-control-error>
|
||||
{{ 'TOOLTIP.ITEM_REQUIRED' | translate }}
|
||||
</clr-control-error>
|
||||
</clr-input-container>
|
||||
<clr-input-container>
|
||||
<label class="required clr-control-label" for="auth_data_password">{{
|
||||
'DISTRIBUTION.PASSWORD' | translate
|
||||
}}</label>
|
||||
<input
|
||||
clrInput
|
||||
required
|
||||
type="password"
|
||||
id="auth_data_password"
|
||||
[(ngModel)]="authData['password']"
|
||||
placeholder="{{
|
||||
'DISTRIBUTION.SETUP.PASSWORD_PLACEHOLDER' | translate
|
||||
}}"
|
||||
name="auth_data_password"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<clr-control-error>
|
||||
{{ 'TOOLTIP.ITEM_REQUIRED' | translate }}
|
||||
</clr-control-error>
|
||||
</clr-input-container>
|
||||
</span>
|
||||
<span *ngIf="model.auth_mode == 'OAUTH'">
|
||||
<clr-input-container>
|
||||
<label class="required clr-control-label" for="auth_data_token">{{
|
||||
'DISTRIBUTION.TOKEN' | translate
|
||||
}}</label>
|
||||
<input
|
||||
clrInput
|
||||
required
|
||||
type="text"
|
||||
id="auth_data_token"
|
||||
[(ngModel)]="authData['token']"
|
||||
placeholder="{{
|
||||
'DISTRIBUTION.SETUP.TOKEN_PLACEHOLDER' | translate
|
||||
}}"
|
||||
name="auth_data_token"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<clr-control-error>
|
||||
{{ 'TOOLTIP.ITEM_REQUIRED' | translate }}
|
||||
</clr-control-error>
|
||||
</clr-input-container>
|
||||
</span>
|
||||
<span *ngIf="model.auth_mode == 'NONE'"></span>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-outline" (click)="cancel()">
|
||||
{{ 'BUTTON.CANCEL' | translate }}
|
||||
</button>
|
||||
<button
|
||||
[clrLoading]="saveBtnState"
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
(click)="submit()"
|
||||
[disabled]="!isValid || !hasChangesForEdit()"
|
||||
>
|
||||
{{ 'BUTTON.OK' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</clr-modal>
|
@ -0,0 +1,3 @@
|
||||
.display-none {
|
||||
display: none;
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { ClarityModule } from '@clr/angular';
|
||||
import { SharedModule } from '../../shared/shared.module';
|
||||
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
|
||||
import { HttpClientTestingModule } from '@angular/common/http/testing';
|
||||
import { DistributionSetupModalComponent } from './distribution-setup-modal.component';
|
||||
import { PreheatService } from "../../../../ng-swagger-gen/services/preheat.service";
|
||||
import { Instance } from '../../../../ng-swagger-gen/models/instance';
|
||||
|
||||
describe('DistributionSetupModalComponent', () => {
|
||||
let component: DistributionSetupModalComponent;
|
||||
let fixture: ComponentFixture<DistributionSetupModalComponent>;
|
||||
|
||||
const instance1: Instance = {
|
||||
name: 'Test1',
|
||||
default: true,
|
||||
enabled: true,
|
||||
description: 'Test1',
|
||||
endpoint: 'http://test.com',
|
||||
id: 1,
|
||||
setup_timestamp: new Date().getTime(),
|
||||
auth_mode: 'NONE',
|
||||
vendor: 'kraken',
|
||||
status: 'Healthy'
|
||||
};
|
||||
|
||||
beforeEach(async(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
ClarityModule,
|
||||
TranslateModule,
|
||||
SharedModule,
|
||||
HttpClientTestingModule
|
||||
],
|
||||
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
||||
providers: [PreheatService],
|
||||
declarations: [DistributionSetupModalComponent]
|
||||
}).compileComponents();
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DistributionSetupModalComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should show "name is required"', async () => {
|
||||
fixture.autoDetectChanges();
|
||||
component._open();
|
||||
await fixture.whenStable();
|
||||
const nameInput = fixture.nativeElement.querySelector('#name');
|
||||
nameInput.value = "";
|
||||
nameInput.dispatchEvent(new Event('input'));
|
||||
nameInput.blur();
|
||||
nameInput.dispatchEvent(new Event('blur'));
|
||||
let el = fixture.nativeElement.querySelector('clr-control-error');
|
||||
expect(el).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should show "endpoint is required"', async () => {
|
||||
fixture.autoDetectChanges();
|
||||
component._open();
|
||||
await fixture.whenStable();
|
||||
const endpointInput = fixture.nativeElement.querySelector('#endpoint');
|
||||
endpointInput.value = "svn://test.com";
|
||||
endpointInput.dispatchEvent(new Event('input'));
|
||||
endpointInput.blur();
|
||||
endpointInput.dispatchEvent(new Event('blur'));
|
||||
let el = fixture.nativeElement.querySelector('clr-control-error');
|
||||
expect(el).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should be edit model', async () => {
|
||||
fixture.autoDetectChanges();
|
||||
component.openSetupModal(true, instance1);
|
||||
await fixture.whenStable();
|
||||
const nameInput = fixture.nativeElement.querySelector('#name');
|
||||
expect(nameInput.value).toEqual('Test1');
|
||||
});
|
||||
|
||||
it('should be valid', async () => {
|
||||
fixture.autoDetectChanges();
|
||||
component._open();
|
||||
await fixture.whenStable();
|
||||
component.model.vendor = 'kraken';
|
||||
const nameInput = fixture.nativeElement.querySelector('#name');
|
||||
nameInput.value = "test";
|
||||
nameInput.dispatchEvent(new Event('input'));
|
||||
const endpointInput = fixture.nativeElement.querySelector('#endpoint');
|
||||
endpointInput.value = "https://test.com";
|
||||
endpointInput.dispatchEvent(new Event('input'));
|
||||
await fixture.whenStable();
|
||||
expect(component.isValid).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,219 @@
|
||||
import { MessageHandlerService } from '../../shared/message-handler/message-handler.service';
|
||||
import { Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
|
||||
import { NgForm } from '@angular/forms';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { errorHandler } from '../../../lib/utils/shared/shared.utils';
|
||||
import { PreheatService } from "../../../../ng-swagger-gen/services/preheat.service";
|
||||
import { Instance } from "../../../../ng-swagger-gen/models/instance";
|
||||
import { AuthMode } from "../distribution-interface";
|
||||
import { clone } from '../../../lib/utils/utils';
|
||||
import { InlineAlertComponent } from "../../shared/inline-alert/inline-alert.component";
|
||||
import { ClrLoadingState } from "@clr/angular";
|
||||
import { Metadata } from '../../../../ng-swagger-gen/models/metadata';
|
||||
import { operateChanges, OperateInfo, OperationState } from '../../../lib/components/operation/operate';
|
||||
import { OperationService } from '../../../lib/components/operation/operation.service';
|
||||
|
||||
@Component({
|
||||
selector: 'dist-setup-modal',
|
||||
templateUrl: './distribution-setup-modal.component.html',
|
||||
styleUrls: ['./distribution-setup-modal.component.scss']
|
||||
})
|
||||
export class DistributionSetupModalComponent implements OnInit {
|
||||
@Input()
|
||||
providers: Metadata[] = [];
|
||||
model: Instance;
|
||||
originModelForEdit: Instance;
|
||||
opened: boolean = false;
|
||||
editingMode: boolean = false;
|
||||
authData: {[key: string]: any} = {};
|
||||
@ViewChild('instanceForm', { static: true }) instanceForm: NgForm;
|
||||
@ViewChild(InlineAlertComponent, { static: false }) inlineAlert: InlineAlertComponent;
|
||||
saveBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
|
||||
|
||||
@Output()
|
||||
refresh: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
constructor(
|
||||
private distributionService: PreheatService,
|
||||
private msgHandler: MessageHandlerService,
|
||||
private translate: TranslateService,
|
||||
private operationService: OperationService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
public get isValid(): boolean {
|
||||
return this.instanceForm && this.instanceForm.valid;
|
||||
}
|
||||
|
||||
get title(): string {
|
||||
return this.editingMode
|
||||
? 'DISTRIBUTION.EDIT_INSTANCE'
|
||||
: 'DISTRIBUTION.SETUP_NEW_INSTANCE';
|
||||
}
|
||||
|
||||
authModeChange() {
|
||||
if (this.editingMode && this.model.auth_mode === this.originModelForEdit.auth_mode) {
|
||||
this.authData = clone(this.originModelForEdit.auth_info);
|
||||
} else {
|
||||
switch (this.model.auth_mode) {
|
||||
case AuthMode.BASIC:
|
||||
this.authData = {
|
||||
password: '',
|
||||
username: ''
|
||||
};
|
||||
break;
|
||||
case AuthMode.OAUTH:
|
||||
this.authData = {
|
||||
token: ''
|
||||
};
|
||||
break;
|
||||
default:
|
||||
this.authData = null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_open() {
|
||||
this.inlineAlert.close();
|
||||
this.opened = true;
|
||||
}
|
||||
|
||||
_close() {
|
||||
this.opened = false;
|
||||
this.reset();
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.model = {
|
||||
name: '',
|
||||
endpoint: '',
|
||||
enabled: true,
|
||||
vendor: '',
|
||||
auth_mode: AuthMode.NONE,
|
||||
auth_info: this.authData
|
||||
};
|
||||
this.instanceForm.reset();
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this._close();
|
||||
}
|
||||
|
||||
submit() {
|
||||
if (this.editingMode) {
|
||||
const data: Instance = {
|
||||
endpoint: this.model.endpoint,
|
||||
enabled: this.model.enabled,
|
||||
description: this.model.description,
|
||||
auth_mode: this.model.auth_mode,
|
||||
auth_info: this.model.auth_info
|
||||
};
|
||||
const operMessageForEdit = new OperateInfo();
|
||||
operMessageForEdit.name = 'DISTRIBUTION.UPDATE_INSTANCE';
|
||||
operMessageForEdit.data.id = this.model.id;
|
||||
operMessageForEdit.state = OperationState.progressing;
|
||||
operMessageForEdit.data.name = this.model.name;
|
||||
this.operationService.publishInfo(operMessageForEdit);
|
||||
this.saveBtnState = ClrLoadingState.LOADING;
|
||||
this.distributionService.UpdateInstance({instanceId: this.model.id, propertySet: data
|
||||
}).subscribe(
|
||||
response => {
|
||||
this.translate.get('DISTRIBUTION.UPDATE_SUCCESS').subscribe(msg => {
|
||||
operateChanges(operMessageForEdit, OperationState.success);
|
||||
this.msgHandler.info(msg);
|
||||
});
|
||||
this.saveBtnState = ClrLoadingState.SUCCESS;
|
||||
this._close();
|
||||
this.refresh.emit();
|
||||
},
|
||||
err => {
|
||||
const message = errorHandler(err);
|
||||
this.translate.get('DISTRIBUTION.UPDATE_FAILED').subscribe(msg => {
|
||||
this.translate.get(message).subscribe(errMsg => {
|
||||
operateChanges(operMessageForEdit, OperationState.failure, msg);
|
||||
this.inlineAlert.showInlineError(msg + ': ' + errMsg);
|
||||
this.saveBtnState = ClrLoadingState.ERROR;
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
} else {
|
||||
const operMessage = new OperateInfo();
|
||||
operMessage.name = 'DISTRIBUTION.CREATE_INSTANCE';
|
||||
operMessage.state = OperationState.progressing;
|
||||
operMessage.data.name = this.model.name;
|
||||
this.operationService.publishInfo(operMessage);
|
||||
this.saveBtnState = ClrLoadingState.LOADING;
|
||||
if (this.model.auth_mode !== AuthMode.NONE) {
|
||||
this.model.auth_info = this.authData;
|
||||
} else {
|
||||
delete this.model.auth_info;
|
||||
}
|
||||
this.distributionService.CreateInstance({instance: this.model}).subscribe(
|
||||
response => {
|
||||
this.translate.get('DISTRIBUTION.CREATE_SUCCESS').subscribe(msg => {
|
||||
operateChanges(operMessage, OperationState.success);
|
||||
this.msgHandler.info(msg);
|
||||
});
|
||||
this.saveBtnState = ClrLoadingState.SUCCESS;
|
||||
this._close();
|
||||
this.refresh.emit();
|
||||
},
|
||||
err => {
|
||||
const message = errorHandler(err);
|
||||
this.translate.get('DISTRIBUTION.CREATE_FAILED').subscribe(msg => {
|
||||
this.translate.get(message).subscribe(errMsg => {
|
||||
operateChanges(operMessage, OperationState.failure, msg);
|
||||
this.inlineAlert.showInlineError(msg + ': ' + errMsg);
|
||||
this.saveBtnState = ClrLoadingState.ERROR;
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
openSetupModal(editingMode: boolean, data?: Instance): void {
|
||||
this.editingMode = editingMode;
|
||||
this._open();
|
||||
if (editingMode) {
|
||||
this.model = clone(data);
|
||||
this.originModelForEdit = clone(data);
|
||||
this.authData = this.model.auth_info || {};
|
||||
}
|
||||
}
|
||||
|
||||
hasChangesForEdit(): boolean {
|
||||
if ( this.editingMode) {
|
||||
if ( this.model.description !== this.originModelForEdit.description) {
|
||||
return true;
|
||||
}
|
||||
if ( this.model.endpoint !== this.originModelForEdit.endpoint) {
|
||||
return true;
|
||||
}
|
||||
if (this.model.auth_mode !== this.originModelForEdit.auth_mode) {
|
||||
return true;
|
||||
} else {
|
||||
if (this.model.auth_mode === AuthMode.BASIC) {
|
||||
if (this.originModelForEdit.auth_info['username'] !== this.authData['username']) {
|
||||
return true;
|
||||
}
|
||||
if (this.originModelForEdit.auth_info['password'] !== this.authData['password']) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (this.model.auth_mode === AuthMode.OAUTH) {
|
||||
if (this.originModelForEdit.auth_info['token'] !== this.authData['token']) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
14
src/portal/src/app/distribution/distribution.module.ts
Normal file
14
src/portal/src/app/distribution/distribution.module.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { DistributionInstancesComponent } from './distribution-instances/distribution-instances.component';
|
||||
import { DistributionSetupModalComponent } from './distribution-setup-modal/distribution-setup-modal.component';
|
||||
import { SharedModule } from '../shared/shared.module';
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, SharedModule],
|
||||
declarations: [
|
||||
DistributionSetupModalComponent,
|
||||
DistributionInstancesComponent
|
||||
],
|
||||
})
|
||||
export class DistributionModule {}
|
@ -63,6 +63,7 @@ import { ArtifactSummaryComponent } from "./project/repository/artifact/artifact
|
||||
import { ReplicationTasksComponent } from "../lib/components/replication/replication-tasks/replication-tasks.component";
|
||||
import { ReplicationTasksRoutingResolverService } from "./services/routing-resolvers/replication-tasks-routing-resolver.service";
|
||||
import { ArtifactDetailRoutingResolverService } from "./services/routing-resolvers/artifact-detail-routing-resolver.service";
|
||||
import { DistributionInstancesComponent } from './distribution/distribution-instances/distribution-instances.component';
|
||||
|
||||
const harborRoutes: Routes = [
|
||||
{ path: '', redirectTo: 'harbor', pathMatch: 'full' },
|
||||
@ -124,6 +125,11 @@ const harborRoutes: Routes = [
|
||||
canActivate: [SystemAdminGuard],
|
||||
canActivateChild: [SystemAdminGuard]
|
||||
},
|
||||
{
|
||||
path: 'distribution/instances',
|
||||
component: DistributionInstancesComponent,
|
||||
canActivate: [SystemAdminGuard]
|
||||
},
|
||||
{
|
||||
path: 'interrogation-services',
|
||||
component: InterrogationServicesComponent,
|
||||
|
@ -43,7 +43,8 @@ export const enum ConfirmationTargets {
|
||||
HELM_CHART,
|
||||
HELM_CHART_VERSION,
|
||||
WEBHOOK,
|
||||
SCANNER
|
||||
SCANNER,
|
||||
INSTANCE
|
||||
}
|
||||
|
||||
export const enum ActionType {
|
||||
|
@ -175,7 +175,11 @@
|
||||
"TASKS": "Tasks",
|
||||
"API_EXPLORER": "Api Explorer",
|
||||
"HARBOR_API_MANAGEMENT": "Harbor API V2.0",
|
||||
"HELM_API_MANAGEMENT": "Harbor API"
|
||||
"HELM_API_MANAGEMENT": "Harbor API",
|
||||
"DISTRIBUTIONS": {
|
||||
"NAME": "Distributions",
|
||||
"INSTANCES": "Instances"
|
||||
}
|
||||
},
|
||||
"USER": {
|
||||
"ADD_ACTION": "New User",
|
||||
@ -1425,6 +1429,71 @@
|
||||
"HELP_INFO_1": "The default scanner has been installed. To install other scanners refer to the ",
|
||||
"HELP_INFO_2": "documentation.",
|
||||
"NO_DEFAULT_SCANNER": "No default scanner"
|
||||
|
||||
},
|
||||
"DISTRIBUTION": {
|
||||
"FILTER_INSTANCE_PLACEHOLDER": "Filter instances",
|
||||
"FILTER_HISTORIES_PLACEHOLDER": "Filter histories",
|
||||
"ADD_ACTION": "NEW INSTANCE",
|
||||
"PREHEAT_ACTION": "Preheat",
|
||||
"EDIT_ACTION": "Edit",
|
||||
"ENABLE_ACTION": "Enable",
|
||||
"DISABLE_ACTION": "Disable",
|
||||
"DELETE_ACTION": "Delete",
|
||||
"NOT_FOUND": "Not found",
|
||||
"NAME": "Name",
|
||||
"ENDPOINT": "Endpoint",
|
||||
"STATUS": "Status",
|
||||
"ENABLED": "Enable",
|
||||
"SETUP_TIMESTAMP": "Setup Timestamp",
|
||||
"PROVIDER": "Provider",
|
||||
"DELETION_TITLE": "Confirm instance deletion",
|
||||
"DELETION_SUMMARY": "Do you want to delete instance(s) {{param}}?",
|
||||
"ENABLE_TITLE": "Enable instance(s)",
|
||||
"ENABLE_SUMMARY": "Do you want to enable instance(s) {{param}}?",
|
||||
"DISABLE_TITLE": "Disable instance(s)",
|
||||
"DISABLE_SUMMARY": "Do you want to disable instance(s) {{param}}?",
|
||||
"IMAGE": "Image",
|
||||
"START_TIME": "Start time",
|
||||
"FINISH_TIME": "Finish Time",
|
||||
"INSTANCE": "Instance",
|
||||
"HISTORIES": "Histories",
|
||||
"CREATE_SUCCESS": "Instance created successfully",
|
||||
"CREATE_FAILED": "Creating instance failed",
|
||||
"DELETED_SUCCESS": "Instance(s) deleted successfully",
|
||||
"DELETED_FAILED": "Deleting instance(s) failed",
|
||||
"ENABLE_SUCCESS": "Instance(s) enabled successfully",
|
||||
"ENABLE_FAILED": "Enabling instance(s) failed",
|
||||
"DISABLE_SUCCESS": "Instance(s) disabled successfully",
|
||||
"DISABLE_FAILED": "Disabling instance(s) failed",
|
||||
"UPDATE_SUCCESS": "Instance updated successfully",
|
||||
"UPDATE_FAILED": "Updating instance failed",
|
||||
"REQUEST_PREHEAT_SUCCESS": "Preheat request successfully",
|
||||
"REQUEST_PREHEAT_FAILED": "Preheat request failed",
|
||||
"DESCRIPTION": "description",
|
||||
"AUTH_MODE": "Auth Mode",
|
||||
"USERNAME": "Username",
|
||||
"PASSWORD": "Password",
|
||||
"TOKEN": "Token",
|
||||
"SETUP_NEW_INSTANCE": "Setup new instance",
|
||||
"EDIT_INSTANCE": "Edit instance",
|
||||
"SETUP": {
|
||||
"NAME_PLACEHOLDER": "Input instance's name",
|
||||
"DESCRIPTION_PLACEHOLDER": "Input instance's description",
|
||||
"ENDPOINT_PLACEHOLDER": "Input instance's endpoint ",
|
||||
"USERNAME_PLACEHOLDER": "Input username",
|
||||
"PASSWORD_PLACEHOLDER": "Input password",
|
||||
"TOKEN_PLACEHOLDER": "Input token"
|
||||
},
|
||||
"MAINTAINER": "Maintainer(s)",
|
||||
"SOURCE": "Source",
|
||||
"VERSION": "Version",
|
||||
"SET_AS_DEFAULT": "Set as default",
|
||||
"DELETE_INSTANCE": "Delete instance",
|
||||
"ENABLE_INSTANCE": "Enable instance",
|
||||
"DISABLE_INSTANCE": "Disable instance",
|
||||
"SET_DEFAULT_SUCCESS": "Set as default successfully",
|
||||
"SET_DEFAULT_FAILED": "Setting as default failed",
|
||||
"UPDATE_INSTANCE": "Update instance",
|
||||
"CREATE_INSTANCE": "Create instance"
|
||||
}
|
||||
}
|
||||
|
@ -175,7 +175,11 @@
|
||||
"TASKS": "Tasks",
|
||||
"API_EXPLORER": "Api Explorer",
|
||||
"HARBOR_API_MANAGEMENT": "Harbor API V2.0",
|
||||
"HELM_API_MANAGEMENT": "Harbor API"
|
||||
"HELM_API_MANAGEMENT": "Harbor API",
|
||||
"DISTRIBUTIONS": {
|
||||
"NAME": "Distributions",
|
||||
"INSTANCES": "Instances"
|
||||
}
|
||||
},
|
||||
"USER": {
|
||||
"ADD_ACTION": "New User",
|
||||
@ -1423,5 +1427,71 @@
|
||||
"HELP_INFO_1": "The default scanner has been installed. To install other scanners refer to the ",
|
||||
"HELP_INFO_2": "documentation.",
|
||||
"NO_DEFAULT_SCANNER": "No default scanner"
|
||||
},
|
||||
"DISTRIBUTION": {
|
||||
"FILTER_INSTANCE_PLACEHOLDER": "Filter instances",
|
||||
"FILTER_HISTORIES_PLACEHOLDER": "Filter histories",
|
||||
"ADD_ACTION": "NEW INSTANCE",
|
||||
"PREHEAT_ACTION": "Preheat",
|
||||
"EDIT_ACTION": "Edit",
|
||||
"ENABLE_ACTION": "Enable",
|
||||
"DISABLE_ACTION": "Disable",
|
||||
"DELETE_ACTION": "Delete",
|
||||
"NOT_FOUND": "Not found",
|
||||
"NAME": "Name",
|
||||
"ENDPOINT": "Endpoint",
|
||||
"STATUS": "Status",
|
||||
"ENABLED": "Enable",
|
||||
"SETUP_TIMESTAMP": "Setup Timestamp",
|
||||
"PROVIDER": "Provider",
|
||||
"DELETION_TITLE": "Confirm instance deletion",
|
||||
"DELETION_SUMMARY": "Do you want to delete instance(s) {{param}}?",
|
||||
"ENABLE_TITLE": "Enable instance(s)",
|
||||
"ENABLE_SUMMARY": "Do you want to enable instance(s) {{param}}?",
|
||||
"DISABLE_TITLE": "Disable instance(s)",
|
||||
"DISABLE_SUMMARY": "Do you want to disable instance(s) {{param}}?",
|
||||
"IMAGE": "Image",
|
||||
"START_TIME": "Start time",
|
||||
"FINISH_TIME": "Finish Time",
|
||||
"INSTANCE": "Instance",
|
||||
"HISTORIES": "Histories",
|
||||
"CREATE_SUCCESS": "Instance created successfully",
|
||||
"CREATE_FAILED": "Creating instance failed",
|
||||
"DELETED_SUCCESS": "Instance(s) deleted successfully",
|
||||
"DELETED_FAILED": "Deleting instance(s) failed",
|
||||
"ENABLE_SUCCESS": "Instance(s) enabled successfully",
|
||||
"ENABLE_FAILED": "Enabling instance(s) failed",
|
||||
"DISABLE_SUCCESS": "Instance(s) disabled successfully",
|
||||
"DISABLE_FAILED": "Disabling instance(s) failed",
|
||||
"UPDATE_SUCCESS": "Instance updated successfully",
|
||||
"UPDATE_FAILED": "Updating instance failed",
|
||||
"REQUEST_PREHEAT_SUCCESS": "Preheat request successfully",
|
||||
"REQUEST_PREHEAT_FAILED": "Preheat request failed",
|
||||
"DESCRIPTION": "description",
|
||||
"AUTH_MODE": "Auth Mode",
|
||||
"USERNAME": "Username",
|
||||
"PASSWORD": "Password",
|
||||
"TOKEN": "Token",
|
||||
"SETUP_NEW_INSTANCE": "Setup new instance",
|
||||
"EDIT_INSTANCE": "Edit instance",
|
||||
"SETUP": {
|
||||
"NAME_PLACEHOLDER": "Input instance's name",
|
||||
"DESCRIPTION_PLACEHOLDER": "Input instance's description",
|
||||
"ENDPOINT_PLACEHOLDER": "Input instance's endpoint ",
|
||||
"USERNAME_PLACEHOLDER": "Input username",
|
||||
"PASSWORD_PLACEHOLDER": "Input password",
|
||||
"TOKEN_PLACEHOLDER": "Input token"
|
||||
},
|
||||
"MAINTAINER": "Maintainer(s)",
|
||||
"SOURCE": "Source",
|
||||
"VERSION": "Version",
|
||||
"SET_AS_DEFAULT": "Set as default",
|
||||
"DELETE_INSTANCE": "Delete instance",
|
||||
"ENABLE_INSTANCE": "Enable instance",
|
||||
"DISABLE_INSTANCE": "Disable instance",
|
||||
"SET_DEFAULT_SUCCESS": "Set as default successfully",
|
||||
"SET_DEFAULT_FAILED": "Setting as default failed",
|
||||
"UPDATE_INSTANCE": "Update instance",
|
||||
"CREATE_INSTANCE": "Create instance"
|
||||
}
|
||||
}
|
||||
|
@ -169,7 +169,11 @@
|
||||
"TASKS": "Tasks",
|
||||
"API_EXPLORER": "Api Explorer",
|
||||
"HARBOR_API_MANAGEMENT": "Harbor API V2.0",
|
||||
"HELM_API_MANAGEMENT": "Harbor API"
|
||||
"HELM_API_MANAGEMENT": "Harbor API",
|
||||
"DISTRIBUTIONS": {
|
||||
"NAME": "Distributions",
|
||||
"INSTANCES": "Instances"
|
||||
}
|
||||
},
|
||||
"USER": {
|
||||
"ADD_ACTION": "UTILISATEUR",
|
||||
@ -1393,5 +1397,71 @@
|
||||
"HELP_INFO_1": "The default scanner has been installed. To install other scanners refer to the ",
|
||||
"HELP_INFO_2": "documentation.",
|
||||
"NO_DEFAULT_SCANNER": "No default scanner"
|
||||
},
|
||||
"DISTRIBUTION": {
|
||||
"FILTER_INSTANCE_PLACEHOLDER": "Filter instances",
|
||||
"FILTER_HISTORIES_PLACEHOLDER": "Filter histories",
|
||||
"ADD_ACTION": "NEW INSTANCE",
|
||||
"PREHEAT_ACTION": "Preheat",
|
||||
"EDIT_ACTION": "Edit",
|
||||
"ENABLE_ACTION": "Enable",
|
||||
"DISABLE_ACTION": "Disable",
|
||||
"DELETE_ACTION": "Delete",
|
||||
"NOT_FOUND": "Not found",
|
||||
"NAME": "Name",
|
||||
"ENDPOINT": "Endpoint",
|
||||
"STATUS": "Status",
|
||||
"ENABLED": "Enable",
|
||||
"SETUP_TIMESTAMP": "Setup Timestamp",
|
||||
"PROVIDER": "Provider",
|
||||
"DELETION_TITLE": "Confirm instance deletion",
|
||||
"DELETION_SUMMARY": "Do you want to delete instance(s) {{param}}?",
|
||||
"ENABLE_TITLE": "Enable instance(s)",
|
||||
"ENABLE_SUMMARY": "Do you want to enable instance(s) {{param}}?",
|
||||
"DISABLE_TITLE": "Disable instance(s)",
|
||||
"DISABLE_SUMMARY": "Do you want to disable instance(s) {{param}}?",
|
||||
"IMAGE": "Image",
|
||||
"START_TIME": "Start time",
|
||||
"FINISH_TIME": "Finish Time",
|
||||
"INSTANCE": "Instance",
|
||||
"HISTORIES": "Histories",
|
||||
"CREATE_SUCCESS": "Instance created successfully",
|
||||
"CREATE_FAILED": "Creating instance failed",
|
||||
"DELETED_SUCCESS": "Instance(s) deleted successfully",
|
||||
"DELETED_FAILED": "Deleting instance(s) failed",
|
||||
"ENABLE_SUCCESS": "Instance(s) enabled successfully",
|
||||
"ENABLE_FAILED": "Enabling instance(s) failed",
|
||||
"DISABLE_SUCCESS": "Instance(s) disabled successfully",
|
||||
"DISABLE_FAILED": "Disabling instance(s) failed",
|
||||
"UPDATE_SUCCESS": "Instance updated successfully",
|
||||
"UPDATE_FAILED": "Updating instance failed",
|
||||
"REQUEST_PREHEAT_SUCCESS": "Preheat request successfully",
|
||||
"REQUEST_PREHEAT_FAILED": "Preheat request failed",
|
||||
"DESCRIPTION": "description",
|
||||
"AUTH_MODE": "Auth Mode",
|
||||
"USERNAME": "Username",
|
||||
"PASSWORD": "Password",
|
||||
"TOKEN": "Token",
|
||||
"SETUP_NEW_INSTANCE": "Setup new instance",
|
||||
"EDIT_INSTANCE": "Edit instance",
|
||||
"SETUP": {
|
||||
"NAME_PLACEHOLDER": "Input instance's name",
|
||||
"DESCRIPTION_PLACEHOLDER": "Input instance's description",
|
||||
"ENDPOINT_PLACEHOLDER": "Input instance's endpoint ",
|
||||
"USERNAME_PLACEHOLDER": "Input username",
|
||||
"PASSWORD_PLACEHOLDER": "Input password",
|
||||
"TOKEN_PLACEHOLDER": "Input token"
|
||||
},
|
||||
"MAINTAINER": "Maintainer(s)",
|
||||
"SOURCE": "Source",
|
||||
"VERSION": "Version",
|
||||
"SET_AS_DEFAULT": "Set as default",
|
||||
"DELETE_INSTANCE": "Delete instance",
|
||||
"ENABLE_INSTANCE": "Enable instance",
|
||||
"DISABLE_INSTANCE": "Disable instance",
|
||||
"SET_DEFAULT_SUCCESS": "Set as default successfully",
|
||||
"SET_DEFAULT_FAILED": "Setting as default failed",
|
||||
"UPDATE_INSTANCE": "Update instance",
|
||||
"CREATE_INSTANCE": "Create instance"
|
||||
}
|
||||
}
|
||||
|
@ -173,7 +173,11 @@
|
||||
"TASKS": "Tasks",
|
||||
"API_EXPLORER": "Api Explorer",
|
||||
"HARBOR_API_MANAGEMENT": "Harbor API V2.0",
|
||||
"HELM_API_MANAGEMENT": "Harbor API"
|
||||
"HELM_API_MANAGEMENT": "Harbor API",
|
||||
"DISTRIBUTIONS": {
|
||||
"NAME": "Distributions",
|
||||
"INSTANCES": "Instances"
|
||||
}
|
||||
},
|
||||
"USER": {
|
||||
"ADD_ACTION": "Novo Usuário",
|
||||
@ -1421,6 +1425,72 @@
|
||||
"HELP_INFO_1": "The default scanner has been installed. To install other scanners refer to the ",
|
||||
"HELP_INFO_2": "documentation.",
|
||||
"NO_DEFAULT_SCANNER": "No default scanner"
|
||||
},
|
||||
"DISTRIBUTION": {
|
||||
"FILTER_INSTANCE_PLACEHOLDER": "Filter instances",
|
||||
"FILTER_HISTORIES_PLACEHOLDER": "Filter histories",
|
||||
"ADD_ACTION": "NEW INSTANCE",
|
||||
"PREHEAT_ACTION": "Preheat",
|
||||
"EDIT_ACTION": "Edit",
|
||||
"ENABLE_ACTION": "Enable",
|
||||
"DISABLE_ACTION": "Disable",
|
||||
"DELETE_ACTION": "Delete",
|
||||
"NOT_FOUND": "Not found",
|
||||
"NAME": "Name",
|
||||
"ENDPOINT": "Endpoint",
|
||||
"STATUS": "Status",
|
||||
"ENABLED": "Enable",
|
||||
"SETUP_TIMESTAMP": "Setup Timestamp",
|
||||
"PROVIDER": "Provider",
|
||||
"DELETION_TITLE": "Confirm instance deletion",
|
||||
"DELETION_SUMMARY": "Do you want to delete instance(s) {{param}}?",
|
||||
"ENABLE_TITLE": "Enable instance(s)",
|
||||
"ENABLE_SUMMARY": "Do you want to enable instance(s) {{param}}?",
|
||||
"DISABLE_TITLE": "Disable instance(s)",
|
||||
"DISABLE_SUMMARY": "Do you want to disable instance(s) {{param}}?",
|
||||
"IMAGE": "Image",
|
||||
"START_TIME": "Start time",
|
||||
"FINISH_TIME": "Finish Time",
|
||||
"INSTANCE": "Instance",
|
||||
"HISTORIES": "Histories",
|
||||
"CREATE_SUCCESS": "Instance created successfully",
|
||||
"CREATE_FAILED": "Creating instance failed",
|
||||
"DELETED_SUCCESS": "Instance(s) deleted successfully",
|
||||
"DELETED_FAILED": "Deleting instance(s) failed",
|
||||
"ENABLE_SUCCESS": "Instance(s) enabled successfully",
|
||||
"ENABLE_FAILED": "Enabling instance(s) failed",
|
||||
"DISABLE_SUCCESS": "Instance(s) disabled successfully",
|
||||
"DISABLE_FAILED": "Disabling instance(s) failed",
|
||||
"UPDATE_SUCCESS": "Instance updated successfully",
|
||||
"UPDATE_FAILED": "Updating instance failed",
|
||||
"REQUEST_PREHEAT_SUCCESS": "Preheat request successfully",
|
||||
"REQUEST_PREHEAT_FAILED": "Preheat request failed",
|
||||
"DESCRIPTION": "description",
|
||||
"AUTH_MODE": "Auth Mode",
|
||||
"USERNAME": "Username",
|
||||
"PASSWORD": "Password",
|
||||
"TOKEN": "Token",
|
||||
"SETUP_NEW_INSTANCE": "Setup new instance",
|
||||
"EDIT_INSTANCE": "Edit instance",
|
||||
"SETUP": {
|
||||
"NAME_PLACEHOLDER": "Input instance's name",
|
||||
"DESCRIPTION_PLACEHOLDER": "Input instance's description",
|
||||
"ENDPOINT_PLACEHOLDER": "Input instance's endpoint ",
|
||||
"USERNAME_PLACEHOLDER": "Input username",
|
||||
"PASSWORD_PLACEHOLDER": "Input password",
|
||||
"TOKEN_PLACEHOLDER": "Input token"
|
||||
},
|
||||
"MAINTAINER": "Maintainer(s)",
|
||||
"SOURCE": "Source",
|
||||
"VERSION": "Version",
|
||||
"SET_AS_DEFAULT": "Set as default",
|
||||
"DELETE_INSTANCE": "Delete instance",
|
||||
"ENABLE_INSTANCE": "Enable instance",
|
||||
"DISABLE_INSTANCE": "Disable instance",
|
||||
"SET_DEFAULT_SUCCESS": "Set as default successfully",
|
||||
"SET_DEFAULT_FAILED": "Setting as default failed",
|
||||
"UPDATE_INSTANCE": "Update instance",
|
||||
"CREATE_INSTANCE": "Create instance"
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -175,7 +175,11 @@
|
||||
"TASKS": "Görevler",
|
||||
"API_EXPLORER": "Api Explorer",
|
||||
"HARBOR_API_MANAGEMENT": "Harbor API V2.0",
|
||||
"HELM_API_MANAGEMENT": "Harbor API"
|
||||
"HELM_API_MANAGEMENT": "Harbor API",
|
||||
"DISTRIBUTIONS": {
|
||||
"NAME": "Distributions",
|
||||
"INSTANCES": "Instances"
|
||||
}
|
||||
},
|
||||
"USER": {
|
||||
"ADD_ACTION": "Yeni Kullanıcı",
|
||||
@ -1425,5 +1429,71 @@
|
||||
"HELP_INFO_1": "The default scanner has been installed. To install other scanners refer to the ",
|
||||
"HELP_INFO_2": "documentation.",
|
||||
"NO_DEFAULT_SCANNER": "No default scanner"
|
||||
},
|
||||
"DISTRIBUTION": {
|
||||
"FILTER_INSTANCE_PLACEHOLDER": "Filter instances",
|
||||
"FILTER_HISTORIES_PLACEHOLDER": "Filter histories",
|
||||
"ADD_ACTION": "NEW INSTANCE",
|
||||
"PREHEAT_ACTION": "Preheat",
|
||||
"EDIT_ACTION": "Edit",
|
||||
"ENABLE_ACTION": "Enable",
|
||||
"DISABLE_ACTION": "Disable",
|
||||
"DELETE_ACTION": "Delete",
|
||||
"NOT_FOUND": "Not found",
|
||||
"NAME": "Name",
|
||||
"ENDPOINT": "Endpoint",
|
||||
"STATUS": "Status",
|
||||
"ENABLED": "Enable",
|
||||
"SETUP_TIMESTAMP": "Setup Timestamp",
|
||||
"PROVIDER": "Provider",
|
||||
"DELETION_TITLE": "Confirm instance deletion",
|
||||
"DELETION_SUMMARY": "Do you want to delete instance(s) {{param}}?",
|
||||
"ENABLE_TITLE": "Enable instance(s)",
|
||||
"ENABLE_SUMMARY": "Do you want to enable instance(s) {{param}}?",
|
||||
"DISABLE_TITLE": "Disable instance(s)",
|
||||
"DISABLE_SUMMARY": "Do you want to disable instance(s) {{param}}?",
|
||||
"IMAGE": "Image",
|
||||
"START_TIME": "Start time",
|
||||
"FINISH_TIME": "Finish Time",
|
||||
"INSTANCE": "Instance",
|
||||
"HISTORIES": "Histories",
|
||||
"CREATE_SUCCESS": "Instance created successfully",
|
||||
"CREATE_FAILED": "Creating instance failed",
|
||||
"DELETED_SUCCESS": "Instance(s) deleted successfully",
|
||||
"DELETED_FAILED": "Deleting instance(s) failed",
|
||||
"ENABLE_SUCCESS": "Instance(s) enabled successfully",
|
||||
"ENABLE_FAILED": "Enabling instance(s) failed",
|
||||
"DISABLE_SUCCESS": "Instance(s) disabled successfully",
|
||||
"DISABLE_FAILED": "Disabling instance(s) failed",
|
||||
"UPDATE_SUCCESS": "Instance updated successfully",
|
||||
"UPDATE_FAILED": "Updating instance failed",
|
||||
"REQUEST_PREHEAT_SUCCESS": "Preheat request successfully",
|
||||
"REQUEST_PREHEAT_FAILED": "Preheat request failed",
|
||||
"DESCRIPTION": "description",
|
||||
"AUTH_MODE": "Auth Mode",
|
||||
"USERNAME": "Username",
|
||||
"PASSWORD": "Password",
|
||||
"TOKEN": "Token",
|
||||
"SETUP_NEW_INSTANCE": "Setup new instance",
|
||||
"EDIT_INSTANCE": "Edit instance",
|
||||
"SETUP": {
|
||||
"NAME_PLACEHOLDER": "Input instance's name",
|
||||
"DESCRIPTION_PLACEHOLDER": "Input instance's description",
|
||||
"ENDPOINT_PLACEHOLDER": "Input instance's endpoint ",
|
||||
"USERNAME_PLACEHOLDER": "Input username",
|
||||
"PASSWORD_PLACEHOLDER": "Input password",
|
||||
"TOKEN_PLACEHOLDER": "Input token"
|
||||
},
|
||||
"MAINTAINER": "Maintainer(s)",
|
||||
"SOURCE": "Source",
|
||||
"VERSION": "Version",
|
||||
"SET_AS_DEFAULT": "Set as default",
|
||||
"DELETE_INSTANCE": "Delete instance",
|
||||
"ENABLE_INSTANCE": "Enable instance",
|
||||
"DISABLE_INSTANCE": "Disable instance",
|
||||
"SET_DEFAULT_SUCCESS": "Set as default successfully",
|
||||
"SET_DEFAULT_FAILED": "Setting as default failed",
|
||||
"UPDATE_INSTANCE": "Update instance",
|
||||
"CREATE_INSTANCE": "Create instance"
|
||||
}
|
||||
}
|
||||
|
@ -174,7 +174,11 @@
|
||||
"TASKS": "任务",
|
||||
"API_EXPLORER": "API控制中心",
|
||||
"HARBOR_API_MANAGEMENT": "Harbor Api V2.0",
|
||||
"HELM_API_MANAGEMENT": "Harbor Api"
|
||||
"HELM_API_MANAGEMENT": "Harbor Api",
|
||||
"DISTRIBUTIONS": {
|
||||
"NAME": "分布式分发",
|
||||
"INSTANCES": "实例"
|
||||
}
|
||||
},
|
||||
"USER": {
|
||||
"ADD_ACTION": "创建用户",
|
||||
@ -1422,5 +1426,71 @@
|
||||
"HELP_INFO_1": "默认扫描器已安装。获取扫描器安装帮助,请查看",
|
||||
"HELP_INFO_2": "文档。",
|
||||
"NO_DEFAULT_SCANNER": "未配置默认扫描器"
|
||||
},
|
||||
"DISTRIBUTION": {
|
||||
"FILTER_INSTANCE_PLACEHOLDER": "过滤实例",
|
||||
"FILTER_HISTORIES_PLACEHOLDER": "过滤历史记录",
|
||||
"ADD_ACTION": "新建实例",
|
||||
"PREHEAT_ACTION": "预热",
|
||||
"EDIT_ACTION": "编辑",
|
||||
"ENABLE_ACTION": "启用",
|
||||
"DISABLE_ACTION": "禁用",
|
||||
"DELETE_ACTION": "删除",
|
||||
"NOT_FOUND": "未发现任何记录",
|
||||
"NAME": "名称",
|
||||
"ENDPOINT": "端点",
|
||||
"STATUS": "状态",
|
||||
"ENABLED": "启用",
|
||||
"SETUP_TIMESTAMP": "设置时间",
|
||||
"PROVIDER": "供应商",
|
||||
"DELETION_TITLE": "删除实例",
|
||||
"DELETION_SUMMARY": "你确认删除实例 {{param}}?",
|
||||
"ENABLE_TITLE": "启用实例",
|
||||
"ENABLE_SUMMARY": "你确认启用实例 {{param}}?",
|
||||
"DISABLE_TITLE": "禁用实例",
|
||||
"DISABLE_SUMMARY": "你确认禁用实例 {{param}}?",
|
||||
"IMAGE": "镜像",
|
||||
"START_TIME": "开始时间",
|
||||
"FINISH_TIME": "完成时间",
|
||||
"INSTANCE": "实例",
|
||||
"HISTORIES": "历史记录",
|
||||
"CREATE_SUCCESS": "添加实例成功",
|
||||
"CREATE_FAILED": "添加实例失败",
|
||||
"DELETED_SUCCESS": "删除实例成功",
|
||||
"DELETED_FAILED": "删除实例失败",
|
||||
"ENABLE_SUCCESS": "启用实例成功",
|
||||
"ENABLE_FAILED": "启用实例失败",
|
||||
"DISABLE_SUCCESS": "禁用实例成功",
|
||||
"DISABLE_FAILED": "禁用实例失败",
|
||||
"UPDATE_SUCCESS": "更新实例成功",
|
||||
"UPDATE_FAILED": "更新实例失败",
|
||||
"REQUEST_PREHEAT_SUCCESS": "请求预热成功",
|
||||
"REQUEST_PREHEAT_FAILED": "请求预热失败",
|
||||
"DESCRIPTION": "描述",
|
||||
"AUTH_MODE": "认证模式",
|
||||
"USERNAME": "用户名",
|
||||
"PASSWORD": "密码",
|
||||
"TOKEN": "令牌",
|
||||
"SETUP_NEW_INSTANCE": "设置新实例",
|
||||
"EDIT_INSTANCE": "编辑实例",
|
||||
"SETUP": {
|
||||
"NAME_PLACEHOLDER": "输入实例名称",
|
||||
"DESCRIPTION_PLACEHOLDER": "输入实例描述",
|
||||
"ENDPOINT_PLACEHOLDER": "输入实例 endpoint ",
|
||||
"USERNAME_PLACEHOLDER": "输入认证用户名",
|
||||
"PASSWORD_PLACEHOLDER": "输入认证密码",
|
||||
"TOKEN_PLACEHOLDER": "输入认证令牌"
|
||||
},
|
||||
"MAINTAINER": "维护人员",
|
||||
"SOURCE": "资源",
|
||||
"VERSION": "版本",
|
||||
"SET_AS_DEFAULT": "设为默认",
|
||||
"DELETE_INSTANCE": "删除实例",
|
||||
"ENABLE_INSTANCE": "启用实例",
|
||||
"DISABLE_INSTANCE": "禁用实例",
|
||||
"SET_DEFAULT_SUCCESS": "设为默认成功",
|
||||
"SET_DEFAULT_FAILED": "设为默认失败",
|
||||
"UPDATE_INSTANCE": "更新实例",
|
||||
"CREATE_INSTANCE": "新建实例"
|
||||
}
|
||||
}
|
||||
|
@ -172,7 +172,11 @@
|
||||
},
|
||||
"LOGS": "日誌",
|
||||
"TASKS": "任務",
|
||||
"API_EXPLORER": "API控制中心"
|
||||
"API_EXPLORER": "API控制中心",
|
||||
"DISTRIBUTIONS": {
|
||||
"NAME": "Distributions",
|
||||
"INSTANCES": "Instances"
|
||||
}
|
||||
},
|
||||
"USER":{
|
||||
"ADD_ACTION": "創建用戶",
|
||||
@ -1409,5 +1413,71 @@
|
||||
"HELP_INFO_1": "默認掃描器已安装。獲取掃描變安装幫助,請查看",
|
||||
"HELP_INFO_2": "文檔。",
|
||||
"NO_DEFAULT_SCANNER": "未配置默認掃描器"
|
||||
},
|
||||
"DISTRIBUTION": {
|
||||
"FILTER_INSTANCE_PLACEHOLDER": "Filter instances",
|
||||
"FILTER_HISTORIES_PLACEHOLDER": "Filter histories",
|
||||
"ADD_ACTION": "NEW INSTANCE",
|
||||
"PREHEAT_ACTION": "Preheat",
|
||||
"EDIT_ACTION": "Edit",
|
||||
"ENABLE_ACTION": "Enable",
|
||||
"DISABLE_ACTION": "Disable",
|
||||
"DELETE_ACTION": "Delete",
|
||||
"NOT_FOUND": "Not found",
|
||||
"NAME": "Name",
|
||||
"ENDPOINT": "Endpoint",
|
||||
"STATUS": "Status",
|
||||
"ENABLED": "Enable",
|
||||
"SETUP_TIMESTAMP": "Setup Timestamp",
|
||||
"PROVIDER": "Provider",
|
||||
"DELETION_TITLE": "Confirm instance deletion",
|
||||
"DELETION_SUMMARY": "Do you want to delete instance(s) {{param}}?",
|
||||
"ENABLE_TITLE": "Enable instance(s)",
|
||||
"ENABLE_SUMMARY": "Do you want to enable instance(s) {{param}}?",
|
||||
"DISABLE_TITLE": "Disable instance(s)",
|
||||
"DISABLE_SUMMARY": "Do you want to disable instance(s) {{param}}?",
|
||||
"IMAGE": "Image",
|
||||
"START_TIME": "Start time",
|
||||
"FINISH_TIME": "Finish Time",
|
||||
"INSTANCE": "Instance",
|
||||
"HISTORIES": "Histories",
|
||||
"CREATE_SUCCESS": "Instance created successfully",
|
||||
"CREATE_FAILED": "Creating instance failed",
|
||||
"DELETED_SUCCESS": "Instance(s) deleted successfully",
|
||||
"DELETED_FAILED": "Deleting instance(s) failed",
|
||||
"ENABLE_SUCCESS": "Instance(s) enabled successfully",
|
||||
"ENABLE_FAILED": "Enabling instance(s) failed",
|
||||
"DISABLE_SUCCESS": "Instance(s) disabled successfully",
|
||||
"DISABLE_FAILED": "Disabling instance(s) failed",
|
||||
"UPDATE_SUCCESS": "Instance updated successfully",
|
||||
"UPDATE_FAILED": "Updating instance failed",
|
||||
"REQUEST_PREHEAT_SUCCESS": "Preheat request successfully",
|
||||
"REQUEST_PREHEAT_FAILED": "Preheat request failed",
|
||||
"DESCRIPTION": "description",
|
||||
"AUTH_MODE": "Auth Mode",
|
||||
"USERNAME": "Username",
|
||||
"PASSWORD": "Password",
|
||||
"TOKEN": "Token",
|
||||
"SETUP_NEW_INSTANCE": "Setup new instance",
|
||||
"EDIT_INSTANCE": "Edit instance",
|
||||
"SETUP": {
|
||||
"NAME_PLACEHOLDER": "Input instance's name",
|
||||
"DESCRIPTION_PLACEHOLDER": "Input instance's description",
|
||||
"ENDPOINT_PLACEHOLDER": "Input instance's endpoint ",
|
||||
"USERNAME_PLACEHOLDER": "Input username",
|
||||
"PASSWORD_PLACEHOLDER": "Input password",
|
||||
"TOKEN_PLACEHOLDER": "Input token"
|
||||
},
|
||||
"MAINTAINER": "Maintainer(s)",
|
||||
"SOURCE": "Source",
|
||||
"VERSION": "Version",
|
||||
"SET_AS_DEFAULT": "Set as default",
|
||||
"DELETE_INSTANCE": "Delete instance",
|
||||
"ENABLE_INSTANCE": "Enable instance",
|
||||
"DISABLE_INSTANCE": "Disable instance",
|
||||
"SET_DEFAULT_SUCCESS": "Set as default successfully",
|
||||
"SET_DEFAULT_FAILED": "Setting as default failed",
|
||||
"UPDATE_INSTANCE": "Update instance",
|
||||
"CREATE_INSTANCE": "Create instance"
|
||||
}
|
||||
}
|
||||
|
1
src/portal/src/images/kraken-logo-color.svg
Normal file
1
src/portal/src/images/kraken-logo-color.svg
Normal file
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 6.7 KiB |
Loading…
Reference in New Issue
Block a user