Merge pull request #10853 from AllForNothing/fix-scan

Fix scanning function
This commit is contained in:
Will Sun 2020-02-26 18:56:31 +08:00 committed by GitHub
commit 26f71d47b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 361 additions and 160 deletions

View File

@ -71,7 +71,7 @@
[(clrDgSelected)]="selectedRow"> [(clrDgSelected)]="selectedRow">
<clr-dg-action-bar> <clr-dg-action-bar>
<button [clrLoading]="scanBtnState" type="button" class="btn btn-secondary scan-btn" <button [clrLoading]="scanBtnState" type="button" class="btn btn-secondary scan-btn"
[disabled]="!(canScanNow() && selectedRow.length==1 && hasEnabledScanner && !depth)" (click)="scanNow()"> [disabled]="!(canScanNow() && selectedRow.length==1 && hasEnabledScanner && hasScanImagePermission && !depth)" (click)="scanNow()">
<clr-icon shape="shield-check" size="16"></clr-icon>&nbsp;{{'VULNERABILITY.SCAN_NOW' | translate}} <clr-icon shape="shield-check" size="16"></clr-icon>&nbsp;{{'VULNERABILITY.SCAN_NOW' | translate}}
</button> </button>
@ -225,7 +225,7 @@
<div class="cell"> <div class="cell">
<hbr-vulnerability-bar [scanner]="handleScanOverview(artifact.scan_overview)?.scanner" <hbr-vulnerability-bar [scanner]="handleScanOverview(artifact.scan_overview)?.scanner"
(submitFinish)="submitFinish($event)" [projectName]="projectName" [repoName]="repoName" (submitFinish)="submitFinish($event)" [projectName]="projectName" [repoName]="repoName"
[artifactId]="artifact.id" [summary]="handleScanOverview(artifact.scan_overview)"> [artifactDigest]="artifact.digest" [summary]="handleScanOverview(artifact.scan_overview)">
</hbr-vulnerability-bar> </hbr-vulnerability-bar>
</div> </div>
</clr-dg-cell> </clr-dg-cell>

View File

@ -9,7 +9,10 @@ export class AdditionsService {
constructor(private http: HttpClient) { constructor(private http: HttpClient) {
} }
getDetailByLink(link: string): Observable<any> { getDetailByLink(link: string, shouldReturnText?: boolean): Observable<any> {
return this.http.get(link); if (shouldReturnText) {
return this.http.get(link, { observe: 'body', responseType: 'text'} );
}
return this.http.get(link);
} }
} }

View File

@ -1,38 +1,42 @@
<ng-container *ngIf="additionLinks"> <ng-container *ngIf="additionLinks">
<h4 class="margin-bottom-025">{{'ARTIFACT.ADDITIONS' | translate}}</h4> <h4 class="margin-bottom-025">{{'ARTIFACT.ADDITIONS' | translate}}</h4>
<clr-tabs> <div class="min-15">
<clr-tab *ngIf="getVulnerability()"> <clr-tabs>
<button clrTabLink id="vulnerability">{{'REPOSITORY.VULNERABILITY' | translate}}</button> <clr-tab *ngIf="getVulnerability()">
<clr-tab-content id="vulnerability-content" *clrIfActive> <button clrTabLink id="vulnerability">{{'REPOSITORY.VULNERABILITY' | translate}}</button>
<hbr-artifact-vulnerabilities *ngIf="getBuildHistory()" <clr-tab-content id="vulnerability-content" *clrIfActive>
[vulnerabilitiesLink]="getVulnerability()"></hbr-artifact-vulnerabilities> <hbr-artifact-vulnerabilities [projectName]="projectName"
</clr-tab-content> [projectId]="projectId"
</clr-tab> [repoName]="repoName"
<clr-tab *ngIf="getBuildHistory()"> [digest]="digest" [vulnerabilitiesLink]="getVulnerability()"></hbr-artifact-vulnerabilities>
<button clrTabLink id="build-history">{{ 'REPOSITORY.BUILD_HISTORY' | translate }}</button> </clr-tab-content>
<clr-tab-content *clrIfActive> </clr-tab>
<hbr-artifact-build-history [buildHistoryLink]="getBuildHistory()"></hbr-artifact-build-history> <clr-tab *ngIf="getBuildHistory()">
</clr-tab-content> <button clrTabLink id="build-history">{{ 'REPOSITORY.BUILD_HISTORY' | translate }}</button>
</clr-tab> <clr-tab-content *clrIfActive>
<clr-tab *ngIf="getSummary()"> <hbr-artifact-build-history [buildHistoryLink]="getBuildHistory()"></hbr-artifact-build-history>
<button clrTabLink id="summary-link">{{'HELM_CHART.SUMMARY' | translate}}</button> </clr-tab-content>
<clr-tab-content id="summary-content" *clrIfActive> </clr-tab>
<hbr-artifact-summary [summaryLink]="getSummary()"></hbr-artifact-summary> <clr-tab *ngIf="getSummary()">
</clr-tab-content> <button clrTabLink id="summary-link">{{'HELM_CHART.SUMMARY' | translate}}</button>
</clr-tab> <clr-tab-content id="summary-content" *clrIfActive>
<clr-tab *ngIf="getDependencies()"> <hbr-artifact-summary [summaryLink]="getSummary()"></hbr-artifact-summary>
<button clrTabLink id="depend-link">{{'HELM_CHART.DEPENDENCIES' | translate}}</button> </clr-tab-content>
<clr-tab-content id="depend-content" *clrIfActive> </clr-tab>
<hbr-artifact-dependencies [dependenciesLink]="getDependencies()"></hbr-artifact-dependencies> <clr-tab *ngIf="getDependencies()">
</clr-tab-content> <button clrTabLink id="depend-link">{{'HELM_CHART.DEPENDENCIES' | translate}}</button>
</clr-tab> <clr-tab-content id="depend-content" *clrIfActive>
<clr-tab *ngIf="getValues()"> <hbr-artifact-dependencies [dependenciesLink]="getDependencies()"></hbr-artifact-dependencies>
<button clrTabLink id="value-link">{{'HELM_CHART.VALUES' | translate}}</button> </clr-tab-content>
<clr-tab-content id="value-content" *clrIfActive> </clr-tab>
<hbr-artifact-values [valuesLink]="getValues()"></hbr-artifact-values> <clr-tab *ngIf="getValues()">
</clr-tab-content> <button clrTabLink id="value-link">{{'HELM_CHART.VALUES' | translate}}</button>
</clr-tab> <clr-tab-content id="value-content" *clrIfActive>
</clr-tabs> <hbr-artifact-values [valuesLink]="getValues()"></hbr-artifact-values>
</clr-tab-content>
</clr-tab>
</clr-tabs>
</div>
</ng-container> </ng-container>

View File

@ -1,3 +1,6 @@
.margin-bottom-025 { .margin-bottom-025 {
margin-bottom: 0.25rem; margin-bottom: 0.25rem;
} }
.min-15 {
min-height: 15rem;
}

View File

@ -10,6 +10,13 @@ import { AdditionLink } from "../../../../../../ng-swagger-gen/models/addition-l
}) })
export class ArtifactAdditionsComponent implements OnInit { export class ArtifactAdditionsComponent implements OnInit {
@Input() additionLinks: AdditionLinks; @Input() additionLinks: AdditionLinks;
@Input() projectName: string;
@Input()
projectId: number;
@Input()
repoName: string;
@Input()
digest: string;
constructor() { } constructor() { }
ngOnInit() { ngOnInit() {

View File

@ -2,7 +2,6 @@
<div> <div>
<div class="row flex-items-xs-right rightPos"> <div class="row flex-items-xs-right rightPos">
<div class="flex-xs-middle option-right"> <div class="flex-xs-middle option-right">
<hbr-filter [withDivider]="true" filterPlaceholder="{{'VULNERABILITY.PLACEHOLDER' | translate}}"></hbr-filter>
<span class="refresh-btn" (click)="refresh()"><clr-icon shape="refresh"></clr-icon></span> <span class="refresh-btn" (click)="refresh()"><clr-icon shape="refresh"></clr-icon></span>
</div> </div>
</div> </div>
@ -10,7 +9,19 @@
<div class="col-lg-12 col-md-12 col-sm-12 col-xs-12"> <div class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<clr-datagrid [clrDgLoading]="loading"> <clr-datagrid [clrDgLoading]="loading">
<clr-dg-action-bar> <clr-dg-action-bar>
<button type="button" class="btn btn-secondary" [clrLoading]="scanBtnState" [disabled]="!hasEnabledScanner"><clr-icon shape="shield-check" size="16"></clr-icon>&nbsp;{{'VULNERABILITY.SCAN_NOW' | translate}}</button> <div class="clr-row center">
<div class="clr-col-1">
<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 class="clr-col">
<div [hidden]="!shouldShowBar()">
<hbr-vulnerability-bar [scanner]="scanner"
(submitFinish)="submitFinish($event)" [projectName]="projectName" [repoName]="repoName"
[artifactDigest]="digest">
</hbr-vulnerability-bar>
</div>
</div>
</div>
</clr-dg-action-bar> </clr-dg-action-bar>
<clr-dg-column [clrDgField]="'id'">{{'VULNERABILITY.GRID.COLUMN_ID' | translate}}</clr-dg-column> <clr-dg-column [clrDgField]="'id'">{{'VULNERABILITY.GRID.COLUMN_ID' | translate}}</clr-dg-column>
<clr-dg-column [clrDgSortBy]="severitySort">{{'VULNERABILITY.GRID.COLUMN_SEVERITY' | translate}}</clr-dg-column> <clr-dg-column [clrDgSortBy]="severitySort">{{'VULNERABILITY.GRID.COLUMN_SEVERITY' | translate}}</clr-dg-column>

View File

@ -11,4 +11,38 @@
.option-right { .option-right {
padding-right: 16px; padding-right: 16px;
margin-top: 5px;
}
.center {
align-items: center;
}
.label-critical {
background:red;
color:#621501;
}
.label-danger {
background:#e64524!important;
color:#621501!important;
}
.label-medium {
background-color: orange;
color:#621501;
}
.label-low {
background: #007CBB;
color:#cab6b1;
}
.label-negligible {
background-color: green;
color:#bad7ba;
}
.label-unknown {
background-color: grey;
color:#bad7ba;
}
.no-border {
border: none;
} }

View File

@ -1,5 +1,4 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ArtifactVulnerabilitiesComponent } from './artifact-vulnerabilities.component'; import { ArtifactVulnerabilitiesComponent } from './artifact-vulnerabilities.component';
import { NO_ERRORS_SCHEMA } from "@angular/core"; import { NO_ERRORS_SCHEMA } from "@angular/core";
import { ClarityModule } from "@clr/angular"; import { ClarityModule } from "@clr/angular";
@ -7,9 +6,11 @@ import { AdditionsService } from "../additions.service";
import { of } from "rxjs"; import { of } from "rxjs";
import { TranslateFakeLoader, TranslateLoader, TranslateModule } from "@ngx-translate/core"; import { TranslateFakeLoader, TranslateLoader, TranslateModule } from "@ngx-translate/core";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { VulnerabilityItem } from "../../../../../../lib/services"; import { ScanningResultService, UserPermissionService, VulnerabilityItem } from "../../../../../../lib/services";
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link"; import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
import { ErrorHandler } from "../../../../../../lib/utils/error-handler"; import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
import { ChannelService } from "../../../../../../lib/services/channel.service";
import { DEFAULT_SUPPORTED_MIME_TYPE } from "../../../../../../lib/utils/utils";
describe('ArtifactVulnerabilitiesComponent', () => { describe('ArtifactVulnerabilitiesComponent', () => {
@ -35,16 +36,35 @@ describe('ArtifactVulnerabilitiesComponent', () => {
description: 'just a test' description: 'just a test'
}, },
]; ];
let scanOverview = {};
scanOverview[DEFAULT_SUPPORTED_MIME_TYPE] = {};
scanOverview[DEFAULT_SUPPORTED_MIME_TYPE].vulnerabilities = mockedVulnerabilities;
const mockedLink: AdditionLink = { const mockedLink: AdditionLink = {
absolute: false, absolute: false,
href: '/test' href: '/test'
}; };
const fakedAdditionsService = { const fakedAdditionsService = {
getDetailByLink() { getDetailByLink() {
return of(mockedVulnerabilities); return of(scanOverview);
}
};
const fakedUserPermissionService = {
hasProjectPermissions() {
return of(true);
}
};
const fakedScanningResultService = {
getProjectScanner() {
return of(true);
}
};
const fakedChannelService = {
ArtifactDetail$: {
subscribe() {
return null;
}
} }
}; };
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ imports: [
@ -60,7 +80,10 @@ describe('ArtifactVulnerabilitiesComponent', () => {
declarations: [ArtifactVulnerabilitiesComponent], declarations: [ArtifactVulnerabilitiesComponent],
providers: [ providers: [
ErrorHandler, ErrorHandler,
{provide: AdditionsService, useValue: fakedAdditionsService} {provide: AdditionsService, useValue: fakedAdditionsService},
{provide: UserPermissionService, useValue: fakedUserPermissionService},
{provide: ScanningResultService, useValue: fakedScanningResultService},
{provide: ChannelService, useValue: fakedChannelService},
], ],
schemas: [ schemas: [
NO_ERRORS_SCHEMA NO_ERRORS_SCHEMA
@ -72,6 +95,10 @@ describe('ArtifactVulnerabilitiesComponent', () => {
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(ArtifactVulnerabilitiesComponent); fixture = TestBed.createComponent(ArtifactVulnerabilitiesComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
component.hasScanningPermission = true;
component.hasEnabledScanner = true;
component.vulnerabilitiesLink = mockedLink;
component.ngOnInit();
fixture.detectChanges(); fixture.detectChanges();
}); });
@ -79,8 +106,6 @@ describe('ArtifactVulnerabilitiesComponent', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
}); });
it('should get vulnerability list and render', async () => { it('should get vulnerability list and render', async () => {
component.vulnerabilitiesLink = mockedLink;
component.ngOnInit();
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable(); await fixture.whenStable();
const rows = fixture.nativeElement.getElementsByTagName('clr-dg-row'); const rows = fixture.nativeElement.getElementsByTagName('clr-dg-row');

View File

@ -1,11 +1,23 @@
import { Component, Input, OnInit } from '@angular/core'; import { Component, Input, OnInit, ViewChild } from '@angular/core';
import { AdditionsService } from "../additions.service"; import { AdditionsService } from "../additions.service";
import { ClrDatagridComparatorInterface, ClrLoadingState } from "@clr/angular"; import { ClrDatagridComparatorInterface, ClrLoadingState } from "@clr/angular";
import { finalize } from "rxjs/operators"; import { finalize } from "rxjs/operators";
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link"; import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
import { VulnerabilityItem } from "../../../../../../lib/services"; import {
ScannerVo,
ScanningResultService,
UserPermissionService,
USERSTATICPERMISSION,
VulnerabilityItem
} from "../../../../../../lib/services";
import { ErrorHandler } from "../../../../../../lib/utils/error-handler"; import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
import { SEVERITY_LEVEL_MAP, VULNERABILITY_SEVERITY } from "../../../../../../lib/utils/utils"; import {
DEFAULT_SUPPORTED_MIME_TYPE,
SEVERITY_LEVEL_MAP,
VULNERABILITY_SEVERITY
} from "../../../../../../lib/utils/utils";
import { ChannelService } from "../../../../../../lib/services/channel.service";
import { ResultBarChartComponent } from "../../../vulnerability-scanning/result-bar-chart.component";
@Component({ @Component({
selector: 'hbr-artifact-vulnerabilities', selector: 'hbr-artifact-vulnerabilities',
@ -15,17 +27,33 @@ import { SEVERITY_LEVEL_MAP, VULNERABILITY_SEVERITY } from "../../../../../../li
export class ArtifactVulnerabilitiesComponent implements OnInit { export class ArtifactVulnerabilitiesComponent implements OnInit {
@Input() @Input()
vulnerabilitiesLink: AdditionLink; vulnerabilitiesLink: AdditionLink;
@Input()
projectName: string;
@Input()
projectId: number;
@Input()
repoName: string;
@Input()
digest: string;
scan_overview: any;
scanner: ScannerVo;
scanningResults: VulnerabilityItem[] = []; scanningResults: VulnerabilityItem[] = [];
loading: boolean = false; loading: boolean = false;
shouldShowLoading: boolean = true;
hasEnabledScanner: boolean = false; hasEnabledScanner: boolean = false;
scanBtnState: ClrLoadingState = ClrLoadingState.DEFAULT; scanBtnState: ClrLoadingState = ClrLoadingState.DEFAULT;
severitySort: ClrDatagridComparatorInterface<VulnerabilityItem>; severitySort: ClrDatagridComparatorInterface<VulnerabilityItem>;
hasScanningPermission: boolean = false;
onSendingScanCommand: boolean = false;
hasShowLoading: boolean = false;
@ViewChild(ResultBarChartComponent, {static: false})
resultBarChartComponent: ResultBarChartComponent;
constructor( constructor(
private errorHandler: ErrorHandler, private errorHandler: ErrorHandler,
private additionsService: AdditionsService, private additionsService: AdditionsService,
private userPermissionService: UserPermissionService,
private scanningService: ScanningResultService,
private channel: ChannelService,
) { ) {
const that = this; const that = this;
this.severitySort = { this.severitySort = {
@ -37,29 +65,61 @@ export class ArtifactVulnerabilitiesComponent implements OnInit {
ngOnInit() { ngOnInit() {
this.getVulnerabilities(); this.getVulnerabilities();
this.getScanningPermission();
this.getProjectScanner();
this.channel.ArtifactDetail$.subscribe(tag => {
this.getVulnerabilities();
});
} }
getVulnerabilities() { getVulnerabilities() {
if (this.vulnerabilitiesLink if (this.vulnerabilitiesLink
&& !this.vulnerabilitiesLink.absolute && !this.vulnerabilitiesLink.absolute
&& this.vulnerabilitiesLink.href) { && this.vulnerabilitiesLink.href) {
// only show loading for one time if (!this.hasShowLoading) {
if (this.shouldShowLoading) {
this.loading = true; this.loading = true;
this.shouldShowLoading = false; this.hasShowLoading = true;
} }
this.additionsService.getDetailByLink(this.vulnerabilitiesLink.href) this.additionsService.getDetailByLink(this.vulnerabilitiesLink.href)
.pipe(finalize(() => this.loading = false)) .pipe(finalize(() => this.loading = false))
.subscribe( .subscribe(
res => { res => {
this.scanningResults = res; this.scan_overview = res;
if (this.scan_overview && this.scan_overview[DEFAULT_SUPPORTED_MIME_TYPE]) {
this.scanningResults = this.scan_overview[DEFAULT_SUPPORTED_MIME_TYPE].vulnerabilities;
this.scanner = this.scan_overview[DEFAULT_SUPPORTED_MIME_TYPE].scanner;
}
}, error => { }, error => {
this.errorHandler.error(error); this.errorHandler.error(error);
} }
); );
} }
} }
getScanningPermission(): void {
const permissions = [
{ resource: USERSTATICPERMISSION.REPOSITORY_TAG_SCAN_JOB.KEY, action: USERSTATICPERMISSION.REPOSITORY_TAG_SCAN_JOB.VALUE.CREATE },
];
this.userPermissionService.hasProjectPermissions(this.projectId, permissions).subscribe((results: Array<boolean>) => {
this.hasScanningPermission = results[0];
// only has label permission
}, error => this.errorHandler.error(error));
}
getProjectScanner(): void {
this.hasEnabledScanner = false;
this.scanBtnState = ClrLoadingState.LOADING;
this.scanningService.getProjectScanner(this.projectId)
.subscribe(response => {
if (response && "{}" !== JSON.stringify(response) && !response.disabled
&& response.health === "healthy") {
this.scanBtnState = ClrLoadingState.SUCCESS;
this.hasEnabledScanner = true;
} else {
this.scanBtnState = ClrLoadingState.ERROR;
}
}, error => {
this.scanBtnState = ClrLoadingState.ERROR;
});
}
getLevel(v: VulnerabilityItem): number { getLevel(v: VulnerabilityItem): number {
if (v && v.severity && SEVERITY_LEVEL_MAP[v.severity]) { if (v && v.severity && SEVERITY_LEVEL_MAP[v.severity]) {
return SEVERITY_LEVEL_MAP[v.severity]; return SEVERITY_LEVEL_MAP[v.severity];
@ -88,4 +148,15 @@ export class ArtifactVulnerabilitiesComponent implements OnInit {
return 'UNKNOWN'; return 'UNKNOWN';
} }
} }
scanNow() {
this.onSendingScanCommand = true;
this.channel.publishScanEvent(this.repoName + "/" + this.digest);
}
submitFinish(e: boolean) {
this.onSendingScanCommand = e;
}
shouldShowBar(): boolean {
return this.resultBarChartComponent
&& (this.resultBarChartComponent.queued || this.resultBarChartComponent.scanning || this.resultBarChartComponent.error);
}
} }

View File

@ -1,20 +1,10 @@
<div class="row flex-items-xs-center dep-container"> <clr-datagrid [clrDgLoading]="loading">
<div class="col-md-12"> <clr-dg-column>{{'HELM_CHART.NAME' | translate}}</clr-dg-column>
<table class="table"> <clr-dg-column>{{'HELM_CHART.VERSION' | translate}}</clr-dg-column>
<thead> <clr-dg-column>{{'HELM_CHART.REPO' | translate}}</clr-dg-column>
<tr> <clr-dg-row *clrDgItems="let dep of dependencyList" [clrDgItem]='dep' class="history-item">
<th class="left">{{'HELM_CHART.NAME' | translate}}</th> <clr-dg-cell class="left">{{dep.name}}</clr-dg-cell>
<th class="left">{{'HELM_CHART.VERSION' | translate}}</th> <clr-dg-cell class="left">{{dep.version}}</clr-dg-cell>
<th class="left">{{'HELM_CHART.REPO' | translate}}</th> <clr-dg-cell class="left"><a href="{{dep.repository}}">{{dep.repository}}</a></clr-dg-cell>
</tr> </clr-dg-row>
</thead> </clr-datagrid>
<tbody>
<tr *ngFor="let dep of dependencyList">
<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

@ -7,6 +7,7 @@ import { ArtifactDependency } from "../models";
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link"; import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
import { IServiceConfig, SERVICE_CONFIG } from "../../../../../../lib/entities/service.config"; import { IServiceConfig, SERVICE_CONFIG } from "../../../../../../lib/entities/service.config";
import { ErrorHandler } from "../../../../../../lib/utils/error-handler"; import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
import { ClarityModule } from "@clr/angular";
import { CURRENT_BASE_HREF } from "../../../../../../lib/utils/utils"; import { CURRENT_BASE_HREF } from "../../../../../../lib/utils/utils";
@ -41,7 +42,8 @@ describe('DependenciesComponent', () => {
beforeEach(async(() => { beforeEach(async(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [ imports: [
TranslateModule.forRoot() TranslateModule.forRoot(),
ClarityModule
], ],
declarations: [DependenciesComponent], declarations: [DependenciesComponent],
providers: [ providers: [
@ -70,7 +72,7 @@ describe('DependenciesComponent', () => {
component.ngOnInit(); component.ngOnInit();
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable(); await fixture.whenStable();
const trs = fixture.nativeElement.getElementsByTagName('tr'); const rows = fixture.nativeElement.getElementsByTagName('clr-dg-row');
expect(trs.length).toEqual(3); expect(rows.length).toEqual(2);
}); });
}); });

View File

@ -7,6 +7,8 @@ import { ArtifactDependency } from "../models";
import { AdditionsService } from "../additions.service"; import { AdditionsService } from "../additions.service";
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link"; import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
import { ErrorHandler } from "../../../../../../lib/utils/error-handler"; import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
import { pipe } from "rxjs";
import { finalize } from "rxjs/operators";
@Component({ @Component({
@ -18,6 +20,7 @@ export class DependenciesComponent implements OnInit {
@Input() @Input()
dependenciesLink: AdditionLink; dependenciesLink: AdditionLink;
dependencyList: ArtifactDependency[] = []; dependencyList: ArtifactDependency[] = [];
loading: boolean = false;
constructor( private errorHandler: ErrorHandler, constructor( private errorHandler: ErrorHandler,
private additionsService: AdditionsService) {} private additionsService: AdditionsService) {}
@ -28,7 +31,10 @@ export class DependenciesComponent implements OnInit {
if (this.dependenciesLink if (this.dependenciesLink
&& !this.dependenciesLink.absolute && !this.dependenciesLink.absolute
&& this.dependenciesLink.href) { && this.dependenciesLink.href) {
this.additionsService.getDetailByLink(this.dependenciesLink.href).subscribe( this.loading = true;
this.additionsService.getDetailByLink(this.dependenciesLink.href)
.pipe(finalize(() => this.loading = false))
.subscribe(
res => { res => {
this.dependencyList = res; this.dependencyList = res;
}, error => { }, error => {

View File

@ -34,7 +34,7 @@ export interface Addition {
export enum ADDITIONS { export enum ADDITIONS {
VULNERABILITIES = 'vulnerabilities', VULNERABILITIES = 'vulnerabilities',
BUILD_HISTORY = 'build_history', BUILD_HISTORY = 'build_history',
SUMMARY = 'readme', SUMMARY = 'readme.md',
VALUES = 'values.yaml', VALUES = 'values.yaml',
DEPENDENCIES = 'dependencies' DEPENDENCIES = 'dependencies'
} }

View File

@ -1,7 +1,9 @@
<div class="row content-wrapper"> <div class="row content-wrapper" *ngIf="!loading">
<div class="col-md-8 md-container pl-1"> <div class="col-md-8 md-container pl-1">
<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> </div>
<table class="table"></table> <div *ngIf="loading" class="clr-row mt-3 center">
<span class="spinner spinner-md"></span>
</div>

View File

@ -5,3 +5,7 @@
border: solid 1px #ddd; border: solid 1px #ddd;
} }
} }
.center {
justify-content: center;
align-items: center;
}

View File

@ -195,6 +195,6 @@ describe('SummaryComponent', () => {
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable(); await fixture.whenStable();
const tables = fixture.nativeElement.getElementsByTagName('table'); const tables = fixture.nativeElement.getElementsByTagName('table');
expect(tables.length).toEqual(2); expect(tables.length).toEqual(1);
}); });
}); });

View File

@ -6,6 +6,7 @@ import {
import { AdditionsService } from "../additions.service"; import { AdditionsService } from "../additions.service";
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link"; import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
import { ErrorHandler } from "../../../../../../lib/utils/error-handler"; import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
import { finalize } from "rxjs/operators";
@Component({ @Component({
@ -16,6 +17,7 @@ import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
export class SummaryComponent implements OnInit { export class SummaryComponent implements OnInit {
@Input() summaryLink: AdditionLink; @Input() summaryLink: AdditionLink;
readme: string; readme: string;
loading: boolean = false;
constructor( constructor(
private errorHandler: ErrorHandler, private errorHandler: ErrorHandler,
private additionsService: AdditionsService private additionsService: AdditionsService
@ -28,7 +30,10 @@ export class SummaryComponent implements OnInit {
if (this.summaryLink if (this.summaryLink
&& !this.summaryLink.absolute && !this.summaryLink.absolute
&& this.summaryLink.href) { && this.summaryLink.href) {
this.additionsService.getDetailByLink(this.summaryLink.href).subscribe( this.loading = true;
this.additionsService.getDetailByLink(this.summaryLink.href, true)
.pipe(finalize(() => this.loading = false))
.subscribe(
res => { res => {
this.readme = res; this.readme = res;
}, error => { }, error => {

View File

@ -2,22 +2,34 @@
<div *ngIf="valueMode" class="title-container"> <div *ngIf="valueMode" class="title-container">
<label>{{'HELM_CHART.SHOW_KV' | translate }}</label> <label>{{'HELM_CHART.SHOW_KV' | translate }}</label>
</div> </div>
<div *ngIf="!valueMode" class="title-container">
<label>{{'HELM_CHART.SHOW_YAML' | translate }}</label>
</div>
<div class="switch-container"> <div class="switch-container">
<span class="card-btn" (click)="showYamlFile(false)" (mouseenter)="mouseEnter('value') " (mouseleave)="mouseLeave('value')"> <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> <clr-icon size="24" shape="view-list" title='list values' [ngClass]="{'is-highlight': isValueMode || isHovering('value') }"></clr-icon>
</span> </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> </div>
<div class="row value-container"> <div class="row value-container" *ngIf="!loading">
<div class="col-xs-8" *ngIf="valueMode"> <div class="col-xs-8" *ngIf="valueMode">
<table class="table"> <table class="table">
<tbody> <tbody>
<tr *ngFor="let item of values | keyvalue"> <tr *ngFor="let item of valuesObj | keyvalue">
<td class="left">{{item?.key}}</td> <td class="left">{{item?.key}}</td>
<td class="left">{{item?.value}}</td> <td class="left">{{item?.value}}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="col-xs-8" *ngIf="!valueMode">
<div class="yaml-container" [innerHTML]="values | language : 'yaml' | markdown"></div>
</div>
</div>
<div *ngIf="loading" class="clr-row mt-1 center">
<span class="spinner spinner-md"></span>
</div> </div>

View File

@ -1,15 +1,16 @@
.value-container { .value-container {
::ng-deep pre { ::ng-deep pre {
min-height: fit-content; min-height: fit-content;
max-height: none !important;
} }
} }
.values-header { .values-header {
margin-top: 12px; margin-top: 12px;
} }
pre { pre {
max-height: max-content;
padding-left: 21px; padding-left: 21px;
} }
.center {
justify-content: center;
align-items: center;
}

View File

@ -16,11 +16,11 @@ describe('ValuesComponent', () => {
let component: ValuesComponent; let component: ValuesComponent;
let fixture: ComponentFixture<ValuesComponent>; let fixture: ComponentFixture<ValuesComponent>;
const mockedValues = { const mockedValues = `
"adminserver.image.pullPolicy": "IfNotPresent", adminserver.image.pullPolicy: IfNotPresent,
"adminserver.image.repository": "vmware/harbor-adminserver", adminserver.image.repository: vmware/harbor-adminserver,
"adminserver.image.tag": "dev" adminserver.image.tag: dev
}; `;
const fakedAdditionsService = { const fakedAdditionsService = {
getDetailByLink() { getDetailByLink() {
return of(mockedValues); return of(mockedValues);
@ -56,15 +56,16 @@ describe('ValuesComponent', () => {
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(ValuesComponent); fixture = TestBed.createComponent(ValuesComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
component.valueMode = true;
component.valuesLink = mockedLink;
component.ngOnInit();
fixture.detectChanges(); fixture.detectChanges();
}); });
/*it('should create', () => { it('should create', () => {
expect(component).toBeTruthy(); expect(component).toBeTruthy();
});*/ });
it('should get values and render', async () => { it('should get values and render', async () => {
component.valuesLink = mockedLink;
component.ngOnInit();
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable(); await fixture.whenStable();
const trs = fixture.nativeElement.getElementsByTagName('tr'); const trs = fixture.nativeElement.getElementsByTagName('tr');

View File

@ -6,7 +6,8 @@ import {
import { AdditionsService } from "../additions.service"; import { AdditionsService } from "../additions.service";
import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link"; import { AdditionLink } from "../../../../../../../ng-swagger-gen/models/addition-link";
import { ErrorHandler } from "../../../../../../lib/utils/error-handler"; import { ErrorHandler } from "../../../../../../lib/utils/error-handler";
import * as yaml from "js-yaml";
import { finalize } from "rxjs/operators";
@Component({ @Component({
selector: "hbr-artifact-values", selector: "hbr-artifact-values",
@ -17,22 +18,31 @@ export class ValuesComponent implements OnInit {
@Input() @Input()
valuesLink: AdditionLink; valuesLink: AdditionLink;
values: any; values: string;
valuesObj: object = {};
// Default set to yaml file // Default set to yaml file
valueMode = true; valueMode = false;
valueHover = false; valueHover = false;
yamlHover = true; yamlHover = true;
loading: boolean = false;
constructor(private errorHandler: ErrorHandler, constructor(private errorHandler: ErrorHandler,
private additionsService: AdditionsService) { private additionsService: AdditionsService) {
} }
ngOnInit(): void { ngOnInit(): void {
if (this.valuesLink && !this.valuesLink.absolute && this.valuesLink.href) { if (this.valuesLink && !this.valuesLink.absolute && this.valuesLink.href) {
this.additionsService.getDetailByLink(this.valuesLink.href).subscribe( this.loading = true;
this.additionsService.getDetailByLink(this.valuesLink.href, true)
.pipe(finalize(() => this.loading = false))
.subscribe(
res => { res => {
this.values = res; try {
this.format(yaml.safeLoad(res));
this.values = res;
} catch (e) {
this.errorHandler.error(e);
}
}, error => { }, error => {
this.errorHandler.error(error); this.errorHandler.error(error);
} }
@ -71,4 +81,21 @@ export class ValuesComponent implements OnInit {
this.yamlHover = false; this.yamlHover = false;
} }
} }
format(obj: object) {
for (let name in obj) {
if (obj.hasOwnProperty(name)) {
if (obj[name] instanceof Object) {
for (let key in obj[name]) {
if (obj[name].hasOwnProperty(key)) {
obj[`${name}.${key}`] = obj[name][key];
}
}
delete obj[name];
this.format(obj);
} else {
this.valuesObj[name] = obj[name];
}
}
}
}
} }

View File

@ -26,7 +26,12 @@
(refreshArtifact)="refreshArtifact()"></artifact-tag> (refreshArtifact)="refreshArtifact()"></artifact-tag>
<!-- Additions --> <!-- Additions -->
<artifact-additions [additionLinks]="artifact?.addition_links"></artifact-additions> <artifact-additions
[projectName]="projectName"
[projectId]="projectId"
[repoName]="repositoryName"
[digest]="artifactDigest"
[additionLinks]="artifact?.addition_links"></artifact-additions>
</ng-container> </ng-container>
<div *ngIf="loading" class="clr-row mt-3 center"> <div *ngIf="loading" class="clr-row mt-3 center">
<span class="spinner spinner-md"></span> <span class="spinner spinner-md"></span>

View File

@ -22,7 +22,7 @@
</clr-control-error> </clr-control-error>
</label> </label>
</label> </label>
<label> <label class="ml-1">
<button type="button" class="btn btn-sm btn-outline" (click)="cancelAddTag()">{{ <button type="button" class="btn btn-sm btn-outline" (click)="cancelAddTag()">{{
'BUTTON.CANCEL' | translate }} 'BUTTON.CANCEL' | translate }}
</button> </button>

View File

@ -119,7 +119,7 @@ export class RepositoryGridviewComponent implements OnChanges, OnInit, OnDestroy
return this.systemInfo && this.systemInfo.has_ca_root; return this.systemInfo && this.systemInfo.has_ca_root;
} }
goIntoRepo(repoEvt: RepositoryItem): void { goIntoRepo(repoEvt: NewRepository): void {
let linkUrl = ['harbor', 'projects', repoEvt.project_id, 'repositories', repoEvt.name.split('/')[1]]; let linkUrl = ['harbor', 'projects', repoEvt.project_id, 'repositories', repoEvt.name.split('/')[1]];
this.router.navigate(linkUrl); this.router.navigate(linkUrl);
} }

View File

@ -9,13 +9,13 @@ import {
JobLogService, JobLogService,
ScanningResultDefaultService, ScanningResultDefaultService,
ScanningResultService, ScanningResultService,
VulnerabilitySummary
} from "../../../../lib/services"; } from "../../../../lib/services";
import { CURRENT_BASE_HREF, VULNERABILITY_SCAN_STATUS } from "../../../../lib/utils/utils"; import { CURRENT_BASE_HREF, VULNERABILITY_SCAN_STATUS } from "../../../../lib/utils/utils";
import { SharedModule } from "../../../../lib/utils/shared/shared.module"; import { SharedModule } from "../../../../lib/utils/shared/shared.module";
import { ErrorHandler } from "../../../../lib/utils/error-handler"; import { ErrorHandler } from "../../../../lib/utils/error-handler";
import { ChannelService } from "../../../../lib/services/channel.service"; import { ChannelService } from "../../../../lib/services/channel.service";
import { ArtifactDefaultService, ArtifactService } from "../artifact/artifact.service"; import { ArtifactDefaultService, ArtifactService } from "../artifact/artifact.service";
import { NativeReportSummary } from "../../../../../ng-swagger-gen/models/native-report-summary";
describe('ResultBarChartComponent (inline template)', () => { describe('ResultBarChartComponent (inline template)', () => {
let component: ResultBarChartComponent; let component: ResultBarChartComponent;
@ -24,10 +24,10 @@ describe('ResultBarChartComponent (inline template)', () => {
let testConfig: IServiceConfig = { let testConfig: IServiceConfig = {
vulnerabilityScanningBaseEndpoint: CURRENT_BASE_HREF + "/vulnerability/testing" vulnerabilityScanningBaseEndpoint: CURRENT_BASE_HREF + "/vulnerability/testing"
}; };
let mockData: VulnerabilitySummary = { let mockData: NativeReportSummary = {
scan_status: VULNERABILITY_SCAN_STATUS.SUCCESS, scan_status: VULNERABILITY_SCAN_STATUS.SUCCESS,
severity: "High", severity: "High",
end_time: new Date(), end_time: new Date().toUTCString(),
summary: { summary: {
total: 124, total: 124,
fixable: 50, fixable: 50,
@ -64,7 +64,7 @@ describe('ResultBarChartComponent (inline template)', () => {
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(ResultBarChartComponent); fixture = TestBed.createComponent(ResultBarChartComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
component.artifactId = "mockTag"; component.artifactDigest = "mockTag";
component.summary = mockData; component.summary = mockData;
serviceConfig = TestBed.get(SERVICE_CONFIG); serviceConfig = TestBed.get(SERVICE_CONFIG);

View File

@ -7,13 +7,19 @@ import {
} from '@angular/core'; } from '@angular/core';
import { Subscription , timer} from "rxjs"; import { Subscription , timer} from "rxjs";
import { finalize } from "rxjs/operators"; import { finalize } from "rxjs/operators";
import { ScannerVo, ScanningResultService, VulnerabilitySummary } from "../../../../lib/services"; import { ScannerVo, ScanningResultService } from "../../../../lib/services";
import { ArtifactDefaultService } from "../artifact/artifact.service";
import { ErrorHandler } from "../../../../lib/utils/error-handler"; import { ErrorHandler } from "../../../../lib/utils/error-handler";
import { ChannelService } from "../../../../lib/services/channel.service"; import { ChannelService } from "../../../../lib/services/channel.service";
import { clone, CURRENT_BASE_HREF, DEFAULT_SUPPORTED_MIME_TYPE, VULNERABILITY_SCAN_STATUS } from "../../../../lib/utils/utils"; import {
import { ArtifactFront as Artifact } from "../artifact/artifact"; clone,
import { NativeReportSummary } from '../../../../../ng-swagger-gen/models/native-report-summary'; CURRENT_BASE_HREF,
DEFAULT_SUPPORTED_MIME_TYPE,
VULNERABILITY_SCAN_STATUS
} from "../../../../lib/utils/utils";
import { ArtifactService } from "../../../../../ng-swagger-gen/services/artifact.service";
import { Artifact } from "../../../../../ng-swagger-gen/models/artifact";
import { NativeReportSummary } from "../../../../../ng-swagger-gen/models/native-report-summary";
const STATE_CHECK_INTERVAL: number = 3000; // 3s const STATE_CHECK_INTERVAL: number = 3000; // 3s
const RETRY_TIMES: number = 3; const RETRY_TIMES: number = 3;
@ -27,8 +33,8 @@ export class ResultBarChartComponent implements OnInit, OnDestroy {
@Input() scanner: ScannerVo; @Input() scanner: ScannerVo;
@Input() repoName: string = ""; @Input() repoName: string = "";
@Input() projectName: string = ""; @Input() projectName: string = "";
@Input() artifactId: string = ""; @Input() artifactDigest: string = "";
@Input() summary: VulnerabilitySummary; @Input() summary: NativeReportSummary;
onSubmitting: boolean = false; onSubmitting: boolean = false;
retryCounter: number = 0; retryCounter: number = 0;
stateCheckTimer: Subscription; stateCheckTimer: Subscription;
@ -38,11 +44,10 @@ export class ResultBarChartComponent implements OnInit, OnDestroy {
submitFinish: EventEmitter<boolean> = new EventEmitter<boolean>(); submitFinish: EventEmitter<boolean> = new EventEmitter<boolean>();
constructor( constructor(
private artifactService: ArtifactDefaultService, private artifactService: ArtifactService,
private scanningService: ScanningResultService, private scanningService: ScanningResultService,
private errorHandler: ErrorHandler, private errorHandler: ErrorHandler,
private channel: ChannelService, private channel: ChannelService,
private ref: ChangeDetectorRef,
) { } ) { }
ngOnInit(): void { ngOnInit(): void {
@ -55,9 +60,9 @@ export class ResultBarChartComponent implements OnInit, OnDestroy {
this.getSummary(); this.getSummary();
}); });
} }
this.scanSubscription = this.channel.scanCommand$.subscribe((artifactId: string) => { this.scanSubscription = this.channel.scanCommand$.subscribe((artifactDigest: string) => {
let myFullTag: string = this.repoName + "/" + this.artifactId; let myFullTag: string = this.repoName + "/" + this.artifactDigest;
if (myFullTag === artifactId) { if (myFullTag === artifactDigest) {
this.scanNow(); this.scanNow();
} }
}); });
@ -107,26 +112,21 @@ export class ResultBarChartComponent implements OnInit, OnDestroy {
return; return;
} }
if (!this.repoName || !this.artifactId) { if (!this.repoName || !this.artifactDigest) {
console.log("bad repository or tag"); console.log("bad repository or tag");
return; return;
} }
this.onSubmitting = true; this.onSubmitting = true;
this.scanningService.startVulnerabilityScanning(this.projectName, this.repoName, this.artifactId) this.scanningService.startVulnerabilityScanning(this.projectName, this.repoName, this.artifactDigest)
.pipe(finalize(() => this.submitFinish.emit(false))) .pipe(finalize(() => this.submitFinish.emit(false)))
.subscribe(() => { .subscribe(() => {
this.onSubmitting = false; this.onSubmitting = false;
// Forcely change status to queued after successful submitting // Forcely change status to queued after successful submitting
this.summary = { this.summary = {
scan_status: VULNERABILITY_SCAN_STATUS.PENDING, scan_status: VULNERABILITY_SCAN_STATUS.PENDING,
}; };
// Forcely refresh view
this.forceRefreshView(1000);
// Start check status util the job is done // Start check status util the job is done
if (!this.stateCheckTimer) { if (!this.stateCheckTimer) {
// Avoid duplicated subscribing // Avoid duplicated subscribing
@ -145,20 +145,22 @@ export class ResultBarChartComponent implements OnInit, OnDestroy {
} }
getSummary(): void { getSummary(): void {
if (!this.repoName || !this.artifactId) { if (!this.repoName || !this.artifactDigest) {
return; return;
} }
// this.tagService.getTag(this.repoName, this.artifactId) // this.tagService.getTag(this.repoName, this.artifactId)
this.artifactService.getArtifactFromDigest(this.projectName, this.repoName, this.artifactId) this.artifactService.getArtifact({
projectName: this.projectName,
repositoryName: this.repoName,
reference: this.artifactDigest,
withScanOverview: true
})
.subscribe((artifact: Artifact) => { .subscribe((artifact: Artifact) => {
// To keep the same summary reference, use value copy. // To keep the same summary reference, use value copy.
if (artifact.scan_overview) { if (artifact.scan_overview) {
this.copyValue(artifact.scan_overview[DEFAULT_SUPPORTED_MIME_TYPE]); this.copyValue(artifact.scan_overview[DEFAULT_SUPPORTED_MIME_TYPE]);
} }
// Forcely refresh view
this.forceRefreshView(1000);
if (!this.queued && !this.scanning) { if (!this.queued && !this.scanning) {
// Scanning should be done // Scanning should be done
if (this.stateCheckTimer) { if (this.stateCheckTimer) {
@ -185,22 +187,8 @@ export class ResultBarChartComponent implements OnInit, OnDestroy {
if (!this.summary || !newVal || !newVal.scan_status) { return; } if (!this.summary || !newVal || !newVal.scan_status) { return; }
this.summary = clone(newVal); this.summary = clone(newVal);
} }
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);
}
viewLog(): string { viewLog(): string {
return `${ CURRENT_BASE_HREF }/projects/${this.projectName}/repositories/${this.repoName} return `${ CURRENT_BASE_HREF }/projects/${this.projectName}/repositories/${this.repoName}
/artifacts/${this.artifactId}/scan/${this.summary.report_id}/log`; /artifacts/${this.artifactDigest}/scan/${this.summary.report_id}/log`;
} }
} }

View File

@ -67,7 +67,7 @@ export abstract class ScanningResultService {
abstract startVulnerabilityScanning( abstract startVulnerabilityScanning(
projectName: string, projectName: string,
repoName: string, repoName: string,
artifactId: string artifactDigest: string
): Observable<any>; ): Observable<any>;
/** /**
@ -148,15 +148,15 @@ export class ScanningResultDefaultService extends ScanningResultService {
startVulnerabilityScanning( startVulnerabilityScanning(
projectName: string, projectName: string,
repoName: string, repoName: string,
artifactId: string artifactDigest: string
): Observable<any> { ): Observable<any> {
if (!repoName || repoName.trim() === "" || !artifactId || artifactId.trim() === "") { if (!repoName || repoName.trim() === "" || !artifactDigest || artifactDigest.trim() === "") {
return observableThrowError("Bad argument"); return observableThrowError("Bad argument");
} }
return this.http return this.http
.post( .post(
`${ CURRENT_BASE_HREF }/projects//${projectName}/repositories/${repoName}/artifacts/${artifactId}/scan`, `${ CURRENT_BASE_HREF }/projects//${projectName}/repositories/${repoName}/artifacts/${artifactDigest}/scan`,
HTTP_JSON_OPTIONS HTTP_JSON_OPTIONS
) )
.pipe(map(() => { .pipe(map(() => {