* Change vul result bar texts

* Support server-driven pagination for replication jobs
This commit is contained in:
Steven Zou 2017-08-03 15:09:13 +08:00 committed by Yan
parent b4a81db15c
commit baa863d026
14 changed files with 232 additions and 100 deletions

View File

@ -14,7 +14,7 @@ import { DatePickerComponent } from '../datetime-picker/datetime-picker.componen
import { DateValidatorDirective } from '../datetime-picker/date-validator.directive'; import { DateValidatorDirective } from '../datetime-picker/date-validator.directive';
import { FilterComponent } from '../filter/filter.component'; import { FilterComponent } from '../filter/filter.component';
import { InlineAlertComponent } from '../inline-alert/inline-alert.component'; import { InlineAlertComponent } from '../inline-alert/inline-alert.component';
import { ReplicationRule, ReplicationJob, Endpoint } from '../service/interface'; import { ReplicationRule, ReplicationJob, Endpoint, ReplicationJobItem } from '../service/interface';
import { ErrorHandler } from '../error-handler/error-handler'; import { ErrorHandler } from '../error-handler/error-handler';
import { SERVICE_CONFIG, IServiceConfig } from '../service.config'; import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
@ -71,7 +71,7 @@ describe('CreateEditRuleComponent (inline template)', ()=>{
} }
]; ];
let mockJobs: ReplicationJob[] = [ let mockJobs: ReplicationJobItem[] = [
{ {
"id": 1, "id": 1,
"status": "stopped", "status": "stopped",
@ -98,6 +98,11 @@ describe('CreateEditRuleComponent (inline template)', ()=>{
} }
]; ];
let mockJob: ReplicationJob = {
metadata: {xTotalCount: 3},
data: mockJobs
};
let mockEndpoints: Endpoint[] = [ let mockEndpoints: Endpoint[] = [
{ {
"id": 1, "id": 1,
@ -205,7 +210,7 @@ describe('CreateEditRuleComponent (inline template)', ()=>{
spyRules = spyOn(replicationService, 'getReplicationRules').and.returnValues(Promise.resolve(mockRules)); spyRules = spyOn(replicationService, 'getReplicationRules').and.returnValues(Promise.resolve(mockRules));
spyOneRule = spyOn(replicationService, 'getReplicationRule').and.returnValue(Promise.resolve(mockRule)); spyOneRule = spyOn(replicationService, 'getReplicationRule').and.returnValue(Promise.resolve(mockRule));
spyJobs = spyOn(replicationService, 'getJobs').and.returnValues(Promise.resolve(mockJobs)); spyJobs = spyOn(replicationService, 'getJobs').and.returnValues(Promise.resolve(mockJob));
fixture.detectChanges(); fixture.detectChanges();
}); });

View File

@ -46,7 +46,7 @@ export const REPLICATION_TEMPLATE: string = `
</div> </div>
</div> </div>
<div *ngIf="withReplicationJob" class="col-lg-12 col-md-12 col-sm-12 col-xs-12"> <div *ngIf="withReplicationJob" class="col-lg-12 col-md-12 col-sm-12 col-xs-12">
<clr-datagrid [clrDgLoading]="loading"> <clr-datagrid [clrDgLoading]="jobsLoading" (clrDgRefresh)="clrLoadJobs($event)">
<clr-dg-column [clrDgField]="'repository'">{{'REPLICATION.NAME' | translate}}</clr-dg-column> <clr-dg-column [clrDgField]="'repository'">{{'REPLICATION.NAME' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'status'">{{'REPLICATION.STATUS' | translate}}</clr-dg-column> <clr-dg-column [clrDgField]="'status'">{{'REPLICATION.STATUS' | translate}}</clr-dg-column>
<clr-dg-column [clrDgField]="'operation'">{{'REPLICATION.OPERATION' | translate}}</clr-dg-column> <clr-dg-column [clrDgField]="'operation'">{{'REPLICATION.OPERATION' | translate}}</clr-dg-column>
@ -54,7 +54,7 @@ export const REPLICATION_TEMPLATE: string = `
<clr-dg-column [clrDgSortBy]="updateTimeComparator">{{'REPLICATION.UPDATE_TIME' | translate}}</clr-dg-column> <clr-dg-column [clrDgSortBy]="updateTimeComparator">{{'REPLICATION.UPDATE_TIME' | translate}}</clr-dg-column>
<clr-dg-column>{{'REPLICATION.LOGS' | translate}}</clr-dg-column> <clr-dg-column>{{'REPLICATION.LOGS' | translate}}</clr-dg-column>
<clr-dg-placeholder>{{'REPLICATION.JOB_PLACEHOLDER' | translate }}</clr-dg-placeholder> <clr-dg-placeholder>{{'REPLICATION.JOB_PLACEHOLDER' | translate }}</clr-dg-placeholder>
<clr-dg-row *clrDgItems="let j of jobs" [clrDgItem]='j'> <clr-dg-row *ngFor="let j of jobs">
<clr-dg-cell>{{j.repository}}</clr-dg-cell> <clr-dg-cell>{{j.repository}}</clr-dg-cell>
<clr-dg-cell>{{j.status}}</clr-dg-cell> <clr-dg-cell>{{j.status}}</clr-dg-cell>
<clr-dg-cell>{{j.operation}}</clr-dg-cell> <clr-dg-cell>{{j.operation}}</clr-dg-cell>
@ -66,13 +66,12 @@ export const REPLICATION_TEMPLATE: string = `
<a href="javascript:void(0);" (click)="viewLog(j.id)"> <a href="javascript:void(0);" (click)="viewLog(j.id)">
<clr-icon shape="clipboard"></clr-icon> <clr-icon shape="clipboard"></clr-icon>
</a></ng-template> </a></ng-template>
</clr-dg-cell> </clr-dg-cell>
</clr-dg-row> </clr-dg-row>
<clr-dg-footer> <clr-dg-footer>
<span *ngIf="pagination.totalItems">{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'REPLICATION.OF' | translate}}</span> <span *ngIf="showPaginationIndex">{{pagination.firstItem + 1}} - {{pagination.lastItem + 1}} {{'REPLICATION.OF' | translate}}</span>
{{pagination.totalItems}} {{'REPLICATION.ITEMS' | translate}} {{pagination.totalItems}} {{'REPLICATION.ITEMS' | translate}}
<clr-dg-pagination #pagination [clrDgPageSize]="5"></clr-dg-pagination> <clr-dg-pagination #pagination [(clrDgPage)]="currentPage" [clrDgPageSize]="pageSize" [clrDgTotalItems]="totalCount"></clr-dg-pagination>
</clr-dg-footer> </clr-dg-footer>
</clr-datagrid> </clr-datagrid>
</div> </div>

View File

@ -19,7 +19,7 @@ import { SERVICE_CONFIG, IServiceConfig } from '../service.config';
import { ReplicationService, ReplicationDefaultService } from '../service/replication.service'; import { ReplicationService, ReplicationDefaultService } from '../service/replication.service';
import { EndpointService, EndpointDefaultService } from '../service/endpoint.service'; import { EndpointService, EndpointDefaultService } from '../service/endpoint.service';
import { JobLogViewerComponent } from '../job-log-viewer/job-log-viewer.component'; import { JobLogViewerComponent } from '../job-log-viewer/job-log-viewer.component';
import { JobLogService, JobLogDefaultService } from '../service/index'; import { JobLogService, JobLogDefaultService, ReplicationJobItem } from '../service/index';
describe('Replication Component (inline template)', ()=>{ describe('Replication Component (inline template)', ()=>{
@ -65,7 +65,7 @@ describe('Replication Component (inline template)', ()=>{
} }
]; ];
let mockJobs: ReplicationJob[] = [ let mockJobs: ReplicationJobItem[] = [
{ {
"id": 1, "id": 1,
"status": "error", "status": "error",
@ -95,6 +95,11 @@ describe('Replication Component (inline template)', ()=>{
} }
]; ];
let mockJob: ReplicationJob = {
metadata: {xTotalCount: 3},
data: mockJobs
};
let mockEndpoints: Endpoint[] = [ let mockEndpoints: Endpoint[] = [
{ {
"id": 1, "id": 1,
@ -200,7 +205,7 @@ describe('Replication Component (inline template)', ()=>{
replicationService = fixture.debugElement.injector.get(ReplicationService); replicationService = fixture.debugElement.injector.get(ReplicationService);
spyRules = spyOn(replicationService, 'getReplicationRules').and.returnValues(Promise.resolve(mockRules)); spyRules = spyOn(replicationService, 'getReplicationRules').and.returnValues(Promise.resolve(mockRules));
spyJobs = spyOn(replicationService, 'getJobs').and.returnValues(Promise.resolve(mockJobs)); spyJobs = spyOn(replicationService, 'getJobs').and.returnValues(Promise.resolve(mockJob));
fixture.detectChanges(); fixture.detectChanges();
fixture.whenStable().then(()=>{ fixture.whenStable().then(()=>{

View File

@ -23,9 +23,16 @@ import { ErrorHandler } from '../error-handler/error-handler';
import { ReplicationService } from '../service/replication.service'; import { ReplicationService } from '../service/replication.service';
import { RequestQueryParams } from '../service/RequestQueryParams'; import { RequestQueryParams } from '../service/RequestQueryParams';
import { ReplicationRule, ReplicationJob, Endpoint } from '../service/interface'; import { ReplicationRule, ReplicationJob, Endpoint, ReplicationJobItem } from '../service/interface';
import { toPromise, CustomComparator } from '../utils'; import {
toPromise,
CustomComparator,
DEFAULT_PAGE_SIZE,
doFiltering,
doSorting,
calculatePage
} from '../utils';
import { Comparator } from 'clarity-angular'; import { Comparator } from 'clarity-angular';
@ -33,6 +40,7 @@ import { REPLICATION_TEMPLATE } from './replication.component.html';
import { REPLICATION_STYLE } from './replication.component.css'; import { REPLICATION_STYLE } from './replication.component.css';
import { JobLogViewerComponent } from '../job-log-viewer/index'; import { JobLogViewerComponent } from '../job-log-viewer/index';
import { State } from "clarity-angular";
const ruleStatus: { [key: string]: any } = [ const ruleStatus: { [key: string]: any } = [
{ 'key': 'all', 'description': 'REPLICATION.ALL_STATUS' }, { 'key': 'all', 'description': 'REPLICATION.ALL_STATUS' },
@ -63,7 +71,7 @@ export class SearchOption {
endTime: string = ''; endTime: string = '';
endTimestamp: string = ''; endTimestamp: string = '';
page: number = 1; page: number = 1;
pageSize: number = 5; pageSize: number = DEFAULT_PAGE_SIZE;
} }
@Component({ @Component({
@ -93,10 +101,7 @@ export class ReplicationComponent implements OnInit {
rules: ReplicationRule[]; rules: ReplicationRule[];
loading: boolean; loading: boolean;
jobs: ReplicationJob[]; jobs: ReplicationJobItem[];
jobsTotalRecordCount: number;
jobsTotalPage: number;
toggleJobSearchOption = optionalSearch; toggleJobSearchOption = optionalSearch;
currentJobSearchOption: number; currentJobSearchOption: number;
@ -113,6 +118,13 @@ export class ReplicationComponent implements OnInit {
creationTimeComparator: Comparator<ReplicationJob> = new CustomComparator<ReplicationJob>('creation_time', 'date'); creationTimeComparator: Comparator<ReplicationJob> = new CustomComparator<ReplicationJob>('creation_time', 'date');
updateTimeComparator: Comparator<ReplicationJob> = new CustomComparator<ReplicationJob>('update_time', 'date'); updateTimeComparator: Comparator<ReplicationJob> = new CustomComparator<ReplicationJob>('update_time', 'date');
//Server driven pagination
currentPage: number = 1;
totalCount: number = 0;
pageSize: number = DEFAULT_PAGE_SIZE;
currentState: State;
jobsLoading: boolean = false;
constructor( constructor(
private errorHandler: ErrorHandler, private errorHandler: ErrorHandler,
private replicationService: ReplicationService, private replicationService: ReplicationService,
@ -123,6 +135,10 @@ export class ReplicationComponent implements OnInit {
return !this.readonly && this.projectId ? true : false; return !this.readonly && this.projectId ? true : false;
} }
public get showPaginationIndex(): boolean {
return this.totalCount > 0;
}
ngOnInit() { ngOnInit() {
this.currentRuleStatus = this.ruleStatus[0]; this.currentRuleStatus = this.ruleStatus[0];
this.currentJobStatus = this.jobStatus[0]; this.currentJobStatus = this.jobStatus[0];
@ -143,32 +159,78 @@ export class ReplicationComponent implements OnInit {
} }
} }
fetchReplicationJobs() { //Server driven data loading
clrLoadJobs(state: State): void {
if (!state || !state.page || !this.search.ruleId) {
return;
}
this.currentState = state;
let pageNumber: number = calculatePage(state);
if (pageNumber <= 0) { pageNumber = 1; }
let params: RequestQueryParams = new RequestQueryParams(); let params: RequestQueryParams = new RequestQueryParams();
params.set('status', this.search.status); //Pagination
params.set('repository', this.search.repoName); params.set("page", '' + pageNumber);
params.set('start_time', this.search.startTimestamp); params.set("page_size", '' + this.pageSize);
params.set('end_time', this.search.endTimestamp); //Search by status
if (this.search.status.trim()) {
params.set('status', this.search.status);
}
//Search by repository
if (this.search.repoName.trim()) {
params.set('repository', this.search.repoName);
}
//Search by timestamps
if (this.search.startTimestamp.trim()) {
params.set('start_time', this.search.startTimestamp);
}
if (this.search.endTimestamp.trim()) {
params.set('end_time', this.search.endTimestamp);
}
toPromise<ReplicationJob[]>(this.replicationService this.jobsLoading = true;
toPromise<ReplicationJob>(this.replicationService
.getJobs(this.search.ruleId, params)) .getJobs(this.search.ruleId, params))
.then( .then(
response => { response => {
this.jobs = response; this.totalCount = response.metadata.xTotalCount;
this.jobs = response.data;
//Do filtering and sorting
this.jobs = doFiltering<ReplicationJobItem>(this.jobs, state);
this.jobs = doSorting<ReplicationJobItem>(this.jobs, state);
this.jobsLoading = false;
}).catch(error => { }).catch(error => {
this.jobsLoading = false;
this.errorHandler.error(error); this.errorHandler.error(error);
}); });
} }
loadFirstPage(): void {
let st: State = this.currentState;
if (!st) {
st = {
page: {}
};
}
st.page.size = this.pageSize;
st.page.from = 0;
st.page.to = this.pageSize - 1;
this.clrLoadJobs(st);
}
selectOneRule(rule: ReplicationRule) { selectOneRule(rule: ReplicationRule) {
if (rule) { if (rule && rule.id) {
this.search.ruleId = rule.id || ''; this.search.ruleId = rule.id || '';
this.search.repoName = ''; this.search.repoName = '';
this.search.status = ''; this.search.status = '';
this.currentJobSearchOption = 0; this.currentJobSearchOption = 0;
this.currentJobStatus = { 'key': 'all', 'description': 'REPLICATION.ALL' }; this.currentJobStatus = { 'key': 'all', 'description': 'REPLICATION.ALL' };
this.fetchReplicationJobs(); this.loadFirstPage();
} }
} }
@ -205,7 +267,7 @@ export class ReplicationComponent implements OnInit {
doSearchJobs(repoName: string) { doSearchJobs(repoName: string) {
this.search.repoName = repoName; this.search.repoName = repoName;
this.fetchReplicationJobs(); this.loadFirstPage();
} }
reloadRules(isReady: boolean) { reloadRules(isReady: boolean) {
@ -220,7 +282,21 @@ export class ReplicationComponent implements OnInit {
} }
refreshJobs() { refreshJobs() {
this.fetchReplicationJobs(); this.search.repoName = "";
this.search.startTimestamp = "";
this.search.endTimestamp = "";
this.search.status = "";
this.currentPage = 1;
let st: State = {
page: {
from: 0,
to: this.pageSize - 1,
size: this.pageSize
}
};
this.clrLoadJobs(st);
} }
toggleSearchJobOptionalName(option: number) { toggleSearchJobOptionalName(option: number) {
@ -229,12 +305,12 @@ export class ReplicationComponent implements OnInit {
doJobSearchByStartTime(fromTimestamp: string) { doJobSearchByStartTime(fromTimestamp: string) {
this.search.startTimestamp = fromTimestamp; this.search.startTimestamp = fromTimestamp;
this.fetchReplicationJobs(); this.loadFirstPage();
} }
doJobSearchByEndTime(toTimestamp: string) { doJobSearchByEndTime(toTimestamp: string) {
this.search.endTimestamp = toTimestamp; this.search.endTimestamp = toTimestamp;
this.fetchReplicationJobs(); this.loadFirstPage();
} }
viewLog(jobId: number | string): void { viewLog(jobId: number | string): void {

View File

@ -100,7 +100,19 @@ export interface ReplicationRule extends Base {
* @export * @export
* @interface ReplicationJob * @interface ReplicationJob
*/ */
export interface ReplicationJob extends Base { export interface ReplicationJob {
metadata?: Metadata;
data: ReplicationJobItem[];
}
/**
* Interface for replication job item.
*
* @export
* @interface ReplicationJob
*/
export interface ReplicationJobItem extends Base {
[key: string]: any | any[]
status: string; status: string;
repository: string; repository: string;
policy_id: number; policy_id: number;

View File

@ -1,6 +1,6 @@
import { Observable } from 'rxjs/Observable'; import { Observable } from 'rxjs/Observable';
import { RequestQueryParams } from './RequestQueryParams'; import { RequestQueryParams } from './RequestQueryParams';
import { ReplicationJob, ReplicationRule } from './interface'; import { ReplicationJob, ReplicationRule, ReplicationJobItem } from './interface';
import { Injectable, Inject } from "@angular/core"; import { Injectable, Inject } from "@angular/core";
import 'rxjs/add/observable/of'; import 'rxjs/add/observable/of';
import { Http, RequestOptions } from '@angular/http'; import { Http, RequestOptions } from '@angular/http';
@ -109,11 +109,11 @@ export abstract class ReplicationService {
* @abstract * @abstract
* @param {(number | string)} ruleId * @param {(number | string)} ruleId
* @param {RequestQueryParams} [queryParams] * @param {RequestQueryParams} [queryParams]
* @returns {(Observable<ReplicationJob> | Promise<ReplicationJob[]> | ReplicationJob)} * @returns {(Observable<ReplicationJob> | Promise<ReplicationJob> | ReplicationJob)}
* *
* @memberOf ReplicationService * @memberOf ReplicationService
*/ */
abstract getJobs(ruleId: number | string, queryParams?: RequestQueryParams): Observable<ReplicationJob[]> | Promise<ReplicationJob[]> | ReplicationJob[]; abstract getJobs(ruleId: number | string, queryParams?: RequestQueryParams): Observable<ReplicationJob> | Promise<ReplicationJob> | ReplicationJob;
/** /**
* Get the log of the specified job. * Get the log of the specified job.
@ -238,7 +238,7 @@ export class ReplicationDefaultService extends ReplicationService {
.catch(error => Promise.reject(error)); .catch(error => Promise.reject(error));
} }
public getJobs(ruleId: number | string, queryParams?: RequestQueryParams): Observable<ReplicationJob[]> | Promise<ReplicationJob[]> | ReplicationJob[] { public getJobs(ruleId: number | string, queryParams?: RequestQueryParams): Observable<ReplicationJob> | Promise<ReplicationJob> | ReplicationJob {
if (!ruleId || ruleId <= 0) { if (!ruleId || ruleId <= 0) {
return Promise.reject('Bad argument'); return Promise.reject('Bad argument');
} }
@ -249,7 +249,29 @@ export class ReplicationDefaultService extends ReplicationService {
queryParams.set('policy_id', '' + ruleId); queryParams.set('policy_id', '' + ruleId);
return this.http.get(this._jobBaseUrl, buildHttpRequestOptions(queryParams)).toPromise() return this.http.get(this._jobBaseUrl, buildHttpRequestOptions(queryParams)).toPromise()
.then(response => response.json() as ReplicationJob[]) .then(response => {
let result: ReplicationJob = {
metadata: {
xTotalCount: 0
},
data: []
};
if (response && response.headers) {
let xHeader: string = response.headers.get("X-Total-Count");
if (xHeader) {
result.metadata.xTotalCount = parseInt(xHeader, 0);
}
}
result.data = response.json() as ReplicationJobItem[];
if (result.metadata.xTotalCount === 0) {
if (result.data && result.data.length > 0) {
result.metadata.xTotalCount = result.data.length;
}
}
return result;
})
.catch(error => Promise.reject(error)); .catch(error => Promise.reject(error));
} }
@ -260,7 +282,7 @@ export class ReplicationDefaultService extends ReplicationService {
let logUrl: string = `${this._jobBaseUrl}/${jobId}/log`; let logUrl: string = `${this._jobBaseUrl}/${jobId}/log`;
return this.http.get(logUrl).toPromise() return this.http.get(logUrl).toPromise()
.then(response => response.text()) .then(response => response.text())
.catch(error => Promise.reject(error)); .catch(error => Promise.reject(error));
} }
} }

View File

@ -92,6 +92,12 @@ export class RepositoryDefaultService extends RepositoryService {
result.data = response.json() as RepositoryItem[]; result.data = response.json() as RepositoryItem[];
if (result.metadata.xTotalCount === 0) {
if (result.data && result.data.length > 0) {
result.metadata.xTotalCount = result.data.length;
}
}
return result; return result;
}) })
.catch(error => Promise.reject(error)); .catch(error => Promise.reject(error));

View File

@ -67,18 +67,19 @@ export class ResultTipComponent implements OnInit {
}); });
} }
this.translate.get(this.packageText(this.totalPackages)).subscribe((p1: string) => { this.translate.get(this.packageText(this.totalPackages)).subscribe((p1: string) => {
this.translate.get(this.packageText(this.packagesWithVul)).subscribe((p2: string) => { this.translate.get(this.unitText(this.packagesWithVul)).subscribe((vul: string) => {
this.translate.get(this.unitText(this.packagesWithVul)).subscribe((vul: string) => { let messageKey: string = "VULNERABILITY.CHART.TOOLTIPS_TITLE";
this.translate.get('VULNERABILITY.CHART.TOOLTIPS_TITLE', if (this.packagesWithVul > 1) {
{ messageKey = "VULNERABILITY.CHART.TOOLTIPS_TITLE_SINGULAR";
totalVulnerability: this.packagesWithVul, }
totalPackages: this.totalPackages, this.translate.get(messageKey,
package: p1, {
packageExt: p2, totalVulnerability: this.packagesWithVul,
vulnerability: vul totalPackages: this.totalPackages,
}) package: p1,
.subscribe((res: string) => this._tipTitle = res); vulnerability: vul
}); })
.subscribe((res: string) => this._tipTitle = res);
}); });
}); });
} }

View File

@ -31,7 +31,7 @@
"clarity-icons": "^0.9.8", "clarity-icons": "^0.9.8",
"clarity-ui": "^0.9.8", "clarity-ui": "^0.9.8",
"core-js": "^2.4.1", "core-js": "^2.4.1",
"harbor-ui": "0.3.66", "harbor-ui": "0.3.91",
"intl": "^1.2.5", "intl": "^1.2.5",
"mutationobserver-shim": "^0.3.2", "mutationobserver-shim": "^0.3.2",
"ngx-cookie": "^1.0.0", "ngx-cookie": "^1.0.0",

View File

@ -56,9 +56,8 @@
<clr-dg-cell>{{l.op_time | date: 'short'}}</clr-dg-cell> <clr-dg-cell>{{l.op_time | date: 'short'}}</clr-dg-cell>
</clr-dg-row> </clr-dg-row>
<clr-dg-footer> <clr-dg-footer>
<span *ngIf="pagination.totalItems">{{pagination.firstItem + 1}} - {{pagination.lastItem +1 }} {{'AUDIT_LOG.OF' | translate}} </span> <span *ngIf="showPaginationIndex">{{pagination.firstItem + 1}} - {{pagination.lastItem +1 }} {{'AUDIT_LOG.OF' | translate}} </span> {{pagination.totalItems }} {{'AUDIT_LOG.ITEMS' | translate}}
{{pagination.totalItems }} {{'AUDIT_LOG.ITEMS' | translate}} <clr-dg-pagination #pagination [clrDgPageSize]="15" [(clrDgPage)]="currentPage" [clrDgTotalItems]="totalRecordCount"></clr-dg-pagination>
<clr-dg-pagination #pagination [clrDgPageSize]="15" [(clrDgPage)]="currentPage" [clrDgTotalItems]="totalRecordCount"></clr-dg-pagination>
<!--{{totalRecordCount}} {{'AUDIT_LOG.ITEMS' | translate}} <!--{{totalRecordCount}} {{'AUDIT_LOG.ITEMS' | translate}}
<clr-dg-pagination [clrDgPageSize]="pageOffset" [clrDgTotalItems]="totalPage"></clr-dg-pagination>--> <clr-dg-pagination [clrDgPageSize]="pageOffset" [clrDgTotalItems]="totalPage"></clr-dg-pagination>-->
</clr-dg-footer> </clr-dg-footer>

View File

@ -25,7 +25,7 @@ import { AlertType } from '../shared/shared.const';
import { State } from 'clarity-angular'; import { State } from 'clarity-angular';
const optionalSearch: {} = {0: 'AUDIT_LOG.ADVANCED', 1: 'AUDIT_LOG.SIMPLE'}; const optionalSearch: {} = { 0: 'AUDIT_LOG.ADVANCED', 1: 'AUDIT_LOG.SIMPLE' };
class FilterOption { class FilterOption {
key: string; key: string;
@ -46,7 +46,7 @@ class FilterOption {
@Component({ @Component({
selector: 'audit-log', selector: 'audit-log',
templateUrl: './audit-log.component.html', templateUrl: './audit-log.component.html',
styleUrls: [ './audit-log.component.css' ] styleUrls: ['./audit-log.component.css']
}) })
export class AuditLogComponent implements OnInit { export class AuditLogComponent implements OnInit {
@ -54,24 +54,24 @@ export class AuditLogComponent implements OnInit {
projectId: number; projectId: number;
queryParam: AuditLog = new AuditLog(); queryParam: AuditLog = new AuditLog();
auditLogs: AuditLog[]; auditLogs: AuditLog[];
toggleName = optionalSearch; toggleName = optionalSearch;
currentOption: number = 0; currentOption: number = 0;
filterOptions: FilterOption[] = [ filterOptions: FilterOption[] = [
new FilterOption('all', 'AUDIT_LOG.ALL_OPERATIONS', true), new FilterOption('all', 'AUDIT_LOG.ALL_OPERATIONS', true),
new FilterOption('pull', 'AUDIT_LOG.PULL', true), new FilterOption('pull', 'AUDIT_LOG.PULL', true),
new FilterOption('push', 'AUDIT_LOG.PUSH', true), new FilterOption('push', 'AUDIT_LOG.PUSH', true),
new FilterOption('create', 'AUDIT_LOG.CREATE', true), new FilterOption('create', 'AUDIT_LOG.CREATE', true),
new FilterOption('delete', 'AUDIT_LOG.DELETE', true), new FilterOption('delete', 'AUDIT_LOG.DELETE', true),
new FilterOption('others', 'AUDIT_LOG.OTHERS', true) new FilterOption('others', 'AUDIT_LOG.OTHERS', true)
]; ];
pageOffset: number = 1; pageOffset: number = 1;
pageSize: number = 15; pageSize: number = 15;
totalRecordCount: number; totalRecordCount: number = 0;
currentPage: number; currentPage: number;
totalPage: number; totalPage: number;
@ViewChild('fromTime') fromTimeInput: NgModel; @ViewChild('fromTime') fromTimeInput: NgModel;
@ViewChild('toTime') toTimeInput: NgModel; @ViewChild('toTime') toTimeInput: NgModel;
@ -83,35 +83,39 @@ export class AuditLogComponent implements OnInit {
return this.toTimeInput.errors && this.toTimeInput.errors.dateValidator && (this.toTimeInput.dirty || this.toTimeInput.touched); return this.toTimeInput.errors && this.toTimeInput.errors.dateValidator && (this.toTimeInput.dirty || this.toTimeInput.touched);
} }
get showPaginationIndex(): boolean {
return this.totalRecordCount > 0;
}
constructor(private route: ActivatedRoute, private router: Router, private auditLogService: AuditLogService, private messageHandlerService: MessageHandlerService) { constructor(private route: ActivatedRoute, private router: Router, private auditLogService: AuditLogService, private messageHandlerService: MessageHandlerService) {
//Get current user from registered resolver. //Get current user from registered resolver.
this.route.data.subscribe(data=>this.currentUser = <SessionUser>data['auditLogResolver']); this.route.data.subscribe(data => this.currentUser = <SessionUser>data['auditLogResolver']);
} }
ngOnInit(): void { ngOnInit(): void {
this.projectId = +this.route.snapshot.parent.params['id']; this.projectId = +this.route.snapshot.parent.params['id'];
this.queryParam.project_id = this.projectId; this.queryParam.project_id = this.projectId;
this.queryParam.page_size = this.pageSize; this.queryParam.page_size = this.pageSize;
} }
retrieve(state?: State): void { retrieve(state?: State): void {
if(state) { if (state) {
this.queryParam.page = Math.ceil((state.page.to + 1) / this.pageSize); this.queryParam.page = Math.ceil((state.page.to + 1) / this.pageSize);
this.currentPage = this.queryParam.page; this.currentPage = this.queryParam.page;
} }
this.auditLogService this.auditLogService
.listAuditLogs(this.queryParam) .listAuditLogs(this.queryParam)
.subscribe( .subscribe(
response=>{ response => {
this.totalRecordCount =parseInt(response.headers.get('x-total-count')); this.totalRecordCount = parseInt(response.headers.get('x-total-count'));
this.auditLogs = response.json(); this.auditLogs = response.json();
}, },
error=>{ error => {
this.router.navigate(['/harbor', 'projects']); this.router.navigate(['/harbor', 'projects']);
this.messageHandlerService.handleError(error); this.messageHandlerService.handleError(error);
} }
); );
} }
doSearchAuditLogs(searchUsername: string): void { doSearchAuditLogs(searchUsername: string): void {
@ -120,16 +124,16 @@ export class AuditLogComponent implements OnInit {
} }
convertDate(strDate: string): string { convertDate(strDate: string): string {
if(/^(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.](19|20)\d\d$/.test(strDate)) { if (/^(0[1-9]|[12][0-9]|3[01])[- /.](0[1-9]|1[012])[- /.](19|20)\d\d$/.test(strDate)) {
let parts = strDate.split(/[-\/]/); let parts = strDate.split(/[-\/]/);
strDate = parts[2] /*Year*/ + '-' +parts[1] /*Month*/ + '-' + parts[0] /*Date*/; strDate = parts[2] /*Year*/ + '-' + parts[1] /*Month*/ + '-' + parts[0] /*Date*/;
} }
return strDate; return strDate;
} }
doSearchByStartTime(strDate: string): void { doSearchByStartTime(strDate: string): void {
this.queryParam.begin_timestamp = 0; this.queryParam.begin_timestamp = 0;
if(this.fromTimeInput.valid && strDate){ if (this.fromTimeInput.valid && strDate) {
strDate = this.convertDate(strDate); strDate = this.convertDate(strDate);
this.queryParam.begin_timestamp = new Date(strDate).getTime() / 1000; this.queryParam.begin_timestamp = new Date(strDate).getTime() / 1000;
} }
@ -138,7 +142,7 @@ export class AuditLogComponent implements OnInit {
doSearchByEndTime(strDate: string): void { doSearchByEndTime(strDate: string): void {
this.queryParam.end_timestamp = 0; this.queryParam.end_timestamp = 0;
if(this.toTimeInput.valid && strDate) { if (this.toTimeInput.valid && strDate) {
strDate = this.convertDate(strDate); strDate = this.convertDate(strDate);
let oneDayOffset = 3600 * 24; let oneDayOffset = 3600 * 24;
this.queryParam.end_timestamp = new Date(strDate).getTime() / 1000 + oneDayOffset; this.queryParam.end_timestamp = new Date(strDate).getTime() / 1000 + oneDayOffset;
@ -149,19 +153,19 @@ export class AuditLogComponent implements OnInit {
doSearchByOptions() { doSearchByOptions() {
let selectAll = true; let selectAll = true;
let operationFilter: string[] = []; let operationFilter: string[] = [];
for(var i in this.filterOptions) { for (var i in this.filterOptions) {
let filterOption = this.filterOptions[i]; let filterOption = this.filterOptions[i];
if(filterOption.checked) { if (filterOption.checked) {
operationFilter.push('operation=' + this.filterOptions[i].key); operationFilter.push('operation=' + this.filterOptions[i].key);
}else{ } else {
selectAll = false; selectAll = false;
} }
} }
if(selectAll) { if (selectAll) {
operationFilter = []; operationFilter = [];
} }
this.queryParam.keywords = operationFilter.join('&'); this.queryParam.keywords = operationFilter.join('&');
this.retrieve(); this.retrieve();
} }
toggleOptionalName(option: number): void { toggleOptionalName(option: number): void {
@ -169,21 +173,21 @@ export class AuditLogComponent implements OnInit {
} }
toggleFilterOption(option: string): void { toggleFilterOption(option: string): void {
let selectedOption = this.filterOptions.find(value =>(value.key === option)); let selectedOption = this.filterOptions.find(value => (value.key === option));
selectedOption.checked = !selectedOption.checked; selectedOption.checked = !selectedOption.checked;
if(selectedOption.key === 'all') { if (selectedOption.key === 'all') {
this.filterOptions.filter(value=> value.key !== selectedOption.key).forEach(value => value.checked = selectedOption.checked); this.filterOptions.filter(value => value.key !== selectedOption.key).forEach(value => value.checked = selectedOption.checked);
} else { } else {
if(!selectedOption.checked) { if (!selectedOption.checked) {
this.filterOptions.find(value=>value.key === 'all').checked = false; this.filterOptions.find(value => value.key === 'all').checked = false;
} }
let selectAll = true; let selectAll = true;
this.filterOptions.filter(value=> value.key !== 'all').forEach(value =>{ this.filterOptions.filter(value => value.key !== 'all').forEach(value => {
if(!value.checked) { if (!value.checked) {
selectAll = false; selectAll = false;
} }
}); });
this.filterOptions.find(value=>value.key === 'all').checked = selectAll; this.filterOptions.find(value => value.key === 'all').checked = selectAll;
} }
this.doSearchByOptions(); this.doSearchByOptions();
} }

View File

@ -487,8 +487,9 @@
"FOOT_OF": "of" "FOOT_OF": "of"
}, },
"CHART": { "CHART": {
"SCANNING_TIME": "Scan completed datetime", "SCANNING_TIME": "Scan completed time:",
"TOOLTIPS_TITLE": "This image includes {{totalPackages}} {{package}} with {{vulnerability}} in {{totalVulnerability}} of the {{packageExt}}." "TOOLTIPS_TITLE": "{{totalVulnerability}} of {{totalPackages}} {{package}} have known {{vulnerability}}.",
"TOOLTIPS_TITLE_SINGULAR": "{{totalVulnerability}} of {{totalPackages}} {{package}} has known {{vulnerability}}."
}, },
"SEVERITY": { "SEVERITY": {
"HIGH": "high", "HIGH": "high",

View File

@ -486,8 +486,9 @@
"FOOT_OF": "of" "FOOT_OF": "of"
}, },
"CHART": { "CHART": {
"SCANNING_TIME": "Scan completed datetime", "SCANNING_TIME": "Scan completed time:",
"TOOLTIPS_TITLE": "This image includes {{totalPackages}} {{package}} with {{vulnerability}} in {{totalVulnerability}} of the {{packageExt}}." "TOOLTIPS_TITLE": "{{totalVulnerability}} of {{totalPackages}} {{package}} have known {{vulnerability}}.",
"TOOLTIPS_TITLE_SINGULAR": "{{totalVulnerability}} of {{totalPackages}} {{package}} has known {{vulnerability}}."
}, },
"SEVERITY": { "SEVERITY": {
"HIGH": "high", "HIGH": "high",

View File

@ -487,8 +487,9 @@
"FOOT_OF": "总共" "FOOT_OF": "总共"
}, },
"CHART": { "CHART": {
"SCANNING_TIME": "扫描完成", "SCANNING_TIME": "扫描完成时间:",
"TOOLTIPS_TITLE": "在此镜像总共包含{{totalPackages}}{{package}},其中{{totalVulnerability}}个{{packageExt}}含有{{vulnerability}}。" "TOOLTIPS_TITLE": "{{totalPackages}}个{{package}}中的{{totalVulnerability}}个含有{{vulnerability}}.",
"TOOLTIPS_TITLE_SINGULAR": "{{totalPackages}}个{{package}}中的{{totalVulnerability}}个含有{{vulnerability}}."
}, },
"SEVERITY": { "SEVERITY": {
"HIGH": "严重", "HIGH": "严重",