Remove helm chart UI (#18099)

1.Remove all helm chart v2 related code

Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
Shijun Sun 2023-02-08 16:47:03 +08:00 committed by GitHub
parent 9d30955607
commit ff9dcd5483
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
97 changed files with 28 additions and 5354 deletions

View File

@ -118,13 +118,6 @@ export class SignInComponent implements AfterViewChecked, OnInit {
// App title
public get appTitle(): string {
if (
this.appConfigService.getConfig() &&
this.appConfigService.getConfig().with_admiral
) {
return 'APP_TITLE.VIC';
}
return 'APP_TITLE.VMW_HARBOR';
}

View File

@ -50,10 +50,6 @@ export class AppComponent {
this.initLanguage();
// Override page title
let key: string = 'APP_TITLE.HARBOR';
if (this.appConfigService.isIntegrationMode()) {
key = 'APP_TITLE.REG';
}
translate.get(key).subscribe((res: string) => {
const customSkinData: CustomStyle =
this.skinableConfig.getSkinConfig();

View File

@ -161,17 +161,6 @@ const routes: Routes = [
projectResolver: ProjectRoutingResolver,
},
},
{
path: 'projects/:id/helm-charts',
canActivate: [MemberGuard],
resolve: {
projectResolver: ProjectRoutingResolver,
},
loadChildren: () =>
import(
'./project/helm-chart/helm-chart-detail/helm-chart-detail.module'
).then(m => m.HelmChartListModule),
},
],
},
];

View File

@ -129,7 +129,6 @@
{{ 'SIDE_NAV.DISTRIBUTIONS.NAME' | translate }}
</a>
<a
*ngIf="!withAdmiral"
clrVerticalNavLink
routerLink="/harbor/labels"
routerLinkActive="active">

View File

@ -242,9 +242,6 @@ export class HarborShellComponent implements OnInit, OnDestroy {
this.session.getCurrentUser().has_admin_role
);
}
public get withAdmiral(): boolean {
return this.appConfigService.getConfig().with_admiral;
}
// Open modal dialog
openModal(event: ModalEvent): void {
switch (event.modalName) {

View File

@ -24,7 +24,7 @@
type="button"
routerLink="security"
routerLinkActive="active">
{{ 'HELM_CHART.SECURITY' | translate }}
{{ 'CONFIG.SECURITY' | translate }}
</button>
</li>
<li role="presentation" class="nav-item">

View File

@ -53,9 +53,6 @@
<clr-dg-column>{{ 'PROJECT.ROLE' | translate }}</clr-dg-column>
<clr-dg-column>{{ 'PROJECT.TYPE' | translate }}</clr-dg-column>
<clr-dg-column>{{ 'PROJECT.REPO_COUNT' | translate }}</clr-dg-column>
<clr-dg-column *ngIf="withChartMuseum">{{
'PROJECT.CHART_COUNT' | translate
}}</clr-dg-column>
<clr-dg-column [clrDgSortBy]="'creation_time'">{{
'PROJECT.CREATION_TIME' | translate
}}</clr-dg-column>
@ -83,7 +80,6 @@
projectTypeMap[p.registry_id ? 1 : 0] | translate
}}</clr-dg-cell>
<clr-dg-cell>{{ p.repo_count }}</clr-dg-cell>
<clr-dg-cell *ngIf="withChartMuseum">{{ p.chart_count }}</clr-dg-cell>
<clr-dg-cell>{{
p.creation_time | harborDatetime: 'short'
}}</clr-dg-cell>

View File

@ -132,11 +132,6 @@ export class ListProjectComponent implements OnDestroy {
}
return false;
}
get withChartMuseum(): boolean {
return this.appConfigService.getConfig().with_chartmuseum;
}
public get isSystemAdmin(): boolean {
let account = this.session.getCurrentUser();
return account != null && account.has_admin_role;

View File

@ -101,12 +101,10 @@ export class CreateEditEndpointComponent
this.endpointService.getAdapters().subscribe(
adapters => {
this.adapterList = adapters || [];
if (!this.appConfigService.getConfig().with_chartmuseum) {
// disable helm-hub
for (let i = 0; i < this.adapterList.length; i++) {
if (this.adapterList[i] === HELM_HUB) {
this.adapterList.splice(i, 1);
}
// disable helm-hub
for (let i = 0; i < this.adapterList.length; i++) {
if (this.adapterList[i] === HELM_HUB) {
this.adapterList.splice(i, 1);
}
}
},

View File

@ -246,12 +246,7 @@
?.values
"
value="{{ value }}">
{{ value
}}{{
value === 'chart'
? ' (chartmuseum)'
: ''
}}
{{ value }}
</option>
</select>
</div>
@ -610,7 +605,6 @@
| translate
}}
</div>
<div>{{ 'REPLICATION.NOTE' | translate }}</div>
</clr-tooltip-content>
</clr-tooltip>
</div>

View File

@ -111,7 +111,6 @@ export class ReplicationComponent implements OnInit, OnDestroy {
@Input() projectId: number | string;
@Input() projectName: string;
@Input() isSystemAdmin: boolean;
@Input() withAdmiral: boolean;
@Input() withReplicationJob: boolean;
@Input() hasCreateReplicationPermission: boolean;
@Input() hasUpdateReplicationPermission: boolean;

View File

@ -3,7 +3,6 @@
<hbr-replication
[withReplicationJob]="true"
[isSystemAdmin]="isSystemAdmin"
[withAdmiral]="withAdmiral"
(goToRegistry)="goRegistry()"
[hasCreateReplicationPermission]="true"
[hasUpdateReplicationPermission]="true"

View File

@ -80,8 +80,4 @@ export class TotalReplicationPageComponent implements OnInit, OnDestroy {
let account = this.session.getCurrentUser();
return account != null && account.has_admin_role;
}
get withAdmiral(): boolean {
return this.appConfigService.getConfig().with_admiral;
}
}

View File

@ -28,8 +28,6 @@ export enum PermissionsKinds {
export enum Resource {
REPO = 'repository',
HELM_CHART = 'helm-chart',
HELM_CHART_VERSION = 'helm-chart-version',
ARTIFACT = 'artifact',
}
@ -116,31 +114,6 @@ export const INITIAL_ACCESSES: FrontAccess[] = [
action: 'stop',
checked: true,
},
{
resource: 'helm-chart',
action: 'read',
checked: true,
},
{
resource: 'helm-chart-version',
action: 'create',
checked: true,
},
{
resource: 'helm-chart-version',
action: 'delete',
checked: true,
},
{
resource: 'helm-chart-version-label',
action: 'create',
checked: true,
},
{
resource: 'helm-chart-version-label',
action: 'delete',
checked: true,
},
];
export const ACTION_RESOURCE_I18N_MAP = {
@ -151,11 +124,8 @@ export const ACTION_RESOURCE_I18N_MAP = {
delete: 'SYSTEM_ROBOT.DELETE',
repository: 'SYSTEM_ROBOT.REPOSITORY',
artifact: 'SYSTEM_ROBOT.ARTIFACT',
'helm-chart': 'SYSTEM_ROBOT.HELM',
'helm-chart-version': 'SYSTEM_ROBOT.HELM_VERSION',
tag: 'REPLICATION.TAG',
'artifact-label': 'SYSTEM_ROBOT.ARTIFACT_LABEL',
'helm-chart-version-label': 'SYSTEM_ROBOT.HELM_LABEL',
scan: 'SYSTEM_ROBOT.SCAN',
'scanner-pull': 'SYSTEM_ROBOT.SCANNER_PULL',
stop: 'SYSTEM_ROBOT.STOP',

View File

@ -1,18 +0,0 @@
<div>
<div class="breadcrumb">
<span class="back-icon"><</span>
<a (click)="gotoProjectList()">{{ 'SIDE_NAV.PROJECTS' | translate }}</a>
<span class="back-icon"><</span>
<a (click)="gotoChartList()">{{ projectName }}</a>
<span class="back-icon"><</span>
<a (click)="gotoChartVersion()">{{
'HELM_CHART.CHARTVERSIONS' | translate
}}</a>
</div>
<hbr-chart-detail
[projectId]="projectId"
[project]="project"
[chartName]="chartName"
[chartVersion]="chartVersion"
[roleName]="roleName"></hbr-chart-detail>
</div>

View File

@ -1,16 +0,0 @@
.breadcrumb a {
text-decoration: none;
cursor: pointer;
color: #007cbb;
font-size: 16px;
margin: 5px;
}
.breadcrumb {
margin-bottom: 0.5rem;
}
.back-icon {
color: #007cbb;
font-size: 16px;
}

View File

@ -1,60 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { SessionService } from '../../../../shared/services/session.service';
import { of } from 'rxjs';
import { HelmChartDetailComponent } from './chart-detail.component';
import { SharedTestingModule } from '../../../../shared/shared.module';
describe('ChartDetailComponent', () => {
let component: HelmChartDetailComponent;
let fixture: ComponentFixture<HelmChartDetailComponent>;
let fakeRouter = null;
let fakeSessionService = {
getCurrentUser: function () {
return { has_admin_role: true };
},
};
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [HelmChartDetailComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
imports: [SharedTestingModule],
providers: [
{
provide: ActivatedRoute,
useValue: {
paramMap: of({ get: key => 'value' }),
snapshot: {
parent: {
data: {
projectResolver: {
role_name: 'admin',
},
},
params: { id: 1 },
},
params: {
chart: 'chart',
version: 1.0,
},
},
},
},
{ provide: Router, useValue: fakeRouter },
{ provide: SessionService, useValue: fakeSessionService },
],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(HelmChartDetailComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,59 +0,0 @@
import { ActivatedRoute, Router } from '@angular/router';
import { Component, OnInit } from '@angular/core';
import { Project } from '../../project';
import { SessionService } from '../../../../shared/services/session.service';
import { SessionUser } from '../../../../shared/entities/session-user';
@Component({
selector: 'project-chart-detail',
templateUrl: './chart-detail.component.html',
styleUrls: ['./chart-detail.component.scss'],
})
export class HelmChartDetailComponent implements OnInit {
projectId: number | string;
project: Project;
projectName: string;
chartName: string;
chartVersion: string;
currentUser: SessionUser;
hasProjectAdminRole: boolean;
roleName: string;
constructor(
private route: ActivatedRoute,
private router: Router,
private session: SessionService
) {}
ngOnInit() {
// Get projectId from router-guard params snapshot.
this.projectId = +this.route.snapshot.parent.params['id'];
this.chartName = this.route.snapshot.params['chart'];
this.chartVersion = this.route.snapshot.params['version'];
// Get current user from registered resolver.
this.currentUser = this.session.getCurrentUser();
let resolverData = this.route.snapshot.parent.data;
if (resolverData) {
this.project = <Project>resolverData['projectResolver'];
this.roleName = this.project.role_name;
this.projectName = this.project.name;
this.hasProjectAdminRole = this.project.has_project_admin_role;
}
}
gotoProjectList() {
this.router.navigateByUrl('/harbor/projects');
}
gotoChartList() {
this.router.navigateByUrl(
`/harbor/projects/${this.projectId}/helm-charts`
);
}
gotoChartVersion() {
this.router.navigateByUrl(
`/harbor/projects/${this.projectId}/helm-charts/${this.chartName}/versions`
);
}
}

View File

@ -1,22 +0,0 @@
<div class="row flex-items-xs-center dep-container">
<div class="col-md-12">
<table class="table">
<thead>
<tr>
<th class="left">{{ 'HELM_CHART.NAME' | translate }}</th>
<th class="left">{{ 'HELM_CHART.VERSION' | translate }}</th>
<th class="left">{{ 'HELM_CHART.REPO' | translate }}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let dep of dependencies">
<td class="left">{{ dep.name }}</td>
<td class="left">{{ dep.version }}</td>
<td class="left">
<a href="{{ dep.repository }}">{{ dep.repository }}</a>
</td>
</tr>
</tbody>
</table>
</div>
</div>

View File

@ -1,25 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ChartDetailDependencyComponent } from './chart-detail-dependency.component';
import { SharedTestingModule } from '../../../../../shared/shared.module';
describe('ChartDetailDependencyComponent', () => {
let component: ChartDetailDependencyComponent;
let fixture: ComponentFixture<ChartDetailDependencyComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [SharedTestingModule],
declarations: [ChartDetailDependencyComponent],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ChartDetailDependencyComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,15 +0,0 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
import { HelmChartDependency } from '../helm-chart.interface.service';
@Component({
selector: 'hbr-chart-detail-dependency',
templateUrl: './chart-detail-dependency.component.html',
styleUrls: ['./chart-detail-dependency.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ChartDetailDependencyComponent {
@Input() dependencies: HelmChartDependency;
constructor() {}
}

View File

@ -1,224 +0,0 @@
<div class="row">
<div class="col-md-12">
<p>{{ summary.description }}</p>
</div>
</div>
<div class="row content-wrapper">
<div class="col-md-8 md-container pl-1">
<div
*ngIf="readme"
class="md-div"
[innerHTML]="readme | markdown"></div>
<div *ngIf="!readme">{{ 'HELM_CHART.NO_README' | translate }}</div>
</div>
<div class="col-md-4 summary-container mt-1">
<div class="col-md-12 content-group">
<div>
<label>{{ 'HELM_CHART.OVERVIEW' | translate }}</label>
</div>
<table class="table">
<tbody>
<tr>
<td class="left">
{{ 'HELM_CHART.HOME' | translate }}
</td>
<td class="left text-wrapper">
<a href="{{ summary.home }}">{{ summary.home }}</a>
</td>
</tr>
<tr *ngFor="let src of summary.sources; let i = index">
<td class="left" *ngIf="i === 0">
{{ 'HELM_CHART.SRC_REPO' | translate }}
</td>
<td class="left" *ngIf="i !== 0"></td>
<td class="left text-wrapper">
<a href="{{ src }}">{{ src }}</a>
</td>
</tr>
<tr>
<td class="left">
{{ 'HELM_CHART.CREATED' | translate }}
</td>
<td class="left">
{{ summary.created | harborDatetime }}
</td>
</tr>
<tr
*ngFor="
let maintainer of summary.maintainers;
let i = index
">
<td class="left" *ngIf="i === 0">
{{ 'HELM_CHART.MAINTAINERS' | translate }}
</td>
<td class="left" *ngIf="i !== 0"></td>
<td class="left">
<a href="mailto:{{ maintainer.email }}">{{
maintainer.name
}}</a>
</td>
</tr>
<tr>
<td class="left">
{{ 'HELM_CHART.APP_VERSION' | translate }}
</td>
<td class="left">{{ summary.appVersion }}</td>
</tr>
</tbody>
</table>
</div>
<div class="col-md-12 content-group">
<div>
<label>{{ 'HELM_CHART.COMMAND' | translate }}</label>
</div>
<table class="table">
<tbody>
<tr>
<td class="left cmd-title">
{{ 'HELM_CHART.ADD_REPO' | translate }}
</td>
<td class="left cmd-content">
<input
class="cmd-content clr-input"
type="text"
[(ngModel)]="addCMD"
#addCMDInput
readonly />
</td>
<td class="left">
<span>
<clr-icon
shape="copy"
size="24"
[class.is-success]="isCopied('add')"
[ngxClipboard]="addCMDInput"
(cbOnSuccess)="
onCopySuccess($event, 'add')
"></clr-icon>
</span>
</td>
</tr>
<tr>
<td class="left cmd-title">
{{ 'HELM_CHART.INSTALL_CHART' | translate }}
</td>
<td class="left">
<input
class="cmd-content clr-input"
type="text"
[(ngModel)]="installCMD"
#installCMDInput
readonly />
</td>
<td class="left">
<span>
<clr-icon
shape="copy"
size="24"
[class.is-success]="isCopied('install')"
[ngxClipboard]="installCMDInput"
(cbOnSuccess)="
onCopySuccess($event, 'install')
"></clr-icon>
</span>
</td>
</tr>
<tr *ngIf="prov_ready">
<td class="left cmd-title">
{{ 'HELM_CHART.VERIFY_CHART' | translate }}
</td>
<td class="left">
<input
class="cmd-content clr-input"
type="text"
[(ngModel)]="verifyCMD"
#verifyCMDInput
readonly />
</td>
<td class="left">
<span>
<clr-icon
shape="copy"
size="24"
[class.is-success]="isCopied('verify')"
[ngxClipboard]="verifyCMDInput"
(cbOnSuccess)="
onCopySuccess($event, 'verify')
"></clr-icon>
</span>
</td>
</tr>
</tbody>
</table>
</div>
<div class="col-md-12 content-group">
<div>
<label>{{ 'HELM_CHART.SECURITY' | translate }}</label>
</div>
<table class="table">
<tbody>
<tr>
<td class="left">
{{ 'HELM_CHART.PROV_FILE' | translate }}
</td>
<div
*ngIf="
prov_ready;
then signedContent;
else unsignedContent
"></div>
<ng-template #signedContent>
<td class="left">
<span class="content-icon">
<clr-icon
shape="shield-check"
class="is-success"></clr-icon> </span
>&nbsp;<a
href="javascript:void(0)"
(click)="downloadChart()"
>{{ 'HELM_CHART.READY' | translate }}</a
>
</td>
</ng-template>
<ng-template #unsignedContent>
<td class="left">
<span class="content-icon">
<clr-icon
shape="shield-x"
class="is-error"></clr-icon> </span
>&nbsp;{{ 'HELM_CHART.NOT_READY' | translate }}
</td>
</ng-template>
</tr>
</tbody>
</table>
</div>
<div class="col-md-12 content-group">
<div>
<label>{{ 'HELM_CHART.LABELS' | translate }}</label>
</div>
<table class="table">
<tbody>
<tr>
<td class="left">
{{ 'HELM_CHART.LABELS' | translate }}
</td>
<td class="left">{{ labels?.length }}</td>
</tr>
<tr *ngIf="labels?.length > 0">
<td></td>
<td class="left">
<hbr-label-piece
*ngFor="let label of labels"
[label]="label"
[labelWidth]="90">
</hbr-label-piece>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>

View File

@ -1,45 +0,0 @@
.content-wrapper {
margin-top: 20px;
padding: 0 0 0 15px;
.md-container {
border: solid 1px;
}
.summary-container {
padding: 0;
table {
margin-top: 0.5rem;
}
.content-group {
margin-bottom: 30px;
}
.content-icon {
margin-right: 6px;
}
.text-wrapper {
word-break: break-all;
}
}
.cmd-title {
white-space: nowrap;
vertical-align: middle;
}
.cmd-content {
width: 100%;
}
}
@mixin align-text-mixin($values...) {
@each $var in $values {
&[align="$var"] {
text-align: $var;
}
}
}

View File

@ -1,59 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TranslateService } from '@ngx-translate/core';
import { ChartDetailSummaryComponent } from './chart-detail-summary.component';
import { CUSTOM_ELEMENTS_SCHEMA, SecurityContext } from '@angular/core';
import { MarkdownModule, MarkedOptions } from 'ngx-markdown';
import { HelmChartService } from '../helm-chart.service';
import { ErrorHandler } from '../../../../../shared/units/error-handler';
import { MessageHandlerService } from '../../../../../shared/services/message-handler.service';
import { SharedTestingModule } from '../../../../../shared/shared.module';
describe('ChartDetailSummaryComponent', () => {
let component: ChartDetailSummaryComponent;
let fixture: ComponentFixture<ChartDetailSummaryComponent>;
const mockHelmChartService = {
downloadChart: function () {},
};
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
SharedTestingModule,
MarkdownModule.forRoot({ sanitize: SecurityContext.HTML }),
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [ChartDetailSummaryComponent],
providers: [
TranslateService,
{ provide: MarkedOptions, useValue: {} },
{ provide: ErrorHandler, useValue: MessageHandlerService },
{ provide: HelmChartService, useValue: mockHelmChartService },
],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ChartDetailSummaryComponent);
component = fixture.componentInstance;
component.summary = {
name: 'string',
home: 'string',
sources: [],
version: 'string',
description: 'string',
keywords: [],
maintainers: [],
engine: 'string',
icon: 'string',
appVersion: 'string',
urls: [],
created: new Date().toDateString(),
digest: 'string',
};
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,87 +0,0 @@
import {
ChangeDetectionStrategy,
Component,
Input,
OnInit,
} from '@angular/core';
import {
HelmChartMetaData,
HelmChartSecurity,
} from '../helm-chart.interface.service';
import { HelmChartService } from '../helm-chart.service';
import { Label } from '../../../../../shared/services';
import { ErrorHandler } from '../../../../../shared/units/error-handler';
import { downloadFile } from '../../../../../shared/units/utils';
@Component({
selector: 'hbr-chart-detail-summary',
templateUrl: './chart-detail-summary.component.html',
styleUrls: ['./chart-detail-summary.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ChartDetailSummaryComponent implements OnInit {
@Input() summary: HelmChartMetaData;
@Input() security: HelmChartSecurity;
@Input() repoURL: string;
@Input() projectName: string;
@Input() chartName: string;
@Input() chartVersion: string;
@Input() readme: string;
@Input() labels: Label[];
copiedCMD = '';
addCMD: string;
installCMD: string;
verifyCMD: string;
constructor(
private errorHandler: ErrorHandler,
private helmChartService: HelmChartService
) {}
ngOnInit(): void {
this.addCMD = `helm repo add --ca-file <ca file> --cert-file <cert file> --key-file <key file> \
--username <username> --password <password> <repo name> ${this.repoURL}/chartrepo/${this.projectName}`;
this.installCMD = `helm install --ca-file <ca file> --cert-file <cert file> --key-file <key file> \
--username=<username> --password=<password> --version ${this.chartVersion} <repo name>/${this.chartName}`;
this.verifyCMD = `helm verify --keyring <key path> ${this.chartName}-${this.chartVersion}.tgz`;
}
isCopied(cmd: string) {
return this.copiedCMD === cmd;
}
onCopySuccess(e: Event, cmd: string) {
this.copiedCMD = cmd;
}
public get prov_ready() {
return (
this.security &&
this.security.signature &&
this.security.signature.signed
);
}
downloadChart() {
if (
!this.summary ||
!this.summary.urls ||
this.summary.urls.length < 1
) {
return;
}
let filename = `${this.summary.urls[0]}.prov`;
this.helmChartService
.downloadChart(this.projectName, filename)
.subscribe(
res => {
downloadFile(res);
},
error => {
this.errorHandler.error(error);
}
);
}
}

View File

@ -1,54 +0,0 @@
<div class="row flex-items-xs-between values-header">
<div *ngIf="valueMode" class="title-container">
<label>{{ 'HELM_CHART.SHOW_KV' | translate }}</label>
</div>
<div *ngIf="!valueMode" class="title-container">
<label>{{ 'HELM_CHART.SHOW_YAML' | translate }}</label>
</div>
<div class="switch-container">
<span
class="card-btn"
(click)="showYamlFile(false)"
(mouseenter)="mouseEnter('value')"
(mouseleave)="mouseLeave('value')">
<clr-icon
size="24"
shape="view-list"
title="list values"
[ngClass]="{
'is-highlight': isValueMode || isHovering('value')
}"></clr-icon>
</span>
<span
class="list-btn"
(click)="showYamlFile(true)"
(mouseenter)="mouseEnter('yaml')"
(mouseleave)="mouseLeave('yaml')">
<clr-icon
size="24"
shape="file"
title="yaml file"
[ngClass]="{
'is-highlight': !isValueMode || isHovering('yaml')
}"></clr-icon>
</span>
</div>
</div>
<div class="row value-container">
<div class="col-xs-8" *ngIf="valueMode">
<table class="table">
<tbody>
<tr *ngFor="let key of objKeys(values)">
<td class="left">{{ key }}</td>
<td class="left">{{ values[key] }}</td>
</tr>
</tbody>
</table>
</div>
<div class="col-xs-8" *ngIf="!valueMode">
<div
class="yaml-container"
[innerHTML]="yaml | language: 'yaml' | markdown"></div>
</div>
</div>

View File

@ -1,24 +0,0 @@
.value-container {
::ng-deep pre {
min-height: fit-content;
max-height: none !important;
}
}
.values-header {
margin-top: 12px;
.title-container {
margin-left: 15px;
}
.switch-container {
margin-right: 15px;
}
}
/* stylelint-disable */
pre {
max-height: max-content;
padding-left: 21px;
}

View File

@ -1,42 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { ChartDetailValueComponent } from './chart-detail-value.component';
import { CUSTOM_ELEMENTS_SCHEMA, SecurityContext } from '@angular/core';
import { ClarityModule } from '@clr/angular';
import { FormsModule } from '@angular/forms';
import { MarkdownModule, MarkedOptions } from 'ngx-markdown';
import { BrowserModule } from '@angular/platform-browser';
describe('ChartDetailValueComponent', () => {
let component: ChartDetailValueComponent;
let fixture: ComponentFixture<ChartDetailValueComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [
TranslateModule.forRoot(),
ClarityModule,
FormsModule,
BrowserModule,
MarkdownModule.forRoot({ sanitize: SecurityContext.HTML }),
],
declarations: [ChartDetailValueComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
providers: [
TranslateService,
{ provide: MarkedOptions, useValue: {} },
],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ChartDetailValueComponent);
component = fixture.componentInstance;
component.yaml = 'rfrf';
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,53 +0,0 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core';
@Component({
selector: 'hbr-chart-detail-value',
templateUrl: './chart-detail-value.component.html',
styleUrls: ['./chart-detail-value.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ChartDetailValueComponent {
@Input() values;
@Input() yaml;
// Default set to yaml file
valueMode = false;
valueHover = false;
yamlHover = true;
objKeys = Object.keys;
constructor() {}
public get isValueMode() {
return this.valueMode;
}
isHovering(view: string) {
if (view === 'value') {
return this.valueHover;
} else {
return this.yamlHover;
}
}
showYamlFile(showYaml: boolean) {
this.valueMode = !showYaml;
}
mouseEnter(mode: string) {
if (mode === 'value') {
this.valueHover = true;
} else {
this.yamlHover = true;
}
}
mouseLeave(mode: string) {
if (mode === 'value') {
this.valueHover = false;
} else {
this.yamlHover = false;
}
}
}

View File

@ -1,68 +0,0 @@
<div>
<div class="flex-container">
<div class="title-container">
<div class="chart-name">
{{ chartNameWithVersion | translate }}
</div>
<div>
{{ roleName | translate }}
</div>
</div>
<div>
<button class="btn btn-secondary" (click)="downloadChart()">
{{ 'HELM_CHART.DOWNLOAD' | translate }}
</button>
</div>
</div>
<span *ngIf="loading" class="spinner spinner-lg detail-loading">
Loading...
</span>
<div *ngIf="!loading && isChartExist">
<clr-tabs>
<clr-tab>
<button clrTabLink id="summary-link">
{{ 'HELM_CHART.SUMMARY' | translate }}
</button>
<clr-tab-content id="summary-content" *clrIfActive>
<hbr-chart-detail-summary
[summary]="chartDetail.metadata"
[chartName]="chartName"
[repoURL]="repoURL"
[projectName]="project.name"
[chartVersion]="chartVersion"
[security]="chartDetail.security"
[readme]="chartDetail.files['README.md']"
[labels]="
chartDetail.labels
"></hbr-chart-detail-summary>
</clr-tab-content>
</clr-tab>
<clr-tab>
<button clrTabLink id="depend-link">
{{ 'HELM_CHART.DEPENDENCIES' | translate }}
</button>
<clr-tab-content id="depend-content">
<hbr-chart-detail-dependency
[dependencies]="
chartDetail.dependencies
"></hbr-chart-detail-dependency>
</clr-tab-content>
</clr-tab>
<clr-tab>
<button clrTabLink id="value-link">
{{ 'HELM_CHART.VALUES' | translate }}
</button>
<clr-tab-content id="value-content">
<hbr-chart-detail-value
[values]="chartDetail.values"
[yaml]="
chartDetail.files['values.yaml']
"></hbr-chart-detail-value>
</clr-tab-content>
</clr-tab>
</clr-tabs>
</div>
<div *ngIf="!loading && !isChartExist">
<h6>{{ 'HELM_CHART.NO_DETAIL' | translate }}</h6>
</div>
</div>

View File

@ -1,28 +0,0 @@
@import "../../../../../shared/mixin";
.title-container {
display: flex;
.chart-name {
border-right: 1px solid gray;
font-size: 27px;
font-weight: normal;
padding-right: 9px;
margin-right: 9px;
}
}
.detail-loading {
@include absolute-center;
}
.flex-container {
display: flex;
-webkit-display:flex;
justify-content: space-between;
.title-container {
display: flex;
align-items:center;
}
}

View File

@ -1,143 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { ChartDetailComponent } from './chart-detail.component';
import { ClarityModule } from '@clr/angular';
import { FormsModule } from '@angular/forms';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { HelmChartService } from '../helm-chart.service';
import { of } from 'rxjs';
import { ErrorHandler } from '../../../../../shared/units/error-handler';
import { SystemInfoService } from '../../../../../shared/services';
describe('ChartDetailComponent', () => {
let component: ChartDetailComponent;
let fixture: ComponentFixture<ChartDetailComponent>;
const mockErrorHandler = {
error: function () {},
};
const mockSystemInfoService = {
getSystemInfo: function () {
return of({
with_notary: false,
with_admiral: false,
admiral_endpoint: '',
auth_mode: 'oidc_auth',
registry_url: 'nightly-oidc.harbor.io',
external_url: 'https://nightly-oidc.harbor.io',
project_creation_restriction: 'everyone',
self_registration: false,
has_ca_root: false,
harbor_version: 'dev',
registry_storage_provider_name: 'filesystem',
read_only: false,
with_chartmuseum: true,
notification_enable: true,
});
},
};
const mockHelmChartService = {
getChartDetail: function () {
return of({
metadata: {
name: 'harbor',
home: 'https://github.com/vmware/harbor',
sources: [
'https://github.com/vmware/harbor/tree/master/contrib/helm/harbor',
],
version: '0.2.0',
description: 'Ane',
keywords: ['vmware', 'docker', 'registry', 'harbor'],
maintainers: [
{
name: 'Jessde Hu',
email: 'huh@qq.com',
},
{
name: 'paulczar',
email: 'username@qq.com',
},
],
engine: '',
icon: 'ht',
appVersion: '1.5.0',
urls: [''],
created: '201940492141Z',
digest: '',
},
dependencies: [
{
name: 'redis',
version: '3.2.5',
repository: '',
},
],
values: {
'adminserver.image.pullPolicy': 'IfNotPresent',
},
files: {
'README.md': '',
'values.yaml': '',
},
security: {
signature: {
signed: false,
prov_file: '',
},
},
labels: [],
});
},
downloadChart: function () {},
};
beforeEach(() => {
TestBed.configureTestingModule({
imports: [TranslateModule.forRoot(), ClarityModule, FormsModule],
declarations: [ChartDetailComponent],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
providers: [
TranslateService,
{ provide: ErrorHandler, useValue: mockErrorHandler },
{ provide: SystemInfoService, useValue: mockSystemInfoService },
{ provide: HelmChartService, useValue: mockHelmChartService },
],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ChartDetailComponent);
component = fixture.componentInstance;
component.chartName = 'chart';
component.chartVersion = 'chart-version';
component.project = {
project_id: 1,
owner_id: 1,
name: 'library',
creation_time: new Date(),
creation_time_str: '123',
update_time: new Date(),
deleted: 1,
owner_name: '',
togglable: true,
current_user_role_id: 1,
has_project_admin_role: true,
is_member: true,
role_name: 'maintainer',
repo_count: 0,
chart_count: 1,
registry_id: 0,
metadata: {
public: 'true',
enable_content_trust: 'string',
prevent_vul: 'string',
severity: 'string',
auto_scan: true,
retention_id: 1,
},
};
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,109 +0,0 @@
import { Component, Input, OnInit } from '@angular/core';
import { Project } from '../../../project';
import { HelmChartService } from '../helm-chart.service';
import { HelmChartDetail } from '../helm-chart.interface.service';
import { finalize } from 'rxjs/operators';
import { SystemInfo, SystemInfoService } from '../../../../../shared/services';
import { ErrorHandler } from '../../../../../shared/units/error-handler';
import { downloadFile } from '../../../../../shared/units/utils';
@Component({
selector: 'hbr-chart-detail',
templateUrl: './chart-detail.component.html',
styleUrls: ['./chart-detail.component.scss'],
})
export class ChartDetailComponent implements OnInit {
@Input() projectId: number;
@Input() project: Project;
@Input() chartName: string;
@Input() chartVersion: string;
@Input() roleName: string;
@Input() hasSignedIn: boolean;
@Input() hasProjectAdminRole: boolean;
loading = true;
isMember = false;
chartDetail: HelmChartDetail;
systemInfo: SystemInfo;
repoURL = '';
constructor(
private errorHandler: ErrorHandler,
private systemInfoService: SystemInfoService,
private helmChartService: HelmChartService
) {}
ngOnInit(): void {
this.systemInfoService.getSystemInfo().subscribe(
systemInfo => {
this.systemInfo = systemInfo;
if (this.systemInfo.external_url) {
this.repoURL = `${this.systemInfo.external_url}`;
} else {
let scheme = 'http://';
if (this.systemInfo.has_ca_root) {
scheme = 'https://';
}
this.repoURL = `${scheme}${this.systemInfo.registry_url}`;
}
},
error => this.errorHandler.error(error)
);
this.refresh();
}
public get chartNameWithVersion() {
return `${this.chartName}:${this.chartVersion}`;
}
public get isChartExist() {
return !!this.chartDetail;
}
refresh() {
this.loading = true;
this.helmChartService
.getChartDetail(
this.project.name,
this.chartName,
this.chartVersion
)
.pipe(
finalize(() => {
this.loading = false;
})
)
.subscribe(
chartDetail => {
this.chartDetail = chartDetail;
},
err => {
this.errorHandler.error(err);
}
);
}
downloadChart() {
if (
!this.chartDetail ||
!this.chartDetail.metadata ||
!this.chartDetail.metadata.urls ||
this.chartDetail.metadata.urls.length < 1
) {
return;
}
let filename = this.chartDetail.metadata.urls[0];
this.helmChartService
.downloadChart(this.project.name, filename)
.subscribe(
res => {
downloadFile(res);
},
error => {
this.errorHandler.error(error);
}
);
}
}

View File

@ -1,51 +0,0 @@
// Copyright Project Harbor Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { NgModule } from '@angular/core';
import { LabelFilterComponent } from './label-filter/label-filter.component';
import { LabelMarkerComponent } from './label-marker/label-marker.component';
import { ListChartVersionsComponent } from './list-chart-versions/list-chart-versions.component';
import { ChartVersionComponent } from './list-chart-versions/helm-chart-versions-detail/helm-chart-version.component';
import { ChartDetailDependencyComponent } from './chart-detail/chart-detail-dependency.component';
import { ChartDetailSummaryComponent } from './chart-detail/chart-detail-summary.component';
import { ChartDetailValueComponent } from './chart-detail/chart-detail-value.component';
import { ChartDetailComponent } from './chart-detail/chart-detail.component';
import { HelmChartDetailComponent } from './chart-detail.component';
import { SharedModule } from '../../../../shared/shared.module';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{
path: ':chart/versions',
component: ListChartVersionsComponent,
},
{
path: ':chart/versions/:version',
component: HelmChartDetailComponent,
},
];
@NgModule({
imports: [SharedModule, RouterModule.forChild(routes)],
declarations: [
LabelFilterComponent,
LabelMarkerComponent,
ListChartVersionsComponent,
ChartVersionComponent,
ChartDetailDependencyComponent,
ChartDetailSummaryComponent,
ChartDetailValueComponent,
ChartDetailComponent,
HelmChartDetailComponent,
],
})
export class HelmChartListModule {}

View File

@ -1,89 +0,0 @@
import { Label } from '../../../../shared/services';
export interface HelmChartSearchResultItem {
Name: string;
Score: number;
Chart: HelmChartVersion;
}
export interface HelmChartItem {
name: string;
total_versions: number;
latest_version: string;
created: string;
updated: string;
icon: string;
home: string;
deprecated?: boolean;
status?: string;
pulls?: number;
maintainer?: string;
}
export interface HelmChartVersion {
name: string;
home: string;
sources: string[];
version: string;
description: string;
keywords: string[];
maintainers: HelmChartMaintainer[];
engine: string;
icon: string;
appVersion: string;
apiVersion: string;
urls: string[];
created: string;
digest: string;
labels: Label[];
deprecated?: boolean;
}
export interface HelmChartDetail {
metadata: HelmChartMetaData;
dependencies: HelmChartDependency[];
values: any;
files: HelmchartFile;
security: HelmChartSecurity;
labels: Label[];
}
export interface HelmChartMetaData {
name: string;
home: string;
sources: string[];
version: string;
description: string;
keywords: string[];
maintainers: HelmChartMaintainer[];
engine: string;
icon: string;
appVersion: string;
urls: string[];
created?: string;
digest: string;
}
export interface HelmChartMaintainer {
name: string;
email: string;
}
export interface HelmChartDependency {
name: string;
version: string;
repository: string;
}
export interface HelmchartFile {
'README.MD': string;
'values.yaml': string;
}
export interface HelmChartSecurity {
signature: HelmChartSignature;
}
export interface HelmChartSignature {
signed: boolean;
prov_file: string;
}

View File

@ -1,18 +0,0 @@
import { inject, TestBed } from '@angular/core/testing';
import { HelmChartService } from './helm-chart.service';
describe('HelmChartService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [HelmChartService],
});
});
it('should be created', inject(
[HelmChartService],
(service: HelmChartService) => {
expect(service).toBeTruthy();
}
));
});

View File

@ -1,266 +0,0 @@
import { Observable, throwError as observableThrowError } from 'rxjs';
import { Injectable } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { catchError, map } from 'rxjs/operators';
import {
HelmChartDetail,
HelmChartItem,
HelmChartVersion,
} from './helm-chart.interface.service';
import { RequestQueryParams } from '../../../../shared/services';
import {
HTTP_GET_OPTIONS,
HTTP_JSON_OPTIONS,
V1_BASE_HREF,
} from '../../../../shared/units/utils';
/**
* Define service methods for handling the helmchart related things.
* Loose couple with project module.
*
**
* @abstract
* class HelmChartService
*/
export abstract class HelmChartService {
/**
* Get all helm charts info
* ** deprecated param projectName Id of the project
* ** deprecated param queryParams options params for query data
*/
abstract getHelmCharts(
projectName: string,
queryParams?: RequestQueryParams
): Observable<HelmChartItem[]>;
/**
* Delete an helmchart
* ** deprecated param projectId Id of the project
* ** deprecated param chartId ID of helmChart in this specific project
*/
abstract deleteHelmChart(
projectId: number | string,
chartName: string
): Observable<any>;
/**
* Get all the versions of helmchart
* ** deprecated param projectName Id of the project
* ** deprecated param chartName ID of the helm chart
* ** deprecated param queryParams option params for query
*/
abstract getChartVersions(
projectName: string,
chartName: string
): Observable<HelmChartVersion[]>;
/**
* Delete a version of helmchart
* ** deprecated param projectName ID of the project
* ** deprecated param chartName ID of the chart you want to delete
* ** deprecated param version name of the version
*/
abstract deleteChartVersion(
projectName: string,
chartName: string,
version: string
): Observable<any>;
/**
* Get the all details of an helmchart
* ** deprecated param projectName ID of the project
* ** deprecated param chartname ID of the chart
* ** deprecated param version name of the chart's version
* ** deprecated param queryParams options
*/
abstract getChartDetail(
projectName: string,
chartname: string,
version: string
): Observable<HelmChartDetail>;
/**
* Download an specific verison
* ** deprecated param projectName ID of the project
* ** deprecated param filename ID of the helm chart
* ** deprecated param version Name of version
* ** deprecated param queryParams options
*/
abstract downloadChart(
projectName: string,
filename: string
): Observable<any>;
/**
* Upload chart and prov files to chartmuseam
* ** deprecated param projectName Name of the project
* ** deprecated param chart chart file
* ** deprecated param prov prov file
*/
abstract uploadChart(
projectName: string,
chart: File,
prov: File
): Observable<any>;
}
/**
* Implement default service for helm chart.
*/
@Injectable()
export class HelmChartDefaultService extends HelmChartService {
constructor(private http: HttpClient) {
super();
}
private handleErrorObservable(error: HttpErrorResponse) {
return observableThrowError(error);
}
public getHelmCharts(projectName: string): Observable<HelmChartItem[]> {
if (!projectName) {
return observableThrowError(
'Bad argument, No project id to get helm charts'
);
}
return this.http
.get<HelmChartItem[]>(
`${V1_BASE_HREF + '/chartrepo'}/${projectName}/charts`,
HTTP_GET_OPTIONS
)
.pipe(
map(response => response || []),
catchError(error => this.handleErrorObservable(error))
);
}
public deleteHelmChart(
projectId: number | string,
chartName: string
): Observable<any> {
if (!chartName) {
observableThrowError('Bad argument');
}
return this.http
.delete(
`${
V1_BASE_HREF + '/chartrepo'
}/${projectId}/charts/${chartName}`
)
.pipe(
map(response => {
return response || [];
})
)
.pipe(catchError(this.handleErrorObservable));
}
public getChartVersions(
projectName: string,
chartName: string
): Observable<HelmChartVersion[]> {
return this.http
.get<HelmChartVersion[]>(
`${
V1_BASE_HREF + '/chartrepo'
}/${projectName}/charts/${chartName}`,
HTTP_GET_OPTIONS
)
.pipe(
map(response => response || []),
catchError(this.handleErrorObservable)
);
}
public deleteChartVersion(
projectName: string,
chartName: string,
version: string
): any {
return this.http
.delete(
`${
V1_BASE_HREF + '/chartrepo'
}/${projectName}/charts/${chartName}/${version}`,
HTTP_JSON_OPTIONS
)
.pipe(
map(response => {
return response || [];
})
)
.pipe(catchError(this.handleErrorObservable));
}
public getChartDetail(
projectName: string,
chartName: string,
version: string
): Observable<HelmChartDetail> {
return this.http
.get<HelmChartDetail>(
`${
V1_BASE_HREF + '/chartrepo'
}/${projectName}/charts/${chartName}/${version}`
)
.pipe(catchError(this.handleErrorObservable));
}
public downloadChart(
projectName: string,
filename: string
): Observable<any> {
let url: string;
let chartFileRegexPattern = new RegExp('^http.*/chartrepo/(.*)');
if (chartFileRegexPattern.test(filename)) {
let match = filename.match('^http.*/chartrepo/(.*)');
url = `${DOWNLOAD_CHART_ENDPOINT}/${match[1]}`;
} else {
url = `${DOWNLOAD_CHART_ENDPOINT}/${projectName}/${filename}`;
}
return this.http
.get(url, {
responseType: 'blob',
})
.pipe(
map(response => {
let parts = filename.split('/');
return {
filename: parts[parts.length - 1],
data: response,
};
})
)
.pipe(catchError(this.handleErrorObservable));
}
public uploadChart(
projectName: string,
chart?: File,
prov?: File
): Observable<any> {
let formData = new FormData();
let uploadURL = `${V1_BASE_HREF + '/chartrepo'}/${projectName}/charts`;
if (chart) {
formData.append('chart', chart);
}
if (prov) {
formData.append('prov', prov);
if (!chart) {
uploadURL = `${
V1_BASE_HREF + '/chartrepo'
}/${projectName}/prov`;
}
}
return this.http
.post(uploadURL, formData, {
responseType: 'json',
})
.pipe(map(response => response || []))
.pipe(catchError(this.handleErrorObservable));
}
}
export const DOWNLOAD_CHART_ENDPOINT: string = '/chartrepo';

View File

@ -1,32 +0,0 @@
<div>
<div class="form-group filter-div">
<input
#filterInput
class="clr-input"
type="text"
placeholder="{{ 'LABEL.FILTER_LABEL_PLACEHOLDER' | translate }}"
[(ngModel)]="labelFilter" />
</div>
<div class="label-items-container">
<div class="dropdown-item" *ngFor="let label of filteredLabels">
<div class="cur-pointer" (click)="selectLabel(label)">
<span class="check-label-span">
<clr-icon
*ngIf="isSelected(label)"
shape="check"></clr-icon>
</span>
<span class="cur-pointer">
<hbr-label-piece
[label]="label"
[labelWidth]="90"></hbr-label-piece>
</span>
<span
*ngIf="isSelected(label)"
class="x-label-span"
(click)="$event.stopPropagation(); unselectLabel(label)">
<clr-icon shape="times-circle"></clr-icon>
</span>
</div>
</div>
</div>
</div>

View File

@ -1,37 +0,0 @@
@mixin icon-span {
width: 12px;
min-height: 12px;
}
.filter-div {
margin-left: 9px;
margin-bottom: 12px;
}
.cur-pointer {
cursor: pointer;
}
.label-items-container {
max-height: 300px;
overflow-y: auto;
.dropdown-item {
padding-left: 12px;
padding-right: 12px;
.x-label-span {
@include icon-span;
float: right;
}
.check-label-span {
@include icon-span;
float: left;
margin-right: 9px;
}
}
}

View File

@ -1,27 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LabelFilterComponent } from './label-filter.component';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { SharedTestingModule } from '../../../../../shared/shared.module';
describe('LabelFilterComponent', () => {
let component: LabelFilterComponent;
let fixture: ComponentFixture<LabelFilterComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
imports: [SharedTestingModule],
declarations: [LabelFilterComponent],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(LabelFilterComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,84 +0,0 @@
import {
ChangeDetectorRef,
Component,
ElementRef,
EventEmitter,
Input,
OnInit,
ViewChild,
} from '@angular/core';
import { ClrDatagridFilterInterface } from '@clr/angular';
import { fromEvent } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
import { HelmChartVersion } from '../helm-chart.interface.service';
import { Label } from '../../../../../shared/services';
import { Artifact } from '../../../../../../../ng-swagger-gen/models/artifact';
import { ResourceType } from '../../../../../shared/entities/shared.const';
@Component({
selector: 'hbr-chart-version-label-filter',
templateUrl: './label-filter.component.html',
styleUrls: ['./label-filter.component.scss'],
})
export class LabelFilterComponent
implements ClrDatagridFilterInterface<any>, OnInit
{
@Input() labels: Label[] = [];
@Input() resourceType: ResourceType;
@ViewChild('filterInput', { static: true }) filterInputRef: ElementRef;
selectedLabels: Map<number, boolean> = new Map<number, boolean>();
changes: EventEmitter<any> = new EventEmitter<any>(false);
labelFilter = '';
ngOnInit(): void {
fromEvent(this.filterInputRef.nativeElement, 'keyup')
.pipe(debounceTime(500))
.subscribe(() => {
let hnd = setInterval(() => this.cdr.markForCheck(), 100);
setTimeout(() => clearInterval(hnd), 2000);
});
}
constructor(private cdr: ChangeDetectorRef) {}
get filteredLabels() {
return this.labels.filter(label =>
label.name.includes(this.labelFilter)
);
}
isActive(): boolean {
return this.selectedLabels.size > 0;
}
accepts(cv: any): boolean {
if (this.resourceType === ResourceType.CHART_VERSION) {
return (cv as HelmChartVersion).labels.some(label =>
this.selectedLabels.get(label.id)
);
} else if (this.resourceType === ResourceType.REPOSITORY_TAG) {
return (cv as Artifact).labels.some(label =>
this.selectedLabels.get(label.id)
);
} else {
return true;
}
}
selectLabel(label: Label) {
this.selectedLabels.set(label.id, true);
this.changes.emit();
}
unselectLabel(label: Label) {
this.selectedLabels.delete(label.id);
this.changes.emit(true);
}
isSelected(label: Label) {
return this.selectedLabels.has(label.id);
}
}

View File

@ -1,33 +0,0 @@
<div>
<label class="dropdown-header">{{ addLabelHeaders | translate }}</label>
<div class="form-group filter-div">
<input
class="clr-input"
#filterInput
type="text"
placeholder="Filter labels"
[(ngModel)]="labelFilter" />
</div>
<div class="label-items-container">
<div class="dropdown-item" *ngFor="let label of sortedLabels">
<div (click)="markLabel(label)">
<div *ngIf="!isMarkOngoing(label)" class="mark-label-div">
<clr-icon *ngIf="isMarked(label)" shape="check"></clr-icon>
</div>
<div *ngIf="isMarkOngoing(label)" class="spinner spinner-sm">
Loading...
</div>
<div class="label-div">
<hbr-label-piece
[label]="label"
[labelWidth]="130"></hbr-label-piece>
</div>
<div class="unmark-label-div" (click)="unmarkLabel(label)">
<clr-icon
*ngIf="isMarked(label)"
shape="times-circle"></clr-icon>
</div>
</div>
</div>
</div>
</div>

View File

@ -1,53 +0,0 @@
@mixin icon-span {
width: 16px;
height: 16px;
clr-icon {
margin-bottom: 3px;
}
}
@mixin flex-item {
display: inline-block;
}
.filter-div {
margin-left: 9px;
margin-right: 9px;
margin-bottom: 9px;
}
.label-items-container {
max-height: 300px;
overflow-y: auto;
.dropdown-item {
padding-left: 12px;
padding-right: 12px;
.mark-label-div {
@include icon-span;
@include flex-item;
float: left;
margin-right: 9px;
}
.spinner {
@include flex-item;
}
.label-div {
@include flex-item;
margin-top: 4px;
}
.unmark-label-div {
@include icon-span;
@include flex-item;
float: right;
}
}
}

View File

@ -1,52 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { LabelMarkerComponent } from './label-marker.component';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ClarityModule } from '@clr/angular';
import { FormsModule } from '@angular/forms';
import { of } from 'rxjs';
import { LabelService } from '../../../../../shared/services';
import { ErrorHandler } from '../../../../../shared/units/error-handler';
describe('LabelMarkerComponent', () => {
const mockErrorHandler = null;
const mockLabelService = {
getChartVersionLabels: () => {
return of({
name: '111',
description: 'string',
color: 'string',
scope: 'string',
project_id: 1,
});
},
markChartLabel: () => {},
unmarkChartLabel: () => {},
};
let component: LabelMarkerComponent;
let fixture: ComponentFixture<LabelMarkerComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
imports: [ClarityModule, TranslateModule.forRoot(), FormsModule],
declarations: [LabelMarkerComponent],
providers: [
TranslateService,
{ provide: LabelService, useValue: mockLabelService },
{ provide: ErrorHandler, useValue: mockErrorHandler },
],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(LabelMarkerComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,171 +0,0 @@
import {
ChangeDetectorRef,
Component,
ElementRef,
EventEmitter,
Input,
OnInit,
Output,
ViewChild,
} from '@angular/core';
import { fromEvent, Subject } from 'rxjs';
import { debounceTime, finalize } from 'rxjs/operators';
import { HelmChartVersion } from '../helm-chart.interface.service';
import { Label, LabelService } from '../../../../../shared/services';
import { ErrorHandler } from '../../../../../shared/units/error-handler';
import { ResourceType } from '../../../../../shared/entities/shared.const';
@Component({
selector: 'hbr-resource-label-marker',
templateUrl: './label-marker.component.html',
styleUrls: ['./label-marker.component.scss'],
})
export class LabelMarkerComponent implements OnInit {
@Input() labels: Label[] = [];
@Input() projectName: string;
@Input() resource: HelmChartVersion;
@Input() resourceType: ResourceType;
@Input() addLabelHeaders: string;
@Output() changeEvt = new EventEmitter<any>();
labelFilter = '';
markedMap: Map<number, boolean> = new Map<number, boolean>();
markingMap: Map<number, boolean> = new Map<number, boolean>();
sortedLabels: Label[] = [];
loading = false;
labelChangeDebouncer: Subject<any> = new Subject();
@ViewChild('filterInput', { static: true }) filterInputRef: ElementRef;
ngOnInit(): void {
this.sortedLabels = this.labels;
this.refresh();
fromEvent(this.filterInputRef.nativeElement, 'keyup')
.pipe(debounceTime(500))
.subscribe(() => this.refresh());
this.labelChangeDebouncer
.pipe(debounceTime(1000))
.subscribe(() => this.changeEvt.emit());
}
constructor(
private labelService: LabelService,
private errorHandler: ErrorHandler,
private cdr: ChangeDetectorRef
) {}
refresh() {
this.loading = true;
if (this.resourceType === ResourceType.CHART_VERSION) {
this.labelService
.getChartVersionLabels(
this.projectName,
this.resource.name,
(this.resource as HelmChartVersion).version
)
.pipe(
finalize(() => {
this.loading = false;
let hnd = setInterval(
() => this.cdr.markForCheck(),
100
);
setTimeout(() => clearInterval(hnd), 2000);
})
)
.subscribe(chartVersionLabels => {
for (let label of chartVersionLabels) {
this.markedMap.set(label.id, true);
}
this.sortedLabels = this.getSortedLabels();
});
}
}
markLabel(label: Label) {
if (this.markedMap.get(label.id) || this.isMarkOngoing(label)) {
return;
}
this.markingMap.set(label.id, true);
this.labelService
.markChartLabel(
this.projectName,
this.resource.name,
(this.resource as HelmChartVersion).version,
label
)
.pipe(
finalize(() => {
this.markingMap.set(label.id, false);
let hnd = setInterval(() => this.cdr.markForCheck(), 100);
setTimeout(() => clearInterval(hnd), 5000);
})
)
.subscribe(
() => {
this.markedMap.set(label.id, true);
this.refresh();
this.labelChangeDebouncer.next(null);
let hnd = setInterval(() => this.cdr.markForCheck(), 100);
setTimeout(() => clearInterval(hnd), 5000);
},
err => this.errorHandler.error(err)
);
}
unmarkLabel(label: Label) {
if (!this.isMarked(label) || this.isMarkOngoing(label)) {
return;
}
this.markingMap.set(label.id, true);
this.labelService
.unmarkChartLabel(
this.projectName,
this.resource.name,
(this.resource as HelmChartVersion).version,
label
)
.pipe(
finalize(() => {
this.markingMap.set(label.id, false);
let hnd = setInterval(() => this.cdr.markForCheck(), 100);
setTimeout(() => clearInterval(hnd), 5000);
})
)
.subscribe(
() => {
this.markedMap.set(label.id, false);
this.refresh();
this.labelChangeDebouncer.next(null);
let hnd = setInterval(() => this.cdr.markForCheck(), 100);
setTimeout(() => clearInterval(hnd), 5000);
},
err => this.errorHandler.error(err)
);
}
isMarked(label: Label) {
return this.markedMap.get(label.id) ? true : false;
}
isMarkOngoing(label: Label) {
return this.markingMap.get(label.id) ? true : false;
}
getSortedLabels(): Label[] {
return this.labels
.filter(l => l.name.includes(this.labelFilter))
.sort((a, b) => {
if (this.isMarked(a) && !this.isMarked(b)) {
return -1;
} else if (!this.isMarked(a) && this.isMarked(b)) {
return 1;
} else {
return a.name < b.name ? -1 : a.name > b.name ? 1 : 0;
}
});
}
}

View File

@ -1,276 +0,0 @@
<div class="version-position">
<div class="row flex-items-xs-between">
<div class="col-xs-4">
<div class="title-container">
<div class="chart-name-span">
{{ chartName | translate }}
</div>
<div>
{{ roleName | translate }}
</div>
</div>
</div>
</div>
<div class="row version-tool">
<div class="toolbar">
<div class="row flex-items-xs-right option-right rightPos">
<div class="flex-xs-middle">
<hbr-filter
[withDivider]="true"
filterPlaceholder="{{
'HELM_CHART.FILTER_FOR_CHARTS' | translate
}}"
[currentValue]="lastFilteredVersionName"
(filterEvt)="updateFilterValue($event)"></hbr-filter>
<span
class="card-btn"
(click)="showCard(true)"
(mouseenter)="mouseEnter('card')"
(mouseleave)="mouseLeave('card')">
<clr-icon
[ngClass]="{
'is-highlight': isCardView || isHovering('card')
}"
shape="view-cards"></clr-icon>
</span>
<span
class="list-btn"
(click)="showCard(false)"
(mouseenter)="mouseEnter('list')"
(mouseleave)="mouseLeave('list')">
<clr-icon
[ngClass]="{
'is-highlight':
!isCardView || isHovering('list')
}"
shape="view-list"></clr-icon>
</span>
<span class="filter-divider"></span>
<span class="refresh-btn" (click)="refresh()">
<clr-icon shape="refresh"></clr-icon>
</span>
</div>
</div>
</div>
</div>
<div class="row">
<div
*ngIf="!isCardView"
class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<clr-datagrid
(clrDgRefresh)="refresh($event)"
[clrDgLoading]="loading"
[(clrDgSelected)]="selectedRows">
<clr-dg-action-bar>
<button
type="button"
class="btn btn-secondary"
[disabled]="
!(selectedRows.length === 1) ||
!hasDownloadHelmChartVersionPermission
"
(click)="versionDownload()">
<clr-icon shape="download" size="16"></clr-icon>&nbsp;{{
'HELM_CHART.DOWNLOAD' | translate
}}
</button>
<button
type="button"
class="btn btn-secondary"
[disabled]="
selectedRows.length <= 0 ||
!hasDeleteHelmChartVersionPermission
"
(click)="openVersionDeleteModal()">
<clr-icon shape="times" size="16"></clr-icon>&nbsp;{{
'BUTTON.DELETE' | translate
}}
</button>
<clr-dropdown>
<button
type="button"
class="btn btn-secondary"
clrDropdownTrigger
[disabled]="
!(selectedRows.length === 1) ||
!hasAddRemoveHelmChartVersionPermission
">
<clr-icon shape="plus" size="16"></clr-icon
>{{ 'REPOSITORY.ADD_LABELS' | translate }}
</button>
<clr-dropdown-menu clrPosition="bottom-left" *clrIfOpen>
<hbr-resource-label-marker
[labels]="labels"
[projectName]="projectName"
[resource]="selectedRows[0]"
[resourceType]="resourceType"
[addLabelHeaders]="addLabelHeaders"
(changeEvt)="onLabelChange(selectedRows[0])">
</hbr-resource-label-marker>
</clr-dropdown-menu>
</clr-dropdown>
</clr-dg-action-bar>
<clr-dg-column>{{
'HELM_CHART.VERSION' | translate
}}</clr-dg-column>
<clr-dg-column>{{
'HELM_CHART.STATUS' | translate
}}</clr-dg-column>
<clr-dg-column>{{
'HELM_CHART.ENGINE' | translate
}}</clr-dg-column>
<clr-dg-column>{{
'HELM_CHART.MAINTAINERS' | translate
}}</clr-dg-column>
<clr-dg-column>{{
'HELM_CHART.CREATED' | translate
}}</clr-dg-column>
<clr-dg-column>
{{ 'REPOSITORY.LABELS' | translate }}
<clr-dg-filter [clrDgFilter]="labelFilter">
<hbr-chart-version-label-filter
#labelFilter
class="label-filter"
[labels]="labels"
[resourceType]="resourceType">
</hbr-chart-version-label-filter>
</clr-dg-filter>
</clr-dg-column>
<clr-dg-placeholder>{{
'HELM_CHART.NO_VERSION_PLACEHOLDER' | translate
}}</clr-dg-placeholder>
<clr-dg-row
*clrDgItems="let v of chartVersions"
[clrDgItem]="v">
<clr-dg-cell>
<span class="list-img">
<img
[src]="v.icon ? v.icon : chartDefaultIcon"
(error)="getDefaultIcon(v)" />
</span>
<a
href="javascript:void(0)"
(click)="onVersionClick(v)"
>{{ v.version }}</a
>
</clr-dg-cell>
<clr-dg-cell>{{
getStatusString(v) | translate
}}</clr-dg-cell>
<clr-dg-cell>{{ v.engine }}</clr-dg-cell>
<clr-dg-cell>{{
getMaintainerString(v.maintainers)
}}</clr-dg-cell>
<clr-dg-cell>{{ v.created | harborDatetime }}</clr-dg-cell>
<clr-dg-cell>
<div>
<hbr-label-piece
*ngIf="v.labels?.length"
[label]="v.labels[0]"
[labelWidth]="130">
</hbr-label-piece>
<hbr-resource-label-signpost
*ngIf="v.labels?.length > 1"
[labels]="
v.labels
"></hbr-resource-label-signpost>
</div>
</clr-dg-cell>
</clr-dg-row>
<clr-dg-footer>
<clr-dg-pagination
#pagination
[clrDgPageSize]="pageSize"
[clrDgTotalItems]="totalCount">
<clr-dg-page-size [clrPageSizeOptions]="[15, 25, 50]">{{
'PAGINATION.PAGE_SIZE' | translate
}}</clr-dg-page-size>
<span *ngIf="totalCount">
{{ pagination.firstItem + 1 }} -
{{ pagination.lastItem + 1 }}
{{ 'HELM_CHART.OF' | translate }}
</span>
{{ totalCount }} {{ 'HELM_CHART.ITEMS' | translate }}
</clr-dg-pagination>
</clr-dg-footer>
</clr-datagrid>
</div>
</div>
<div *ngIf="isCardView" class="row card-container">
<div
*ngFor="let item of chartVersions"
class="col-lg-3 col-md-4 col-sm-6">
<a
let
i="index;"
class="card clickable"
(click)="onVersionClick(item)">
<div class="card-header">
<div class="card-media-block">
<img
[src]="item.icon ? item.icon : chartDefaultIcon"
(error)="getDefaultIcon(item)" />
<div class="card-media-description">
<span class="card-media-title">{{
item.name
}}</span>
<a class="card-media-text">{{ item.home }}</a>
</div>
</div>
</div>
<div class="card-block">
<div class="form-group">
<label class="card-label">{{
'HELM_CHART.STATUS' | translate
}}</label>
<div>{{ getStatusString(item) | translate }}</div>
</div>
<div class="form-group">
<label class="card-label">{{
'HELM_CHART.ENGINE' | translate
}}</label>
<div>{{ item.engine }}</div>
</div>
<div class="form-group">
<label class="card-label">{{
'HELM_CHART.MAINTAINERS' | translate
}}</label>
<div>{{ getMaintainerString(item.maintainers) }}</div>
</div>
<div class="form-group">
<label class="card-label">{{
'HELM_CHART.VERSION' | translate
}}</label>
<div>{{ item.version }}</div>
</div>
</div>
<div class="card-footer">
<clr-dropdown [clrCloseMenuOnItemClick]="false">
<button
type="button"
class="btn btn-link"
(click)="versionDownload($event, item)">
{{ 'HELM_CHART.DOWNLOAD' | translate }}
</button>
<button
type="button"
class="btn btn-link"
[disabled]="!hasDeleteHelmChartVersionPermission"
(click)="deleteVersionCard($event, item)">
{{ 'BUTTON.DELETE' | translate }}
</button>
</clr-dropdown>
</div>
</a>
</div>
<div *ngIf="loading" class="center-x">
<span class="vertical-helper"></span>
<span class="spinner"></span>
</div>
</div>
<confirmation-dialog
#confirmationDialog
(confirmAction)="confirmDeletion($event)"></confirmation-dialog>
</div>

View File

@ -1,131 +0,0 @@
@import "../../../../../../shared/mixin";
.title-container {
display: flex;
.chart-name-span {
border-right: 1px solid gray;
font-size: 27px;
font-weight: normal;
padding-right: 9px;
margin-right: 9px;
}
}
.version-tool {
position: relative;
.toolbar {
overflow: hidden;
/* stylelint-disable */
.rightPos {
@include grid-right-top-pos;
.filter-divider {
display: inline-block;
height: 16px;
width: 1px;
padding-top: 12px;
padding-bottom: 12px;
position: relative;
top: 9px;
margin-right: 6px;
margin-left: 6px;
}
}
}
}
clr-dg-action-bar {
clr-dropdown {
.btn {
@include dropdown-as-action-button;
}
}
}
hbr-resource-label-signpost {
display: inline-block;
max-height: 24px;
margin-left: 3px;
}
.card-container {
margin-top: 40px;
.card-header {
.card-media-block {
img {
height: 45px;
width: 45px;
}
.card-media-description {
width:80%;
height: 45px;
.card-media-title {
overflow: hidden;
height: 24px;
}
.card-media-text {
font-size:0.8em;
color:#007cbb;
@include text-overflow;
height: 20px;
}
}
}
}
.card-block {
font-size:0.9em;
margin-top: 24px;
min-height: 100px;
.form-group {
display: flex;
label {
width: 100px;
color:#aaa;
}
}
margin-top: 0;
}
.card-footer {
padding-top: 6px;
padding-bottom: 6px;
}
}
.list-img {
img {
height: 24px;
width: 24px;
margin-right: 12px;
}
}
.vertical-helper {
display: inline-block;
height: 100%;
vertical-align: middle;
}
.center-x {
display: flex;
justify-content: center;
position: absolute;
top: 6.5rem;
left: 50%;
transform: translateX(-50%);
}
.version-position {
position: relative;
}

View File

@ -1,110 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ChartVersionComponent } from './helm-chart-version.component';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { HelmChartService } from '../../helm-chart.service';
import { LabelFilterComponent } from '../../label-filter/label-filter.component';
import { of } from 'rxjs';
import {
LabelService,
SystemInfoService,
UserPermissionService,
} from '../../../../../../shared/services';
import { ErrorHandler } from '../../../../../../shared/units/error-handler';
import { OperationService } from '../../../../../../shared/components/operation/operation.service';
import { delay } from 'rxjs/operators';
import { SharedTestingModule } from '../../../../../../shared/shared.module';
describe('ChartVersionComponent', () => {
let component: ChartVersionComponent;
let fixture: ComponentFixture<ChartVersionComponent>;
const mockSystemInfoService = {
getSystemInfo: () => {
return of({
with_notary: false,
with_admiral: false,
admiral_endpoint: '',
auth_mode: 'oidc_auth',
registry_url: 'nightly-oidc.harbor.io',
external_url: 'https://nightly-oidc.harbor.io',
project_creation_restriction: 'everyone',
self_registration: false,
has_ca_root: false,
harbor_version: 'dev',
registry_storage_provider_name: 'filesystem',
read_only: false,
with_chartmuseum: true,
notification_enable: true,
});
},
};
const mockLabelService = {
getLabels: () => {
return of([]);
},
getProjectLabels: () => {
return of([]);
},
};
const mockErrorHandler = null;
const mockOperationService = {
publishInfo: () => {
return of([]);
},
};
const mockUserPermissionService = {
getPermission() {
return of(true);
},
};
const mockHelmChartService = {
getChartVersions() {
return of([
{
name: 'string',
home: 'string',
sources: [],
version: 'string',
description: 'string',
keywords: [],
maintainers: [],
engine: 'string',
icon: 'string',
appVersion: 'string',
apiVersion: 'string',
urls: [],
created: 'string',
digest: 'string',
labels: [],
},
]).pipe(delay(0));
},
};
beforeEach(async () => {
await TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
imports: [SharedTestingModule],
declarations: [ChartVersionComponent, LabelFilterComponent],
providers: [
{ provide: SystemInfoService, useValue: mockSystemInfoService },
{ provide: LabelService, useValue: mockLabelService },
{
provide: UserPermissionService,
useValue: mockUserPermissionService,
},
{ provide: ErrorHandler, useValue: mockErrorHandler },
{ provide: HelmChartService, useValue: mockHelmChartService },
{ provide: OperationService, useValue: mockOperationService },
],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ChartVersionComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,504 +0,0 @@
import {
Component,
EventEmitter,
Input,
OnInit,
Output,
ViewChild,
} from '@angular/core';
import { forkJoin, Observable, throwError as observableThrowError } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core';
import {
HelmChartMaintainer,
HelmChartVersion,
} from '../../helm-chart.interface.service';
import { HelmChartService } from '../../helm-chart.service';
import {
LabelService as OldLabelService,
State,
SystemInfo,
SystemInfoService,
UserPermissionService,
USERSTATICPERMISSION,
} from '../../../../../../shared/services';
import {
downloadFile,
getPageSizeFromLocalStorage,
PageSizeMapKeys,
setPageSizeToLocalStorage,
} from '../../../../../../shared/units/utils';
import { ErrorHandler } from '../../../../../../shared/units/error-handler';
import { OperationService } from '../../../../../../shared/components/operation/operation.service';
import {
operateChanges,
OperateInfo,
OperationState,
} from '../../../../../../shared/components/operation/operate';
import {
ConfirmationButtons,
ConfirmationState,
ConfirmationTargets,
DefaultHelmIcon,
ResourceType,
} from '../../../../../../shared/entities/shared.const';
import { errorHandler } from '../../../../../../shared/units/shared.utils';
import { ConfirmationDialogComponent } from '../../../../../../shared/components/confirmation-dialog';
import { ConfirmationMessage } from '../../../../../global-confirmation-dialog/confirmation-message';
import { ConfirmationAcknowledgement } from '../../../../../global-confirmation-dialog/confirmation-state-message';
import { Label } from '../../../../../../../../ng-swagger-gen/models/label';
import { LabelService } from '../../../../../../../../ng-swagger-gen/services/label.service';
import { ClrDatagridStateInterface } from '@clr/angular';
const PAGE_SIZE: number = 100;
@Component({
selector: 'hbr-helm-chart-version',
templateUrl: './helm-chart-version.component.html',
styleUrls: ['./helm-chart-version.component.scss'],
})
export class ChartVersionComponent implements OnInit {
signedCon: { [key: string]: any | string[] } = {};
@Input() projectId: number;
@Input() projectName: string;
@Input() chartName: string;
@Input() roleName: string;
@Input() hasSignedIn: boolean;
@Input() chartDefaultIcon: string = DefaultHelmIcon;
@Output() versionClickEvt = new EventEmitter<string>();
@Output() backEvt = new EventEmitter<any>();
lastFilteredVersionName: string;
chartVersions: HelmChartVersion[] = [];
systemInfo: SystemInfo;
selectedRows: HelmChartVersion[] = [];
labels: Label[] = [];
loading = true;
resourceType = ResourceType.CHART_VERSION;
isCardView: boolean;
cardHover = false;
listHover = false;
pageSize: number = getPageSizeFromLocalStorage(
PageSizeMapKeys.CHART_VERSION_COMPONENT
);
currentPage = 1;
totalCount = 0;
currentState: State;
chartFile: File;
provFile: File;
addLabelHeaders = 'HELM_CHART.ADD_LABEL_TO_CHART_VERSION';
@ViewChild('confirmationDialog')
confirmationDialog: ConfirmationDialogComponent;
hasAddRemoveHelmChartVersionPermission: boolean;
hasDownloadHelmChartVersionPermission: boolean;
hasDeleteHelmChartVersionPermission: boolean;
constructor(
private errorHandlerEntity: ErrorHandler,
private systemInfoService: SystemInfoService,
private helmChartService: HelmChartService,
private labelService: LabelService,
private resrouceLabelService: OldLabelService,
public userPermissionService: UserPermissionService,
private operationService: OperationService,
private translateService: TranslateService
) {}
public get registryUrl(): string {
return this.systemInfo ? this.systemInfo.registry_url : '';
}
ngOnInit(): void {
// Get system info for tag views
this.systemInfoService.getSystemInfo().subscribe(
systemInfo => (this.systemInfo = systemInfo),
error => this.errorHandlerEntity.error(error)
);
this.refresh();
this.getLabels();
this.lastFilteredVersionName = '';
this.getHelmChartVersionPermission(this.projectId);
}
updateFilterValue(value: string) {
this.lastFilteredVersionName = value;
this.refresh();
}
getLabels() {
// get all project labels
this.labelService
.ListLabelsResponse({
pageSize: PAGE_SIZE,
page: 1,
scope: 'p',
projectId: this.projectId,
})
.subscribe(res => {
if (res.headers) {
const xHeader: string = res.headers.get('X-Total-Count');
const totalCount = parseInt(xHeader, 0);
let arr = res.body || [];
if (totalCount <= PAGE_SIZE) {
// already gotten all project labels
if (arr && arr.length) {
this.labels = this.labels.concat(arr);
}
} else {
// get all the project labels in specified times
const times: number = Math.ceil(totalCount / PAGE_SIZE);
const observableList: Observable<Label[]>[] = [];
for (let i = 2; i <= times; i++) {
observableList.push(
this.labelService.ListLabels({
page: i,
pageSize: PAGE_SIZE,
scope: 'p',
projectId: this.projectId,
})
);
}
forkJoin(observableList).subscribe(response => {
if (response && response.length) {
response.forEach(item => {
arr = arr.concat(item);
});
this.labels = this.labels.concat(arr);
}
});
}
}
});
// get all global labels
this.labelService
.ListLabelsResponse({
pageSize: PAGE_SIZE,
page: 1,
scope: 'g',
})
.subscribe(res => {
if (res.headers) {
const xHeader: string = res.headers.get('X-Total-Count');
const totalCount = parseInt(xHeader, 0);
let arr = res.body || [];
if (totalCount <= PAGE_SIZE) {
// already gotten all global labels
if (arr && arr.length) {
this.labels = this.labels.concat(arr);
}
} else {
// get all the global labels in specified times
const times: number = Math.ceil(totalCount / PAGE_SIZE);
const observableList: Observable<Label[]>[] = [];
for (let i = 2; i <= times; i++) {
observableList.push(
this.labelService.ListLabels({
page: i,
pageSize: PAGE_SIZE,
scope: 'g',
})
);
}
forkJoin(observableList).subscribe(response => {
if (response && response.length) {
response.forEach(item => {
arr = arr.concat(item);
});
this.labels = this.labels.concat(arr);
}
});
}
}
});
}
refresh(state?: ClrDatagridStateInterface) {
if (state?.page?.size) {
setPageSizeToLocalStorage(
PageSizeMapKeys.CHART_VERSION_COMPONENT,
state.page.size
);
}
this.loading = true;
this.helmChartService
.getChartVersions(this.projectName, this.chartName)
.pipe(
finalize(() => {
this.selectedRows = [];
this.loading = false;
})
)
.subscribe(
versions => {
this.chartVersions = versions.filter(x =>
x?.version?.includes(this.lastFilteredVersionName)
);
this.totalCount = versions.length;
},
err => {
this.errorHandlerEntity.error(err);
}
);
}
getMaintainerString(maintainers: HelmChartMaintainer[]) {
if (!maintainers || maintainers.length < 1) {
return '';
}
let maintainer_string = maintainers[0].name;
if (maintainers.length > 1) {
maintainer_string = `${maintainer_string} (${
maintainers.length - 1
} others)`;
}
return maintainer_string;
}
onVersionClick(version: HelmChartVersion) {
this.versionClickEvt.emit(version.version);
}
deleteVersion(version: HelmChartVersion): Observable<any> {
// init operation info
let operateMsg = new OperateInfo();
operateMsg.name = 'OPERATION.DELETE_CHART_VERSION';
operateMsg.data.id = version.digest;
operateMsg.state = OperationState.progressing;
operateMsg.data.name = `${version.name}:${version.version}`;
this.operationService.publishInfo(operateMsg);
return this.helmChartService
.deleteChartVersion(
this.projectName,
this.chartName,
version.version
)
.pipe(
map(() => operateChanges(operateMsg, OperationState.success)),
catchError(error => {
const message = errorHandler(error);
this.translateService
.get(message)
.subscribe(res =>
operateChanges(
operateMsg,
OperationState.failure,
res
)
);
return observableThrowError(error);
})
);
}
deleteVersions(versions: HelmChartVersion[]) {
if (versions && versions.length < 1) {
return;
}
let successCount: number;
let totalCount = this.chartVersions.length;
let versionObs = versions.map(v => this.deleteVersion(v));
forkJoin(versionObs)
.pipe(
finalize(() => {
if (totalCount !== successCount) {
this.refresh();
}
})
)
.subscribe(
res => {
successCount = res.filter(
r => r.state === OperationState.success
).length;
if (totalCount === successCount) {
this.backEvt.emit();
}
},
error => {
this.errorHandlerEntity.error(error);
}
);
}
versionDownload(evt?: Event, item?: HelmChartVersion) {
if (evt) {
evt.stopPropagation();
}
let selectedVersion: HelmChartVersion;
if (item) {
selectedVersion = item;
} else {
// return if selected version less then 1
if (this.selectedRows.length < 1) {
return;
}
selectedVersion = this.selectedRows[0];
}
if (!selectedVersion) {
return;
}
let filename = selectedVersion.urls[0];
this.helmChartService
.downloadChart(this.projectName, filename)
.subscribe(
res => {
downloadFile(res);
},
error => {
this.errorHandlerEntity.error(error);
}
);
}
showCard(cardView: boolean) {
if (this.isCardView === cardView) {
return;
}
this.isCardView = cardView;
}
mouseEnter(itemName: string) {
if (itemName === 'card') {
this.cardHover = true;
} else {
this.listHover = true;
}
}
mouseLeave(itemName: string) {
if (itemName === 'card') {
this.cardHover = false;
} else {
this.listHover = false;
}
}
isHovering(itemName: string) {
if (itemName === 'card') {
return this.cardHover;
} else {
return this.listHover;
}
}
onChartFileChangeEvent(event) {
if (event.target.files && event.target.files.length > 0) {
this.chartFile = event.target.files[0];
}
}
onProvFileChangeEvent(event) {
if (event.target.files && event.target.files.length > 0) {
this.provFile = event.target.files[0];
}
}
deleteVersionCard(env: Event, version: HelmChartVersion) {
env.stopPropagation();
this.openVersionDeleteModal([version]);
}
openVersionDeleteModal(versions?: HelmChartVersion[]) {
if (!versions) {
versions = this.selectedRows;
}
let versionNames = versions.map(v => v.version).join(',');
let message = new ConfirmationMessage(
'HELM_CHART.DELETE_CHART_VERSION_TITLE',
'HELM_CHART.DELETE_CHART_VERSION',
versionNames,
versions,
ConfirmationTargets.HELM_CHART_VERSION,
ConfirmationButtons.DELETE_CANCEL
);
this.confirmationDialog.open(message);
}
confirmDeletion(message: ConfirmationAcknowledgement) {
if (
message &&
message.source === ConfirmationTargets.HELM_CHART_VERSION &&
message.state === ConfirmationState.CONFIRMED
) {
let versions = message.data;
this.deleteVersions(versions);
}
}
getImgLink(v: HelmChartVersion) {
if (v.icon) {
return v.icon;
} else {
return DefaultHelmIcon;
}
}
getDefaultIcon(v: HelmChartVersion) {
v.icon = this.chartDefaultIcon;
}
getStatusString(chartVersion: HelmChartVersion) {
if (chartVersion.deprecated) {
return 'HELM_CHART.DEPRECATED';
} else {
return 'HELM_CHART.ACTIVE';
}
}
onLabelChange(version: HelmChartVersion) {
this.resrouceLabelService
.getChartVersionLabels(
this.projectName,
this.chartName,
version.version
)
.subscribe(labels => {
let versionIdx = this.chartVersions.findIndex(
v => v.name === version.name
);
this.chartVersions[versionIdx].labels = labels;
});
}
getHelmChartVersionPermission(projectId: number): void {
let hasAddRemoveHelmChartVersionPermission =
this.userPermissionService.getPermission(
projectId,
USERSTATICPERMISSION.HELM_CHART_VERSION_LABEL.KEY,
USERSTATICPERMISSION.HELM_CHART_VERSION_LABEL.VALUE.CREATE
);
let hasDownloadHelmChartVersionPermission =
this.userPermissionService.getPermission(
projectId,
USERSTATICPERMISSION.HELM_CHART_VERSION.KEY,
USERSTATICPERMISSION.HELM_CHART_VERSION.VALUE.READ
);
let hasDeleteHelmChartVersionPermission =
this.userPermissionService.getPermission(
projectId,
USERSTATICPERMISSION.HELM_CHART_VERSION.KEY,
USERSTATICPERMISSION.HELM_CHART_VERSION.VALUE.DELETE
);
forkJoin(
hasAddRemoveHelmChartVersionPermission,
hasDownloadHelmChartVersionPermission,
hasDeleteHelmChartVersionPermission
).subscribe(
permissions => {
this.hasAddRemoveHelmChartVersionPermission =
permissions[0] as boolean;
this.hasDownloadHelmChartVersionPermission =
permissions[1] as boolean;
this.hasDeleteHelmChartVersionPermission =
permissions[2] as boolean;
},
error => this.errorHandlerEntity.error(error)
);
}
}

View File

@ -1,21 +0,0 @@
<div>
<div class="breadcrumb">
<span class="back-icon"><</span>
<a href="javascript:void(0)" (click)="gotoProjectList()">{{
'SIDE_NAV.PROJECTS' | translate
}}</a>
<span class="back-icon"><</span>
<a href="javascript:void(0)" (click)="gotoChartList()">{{
projectName
}}</a>
</div>
<hbr-helm-chart-version
[projectId]="projectId"
[projectName]="projectName"
[chartName]="chartName"
[roleName]="roleName"
[hasSignedIn]="hasSignedIn"
(versionClickEvt)="onVersionClick($event)"
(backEvt)="gotoChartList()">
</hbr-helm-chart-version>
</div>

View File

@ -1,16 +0,0 @@
.breadcrumb a {
text-decoration: none;
cursor: pointer;
color: #007cbb;
font-size: 16px;
margin: 5px;
}
.breadcrumb {
margin-bottom: 0.5rem;
}
.back-icon {
color: #007cbb;
font-size: 16px;
}

View File

@ -1,56 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ClarityModule } from '@clr/angular';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { ActivatedRoute, Router } from '@angular/router';
import { SessionService } from '../../../../../shared/services/session.service';
import { ListChartVersionsComponent } from './list-chart-versions.component';
describe('ListChartVersionsComponent', () => {
let component: ListChartVersionsComponent;
let fixture: ComponentFixture<ListChartVersionsComponent>;
let fakeSessionService = {
getCurrentUser: function () {
return 'admin';
},
};
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [ListChartVersionsComponent],
imports: [ClarityModule, TranslateModule.forRoot()],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
providers: [
TranslateService,
{
provide: ActivatedRoute,
useValue: {
snapshot: {
parent: {
params: {
id: 1,
},
data: null,
},
params: {
chart: 'chart',
},
},
},
},
{ provide: Router, useValue: null },
{ provide: SessionService, useValue: fakeSessionService },
],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ListChartVersionsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,57 +0,0 @@
import { ActivatedRoute, Router } from '@angular/router';
import { Component, OnInit } from '@angular/core';
import { Project } from '../../../project';
import { SessionUser } from '../../../../../shared/entities/session-user';
import { SessionService } from '../../../../../shared/services/session.service';
@Component({
selector: 'list-chart-version',
templateUrl: './list-chart-versions.component.html',
styleUrls: ['./list-chart-versions.component.scss'],
})
export class ListChartVersionsComponent implements OnInit {
loading = false;
projectId: number;
projectName: string;
chartName: string;
roleName: string;
hasSignedIn: boolean;
currentUser: SessionUser;
constructor(
private route: ActivatedRoute,
private router: Router,
private session: SessionService
) {}
ngOnInit() {
// Get projectId from router-guard params snapshot.
this.projectId = +this.route.snapshot.parent.params['id'];
this.chartName = this.route.snapshot.params['chart'];
// Get current user from registered resolver.
this.currentUser = this.session.getCurrentUser();
let resolverData = this.route.snapshot.parent.data;
if (resolverData) {
let project = <Project>resolverData['projectResolver'];
this.roleName = project.role_name;
this.projectName = project.name;
}
}
onVersionClick(version: string) {
this.router.navigateByUrl(`${this.router.url}/${version}`);
}
gotoProjectList() {
this.router.navigateByUrl('/harbor/projects');
}
gotoChartList() {
this.router.navigateByUrl(
`/harbor/projects/${this.projectId}/helm-charts`
);
}
}

View File

@ -1,30 +0,0 @@
// Copyright Project Harbor Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import { NgModule } from '@angular/core';
import { ListChartsComponent } from './list-charts.component';
import { HelmChartComponent } from './list-charts-detail/helm-chart.component';
import { SharedModule } from '../../../../shared/shared.module';
import { RouterModule, Routes } from '@angular/router';
const routes: Routes = [
{
path: '',
component: ListChartsComponent,
},
];
@NgModule({
imports: [SharedModule, RouterModule.forChild(routes)],
declarations: [ListChartsComponent, HelmChartComponent],
})
export class HelmChartListModule {}

View File

@ -1,290 +0,0 @@
<div>
<div class="row chart-tool">
<div class="toolbar">
<div class="row flex-items-xs-right option-right rightPos">
<div class="flex-xs-middle">
<hbr-filter
[withDivider]="true"
filterPlaceholder="{{
'HELM_CHART.FILTER_FOR_CHARTS' | translate
}}"
[currentValue]="lastFilteredChartName"
(filterEvt)="updateFilterValue($event)"></hbr-filter>
<span
class="card-btn"
(click)="showCard(true)"
(mouseenter)="mouseEnter('card')"
(mouseleave)="mouseLeave('card')">
<clr-icon
[ngClass]="{
'is-highlight': isCardView || isHovering('card')
}"
shape="view-cards"></clr-icon>
</span>
<span
class="list-btn"
(click)="showCard(false)"
(mouseenter)="mouseEnter('list')"
(mouseleave)="mouseLeave('list')">
<clr-icon
[ngClass]="{
'is-highlight':
!isCardView || isHovering('list')
}"
shape="view-list"></clr-icon>
</span>
<span class="filter-divider"></span>
<span class="refresh-btn" (click)="refresh()">
<clr-icon shape="refresh"></clr-icon>
</span>
</div>
</div>
</div>
</div>
<div class="row">
<div
*ngIf="!isCardView"
class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<clr-datagrid
(clrDgRefresh)="clrLoad($event)"
[clrDgLoading]="loading"
[(clrDgSelected)]="selectedRows">
<clr-dg-action-bar>
<button
type="button"
id="helm-chart-upload"
class="btn btn-secondary"
[disabled]="!hasUploadHelmChartsPermission"
(click)="onChartUpload()">
<clr-icon shape="upload" size="16"></clr-icon
>{{ 'HELM_CHART.UPLOAD' | translate }}
</button>
<button
type="button"
class="btn btn-secondary"
[disabled]="
!hasDeleteHelmChartsPermission ||
selectedRows.length < 1
"
(click)="openChartDeleteModal()">
<clr-icon shape="trash" size="16"></clr-icon
>{{ 'BUTTON.DELETE' | translate }}
</button>
<button
type="button"
class="btn btn-secondary"
[disabled]="
!hasDownloadHelmChartsPermission ||
selectedRows.length !== 1
"
(click)="downloadLatestVersion()">
<clr-icon shape="download" size="16"></clr-icon
>{{ 'HELM_CHART.DOWNLOAD' | translate }}
</button>
</clr-dg-action-bar>
<clr-dg-column>{{
'HELM_CHART.NAME' | translate
}}</clr-dg-column>
<clr-dg-column>{{
'HELM_CHART.STATUS' | translate
}}</clr-dg-column>
<clr-dg-column>{{
'HELM_CHART.CHARTVERSIONS' | translate
}}</clr-dg-column>
<clr-dg-column>{{
'HELM_CHART.CREATED' | translate
}}</clr-dg-column>
<clr-dg-placeholder>{{
'HELM_CHART.PLACEHOLDER' | translate
}}</clr-dg-placeholder>
<clr-dg-row
*clrDgItems="let chart of charts"
[clrDgItem]="chart">
<clr-dg-cell>
<span class="list-img">
<img
class="size-24 margin-right-12"
[src]="
chart.icon ? chart.icon : chartDefaultIcon
"
(error)="getDefaultIcon(chart)" />
</span>
<a
href="javascript:void(0)"
(click)="onChartClick(chart)"
>{{ chart.name }}</a
>
</clr-dg-cell>
<clr-dg-cell class="table-center">{{
getStatusString(chart) | translate
}}</clr-dg-cell>
<clr-dg-cell class="table-center">{{
chart.total_versions
}}</clr-dg-cell>
<clr-dg-cell class="table-center">{{
chart.created | harborDatetime
}}</clr-dg-cell>
</clr-dg-row>
<clr-dg-footer>
<clr-dg-pagination
#pagination
[clrDgPageSize]="pageSize"
[clrDgTotalItems]="totalCount">
<clr-dg-page-size [clrPageSizeOptions]="[15, 25, 50]">{{
'PAGINATION.PAGE_SIZE' | translate
}}</clr-dg-page-size>
<span *ngIf="totalCount">
{{ pagination.firstItem + 1 }} -
{{ pagination.lastItem + 1 }}
{{ 'HELM_CHART.OF' | translate }}
</span>
{{ totalCount }} {{ 'HELM_CHART.ITEMS' | translate }}
</clr-dg-pagination>
</clr-dg-footer>
</clr-datagrid>
</div>
</div>
<div *ngIf="isCardView" class="row card-container version-position">
<div *ngFor="let item of charts" class="chart-card">
<a
let
i="index;"
class="card clickable"
(click)="onChartClick(item)">
<div class="card-header">
<div class="card-icon">
<img
class="size-60"
[src]="item.icon ? item.icon : chartDefaultIcon"
(error)="getDefaultIcon(item)" />
</div>
<div class="card-title">{{ item.name }}</div>
</div>
<div class="card-footer">
<div class="row flex-items-xs-between">
<div>
<span class="version-text">{{
item.total_versions
}}</span>
<label
class="card-label"
*ngIf="item.total_versions !== 1"
>{{ 'HELM_CHART.CHARTVERSIONS' | translate }}
</label>
<label
class="card-label"
*ngIf="item.total_versions === 1"
>{{ 'HELM_CHART.VERSION' | translate }}
</label>
</div>
<div>
<span
class="label"
[class.label-danger]="item.deprecated"
[class.label-success]="!item.deprecated"
>{{ getStatusString(item) | translate }}</span
>
</div>
</div>
</div>
</a>
</div>
<div *ngIf="loading" class="center-x">
<span class="vertical-helper"></span>
<span class="spinner"></span>
</div>
</div>
<confirmation-dialog
#confirmationDialog
(confirmAction)="confirmDeletion($event)"></confirmation-dialog>
<clr-modal
[(clrModalOpen)]="isUploadModalOpen"
[clrModalStaticBackdrop]="true"
[clrModalClosable]="false">
<h3 class="modal-title">
{{ 'HELM_CHART.UPLOAD_TITLE' | translate | titlecase }}
</h3>
<div class="modal-body">
<form
#chartUploadForm="ngForm"
clrForm
enctype="multipart/form-data">
<div class="clr-form-control">
<label
class="filename-label clr-control-label clr-col-md-3">
{{ 'HELM_CHART.CHART_FILE' | translate }}</label
>
<div class="clr-input-wrapper">
<input
class="filename-input clr-input"
type="text"
placeholder="{{
this.chartFile?.name || 'BUTTON.NO_FILE'
| translate
}}"
disabled />
<label
for="chart"
class="btn btn-secondary file-browser-btn"
>{{ 'BUTTON.BROWSE' | translate }}
</label>
<input
class="file-input"
type="file"
id="chart"
name="chart"
ngModel
(change)="onChartFileChangeEvent($event)" />
</div>
</div>
<div class="clr-form-control mb-10">
<label
class="filename-label clr-control-label clr-col-md-3">
{{ 'HELM_CHART.CHART_PROV' | translate }}
</label>
<div class="clr-input-wrapper">
<input
class="filename-input clr-input"
type="text"
placeholder="{{
this.provFile?.name || 'BUTTON.NO_FILE'
| translate
}}"
disabled />
<label
for="prov"
class="btn btn-secondary file-browser-btn"
>{{ 'BUTTON.BROWSE' | translate }}
</label>
<input
class="file-input"
type="file"
id="prov"
name="prov"
ngModel
(change)="onProvFileChangeEvent($event)" />
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button
class="btn btn-secondary"
[disabled]="isUploading"
(click)="cancelUpload()">
<span>{{ 'BUTTON.CANCEL' | translate }}</span>
</button>
<button
type="submit"
class="btn btn-primary"
id="upload-chart"
[disabled]="isUploading"
(click)="upload()">
<span>{{ 'HELM_CHART.UPLOAD' | translate }}</span>
<span *ngIf="isUploading" class="spinner spinner-inline">
Loading...
</span>
</button>
</div>
</clr-modal>
</div>

View File

@ -1,148 +0,0 @@
@import "../../../../../shared/mixin";
$size24:24px;
$size60:60px;
@mixin flex-center {
display: flex;
justify-content: center;
align-items: center;
}
.chart-tool {
position: relative;
margin-top: 5px;
.toolbar {
overflow: hidden;
/* stylelint-disable */
.rightPos {
@include grid-right-top-pos;
margin-top: 20px;
.filter-divider {
display: inline-block;
height: 16px;
width: 1px;
padding-top: 12px;
padding-bottom: 12px;
position: relative;
top: 9px;
margin-right: 6px;
margin-left: 6px;
}
}
}
}
.card-container {
margin-top: 40px;
.chart-card {
width: 200px;
margin: 10px;
}
.card-header {
.card-icon {
@include flex-center;
}
.card-title {
@include text-overflow;
text-align:center;
margin:15px;
}
}
.card-footer {
background-color:#d7d7d7;
.version-text {
font-size:1.1rem;
}
.card-label {
width: 60px;
display: inline-block;
}
}
}
.vertical-helper {
display: inline-block;
height: 100%;
vertical-align: middle;
}
.size-24 {
width:$size24;
height:$size24;
}
.size-60 {
height:$size60;
max-width:100%;
}
.margin-right-12 {
margin-right:12px;
}
.file-input {
display: none;
}
.filename-span {
@include text-overflow;
display: inline-block;
width: 50%;
vertical-align: top;
}
clr-modal {
.filename-label {
padding-top: 9px;
}
.filename-input {
margin-top: 12px;
width: 68%;
}
.file-browser-btn {
margin-left: 15px;
max-width: 34%;
}
}
button {
clr-icon {
margin-right: 6px;
}
}
.mb-10 {
margin-bottom:10px;
}
.table-center {
display: flex;
align-items: center;
}
.center-x {
display: flex;
justify-content: center;
position: absolute;
top: 2.5rem;
left: 50%;
transform: translateX(-50%);
}
.version-position {
position: relative;
}

View File

@ -1,104 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { HelmChartComponent } from './helm-chart.component';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ClarityModule } from '@clr/angular';
import { FormsModule } from '@angular/forms';
import { of } from 'rxjs';
import { HelmChartService } from '../../helm-chart-detail/helm-chart.service';
import { ErrorHandler } from '../../../../../shared/units/error-handler';
import {
SystemInfoService,
UserPermissionService,
} from '../../../../../shared/services';
import { OperationService } from '../../../../../shared/components/operation/operation.service';
describe('HelmChartComponent', () => {
let component: HelmChartComponent;
let fixture: ComponentFixture<HelmChartComponent>;
const mockErrorHandler = null;
const mockSystemInfoService = {
getSystemInfo: () => {
return of({
with_notary: false,
with_admiral: false,
admiral_endpoint: '',
auth_mode: 'oidc_auth',
registry_url: 'nightly-oidc.harbor.io',
external_url: 'https://nightly-oidc.harbor.io',
project_creation_restriction: 'everyone',
self_registration: false,
has_ca_root: false,
harbor_version: 'dev',
registry_storage_provider_name: 'filesystem',
read_only: false,
with_chartmuseum: true,
notification_enable: true,
});
},
};
const mockHelmChartService = {
getChartVersions() {
return of([
{
name: 'string',
home: 'string',
sources: [],
version: 'string',
description: 'string',
keywords: [],
maintainers: [],
engine: 'string',
icon: 'string',
appVersion: 'string',
apiVersion: 'string',
urls: [],
created: 'string',
digest: 'string',
labels: [],
},
]);
},
getHelmCharts() {
return of([]);
},
};
const mockUserPermissionService = {
getPermission() {
return of(true);
},
};
const mockOperationService = {
publishInfo: () => {
return of([]);
},
};
beforeEach(async () => {
await TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
imports: [ClarityModule, TranslateModule.forRoot(), FormsModule],
declarations: [HelmChartComponent],
providers: [
TranslateService,
{ provide: ErrorHandler, useValue: mockErrorHandler },
{ provide: SystemInfoService, useValue: mockSystemInfoService },
{ provide: HelmChartService, useValue: mockHelmChartService },
{
provide: UserPermissionService,
useValue: mockUserPermissionService,
},
{ provide: OperationService, useValue: mockOperationService },
],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(HelmChartComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,392 +0,0 @@
import {
Component,
EventEmitter,
Input,
OnInit,
Output,
ViewChild,
} from '@angular/core';
import { NgForm } from '@angular/forms';
import { TranslateService } from '@ngx-translate/core';
import { forkJoin, Observable, throwError as observableThrowError } from 'rxjs';
import { catchError, finalize, map } from 'rxjs/operators';
import { HelmChartItem } from '../../helm-chart-detail/helm-chart.interface.service';
import { HelmChartService } from '../../helm-chart-detail/helm-chart.service';
import {
State,
SystemInfo,
SystemInfoService,
UserPermissionService,
USERSTATICPERMISSION,
} from '../../../../../shared/services';
import {
downloadFile,
getPageSizeFromLocalStorage,
PageSizeMapKeys,
setPageSizeToLocalStorage,
} from '../../../../../shared/units/utils';
import { ErrorHandler } from '../../../../../shared/units/error-handler';
import { OperationService } from '../../../../../shared/components/operation/operation.service';
import {
operateChanges,
OperateInfo,
OperationState,
} from '../../../../../shared/components/operation/operate';
import {
ConfirmationButtons,
ConfirmationState,
ConfirmationTargets,
DefaultHelmIcon,
Roles,
} from '../../../../../shared/entities/shared.const';
import { errorHandler } from '../../../../../shared/units/shared.utils';
import { ConfirmationDialogComponent } from '../../../../../shared/components/confirmation-dialog';
import { ConfirmationMessage } from '../../../../global-confirmation-dialog/confirmation-message';
import { ConfirmationAcknowledgement } from '../../../../global-confirmation-dialog/confirmation-state-message';
import { ClrDatagridStateInterface } from '@clr/angular';
@Component({
selector: 'hbr-helm-chart',
templateUrl: './helm-chart.component.html',
styleUrls: ['./helm-chart.component.scss'],
})
export class HelmChartComponent implements OnInit {
signedCon: { [key: string]: any | string[] } = {};
@Input() projectId: number;
@Input() projectName = 'unknown';
@Input() urlPrefix: string;
@Input() hasSignedIn: boolean;
@Input() projectRoleID = Roles.OTHER;
@Output() chartClickEvt = new EventEmitter<any>();
@Output() chartDownloadEve = new EventEmitter<string>();
@Input() chartDefaultIcon: string = DefaultHelmIcon;
lastFilteredChartName: string;
charts: HelmChartItem[] = [];
chartsCopy: HelmChartItem[] = [];
systemInfo: SystemInfo;
selectedRows: HelmChartItem[] = [];
loading = true;
// For Upload
isUploading = false;
isUploadModalOpen = false;
provFile: File;
chartFile: File;
// For View swtich
isCardView: boolean;
cardHover = false;
listHover = false;
pageSize: number = getPageSizeFromLocalStorage(
PageSizeMapKeys.HELM_CHART_COMPONENT
);
currentPage = 1;
totalCount = 0;
currentState: State;
@ViewChild('chartUploadForm') uploadForm: NgForm;
@ViewChild('confirmationDialog')
confirmationDialog: ConfirmationDialogComponent;
hasUploadHelmChartsPermission: boolean;
hasDownloadHelmChartsPermission: boolean;
hasDeleteHelmChartsPermission: boolean;
constructor(
private errorHandlerEntity: ErrorHandler,
private translateService: TranslateService,
private systemInfoService: SystemInfoService,
private helmChartService: HelmChartService,
private userPermissionService: UserPermissionService,
private operationService: OperationService
) {}
public get registryUrl(): string {
return this.systemInfo ? this.systemInfo.registry_url : '';
}
ngOnInit(): void {
// Get system info for tag views
this.systemInfoService.getSystemInfo().subscribe(
systemInfo => (this.systemInfo = systemInfo),
error => this.errorHandlerEntity.error(error)
);
this.lastFilteredChartName = '';
this.refresh();
this.getHelmPermissionRule(this.projectId);
}
getHelmPermissionRule(projectId: number): void {
let hasUploadHelmChartsPermission =
this.userPermissionService.getPermission(
projectId,
USERSTATICPERMISSION.HELM_CHART.KEY,
USERSTATICPERMISSION.HELM_CHART.VALUE.UPLOAD
);
let hasDownloadHelmChartsPermission =
this.userPermissionService.getPermission(
projectId,
USERSTATICPERMISSION.HELM_CHART.KEY,
USERSTATICPERMISSION.HELM_CHART.VALUE.DOWNLOAD
);
let hasDeleteHelmChartsPermission =
this.userPermissionService.getPermission(
projectId,
USERSTATICPERMISSION.HELM_CHART.KEY,
USERSTATICPERMISSION.HELM_CHART.VALUE.DELETE
);
forkJoin(
hasUploadHelmChartsPermission,
hasDownloadHelmChartsPermission,
hasDeleteHelmChartsPermission
).subscribe(
permissions => {
this.hasUploadHelmChartsPermission = permissions[0] as boolean;
this.hasDownloadHelmChartsPermission =
permissions[1] as boolean;
this.hasDeleteHelmChartsPermission = permissions[2] as boolean;
},
error => this.errorHandlerEntity.error(error)
);
}
updateFilterValue(value: string) {
this.lastFilteredChartName = value;
this.refresh();
}
refresh() {
this.loading = true;
this.selectedRows = [];
this.helmChartService
.getHelmCharts(this.projectName)
.pipe(
finalize(() => {
this.loading = false;
})
)
.subscribe(
charts => {
this.charts = charts.filter(x =>
x.name.includes(this.lastFilteredChartName)
);
this.chartsCopy = charts.map(x => Object.assign({}, x));
this.totalCount = charts.length;
},
err => {
this.errorHandlerEntity.error(err);
}
);
}
onChartClick(item: HelmChartItem) {
this.chartClickEvt.emit(item.name);
}
resetUploadForm() {
this.chartFile = null;
this.provFile = null;
this.uploadForm.reset();
}
onChartUpload() {
this.resetUploadForm();
this.isUploadModalOpen = true;
}
cancelUpload() {
this.resetUploadForm();
this.isUploadModalOpen = false;
}
upload() {
if (!this.chartFile && !this.provFile) {
return;
}
if (this.isUploading) {
return;
}
this.isUploading = true;
this.helmChartService
.uploadChart(this.projectName, this.chartFile, this.provFile)
.pipe(
finalize(() => {
this.isUploading = false;
this.isUploadModalOpen = false;
this.refresh();
})
)
.subscribe(
() => {
this.translateService
.get('HELM_CHART.FILE_UPLOADED')
.subscribe(res => this.errorHandlerEntity.info(res));
},
err => this.errorHandlerEntity.error(err)
);
}
onChartFileChangeEvent(event) {
if (event.target.files && event.target.files.length > 0) {
this.chartFile = event.target.files[0];
}
}
onProvFileChangeEvent(event) {
if (event.target.files && event.target.files.length > 0) {
this.provFile = event.target.files[0];
}
}
deleteChart(chartName: string): Observable<any> {
let operateMsg = new OperateInfo();
operateMsg.name = 'OPERATION.DELETE_CHART';
operateMsg.data.id = chartName;
operateMsg.state = OperationState.progressing;
operateMsg.data.name = chartName;
this.operationService.publishInfo(operateMsg);
return this.helmChartService
.deleteHelmChart(this.projectName, chartName)
.pipe(
map(() => operateChanges(operateMsg, OperationState.success)),
catchError(error => {
const message = errorHandler(error);
this.translateService
.get(message)
.subscribe(res =>
operateChanges(
operateMsg,
OperationState.failure,
res
)
);
return observableThrowError(error);
})
);
}
deleteCharts(charts: HelmChartItem[]) {
if (charts && charts.length < 1) {
return;
}
let chartsDelete$ = charts.map(chart => this.deleteChart(chart.name));
forkJoin(chartsDelete$)
.pipe(
finalize(() => {
this.refresh();
this.selectedRows = [];
})
)
.subscribe(
() => {},
error => {
this.errorHandlerEntity.error(error);
}
);
}
downloadLatestVersion(evt?: Event, item?: HelmChartItem) {
if (evt) {
evt.stopPropagation();
}
let selectedChart: HelmChartItem;
if (item) {
selectedChart = item;
} else {
// return if selected version less then 1
if (this.selectedRows.length < 1) {
return;
}
selectedChart = this.selectedRows[0];
}
if (!selectedChart) {
return;
}
let filename = `charts/${selectedChart.name}-${selectedChart.latest_version}.tgz`;
this.helmChartService
.downloadChart(this.projectName, filename)
.subscribe(
res => {
downloadFile(res);
},
error => {
this.errorHandlerEntity.error(error);
}
);
}
openChartDeleteModal() {
let chartNames = this.selectedRows.map(chart => chart.name).join(',');
let message = new ConfirmationMessage(
'HELM_CHART.DELETE_CHART_VERSION_TITLE',
'HELM_CHART.DELETE_CHART_VERSION',
chartNames,
this.selectedRows,
ConfirmationTargets.HELM_CHART,
ConfirmationButtons.DELETE_CANCEL
);
this.confirmationDialog.open(message);
}
confirmDeletion(message: ConfirmationAcknowledgement) {
if (
message &&
message.source === ConfirmationTargets.HELM_CHART &&
message.state === ConfirmationState.CONFIRMED
) {
let charts = message.data;
this.deleteCharts(charts);
}
}
showCard(cardView: boolean) {
if (this.isCardView === cardView) {
return;
}
this.isCardView = cardView;
}
mouseEnter(itemName: string) {
if (itemName === 'card') {
this.cardHover = true;
} else {
this.listHover = true;
}
}
mouseLeave(itemName: string) {
if (itemName === 'card') {
this.cardHover = false;
} else {
this.listHover = false;
}
}
isHovering(itemName: string) {
if (itemName === 'card') {
return this.cardHover;
} else {
return this.listHover;
}
}
getDefaultIcon(chart: HelmChartItem) {
chart.icon = this.chartDefaultIcon;
}
getStatusString(chart: HelmChartItem) {
if (chart.deprecated) {
return 'HELM_CHART.DEPRECATED';
} else {
return 'HELM_CHART.ACTIVE';
}
}
clrLoad(state: ClrDatagridStateInterface) {
if (state?.page?.size) {
setPageSizeToLocalStorage(
PageSizeMapKeys.HELM_CHART_COMPONENT,
state.page.size
);
}
}
}

View File

@ -1,8 +0,0 @@
<hbr-helm-chart
[projectId]="projectId"
[projectName]="projectName"
[urlPrefix]="urlPrefix"
[hasSignedIn]="hasSignedIn"
[projectRoleID]="project_member_role_id"
(chartClickEvt)="onChartClick($event)">
</hbr-helm-chart>

View File

@ -1,53 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { SessionService } from '../../../../shared/services/session.service';
import { ListChartsComponent } from './list-charts.component';
import { SharedTestingModule } from '../../../../shared/shared.module';
describe('ListChartsComponent', () => {
let component: ListChartsComponent;
let fixture: ComponentFixture<ListChartsComponent>;
let fakeSessionService = {
getCurrentUser: function () {
return 'admin';
},
};
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [ListChartsComponent],
imports: [SharedTestingModule],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
providers: [
{
provide: ActivatedRoute,
useValue: {
snapshot: {
parent: {
parent: {
params: {
id: 1,
},
data: null,
},
},
},
},
},
{ provide: Router, useValue: null },
{ provide: SessionService, useValue: fakeSessionService },
],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ListChartsComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,44 +0,0 @@
import { Project } from '../../project';
import { Component, OnInit } from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { SessionService } from '../../../../shared/services/session.service';
import { SessionUser } from '../../../../shared/entities/session-user';
@Component({
selector: 'project-list-charts',
templateUrl: './list-charts.component.html',
styleUrls: ['./list-charts.component.scss'],
})
export class ListChartsComponent implements OnInit {
projectId: number;
projectName: string;
urlPrefix: string;
hasSignedIn: boolean;
project_member_role_id: number;
currentUser: SessionUser;
constructor(
private route: ActivatedRoute,
private router: Router,
private session: SessionService
) {}
ngOnInit() {
// Get projectId from router-guard params snapshot.
this.projectId = +this.route.snapshot.parent.parent.params['id'];
// Get current user from registered resolver.
this.currentUser = this.session.getCurrentUser();
let resolverData = this.route.snapshot.parent.parent.data;
if (resolverData) {
let project = <Project>resolverData['projectResolver'];
this.projectName = project.name;
this.project_member_role_id = project.current_user_role_id;
}
}
onChartClick(chartName: string) {
this.router.navigateByUrl(`${this.router.url}/${chartName}/versions`);
}
}

View File

@ -64,7 +64,6 @@ export class ProjectDetailComponent
roleName: string;
projectId: number;
hasProjectReadPermission: boolean;
hasHelmChartsListPermission: boolean;
hasRepositoryListPermission: boolean;
hasMemberListPermission: boolean;
hasLabelListPermission: boolean;
@ -91,13 +90,6 @@ export class ProjectDetailComponent
showTabName: 'PROJECT_DETAIL.REPOSITORIES',
permissions: () => this.hasRepositoryListPermission,
},
{
linkName: 'helm-charts',
tabLinkInOverflow: false,
showTabName: 'PROJECT_DETAIL.HELMCHART',
permissions: () =>
this.withHelmChart && this.hasHelmChartsListPermission,
},
{
linkName: 'members',
tabLinkInOverflow: false,
@ -109,9 +101,7 @@ export class ProjectDetailComponent
tabLinkInOverflow: false,
showTabName: 'PROJECT_DETAIL.LABELS',
permissions: () =>
this.hasLabelListPermission &&
this.hasLabelCreatePermission &&
!this.withAdmiral,
this.hasLabelListPermission && this.hasLabelCreatePermission,
},
{
linkName: 'scanner',
@ -272,13 +262,6 @@ export class ProjectDetailComponent
USERSTATICPERMISSION.REPOSITORY.VALUE.LIST
)
);
permissionsList.push(
this.userPermissionService.getPermission(
projectId,
USERSTATICPERMISSION.HELM_CHART.KEY,
USERSTATICPERMISSION.HELM_CHART.VALUE.LIST
)
);
permissionsList.push(
this.userPermissionService.getPermission(
projectId,
@ -345,7 +328,6 @@ export class ProjectDetailComponent
this.hasMemberListPermission,
this.hasLabelListPermission,
this.hasRepositoryListPermission,
this.hasHelmChartsListPermission,
this.hasRobotListPermission,
this.hasLabelCreatePermission,
this.hasTagRetentionPermission,
@ -383,14 +365,6 @@ export class ProjectDetailComponent
return this.sessionService.getCurrentUser() != null;
}
public get withAdmiral(): boolean {
return this.appConfigService.getConfig().with_admiral;
}
public get withHelmChart(): boolean {
return this.appConfigService.getConfig().with_chartmuseum;
}
backToProject(): void {
if (window.sessionStorage) {
window.sessionStorage.setItem('fromDetails', 'true');

View File

@ -51,20 +51,6 @@ const routes: Routes = [
m => m.RepositoryModule
),
},
{
path: 'helm-charts',
canActivate: [MemberPermissionGuard],
data: {
permissionParam: {
resource: USERSTATICPERMISSION.HELM_CHART.KEY,
action: USERSTATICPERMISSION.HELM_CHART.VALUE.LIST,
},
},
loadChildren: () =>
import(
'./helm-chart/helm-chart-list/helm-chart-list.module'
).then(m => m.HelmChartListModule),
},
{
path: 'members',
canActivate: [MemberPermissionGuard],

View File

@ -23,7 +23,6 @@ export class Project {
update_time: Date;
current_user_role_id: number;
repo_count: number;
chart_count: number;
has_project_admin_role: boolean;
is_member: boolean;
role_name: string;

View File

@ -1,5 +1,3 @@
import { HelmChartMaintainer } from '../../../helm-chart/helm-chart-detail/helm-chart.interface.service';
export class ArtifactBuildHistory {
created: Date;
created_by: string;
@ -9,22 +7,6 @@ export interface ArtifactDependency {
version: string;
repository: string;
}
export interface ArtifactSummary {
name: string;
home: string;
sources: string[];
version: string;
description: string;
keywords: string[];
maintainers: HelmChartMaintainer[];
engine: string;
icon: string;
appVersion: string;
urls: string[];
created?: string;
digest: string;
}
export interface Addition {
type: string;
data?: object;

View File

@ -170,61 +170,6 @@
</button>
</div>
</div>
<div
class="card clickable"
*ngIf="hasReadChartPermission && withHelmChart">
<div class="card-header">
<div>{{ 'PROJECT_DETAIL.HELMCHART' | translate }}</div>
<div class="clr-row number">
{{
summaryInformation?.chart_count
? summaryInformation?.chart_count
: 0
}}
</div>
<div class="clr-row">
<div class="clr-col-4 column">
{{ 'HELM_CHART.NAME' | translate }}
</div>
<div class="clr-col-2 column">
{{ 'HELM_CHART.STATUS' | translate }}
</div>
<div class="clr-col-2 column">
{{ 'HELM_CHART.CHARTVERSIONS' | translate }}
</div>
<div class="clr-col-4 column">
{{ 'HELM_CHART.CREATED' | translate }}
</div>
</div>
</div>
<div class="card-block">
<div class="clr-row" *ngFor="let chart of charts">
<div class="clr-col-4 ellipsis chart-name">
<img
class="size-24 mr-5px"
[src]="chart.icon ? chart.icon : chartDefaultIcon"
(error)="getDefaultIcon(chart)" />
<a
href="javascript:void(0)"
(click)="onChartClick(chart.name)"
>{{ chart.name }}</a
>
</div>
<div class="clr-col-2">
{{ getStatusString(chart) | translate }}
</div>
<div class="clr-col-2">{{ chart.total_versions }}</div>
<div class="clr-col-4 ellipsis">
{{ chart.created | harborDatetime: 'short' }}
</div>
</div>
</div>
<div class="card-footer">
<button class="btn btn-link" (click)="goToCharts()">
{{ 'SUMMARY.SEE_ALL' | translate }}
</button>
</div>
</div>
<div class="card clickable" *ngIf="showProjectMemberInfo">
<div class="card-header no-underline">
<div>{{ 'PROJECT_DETAIL.USERS' | translate }}</div>

View File

@ -140,7 +140,6 @@ describe('SummaryComponent', () => {
it('should show two cards', async () => {
component.summaryInformation = mockedSummaryInformation;
component.isCardView = true;
component.hasReadChartPermission = true;
fixture.detectChanges();
await fixture.whenStable();
const cards = fixture.nativeElement.querySelectorAll('.card');

View File

@ -9,7 +9,6 @@ import {
} from '../../../shared/services';
import { ErrorHandler } from '../../../shared/units/error-handler';
import {
DefaultHelmIcon,
FALSE_STR,
PROJECT_SUMMARY_CARD_VIEW_LOCALSTORAGE_KEY,
TRUE_STR,
@ -17,8 +16,6 @@ import {
import { RepositoryService } from '../../../../../ng-swagger-gen/services/repository.service';
import { Project } from '../../../../../ng-swagger-gen/models/project';
import { Repository } from '../../../../../ng-swagger-gen/models/repository';
import { HelmChartItem } from '../helm-chart/helm-chart-detail/helm-chart.interface.service';
import { HelmChartService } from '../helm-chart/helm-chart-detail/helm-chart.service';
@Component({
selector: 'summary',
@ -28,7 +25,6 @@ import { HelmChartService } from '../helm-chart/helm-chart-detail/helm-chart.ser
export class SummaryComponent implements OnInit {
showProjectMemberInfo: boolean = false;
hasReadRepoPermission: boolean = false;
hasReadChartPermission: boolean = false;
projectId: number;
projectName: string;
summaryInformation: any;
@ -37,8 +33,6 @@ export class SummaryComponent implements OnInit {
cardHover: boolean = false;
listHover: boolean = false;
repos: Repository[] = [];
charts: HelmChartItem[] = [];
chartDefaultIcon: string = DefaultHelmIcon;
constructor(
private projectService: ProjectService,
private userPermissionService: UserPermissionService,
@ -46,8 +40,7 @@ export class SummaryComponent implements OnInit {
private appConfigService: AppConfigService,
private route: ActivatedRoute,
private repoService: RepositoryService,
private router: Router,
private helmChartService: HelmChartService
private router: Router
) {
if (localStorage) {
if (
@ -83,17 +76,12 @@ export class SummaryComponent implements OnInit {
resource: USERSTATICPERMISSION.REPOSITORY.KEY,
action: USERSTATICPERMISSION.REPOSITORY.VALUE.LIST,
},
{
resource: USERSTATICPERMISSION.HELM_CHART.KEY,
action: USERSTATICPERMISSION.HELM_CHART.VALUE.LIST,
},
];
this.userPermissionService
.hasProjectPermissions(this.projectId, permissions)
.subscribe((results: Array<boolean>) => {
this.showProjectMemberInfo = results[0];
this.hasReadRepoPermission = results[1];
this.hasReadChartPermission = results[2];
});
this.projectService.getProjectSummary(this.projectId).subscribe(
res => {
@ -107,10 +95,6 @@ export class SummaryComponent implements OnInit {
this.getDataForCardView();
}
}
public get withHelmChart(): boolean {
return this.appConfigService.getConfig().with_chartmuseum;
}
showCard(cardView: boolean) {
if (this.isCardView === cardView) {
return;
@ -159,7 +143,6 @@ export class SummaryComponent implements OnInit {
}
getDataForCardView() {
this.getTop4Repos();
this.getTop4Charts();
}
getTop4Repos() {
if (this.hasReadRepoPermission) {
@ -174,17 +157,6 @@ export class SummaryComponent implements OnInit {
});
}
}
getTop4Charts() {
if (this.hasReadChartPermission) {
this.helmChartService
.getHelmCharts(this.projectName)
.subscribe(res => {
if (res && res.length) {
this.charts = res.slice(0, 4);
}
});
}
}
goIntoRepo(repoEvt: Repository): void {
const linkUrl = [
'harbor',
@ -199,31 +171,6 @@ export class SummaryComponent implements OnInit {
const linkUrl = ['harbor', 'projects', this.projectId, 'repositories'];
this.router.navigate(linkUrl);
}
getDefaultIcon(chart: HelmChartItem) {
chart.icon = this.chartDefaultIcon;
}
getStatusString(chart: HelmChartItem) {
if (chart.deprecated) {
return 'HELM_CHART.DEPRECATED';
} else {
return 'HELM_CHART.ACTIVE';
}
}
onChartClick(chartName: string) {
const linkUrl = [
'harbor',
'projects',
this.projectId,
'helm-charts',
chartName,
'versions',
];
this.router.navigate(linkUrl);
}
goToCharts() {
const linkUrl = ['harbor', 'projects', this.projectId, 'helm-charts'];
this.router.navigate(linkUrl);
}
goToMembers() {
const linkUrl = ['harbor', 'projects', this.projectId, 'members'];
this.router.navigate(linkUrl);

View File

@ -22,7 +22,6 @@ export enum RouteConfigId {
const ShouldNotReuseRouteRegExps: RegExp[] = [
/\/harbor\/projects\/(\d+)\/repositories$/,
/\/harbor\/projects\/(\d+)\/repositories\/(\S+)\/artifacts-tab$/,
/\/harbor\/projects\/(\d+)\/helm-charts\/(\S+)\/versions\/(\S+)/,
];
function testRoute(url: string) {

View File

@ -47,7 +47,6 @@ describe('AppConfigService', () => {
expect(req.request.method).toBe('GET');
req.flush(systeminfo);
expect(service.getConfig()).toEqual(systeminfo);
expect(service.isIntegrationMode()).toBeFalsy();
expect(service.isLdapMode()).toBeFalsy();
expect(service.isHttpAuthMode()).toBeFalsy();
expect(service.isOidcMode()).toBeFalsy();

View File

@ -62,14 +62,6 @@ export class AppConfigService {
return this.configurations;
}
public isIntegrationMode(): boolean {
return (
this.configurations &&
this.configurations.with_admiral &&
this.configurations.admiral_endpoint.trim() !== ''
);
}
public isLdapMode(): boolean {
return (
this.configurations &&

View File

@ -16,7 +16,6 @@ import { ClairDBStatus } from '../shared/services';
export class AppConfig {
with_notary: boolean;
with_admiral: boolean;
with_trivy: boolean;
admiral_endpoint: string;
auth_mode: string;
@ -29,13 +28,11 @@ export class AppConfig {
next_scan_all: number;
registry_storage_provider_name: string;
read_only: boolean;
with_chartmuseum: boolean;
show_popular_repo: boolean;
constructor() {
// Set default value
this.with_notary = false;
this.with_admiral = false;
this.with_trivy = false;
this.admiral_endpoint = '';
this.auth_mode = 'db_auth';
@ -51,7 +48,6 @@ export class AppConfig {
this.next_scan_all = 0;
this.registry_storage_provider_name = '';
this.read_only = false;
this.with_chartmuseum = false;
this.show_popular_repo = false;
}
}

View File

@ -84,10 +84,6 @@ export class GlobalSearchComponent implements OnInit, OnDestroy {
this.closeSub = this.searchTrigger.searchClearChan$.subscribe(clear => {
this.searchTerm = '';
});
if (this.appConfigService.isIntegrationMode()) {
this.placeholderText = 'GLOBAL_SEARCH.PLACEHOLDER_VIC';
}
// init _searchTerm from queryParams
this._searchTerm = this.activatedRoute.snapshot.queryParams[SEARCH_KEY];
if (this._searchTerm) {

View File

@ -20,10 +20,5 @@
<list-repository-ro
[repositories]="searchResults.repository"></list-repository-ro>
</div>
<div *ngIf="withHelmChart" id="chart-results">
<h2>{{ 'HELM_CHART.HELMCHARTS' | translate }}</h2>
<list-chart-version-ro
[charts]="searchResults.chart"></list-chart-version-ro>
</div>
</div>
</div>

View File

@ -50,8 +50,7 @@ export class SearchResultComponent implements OnInit, OnDestroy {
constructor(
private search: GlobalSearchService,
private msgHandler: MessageHandlerService,
private searchTrigger: SearchTriggerService,
private appConfigService: AppConfigService
private searchTrigger: SearchTriggerService
) {}
ngOnInit() {
@ -61,9 +60,6 @@ export class SearchResultComponent implements OnInit, OnDestroy {
if (term === '') {
this.searchResults.project = [];
this.searchResults.repository = [];
if (this.withHelmChart) {
this.searchResults.chart = [];
}
}
return !!(term && term.trim());
}),
@ -116,11 +112,6 @@ export class SearchResultComponent implements OnInit, OnDestroy {
src.repository.forEach(repo =>
res.repository.push(Object.assign({}, repo))
);
if (this.withHelmChart) {
src.chart.forEach(chart =>
res.chart.push(JSON.parse(JSON.stringify(chart)))
);
}
return res;
}
@ -157,9 +148,6 @@ export class SearchResultComponent implements OnInit, OnDestroy {
if (!term || term.trim() === '') {
this.searchResults.project = [];
this.searchResults.repository = [];
if (this.withHelmChart) {
this.searchResults.chart = [];
}
return;
}
// Do nothing if search is ongoing
@ -188,7 +176,4 @@ export class SearchResultComponent implements OnInit, OnDestroy {
}
);
}
get withHelmChart(): boolean {
return this.appConfigService.getConfig().with_chartmuseum;
}
}

View File

@ -12,17 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Project } from '../../../base/project/project';
import { HelmChartSearchResultItem } from '../../../base/project/helm-chart/helm-chart-detail/helm-chart.interface.service';
import { Repository } from '../../../../../ng-swagger-gen/models/repository';
export class SearchResults {
constructor() {
this.project = [];
this.repository = [];
this.chart = [];
}
project: Project[];
repository: Repository[];
chart: HelmChartSearchResultItem[];
}

View File

@ -1,40 +0,0 @@
<clr-datagrid>
<clr-dg-column>{{ 'HELM_CHART.NAME' | translate }}</clr-dg-column>
<clr-dg-column>{{ 'HELM_CHART.VERSION' | translate }}</clr-dg-column>
<clr-dg-column>{{ 'HELM_CHART.STATUS' | translate }}</clr-dg-column>
<clr-dg-column>{{ 'HELM_CHART.MAINTAINERS' | translate }}</clr-dg-column>
<clr-dg-column>{{ 'HELM_CHART.CREATED' | translate }}</clr-dg-column>
<clr-dg-placeholder>{{
'HELM_CHART.NO_VERSION_PLACEHOLDER' | translate
}}</clr-dg-placeholder>
<clr-dg-row *clrDgItems="let chart of charts" [clrDgItem]="chart">
<clr-dg-cell>
<a
href="javascript:void(0)"
(click)="gotoChartVersion(chart.Chart)"
>{{ chart.Name }}</a
>
</clr-dg-cell>
<clr-dg-cell>{{ chart.Chart.version }}</clr-dg-cell>
<clr-dg-cell>{{
getStatusString(chart.Chart) | translate
}}</clr-dg-cell>
<clr-dg-cell>{{
getMaintainerString(chart.Chart.maintainers)
| translate: getMaintainerTranslateInfo(chart.Chart.maintainers)
}}</clr-dg-cell>
<clr-dg-cell>{{ chart.Chart.created | harborDatetime }}</clr-dg-cell>
</clr-dg-row>
<clr-dg-footer>
<clr-dg-pagination
#pagination
[clrDgPageSize]="5"
[clrDgTotalItems]="charts?.length">
<span *ngIf="charts?.length">
{{ pagination.firstItem + 1 }} - {{ pagination.lastItem + 1 }}
{{ 'HELM_CHART.OF' | translate }}
</span>
{{ charts?.length }} {{ 'HELM_CHART.ITEMS' | translate }}
</clr-dg-pagination>
</clr-dg-footer>
</clr-datagrid>

View File

@ -1,46 +0,0 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ListChartVersionRoComponent } from './list-chart-version-ro.component';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { of } from 'rxjs';
import { SearchTriggerService } from '../global-search/search-trigger.service';
import { ProjectService } from '../../services';
import { SharedTestingModule } from '../../shared.module';
describe('ListChartVersionRoComponent', () => {
let component: ListChartVersionRoComponent;
let fixture: ComponentFixture<ListChartVersionRoComponent>;
const mockSearchTriggerService = {
closeSearch: () => {},
};
const mockProjectService = {
listProjects: () => {
return of({
body: [],
});
},
};
beforeEach(async () => {
await TestBed.configureTestingModule({
schemas: [CUSTOM_ELEMENTS_SCHEMA],
imports: [SharedTestingModule],
declarations: [ListChartVersionRoComponent],
providers: [
{ provide: ProjectService, useValue: mockProjectService },
{
provide: SearchTriggerService,
useValue: mockSearchTriggerService,
},
],
}).compileComponents();
});
beforeEach(() => {
fixture = TestBed.createComponent(ListChartVersionRoComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@ -1,75 +0,0 @@
import { Router } from '@angular/router';
import { Component, Input, ChangeDetectionStrategy } from '@angular/core';
import {
HelmChartSearchResultItem,
HelmChartVersion,
HelmChartMaintainer,
} from '../../../base/project/helm-chart/helm-chart-detail/helm-chart.interface.service';
import { SearchTriggerService } from '../global-search/search-trigger.service';
import { ProjectService } from '../../services';
@Component({
selector: 'list-chart-version-ro',
templateUrl: './list-chart-version-ro.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ListChartVersionRoComponent {
@Input() charts: HelmChartSearchResultItem[];
constructor(
private searchTrigger: SearchTriggerService,
private projectService: ProjectService,
private router: Router
) {}
getStatusString(chart: HelmChartVersion) {
if (chart.deprecated) {
return 'HELM_CHART.DEPRECATED';
} else {
return 'HELM_CHART.ACTIVE';
}
}
getMaintainerString(maintainers: HelmChartMaintainer[]) {
if (!maintainers || maintainers.length < 1) {
return '';
}
let maintainer_string = maintainers[0].name;
if (maintainers.length > 1) {
maintainer_string = 'HELM_CHART.OTHER_MAINTAINERS';
}
return maintainer_string;
}
getMaintainerTranslateInfo(maintainers: HelmChartMaintainer[]) {
if (!maintainers || maintainers.length < 1) {
return {};
}
let name = maintainers[0].name;
let number = maintainers.length;
return { name: name, number: number };
}
gotoChartVersion(chartVersion: HelmChartVersion) {
this.searchTrigger.closeSearch(true);
let [projectName, chartName] = chartVersion.name.split('/');
this.projectService.listProjects(projectName).subscribe(res => {
let projects = res.body || [];
if (projects || projects.length >= 1) {
let linkUrl = [
'harbor',
'projects',
projects[0].project_id,
'helm-charts',
chartName,
'versions',
chartVersion.version,
];
this.router.navigate(linkUrl);
} else {
return;
}
});
}
}

View File

@ -15,24 +15,10 @@
}}</span>
</a>
</div>
<div class="header-nav">
<a href="{{ admiralLink }}" class="nav-link" *ngIf="isIntegrationMode"
><span class="nav-text">{{ 'APP_TITLE.MGMT' | translate }}</span></a
>
<a
href="javascript:void(0)"
(click)="registryAction()"
routerLink="/harbor"
class="active nav-link"
*ngIf="isIntegrationMode"
><span class="nav-text">{{ 'APP_TITLE.REG' | translate }}</span></a
>
</div>
<global-search></global-search>
<div class="header-actions">
<clr-dropdown
class="dropdown-lang dropdown bottom-left"
*ngIf="!isIntegrationMode">
<clr-dropdown class="dropdown-lang dropdown bottom-left">
<button class="nav-icon nav-icon-width" clrDropdownToggle>
<clr-icon shape="world" class="icon-left"></clr-icon>
<span class="currentLocale">{{ currentLang }}</span>
@ -49,9 +35,7 @@
>
</clr-dropdown-menu>
</clr-dropdown>
<clr-dropdown
class="dropdown-locale dropdown bottom-left"
*ngIf="!isIntegrationMode">
<clr-dropdown class="dropdown-locale dropdown bottom-left">
<button class="nav-icon nav-icon-width" clrDropdownToggle>
<clr-icon shape="date" class="icon-left"></clr-icon>
<span class="currentLocale">{{

View File

@ -9,9 +9,6 @@ import { MessageHandlerService } from '../../services/message-handler.service';
import { SearchTriggerService } from '../global-search/search-trigger.service';
import { SkinableConfig } from '../../../services/skinable-config.service';
import { SharedTestingModule } from '../../shared.module';
import { TranslateService } from '@ngx-translate/core';
import { DeFaultLang } from '../../entities/shared.const';
import { of } from 'rxjs';
describe('NavigatorComponent', () => {
let component: TestComponentWrapperComponent;

View File

@ -84,10 +84,6 @@ export class NavigatorComponent implements OnInit {
this.translateClarityComponents();
}
this.selectedDatetimeRendering = getDatetimeRendering();
if (this.appConfigService.isIntegrationMode()) {
this.appTitle = 'APP_TITLE.VIC';
}
if (this.appConfigService.getConfig().read_only) {
this.msgHandler.handleReadOnly();
}
@ -126,7 +122,10 @@ export class NavigatorComponent implements OnInit {
}
public get currentLang(): string {
return LANGUAGES[this.selectedLang][0] as string;
if (this.selectedLang) {
return LANGUAGES[this.selectedLang][0] as string;
}
return null;
}
public get currentDatetimeRendering(): string {
@ -136,11 +135,6 @@ export class NavigatorComponent implements OnInit {
public get admiralLink(): string {
return this.appConfigService.getAdmiralEndpoint(window.location.href);
}
public get isIntegrationMode(): boolean {
return this.appConfigService.isIntegrationMode();
}
public get canDownloadCert(): boolean {
return (
this.session.getCurrentUser() &&

View File

@ -48,8 +48,6 @@ export const enum ConfirmationTargets {
CONFIG,
CONFIG_ROUTE,
CONFIG_TAB,
HELM_CHART,
HELM_CHART_VERSION,
STOP_EXECUTIONS,
SCANNER,
REPLICATION,

View File

@ -38,26 +38,11 @@ export class ModeGuard implements CanActivate, CanActivateChild {
): Observable<boolean> | boolean {
// Show the right sign-in page for different modes
return new Observable(observer => {
if (this.appConfigService.isIntegrationMode()) {
if (state.url.startsWith(CommonRoutes.SIGN_IN)) {
this.router.navigate(
[CommonRoutes.EMBEDDED_SIGN_IN],
route.queryParams
);
observer.next(false);
} else {
observer.next(true);
}
if (state.url.startsWith(CommonRoutes.EMBEDDED_SIGN_IN)) {
this.router.navigate([CommonRoutes.SIGN_IN], route.queryParams);
observer.next(false);
} else {
if (state.url.startsWith(CommonRoutes.EMBEDDED_SIGN_IN)) {
this.router.navigate(
[CommonRoutes.SIGN_IN],
route.queryParams
);
observer.next(false);
} else {
observer.next(true);
}
observer.next(true);
}
});
}

View File

@ -112,31 +112,6 @@ export const USERSTATICPERMISSION = {
DELETE: 'delete',
},
},
HELM_CHART: {
KEY: 'helm-chart',
VALUE: {
UPLOAD: 'create',
DOWNLOAD: 'read',
DELETE: 'delete',
LIST: 'list',
},
},
HELM_CHART_VERSION: {
KEY: 'helm-chart-version',
VALUE: {
DELETE: 'delete',
LIST: 'list',
CREATE: 'create',
READ: 'read',
},
},
HELM_CHART_VERSION_LABEL: {
KEY: 'helm-chart-version-label',
VALUE: {
CREATE: 'create',
DELETE: 'delete',
},
},
ROBOT: {
KEY: 'robot',
VALUE: {

View File

@ -62,17 +62,12 @@ import { LabelComponent } from './components/label/label.component';
import { LabelSignPostComponent } from './components/label/label-signpost/label-signpost.component';
import { LabelPieceComponent } from './components/label/label-piece/label-piece.component';
import { CreateEditLabelComponent } from './components/label/create-edit-label/create-edit-label.component';
import { ListChartVersionRoComponent } from './components/list-chart-version-ro/list-chart-version-ro.component';
import { DatePickerComponent } from './components/datetime-picker/datetime-picker.component';
import {
EndpointDefaultService,
EndpointService,
} from './services/endpoint.service';
import { ImageNameInputComponent } from './components/image-name-input/image-name-input.component';
import {
HelmChartDefaultService,
HelmChartService,
} from '../base/project/helm-chart/helm-chart-detail/helm-chart.service';
import { MessageHandlerService } from './services/message-handler.service';
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { RouterTestingModule } from '@angular/router/testing';
@ -142,7 +137,6 @@ ClarityIcons.add({
LabelPieceComponent,
CreateEditLabelComponent,
CronScheduleComponent,
ListChartVersionRoComponent,
DatePickerComponent,
ImageNameInputComponent,
HarborDatetimePipe,
@ -182,7 +176,6 @@ ClarityIcons.add({
LabelPieceComponent,
CreateEditLabelComponent,
CronScheduleComponent,
ListChartVersionRoComponent,
DatePickerComponent,
ImageNameInputComponent,
HarborDatetimePipe,
@ -198,7 +191,6 @@ ClarityIcons.add({
provide: ScanningResultService,
useClass: ScanningResultDefaultService,
},
{ provide: HelmChartService, useClass: HelmChartDefaultService },
],
})
export class SharedModule {}

View File

@ -957,8 +957,6 @@ export enum PageSizeMapKeys {
ARTIFACT_LIST_TAB_COMPONENT = 'ArtifactListTabComponent',
ARTIFACT_TAGS_COMPONENT = 'ArtifactTagComponent',
ARTIFACT_VUL_COMPONENT = 'ArtifactVulnerabilitiesComponent',
HELM_CHART_COMPONENT = 'HelmChartComponent',
CHART_VERSION_COMPONENT = 'ChartVersionComponent',
MEMBER_COMPONENT = 'MemberComponent',
LABEL_COMPONENT = 'LabelComponent',
P2P_POLICY_COMPONENT = 'P2pPolicyComponent',

View File

@ -651,7 +651,6 @@
"FLATTEN_LEVEL_TIP_1": "'Reduzierung um 1 Ebene'(Default): 'a/b/c/d/img' -> 'ns/b/c/d/img'",
"FLATTEN_LEVEL_TIP_2": "'Reduzierung um 2 Ebenen': 'a/b/c/d/img' -> 'ns/c/d/img'",
"FLATTEN_LEVEL_TIP_3": "'Reduzierung um 3 Ebenen': 'a/b/c/d/img' -> 'ns/d/img'",
"NOTE": "Hinweis: Charts im Chartmuseum unterstützen lediglich eine Struktur mit zwei Komponenten im Pfad: 'a/chart'",
"BANDWIDTH": "Bandbreite",
"BANDWIDTH_ERROR_TIP": "Bitte -1 oder einen Integer-Wert größer als 0 eingeben",
"BANDWIDTH_TOOLTIP": "Legt die maximale Netzwerkbandbreite für jede Ausführung fest. Bitte auf die Anzahl der parallelen Ausführungen achten. Für umbegrenzte Bandbreite, bitte -1 eingeben",
@ -763,68 +762,9 @@
"MARKDOWN": "Markdown wird unterstützt",
"LAST_MODIFIED": "Geändert am"
},
"HELM_CHART": {
"HELMCHARTS": "Charts",
"CHARTVERSIONS": "Versionen",
"UPLOAD_TITLE": "Chart Dateien hochladen",
"CHART_FILE": "Chart Datei",
"CHART_PROV": "Prov Datei",
"DOWNLOAD": "Download",
"SUMMARY": "Zusammenfassung",
"DEPENDENCIES": "Dependencies",
"VALUES": "Values",
"OVERVIEW": "Übersicht",
"HOME": "Home",
"SRC_REPO": "Quell Repository",
"CREATED": "Erstellt am",
"MAINTAINERS": "Maintainers",
"OTHER_MAINTAINERS": "{{ name }} und {{ number }} andere",
"PULLS": "Pull Anzahl",
"VERSION": "Version",
"APP_VERSION": "Application Version",
"INSTALL": "Installieren",
"INSTALL_CHART": "Chart installieren",
"NAME": "Name",
"REPO": "Repository",
"FILTER_FOR_CHARTS": "Filter Charts",
"DELETE": "Löschen",
"OF": "von",
"VERSIONS": "Versionen",
"IMAGES": "Images",
"ENGINE": "Engine",
"ACTION": "Aktion",
"UPLOAD": "Hochladen",
"DELETE_CHART_VERSION_TITLE": "Lösche Chart Version",
"DELETE_CHART_VERSION": "Soll die Version {{param}} gelöscht werden?",
"IMPORT": "Importieren",
"EXPORT": "Exportieren",
"ADD_REPO": "Repo hinzufügen",
"SHOW_KV": "Key/Value-Paare",
"SHOW_YAML": "YAML Datei",
"PLACEHOLDER": "Es konnte kein Chart gefunden werden!",
"NO_VERSION_PLACEHOLDER": "Es konnten keine Versionen gefunden werden!",
"FILE_UPLOADED": "Datei erfolgreich hochgeladen",
"SIGN": "Signieren",
"SIGNED": "signiert",
"UNSIGNED": "nicht signiert",
"ITEMS": "Einträge",
"NO_README": "Kein Readme für dieses Chart verfügbar.",
"SECURITY": "Sicherheit",
"ACTIVE": "Aktiv",
"DEPRECATED": "Veraltet",
"VERIFY_CHART": "Verifiziere Chart",
"COMMAND": "Befehle",
"PROV_FILE": "Prov Datei",
"READY": "Bereit",
"NOT_READY": "Nicht bereit",
"LABELS": "Label",
"ADD_LABEL_TO_CHART_VERSION": "Label zu dieser Chart-Version hinzufügen",
"STATUS": "Status"
},
"SUMMARY": {
"QUOTAS": "Beschränkungen",
"PROJECT_REPOSITORY": "Repositories",
"PROJECT_HELM_CHART": "Helm Chart",
"PROJECT_MEMBER": "Mitglieder",
"PROJECT_QUOTAS": "Beschränkungen",
"ARTIFACT_COUNT": "Anzahl an Artefakten",
@ -852,6 +792,7 @@
"SUB_TITLE_SUFIX": "logs"
},
"CONFIG": {
"SECURITY": "Security",
"HISTORY": "Archiv",
"TITLE": "Konfiguration",
"AUTH": "Authentifizierung",
@ -1636,8 +1577,6 @@
"READ": "Lesen",
"CREATE": "Erzeugen",
"ARTIFACT": "Artefakt",
"HELM": "Helm Chart",
"HELM_VERSION": "Helm Chart Version",
"ADD_ROBOT": "Robot-Zugang hinzufügen",
"UPDATE_ROBOT": "Robot-Zugang aktualisieren",
"UPDATE_ROBOT_SUCCESSFULLY": "Robot-Zugang aktualisieren erfolgreich",
@ -1707,7 +1646,6 @@
"STOP": "Stoppen",
"LIST": "Auflisten",
"REPOSITORY": "Repository",
"HELM_LABEL": "Helm Chart Label",
"EXPIRES_IN": "Läuft ab in",
"EXPIRED": "Abgelaufen"
},

View File

@ -651,7 +651,6 @@
"FLATTEN_LEVEL_TIP_1": "'Flatten 1 Level'(Default): 'a/b/c/d/img' -> 'ns/b/c/d/img'",
"FLATTEN_LEVEL_TIP_2": "'Flatten 2 Levels': 'a/b/c/d/img' -> 'ns/c/d/img'",
"FLATTEN_LEVEL_TIP_3": "'Flatten 3 Levels': 'a/b/c/d/img' -> 'ns/d/img'",
"NOTE": "Notes: The Chartmuseum Charts only support the repository structure with 2 path components: 'a/chart'",
"BANDWIDTH": "Bandwidth",
"BANDWIDTH_ERROR_TIP": "Please enter -1 or an integer greater than 0",
"BANDWIDTH_TOOLTIP": "Set the maximum network bandwidth for each execution. Please pay attention to the number of concurrent executions. For unlimited bandwidth, please enter -1",
@ -763,68 +762,9 @@
"MARKDOWN": "Styling with Markdown is supported",
"LAST_MODIFIED": "Last Modified Time"
},
"HELM_CHART": {
"HELMCHARTS": "Charts",
"CHARTVERSIONS": "Versions",
"UPLOAD_TITLE": "Upload chart files",
"CHART_FILE": "Chart File",
"CHART_PROV": "Prov File",
"DOWNLOAD": "Download",
"SUMMARY": "Summary",
"DEPENDENCIES": "Dependencies",
"VALUES": "Values",
"OVERVIEW": "Overview",
"HOME": "Home",
"SRC_REPO": "Source Repository",
"CREATED": "Created Time",
"MAINTAINERS": "Maintainers",
"OTHER_MAINTAINERS": "{{ name }} and {{ number }} others",
"PULLS": "Pull Count",
"VERSION": "Version",
"APP_VERSION": "Application Version",
"INSTALL": "Install",
"INSTALL_CHART": "Install Chart",
"NAME": "Name",
"REPO": "Repository",
"FILTER_FOR_CHARTS": "Filter for charts",
"DELETE": "Delete",
"OF": "of",
"VERSIONS": "versions",
"IMAGES": "Images",
"ENGINE": "Engine",
"ACTION": "Action",
"UPLOAD": "Upload",
"DELETE_CHART_VERSION_TITLE": "Delete Chart Versions",
"DELETE_CHART_VERSION": "Do you want to delete version {{param}}?",
"IMPORT": "Import",
"EXPORT": "Export",
"ADD_REPO": "Add Repo",
"SHOW_KV": "Key Value Pairs",
"SHOW_YAML": "YAML File",
"PLACEHOLDER": "We couldn't find any charts!",
"NO_VERSION_PLACEHOLDER": "We couldn't find any versions!",
"FILE_UPLOADED": "File upload successfully",
"SIGN": "Sign",
"SIGNED": "Signed",
"UNSIGNED": "Unsigned",
"ITEMS": "items",
"NO_README": "No readme file provided by this chart.",
"SECURITY": "Security",
"ACTIVE": "Active",
"DEPRECATED": "Deprecated",
"VERIFY_CHART": "Verify Chart",
"COMMAND": "Commands",
"PROV_FILE": "Prov File",
"READY": "Ready",
"NOT_READY": "Not Ready",
"LABELS": "Labels",
"ADD_LABEL_TO_CHART_VERSION": "Add labels to this chart version",
"STATUS": "Status"
},
"SUMMARY": {
"QUOTAS": "quotas",
"PROJECT_REPOSITORY": "Repositories",
"PROJECT_HELM_CHART": "Helm Chart",
"PROJECT_MEMBER": "Members",
"PROJECT_QUOTAS": "Quotas",
"ARTIFACT_COUNT": "Artifact count",
@ -852,6 +792,7 @@
"SUB_TITLE_SUFIX": "logs"
},
"CONFIG": {
"SECURITY": "Security",
"HISTORY": "History",
"TITLE": "Configuration",
"AUTH": "Authentication",
@ -1636,8 +1577,6 @@
"READ": "Read",
"CREATE": "Create",
"ARTIFACT": "Artifact",
"HELM": "Helm Chart",
"HELM_VERSION": "Helm Chart Version",
"ADD_ROBOT": "Add Robot",
"UPDATE_ROBOT": "Update Robot",
"UPDATE_ROBOT_SUCCESSFULLY": "Updated robot successfully",
@ -1707,7 +1646,6 @@
"STOP": "Stop",
"LIST": "List",
"REPOSITORY": "Repository",
"HELM_LABEL": "Helm Chart label",
"EXPIRES_IN": "Expires in",
"EXPIRED": "Expired"
},

View File

@ -653,7 +653,6 @@
"FLATTEN_LEVEL_TIP_1": "'Flatten 1 Level'(Default): 'a/b/c/d/img' -> 'ns/b/c/d/img'",
"FLATTEN_LEVEL_TIP_2": "'Flatten 2 Levels': 'a/b/c/d/img' -> 'ns/c/d/img'",
"FLATTEN_LEVEL_TIP_3": "'Flatten 3 Levels': 'a/b/c/d/img' -> 'ns/d/img'",
"NOTE": "Notes: The Chartmuseum Charts only support the repository structure with 2 path components: 'a/chart'",
"BANDWIDTH": "Bandwidth",
"BANDWIDTH_ERROR_TIP": "Please enter -1 or an integer greater than 0",
"BANDWIDTH_TOOLTIP": "Set the maximum network bandwidth for each execution. Please pay attention to the number of concurrent executions. For unlimited bandwidth, please enter -1",
@ -765,68 +764,9 @@
"MARKDOWN": "Styling with Markdown is supported",
"LAST_MODIFIED": "Last Modified Time"
},
"HELM_CHART": {
"HELMCHARTS": "Charts",
"CHARTVERSIONS": "Versions",
"UPLOAD_TITLE": "Upload chart files",
"CHART_FILE": "Chart File",
"CHART_PROV": "Prov File",
"DOWNLOAD": "Download",
"SUMMARY": "Summary",
"DEPENDENCIES": "Dependencies",
"VALUES": "Values",
"OVERVIEW": "Overview",
"HOME": "Home",
"SRC_REPO": "Source Repository",
"CREATED": "Created Time",
"MAINTAINERS": "Maintainers",
"OTHER_MAINTAINERS": "{{ name }} and {{ number }} others",
"PULLS": "Pull Count",
"VERSION": "Version",
"APP_VERSION": "Application Version",
"INSTALL": "Install",
"INSTALL_CHART": "Install Chart",
"NAME": "Name",
"REPO": "Repository",
"FILTER_FOR_CHARTS": "Filter for charts",
"DELETE": "Delete",
"OF": "of",
"VERSIONS": "versions",
"IMAGES": "Images",
"ENGINE": "Engine",
"ACTION": "Action",
"UPLOAD": "Upload",
"DELETE_CHART_VERSION_TITLE": "Delete Chart Versions",
"DELETE_CHART_VERSION": "Do you want to delete version {{param}}?",
"IMPORT": "Import",
"EXPORT": "Export",
"ADD_REPO": "Add Repo",
"SHOW_KV": "Key Value Pairs",
"SHOW_YAML": "YAML File",
"PLACEHOLDER": "We couldn't find any charts!",
"NO_VERSION_PLACEHOLDER": "We couldn't find any versions!",
"FILE_UPLOADED": "File upload successfully",
"SIGN": "Sign",
"SIGNED": "Signed",
"UNSIGNED": "Unsigned",
"ITEMS": "items",
"NO_README": "No readme file provided by this chart.",
"SECURITY": "Security",
"ACTIVE": "Active",
"DEPRECATED": "Deprecated",
"VERIFY_CHART": "Verify Chart",
"COMMAND": "Commands",
"PROV_FILE": "Prov File",
"READY": "Ready",
"NOT_READY": "Not Ready",
"LABELS": "Labels",
"ADD_LABEL_TO_CHART_VERSION": "Add labels to this chart version",
"STATUS": "Status"
},
"SUMMARY": {
"QUOTAS": "quotas",
"PROJECT_REPOSITORY": "Repositories",
"PROJECT_HELM_CHART": "Helm Chart",
"PROJECT_MEMBER": "Members",
"PROJECT_QUOTAS": "Quotas",
"ARTIFACT_COUNT": "Artifact count",
@ -854,6 +794,7 @@
"SUB_TITLE_SUFIX": "logs"
},
"CONFIG": {
"SECURITY": "Security",
"HISTORY": "History",
"TITLE": "Configuración",
"AUTH": "Autentificación",
@ -1635,8 +1576,6 @@
"READ": "Read",
"CREATE": "Create",
"ARTIFACT": "Artifact",
"HELM": "Helm Chart",
"HELM_VERSION": "Helm Chart Version",
"ADD_ROBOT": "Add Robot",
"UPDATE_ROBOT": "Update Robot",
"UPDATE_ROBOT_SUCCESSFULLY": "Updated robot successfully",
@ -1706,7 +1645,6 @@
"STOP": "Stop",
"LIST": "List",
"REPOSITORY": "Repository",
"HELM_LABEL": "Helm Chart label",
"EXPIRES_IN": "Expires in",
"EXPIRED": "Expired"
},

View File

@ -641,7 +641,6 @@
"FLATTEN_LEVEL_TIP_1": "'Aplanissement sur 1 niveau'(Default): 'a/b/c/d/img' -> 'ns/b/c/d/img'",
"FLATTEN_LEVEL_TIP_2": "'Aplanissement sur 2 niveaux': 'a/b/c/d/img' -> 'ns/c/d/img'",
"FLATTEN_LEVEL_TIP_3": "'Aplanissement sur 3 niveaux': 'a/b/c/d/img' -> 'ns/d/img'",
"NOTE": "Notes: Les graphiques Chartmuseum ne prennent en charge que la structure du référentiel avec 2 composants de chemin : `a/chart` ",
"BANDWIDTH": "Bande passante",
"BANDWIDTH_ERROR_TIP": "Veuillez saisir -1 ou un nombre entier supérieur à 0",
"BANDWIDTH_TOOLTIP": "Définissez la bande passante réseau maximale pour chaque exécution. Veuillez faire attention au nombre d'exécutions simultanées. Pour une bande passante illimitée, veuillez entrer -1",
@ -750,67 +749,9 @@
"MARKDOWN": "La syntaxe Markdown est supportée",
"LAST_MODIFIED": "Dernière modification"
},
"HELM_CHART": {
"HELMCHARTS": "Charts",
"CHARTVERSIONS": "Versions",
"UPLOAD_TITLE": "Téléverser des charts",
"CHART_FILE": "Fichier de Chart",
"CHART_PROV": "Prov File",
"DOWNLOAD": "Télécharger",
"SUMMARY": "Résumé",
"DEPENDENCIES": "Dépendances",
"VALUES": "Valeurs",
"OVERVIEW": "Aperçu",
"HOME": "Accueil",
"SRC_REPO": "Dépôt source",
"CREATED": "Date/Heure de création",
"MAINTAINERS": "Mainteneurs",
"OTHER_MAINTAINERS": "{{ name }} et {{ number }} autres",
"PULLS": "Nombre de Pull",
"VERSION": "Version",
"APP_VERSION": "Version de l'Application",
"INSTALL": "Installer",
"INSTALL_CHART": "Installer le Chart",
"NAME": "Nom",
"REPO": "Dépôt",
"FILTER_FOR_CHARTS": "Filtrer les charts",
"DELETE": "Supprimer",
"OF": "sur",
"VERSIONS": "versions",
"IMAGES": "Images",
"ENGINE": "Moteur",
"ACTION": "Action",
"UPLOAD": "Téléverser",
"DELETE_CHART_VERSION_TITLE": "Supprimer des versions de Chart",
"DELETE_CHART_VERSION": "Voulez-vous supprimer la version {{param}}?",
"IMPORT": "Importer",
"EXPORT": "Exporter",
"ADD_REPO": "Ajouter Repo",
"SHOW_KV": "Paires Clé/Valeur",
"SHOW_YAML": "Fichier YAML",
"PLACEHOLDER": "Nous n'avons trouvé aucun chart !",
"NO_VERSION_PLACEHOLDER": "Nous n'avons trouvé aucune version !",
"FILE_UPLOADED": "Fichier envoyé avec succès",
"SIGN": "Signer",
"SIGNED": "Signé",
"UNSIGNED": "Non signé",
"ITEMS": "items",
"SECURITY": "Sécurité",
"ACTIVE": "Actif",
"DEPRECATED": "Obsolète",
"VERIFY_CHART": "Verifier le Chart",
"COMMAND": "Commandes",
"PROV_FILE": "Prov File",
"READY": "Prêt",
"NOT_READY": "Non prêt",
"LABELS": "Labels",
"ADD_LABEL_TO_CHART_VERSION": "Ajouter des labels à cette version de chart",
"STATUS": "Statut"
},
"SUMMARY": {
"QUOTAS": "Quotas",
"PROJECT_REPOSITORY": "Dépôts",
"PROJECT_HELM_CHART": "Chart Helm",
"PROJECT_MEMBER": "Membres",
"PROJECT_QUOTAS": "Quotas",
"ARTIFACT_COUNT": "Nombre d'Artefact",
@ -838,6 +779,7 @@
"SUB_TITLE_SUFIX": "logs"
},
"CONFIG": {
"SECURITY": "Security",
"HISTORY": "Historique",
"TITLE": "Configuration",
"AUTH": "Identification",
@ -1606,8 +1548,6 @@
"READ": "Lecture",
"CREATE": "Création",
"ARTIFACT": "Artefact",
"HELM": "Chart Helm",
"HELM_VERSION": "Version du Chart Helm",
"ADD_ROBOT": "Ajouter un Robot",
"UPDATE_ROBOT": "Mettre à jour le Robot",
"UPDATE_ROBOT_SUCCESSFULLY": "Robot mis à jour avec succès",
@ -1676,7 +1616,6 @@
"PUSH_PERMISSION_TOOLTIP": "L'autorisation 'push' ne peut pas fonctionner seule, elle doit fonctionner avec l'autorisation 'pull'", "STOP": "Stop",
"LIST": "Liste",
"REPOSITORY": "Dépôt",
"HELM_LABEL": "Label du Chart Helm",
"EXPIRES_IN": "Expire dans",
"EXPIRED": "Expiré"
},

View File

@ -651,7 +651,6 @@
"FLATTEN_LEVEL_TIP_1": "'Nivelar 1 posição' (padrão): 'a/b/c/d/img' -> 'ns/b/c/d/img'",
"FLATTEN_LEVEL_TIP_2": "'Nivelar 2 posições': 'a/b/c/d/img' -> 'ns/c/d/img'",
"FLATTEN_LEVEL_TIP_3": "'Nivelar 3 posições': 'a/b/c/d/img' -> 'ns/d/img'",
"NOTE": "Nota: Chartmuseum suporta helm charts apenas com 2 níveis: 'a/chart'",
"BANDWIDTH": "Banda",
"BANDWIDTH_ERROR_TIP": "Informe um número inteiro maior que 0 (zero) ou -1",
"BANDWIDTH_TOOLTIP": "Informe o limite de banda para cada execução. Tome cuidado e observe a relação com o número de execuções simultâneas. Para remover qualquer limite, informe -1",
@ -762,66 +761,9 @@
"MARKDOWN": "Estilização com Markdown suportada",
"LAST_MODIFIED": "Data da última alteração"
},
"HELM_CHART": {
"HELMCHARTS": "Charts",
"CHARTVERSIONS": "Versões",
"UPLOAD_TITLE": "Enviar arquivos de chart",
"CHART_FILE": "Arquivo Chart",
"CHART_PROV": "Arquivo Prov",
"DOWNLOAD": "Baixar",
"SUMMARY": "Resumo",
"DEPENDENCIES": "Dependências",
"VALUES": "Valores",
"OVERVIEW": "Visão Geral",
"HOME": "Painel Principal",
"SRC_REPO": "Repositório de origem",
"CREATED": "Data de criação",
"MAINTAINERS": "Mantenedor",
"PULLS": "Contagem de Pulls",
"VERSION": "Versão",
"APP_VERSION": "Versão da Aplicação",
"INSTALL": "Instalar",
"INSTALL_CHART": "Instalar Chart",
"NAME": "Nome",
"REPO": "Repositório",
"FILTER_FOR_CHARTS": "Filtrar por charts",
"DELETE": "Remover",
"OF": "de",
"VERSIONS": "versões",
"IMAGES": "Imagens",
"ENGINE": "Engine",
"ACTION": "Ação",
"UPLOAD": "Enviar",
"DELETE_CHART_VERSION_TITLE": "Remover versões de Chart",
"DELETE_CHART_VERSION": "Você quer remover a versão {{param}}?",
"IMPORT": "Importar",
"EXPORT": "Exportar",
"ADD_REPO": "Adicionar Repositório",
"SHOW_KV": "Pares chave-valor",
"SHOW_YAML": "Arquivo YAML",
"PLACEHOLDER": "Não foi possível encontrar nenhum charts!",
"NO_VERSION_PLACEHOLDER": "Não foi possível encontrar nenhuma versão!",
"FILE_UPLOADED": "Envio de arquivo realizado com sucesso",
"SIGN": "Assinar",
"SIGNED": "Assinado",
"UNSIGNED": "Não assinado",
"ITEMS": "itens",
"NO_README": "Nenhum arquivo de README fornecido por esse chart.",
"SECURITY": "Segurança",
"ACTIVE": "Ativo",
"DEPRECATED": "Obsoleto",
"VERIFY_CHART": "Verificar Chart",
"COMMAND": "Comandos",
"PROV_FILE": "Arquivo Prov",
"READY": "Pronto",
"NOT_READY": "Não Pronto",
"ADD_LABEL_TO_CHART_VERSION": "Adicionar marcadores",
"STATUS": "Situação"
},
"SUMMARY": {
"QUOTAS": "cotas",
"PROJECT_REPOSITORY": "Repositórios",
"PROJECT_HELM_CHART": "Helm Chart",
"PROJECT_MEMBER": "Membros",
"PROJECT_QUOTAS": "Cotas",
"ARTIFACT_COUNT": "Contagem de artefatos",
@ -849,6 +791,7 @@
"SUB_TITLE_SUFIX": "logs"
},
"CONFIG": {
"SECURITY": "Security",
"HISTORY": "Histórico",
"TITLE": "Configuração",
"AUTH": "Autenticação",
@ -1632,8 +1575,6 @@
"READ": "Ler",
"CREATE": "Criar",
"ARTIFACT": "Artefato",
"HELM": "Helm Chart",
"HELM_VERSION": "Versão do Helm Chart",
"ADD_ROBOT": "Adicionar conta",
"UPDATE_ROBOT": "Atualizar conta",
"UPDATE_ROBOT_SUCCESSFULLY": "Conta atualizada com sucesso",
@ -1703,7 +1644,6 @@
"STOP": "Parar",
"LIST": "Listar",
"REPOSITORY": "Repositório",
"HELM_LABEL": "Marcador do Helm Chart",
"EXPIRES_IN": "Expires in",
"EXPIRED": "Expired"
},

View File

@ -651,7 +651,6 @@
"FLATTEN_LEVEL_TIP_1": "'Flatten 1 Level'(Default): 'a/b/c/d/img' -> 'ns/b/c/d/img'",
"FLATTEN_LEVEL_TIP_2": "'Flatten 2 Levels': 'a/b/c/d/img' -> 'ns/c/d/img'",
"FLATTEN_LEVEL_TIP_3": "'Flatten 3 Levels': 'a/b/c/d/img' -> 'ns/d/img'",
"NOTE": "Notes: The Chartmuseum Charts only support the repository structure with 2 path components: 'a/chart'",
"BANDWIDTH": "Bandwidth",
"BANDWIDTH_ERROR_TIP": "Please enter -1 or an integer greater than 0",
"BANDWIDTH_TOOLTIP": "Set the maximum network bandwidth for each execution. Please pay attention to the number of concurrent executions. For unlimited bandwidth, please enter -1",
@ -763,68 +762,9 @@
"MARKDOWN": "Markdown ile stil desteklenir",
"LAST_MODIFIED": "Last Modified Time"
},
"HELM_CHART": {
"HELMCHARTS": "Tablolar",
"CHARTVERSIONS": "Versiyonlar",
"UPLOAD_TITLE": "Tabloları dosyalarını yükle",
"CHART_FILE": "Tablo Dosyası",
"CHART_PROV": "Prov Dosyası",
"DOWNLOAD": "Yükle",
"SUMMARY": "Özet",
"DEPENDENCIES": "Bağımlılıklar",
"VALUES": "Değerler",
"OVERVIEW": "Genel bakış",
"HOME": "Ev",
"SRC_REPO": "Kaynak Deposu",
"CREATED": "Yaratılış Zamanı",
"MAINTAINERS": "Sürdürenler",
"OTHER_MAINTAINERS": "{{ name }} ve {{ number }} diğerleri",
"PULLS": "İndirme Sayısı",
"VERSION": "Versiyon",
"APP_VERSION": "Uygulama Versiyonu",
"INSTALL": "Yükle",
"INSTALL_CHART": "Tablo Yükle",
"NAME": "İsim",
"REPO": "Depo",
"FILTER_FOR_CHARTS": "Tabloları filtrele",
"DELETE": "Sil",
"OF": "of",
"VERSIONS": "versiyonlar",
"IMAGES": "İmajlar",
"ENGINE": "Motor",
"ACTION": "Aksiyon",
"UPLOAD": "Yükle",
"DELETE_CHART_VERSION_TITLE": "Tablo versiyonlarını sil",
"DELETE_CHART_VERSION": "Sürümü silmek istiyor musunuz {{param}}?",
"IMPORT": "İçe Aktar",
"EXPORT": "Dışa Aktar",
"ADD_REPO": "Depo Ekle",
"SHOW_KV": "Anahtar Değer Çiftleri",
"SHOW_YAML": "YAML Dosyası",
"PLACEHOLDER": "Hiçbir tablo bulamadık!",
"NO_VERSION_PLACEHOLDER": "Herhangi bir sürüm bulamadık!",
"FILE_UPLOADED": "Dosya başarıyla yüklendi",
"SIGN": "İmza",
"SIGNED": "İmzalandı",
"UNSIGNED": "İmzalanmadı",
"ITEMS": "öğeler",
"NO_README": "Bu tablo tarafından sağlanan beni oku dosyası yok.",
"SECURITY": "Güvenlik",
"ACTIVE": "Aktif",
"DEPRECATED": "Kullanımdan kaldırılan",
"VERIFY_CHART": "Tabloyu doğrula",
"COMMAND": "Komutlar",
"PROV_FILE": "Prov Dosyası",
"READY": "Hazır",
"NOT_READY": "Hazır Değil",
"LABELS": "Etiketler",
"ADD_LABEL_TO_CHART_VERSION": "Bu tablo sürümüne etiketler ekle",
"STATUS": "Durum"
},
"SUMMARY": {
"QUOTAS": "kotalar",
"PROJECT_REPOSITORY": "Proje depoları",
"PROJECT_HELM_CHART": "Proje Helm Tablosu",
"PROJECT_MEMBER": "Proje Üyeleri",
"PROJECT_QUOTAS": "Proje kotaları",
"ARTIFACT_COUNT": "Buluntu adeti",
@ -852,6 +792,7 @@
"SUB_TITLE_SUFIX": "kayıtlar"
},
"CONFIG": {
"SECURITY": "Security",
"HISTORY": "Geçmiş",
"TITLE": "Ayarlar",
"AUTH": "Doğrulama",
@ -1636,8 +1577,6 @@
"READ": "Read",
"CREATE": "Create",
"ARTIFACT": "Artifact",
"HELM": "Helm Chart",
"HELM_VERSION": "Helm Chart Version",
"ADD_ROBOT": "Add Robot",
"UPDATE_ROBOT": "Update Robot",
"UPDATE_ROBOT_SUCCESSFULLY": "Updated robot successfully",
@ -1707,7 +1646,6 @@
"STOP": "Stop",
"LIST": "List",
"REPOSITORY": "Repository",
"HELM_LABEL": "Helm Chart label",
"EXPIRES_IN": "Expires in",
"EXPIRED": "Expired"
},

View File

@ -653,7 +653,6 @@
"FLATTEN_LEVEL_TIP_1": "'替换1级'(默认项): 'a/b/c/d/img' -> 'ns/b/c/d/img'",
"FLATTEN_LEVEL_TIP_2": "'替换2级': 'a/b/c/d/img' -> 'ns/c/d/img'",
"FLATTEN_LEVEL_TIP_3": "'替换3级': 'a/b/c/d/img' -> 'ns/d/img'",
"NOTE": "注意: Chartmuseum Charts 仅支持2层的仓库结构如 'a/chart'",
"BANDWIDTH": "带宽",
"BANDWIDTH_ERROR_TIP": "请输入-1或者大于0的整数",
"BANDWIDTH_TOOLTIP": "设置执行该条同步规则时的最大网络带宽。实际总带宽需要考虑并发执行的情况。如无需限制,请输入-1",
@ -765,68 +764,9 @@
"MARKDOWN": "支持使用Markdown进行样式设置",
"LAST_MODIFIED": "最新变更时间"
},
"HELM_CHART": {
"HELMCHARTS": "Charts",
"CHARTVERSIONS": "版本",
"UPLOAD_TITLE": "上传chart文件",
"CHART_FILE": "Chart 文件",
"CHART_PROV": "Prov 文件",
"DOWNLOAD": "下载",
"SUMMARY": "概要",
"DEPENDENCIES": "依赖",
"VALUES": "取值",
"OVERVIEW": "总览",
"HOME": "首页",
"SRC_REPO": "源仓库",
"CREATED": "创建时间",
"MAINTAINERS": "维护者",
"OTHER_MAINTAINERS": "{{ name }} 与其他 {{ number }} 位",
"PULLS": "拉取数",
"VERSION": "版本",
"INSTALL": "安装",
"INSTALL_CHART": "安装Chart",
"NAME": "名称",
"REPO": "仓库",
"FILTER_FOR_CHARTS": "过滤Chart",
"DELETE": "删除",
"OF": "共计",
"VERSIONS": "版本",
"APP_VERSION": "应用版本",
"IMAGES": "镜像",
"ENGINE": "引擎",
"ACTION": "动作",
"UPLOAD": "上传",
"DELETE_CHART_VERSION_TITLE": "删除Chart版本",
"DELETE_CHART_VERSION": "您要删除chart版本 {{param}} 吗?",
"IMPORT": "导入",
"EXPORT": "导出",
"ADD_REPO": "添加仓库",
"SHOW_KV": "展示健值对",
"SHOW_YAML": "展示YAML文件",
"PLACEHOLDER": "找不到任何的chart!",
"NO_VERSION_PLACEHOLDER": "找不到任何的chart版本!",
"FILE_UPLOADED": "文件上传成功",
"SIGN": "签名",
"SIGNED": "已签名",
"UNSIGNED": "未签名",
"ITEMS": "条记录",
"NO_README": "此Chart未提供README文件",
"SECURITY": "安全",
"ACTIVE": "正常",
"DEPRECATED": "废弃",
"VERIFY_CHART": "验证Chart",
"COMMAND": "命令",
"PROV_FILE": "Prov 文件",
"READY": "就绪",
"NOT_READY": "未就绪",
"LABELS": "标签",
"ADD_LABEL_TO_CHART_VERSION": "添加标签到此 Chart Version",
"STATUS": "状态"
},
"SUMMARY": {
"QUOTAS": "容量",
"PROJECT_REPOSITORY": "镜像仓库",
"PROJECT_HELM_CHART": "Helm Chart",
"PROJECT_MEMBER": "成员",
"PROJECT_QUOTAS": "容量",
"ARTIFACT_COUNT": "Artifact 数量",
@ -854,6 +794,7 @@
"SUB_TITLE_SUFIX": "条日志"
},
"CONFIG": {
"SECURITY": "安全",
"HISTORY": "历史记录",
"TITLE": "配置",
"AUTH": "认证模式",
@ -1635,8 +1576,6 @@
"READ": "读取",
"CREATE": "创建",
"ARTIFACT": "Artifact",
"HELM": "Helm Chart",
"HELM_VERSION": "Helm Chart Version",
"ADD_ROBOT": "添加机器人",
"UPDATE_ROBOT": "更新机器人",
"UPDATE_ROBOT_SUCCESSFULLY": "更新机器人成功",
@ -1706,7 +1645,6 @@
"STOP": "停止",
"LIST": "查询",
"REPOSITORY": "仓库",
"HELM_LABEL": "Helm Chart 标签",
"EXPIRES_IN": "有效期剩余",
"EXPIRED": "已过期"
},

View File

@ -648,7 +648,6 @@
"FLATTEN_LEVEL_TIP_1": "'Flatten 1 Level'(Default): 'a/b/c/d/img' -> 'ns/b/c/d/img'",
"FLATTEN_LEVEL_TIP_2": "'Flatten 2 Levels': 'a/b/c/d/img' -> 'ns/c/d/img'",
"FLATTEN_LEVEL_TIP_3": "'Flatten 3 Levels': 'a/b/c/d/img' -> 'ns/d/img'",
"NOTE": "Notes: The Chartmuseum Charts only support the repository structure with 2 path components: 'a/chart'",
"BANDWIDTH": "Bandwidth",
"BANDWIDTH_ERROR_TIP": "Please enter -1 or an integer greater than 0",
"BANDWIDTH_TOOLTIP": "Set the maximum network bandwidth for each execution. Please pay attention to the number of concurrent executions. For unlimited bandwidth, please enter -1",
@ -761,68 +760,9 @@
"MARKDOWN": "支持使用Markdown進行樣式設置",
"LAST_MODIFIED": "Last Modified Time"
},
"HELM_CHART":{
"HELMCHARTS":"圖表",
"CHARTVERSIONS": "版本",
"UPLOAD_TITLE": "上傳chart文件",
"CHART_FILE": "Chart 文件",
"CHART_PROV": "Prov 文件",
"DOWNLOAD": "下載",
"SUMMARY": "概要",
"DEPENDENCIES": "依賴",
"VALUES": "取值",
"OVERVIEW": "總覽",
"HOME": "首頁",
"SRC_REPO":"源倉庫",
"CREATED": "創建時間",
"MAINTAINERS": "維護者",
"OTHER_MAINTAINERS": "{{ name }} 與其他{{ number }} 位",
"PULLS": "拉取數",
"VERSION": "版本",
"INSTALL": "安裝",
"INSTALL_CHART": "安裝Chart",
"NAME": "名稱",
"REPO": "倉庫",
"FILTER_FOR_CHARTS": "過濾Chart",
"DELETE": "刪除",
"OF": "共計",
"VERSIONS": "版本",
"APP_VERSION": "應用版本",
"IMAGES": "鏡像",
"ENGINE": "引擎",
"ACTION": "動作",
"UPLOAD": "上傳",
"DELETE_CHART_VERSION_TITLE": "刪除Chart版本",
"DELETE_CHART_VERSION": "您要刪除chart版本{{param}} 嗎?",
"IMPORT": "導入",
"EXPORT": "導出",
"ADD_REPO": "添加倉庫",
"SHOW_KV": "展示健值對",
"SHOW_YAML": "展示YAML文件",
"PLACEHOLDER": "找不到任何的chart!",
"NO_VERSION_PLACEHOLDER": "找不到任何的chart版本!",
"FILE_UPLOADED": "文件上傳成功",
"SIGN": "簽名",
"SIGNED": "已簽名",
"UNSIGNED": "未簽名",
"ITEMS": "條記錄",
"NO_README": "此Chart未提供README文件",
"SECURITY": "安全",
"ACTIVE": "正常",
"DEPRECATED": "廢棄",
"VERIFY_CHART": "驗證Chart",
"COMMAND":"命令",
"PROV_FILE": "Prov 文件",
"READY":"就緒",
"NOT_READY": "未就緒",
"LABELS": "標籤",
"ADD_LABEL_TO_CHART_VERSION": "添加標籤到此Chart Version",
"STATUS": "狀態"
},
"SUMMARY":{
"QUOTAS": "容量",
"PROJECT_REPOSITORY": "鏡像倉庫",
"PROJECT_HELM_CHART":"Helm Chart",
"PROJECT_MEMBER": "成員",
"PROJECT_QUOTAS": "容量",
"ARTIFACT_COUNT":"工件數量",
@ -850,6 +790,7 @@
"SUB_TITLE_SUFIX": "條日誌"
},
"CONFIG":{
"SECURITY": "Security",
"TITLE": "配置",
"AUTH": "認證模式",
"REPLICATION": "複製",
@ -1627,8 +1568,6 @@
"READ": "Read",
"CREATE": "Create",
"ARTIFACT": "Artifact",
"HELM": "Helm Chart",
"HELM_VERSION": "Helm Chart Version",
"ADD_ROBOT": "Add Robot",
"UPDATE_ROBOT": "Update Robot",
"UPDATE_ROBOT_SUCCESSFULLY": "Updated robot successfully",
@ -1698,7 +1637,6 @@
"STOP": "Stop",
"LIST": "List",
"REPOSITORY": "Repository",
"HELM_LABEL": "Helm Chart label",
"EXPIRES_IN": "Expires in",
"EXPIRED": "Expired"
},