mirror of
https://github.com/goharbor/harbor.git
synced 2025-01-15 20:22:01 +01:00
Add pagination support to scanner list (#14673)
Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
parent
5e75c45873
commit
5e5544cd47
@ -8,7 +8,6 @@ import { SearchTriggerService } from '../../shared/components/global-search/sear
|
|||||||
import { HarborShellComponent } from './harbor-shell.component';
|
import { HarborShellComponent } from './harbor-shell.component';
|
||||||
import { ClarityModule } from "@clr/angular";
|
import { ClarityModule } from "@clr/angular";
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
import { ConfigScannerService } from "../left-side-nav/interrogation-services/scanner/config-scanner.service";
|
|
||||||
import { modalEvents } from '../modal-events.const';
|
import { modalEvents } from '../modal-events.const';
|
||||||
import { PasswordSettingComponent } from '../password-setting/password-setting.component';
|
import { PasswordSettingComponent } from '../password-setting/password-setting.component';
|
||||||
import { AboutDialogComponent } from '../../shared/components/about-dialog/about-dialog.component';
|
import { AboutDialogComponent } from '../../shared/components/about-dialog/about-dialog.component';
|
||||||
@ -21,6 +20,10 @@ import { ErrorHandler } from '../../shared/units/error-handler';
|
|||||||
import { AccountSettingsModalComponent } from "../account-settings/account-settings-modal.component";
|
import { AccountSettingsModalComponent } from "../account-settings/account-settings-modal.component";
|
||||||
import { InlineAlertComponent } from "../../shared/components/inline-alert/inline-alert.component";
|
import { InlineAlertComponent } from "../../shared/components/inline-alert/inline-alert.component";
|
||||||
import { AccountSettingsModalService } from "../account-settings/account-settings-modal-service.service";
|
import { AccountSettingsModalService } from "../account-settings/account-settings-modal-service.service";
|
||||||
|
import { ScannerService } from "../../../../ng-swagger-gen/services/scanner.service";
|
||||||
|
import { HttpHeaders, HttpResponse } from "@angular/common/http";
|
||||||
|
import { Registry } from "../../../../ng-swagger-gen/models/registry";
|
||||||
|
import { delay } from "rxjs/operators";
|
||||||
|
|
||||||
describe('HarborShellComponent', () => {
|
describe('HarborShellComponent', () => {
|
||||||
let component: HarborShellComponent;
|
let component: HarborShellComponent;
|
||||||
@ -71,9 +74,16 @@ describe('HarborShellComponent', () => {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let fakeConfigScannerService = {
|
let fakeScannerService = {
|
||||||
getScanners() {
|
listScannersResponse() {
|
||||||
return of(true);
|
const response: HttpResponse<Array<Registry>> = new HttpResponse<Array<Registry>>({
|
||||||
|
headers: new HttpHeaders({'x-total-count': [].length.toString()}),
|
||||||
|
body: []
|
||||||
|
});
|
||||||
|
return of(response).pipe(delay(0));
|
||||||
|
},
|
||||||
|
listScanners() {
|
||||||
|
return of([]).pipe(delay(0));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
beforeEach(waitForAsync(() => {
|
beforeEach(waitForAsync(() => {
|
||||||
@ -92,7 +102,7 @@ describe('HarborShellComponent', () => {
|
|||||||
{ provide: SessionService, useValue: fakeSessionService },
|
{ provide: SessionService, useValue: fakeSessionService },
|
||||||
{ provide: SearchTriggerService, useValue: fakeSearchTriggerService },
|
{ provide: SearchTriggerService, useValue: fakeSearchTriggerService },
|
||||||
{ provide: AppConfigService, useValue: fakeAppConfigService },
|
{ provide: AppConfigService, useValue: fakeAppConfigService },
|
||||||
{ provide: ConfigScannerService, useValue: fakeConfigScannerService },
|
{ provide: ScannerService, useValue: fakeScannerService },
|
||||||
{ provide: MessageHandlerService, useValue: mockMessageHandlerService },
|
{ provide: MessageHandlerService, useValue: mockMessageHandlerService },
|
||||||
{ provide: AccountSettingsModalService, useValue: mockAccountSettingsModalService },
|
{ provide: AccountSettingsModalService, useValue: mockAccountSettingsModalService },
|
||||||
{ provide: PasswordSettingService, useValue: mockPasswordSettingService },
|
{ provide: PasswordSettingService, useValue: mockPasswordSettingService },
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
import { Component, OnInit, ViewChild, OnDestroy, ElementRef, ChangeDetectorRef } from '@angular/core';
|
import { Component, OnInit, ViewChild, OnDestroy, ElementRef, ChangeDetectorRef } from '@angular/core';
|
||||||
import { Router, ActivatedRoute } from '@angular/router';
|
import { Router, ActivatedRoute } from '@angular/router';
|
||||||
import { Subscription } from "rxjs";
|
import { forkJoin, Observable, Subscription } from "rxjs";
|
||||||
import { AppConfigService } from '../../services/app-config.service';
|
import { AppConfigService } from '../../services/app-config.service';
|
||||||
import { ModalEvent } from '../modal-event';
|
import { ModalEvent } from '../modal-event';
|
||||||
import { modalEvents } from '../modal-events.const';
|
import { modalEvents } from '../modal-events.const';
|
||||||
@ -23,12 +23,14 @@ import { SessionService } from '../../shared/services/session.service';
|
|||||||
import { AboutDialogComponent } from '../../shared/components/about-dialog/about-dialog.component';
|
import { AboutDialogComponent } from '../../shared/components/about-dialog/about-dialog.component';
|
||||||
import { SearchTriggerService } from '../../shared/components/global-search/search-trigger.service';
|
import { SearchTriggerService } from '../../shared/components/global-search/search-trigger.service';
|
||||||
import { CommonRoutes } from "../../shared/entities/shared.const";
|
import { CommonRoutes } from "../../shared/entities/shared.const";
|
||||||
import { ConfigScannerService, SCANNERS_DOC } from "../left-side-nav/interrogation-services/scanner/config-scanner.service";
|
|
||||||
import { THEME_ARRAY, ThemeInterface } from "../../services/theme";
|
import { THEME_ARRAY, ThemeInterface } from "../../services/theme";
|
||||||
import { clone } from "../../shared/units/utils";
|
import { clone, DEFAULT_PAGE_SIZE } from "../../shared/units/utils";
|
||||||
import { ThemeService } from "../../services/theme.service";
|
import { ThemeService } from "../../services/theme.service";
|
||||||
import { AccountSettingsModalComponent } from "../account-settings/account-settings-modal.component";
|
import { AccountSettingsModalComponent } from "../account-settings/account-settings-modal.component";
|
||||||
import { EventService, HarborEvent } from "../../services/event-service/event.service";
|
import { EventService, HarborEvent } from "../../services/event-service/event.service";
|
||||||
|
import { SCANNERS_DOC } from "../left-side-nav/interrogation-services/scanner/scanner";
|
||||||
|
import { ScannerService } from "../../../../ng-swagger-gen/services/scanner.service";
|
||||||
|
import { Project } from "../../../../ng-swagger-gen/models/project";
|
||||||
|
|
||||||
const HAS_SHOWED_SCANNER_INFO: string = 'hasShowScannerInfo';
|
const HAS_SHOWED_SCANNER_INFO: string = 'hasShowScannerInfo';
|
||||||
const YES: string = 'yes';
|
const YES: string = 'yes';
|
||||||
@ -76,7 +78,7 @@ export class HarborShellComponent implements OnInit, OnDestroy {
|
|||||||
private session: SessionService,
|
private session: SessionService,
|
||||||
private searchTrigger: SearchTriggerService,
|
private searchTrigger: SearchTriggerService,
|
||||||
private appConfigService: AppConfigService,
|
private appConfigService: AppConfigService,
|
||||||
private scannerService: ConfigScannerService,
|
private scannerService: ScannerService,
|
||||||
public theme: ThemeService,
|
public theme: ThemeService,
|
||||||
private event: EventService,
|
private event: EventService,
|
||||||
private cd: ChangeDetectorRef
|
private cd: ChangeDetectorRef
|
||||||
@ -108,7 +110,9 @@ export class HarborShellComponent implements OnInit, OnDestroy {
|
|||||||
this.isSearchResultsOpened = false;
|
this.isSearchResultsOpened = false;
|
||||||
});
|
});
|
||||||
if (!(localStorage && localStorage.getItem(HAS_SHOWED_SCANNER_INFO) === YES)) {
|
if (!(localStorage && localStorage.getItem(HAS_SHOWED_SCANNER_INFO) === YES)) {
|
||||||
this.getDefaultScanner();
|
if (this.isSystemAdmin) {
|
||||||
|
this.getDefaultScanner();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// set local in app
|
// set local in app
|
||||||
if (localStorage) {
|
if (localStorage) {
|
||||||
@ -131,11 +135,37 @@ export class HarborShellComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getDefaultScanner() {
|
getDefaultScanner() {
|
||||||
this.scannerService.getScanners()
|
this.scannerService.listScannersResponse({
|
||||||
.subscribe(scanners => {
|
pageSize: DEFAULT_PAGE_SIZE,
|
||||||
if (scanners && scanners.length) {
|
page: 1
|
||||||
this.showScannerInfo = scanners.some(scanner => scanner.is_default);
|
}).subscribe(res => {
|
||||||
}
|
if (res.headers) {
|
||||||
|
const xHeader: string = res.headers.get("X-Total-Count");
|
||||||
|
const totalCount = parseInt(xHeader, 0);
|
||||||
|
let arr = res.body || [];
|
||||||
|
if (totalCount <= DEFAULT_PAGE_SIZE) { // already gotten all scanners
|
||||||
|
if (arr && arr.length) {
|
||||||
|
this.showScannerInfo = arr.some(scanner => scanner.is_default);
|
||||||
|
}
|
||||||
|
} else { // get all the scanners in specified times
|
||||||
|
const times: number = Math.ceil(totalCount / DEFAULT_PAGE_SIZE);
|
||||||
|
const observableList: Observable<Project[]>[] = [];
|
||||||
|
for (let i = 2; i <= times; i++) {
|
||||||
|
observableList.push(this.scannerService.listScanners({
|
||||||
|
page: i,
|
||||||
|
pageSize: DEFAULT_PAGE_SIZE
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
forkJoin(observableList).subscribe(response => {
|
||||||
|
if (response && response.length) {
|
||||||
|
response.forEach(item => {
|
||||||
|
arr = arr.concat(item);
|
||||||
|
});
|
||||||
|
this.showScannerInfo = arr.some(scanner => scanner.is_default);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
|
@ -17,7 +17,6 @@ import { SharedModule } from "../../../shared/shared.module";
|
|||||||
import { NewScannerModalComponent } from "./scanner/new-scanner-modal/new-scanner-modal.component";
|
import { NewScannerModalComponent } from "./scanner/new-scanner-modal/new-scanner-modal.component";
|
||||||
import { ScannerMetadataComponent } from "./scanner/scanner-metadata/scanner-metadata.component";
|
import { ScannerMetadataComponent } from "./scanner/scanner-metadata/scanner-metadata.component";
|
||||||
import { NewScannerFormComponent } from "./scanner/new-scanner-form/new-scanner-form.component";
|
import { NewScannerFormComponent } from "./scanner/new-scanner-form/new-scanner-form.component";
|
||||||
import { ConfigScannerService } from "./scanner/config-scanner.service";
|
|
||||||
import { RouterModule, Routes } from "@angular/router";
|
import { RouterModule, Routes } from "@angular/router";
|
||||||
import { ConfigurationScannerComponent } from "./scanner/config-scanner.component";
|
import { ConfigurationScannerComponent } from "./scanner/config-scanner.component";
|
||||||
import { VulnerabilityConfigComponent } from "./vulnerability/vulnerability-config.component";
|
import { VulnerabilityConfigComponent } from "./vulnerability/vulnerability-config.component";
|
||||||
@ -61,7 +60,6 @@ const routes: Routes = [
|
|||||||
VulnerabilityConfigComponent
|
VulnerabilityConfigComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
ConfigScannerService,
|
|
||||||
ScanAllRepoService,
|
ScanAllRepoService,
|
||||||
{provide: ScanApiRepository, useClass: ScanApiDefaultRepository },
|
{provide: ScanApiRepository, useClass: ScanApiDefaultRepository },
|
||||||
]
|
]
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
</clr-signpost-content>
|
</clr-signpost-content>
|
||||||
</clr-signpost>
|
</clr-signpost>
|
||||||
</h4>
|
</h4>
|
||||||
<clr-datagrid [clrDgLoading]="onGoing" [(clrDgSingleSelected)]="selectedRow">
|
<clr-datagrid (clrDgRefresh)="getScanners($event)" [clrDgLoading]="onGoing" [(clrDgSingleSelected)]="selectedRow">
|
||||||
<clr-dg-action-bar>
|
<clr-dg-action-bar>
|
||||||
<div class="clr-row">
|
<div class="clr-row">
|
||||||
<div class="clr-col-7">
|
<div class="clr-col-7">
|
||||||
@ -52,7 +52,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="clr-col-5">
|
<div class="clr-col-5">
|
||||||
<div class="action-head-pos">
|
<div class="action-head-pos">
|
||||||
<span (click)="getScanners()" class="refresh-btn">
|
<span (click)="refresh()" class="refresh-btn">
|
||||||
<clr-icon shape="refresh" [hidden]="onGoing"></clr-icon>
|
<clr-icon shape="refresh" [hidden]="onGoing"></clr-icon>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -67,7 +67,7 @@
|
|||||||
<clr-dg-placeholder>
|
<clr-dg-placeholder>
|
||||||
{{'SCANNER.NO_SCANNER' | translate}}
|
{{'SCANNER.NO_SCANNER' | translate}}
|
||||||
</clr-dg-placeholder>
|
</clr-dg-placeholder>
|
||||||
<clr-dg-row *clrDgItems="let scanner of scanners" [clrDgItem]="scanner">
|
<clr-dg-row *ngFor="let scanner of scanners" [clrDgItem]="scanner">
|
||||||
<clr-dg-cell class="position-relative">
|
<clr-dg-cell class="position-relative">
|
||||||
<span>{{scanner.name}}</span>
|
<span>{{scanner.name}}</span>
|
||||||
<span *ngIf="scanner.is_default" class="label label-info ml-1">{{'SCANNER.DEFAULT' | translate}}</span>
|
<span *ngIf="scanner.is_default" class="label label-info ml-1">{{'SCANNER.DEFAULT' | translate}}</span>
|
||||||
@ -96,9 +96,10 @@
|
|||||||
<scanner-metadata *clrIfExpanded [uid]="scanner.uuid" ngProjectAs="clr-dg-row-detail"></scanner-metadata>
|
<scanner-metadata *clrIfExpanded [uid]="scanner.uuid" ngProjectAs="clr-dg-row-detail"></scanner-metadata>
|
||||||
</clr-dg-row>
|
</clr-dg-row>
|
||||||
<clr-dg-footer>
|
<clr-dg-footer>
|
||||||
<clr-dg-pagination [clrDgPageSize]="15">
|
<clr-dg-pagination #pagination [clrDgPageSize]="pageSize" [(clrDgPage)]="page" [clrDgTotalItems]="total">
|
||||||
<clr-dg-page-size [clrPageSizeOptions]="[15,25,50]">{{"PAGINATION.PAGE_SIZE" | translate}}</clr-dg-page-size>
|
<clr-dg-page-size [clrPageSizeOptions]="[15,25,50]">{{"PAGINATION.PAGE_SIZE" | translate}}</clr-dg-page-size>
|
||||||
<span *ngIf="scanners?.length > 0">1 - {{scanners?.length}} {{'WEBHOOK.OF' | translate}} </span> {{scanners?.length}} {{'WEBHOOK.ITEMS' | translate}}
|
<span *ngIf="total">{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'DESTINATION.OF' | translate}}</span>
|
||||||
|
{{total}} {{'DESTINATION.ITEMS' | translate}}
|
||||||
</clr-dg-pagination>
|
</clr-dg-pagination>
|
||||||
</clr-dg-footer>
|
</clr-dg-footer>
|
||||||
</clr-datagrid>
|
</clr-datagrid>
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
import { ComponentFixture, ComponentFixtureAutoDetect, TestBed } from '@angular/core/testing';
|
import { ComponentFixture, ComponentFixtureAutoDetect, TestBed } from '@angular/core/testing';
|
||||||
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
|
||||||
import { ClarityModule } from "@clr/angular";
|
|
||||||
import { of } from "rxjs";
|
import { of } from "rxjs";
|
||||||
|
import { delay } from "rxjs/operators";
|
||||||
import { ConfigurationScannerComponent } from "./config-scanner.component";
|
import { ConfigurationScannerComponent } from "./config-scanner.component";
|
||||||
import { ConfigScannerService } from "./config-scanner.service";
|
|
||||||
import { MessageHandlerService } from "../../../../shared/services/message-handler.service";
|
|
||||||
import { SharedTestingModule } from "../../../../shared/shared.module";
|
import { SharedTestingModule } from "../../../../shared/shared.module";
|
||||||
import { ScannerMetadataComponent } from "./scanner-metadata/scanner-metadata.component";
|
import { ScannerMetadataComponent } from "./scanner-metadata/scanner-metadata.component";
|
||||||
import { NewScannerModalComponent } from "./new-scanner-modal/new-scanner-modal.component";
|
import { NewScannerModalComponent } from "./new-scanner-modal/new-scanner-modal.component";
|
||||||
import { NewScannerFormComponent } from "./new-scanner-form/new-scanner-form.component";
|
import { NewScannerFormComponent } from "./new-scanner-form/new-scanner-form.component";
|
||||||
import { TranslateService } from "@ngx-translate/core";
|
import { ScannerService } from "../../../../../../ng-swagger-gen/services/scanner.service";
|
||||||
import { ErrorHandler } from "../../../../shared/units/error-handler";
|
import { HttpHeaders, HttpResponse } from "@angular/common/http";
|
||||||
import { ConfirmationDialogService } from "../../../global-confirmation-dialog/confirmation-dialog.service";
|
import { Registry } from "../../../../../../ng-swagger-gen/models/registry";
|
||||||
|
import { ClrLoadingState } from "@clr/angular";
|
||||||
|
|
||||||
describe('ConfigurationScannerComponent', () => {
|
describe('ConfigurationScannerComponent', () => {
|
||||||
let mockScannerMetadata = {
|
let mockScannerMetadata = {
|
||||||
@ -35,10 +33,14 @@ describe('ConfigurationScannerComponent', () => {
|
|||||||
let fixture: ComponentFixture<ConfigurationScannerComponent>;
|
let fixture: ComponentFixture<ConfigurationScannerComponent>;
|
||||||
let fakedConfigScannerService = {
|
let fakedConfigScannerService = {
|
||||||
getScannerMetadata() {
|
getScannerMetadata() {
|
||||||
return of(mockScannerMetadata);
|
return of(mockScannerMetadata).pipe(delay(10));
|
||||||
},
|
},
|
||||||
getScanners() {
|
listScannersResponse() {
|
||||||
return of([mockScanner1]);
|
const response: HttpResponse<Array<Registry>> = new HttpResponse<Array<Registry>>({
|
||||||
|
headers: new HttpHeaders({'x-total-count': [mockScanner1].length.toString()}),
|
||||||
|
body: [mockScanner1]
|
||||||
|
});
|
||||||
|
return of(response).pipe(delay(10));
|
||||||
},
|
},
|
||||||
updateScanner() {
|
updateScanner() {
|
||||||
return of(true);
|
return of(true);
|
||||||
@ -48,8 +50,6 @@ describe('ConfigurationScannerComponent', () => {
|
|||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [
|
imports: [
|
||||||
SharedTestingModule,
|
SharedTestingModule,
|
||||||
BrowserAnimationsModule,
|
|
||||||
ClarityModule,
|
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
ConfigurationScannerComponent,
|
ConfigurationScannerComponent,
|
||||||
@ -58,11 +58,7 @@ describe('ConfigurationScannerComponent', () => {
|
|||||||
NewScannerFormComponent
|
NewScannerFormComponent
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
ErrorHandler,
|
{ provide: ScannerService, useValue: fakedConfigScannerService },
|
||||||
MessageHandlerService,
|
|
||||||
ConfirmationDialogService,
|
|
||||||
TranslateService,
|
|
||||||
{ provide: ConfigScannerService, useValue: fakedConfigScannerService },
|
|
||||||
// open auto detect
|
// open auto detect
|
||||||
{ provide: ComponentFixtureAutoDetect, useValue: true }
|
{ provide: ComponentFixtureAutoDetect, useValue: true }
|
||||||
]
|
]
|
||||||
@ -71,9 +67,11 @@ describe('ConfigurationScannerComponent', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(ConfigurationScannerComponent);
|
fixture = TestBed.createComponent(ConfigurationScannerComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
|
component.newScannerDialog.saveBtnState = ClrLoadingState.LOADING;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
it('should create', () => {
|
it('should create', async () => {
|
||||||
|
await fixture.whenStable();
|
||||||
expect(component).toBeTruthy();
|
expect(component).toBeTruthy();
|
||||||
expect(component.scanners.length).toBe(1);
|
expect(component.scanners.length).toBe(1);
|
||||||
});
|
});
|
||||||
|
@ -1,14 +1,16 @@
|
|||||||
import { Component, ViewChild, OnInit, OnDestroy } from "@angular/core";
|
import { Component, ViewChild, OnInit, OnDestroy } from "@angular/core";
|
||||||
import { Scanner } from "./scanner";
|
import { Scanner, SCANNERS_DOC } from "./scanner";
|
||||||
import { NewScannerModalComponent } from "./new-scanner-modal/new-scanner-modal.component";
|
import { NewScannerModalComponent } from "./new-scanner-modal/new-scanner-modal.component";
|
||||||
import { ConfigScannerService, SCANNERS_DOC } from "./config-scanner.service";
|
|
||||||
import { finalize } from "rxjs/operators";
|
import { finalize } from "rxjs/operators";
|
||||||
import { MessageHandlerService } from "../../../../shared/services/message-handler.service";
|
import { MessageHandlerService } from "../../../../shared/services/message-handler.service";
|
||||||
import { ErrorHandler } from "../../../../shared/units/error-handler";
|
import { ErrorHandler } from "../../../../shared/units/error-handler";
|
||||||
import { clone } from "../../../../shared/units/utils";
|
import { clone, DEFAULT_PAGE_SIZE, getSortingString } from "../../../../shared/units/utils";
|
||||||
import { ConfirmationDialogService } from "../../../global-confirmation-dialog/confirmation-dialog.service";
|
import { ConfirmationDialogService } from "../../../global-confirmation-dialog/confirmation-dialog.service";
|
||||||
import { ConfirmationButtons, ConfirmationState, ConfirmationTargets } from "../../../../shared/entities/shared.const";
|
import { ConfirmationButtons, ConfirmationState, ConfirmationTargets } from "../../../../shared/entities/shared.const";
|
||||||
import { ConfirmationMessage } from "../../../global-confirmation-dialog/confirmation-message";
|
import { ConfirmationMessage } from "../../../global-confirmation-dialog/confirmation-message";
|
||||||
|
import { ScannerService } from "../../../../../../ng-swagger-gen/services/scanner.service";
|
||||||
|
import { ClrDatagridStateInterface } from "@clr/angular";
|
||||||
|
import { ScannerRegistrationReq } from "../../../../../../ng-swagger-gen/models/scanner-registration-req";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'config-scanner',
|
selector: 'config-scanner',
|
||||||
@ -18,13 +20,17 @@ import { ConfirmationMessage } from "../../../global-confirmation-dialog/confirm
|
|||||||
export class ConfigurationScannerComponent implements OnInit, OnDestroy {
|
export class ConfigurationScannerComponent implements OnInit, OnDestroy {
|
||||||
scanners: Scanner[] = [];
|
scanners: Scanner[] = [];
|
||||||
selectedRow: Scanner;
|
selectedRow: Scanner;
|
||||||
onGoing: boolean = false;
|
onGoing: boolean = true;
|
||||||
@ViewChild(NewScannerModalComponent)
|
@ViewChild(NewScannerModalComponent)
|
||||||
newScannerDialog: NewScannerModalComponent;
|
newScannerDialog: NewScannerModalComponent;
|
||||||
deletionSubscription: any;
|
deletionSubscription: any;
|
||||||
scannerDocUrl: string = SCANNERS_DOC;
|
scannerDocUrl: string = SCANNERS_DOC;
|
||||||
|
page: number = 1;
|
||||||
|
pageSize: number = DEFAULT_PAGE_SIZE;
|
||||||
|
total: number = 0;
|
||||||
|
state: ClrDatagridStateInterface;
|
||||||
constructor(
|
constructor(
|
||||||
private configScannerService: ConfigScannerService,
|
private configScannerService: ScannerService,
|
||||||
private errorHandler: ErrorHandler,
|
private errorHandler: ErrorHandler,
|
||||||
private msgHandler: MessageHandlerService,
|
private msgHandler: MessageHandlerService,
|
||||||
private deletionDialogService: ConfirmationDialogService,
|
private deletionDialogService: ConfirmationDialogService,
|
||||||
@ -35,17 +41,18 @@ export class ConfigurationScannerComponent implements OnInit, OnDestroy {
|
|||||||
if (confirmed &&
|
if (confirmed &&
|
||||||
confirmed.source === ConfirmationTargets.SCANNER &&
|
confirmed.source === ConfirmationTargets.SCANNER &&
|
||||||
confirmed.state === ConfirmationState.CONFIRMED) {
|
confirmed.state === ConfirmationState.CONFIRMED) {
|
||||||
this.configScannerService.deleteScanners(confirmed.data)
|
this.configScannerService.deleteScanner({
|
||||||
|
registrationId: confirmed.data[0].uuid
|
||||||
|
})
|
||||||
.subscribe(response => {
|
.subscribe(response => {
|
||||||
this.msgHandler.showSuccess("SCANNER.DELETE_SUCCESS");
|
this.msgHandler.showSuccess("SCANNER.DELETE_SUCCESS");
|
||||||
this.getScanners();
|
this.refresh();
|
||||||
}, error => {
|
}, error => {
|
||||||
this.errorHandler.error(error);
|
this.errorHandler.error(error);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.getScanners();
|
|
||||||
}
|
}
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
if (this.deletionSubscription) {
|
if (this.deletionSubscription) {
|
||||||
@ -53,13 +60,45 @@ export class ConfigurationScannerComponent implements OnInit, OnDestroy {
|
|||||||
this.deletionSubscription = null;
|
this.deletionSubscription = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getScanners() {
|
refresh() {
|
||||||
|
this.page = 1;
|
||||||
|
this.selectedRow = null;
|
||||||
|
this.total = 0;
|
||||||
|
this.getScanners(this.state);
|
||||||
|
}
|
||||||
|
getScanners(state?: ClrDatagridStateInterface) {
|
||||||
|
this.state = state;
|
||||||
|
if (state && state.page) {
|
||||||
|
this.pageSize = state.page.size;
|
||||||
|
}
|
||||||
|
let q: string;
|
||||||
|
if (state && state.filters && state.filters.length) {
|
||||||
|
q = encodeURIComponent(`${state.filters[0].property}=~${state.filters[0].value}`);
|
||||||
|
}
|
||||||
|
let sort: string;
|
||||||
|
if (state && state.sort && state.sort.by) {
|
||||||
|
sort = getSortingString(state);
|
||||||
|
} else { // sort by creation_time desc by default
|
||||||
|
sort = `-creation_time`;
|
||||||
|
}
|
||||||
this.onGoing = true;
|
this.onGoing = true;
|
||||||
this.configScannerService.getScanners()
|
this.configScannerService.listScannersResponse({
|
||||||
|
page: this.page,
|
||||||
|
pageSize: this.pageSize,
|
||||||
|
q: q,
|
||||||
|
sort: sort
|
||||||
|
})
|
||||||
.pipe(finalize(() => this.onGoing = false))
|
.pipe(finalize(() => this.onGoing = false))
|
||||||
.subscribe(response => {
|
.subscribe(response => {
|
||||||
this.scanners = response;
|
// Get total count
|
||||||
this.getMetadataForAll();
|
if (response.headers) {
|
||||||
|
let xHeader: string = response.headers.get("X-Total-Count");
|
||||||
|
if (xHeader) {
|
||||||
|
this.total = parseInt(xHeader, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.scanners = response.body || [];
|
||||||
|
this.getMetadataForAll();
|
||||||
}, error => {
|
}, error => {
|
||||||
this.errorHandler.error(error);
|
this.errorHandler.error(error);
|
||||||
});
|
});
|
||||||
@ -69,7 +108,9 @@ export class ConfigurationScannerComponent implements OnInit, OnDestroy {
|
|||||||
this.scanners.forEach((scanner, index) => {
|
this.scanners.forEach((scanner, index) => {
|
||||||
if (scanner.uuid ) {
|
if (scanner.uuid ) {
|
||||||
this.scanners[index].loadingMetadata = true;
|
this.scanners[index].loadingMetadata = true;
|
||||||
this.configScannerService.getScannerMetadata(scanner.uuid)
|
this.configScannerService.getScannerMetadata({
|
||||||
|
registrationId: scanner.uuid
|
||||||
|
})
|
||||||
.pipe(finalize(() => this.scanners[index].loadingMetadata = false))
|
.pipe(finalize(() => this.scanners[index].loadingMetadata = false))
|
||||||
.subscribe(response => {
|
.subscribe(response => {
|
||||||
this.scanners[index].metadata = response;
|
this.scanners[index].metadata = response;
|
||||||
@ -91,12 +132,15 @@ export class ConfigurationScannerComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
changeStat() {
|
changeStat() {
|
||||||
if (this.selectedRow) {
|
if (this.selectedRow) {
|
||||||
let scanner: Scanner = clone(this.selectedRow);
|
let scanner: ScannerRegistrationReq = clone(this.selectedRow);
|
||||||
scanner.disabled = !scanner.disabled;
|
scanner.disabled = !scanner.disabled;
|
||||||
this.configScannerService.updateScanner(scanner)
|
this.configScannerService.updateScanner({
|
||||||
|
registrationId: this.selectedRow.uuid,
|
||||||
|
registration: scanner
|
||||||
|
})
|
||||||
.subscribe(response => {
|
.subscribe(response => {
|
||||||
this.msgHandler.showSuccess("SCANNER.UPDATE_SUCCESS");
|
this.msgHandler.showSuccess("SCANNER.UPDATE_SUCCESS");
|
||||||
this.getScanners();
|
this.refresh();
|
||||||
}, error => {
|
}, error => {
|
||||||
this.errorHandler.error(error);
|
this.errorHandler.error(error);
|
||||||
});
|
});
|
||||||
@ -104,10 +148,15 @@ export class ConfigurationScannerComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
setAsDefault() {
|
setAsDefault() {
|
||||||
if (this.selectedRow) {
|
if (this.selectedRow) {
|
||||||
this.configScannerService.setAsDefault(this.selectedRow.uuid)
|
this.configScannerService.setScannerAsDefault({
|
||||||
|
registrationId: this.selectedRow.uuid,
|
||||||
|
payload: {
|
||||||
|
is_default: true
|
||||||
|
}
|
||||||
|
})
|
||||||
.subscribe(response => {
|
.subscribe(response => {
|
||||||
this.msgHandler.showSuccess("SCANNER.UPDATE_SUCCESS");
|
this.msgHandler.showSuccess("SCANNER.UPDATE_SUCCESS");
|
||||||
this.getScanners();
|
this.refresh();
|
||||||
}, error => {
|
}, error => {
|
||||||
this.errorHandler.error(error);
|
this.errorHandler.error(error);
|
||||||
});
|
});
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
import { TestBed, inject } from '@angular/core/testing';
|
|
||||||
import { SharedTestingModule } from "../../../../shared/shared.module";
|
|
||||||
import { ConfigScannerService } from "./config-scanner.service";
|
|
||||||
|
|
||||||
describe('TagService', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
TestBed.configureTestingModule({
|
|
||||||
imports: [
|
|
||||||
SharedTestingModule
|
|
||||||
],
|
|
||||||
providers: [
|
|
||||||
ConfigScannerService
|
|
||||||
]
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be initialized', inject([ConfigScannerService], (service: ConfigScannerService) => {
|
|
||||||
expect(service).toBeTruthy();
|
|
||||||
}));
|
|
||||||
});
|
|
@ -1,82 +0,0 @@
|
|||||||
import {Injectable} from "@angular/core";
|
|
||||||
import {Scanner} from "./scanner";
|
|
||||||
import { forkJoin, Observable, throwError as observableThrowError } from "rxjs";
|
|
||||||
import { catchError, map } from "rxjs/operators";
|
|
||||||
import { HttpClient } from "@angular/common/http";
|
|
||||||
import { ScannerMetadata } from "./scanner-metadata";
|
|
||||||
import { CURRENT_BASE_HREF } from "../../../../shared/units/utils";
|
|
||||||
|
|
||||||
export const SCANNERS_DOC: string = "https://goharbor.io/blog/harbor-1.10-release/#vulnerability-scanning-with-pluggable-scanners";
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root',
|
|
||||||
})
|
|
||||||
export class ConfigScannerService {
|
|
||||||
|
|
||||||
constructor( private http: HttpClient) {}
|
|
||||||
getScannersByName(name: string): Observable<Scanner[]> {
|
|
||||||
name = encodeURIComponent(name);
|
|
||||||
return this.http.get(`${ CURRENT_BASE_HREF }/scanners?ex_name=${name}`)
|
|
||||||
.pipe(catchError(error => observableThrowError(error)))
|
|
||||||
.pipe(map(response => response as Scanner[]));
|
|
||||||
}
|
|
||||||
getScannersByEndpointUrl(endpointUrl: string): Observable<Scanner[]> {
|
|
||||||
endpointUrl = encodeURIComponent(endpointUrl);
|
|
||||||
return this.http.get(`${ CURRENT_BASE_HREF }/scanners?ex_url=${endpointUrl}`)
|
|
||||||
.pipe(catchError(error => observableThrowError(error)))
|
|
||||||
.pipe(map(response => response as Scanner[]));
|
|
||||||
}
|
|
||||||
testEndpointUrl(testValue: any): Observable<any> {
|
|
||||||
return this.http.post(`${ CURRENT_BASE_HREF }/scanners/ping`, testValue)
|
|
||||||
.pipe(catchError(error => observableThrowError(error)));
|
|
||||||
}
|
|
||||||
addScanner(scanner: Scanner): Observable<any> {
|
|
||||||
return this.http.post(CURRENT_BASE_HREF + '/scanners', scanner )
|
|
||||||
.pipe(catchError(error => observableThrowError(error)));
|
|
||||||
}
|
|
||||||
getScanners(): Observable<Scanner[]> {
|
|
||||||
return this.http.get(CURRENT_BASE_HREF + '/scanners')
|
|
||||||
.pipe(map(response => response as Scanner[]))
|
|
||||||
.pipe(catchError(error => observableThrowError(error)));
|
|
||||||
}
|
|
||||||
updateScanner(scanner: Scanner): Observable<any> {
|
|
||||||
return this.http.put(`${ CURRENT_BASE_HREF }/scanners/${scanner.uuid}`, scanner )
|
|
||||||
.pipe(catchError(error => observableThrowError(error)));
|
|
||||||
}
|
|
||||||
deleteScanner(scanner: Scanner): Observable<any> {
|
|
||||||
return this.http.delete(`${ CURRENT_BASE_HREF }/scanners/${scanner.uuid}`)
|
|
||||||
.pipe(catchError(error => observableThrowError(error)));
|
|
||||||
}
|
|
||||||
deleteScanners(scanners: Scanner[]): Observable<any> {
|
|
||||||
let observableLists: any[] = [];
|
|
||||||
if (scanners && scanners.length > 0) {
|
|
||||||
scanners.forEach(scanner => {
|
|
||||||
observableLists.push(this.deleteScanner(scanner));
|
|
||||||
});
|
|
||||||
return forkJoin(...observableLists);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
getProjectScanner(projectId: number): Observable<Scanner> {
|
|
||||||
return this.http.get(`${ CURRENT_BASE_HREF }/projects/${projectId}/scanner`)
|
|
||||||
.pipe(map(response => response as Scanner))
|
|
||||||
.pipe(catchError(error => observableThrowError(error)));
|
|
||||||
}
|
|
||||||
updateProjectScanner(projectId: number , uid: string): Observable<any> {
|
|
||||||
return this.http.put(`${ CURRENT_BASE_HREF }/projects/${projectId}/scanner` , {uuid: uid})
|
|
||||||
.pipe(catchError(error => observableThrowError(error)));
|
|
||||||
}
|
|
||||||
getScannerMetadata(uid: string): Observable<ScannerMetadata> {
|
|
||||||
return this.http.get(`${ CURRENT_BASE_HREF }/scanners/${uid}/metadata`)
|
|
||||||
.pipe(map(response => response as ScannerMetadata))
|
|
||||||
.pipe(catchError(error => observableThrowError(error)));
|
|
||||||
}
|
|
||||||
setAsDefault(uid: string): Observable<any> {
|
|
||||||
return this.http.patch(`${ CURRENT_BASE_HREF }/scanners/${uid}`, {is_default: true} )
|
|
||||||
.pipe(catchError(error => observableThrowError(error)));
|
|
||||||
}
|
|
||||||
getProjectScanners(projectId: number) {
|
|
||||||
return this.http.get(`${ CURRENT_BASE_HREF }/projects/${projectId}/scanner/candidates`)
|
|
||||||
.pipe(map(response => response as Scanner[]))
|
|
||||||
.pipe(catchError(error => observableThrowError(error)));
|
|
||||||
}
|
|
||||||
}
|
|
@ -4,10 +4,10 @@ import { FormBuilder } from "@angular/forms";
|
|||||||
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||||
import { ClarityModule } from "@clr/angular";
|
import { ClarityModule } from "@clr/angular";
|
||||||
import { SharedTestingModule } from "../../../../../shared/shared.module";
|
import { SharedTestingModule } from "../../../../../shared/shared.module";
|
||||||
import { ConfigScannerService } from "../config-scanner.service";
|
|
||||||
import { of } from "rxjs";
|
import { of } from "rxjs";
|
||||||
import { TranslateService } from "@ngx-translate/core";
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
import { delay } from "rxjs/operators";
|
import { delay } from "rxjs/operators";
|
||||||
|
import { ScannerService } from "../../../../../../../ng-swagger-gen/services/scanner.service";
|
||||||
|
|
||||||
describe('NewScannerFormComponent', () => {
|
describe('NewScannerFormComponent', () => {
|
||||||
let mockScanner1 = {
|
let mockScanner1 = {
|
||||||
@ -19,7 +19,7 @@ describe('NewScannerFormComponent', () => {
|
|||||||
let component: NewScannerFormComponent;
|
let component: NewScannerFormComponent;
|
||||||
let fixture: ComponentFixture<NewScannerFormComponent>;
|
let fixture: ComponentFixture<NewScannerFormComponent>;
|
||||||
let fakedConfigScannerService = {
|
let fakedConfigScannerService = {
|
||||||
getScannersByName() {
|
listScanners() {
|
||||||
return of([mockScanner1]).pipe(delay(500));
|
return of([mockScanner1]).pipe(delay(500));
|
||||||
},
|
},
|
||||||
getScannersByEndpointUrl() {
|
getScannersByEndpointUrl() {
|
||||||
@ -37,7 +37,7 @@ describe('NewScannerFormComponent', () => {
|
|||||||
providers: [
|
providers: [
|
||||||
FormBuilder,
|
FormBuilder,
|
||||||
TranslateService,
|
TranslateService,
|
||||||
{ provide: ConfigScannerService, useValue: fakedConfigScannerService },
|
{ provide: ScannerService, useValue: fakedConfigScannerService },
|
||||||
// open auto detect
|
// open auto detect
|
||||||
{ provide: ComponentFixtureAutoDetect, useValue: true }
|
{ provide: ComponentFixtureAutoDetect, useValue: true }
|
||||||
]
|
]
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
|
import { FormBuilder, FormGroup, Validators } from "@angular/forms";
|
||||||
import { fromEvent } from "rxjs";
|
import { fromEvent } from "rxjs";
|
||||||
import { debounceTime, distinctUntilChanged, filter, finalize, map, switchMap } from "rxjs/operators";
|
import { debounceTime, distinctUntilChanged, filter, finalize, map, switchMap } from "rxjs/operators";
|
||||||
import { ConfigScannerService } from "../config-scanner.service";
|
import { ScannerService } from "../../../../../../../ng-swagger-gen/services/scanner.service";
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -48,7 +48,7 @@ export class NewScannerFormComponent implements OnInit, AfterViewInit, OnDestro
|
|||||||
isEdit: boolean;
|
isEdit: boolean;
|
||||||
@ViewChild('name') scannerName: ElementRef;
|
@ViewChild('name') scannerName: ElementRef;
|
||||||
@ViewChild('endpointUrl') scannerEndpointUrl: ElementRef;
|
@ViewChild('endpointUrl') scannerEndpointUrl: ElementRef;
|
||||||
constructor(private fb: FormBuilder, private scannerService: ConfigScannerService) {
|
constructor(private fb: FormBuilder, private scannerService: ScannerService) {
|
||||||
}
|
}
|
||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
if (!this.checkNameSubscribe) {
|
if (!this.checkNameSubscribe) {
|
||||||
@ -65,7 +65,9 @@ export class NewScannerFormComponent implements OnInit, AfterViewInit, OnDestro
|
|||||||
switchMap((name) => {
|
switchMap((name) => {
|
||||||
this.isNameExisting = false;
|
this.isNameExisting = false;
|
||||||
this.checkOnGoing = true;
|
this.checkOnGoing = true;
|
||||||
return this.scannerService.getScannersByName(name)
|
return this.scannerService.listScanners({
|
||||||
|
q: encodeURIComponent(`name=${name}`)
|
||||||
|
})
|
||||||
.pipe(finalize(() => this.checkOnGoing = false));
|
.pipe(finalize(() => this.checkOnGoing = false));
|
||||||
})).subscribe(response => {
|
})).subscribe(response => {
|
||||||
if (response && response.length > 0) {
|
if (response && response.length > 0) {
|
||||||
@ -94,7 +96,9 @@ export class NewScannerFormComponent implements OnInit, AfterViewInit, OnDestro
|
|||||||
switchMap((endpointUrl) => {
|
switchMap((endpointUrl) => {
|
||||||
this.isEndpointUrlExisting = false;
|
this.isEndpointUrlExisting = false;
|
||||||
this.checkEndpointOnGoing = true;
|
this.checkEndpointOnGoing = true;
|
||||||
return this.scannerService.getScannersByEndpointUrl(endpointUrl)
|
return this.scannerService.listScanners({
|
||||||
|
q: encodeURIComponent(`url=${endpointUrl}`)
|
||||||
|
})
|
||||||
.pipe(finalize(() => this.checkEndpointOnGoing = false));
|
.pipe(finalize(() => this.checkEndpointOnGoing = false));
|
||||||
})).subscribe(response => {
|
})).subscribe(response => {
|
||||||
if (response && response.length > 0) {
|
if (response && response.length > 0) {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { ComponentFixture, ComponentFixtureAutoDetect, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
import { ComponentFixture, ComponentFixtureAutoDetect, fakeAsync, TestBed, tick } from '@angular/core/testing';
|
||||||
import { ClrLoadingState } from "@clr/angular";
|
import { ClrLoadingState } from "@clr/angular";
|
||||||
import { ConfigScannerService } from "../config-scanner.service";
|
|
||||||
import { NewScannerModalComponent } from "./new-scanner-modal.component";
|
import { NewScannerModalComponent } from "./new-scanner-modal.component";
|
||||||
import { MessageHandlerService } from "../../../../../shared/services/message-handler.service";
|
import { MessageHandlerService } from "../../../../../shared/services/message-handler.service";
|
||||||
import { NewScannerFormComponent } from "../new-scanner-form/new-scanner-form.component";
|
import { NewScannerFormComponent } from "../new-scanner-form/new-scanner-form.component";
|
||||||
@ -9,6 +8,7 @@ import { of, Subscription } from "rxjs";
|
|||||||
import { delay } from "rxjs/operators";
|
import { delay } from "rxjs/operators";
|
||||||
import { SharedTestingModule } from "../../../../../shared/shared.module";
|
import { SharedTestingModule } from "../../../../../shared/shared.module";
|
||||||
import { Scanner } from "../scanner";
|
import { Scanner } from "../scanner";
|
||||||
|
import { ScannerService } from "../../../../../../../ng-swagger-gen/services/scanner.service";
|
||||||
|
|
||||||
describe('NewScannerModalComponent', () => {
|
describe('NewScannerModalComponent', () => {
|
||||||
let component: NewScannerModalComponent;
|
let component: NewScannerModalComponent;
|
||||||
@ -21,13 +21,13 @@ describe('NewScannerModalComponent', () => {
|
|||||||
auth: "",
|
auth: "",
|
||||||
};
|
};
|
||||||
let fakedConfigScannerService = {
|
let fakedConfigScannerService = {
|
||||||
getScannersByName() {
|
listScanners() {
|
||||||
return of([mockScanner1]);
|
return of([mockScanner1]);
|
||||||
},
|
},
|
||||||
testEndpointUrl() {
|
pingScanner() {
|
||||||
return of(true).pipe(delay(200));
|
return of(true).pipe(delay(200));
|
||||||
},
|
},
|
||||||
addScanner() {
|
createScanner() {
|
||||||
return of(true).pipe(delay(200));
|
return of(true).pipe(delay(200));
|
||||||
},
|
},
|
||||||
updateScanner() {
|
updateScanner() {
|
||||||
@ -45,7 +45,7 @@ describe('NewScannerModalComponent', () => {
|
|||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
MessageHandlerService,
|
MessageHandlerService,
|
||||||
{ provide: ConfigScannerService, useValue: fakedConfigScannerService },
|
{ provide: ScannerService, useValue: fakedConfigScannerService },
|
||||||
FormBuilder,
|
FormBuilder,
|
||||||
// open auto detect
|
// open auto detect
|
||||||
{ provide: ComponentFixtureAutoDetect, useValue: true }
|
{ provide: ComponentFixtureAutoDetect, useValue: true }
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
import { Component, EventEmitter, Output, ViewChild } from '@angular/core';
|
import { Component, EventEmitter, Output, ViewChild } from '@angular/core';
|
||||||
import { Scanner } from "../scanner";
|
import { Scanner } from "../scanner";
|
||||||
import { NewScannerFormComponent } from "../new-scanner-form/new-scanner-form.component";
|
import { NewScannerFormComponent } from "../new-scanner-form/new-scanner-form.component";
|
||||||
import { ConfigScannerService } from "../config-scanner.service";
|
|
||||||
import { ClrLoadingState } from "@clr/angular";
|
import { ClrLoadingState } from "@clr/angular";
|
||||||
import { finalize } from "rxjs/operators";
|
import { finalize } from "rxjs/operators";
|
||||||
import { MessageHandlerService } from "../../../../../shared/services/message-handler.service";
|
import { MessageHandlerService } from "../../../../../shared/services/message-handler.service";
|
||||||
import { TranslateService } from "@ngx-translate/core";
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
import { InlineAlertComponent } from "../../../../../shared/components/inline-alert/inline-alert.component";
|
import { InlineAlertComponent } from "../../../../../shared/components/inline-alert/inline-alert.component";
|
||||||
|
import { ScannerService } from "../../../../../../../ng-swagger-gen/services/scanner.service";
|
||||||
|
import { ScannerRegistrationReq } from "../../../../../../../ng-swagger-gen/models/scanner-registration-req";
|
||||||
|
import { clone } from "../../../../../shared/units/utils";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "new-scanner-modal",
|
selector: "new-scanner-modal",
|
||||||
@ -29,7 +31,7 @@ export class NewScannerModalComponent {
|
|||||||
editScanner: Scanner;
|
editScanner: Scanner;
|
||||||
@ViewChild(InlineAlertComponent) inlineAlert: InlineAlertComponent;
|
@ViewChild(InlineAlertComponent) inlineAlert: InlineAlertComponent;
|
||||||
constructor(
|
constructor(
|
||||||
private configScannerService: ConfigScannerService,
|
private configScannerService: ScannerService,
|
||||||
private msgHandler: MessageHandlerService,
|
private msgHandler: MessageHandlerService,
|
||||||
private translate: TranslateService,
|
private translate: TranslateService,
|
||||||
) {}
|
) {}
|
||||||
@ -47,8 +49,8 @@ export class NewScannerModalComponent {
|
|||||||
create(): void {
|
create(): void {
|
||||||
this.onSaving = true;
|
this.onSaving = true;
|
||||||
this.saveBtnState = ClrLoadingState.LOADING;
|
this.saveBtnState = ClrLoadingState.LOADING;
|
||||||
let scanner: Scanner = new Scanner();
|
const scanner: ScannerRegistrationReq = {name: "", url: ""};
|
||||||
let value = this.newScannerFormComponent.newScannerForm.value;
|
const value = this.newScannerFormComponent.newScannerForm.value;
|
||||||
scanner.name = value.name;
|
scanner.name = value.name;
|
||||||
scanner.description = value.description;
|
scanner.description = value.description;
|
||||||
scanner.url = value.url;
|
scanner.url = value.url;
|
||||||
@ -66,7 +68,9 @@ export class NewScannerModalComponent {
|
|||||||
}
|
}
|
||||||
scanner.skip_certVerify = !!value.skipCertVerify;
|
scanner.skip_certVerify = !!value.skipCertVerify;
|
||||||
scanner.use_internal_addr = !!value.useInner;
|
scanner.use_internal_addr = !!value.useInner;
|
||||||
this.configScannerService.addScanner(scanner)
|
this.configScannerService.createScanner({
|
||||||
|
registration: scanner
|
||||||
|
})
|
||||||
.pipe(finalize(() => this.onSaving = false))
|
.pipe(finalize(() => this.onSaving = false))
|
||||||
.subscribe(response => {
|
.subscribe(response => {
|
||||||
this.close();
|
this.close();
|
||||||
@ -176,8 +180,8 @@ export class NewScannerModalComponent {
|
|||||||
onTestEndpoint() {
|
onTestEndpoint() {
|
||||||
this.onTesting = true;
|
this.onTesting = true;
|
||||||
this.checkBtnState = ClrLoadingState.LOADING;
|
this.checkBtnState = ClrLoadingState.LOADING;
|
||||||
let scanner: Scanner = new Scanner();
|
const scanner: ScannerRegistrationReq = {name: "", url: ""};
|
||||||
let value = this.newScannerFormComponent.newScannerForm.value;
|
const value = this.newScannerFormComponent.newScannerForm.value;
|
||||||
scanner.name = value.name;
|
scanner.name = value.name;
|
||||||
scanner.description = value.description;
|
scanner.description = value.description;
|
||||||
scanner.url = value.url;
|
scanner.url = value.url;
|
||||||
@ -195,7 +199,9 @@ export class NewScannerModalComponent {
|
|||||||
}
|
}
|
||||||
scanner.skip_certVerify = !!value.skipCertVerify;
|
scanner.skip_certVerify = !!value.skipCertVerify;
|
||||||
scanner.use_internal_addr = !!value.useInner;
|
scanner.use_internal_addr = !!value.useInner;
|
||||||
this.configScannerService.testEndpointUrl(scanner)
|
this.configScannerService.pingScanner({
|
||||||
|
settings: scanner
|
||||||
|
})
|
||||||
.pipe(finalize(() => this.onTesting = false))
|
.pipe(finalize(() => this.onTesting = false))
|
||||||
.subscribe(response => {
|
.subscribe(response => {
|
||||||
this.inlineAlert.showInlineSuccess({
|
this.inlineAlert.showInlineSuccess({
|
||||||
@ -236,7 +242,11 @@ export class NewScannerModalComponent {
|
|||||||
this.editScanner.skip_certVerify = !!value.skipCertVerify;
|
this.editScanner.skip_certVerify = !!value.skipCertVerify;
|
||||||
this.editScanner.use_internal_addr = !!value.useInner;
|
this.editScanner.use_internal_addr = !!value.useInner;
|
||||||
this.editScanner.uuid = this.uid;
|
this.editScanner.uuid = this.uid;
|
||||||
this.configScannerService.updateScanner(this.editScanner)
|
const scanner: ScannerRegistrationReq = clone(this.editScanner);
|
||||||
|
this.configScannerService.updateScanner({
|
||||||
|
registrationId: this.editScanner.uuid,
|
||||||
|
registration: scanner
|
||||||
|
})
|
||||||
.pipe(finalize(() => this.onSaving = false))
|
.pipe(finalize(() => this.onSaving = false))
|
||||||
.subscribe(response => {
|
.subscribe(response => {
|
||||||
this.close();
|
this.close();
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
export class ScannerMetadata {
|
|
||||||
scanner?: {
|
|
||||||
name?: string;
|
|
||||||
vendor?: string;
|
|
||||||
version?: string;
|
|
||||||
};
|
|
||||||
capabilities?: [{
|
|
||||||
consumes_mime_types?: Array<string>;
|
|
||||||
produces_mime_types?: Array<string>;
|
|
||||||
}];
|
|
||||||
properties?: {
|
|
||||||
[key: string]: string;
|
|
||||||
};
|
|
||||||
constructor() {
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,10 +2,10 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
|||||||
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||||
import { ClarityModule } from "@clr/angular";
|
import { ClarityModule } from "@clr/angular";
|
||||||
import { SharedTestingModule } from "../../../../../shared/shared.module";
|
import { SharedTestingModule } from "../../../../../shared/shared.module";
|
||||||
import { ConfigScannerService } from "../config-scanner.service";
|
|
||||||
import { of } from "rxjs";
|
import { of } from "rxjs";
|
||||||
import { ScannerMetadataComponent } from "./scanner-metadata.component";
|
import { ScannerMetadataComponent } from "./scanner-metadata.component";
|
||||||
import { ErrorHandler } from "../../../../../shared/units/error-handler";
|
import { ErrorHandler } from "../../../../../shared/units/error-handler";
|
||||||
|
import { ScannerService } from "../../../../../../../ng-swagger-gen/services/scanner.service";
|
||||||
|
|
||||||
describe('ScannerMetadataComponent', () => {
|
describe('ScannerMetadataComponent', () => {
|
||||||
let mockScannerMetadata = {
|
let mockScannerMetadata = {
|
||||||
@ -38,7 +38,7 @@ describe('ScannerMetadataComponent', () => {
|
|||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
ErrorHandler,
|
ErrorHandler,
|
||||||
{ provide: ConfigScannerService, useValue: fakedConfigScannerService },
|
{ provide: ScannerService, useValue: fakedConfigScannerService },
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -2,12 +2,12 @@ import {
|
|||||||
Component, Input,
|
Component, Input,
|
||||||
OnInit
|
OnInit
|
||||||
} from "@angular/core";
|
} from "@angular/core";
|
||||||
import { ConfigScannerService } from "../config-scanner.service";
|
|
||||||
import { finalize } from "rxjs/operators";
|
import { finalize } from "rxjs/operators";
|
||||||
import { ScannerMetadata } from "../scanner-metadata";
|
|
||||||
import { DatePipe } from "@angular/common";
|
import { DatePipe } from "@angular/common";
|
||||||
import { ErrorHandler } from "../../../../../shared/units/error-handler";
|
import { ErrorHandler } from "../../../../../shared/units/error-handler";
|
||||||
import {DATABASE_NEXT_UPDATE_PROPERTY, DATABASE_UPDATED_PROPERTY} from "../../../../../shared/units/utils";
|
import { DATABASE_NEXT_UPDATE_PROPERTY, DATABASE_UPDATED_PROPERTY } from "../../../../../shared/units/utils";
|
||||||
|
import { ScannerService } from "../../../../../../../ng-swagger-gen/services/scanner.service";
|
||||||
|
import { ScannerAdapterMetadata } from "../../../../../../../ng-swagger-gen/models/scanner-adapter-metadata";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'scanner-metadata',
|
selector: 'scanner-metadata',
|
||||||
@ -17,13 +17,15 @@ import {DATABASE_NEXT_UPDATE_PROPERTY, DATABASE_UPDATED_PROPERTY} from "../../..
|
|||||||
export class ScannerMetadataComponent implements OnInit {
|
export class ScannerMetadataComponent implements OnInit {
|
||||||
@Input() uid: string;
|
@Input() uid: string;
|
||||||
loading: boolean = false;
|
loading: boolean = false;
|
||||||
scannerMetadata: ScannerMetadata;
|
scannerMetadata: ScannerAdapterMetadata;
|
||||||
constructor(private configScannerService: ConfigScannerService,
|
constructor(private configScannerService: ScannerService,
|
||||||
private errorHandler: ErrorHandler) {
|
private errorHandler: ErrorHandler) {
|
||||||
}
|
}
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.configScannerService.getScannerMetadata(this.uid)
|
this.configScannerService.getScannerMetadata({
|
||||||
|
registrationId: this.uid
|
||||||
|
})
|
||||||
.pipe(finalize(() => this.loading = false))
|
.pipe(finalize(() => this.loading = false))
|
||||||
.subscribe(response => {
|
.subscribe(response => {
|
||||||
this.scannerMetadata = response;
|
this.scannerMetadata = response;
|
||||||
|
@ -1,24 +1,9 @@
|
|||||||
import { ScannerMetadata } from "./scanner-metadata";
|
import { ScannerRegistration } from "../../../../../../ng-swagger-gen/models/scanner-registration";
|
||||||
|
import { ScannerAdapterMetadata } from "../../../../../../ng-swagger-gen/models/scanner-adapter-metadata";
|
||||||
|
|
||||||
export class Scanner {
|
export interface Scanner extends ScannerRegistration {
|
||||||
name?: string;
|
metadata?: ScannerAdapterMetadata;
|
||||||
description?: string;
|
|
||||||
uuid?: string;
|
|
||||||
url?: string;
|
|
||||||
auth?: string;
|
|
||||||
access_credential?: string;
|
|
||||||
adapter?: string;
|
|
||||||
disabled?: boolean;
|
|
||||||
is_default?: boolean;
|
|
||||||
skip_certVerify?: boolean;
|
|
||||||
use_internal_addr?: boolean;
|
|
||||||
create_time?: any;
|
|
||||||
update_time?: any;
|
|
||||||
vendor?: string;
|
|
||||||
version?: string;
|
|
||||||
metadata?: ScannerMetadata;
|
|
||||||
loadingMetadata?: boolean;
|
loadingMetadata?: boolean;
|
||||||
health?: string;
|
|
||||||
constructor() {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const SCANNERS_DOC: string = "https://goharbor.io/blog/harbor-1.10-release/#vulnerability-scanning-with-pluggable-scanners";
|
||||||
|
@ -3,11 +3,13 @@ import { of } from "rxjs";
|
|||||||
import { TranslateService } from "@ngx-translate/core";
|
import { TranslateService } from "@ngx-translate/core";
|
||||||
import { MessageHandlerService } from "../../../shared/services/message-handler.service";
|
import { MessageHandlerService } from "../../../shared/services/message-handler.service";
|
||||||
import { ScannerComponent } from "./scanner.component";
|
import { ScannerComponent } from "./scanner.component";
|
||||||
import { ConfigScannerService } from "../../left-side-nav/interrogation-services/scanner/config-scanner.service";
|
|
||||||
import { SharedTestingModule } from "../../../shared/shared.module";
|
import { SharedTestingModule } from "../../../shared/shared.module";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
import { Scanner } from "../../left-side-nav/interrogation-services/scanner/scanner";
|
import { Scanner } from "../../left-side-nav/interrogation-services/scanner/scanner";
|
||||||
import { ErrorHandler } from "../../../shared/units/error-handler";
|
import { ErrorHandler } from "../../../shared/units/error-handler";
|
||||||
|
import { ProjectService } from "../../../../../ng-swagger-gen/services/project.service";
|
||||||
|
import { HttpHeaders, HttpResponse } from "@angular/common/http";
|
||||||
|
import { Registry } from "../../../../../ng-swagger-gen/models/registry";
|
||||||
|
|
||||||
describe('ScannerComponent', () => {
|
describe('ScannerComponent', () => {
|
||||||
const mockScanner1: Scanner = {
|
const mockScanner1: Scanner = {
|
||||||
@ -28,17 +30,21 @@ describe('ScannerComponent', () => {
|
|||||||
};
|
};
|
||||||
let component: ScannerComponent;
|
let component: ScannerComponent;
|
||||||
let fixture: ComponentFixture<ScannerComponent>;
|
let fixture: ComponentFixture<ScannerComponent>;
|
||||||
let fakedConfigScannerService = {
|
let fakedProjectService = {
|
||||||
getProjectScanner() {
|
getScannerOfProject() {
|
||||||
return of(mockScanner1);
|
return of(mockScanner1);
|
||||||
},
|
},
|
||||||
getScanners() {
|
listScannerCandidatesOfProject() {
|
||||||
return of([mockScanner1, mockScanner2]);
|
return of([mockScanner1, mockScanner2]);
|
||||||
},
|
},
|
||||||
getProjectScanners() {
|
listScannerCandidatesOfProjectResponse() {
|
||||||
return of([mockScanner1, mockScanner2]);
|
const response: HttpResponse<Array<Registry>> = new HttpResponse<Array<Registry>>({
|
||||||
|
headers: new HttpHeaders({'x-total-count': [mockScanner1, mockScanner2].length.toString()}),
|
||||||
|
body: [mockScanner1, mockScanner2]
|
||||||
|
});
|
||||||
|
return of(response);
|
||||||
},
|
},
|
||||||
updateProjectScanner() {
|
setScannerOfProject() {
|
||||||
return of(true);
|
return of(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -63,8 +69,8 @@ describe('ScannerComponent', () => {
|
|||||||
TranslateService,
|
TranslateService,
|
||||||
MessageHandlerService,
|
MessageHandlerService,
|
||||||
ErrorHandler,
|
ErrorHandler,
|
||||||
{provide: ActivatedRoute, useValue: fakedRoute},
|
{ provide: ActivatedRoute, useValue: fakedRoute },
|
||||||
{ provide: ConfigScannerService, useValue: fakedConfigScannerService },
|
{ provide: ProjectService, useValue: fakedProjectService },
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
.compileComponents();
|
.compileComponents();
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
import { Component, OnInit, ViewChild } from "@angular/core";
|
import { Component, OnInit, ViewChild } from "@angular/core";
|
||||||
import { ConfigScannerService } from "../../left-side-nav/interrogation-services/scanner/config-scanner.service";
|
|
||||||
import { Scanner } from "../../left-side-nav/interrogation-services/scanner/scanner";
|
import { Scanner } from "../../left-side-nav/interrogation-services/scanner/scanner";
|
||||||
import { MessageHandlerService } from "../../../shared/services/message-handler.service";
|
import { MessageHandlerService } from "../../../shared/services/message-handler.service";
|
||||||
import { ActivatedRoute } from "@angular/router";
|
import { ActivatedRoute } from "@angular/router";
|
||||||
@ -22,6 +21,10 @@ import { TranslateService } from "@ngx-translate/core";
|
|||||||
import { ErrorHandler } from "../../../shared/units/error-handler";
|
import { ErrorHandler } from "../../../shared/units/error-handler";
|
||||||
import { UserPermissionService, USERSTATICPERMISSION } from "../../../shared/services";
|
import { UserPermissionService, USERSTATICPERMISSION } from "../../../shared/services";
|
||||||
import { InlineAlertComponent } from "../../../shared/components/inline-alert/inline-alert.component";
|
import { InlineAlertComponent } from "../../../shared/components/inline-alert/inline-alert.component";
|
||||||
|
import { ProjectService } from "../../../../../ng-swagger-gen/services/project.service";
|
||||||
|
import { DEFAULT_PAGE_SIZE } from "../../../shared/units/utils";
|
||||||
|
import { forkJoin, Observable } from "rxjs";
|
||||||
|
import { Project } from "../../../../../ng-swagger-gen/models/project";
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -40,12 +43,12 @@ export class ScannerComponent implements OnInit {
|
|||||||
onSaving: boolean = false;
|
onSaving: boolean = false;
|
||||||
hasCreatePermission: boolean = false;
|
hasCreatePermission: boolean = false;
|
||||||
@ViewChild(InlineAlertComponent) inlineAlert: InlineAlertComponent;
|
@ViewChild(InlineAlertComponent) inlineAlert: InlineAlertComponent;
|
||||||
constructor( private configScannerService: ConfigScannerService,
|
constructor( private msgHandler: MessageHandlerService,
|
||||||
private msgHandler: MessageHandlerService,
|
|
||||||
private errorHandler: ErrorHandler,
|
private errorHandler: ErrorHandler,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
private userPermissionService: UserPermissionService,
|
private userPermissionService: UserPermissionService,
|
||||||
private translate: TranslateService
|
private translate: TranslateService,
|
||||||
|
private projectService: ProjectService
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
@ -70,7 +73,9 @@ export class ScannerComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
getScanner(isCheckHealth?: boolean) {
|
getScanner(isCheckHealth?: boolean) {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
this.configScannerService.getProjectScanner(this.projectId)
|
this.projectService.getScannerOfProject({
|
||||||
|
projectNameOrId: this.projectId.toString()
|
||||||
|
})
|
||||||
.pipe(finalize(() => this.loading = false))
|
.pipe(finalize(() => this.loading = false))
|
||||||
.subscribe(response => {
|
.subscribe(response => {
|
||||||
if (response && "{}" !== JSON.stringify(response)) {
|
if (response && "{}" !== JSON.stringify(response)) {
|
||||||
@ -89,14 +94,44 @@ export class ScannerComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
getScanners() {
|
getScanners() {
|
||||||
if (this.projectId) {
|
if (this.projectId) {
|
||||||
this.configScannerService.getProjectScanners(this.projectId)
|
this.projectService.listScannerCandidatesOfProjectResponse({
|
||||||
.subscribe(response => {
|
projectNameOrId: this.projectId.toString(),
|
||||||
if (response && response.length > 0) {
|
page: 1,
|
||||||
this.scanners = response.filter(scanner => {
|
pageSize: DEFAULT_PAGE_SIZE
|
||||||
return !scanner.disabled;
|
}).subscribe(response => {
|
||||||
|
if (response.headers) {
|
||||||
|
const xHeader: string = response.headers.get("X-Total-Count");
|
||||||
|
const totalCount = parseInt(xHeader, 0);
|
||||||
|
let arr = response.body || [];
|
||||||
|
if (totalCount <= DEFAULT_PAGE_SIZE) { // already gotten all scanners
|
||||||
|
if (arr && arr.length > 0) {
|
||||||
|
this.scanners = arr.filter(scanner => {
|
||||||
|
return !scanner.disabled;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else { // get all the scanners in specified times
|
||||||
|
const times: number = Math.ceil(totalCount / DEFAULT_PAGE_SIZE);
|
||||||
|
const observableList: Observable<Project[]>[] = [];
|
||||||
|
for (let i = 2; i <= times; i++) {
|
||||||
|
observableList.push(this.projectService.listScannerCandidatesOfProject({
|
||||||
|
page: i,
|
||||||
|
pageSize: DEFAULT_PAGE_SIZE,
|
||||||
|
projectNameOrId: this.projectId.toString()
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
forkJoin(observableList).subscribe(res => {
|
||||||
|
if (res && res.length) {
|
||||||
|
res.forEach(item => {
|
||||||
|
arr = arr.concat(item);
|
||||||
|
});
|
||||||
|
this.scanners = arr.filter(scanner => {
|
||||||
|
return !scanner.disabled;
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
close() {
|
close() {
|
||||||
@ -118,8 +153,12 @@ export class ScannerComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
save() {
|
save() {
|
||||||
this.saveBtnState = ClrLoadingState.LOADING;
|
this.saveBtnState = ClrLoadingState.LOADING;
|
||||||
this.configScannerService.updateProjectScanner(this.projectId, this.selectedScanner.uuid)
|
this.projectService.setScannerOfProject({
|
||||||
.subscribe(response => {
|
projectNameOrId: this.projectId.toString(),
|
||||||
|
payload: {
|
||||||
|
uuid: this.selectedScanner.uuid
|
||||||
|
}
|
||||||
|
}).subscribe(response => {
|
||||||
this.close();
|
this.close();
|
||||||
this.msgHandler.showSuccess('Update Success');
|
this.msgHandler.showSuccess('Update Success');
|
||||||
this.getScanner(true);
|
this.getScanner(true);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { delUrlParam, isSameArrayValue, isSameObject } from "./utils";
|
import { delUrlParam, getQueryString, getSortingString, isSameArrayValue, isSameObject } from "./utils";
|
||||||
|
import { ClrDatagridStateInterface } from "@clr/angular";
|
||||||
|
|
||||||
describe('functions in utils.ts should work', () => {
|
describe('functions in utils.ts should work', () => {
|
||||||
it('function isSameArrayValue() should work', () => {
|
it('function isSameArrayValue() should work', () => {
|
||||||
@ -30,4 +31,26 @@ describe('functions in utils.ts should work', () => {
|
|||||||
expect(delUrlParam('http://test.com', 'param2')).toEqual('http://test.com');
|
expect(delUrlParam('http://test.com', 'param2')).toEqual('http://test.com');
|
||||||
expect(delUrlParam('http://test.com?param2', 'param2')).toEqual('http://test.com');
|
expect(delUrlParam('http://test.com?param2', 'param2')).toEqual('http://test.com');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('function getSortingString() should work', () => {
|
||||||
|
expect(getSortingString).toBeTruthy();
|
||||||
|
const state: ClrDatagridStateInterface = {
|
||||||
|
sort: {
|
||||||
|
by: 'name',
|
||||||
|
reverse: true
|
||||||
|
}
|
||||||
|
};
|
||||||
|
expect(getSortingString(state)).toEqual('-name');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('function getQueryString() should work', () => {
|
||||||
|
expect(getQueryString).toBeTruthy();
|
||||||
|
const state: ClrDatagridStateInterface = {
|
||||||
|
filters: [
|
||||||
|
{property: 'name', value: 'test'},
|
||||||
|
{property: 'url', value: 'http://test.com'},
|
||||||
|
]
|
||||||
|
};
|
||||||
|
expect(getQueryString(state)).toEqual(encodeURIComponent('name=~test,url=~http://test.com'));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -631,6 +631,10 @@ export function deleteEmptyKey(obj: Object): void {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get sorting string from current state
|
||||||
|
* @param state
|
||||||
|
*/
|
||||||
export function getSortingString(state: ClrDatagridStateInterface): string {
|
export function getSortingString(state: ClrDatagridStateInterface): string {
|
||||||
if (state && state.sort && state.sort.by) {
|
if (state && state.sort && state.sort.by) {
|
||||||
let sortString: string;
|
let sortString: string;
|
||||||
@ -647,6 +651,32 @@ export function getSortingString(state: ClrDatagridStateInterface): string {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get query string from current state, rules as below:
|
||||||
|
* query string format: q=k=v,k=~v,k=[min~max],k={v1 v2 v3},k=(v1 v2 v3)
|
||||||
|
* exact match: k=v
|
||||||
|
* fuzzy match: k=~v
|
||||||
|
* range: k=[min~max]
|
||||||
|
* or list: k={v1 v2 v3}
|
||||||
|
* and list: k=(v1 v2 v3)
|
||||||
|
* @param state
|
||||||
|
*/
|
||||||
|
export function getQueryString(state: ClrDatagridStateInterface): string {
|
||||||
|
let str: string = '';
|
||||||
|
if (state && state.filters && state.filters.length) {
|
||||||
|
state.filters.forEach(item => {
|
||||||
|
if (str) {
|
||||||
|
str += `,${item.property}=~${item.value}`;
|
||||||
|
} else {
|
||||||
|
str += `${item.property}=~${item.value}`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return encodeURIComponent(str);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* if two object are the same
|
* if two object are the same
|
||||||
* @param a
|
* @param a
|
||||||
|
Loading…
Reference in New Issue
Block a user