From aa681eb018c7fc6fd2ad4213286ccec6dd0c9357 Mon Sep 17 00:00:00 2001 From: Steven Zou Date: Thu, 20 Jul 2017 09:28:00 +0800 Subject: [PATCH] Provide 'Scan Now' menu in the tag list (#2819) --- src/ui_ng/lib/src/channel/channel.service.ts | 28 +++ src/ui_ng/lib/src/channel/index.ts | 1 + src/ui_ng/lib/src/harbor-library.module.ts | 7 +- src/ui_ng/lib/src/index.ts | 3 +- src/ui_ng/lib/src/tag/tag.component.html.ts | 5 +- src/ui_ng/lib/src/tag/tag.component.spec.ts | 2 + src/ui_ng/lib/src/tag/tag.component.ts | 119 ++++------- .../result-bar-chart.component.spec.ts | 47 +++-- .../result-bar-chart.component.ts | 195 +++++++++++++----- .../vulnerability-scanning/scanning.html.ts | 12 +- src/ui_ng/package.json | 2 +- src/ui_ng/src/i18n/lang/en-us-lang.json | 5 +- src/ui_ng/src/i18n/lang/es-es-lang.json | 5 +- src/ui_ng/src/i18n/lang/zh-cn-lang.json | 5 +- 14 files changed, 272 insertions(+), 164 deletions(-) create mode 100644 src/ui_ng/lib/src/channel/channel.service.ts create mode 100644 src/ui_ng/lib/src/channel/index.ts diff --git a/src/ui_ng/lib/src/channel/channel.service.ts b/src/ui_ng/lib/src/channel/channel.service.ts new file mode 100644 index 000000000..69788ce5d --- /dev/null +++ b/src/ui_ng/lib/src/channel/channel.service.ts @@ -0,0 +1,28 @@ +// Copyright (c) 2017 VMware, Inc. All Rights Reserved. +// +// 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 { Injectable } from '@angular/core'; +import { Subject } from 'rxjs/Subject'; +import { Observable } from "rxjs/Observable"; + +@Injectable() +export class ChannelService { + + //Declare for publishing scan event + scanCommandSource = new Subject(); + scanCommand$ = this.scanCommandSource.asObservable(); + + publishScanEvent(tagId: string): void { + this.scanCommandSource.next(tagId); + } +} \ No newline at end of file diff --git a/src/ui_ng/lib/src/channel/index.ts b/src/ui_ng/lib/src/channel/index.ts new file mode 100644 index 000000000..53dcbeb6f --- /dev/null +++ b/src/ui_ng/lib/src/channel/index.ts @@ -0,0 +1 @@ +export * from './channel.service'; \ No newline at end of file diff --git a/src/ui_ng/lib/src/harbor-library.module.ts b/src/ui_ng/lib/src/harbor-library.module.ts index 8d309caeb..1af8b4a9f 100644 --- a/src/ui_ng/lib/src/harbor-library.module.ts +++ b/src/ui_ng/lib/src/harbor-library.module.ts @@ -52,6 +52,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { TranslateServiceInitializer } from './i18n/index'; import { DEFAULT_LANG_COOKIE_KEY, DEFAULT_SUPPORTING_LANGS, DEFAULT_LANG } from './utils'; +import { ChannelService } from './channel/index'; /** * Declare default service configuration; all the endpoints will be defined in @@ -203,7 +204,8 @@ export class HarborLibraryModule { useFactory: initConfig, deps: [TranslateServiceInitializer, SERVICE_CONFIG], multi: true - } + }, + ChannelService ] }; } @@ -221,7 +223,8 @@ export class HarborLibraryModule { config.repositoryService || { provide: RepositoryService, useClass: RepositoryDefaultService }, config.tagService || { provide: TagService, useClass: TagDefaultService }, config.scanningService || { provide: ScanningResultService, useClass: ScanningResultDefaultService }, - config.configService || { provide: ConfigurationService, useClass: ConfigurationDefaultService } + config.configService || { provide: ConfigurationService, useClass: ConfigurationDefaultService }, + ChannelService ] }; } diff --git a/src/ui_ng/lib/src/index.ts b/src/ui_ng/lib/src/index.ts index ad746c21a..136661050 100644 --- a/src/ui_ng/lib/src/index.ts +++ b/src/ui_ng/lib/src/index.ts @@ -16,4 +16,5 @@ export * from './i18n/index'; export * from './push-image/index'; export * from './third-party/index'; export * from './config/index'; -export * from './job-log-viewer/index'; \ No newline at end of file +export * from './job-log-viewer/index'; +export * from './channel/index'; \ No newline at end of file diff --git a/src/ui_ng/lib/src/tag/tag.component.html.ts b/src/ui_ng/lib/src/tag/tag.component.html.ts index bd1d5b65c..c498575df 100644 --- a/src/ui_ng/lib/src/tag/tag.component.html.ts +++ b/src/ui_ng/lib/src/tag/tag.component.html.ts @@ -27,8 +27,9 @@ export const TAG_TEMPLATE = ` {{'TGA.PLACEHOLDER' | translate }} + + - {{t.name}} @@ -36,7 +37,7 @@ export const TAG_TEMPLATE = ` docker pull {{registryUrl}}/{{repoName}}:{{t.name}} - + diff --git a/src/ui_ng/lib/src/tag/tag.component.spec.ts b/src/ui_ng/lib/src/tag/tag.component.spec.ts index 0f91f9952..d63279c5a 100644 --- a/src/ui_ng/lib/src/tag/tag.component.spec.ts +++ b/src/ui_ng/lib/src/tag/tag.component.spec.ts @@ -15,6 +15,7 @@ import { VULNERABILITY_DIRECTIVES } from '../vulnerability-scanning/index'; import { FILTER_DIRECTIVES } from '../filter/index' import { Observable, Subscription } from 'rxjs/Rx'; +import { ChannelService } from '../channel/index'; describe('TagComponent (inline template)', () => { @@ -52,6 +53,7 @@ describe('TagComponent (inline template)', () => { ], providers: [ ErrorHandler, + ChannelService, { provide: SERVICE_CONFIG, useValue: config }, { provide: TagService, useClass: TagDefaultService }, { provide: ScanningResultService, useClass: ScanningResultDefaultService } diff --git a/src/ui_ng/lib/src/tag/tag.component.ts b/src/ui_ng/lib/src/tag/tag.component.ts index 837c22e63..3827344bc 100644 --- a/src/ui_ng/lib/src/tag/tag.component.ts +++ b/src/ui_ng/lib/src/tag/tag.component.ts @@ -20,14 +20,17 @@ import { EventEmitter, ChangeDetectionStrategy, ChangeDetectorRef, - ElementRef, - OnDestroy + ElementRef } from '@angular/core'; import { TagService } from '../service/tag.service'; - import { ErrorHandler } from '../error-handler/error-handler'; -import { ConfirmationTargets, ConfirmationState, ConfirmationButtons } from '../shared/shared.const'; +import { ChannelService } from '../channel/index'; +import { + ConfirmationTargets, + ConfirmationState, + ConfirmationButtons +} from '../shared/shared.const'; import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component'; import { ConfirmationMessage } from '../confirmation-dialog/confirmation-message'; @@ -38,25 +41,23 @@ import { Tag, TagClickEvent } from '../service/interface'; import { TAG_TEMPLATE } from './tag.component.html'; import { TAG_STYLE } from './tag.component.css'; -import { toPromise, CustomComparator, VULNERABILITY_SCAN_STATUS } from '../utils'; +import { + toPromise, + CustomComparator, + VULNERABILITY_SCAN_STATUS +} from '../utils'; import { TranslateService } from '@ngx-translate/core'; import { State, Comparator } from 'clarity-angular'; -import { ScanningResultService } from '../service/index'; - -import { Observable, Subscription } from 'rxjs/Rx'; - -const STATE_CHECK_INTERVAL: number = 2000;//2s - @Component({ selector: 'hbr-tag', template: TAG_TEMPLATE, styles: [TAG_STYLE], changeDetection: ChangeDetectionStrategy.OnPush }) -export class TagComponent implements OnInit, OnDestroy { +export class TagComponent implements OnInit { @Input() projectId: number; @Input() repoName: string; @@ -83,11 +84,6 @@ export class TagComponent implements OnInit, OnDestroy { createdComparator: Comparator = new CustomComparator('created', 'date'); loading: boolean = false; - - stateCheckTimer: Subscription; - tagsInScanning: { [key: string]: any } = {}; - scanningTagCount: number = 0; - copyFailed: boolean = false; @ViewChild('confirmationDialog') @@ -99,8 +95,9 @@ export class TagComponent implements OnInit, OnDestroy { private errorHandler: ErrorHandler, private tagService: TagService, private translateService: TranslateService, - private scanningService: ScanningResultService, - private ref: ChangeDetectorRef) { } + private ref: ChangeDetectorRef, + private channel: ChannelService + ) { } confirmDeletion(message: ConfirmationAcknowledgement) { if (message && @@ -135,18 +132,6 @@ export class TagComponent implements OnInit, OnDestroy { } this.retrieve(); - - this.stateCheckTimer = Observable.timer(STATE_CHECK_INTERVAL, STATE_CHECK_INTERVAL).subscribe(() => { - if (this.scanningTagCount > 0) { - this.updateScanningStates(); - } - }); - } - - ngOnDestroy(): void { - if (this.stateCheckTimer) { - this.stateCheckTimer.unsubscribe(); - } } retrieve() { @@ -203,7 +188,7 @@ export class TagComponent implements OnInit, OnDestroy { this.copyFailed = false; } } - + onTagClick(tag: Tag): void { if (tag) { let evt: TagClickEvent = { @@ -215,49 +200,6 @@ export class TagComponent implements OnInit, OnDestroy { } } - scanTag(tagId: string): void { - //Double check - if (this.tagsInScanning[tagId]) { - return; - } - toPromise(this.scanningService.startVulnerabilityScanning(this.repoName, tagId)) - .then(() => { - //Add to scanning map - this.tagsInScanning[tagId] = tagId; - //Counting - this.scanningTagCount += 1; - }) - .catch(error => this.errorHandler.error(error)); - } - - updateScanningStates(): void { - toPromise(this.tagService - .getTags(this.repoName)) - .then(items => { - console.debug("updateScanningStates called!"); - //Reset the scanning states - this.tagsInScanning = {}; - this.scanningTagCount = 0; - - items.forEach(item => { - if (item.scan_overview) { - if (item.scan_overview.scan_status === VULNERABILITY_SCAN_STATUS.pending || - item.scan_overview.scan_status === VULNERABILITY_SCAN_STATUS.running) { - this.tagsInScanning[item.name] = item.name; - this.scanningTagCount += 1; - } - } - }); - - this.tags = items; - }) - .catch(error => { - this.errorHandler.error(error); - }); - let hnd = setInterval(() => this.ref.markForCheck(), 100); - setTimeout(() => clearInterval(hnd), 1000); - } - onSuccess($event: any): void { this.copyFailed = false; //Directly close dialog @@ -268,8 +210,33 @@ export class TagComponent implements OnInit, OnDestroy { //Show error this.copyFailed = true; //Select all text - if(this.textInput){ + if (this.textInput) { this.textInput.nativeElement.select(); } } + + //Get vulnerability scanning status + scanStatus(t: Tag): string { + if (t && t.scan_overview && t.scan_overview.scan_status) { + return t.scan_overview.scan_status; + } + + return VULNERABILITY_SCAN_STATUS.unknown; + } + + //Whether show the 'scan now' menu + canScanNow(t: Tag): boolean { + if (!this.withClair) { return false; } + let st: string = this.scanStatus(t); + + return st !== VULNERABILITY_SCAN_STATUS.pending && + st !== VULNERABILITY_SCAN_STATUS.running; + } + + //Trigger scan + scanNow(tagId: string): void { + if (tagId) { + this.channel.publishScanEvent(tagId); + } + } } diff --git a/src/ui_ng/lib/src/vulnerability-scanning/result-bar-chart.component.spec.ts b/src/ui_ng/lib/src/vulnerability-scanning/result-bar-chart.component.spec.ts index e14c859d3..f796f628f 100644 --- a/src/ui_ng/lib/src/vulnerability-scanning/result-bar-chart.component.spec.ts +++ b/src/ui_ng/lib/src/vulnerability-scanning/result-bar-chart.component.spec.ts @@ -1,17 +1,20 @@ -import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; -import { By } from '@angular/platform-browser'; -import { HttpModule } from '@angular/http'; -import { DebugElement } from '@angular/core'; +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { Observable } from 'rxjs/Observable'; import { VulnerabilitySummary } from '../service/index'; -import { ResultBarChartComponent, ScanState } from './result-bar-chart.component'; +import { ResultBarChartComponent } from './result-bar-chart.component'; import { ResultTipComponent } from './result-tip.component'; -import { ScanningResultService, ScanningResultDefaultService } from '../service/scanning.service'; +import { + ScanningResultService, + ScanningResultDefaultService, + TagService, + TagDefaultService +} from '../service/index'; import { SERVICE_CONFIG, IServiceConfig } from '../service.config'; import { ErrorHandler } from '../error-handler/index'; import { SharedModule } from '../shared/shared.module'; import { VULNERABILITY_SCAN_STATUS } from '../utils'; +import { ChannelService } from '../channel/index'; describe('ResultBarChartComponent (inline template)', () => { let component: ResultBarChartComponent; @@ -52,7 +55,10 @@ describe('ResultBarChartComponent (inline template)', () => { ResultTipComponent], providers: [ ErrorHandler, - { provide: SERVICE_CONFIG, useValue: testConfig } + ChannelService, + { provide: SERVICE_CONFIG, useValue: testConfig }, + { provide: TagService, useValue: TagDefaultService }, + { provide: ScanningResultService, useValue: ScanningResultDefaultService } ] }); @@ -62,7 +68,7 @@ describe('ResultBarChartComponent (inline template)', () => { fixture = TestBed.createComponent(ResultBarChartComponent); component = fixture.componentInstance; component.tagId = "mockTag"; - component.state = ScanState.UNKNOWN; + component.summary = mockData; serviceConfig = TestBed.get(SERVICE_CONFIG); @@ -71,30 +77,28 @@ describe('ResultBarChartComponent (inline template)', () => { it('should be created', () => { expect(component).toBeTruthy(); - }); - - it('should inject the SERVICE_CONFIG', () => { expect(serviceConfig).toBeTruthy(); expect(serviceConfig.vulnerabilityScanningBaseEndpoint).toEqual("/api/vulnerability/testing"); }); - it('should show a button if status is PENDING', async(() => { - component.state = ScanState.PENDING; + it('should show "not scanned" if status is STOPPED', async(() => { + component.summary.scan_status = VULNERABILITY_SCAN_STATUS.stopped; fixture.detectChanges(); - fixture.whenStable().then(() => { // wait for async getRecentLogs + fixture.whenStable().then(() => { fixture.detectChanges(); - let el: HTMLElement = fixture.nativeElement.querySelector('.scanning-button'); + let el: HTMLElement = fixture.nativeElement.querySelector('span'); expect(el).toBeTruthy(); + expect(el.textContent).toEqual('VULNERABILITY.STATE.STOPPED'); }); })); it('should show progress if status is SCANNING', async(() => { - component.state = ScanState.SCANNING; + component.summary.scan_status = VULNERABILITY_SCAN_STATUS.running; fixture.detectChanges(); - fixture.whenStable().then(() => { // wait for async getRecentLogs + fixture.whenStable().then(() => { fixture.detectChanges(); let el: HTMLElement = fixture.nativeElement.querySelector('.progress'); @@ -103,10 +107,10 @@ describe('ResultBarChartComponent (inline template)', () => { })); it('should show QUEUED if status is QUEUED', async(() => { - component.state = ScanState.QUEUED; + component.summary.scan_status = VULNERABILITY_SCAN_STATUS.pending; fixture.detectChanges(); - fixture.whenStable().then(() => { // wait for async getRecentLogs + fixture.whenStable().then(() => { fixture.detectChanges(); let el: HTMLElement = fixture.nativeElement.querySelector('.bar-state'); @@ -119,11 +123,10 @@ describe('ResultBarChartComponent (inline template)', () => { })); it('should show summary bar chart if status is COMPLETED', async(() => { - component.state = ScanState.COMPLETED; - component.summary = mockData; + component.summary.scan_status = VULNERABILITY_SCAN_STATUS.finished; fixture.detectChanges(); - fixture.whenStable().then(() => { // wait for async getRecentLogs + fixture.whenStable().then(() => { fixture.detectChanges(); let el: HTMLElement = fixture.nativeElement.querySelector('.bar-block-none'); diff --git a/src/ui_ng/lib/src/vulnerability-scanning/result-bar-chart.component.ts b/src/ui_ng/lib/src/vulnerability-scanning/result-bar-chart.component.ts index 52cb72d94..18d156378 100644 --- a/src/ui_ng/lib/src/vulnerability-scanning/result-bar-chart.component.ts +++ b/src/ui_ng/lib/src/vulnerability-scanning/result-bar-chart.component.ts @@ -1,35 +1,40 @@ import { Component, Input, - Output, - EventEmitter, - OnInit + OnInit, + OnDestroy, + ChangeDetectionStrategy, + ChangeDetectorRef } from '@angular/core'; -import { VulnerabilitySummary } from '../service/index'; + import { SCANNING_STYLES } from './scanning.css'; import { BAR_CHART_COMPONENT_HTML } from './scanning.html'; import { VULNERABILITY_SCAN_STATUS } from '../utils'; -import { VulnerabilitySeverity } from '../service/index'; +import { + VulnerabilitySummary, + VulnerabilitySeverity, + TagService, + ScanningResultService, + Tag +} from '../service/index'; +import { ErrorHandler } from '../error-handler/index'; +import { toPromise } from '../utils'; +import { Observable, Subscription } from 'rxjs/Rx'; +import { ChannelService } from '../channel/index'; -export enum ScanState { - COMPLETED, //Scanning work successfully completed - ERROR, //Error occurred when scanning - QUEUED, //Scanning job is queued - SCANNING, //Scanning in progress - PENDING, //Scanning not start - UNKNOWN //Unknown status -} +const STATE_CHECK_INTERVAL: number = 2000;//2s +const RETRY_TIMES: number = 3; @Component({ selector: 'hbr-vulnerability-bar', styles: [SCANNING_STYLES], template: BAR_CHART_COMPONENT_HTML }) -export class ResultBarChartComponent implements OnInit { +export class ResultBarChartComponent implements OnInit, OnDestroy { + @Input() repoName: string = ""; @Input() tagId: string = ""; - @Input() state: ScanState = ScanState.PENDING; @Input() summary: VulnerabilitySummary = { - scan_status: VULNERABILITY_SCAN_STATUS.unknown, + scan_status: VULNERABILITY_SCAN_STATUS.stopped, severity: VulnerabilitySeverity.UNKNOWN, update_time: new Date(), components: { @@ -37,63 +42,157 @@ export class ResultBarChartComponent implements OnInit { summary: [] } }; - @Output() startScanning: EventEmitter = new EventEmitter(); - scanningInProgress: boolean = false; + onSubmitting: boolean = false; + retryCounter: number = 0; + stateCheckTimer: Subscription; + timerHandler: any; - constructor() { } + constructor( + private tagService: TagService, + private scanningService: ScanningResultService, + private errorHandler: ErrorHandler, + private channel: ChannelService, + private ref: ChangeDetectorRef + ) { } ngOnInit(): void { - if (this.summary && this.summary.scan_status) { - switch (this.summary.scan_status) { - case VULNERABILITY_SCAN_STATUS.unknown: - this.state = ScanState.UNKNOWN; - break; - case VULNERABILITY_SCAN_STATUS.error: - this.state = ScanState.ERROR; - break; - case VULNERABILITY_SCAN_STATUS.pending: - this.state = ScanState.QUEUED; - break; - case VULNERABILITY_SCAN_STATUS.stopped: - this.state = ScanState.PENDING; - break; - case VULNERABILITY_SCAN_STATUS.finished: - this.state = ScanState.COMPLETED; - break; - default: - break; + this.channel.scanCommand$.subscribe((tagId: string) => { + if (this.tagId === tagId) { + this.scanNow(); } + }); + } + + ngOnDestroy(): void { + if (this.stateCheckTimer) { + this.stateCheckTimer.unsubscribe(); + this.stateCheckTimer = null; } } + //Get vulnerability scanning status + public get status(): string { + if (this.summary && this.summary.scan_status) { + return this.summary.scan_status; + } + + return VULNERABILITY_SCAN_STATUS.unknown; + } + public get completed(): boolean { - return this.state === ScanState.COMPLETED; + return this.status === VULNERABILITY_SCAN_STATUS.finished; } public get error(): boolean { - return this.state === ScanState.ERROR; + return this.status === VULNERABILITY_SCAN_STATUS.error; } public get queued(): boolean { - return this.state === ScanState.QUEUED; + return this.status === VULNERABILITY_SCAN_STATUS.pending; } public get scanning(): boolean { - return this.state === ScanState.SCANNING; + return this.status === VULNERABILITY_SCAN_STATUS.running; } - public get pending(): boolean { - return this.state === ScanState.PENDING; + public get stopped(): boolean { + return this.status === VULNERABILITY_SCAN_STATUS.stopped; } public get unknown(): boolean { - return this.state === ScanState.UNKNOWN; + return this.status === VULNERABILITY_SCAN_STATUS.unknown; } scanNow(): void { - if (this.tagId && this.tagId !== '') { - this.scanningInProgress = true; - this.startScanning.emit(this.tagId); + if (this.onSubmitting) { + //Avoid duplicated submitting + return; } + + if (!this.repoName || !this.tagId) { + return; + } + + this.onSubmitting = true; + + toPromise(this.scanningService.startVulnerabilityScanning(this.repoName, this.tagId)) + .then(() => { + this.onSubmitting = false; + + //Forcely change status to queued after successful submitting + this.summary.scan_status = VULNERABILITY_SCAN_STATUS.pending; + + //Forcely refresh view + this.forceRefreshView(1000); + + //Start check status util the job is done + if (!this.stateCheckTimer) { + //Avoid duplicated subscribing + this.stateCheckTimer = Observable.timer(STATE_CHECK_INTERVAL, STATE_CHECK_INTERVAL).subscribe(() => { + this.getSummary(); + }); + } + }) + .catch(error => { + this.onSubmitting = false; + this.errorHandler.error(error); + }); + } + + getSummary(): void { + if (!this.repoName || !this.tagId) { + return + } + + toPromise(this.tagService.getTag(this.repoName, this.tagId)) + .then((t: Tag) => { + //To keep the same summary reference, use value copy. + this.copyValue(t.scan_overview); + + //Forcely refresh view + this.forceRefreshView(1000); + + if (!this.queued && !this.scanning) { + //Scanning should be done + if (this.stateCheckTimer) { + this.stateCheckTimer.unsubscribe(); + this.stateCheckTimer = null; + } + } + }) + .catch(error => { + this.errorHandler.error(error); + this.retryCounter++; + if (this.retryCounter >= RETRY_TIMES) { + //Stop timer + if (this.stateCheckTimer) { + this.stateCheckTimer.unsubscribe(); + this.stateCheckTimer = null; + } + this.retryCounter = 0; + } + }); + } + + copyValue(newVal: VulnerabilitySummary): void { + if (!newVal || !newVal.scan_status) { return; } + this.summary.scan_status = newVal.scan_status; + this.summary.severity = newVal.severity; + this.summary.components = newVal.components; + this.summary.update_time = newVal.update_time; + } + + forceRefreshView(duration: number): void { + //Reset timer + if (this.timerHandler) { + clearInterval(this.timerHandler); + } + this.timerHandler = setInterval(() => this.ref.markForCheck(), 100); + setTimeout(() => { + if (this.timerHandler) { + clearInterval(this.timerHandler); + this.timerHandler = null; + } + }, duration); } } diff --git a/src/ui_ng/lib/src/vulnerability-scanning/scanning.html.ts b/src/ui_ng/lib/src/vulnerability-scanning/scanning.html.ts index d88d6bd8e..1f607279b 100644 --- a/src/ui_ng/lib/src/vulnerability-scanning/scanning.html.ts +++ b/src/ui_ng/lib/src/vulnerability-scanning/scanning.html.ts @@ -34,7 +34,7 @@ export const TIP_COMPONENT_HTML: string = `
{{'VULNERABILITY.CHART.SCANNING_TIME' | translate}} - {{completeTimestamp | date}} + {{completeTimestamp | date:'MM/dd/y HH:mm:ss'}}
@@ -89,11 +89,11 @@ export const GRID_COMPONENT_HTML: string = ` export const BAR_CHART_COMPONENT_HTML: string = `
-
- +
+ {{'VULNERABILITY.STATE.STOPPED' | translate}}
- {{'VULNERABILITY.STATE.QUEUED' | translate}} + {{'VULNERABILITY.STATE.QUEUED' | translate}}
@@ -101,9 +101,9 @@ export const BAR_CHART_COMPONENT_HTML: string = `
{{'VULNERABILITY.STATE.SCANNING' | translate}}
-
+
-
+
diff --git a/src/ui_ng/package.json b/src/ui_ng/package.json index eb295ad43..610b77522 100644 --- a/src/ui_ng/package.json +++ b/src/ui_ng/package.json @@ -31,7 +31,7 @@ "clarity-icons": "^0.9.8", "clarity-ui": "^0.9.8", "core-js": "^2.4.1", - "harbor-ui": "0.3.2", + "harbor-ui": "0.3.18", "intl": "^1.2.5", "mutationobserver-shim": "^0.3.2", "ngx-cookie": "^1.0.0", diff --git a/src/ui_ng/src/i18n/lang/en-us-lang.json b/src/ui_ng/src/i18n/lang/en-us-lang.json index 586ca4855..6745c5e56 100644 --- a/src/ui_ng/src/i18n/lang/en-us-lang.json +++ b/src/ui_ng/src/i18n/lang/en-us-lang.json @@ -462,7 +462,7 @@ }, "VULNERABILITY": { "STATE": { - "PENDING": "SCAN NOW", + "STOPPED": "Not Scanned", "QUEUED": "Queued", "ERROR": "Error", "SCANNING": "Scanning", @@ -495,7 +495,8 @@ "PLURAL": "Vulnerabilities", "PLACEHOLDER": "Filter Vulnerabilities", "PACKAGE": "Package with", - "PACKAGES": "Packages with" + "PACKAGES": "Packages with", + "SCAN_NOW": "Scan" }, "PUSH_IMAGE": { "TITLE": "Push Image", diff --git a/src/ui_ng/src/i18n/lang/es-es-lang.json b/src/ui_ng/src/i18n/lang/es-es-lang.json index 25f525f16..84e423c4b 100644 --- a/src/ui_ng/src/i18n/lang/es-es-lang.json +++ b/src/ui_ng/src/i18n/lang/es-es-lang.json @@ -461,7 +461,7 @@ }, "VULNERABILITY": { "STATE": { - "PENDING": "SCAN NOW", + "STOPPED": "Not Scanned", "QUEUED": "Queued", "ERROR": "Error", "SCANNING": "Scanning", @@ -494,7 +494,8 @@ "PLURAL": "Vulnerabilities", "PLACEHOLDER": "Filter Vulnerabilities", "PACKAGE": "Package with", - "PACKAGES": "Packages with" + "PACKAGES": "Packages with", + "SCAN_NOW": "Scan" }, "PUSH_IMAGE": { "TITLE": "Push Image", diff --git a/src/ui_ng/src/i18n/lang/zh-cn-lang.json b/src/ui_ng/src/i18n/lang/zh-cn-lang.json index 42fb598b7..e7a33e58b 100644 --- a/src/ui_ng/src/i18n/lang/zh-cn-lang.json +++ b/src/ui_ng/src/i18n/lang/zh-cn-lang.json @@ -466,7 +466,7 @@ }, "VULNERABILITY": { "STATE": { - "PENDING": "开始扫描", + "STOPPED": "未扫描", "QUEUED": "已入队列", "ERROR": "错误", "SCANNING": "扫描中", @@ -499,7 +499,8 @@ "PLURAL": "缺陷", "PLACEHOLDER": "过滤缺陷", "PACKAGE": "个组件有", - "PACKAGES": "个组件有" + "PACKAGES": "个组件有", + "SCAN_NOW": "扫描" }, "PUSH_IMAGE": { "TITLE": "推送镜像",