Fix bugs with label 'target 2.0.1'

Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
AllForNothing 2020-06-01 14:51:10 +08:00
parent f5d482854b
commit 8bf77d01f4
38 changed files with 166 additions and 107 deletions

View File

@ -20,8 +20,7 @@ export class ScannerMetadataComponent implements OnInit {
loading: boolean = false; loading: boolean = false;
scannerMetadata: ScannerMetadata; scannerMetadata: ScannerMetadata;
constructor(private configScannerService: ConfigScannerService, constructor(private configScannerService: ConfigScannerService,
private errorHandler: ErrorHandler, private errorHandler: ErrorHandler) {
private translate: TranslateService) {
} }
ngOnInit(): void { ngOnInit(): void {
this.loading = true; this.loading = true;
@ -35,7 +34,7 @@ export class ScannerMetadataComponent implements OnInit {
} }
parseDate(item: any): string { parseDate(item: any): string {
if (this.hasValue(item) && this.hasDateValue(item)) { if (this.hasValue(item) && this.hasDateValue(item)) {
return new DatePipe(this.translate.currentLang).transform(item.value, 'short'); return new DatePipe('en-us').transform(item.value, 'short');
} }
if (this.hasValue(item)) { if (this.hasValue(item)) {
return item.value; return item.value;

View File

@ -8,4 +8,4 @@
</div> </div>
</clr-alert> </clr-alert>
</div> </div>
<div *ngIf="globalMessageOpened" class="mask-layer"></div> <div *ngIf="globalMessageOpened && needAuth" class="mask-layer"></div>

View File

@ -1,13 +1,12 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { Component, Input, OnInit, OnDestroy, ElementRef } from '@angular/core'; import { ElementRef } from '@angular/core';
import { Router } from '@angular/router';
import { Subscription } from "rxjs";
import { RouterTestingModule } from '@angular/router/testing'; import { RouterTestingModule } from '@angular/router/testing';
import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { ClarityModule } from "@clr/angular"; import { ClarityModule } from '@clr/angular';
import { Message } from './message'; import { Message } from './message';
import { MessageService } from './message.service'; import { MessageService } from './message.service';
import { MessageComponent } from './message.component'; import { MessageComponent } from './message.component';
import { AlertType } from '../shared/shared.const';
describe('MessageComponent', () => { describe('MessageComponent', () => {
let component: MessageComponent; let component: MessageComponent;
@ -39,4 +38,22 @@ describe('MessageComponent', () => {
it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
it('should open mask layer when unauthorized', async () => {
component.globalMessageOpened = true;
component.globalMessage = Message.newMessage(401, "unauthorized", AlertType.DANGER);
fixture.detectChanges();
await fixture.whenStable();
const ele: HTMLDivElement = fixture.nativeElement.querySelector(".mask-layer");
expect(ele).toBeTruthy();
});
it("should not open mask layer when it's not unauthorized", async () => {
component.globalMessageOpened = true;
component.globalMessage = Message.newMessage(403, "forbidden", AlertType.WARNING);
fixture.detectChanges();
await fixture.whenStable();
const ele: HTMLDivElement = fixture.nativeElement.querySelector(".mask-layer");
expect(ele).toBeFalsy();
});
}); });

View File

@ -320,7 +320,12 @@ const harborRoutes: Routes = [
} }
}, },
component: ScannerComponent component: ScannerComponent
} },
{
path: '',
redirectTo: 'repositories',
pathMatch: 'full'
},
] ]
}, },
{ {

View File

@ -2,7 +2,7 @@
<div class="breadcrumb"> <div class="breadcrumb">
<a (click)="gotoProjectList()"> {{ 'SIDE_NAV.PROJECTS'| translate}} </a> <a (click)="gotoProjectList()"> {{ 'SIDE_NAV.PROJECTS'| translate}} </a>
&lt; &lt;
<a (click)="gotoChartList()">{{ 'HELM_CHART.HELMCHARTS'| translate}}</a> <a (click)="gotoChartList()">{{ projectName }}</a>
&lt; &lt;
<a (click)="gotoChartVersion()">{{ 'HELM_CHART.CHARTVERSIONS'| translate}}</a> <a (click)="gotoChartVersion()">{{ 'HELM_CHART.CHARTVERSIONS'| translate}}</a>
</div> </div>

View File

@ -13,6 +13,7 @@ export class HelmChartDetailComponent implements OnInit {
projectId: number | string; projectId: number | string;
project: Project; project: Project;
projectName: string;
chartName: string; chartName: string;
chartVersion: string; chartVersion: string;
currentUser: SessionUser; currentUser: SessionUser;
@ -36,6 +37,7 @@ export class HelmChartDetailComponent implements OnInit {
if (resolverData) { if (resolverData) {
this.project = <Project>(resolverData["projectResolver"]); this.project = <Project>(resolverData["projectResolver"]);
this.roleName = this.project.role_name; this.roleName = this.project.role_name;
this.projectName = this.project.name;
this.hasProjectAdminRole = this.project.has_project_admin_role; this.hasProjectAdminRole = this.project.has_project_admin_role;
} }
} }

View File

@ -8,7 +8,7 @@
<div *ngIf="readme" class="md-div" [innerHTML]="readme | markdown"></div> <div *ngIf="readme" class="md-div" [innerHTML]="readme | markdown"></div>
<div *ngIf="!readme">{{'HELM_CHART.NO_README' | translate}}</div> <div *ngIf="!readme">{{'HELM_CHART.NO_README' | translate}}</div>
</div> </div>
<div class="col-md-4 summary-container"> <div class="col-md-4 summary-container mt-1">
<div class="col-md-12 content-group"> <div class="col-md-12 content-group">
<div> <div>
<label>{{'HELM_CHART.OVERVIEW' | translate }}</label> <label>{{'HELM_CHART.OVERVIEW' | translate }}</label>
@ -55,7 +55,7 @@
<tr> <tr>
<td class="left cmd-title">{{'HELM_CHART.ADD_REPO' | translate }}</td> <td class="left cmd-title">{{'HELM_CHART.ADD_REPO' | translate }}</td>
<td class="left cmd-content"> <td class="left cmd-content">
<input class="cmd-content" type="text" [(ngModel)]="addCMD" #addCMDInput readonly /> <input class="cmd-content clr-input" type="text" [(ngModel)]="addCMD" #addCMDInput readonly />
</td> </td>
<td class="left"> <td class="left">
<span> <span>
@ -67,7 +67,7 @@
<tr> <tr>
<td class="left cmd-title">{{'HELM_CHART.INSTALL_CHART' | translate }}</td> <td class="left cmd-title">{{'HELM_CHART.INSTALL_CHART' | translate }}</td>
<td class="left"> <td class="left">
<input class="cmd-content" type="text" [(ngModel)]="installCMD" #installCMDInput readonly /> <input class="cmd-content clr-input" type="text" [(ngModel)]="installCMD" #installCMDInput readonly />
</td> </td>
<td class="left"> <td class="left">
<span> <span>
@ -79,7 +79,7 @@
<tr *ngIf="prov_ready"> <tr *ngIf="prov_ready">
<td class="left cmd-title">{{'HELM_CHART.VERIFY_CHART' | translate }}</td> <td class="left cmd-title">{{'HELM_CHART.VERIFY_CHART' | translate }}</td>
<td class="left"> <td class="left">
<input class="cmd-content" type="text" [(ngModel)]="verifyCMD" #verifyCMDInput readonly /> <input class="cmd-content clr-input" type="text" [(ngModel)]="verifyCMD" #verifyCMDInput readonly />
</td> </td>
<td class="left"> <td class="left">
<span> <span>

View File

@ -2,12 +2,11 @@
margin-top: 20px; margin-top: 20px;
padding: 0 0 0 15px; padding: 0 0 0 15px;
.md-container { .md-container {
border: solid 1px #ddd; border: solid 1px;
} }
.summary-container { .summary-container {
padding: 0; padding: 0;
table { table {
background-color: #f2f2f2;
margin-top: 0.5rem; margin-top: 0.5rem;
} }
.content-group { .content-group {
@ -36,45 +35,3 @@
} }
} }
} }
%code-block {
background: #ddd;
border-radius: 2px;
padding: 2px 4px;
}
.md-div {
::ng-deep {
code:not([class*="language-"]) {
@extend %code-block;
color: #657b83;
}
pre:not([class*="language-"]) {
background: #fdf6e3;
code:not([class*="language-"]) {
@extend %code-block;
background: transparent;
}
}
table {
display: block;
width: 100%;
overflow: auto;
padding: 0;
border-spacing: 0;
border-collapse: collapse;
margin-bottom: 16px;
td,
th {
padding: 6px 13px;
border: 1px solid #ddd;
@include align-text-mixin(left, right, center);
}
tr {
&:nth-child(2n) {
background-color: #f2f2f2;
}
}
}
}
}

View File

@ -2,7 +2,7 @@
<div class="breadcrumb"> <div class="breadcrumb">
<a href="javascript:void(0)" (click)="gotoProjectList()"> {{ 'SIDE_NAV.PROJECTS'| translate}} </a> <a href="javascript:void(0)" (click)="gotoProjectList()"> {{ 'SIDE_NAV.PROJECTS'| translate}} </a>
&lt; &lt;
<a href="javascript:void(0)" (click)="gotoChartList()">{{ 'HELM_CHART.HELMCHARTS'| translate}}</a> <a href="javascript:void(0)" (click)="gotoChartList()">{{ projectName }}</a>
</div> </div>
<hbr-helm-chart-version <hbr-helm-chart-version
[projectId]='projectId' [projectId]='projectId'

View File

@ -96,11 +96,7 @@ export class ListProjectComponent implements OnDestroy {
} }
get withChartMuseum(): boolean { get withChartMuseum(): boolean {
if (this.appConfigService.getConfig().with_chartmuseum) { return this.appConfigService.getConfig().with_chartmuseum;
return true;
} else {
return false;
}
} }
public get isSystemAdmin(): boolean { public get isSystemAdmin(): boolean {
@ -129,7 +125,7 @@ export class ListProjectComponent implements OnDestroy {
goToLink(proId: number): void { goToLink(proId: number): void {
this.searchTrigger.closeSearch(true); this.searchTrigger.closeSearch(true);
let linkUrl = ["harbor", "projects", proId, "summary"]; let linkUrl = ["harbor", "projects", proId];
this.router.navigate(linkUrl); this.router.navigate(linkUrl);
} }

View File

@ -2,7 +2,7 @@
<div class="breadcrumb" *ngIf="!withAdmiral"> <div class="breadcrumb" *ngIf="!withAdmiral">
<a (click)="goProBack()">{{'SIDE_NAV.PROJECTS'| translate}}</a> <a (click)="goProBack()">{{'SIDE_NAV.PROJECTS'| translate}}</a>
<span class="back-icon"><</span> <span class="back-icon"><</span>
<a (click)="watchGoBackEvt(projectId)">{{'REPOSITORY.REPOSITORIES'| translate}}</a> <a (click)="watchGoBackEvt(projectId)">{{projectName}}</a>
<span *ngIf="depth">&lt;<a (click)="backInitRepo()">{{repoName}}</a></span> <span *ngIf="depth">&lt;<a (click)="backInitRepo()">{{repoName}}</a></span>
<span *ngIf="referArtifactNameArray?.length>=1" > <span *ngIf="referArtifactNameArray?.length>=1" >
<span *ngFor="let digest of referArtifactNameArray;let i = index"> <span *ngFor="let digest of referArtifactNameArray;let i = index">

View File

@ -28,6 +28,7 @@ import { Project } from "../../project";
export class ArtifactListPageComponent implements OnInit { export class ArtifactListPageComponent implements OnInit {
projectId: number; projectId: number;
projectName: string;
projectMemberRoleId: number; projectMemberRoleId: number;
repoName: string; repoName: string;
referArtifactNameArray: string[] = []; referArtifactNameArray: string[] = [];
@ -60,6 +61,7 @@ export class ArtifactListPageComponent implements OnInit {
} }
let resolverData = this.route.snapshot.data; let resolverData = this.route.snapshot.data;
if (resolverData) { if (resolverData) {
this.projectName = (<Project>resolverData['projectResolver']).name;
this.hasProjectAdminRole = (<Project>resolverData['projectResolver']).has_project_admin_role; this.hasProjectAdminRole = (<Project>resolverData['projectResolver']).has_project_admin_role;
this.isGuest = (<Project>resolverData['projectResolver']).current_user_role_id === 3; this.isGuest = (<Project>resolverData['projectResolver']).current_user_role_id === 3;
this.projectMemberRoleId = (<Project>resolverData['projectResolver']).current_user_role_id; this.projectMemberRoleId = (<Project>resolverData['projectResolver']).current_user_role_id;

View File

@ -264,7 +264,7 @@
<span *ngIf="!hasVul(artifact)"> <span *ngIf="!hasVul(artifact)">
{{'ARTIFACT.SCAN_UNSUPPORTED' | translate}} {{'ARTIFACT.SCAN_UNSUPPORTED' | translate}}
</span> </span>
<hbr-vulnerability-bar *ngIf="hasVul(artifact)" [scanner]="handleScanOverview(artifact.scan_overview)?.scanner" <hbr-vulnerability-bar (scanFinished)="scanFinished($event)" *ngIf="hasVul(artifact)" [scanner]="handleScanOverview(artifact.scan_overview)?.scanner"
(submitFinish)="submitFinish($event)" [projectName]="projectName" [repoName]="repoName" (submitFinish)="submitFinish($event)" [projectName]="projectName" [repoName]="repoName"
[artifactDigest]="artifact.digest" [summary]="handleScanOverview(artifact.scan_overview)"> [artifactDigest]="artifact.digest" [summary]="handleScanOverview(artifact.scan_overview)">
</hbr-vulnerability-bar> </hbr-vulnerability-bar>

View File

@ -981,4 +981,15 @@ export class ArtifactListTabComponent implements OnInit, OnDestroy {
get isFilterReadonly() { get isFilterReadonly() {
return this.filterByType === 'labels' ? 'readonly' : null; return this.filterByType === 'labels' ? 'readonly' : null;
} }
// when finished, remove it from selectedRow
scanFinished(artifact: Artifact) {
if (this.selectedRow && this.selectedRow.length) {
for ( let i = 0; i < this.selectedRow.length; i++) {
if (artifact.digest === this.selectedRow[i].digest) {
this.selectedRow.splice(i, 1);
break;
}
}
}
}
} }

View File

@ -10,10 +10,10 @@
<clr-datagrid [clrDgLoading]="loading"> <clr-datagrid [clrDgLoading]="loading">
<clr-dg-action-bar> <clr-dg-action-bar>
<div class="clr-row center"> <div class="clr-row center">
<div class="clr-col-1"> <div class="ml-05">
<button (click)="scanNow()" type="button" class="btn btn-secondary" [clrLoading]="scanBtnState" [disabled]="!(hasEnabledScanner && hasScanningPermission && !onSendingScanCommand)"><clr-icon shape="shield-check" size="16"></clr-icon>&nbsp;{{'VULNERABILITY.SCAN_NOW' | translate}}</button> <button (click)="scanNow()" type="button" class="btn btn-secondary" [clrLoading]="scanBtnState" [disabled]="!(hasEnabledScanner && hasScanningPermission && !onSendingScanCommand)"><clr-icon shape="shield-check" size="16"></clr-icon>&nbsp;{{'VULNERABILITY.SCAN_NOW' | translate}}</button>
</div> </div>
<div class="clr-col"> <div class="ml-1">
<div [hidden]="!shouldShowBar()"> <div [hidden]="!shouldShowBar()">
<hbr-vulnerability-bar [summary]="handleScanOverview(artifact?.scan_overview)" [scanner]="scanner" <hbr-vulnerability-bar [summary]="handleScanOverview(artifact?.scan_overview)" [scanner]="scanner"
(submitFinish)="submitFinish($event)" [projectName]="projectName" [repoName]="repoName" (submitFinish)="submitFinish($event)" [projectName]="projectName" [repoName]="repoName"

View File

@ -46,3 +46,6 @@
.no-border { .no-border {
border: none; border: none;
} }
.ml-05 {
margin-left: 0.5rem;
}

View File

@ -24,7 +24,7 @@ export class ArtifactCommonPropertiesComponent implements OnInit, OnChanges {
@Input() artifactDetails: Artifact; @Input() artifactDetails: Artifact;
commonProperties: { [key: string]: any } = {}; commonProperties: { [key: string]: any } = {};
constructor(private translate: TranslateService) { constructor() {
} }
ngOnInit() { ngOnInit() {
@ -44,7 +44,7 @@ export class ArtifactCommonPropertiesComponent implements OnInit, OnChanges {
} }
} }
if (name === Types.CREATED) { if (name === Types.CREATED) {
this.commonProperties[name] = new DatePipe(this.translate.currentLang) this.commonProperties[name] = new DatePipe('en-us')
.transform(this.commonProperties[name], 'short'); .transform(this.commonProperties[name], 'short');
} }
} }

View File

@ -1,7 +1,7 @@
<div class="arrow-block" *ngIf="!withAdmiral"> <div class="arrow-block" *ngIf="!withAdmiral">
<a class="pl-0" (click)="goBackPro()">{{'SIDE_NAV.PROJECTS'| translate}}</a> <a class="pl-0" (click)="goBackPro()">{{'SIDE_NAV.PROJECTS'| translate}}</a>
<span class="back-icon"><</span> <span class="back-icon"><</span>
<a (click)="goBackRep()">{{'REPOSITORY.REPOSITORIES'| translate}}</a> <a (click)="goBackRep()">{{projectName}}</a>
<span class="back-icon"><</span> <span class="back-icon"><</span>
<a (click)="goBack()">{{repositoryName}}</a> <a (click)="goBack()">{{repositoryName}}</a>

View File

@ -30,12 +30,14 @@
<clr-dg-column [clrDgField]="'name'">{{'REPOSITORY.NAME' | translate}}</clr-dg-column> <clr-dg-column [clrDgField]="'name'">{{'REPOSITORY.NAME' | translate}}</clr-dg-column>
<clr-dg-column>{{'REPOSITORY.ARTIFACTS_COUNT' | translate}}</clr-dg-column> <clr-dg-column>{{'REPOSITORY.ARTIFACTS_COUNT' | translate}}</clr-dg-column>
<clr-dg-column [clrDgSortBy]="'pull_count'">{{'REPOSITORY.PULL_COUNT' | translate}}</clr-dg-column> <clr-dg-column [clrDgSortBy]="'pull_count'">{{'REPOSITORY.PULL_COUNT' | translate}}</clr-dg-column>
<clr-dg-column [clrDgSortBy]="'update_time'">{{'REPOSITORY.LAST_MODIFIED' | translate}}</clr-dg-column>
<clr-dg-placeholder>{{'REPOSITORY.PLACEHOLDER' | translate }}</clr-dg-placeholder> <clr-dg-placeholder>{{'REPOSITORY.PLACEHOLDER' | translate }}</clr-dg-placeholder>
<clr-dg-row *ngFor="let r of repositories" [clrDgItem]="r"> <clr-dg-row *ngFor="let r of repositories" [clrDgItem]="r">
<clr-dg-cell><a href="javascript:void(0)" (click)="goIntoRepo(r)"><span *ngIf="withAdmiral" class="list-img"><img [src]="getImgLink(r)"/></span>{{r.name}}</a></clr-dg-cell> <clr-dg-cell><a href="javascript:void(0)" (click)="goIntoRepo(r)"><span *ngIf="withAdmiral" class="list-img"><img [src]="getImgLink(r)"/></span>{{r.name}}</a></clr-dg-cell>
<!-- to do --> <!-- to do -->
<clr-dg-cell>{{r.artifact_count}}</clr-dg-cell> <clr-dg-cell>{{r.artifact_count}}</clr-dg-cell>
<clr-dg-cell>{{r.pull_count}}</clr-dg-cell> <clr-dg-cell>{{r.pull_count}}</clr-dg-cell>
<clr-dg-cell>{{r.update_time | date:'short'}}</clr-dg-cell>
</clr-dg-row> </clr-dg-row>
<clr-dg-footer> <clr-dg-footer>
<span *ngIf="totalCount">{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'REPOSITORY.OF' | translate}}</span> <span *ngIf="totalCount">{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'REPOSITORY.OF' | translate}}</span>
@ -72,12 +74,16 @@
</div> </div>
<div class="form-group"> <div class="form-group">
<label>{{'REPOSITORY.ARTIFACTS_COUNT' | translate}}</label> <label>{{'REPOSITORY.ARTIFACTS_COUNT' | translate}}</label>
<div>{{item.tags_count}}</div> <div>{{item.artifact_count}}</div>
</div> </div>
<div class="form-group"> <div class="form-group">
<label>{{'REPOSITORY.PULL_COUNT' | translate}}</label> <label>{{'REPOSITORY.PULL_COUNT' | translate}}</label>
<div>{{item.pull_count}}</div> <div>{{item.pull_count}}</div>
</div> </div>
<div class="form-group">
<label>{{'REPOSITORY.LAST_MODIFIED' | translate}}</label>
<div>{{item.update_time | date: 'short'}}</div>
</div>
</div> </div>
<div class="card-footer"> <div class="card-footer">
<clr-dropdown [clrCloseMenuOnItemClick]="false"> <clr-dropdown [clrCloseMenuOnItemClick]="false">

View File

@ -76,7 +76,7 @@
} }
.form-group > label { .form-group > label {
width: 100px; width: 142px;
} }
.card-media-block > img { .card-media-block > img {

View File

@ -43,6 +43,8 @@ export class ResultBarChartComponent implements OnInit, OnDestroy {
timerHandler: any; timerHandler: any;
@Output() @Output()
submitFinish: EventEmitter<boolean> = new EventEmitter<boolean>(); submitFinish: EventEmitter<boolean> = new EventEmitter<boolean>();
@Output()
scanFinished: EventEmitter<Artifact> = new EventEmitter<Artifact>();
constructor( constructor(
private artifactService: ArtifactService, private artifactService: ArtifactService,
@ -165,6 +167,7 @@ export class ResultBarChartComponent implements OnInit, OnDestroy {
this.stateCheckTimer.unsubscribe(); this.stateCheckTimer.unsubscribe();
this.stateCheckTimer = null; this.stateCheckTimer = null;
} }
this.scanFinished.emit(artifact);
} }
this.channel.ArtifactDetail$.next(artifact); this.channel.ArtifactDetail$.next(artifact);
}, error => { }, error => {

View File

@ -26,8 +26,8 @@
</div> </div>
<div class="clr-row"> <div class="clr-row">
<div class="clr-col-4"></div> <div class="clr-col-4"></div>
<div class="clr-col-8"> <div class="clr-col-8 margin-top-5px">
<span>{{'TAG_RETENTION.REP_SEPARATOR' | translate}}</span> <span class="tootip">{{'TAG_RETENTION.REP_SEPARATOR' | translate}}</span>
</div> </div>
</div> </div>
</div> </div>
@ -57,7 +57,7 @@
</div> </div>
</div> </div>
</div> </div>
<div class="height-85"> <div class="height-95">
<div class="clr-row"> <div class="clr-row">
<div class="clr-col-4"> <div class="clr-col-4">
<label>{{'TAG_RETENTION.TAGS' | translate}}</label> <label>{{'TAG_RETENTION.TAGS' | translate}}</label>
@ -84,7 +84,7 @@
</div> </div>
<div class="clr-row"> <div class="clr-row">
<div class="clr-col-4"></div> <div class="clr-col-4"></div>
<div class="clr-col-8"> <div class="clr-col-8 margin-top-5px">
<span class="tootip">{{'TAG_RETENTION.TAG_SEPARATOR' | translate}}</span> <span class="tootip">{{'TAG_RETENTION.TAG_SEPARATOR' | translate}}</span>
</div> </div>
</div> </div>

View File

@ -11,8 +11,8 @@
.height-72 { .height-72 {
height: 72px; height: 72px;
} }
.height-85 { .height-95 {
height: 85px; height: 95px;
} }
.display-none { .display-none {
@ -33,3 +33,6 @@
.font-size-13 { .font-size-13 {
font-size: 13px; font-size: 13px;
} }
.margin-top-5px {
margin-top: 5px;
}

View File

@ -703,7 +703,8 @@
"DEPLOY": "DEPLOY", "DEPLOY": "DEPLOY",
"ADDITIONAL_INFO": "Add Additional Info", "ADDITIONAL_INFO": "Add Additional Info",
"REPO_NAME": "Repository", "REPO_NAME": "Repository",
"MARKDOWN": "Styling with Markdown is supported" "MARKDOWN": "Styling with Markdown is supported",
"LAST_MODIFIED": "Last Modified Time"
}, },
"HELM_CHART": { "HELM_CHART": {
"HELMCHARTS": "Charts", "HELMCHARTS": "Charts",

View File

@ -704,7 +704,8 @@
"DEPLOY": "DEPLOY", "DEPLOY": "DEPLOY",
"ADDITIONAL_INFO": "Add Additional Info", "ADDITIONAL_INFO": "Add Additional Info",
"REPO_NAME": "Repository", "REPO_NAME": "Repository",
"MARKDOWN": "Styling with Markdown is supported" "MARKDOWN": "Styling with Markdown is supported",
"LAST_MODIFIED": "Last Modified Time"
}, },
"HELM_CHART": { "HELM_CHART": {
"HELMCHARTS": "Charts", "HELMCHARTS": "Charts",

View File

@ -690,7 +690,8 @@
"DEPLOY": "DEPLOY", "DEPLOY": "DEPLOY",
"ADDITIONAL_INFO": "Add Additional Info", "ADDITIONAL_INFO": "Add Additional Info",
"REPO_NAME": "Repository", "REPO_NAME": "Repository",
"MARKDOWN": "Styling with Markdown is supported" "MARKDOWN": "Styling with Markdown is supported",
"LAST_MODIFIED": "Last Modified Time"
}, },
"HELM_CHART": { "HELM_CHART": {
"HELMCHARTS": "Charts", "HELMCHARTS": "Charts",

View File

@ -702,7 +702,8 @@
"ACTION": "AÇÃO", "ACTION": "AÇÃO",
"DEPLOY": "DEPLOY", "DEPLOY": "DEPLOY",
"ADDITIONAL_INFO": "Adicionar informação adicional", "ADDITIONAL_INFO": "Adicionar informação adicional",
"MARKDOWN": "Styling with Markdown is supported" "MARKDOWN": "Styling with Markdown is supported",
"LAST_MODIFIED": "Last Modified Time"
}, },
"HELM_CHART": { "HELM_CHART": {
"HELMCHARTS": "Charts", "HELMCHARTS": "Charts",

View File

@ -703,7 +703,8 @@
"DEPLOY": "YÜKLE", "DEPLOY": "YÜKLE",
"ADDITIONAL_INFO": "Ek Bilgi Ekle", "ADDITIONAL_INFO": "Ek Bilgi Ekle",
"REPO_NAME": "Depo", "REPO_NAME": "Depo",
"MARKDOWN": "Markdown ile stil desteklenir" "MARKDOWN": "Markdown ile stil desteklenir",
"LAST_MODIFIED": "Last Modified Time"
}, },
"HELM_CHART": { "HELM_CHART": {
"HELMCHARTS": "Tablolar", "HELMCHARTS": "Tablolar",

View File

@ -704,7 +704,8 @@
"DEPLOY": "部署", "DEPLOY": "部署",
"ADDITIONAL_INFO": "添加信息", "ADDITIONAL_INFO": "添加信息",
"REPO_NAME": "镜像仓库", "REPO_NAME": "镜像仓库",
"MARKDOWN": "支持使用Markdown进行样式设置" "MARKDOWN": "支持使用Markdown进行样式设置",
"LAST_MODIFIED": "最新变更时间"
}, },
"HELM_CHART": { "HELM_CHART": {
"HELMCHARTS": "Charts", "HELMCHARTS": "Charts",

View File

@ -1,11 +1,11 @@
{ {
"APP_TITLE":{ "APP_TITLE":{
"VMW_HARBOR":"VMW_HARBOR", "VMW_HARBOR":"Harbor",
"HARBOR":"HARBOR", "HARBOR":"Harbor",
"VIC":"vSphere Integrated Containers", "VIC":"vSphere Integrated Containers",
"MGMT":"管理", "MGMT":"管理",
"REG":"註冊表", "REG":"註冊表",
"HARBOR_SWAGGER":"HARBOR_SWAGGER", "HARBOR_SWAGGER":"Harbor Swagger",
"THEME_DARK_TEXT": "深色主題", "THEME_DARK_TEXT": "深色主題",
"THEME_LIGHT_TEXT": "淺色主題" "THEME_LIGHT_TEXT": "淺色主題"
}, },
@ -270,7 +270,7 @@
"NEW_USER": "添加用戶成員", "NEW_USER": "添加用戶成員",
"NEW_MEMBER": "新建成員", "NEW_MEMBER": "新建成員",
"MEMBER": "成員", "MEMBER": "成員",
"NAME": "名", "NAME": "",
"EMAIL": "郵箱", "EMAIL": "郵箱",
"ROLE": "角色", "ROLE": "角色",
"SYS_ADMIN": "系統管理員", "SYS_ADMIN": "系統管理員",
@ -317,7 +317,7 @@
"REMOVE": "移除成員" "REMOVE": "移除成員"
}, },
"ROBOT_ACCOUNT":{ "ROBOT_ACCOUNT":{
"NAME": "名", "NAME": "",
"PERMISSIONS": "權限", "PERMISSIONS": "權限",
"TOKEN": "令牌", "TOKEN": "令牌",
"NEW_ROBOT_ACCOUNT": "添加機器人帳戶", "NEW_ROBOT_ACCOUNT": "添加機器人帳戶",
@ -700,7 +700,8 @@
"DEPLOY": "部署", "DEPLOY": "部署",
"ADDITIONAL_INFO": "添加信息", "ADDITIONAL_INFO": "添加信息",
"REPO_NAME": "鏡像倉庫", "REPO_NAME": "鏡像倉庫",
"MARKDOWN": "支持使用Markdown進行樣式設置" "MARKDOWN": "支持使用Markdown進行樣式設置",
"LAST_MODIFIED": "Last Modified Time"
}, },
"HELM_CHART":{ "HELM_CHART":{
"HELMCHARTS":"圖表", "HELMCHARTS":"圖表",

View File

@ -145,7 +145,7 @@ export class VulnerabilityConfigComponent implements OnInit, OnDestroy {
if (metadata && metadata.properties) { if (metadata && metadata.properties) {
for (let key in metadata.properties) { for (let key in metadata.properties) {
if (key === DATABASE_UPDATED_PROPERTY && metadata.properties[key]) { if (key === DATABASE_UPDATED_PROPERTY && metadata.properties[key]) {
this.updatedTimeStr = new DatePipe(this.translate.currentLang).transform(metadata.properties[key], 'short'); this.updatedTimeStr = new DatePipe('en-us').transform(metadata.properties[key], 'short');
} }
} }
} }

View File

@ -22,6 +22,7 @@ import { of } from "rxjs";
import { HttpClient } from "@angular/common/http"; import { HttpClient } from "@angular/common/http";
import { HttpClientTestingModule } from "@angular/common/http/testing"; import { HttpClientTestingModule } from "@angular/common/http/testing";
import { CURRENT_BASE_HREF } from "../../utils/utils"; import { CURRENT_BASE_HREF } from "../../utils/utils";
import { AppConfigService } from '../../../app/services/app-config.service';
describe("CreateEditEndpointComponent (inline template)", () => { describe("CreateEditEndpointComponent (inline template)", () => {
let mockData: Endpoint = { let mockData: Endpoint = {
@ -263,6 +264,14 @@ describe("CreateEditEndpointComponent (inline template)", () => {
let spy: jasmine.Spy; let spy: jasmine.Spy;
let spyAdapter: jasmine.Spy; let spyAdapter: jasmine.Spy;
const mockAppConfigService = {
getConfig: () => {
return {
project_creation_restriction: "",
with_chartmuseum: ""
};
}
};
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [SharedModule, NoopAnimationsModule], imports: [SharedModule, NoopAnimationsModule],
@ -276,6 +285,7 @@ describe("CreateEditEndpointComponent (inline template)", () => {
{ provide: SERVICE_CONFIG, useValue: config }, { provide: SERVICE_CONFIG, useValue: config },
{ provide: EndpointService, useClass: EndpointDefaultService }, { provide: EndpointService, useClass: EndpointDefaultService },
{ provide: HttpClient, useValue: fakedHttp }, { provide: HttpClient, useValue: fakedHttp },
{ provide: AppConfigService, useValue: mockAppConfigService }
] ]
}); });
})); }));

View File

@ -32,10 +32,12 @@ import { Endpoint, PingEndpoint } from "../../services/interface";
import { clone, compareValue, CURRENT_BASE_HREF, isEmptyObject } from "../../utils/utils"; import { clone, compareValue, CURRENT_BASE_HREF, isEmptyObject } from "../../utils/utils";
import { HttpClient } from "@angular/common/http"; import { HttpClient } from "@angular/common/http";
import { catchError } from "rxjs/operators"; import { catchError } from "rxjs/operators";
import { AppConfigService } from '../../../app/services/app-config.service';
const FAKE_PASSWORD = "rjGcfuRu"; const FAKE_PASSWORD = "rjGcfuRu";
const FAKE_JSON_KEY = "No Change"; const FAKE_JSON_KEY = "No Change";
const METADATA_URL = CURRENT_BASE_HREF + "/replication/adapterinfos"; const METADATA_URL = CURRENT_BASE_HREF + "/replication/adapterinfos";
const HELM_HUB = "helm-hub";
@Component({ @Component({
selector: "hbr-create-edit-endpoint", selector: "hbr-create-edit-endpoint",
templateUrl: "./create-edit-endpoint.component.html", templateUrl: "./create-edit-endpoint.component.html",
@ -78,21 +80,31 @@ export class CreateEditEndpointComponent
private errorHandler: ErrorHandler, private errorHandler: ErrorHandler,
private translateService: TranslateService, private translateService: TranslateService,
private ref: ChangeDetectorRef, private ref: ChangeDetectorRef,
private http: HttpClient private http: HttpClient,
private appConfigService: AppConfigService,
) {} ) {}
ngOnInit(): void { ngOnInit(): void {
this.getAdapters();
this.getAdapterInfo();
}
getAdapters() {
this.endpointService.getAdapters().subscribe( this.endpointService.getAdapters().subscribe(
adapters => { adapters => {
this.adapterList = 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);
}
}
}
}, },
error => { error => {
this.errorHandler.error(error); this.errorHandler.error(error);
} }
); );
this.getAdapterInfo();
} }
getAdapterInfo() { getAdapterInfo() {
this.http.get(METADATA_URL) this.http.get(METADATA_URL)
.pipe(catchError(error => observableThrowError(error))) .pipe(catchError(error => observableThrowError(error)))

View File

@ -22,6 +22,7 @@ import { click, CURRENT_BASE_HREF } from "../../utils/utils";
import { of } from "rxjs"; import { of } from "rxjs";
import { HttpClientTestingModule } from "@angular/common/http/testing"; import { HttpClientTestingModule } from "@angular/common/http/testing";
import { HttpClient } from "@angular/common/http"; import { HttpClient } from "@angular/common/http";
import { AppConfigService } from '../../../app/services/app-config.service';
describe("EndpointComponent (inline template)", () => { describe("EndpointComponent (inline template)", () => {
let adapterInfoMockData = { let adapterInfoMockData = {
@ -313,6 +314,14 @@ describe("EndpointComponent (inline template)", () => {
let config: IServiceConfig = { let config: IServiceConfig = {
systemInfoEndpoint: CURRENT_BASE_HREF + "/endpoints/testing" systemInfoEndpoint: CURRENT_BASE_HREF + "/endpoints/testing"
}; };
const mockAppConfigService = {
getConfig: () => {
return {
project_creation_restriction: "",
with_chartmuseum: ""
};
}
};
let endpointService: EndpointService; let endpointService: EndpointService;
let spy: jasmine.Spy; let spy: jasmine.Spy;
@ -335,6 +344,7 @@ describe("EndpointComponent (inline template)", () => {
{ provide: EndpointService, useClass: EndpointDefaultService }, { provide: EndpointService, useClass: EndpointDefaultService },
{ provide: OperationService }, { provide: OperationService },
{ provide: HttpClient, useValue: fakedHttp }, { provide: HttpClient, useValue: fakedHttp },
{ provide: AppConfigService, useValue: mockAppConfigService }
] ]
}); });
})); }));

View File

@ -108,7 +108,7 @@
<clr-dg-cell>{{t.src_resource}}</clr-dg-cell> <clr-dg-cell>{{t.src_resource}}</clr-dg-cell>
<clr-dg-cell>{{t.dst_resource}}</clr-dg-cell> <clr-dg-cell>{{t.dst_resource}}</clr-dg-cell>
<clr-dg-cell>{{t.operation}}</clr-dg-cell> <clr-dg-cell>{{t.operation}}</clr-dg-cell>
<clr-dg-cell>{{t.status}}</clr-dg-cell> <clr-dg-cell>{{getStatusStr(t.status)}}</clr-dg-cell>
<clr-dg-cell>{{t.start_time | date: 'short'}}</clr-dg-cell> <clr-dg-cell>{{t.start_time | date: 'short'}}</clr-dg-cell>
<clr-dg-cell>{{t.end_time && t.end_time != '0001-01-01T00:00:00Z' ? (t.end_time | date: 'short') : "-"}}</clr-dg-cell> <clr-dg-cell>{{t.end_time && t.end_time != '0001-01-01T00:00:00Z' ? (t.end_time | date: 'short') : "-"}}</clr-dg-cell>
<clr-dg-cell> <clr-dg-cell>

View File

@ -11,6 +11,9 @@ import { RequestQueryParams } from "../../../services/RequestQueryParams";
import { REFRESH_TIME_DIFFERENCE } from '../../../entities/shared.const'; import { REFRESH_TIME_DIFFERENCE } from '../../../entities/shared.const';
const executionStatus = 'InProgress'; const executionStatus = 'InProgress';
const STATUS_MAP = {
"Succeed": "Succeeded"
};
@Component({ @Component({
selector: 'replication-tasks', selector: 'replication-tasks',
templateUrl: './replication-tasks.component.html', templateUrl: './replication-tasks.component.html',
@ -203,11 +206,14 @@ export class ReplicationTasksComponent implements OnInit, OnDestroy {
} }
openFilter(isOpen: boolean): void { openFilter(isOpen: boolean): void {
if (isOpen) { this.isOpenFilterTag = isOpen;
this.isOpenFilterTag = true; }
} else {
this.isOpenFilterTag = false; getStatusStr(status: string): string {
if (STATUS_MAP && STATUS_MAP[status]) {
return STATUS_MAP[status];
} }
return status;
} }
} }

View File

@ -65,7 +65,7 @@
<a href="javascript:void(0)" (click)="goToLink(j.id)">{{j.id}}</a> <a href="javascript:void(0)" (click)="goToLink(j.id)">{{j.id}}</a>
</clr-dg-cell> </clr-dg-cell>
<clr-dg-cell> <clr-dg-cell>
{{j.status}} {{getStatusStr(j.status)}}
<clr-tooltip> <clr-tooltip>
<clr-icon *ngIf="j.status_text" clrTooltipTrigger shape="info-circle" size="20"></clr-icon> <clr-icon *ngIf="j.status_text" clrTooltipTrigger shape="info-circle" size="20"></clr-icon>
<clr-tooltip-content [clrPosition]="'left'" clrSize="md" *clrIfOpen> <clr-tooltip-content [clrPosition]="'left'" clrSize="md" *clrIfOpen>

View File

@ -82,6 +82,10 @@ export class SearchOption {
pageSize: number = DEFAULT_PAGE_SIZE; pageSize: number = DEFAULT_PAGE_SIZE;
} }
const STATUS_MAP = {
"Succeed": "Succeeded"
};
@Component({ @Component({
selector: "hbr-replication", selector: "hbr-replication",
templateUrl: "./replication.component.html", templateUrl: "./replication.component.html",
@ -518,4 +522,10 @@ export class ReplicationComponent implements OnInit, OnDestroy {
return '-'; return '-';
} }
} }
getStatusStr(status: string): string {
if (STATUS_MAP && STATUS_MAP[status]) {
return STATUS_MAP[status];
}
return status;
}
} }