Update jobservice dashboard UI (#17894)

1.Fixes #17855, #17874 and #17863
Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
Shijun Sun 2022-12-01 17:01:59 +08:00 committed by GitHub
parent cb11540a14
commit 491dcf7d9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 377 additions and 240 deletions

View File

@ -0,0 +1,24 @@
import { TestBed } from '@angular/core/testing';
import { JobServiceDashboardSharedDataService } from './job-service-dashboard-shared-data.service';
import { SharedTestingModule } from '../../../shared/shared.module';
describe('JobServiceDashboardSharedDataService', () => {
let service: JobServiceDashboardSharedDataService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [SharedTestingModule],
providers: [JobServiceDashboardSharedDataService],
});
service = TestBed.inject(JobServiceDashboardSharedDataService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
it('should have initial value', () => {
expect(service.getAllWorkers().length).toEqual(0);
expect(service.getJobQueues().length).toEqual(0);
});
});

View File

@ -0,0 +1,136 @@
import { Injectable } from '@angular/core';
import { JobQueue } from '../../../../../ng-swagger-gen/models/job-queue';
import { JobserviceService } from '../../../../../ng-swagger-gen/services/jobservice.service';
import { map, Observable } from 'rxjs';
import { All, ScheduleListResponse } from './job-service-dashboard.interface';
import { Worker } from 'ng-swagger-gen/models';
import { ScheduleService } from '../../../../../ng-swagger-gen/services/schedule.service';
import { ClrDatagridStateInterface } from '@clr/angular/data/datagrid/interfaces/state.interface';
import { doSorting } from '../../../shared/units/utils';
@Injectable()
export class JobServiceDashboardSharedDataService {
private _jobQueues: JobQueue[] = [];
private _allWorkers: Worker[] = [];
private _scheduleListResponse: ScheduleListResponse;
private _scheduleListParam: ScheduleService.ListSchedulesParams = {
page: 1,
pageSize: 1,
};
private _state: ClrDatagridStateInterface;
constructor(
private jobServiceService: JobserviceService,
private scheduleService: ScheduleService
) {}
getJobQueues(): JobQueue[] {
return this._jobQueues;
}
getAllWorkers(): Worker[] {
return this._allWorkers;
}
getScheduleListResponse(): ScheduleListResponse {
return this._scheduleListResponse;
}
retrieveJobQueues(isAutoRefresh?: boolean): Observable<JobQueue[]> {
return this.jobServiceService.listJobQueues().pipe(
map(res => {
// For auto-refresh
// if execute this._jobQueues = res, the selected rows of the datagrid will be reset
// so only refresh properties here
if (isAutoRefresh && this._jobQueues?.length && res?.length) {
this._jobQueues.forEach(item => {
res.forEach(item2 => {
if (item2.job_type === item.job_type) {
item.count = item2.count;
item.latency = item2.latency;
item.paused = item2.paused;
}
});
});
} else {
this._jobQueues = res;
}
// map null and undefined to 0
this._jobQueues.forEach(item => {
if (!item.count) {
item.count = 0;
}
});
return this._jobQueues;
})
);
}
retrieveScheduleListResponse(
params?: ScheduleService.ListSchedulesParams,
state?: ClrDatagridStateInterface
): Observable<ScheduleListResponse> {
if (params) {
this._scheduleListParam = params;
}
if (state) {
this._state = state;
}
return this.scheduleService
.listSchedulesResponse(this._scheduleListParam)
.pipe(
map(res => {
const result: ScheduleListResponse = {
pageSize: this._scheduleListParam?.pageSize | 1,
currentPage: this._scheduleListParam?.page | 1,
scheduleList: res.body,
total: Number.parseInt(
res.headers.get('x-total-count'),
10
),
};
this._scheduleListResponse = result;
if (
this._state &&
this._scheduleListResponse?.scheduleList?.length
) {
this._scheduleListResponse.scheduleList = doSorting(
this._scheduleListResponse?.scheduleList,
this._state
);
}
return this._scheduleListResponse;
})
);
}
retrieveAllWorkers(isAutoRefresh?: boolean): Observable<Worker[]> {
return this.jobServiceService
.getWorkers({
poolId: All.ALL_WORKERS.toString(),
})
.pipe(
map(res => {
// For auto-refresh
// if execute this._allWorkers = res, the selected rows of the datagrid will be reset
// so only refresh properties here
if (
isAutoRefresh &&
this._allWorkers?.length &&
res?.length
) {
this._allWorkers.forEach(item => {
res.forEach(item2 => {
if (item2.id === item.id) {
item.job_id = item2.job_id;
item.job_name = item2.job_name;
item.check_in = item2.check_in;
item.checkin_at = item2.checkin_at;
item.start_at = item2.start_at;
}
});
});
} else {
this._allWorkers = res;
}
return this._allWorkers;
})
);
}
}

View File

@ -1,3 +1,5 @@
import { ScheduleTask } from '../../../../../ng-swagger-gen/models/schedule-task';
export enum All {
ALL_WORKERS = 'all',
}
@ -32,3 +34,10 @@ export const CronTypeI18nMap = {
Hourly: 'SCHEDULE.HOURLY',
Custom: 'SCHEDULE.CUSTOM',
};
export interface ScheduleListResponse {
pageSize: number;
currentPage: number;
total: number;
scheduleList: ScheduleTask[];
}

View File

@ -9,6 +9,7 @@ import { ScheduleListComponent } from './schedule-list/schedule-list.component';
import { DonutChartComponent } from './worker-card/donut-chart/donut-chart.component';
import { WorkerCardComponent } from './worker-card/worker-card.component';
import { WorkerListComponent } from './worker-list/worker-list.component';
import { JobServiceDashboardSharedDataService } from './job-service-dashboard-shared-data.service';
const routes: Routes = [
{
@ -47,5 +48,6 @@ const routes: Routes = [
ScheduleListComponent,
WorkerListComponent,
],
providers: [JobServiceDashboardSharedDataService],
})
export class JobServiceDashboardModule {}

View File

@ -1,35 +1,35 @@
<div class="card">
<div class="card-header">
<div class="card-header text-truncate">
{{ 'JOB_SERVICE_DASHBOARD.PENDING_JOBS' | translate }}
</div>
<div class="card-block">
<div class="duration">
<div class="duration text-truncate">
{{ 'REPLICATION.TOTAL' | translate }}: {{ total() }}
</div>
<ng-container *ngIf="!loading">
<div class="clr-row" *ngIf="jobQueue?.length">
<div class="clr-col-5 center">
<label class="clr-control-label">
<div class="clr-col center">
<label class="clr-control-label text-truncate">
{{ jobQueue[0]?.job_type }}
</label>
</div>
<div class="clr-col-7">{{ jobQueue[0]?.count || 0 }}</div>
<div class="clr-col">{{ jobQueue[0]?.count || 0 }}</div>
</div>
<div class="clr-row" *ngIf="jobQueue?.length > 1">
<div class="clr-col-5 center">
<label class="clr-control-label">
<div class="clr-col center">
<label class="clr-control-label text-truncate">
{{ jobQueue[1]?.job_type }}
</label>
</div>
<div class="clr-col-7">{{ jobQueue[1]?.count || 0 }}</div>
<div class="clr-col">{{ jobQueue[1]?.count || 0 }}</div>
</div>
<div class="clr-row" *ngIf="jobQueue?.length > 2">
<div class="clr-col-5 center">
<label class="clr-control-label">{{
<div class="clr-col center">
<label class="clr-control-label text-truncate">{{
'JOB_SERVICE_DASHBOARD.OTHERS' | translate
}}</label>
</div>
<div class="clr-col-7">{{ otherCount() }}</div>
<div class="clr-col">{{ otherCount() }}</div>
</div>
</ng-container>
<div *ngIf="loading" class="loading">

View File

@ -2,7 +2,9 @@
width: 100%;
text-align: center;
font-size: 50px;
margin: 1.5rem 0 1rem;
margin: 1rem 0;
line-height: 2rem;
height: 2rem;
}
.card-block {

View File

@ -4,6 +4,7 @@ import { SharedTestingModule } from '../../../../shared/shared.module';
import { JobQueue } from '../../../../../../ng-swagger-gen/models/job-queue';
import { of } from 'rxjs';
import { delay } from 'rxjs/operators';
import { JobServiceDashboardSharedDataService } from '../job-service-dashboard-shared-data.service';
describe('PendingCardComponent', () => {
let component: PendingCardComponent;
@ -24,8 +25,12 @@ describe('PendingCardComponent', () => {
},
];
const fakedJobserviceService = {
listJobQueues() {
const fakedJobServiceDashboardSharedDataService = {
_jobQueues: [],
getJobQueues(): JobQueue[] {
return this._jobQueues;
},
retrieveJobQueues() {
return of(mockedJobs).pipe(delay(0));
},
};
@ -34,19 +39,28 @@ describe('PendingCardComponent', () => {
await TestBed.configureTestingModule({
declarations: [PendingCardComponent],
imports: [SharedTestingModule],
providers: [
{
provide: JobServiceDashboardSharedDataService,
useValue: fakedJobServiceDashboardSharedDataService,
},
],
}).compileComponents();
fixture = TestBed.createComponent(PendingCardComponent);
component = fixture.componentInstance;
spyOn(component, 'loopGetPendingJobs').and.callFake(() => {
fakedJobserviceService.listJobQueues().subscribe(res => {
component.loading = false;
component.jobQueue = res.sort((a, b) => {
const ACount: number = a?.count | 0;
const BCount: number = b?.count | 0;
return BCount - ACount;
fakedJobServiceDashboardSharedDataService
.retrieveJobQueues()
.subscribe(res => {
component.loading = false;
fakedJobServiceDashboardSharedDataService._jobQueues =
res.sort((a, b) => {
const ACount: number = a?.count | 0;
const BCount: number = b?.count | 0;
return BCount - ACount;
});
});
});
});
fixture.autoDetectChanges();
});

View File

@ -2,7 +2,6 @@ import { Component, OnDestroy, OnInit } from '@angular/core';
import { ConfirmationDialogService } from '../../../global-confirmation-dialog/confirmation-dialog.service';
import { JobserviceService } from '../../../../../../ng-swagger-gen/services/jobservice.service';
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
import { JobQueue } from '../../../../../../ng-swagger-gen/models/job-queue';
import { finalize } from 'rxjs/operators';
import {
INTERVAL,
@ -14,11 +13,7 @@ import {
ConfirmationState,
ConfirmationTargets,
} from '../../../../shared/entities/shared.const';
import { of, Subscription } from 'rxjs';
import {
EventService,
HarborEvent,
} from '../../../../services/event-service/event.service';
import { Subscription } from 'rxjs';
import {
operateChanges,
OperateInfo,
@ -26,6 +21,7 @@ import {
} from '../../../../shared/components/operation/operate';
import { errorHandler } from '../../../../shared/units/shared.utils';
import { OperationService } from '../../../../shared/components/operation/operation.service';
import { JobServiceDashboardSharedDataService } from '../job-service-dashboard-shared-data.service';
@Component({
selector: 'app-pending-job-card',
@ -34,7 +30,6 @@ import { OperationService } from '../../../../shared/components/operation/operat
})
export class PendingCardComponent implements OnInit, OnDestroy {
loading: boolean = false;
jobQueue: JobQueue[] = [];
timeout: any;
loadingStopAll: boolean = false;
confirmSub: Subscription;
@ -42,8 +37,8 @@ export class PendingCardComponent implements OnInit, OnDestroy {
private operateDialogService: ConfirmationDialogService,
private jobServiceService: JobserviceService,
private messageHandlerService: MessageHandlerService,
private eventService: EventService,
private operationService: OperationService
private operationService: OperationService,
private jobServiceDashboardSharedDataService: JobServiceDashboardSharedDataService
) {}
ngOnInit() {
@ -60,7 +55,15 @@ export class PendingCardComponent implements OnInit, OnDestroy {
this.confirmSub = null;
}
}
get jobQueue() {
return this.jobServiceDashboardSharedDataService
.getJobQueues()
.sort((a, b) => {
const ACount: number = a?.count | 0;
const BCount: number = b?.count | 0;
return BCount - ACount;
});
}
initSub() {
if (!this.confirmSub) {
this.confirmSub =
@ -86,17 +89,10 @@ export class PendingCardComponent implements OnInit, OnDestroy {
if (withLoading) {
this.loading = true;
}
this.jobServiceService
.listJobQueues()
this.jobServiceDashboardSharedDataService
.retrieveJobQueues(true)
.pipe(finalize(() => (this.loading = false)))
.subscribe(res => {
if (res?.length) {
this.jobQueue = res.sort((a, b) => {
const ACount: number = a?.count | 0;
const BCount: number = b?.count | 0;
return BCount - ACount;
});
}
this.timeout = setTimeout(() => {
this.loopGetPendingJobs();
}, INTERVAL);
@ -159,9 +155,6 @@ export class PendingCardComponent implements OnInit, OnDestroy {
'JOB_SERVICE_DASHBOARD.STOP_ALL_SUCCESS'
);
this.refreshNow();
this.eventService.publish(
HarborEvent.REFRESH_JOB_SERVICE_DASHBOARD
);
operateChanges(operationMessage, OperationState.success);
},
error: err => {

View File

@ -65,10 +65,10 @@
<clr-dg-column [clrDgField]="'job_type'">{{
'JOB_SERVICE_DASHBOARD.JOB_TYPE' | translate
}}</clr-dg-column>
<clr-dg-column [clrDgField]="'count'" [clrDgColType]="'number'">{{
<clr-dg-column [clrDgSortBy]="'count'">{{
'JOB_SERVICE_DASHBOARD.PENDING_COUNT' | translate
}}</clr-dg-column>
<clr-dg-column [clrDgField]="'latency'" [clrDgColType]="'number'">{{
<clr-dg-column [clrDgSortBy]="'latency'">{{
'JOB_SERVICE_DASHBOARD.LATENCY' | translate
}}</clr-dg-column>
<clr-dg-column>{{

View File

@ -2,9 +2,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PendingListComponent } from './pending-job-list.component';
import { JobQueue } from '../../../../../../ng-swagger-gen/models/job-queue';
import { of } from 'rxjs';
import { delay, finalize } from 'rxjs/operators';
import { delay } from 'rxjs/operators';
import { SharedTestingModule } from '../../../../shared/shared.module';
import { JobserviceService } from '../../../../../../ng-swagger-gen/services/jobservice.service';
import { JobServiceDashboardSharedDataService } from '../job-service-dashboard-shared-data.service';
describe('PendingListComponent', () => {
let component: PendingListComponent;
@ -25,8 +25,12 @@ describe('PendingListComponent', () => {
},
];
const fakedJobserviceService = {
listJobQueues() {
const fakedJobServiceDashboardSharedDataService = {
_jobQueues: mockedJobs,
getJobQueues(): JobQueue[] {
return this._jobQueues;
},
retrieveJobQueues() {
return of(mockedJobs).pipe(delay(0));
},
};
@ -37,8 +41,8 @@ describe('PendingListComponent', () => {
imports: [SharedTestingModule],
providers: [
{
provide: JobserviceService,
useValue: fakedJobserviceService,
provide: JobServiceDashboardSharedDataService,
useValue: fakedJobServiceDashboardSharedDataService,
},
],
}).compileComponents();

View File

@ -14,10 +14,6 @@ import { NO, YES } from '../../clearing-job/clearing-job-interfact';
import { PendingJobsActions } from '../job-service-dashboard.interface';
import { forkJoin, Subscription } from 'rxjs';
import { ConfirmationDialogService } from '../../../global-confirmation-dialog/confirmation-dialog.service';
import {
EventService,
HarborEvent,
} from '../../../../services/event-service/event.service';
import {
ConfirmationButtons,
ConfirmationState,
@ -30,6 +26,7 @@ import {
} from '../../../../shared/components/operation/operate';
import { OperationService } from '../../../../shared/components/operation/operation.service';
import { errorHandler } from '../../../../shared/units/shared.utils';
import { JobServiceDashboardSharedDataService } from '../job-service-dashboard-shared-data.service';
@Component({
selector: 'app-pending-job-list',
@ -42,23 +39,20 @@ export class PendingListComponent implements OnInit, OnDestroy {
pageSize: number = getPageSizeFromLocalStorage(
PageSizeMapKeys.PENDING_LIST_COMPONENT
);
jobQueue: JobQueue[] = [];
loadingStop: boolean = false;
loadingPause: boolean = false;
loadingResume: boolean = false;
confirmSub: Subscription;
eventSub: Subscription;
constructor(
private jobServiceService: JobserviceService,
private messageHandlerService: MessageHandlerService,
private operateDialogService: ConfirmationDialogService,
private eventService: EventService,
private operationService: OperationService
private operationService: OperationService,
private jobServiceDashboardSharedDataService: JobServiceDashboardSharedDataService
) {}
ngOnInit() {
this.getJobs();
this.initEventSub();
this.initSub();
}
ngOnDestroy() {
@ -66,10 +60,10 @@ export class PendingListComponent implements OnInit, OnDestroy {
this.confirmSub.unsubscribe();
this.confirmSub = null;
}
if (this.eventSub) {
this.eventSub.unsubscribe();
this.eventSub = null;
}
}
get jobQueue() {
return this.jobServiceDashboardSharedDataService.getJobQueues();
}
initSub() {
@ -105,27 +99,14 @@ export class PendingListComponent implements OnInit, OnDestroy {
}
}
initEventSub() {
if (!this.eventSub) {
this.eventSub = this.eventService.subscribe(
HarborEvent.REFRESH_JOB_SERVICE_DASHBOARD,
() => {
this.getJobs();
}
);
}
}
getJobs() {
this.selectedRows = [];
this.loading = true;
this.jobServiceService
.listJobQueues()
this.jobServiceDashboardSharedDataService
.retrieveJobQueues()
.pipe(finalize(() => (this.loading = false)))
.subscribe({
next: res => {
this.jobQueue = res;
},
next: res => {},
error: err => {
this.messageHandlerService.error(err);
},

View File

@ -1,14 +1,14 @@
<div class="card">
<div class="card-header">
<div class="card-header text-truncate">
{{ 'JOB_SERVICE_DASHBOARD.SCHEDULES' | translate }}
</div>
<div class="card-block">
<div class="duration">
<div class="duration text-truncate">
{{ 'REPLICATION.TOTAL' | translate }}: {{ scheduleCount }}
</div>
<div class="clr-row">
<div class="clr-col-5 center">
<label class="clr-control-label">{{
<label class="clr-control-label text-truncate">{{
'WEBHOOK.STATUS' | translate
}}</label>
</div>

View File

@ -2,7 +2,9 @@
width: 100%;
text-align: center;
font-size: 50px;
margin: 1.5rem 0 1rem;
margin: 1rem 0;
line-height: 2rem;
height: 2rem;
}
.card-block {

View File

@ -4,10 +4,14 @@ import { of } from 'rxjs';
import { delay } from 'rxjs/operators';
import { SharedTestingModule } from '../../../../shared/shared.module';
import { JobserviceService } from '../../../../../../ng-swagger-gen/services/jobservice.service';
import { ScheduleStatusString } from '../job-service-dashboard.interface';
import {
ScheduleListResponse,
ScheduleStatusString,
} from '../job-service-dashboard.interface';
import { HttpHeaders, HttpResponse } from '@angular/common/http';
import { ScheduleTask } from '../../../../../../ng-swagger-gen/models/schedule-task';
import { ScheduleService } from '../../../../../../ng-swagger-gen/services/schedule.service';
import { JobServiceDashboardSharedDataService } from '../job-service-dashboard-shared-data.service';
describe('ScheduleCardComponent', () => {
let component: ScheduleCardComponent;
@ -30,6 +34,16 @@ describe('ScheduleCardComponent', () => {
},
};
const fakedJobServiceDashboardSharedDataService = {
_scheduleListResponse: {},
getScheduleListResponse(): ScheduleListResponse {
return this._scheduleListResponse;
},
retrieveScheduleListResponse() {
return of({});
},
};
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ScheduleCardComponent],
@ -43,6 +57,10 @@ describe('ScheduleCardComponent', () => {
provide: ScheduleService,
useValue: fakedScheduleService,
},
{
provide: JobServiceDashboardSharedDataService,
useValue: fakedJobServiceDashboardSharedDataService,
},
],
}).compileComponents();

View File

@ -16,10 +16,7 @@ import {
ConfirmationTargets,
} from '../../../../shared/entities/shared.const';
import { Subscription } from 'rxjs';
import {
EventService,
HarborEvent,
} from '../../../../services/event-service/event.service';
import { EventService } from '../../../../services/event-service/event.service';
import { OperationService } from '../../../../shared/components/operation/operation.service';
import {
operateChanges,
@ -28,6 +25,7 @@ import {
} from '../../../../shared/components/operation/operate';
import { errorHandler } from '../../../../shared/units/shared.utils';
import { ScheduleService } from '../../../../../../ng-swagger-gen/services/schedule.service';
import { JobServiceDashboardSharedDataService } from '../job-service-dashboard-shared-data.service';
@Component({
selector: 'app-schedule-card',
@ -35,7 +33,6 @@ import { ScheduleService } from '../../../../../../ng-swagger-gen/services/sched
styleUrls: ['./schedule-card.component.scss'],
})
export class ScheduleCardComponent implements OnInit, OnDestroy {
scheduleCount: number = 0;
isPaused: boolean = false;
loadingStatus: boolean = false;
confirmSub: Subscription;
@ -47,7 +44,8 @@ export class ScheduleCardComponent implements OnInit, OnDestroy {
private messageHandlerService: MessageHandlerService,
private eventService: EventService,
private operationService: OperationService,
private scheduleService: ScheduleService
private scheduleService: ScheduleService,
private jobServiceDashboardSharedDataService: JobServiceDashboardSharedDataService
) {}
ngOnInit() {
@ -67,6 +65,13 @@ export class ScheduleCardComponent implements OnInit, OnDestroy {
}
}
get scheduleCount(): number {
return (
this.jobServiceDashboardSharedDataService.getScheduleListResponse()
?.total | 0
);
}
initSub() {
if (!this.confirmSub) {
this.confirmSub =
@ -113,21 +118,10 @@ export class ScheduleCardComponent implements OnInit, OnDestroy {
}
getScheduleCount() {
this.scheduleService
.listSchedulesResponse({
page: 1,
pageSize: 1,
})
this.jobServiceDashboardSharedDataService
.retrieveScheduleListResponse()
.subscribe({
next: res => {
// Get total count
if (res.headers) {
let xHeader: string = res.headers.get('x-total-count');
if (xHeader) {
this.scheduleCount = Number.parseInt(xHeader, 10);
}
}
},
next: res => {},
error: err => {
this.messageHandlerService.error(err);
},
@ -198,9 +192,9 @@ export class ScheduleCardComponent implements OnInit, OnDestroy {
);
}
this.refreshNow();
this.eventService.publish(
HarborEvent.REFRESH_JOB_SERVICE_DASHBOARD
);
this.jobServiceDashboardSharedDataService
.retrieveJobQueues()
.subscribe();
operateChanges(operationMessage, OperationState.success);
},
error: err => {

View File

@ -18,7 +18,7 @@
<clr-dg-column>{{
'JOB_SERVICE_DASHBOARD.CRON' | translate
}}</clr-dg-column>
<clr-dg-column [clrDgSortBy]="'creation_time'">{{
<clr-dg-column [clrDgSortBy]="'update_time'">{{
'REPLICATION.UPDATE_TIME' | translate
}}</clr-dg-column>
<clr-dg-placeholder>{{

View File

@ -2,10 +2,9 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ScheduleListComponent } from './schedule-list.component';
import { ScheduleTask } from '../../../../../../ng-swagger-gen/models/schedule-task';
import { of } from 'rxjs';
import { delay } from 'rxjs/operators';
import { SharedTestingModule } from '../../../../shared/shared.module';
import { HttpHeaders, HttpResponse } from '@angular/common/http';
import { ScheduleService } from '../../../../../../ng-swagger-gen/services/schedule.service';
import { JobServiceDashboardSharedDataService } from '../job-service-dashboard-shared-data.service';
import { ScheduleListResponse } from '../job-service-dashboard.interface';
describe('ScheduleListComponent', () => {
let component: ScheduleListComponent;
@ -16,15 +15,15 @@ describe('ScheduleListComponent', () => {
{ id: 2, vendor_type: 'test2' },
];
const fakedScheduleService = {
listSchedulesResponse() {
const res: HttpResponse<Array<ScheduleTask>> = new HttpResponse<
Array<ScheduleTask>
>({
headers: new HttpHeaders({ 'x-total-count': '2' }),
body: mockedSchedules,
});
return of(res).pipe(delay(0));
const fakedJobServiceDashboardSharedDataService = {
_scheduleListResponse: {
scheduleList: mockedSchedules,
},
getScheduleListResponse(): ScheduleListResponse {
return this._scheduleListResponse;
},
retrieveScheduleListResponse() {
return of({});
},
};
@ -34,15 +33,15 @@ describe('ScheduleListComponent', () => {
imports: [SharedTestingModule],
providers: [
{
provide: ScheduleService,
useValue: fakedScheduleService,
provide: JobServiceDashboardSharedDataService,
useValue: fakedJobServiceDashboardSharedDataService,
},
],
}).compileComponents();
fixture = TestBed.createComponent(ScheduleListComponent);
component = fixture.componentInstance;
component.loadingSchedules = true;
component.loadingSchedules = false;
fixture.detectChanges();
});
@ -52,7 +51,6 @@ describe('ScheduleListComponent', () => {
it('should render list', async () => {
await fixture.whenStable();
component.loadingSchedules = false;
fixture.detectChanges();
await fixture.whenStable();
const rows = fixture.nativeElement.querySelectorAll('clr-dg-row');

View File

@ -1,4 +1,4 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Component } from '@angular/core';
import { ClrDatagridStateInterface } from '@clr/angular/data/datagrid/interfaces/state.interface';
import {
doSorting,
@ -9,53 +9,30 @@ import {
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
import { finalize } from 'rxjs/operators';
import { ScheduleTask } from '../../../../../../ng-swagger-gen/models/schedule-task';
import {
EventService,
HarborEvent,
} from '../../../../services/event-service/event.service';
import { Subscription } from 'rxjs';
import { ScheduleService } from '../../../../../../ng-swagger-gen/services/schedule.service';
import { JobServiceDashboardSharedDataService } from '../job-service-dashboard-shared-data.service';
@Component({
selector: 'app-schedule-list',
templateUrl: './schedule-list.component.html',
styleUrls: ['./schedule-list.component.scss'],
})
export class ScheduleListComponent implements OnInit, OnDestroy {
loadingSchedules: boolean = false;
schedules: ScheduleTask[] = [];
export class ScheduleListComponent {
loadingSchedules: boolean = true;
total: number = 0;
page: number = 1;
pageSize: number = getPageSizeFromLocalStorage(
PageSizeMapKeys.SCHEDULE_LIST_COMPONENT
);
eventSub: Subscription;
constructor(
private messageHandlerService: MessageHandlerService,
private eventService: EventService,
private scheduleService: ScheduleService
private jobServiceDashboardSharedDataService: JobServiceDashboardSharedDataService
) {}
ngOnInit() {
this.initEventSub();
}
ngOnDestroy() {
if (this.eventSub) {
this.eventSub.unsubscribe();
this.eventSub = null;
}
}
initEventSub() {
if (!this.eventSub) {
this.eventSub = this.eventService.subscribe(
HarborEvent.REFRESH_JOB_SERVICE_DASHBOARD,
() => {
this.clrLoad();
}
);
}
get schedules(): ScheduleTask[] {
return (
this.jobServiceDashboardSharedDataService.getScheduleListResponse()
?.scheduleList || []
);
}
clrLoad(state?: ClrDatagridStateInterface): void {
@ -67,22 +44,18 @@ export class ScheduleListComponent implements OnInit, OnDestroy {
);
}
this.loadingSchedules = true;
this.scheduleService
.listSchedulesResponse({
page: this.page,
pageSize: this.pageSize,
})
this.jobServiceDashboardSharedDataService
.retrieveScheduleListResponse(
{
page: this.page,
pageSize: this.pageSize,
},
state
)
.pipe(finalize(() => (this.loadingSchedules = false)))
.subscribe({
next: res => {
// Get total count
if (res.headers) {
let xHeader: string = res.headers.get('x-total-count');
if (xHeader) {
this.total = Number.parseInt(xHeader, 10);
}
}
this.schedules = doSorting(res.body, state);
doSorting(res.scheduleList, state);
},
error: err => {
this.messageHandlerService.error(err);

View File

@ -1,5 +1,5 @@
<div class="card">
<div class="card-header">
<div class="card-header text-truncate">
{{ 'JOB_SERVICE_DASHBOARD.WORKERS' | translate }}
</div>
<div class="card-block donut-chart">

View File

@ -1,10 +1,11 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { SharedTestingModule } from '../../../../shared/shared.module';
import { JobserviceService } from '../../../../../../ng-swagger-gen/services/jobservice.service';
import { of } from 'rxjs';
import { WorkerCardComponent } from './worker-card.component';
import { NO_ERRORS_SCHEMA } from '@angular/core';
import { Worker } from '../../../../../../ng-swagger-gen/models/worker';
import { ScheduleListResponse } from '../job-service-dashboard.interface';
import { JobServiceDashboardSharedDataService } from '../job-service-dashboard-shared-data.service';
describe('WorkerCardComponent', () => {
let component: WorkerCardComponent;
@ -15,8 +16,12 @@ describe('WorkerCardComponent', () => {
{ id: '2', job_id: '2', job_name: 'test2' },
];
const fakedJobserviceService = {
getWorkers() {
const fakedJobServiceDashboardSharedDataService = {
_allWorkers: mockedWorkers,
getAllWorkers(): ScheduleListResponse {
return this._allWorkers;
},
retrieveAllWorkers() {
return of(mockedWorkers);
},
};
@ -28,8 +33,8 @@ describe('WorkerCardComponent', () => {
imports: [SharedTestingModule],
providers: [
{
provide: JobserviceService,
useValue: fakedJobserviceService,
provide: JobServiceDashboardSharedDataService,
useValue: fakedJobServiceDashboardSharedDataService,
},
],
}).compileComponents();

View File

@ -10,10 +10,6 @@ import {
} from 'src/app/shared/entities/shared.const';
import { MessageHandlerService } from 'src/app/shared/services/message-handler.service';
import { All, INTERVAL } from '../job-service-dashboard.interface';
import {
EventService,
HarborEvent,
} from '../../../../services/event-service/event.service';
import { OperationService } from '../../../../shared/components/operation/operation.service';
import {
operateChanges,
@ -21,6 +17,7 @@ import {
OperationState,
} from '../../../../shared/components/operation/operate';
import { errorHandler } from '../../../../shared/units/shared.utils';
import { JobServiceDashboardSharedDataService } from '../job-service-dashboard-shared-data.service';
@Component({
selector: 'app-worker-card',
@ -38,8 +35,8 @@ export class WorkerCardComponent implements OnInit, OnDestroy {
private operateDialogService: ConfirmationDialogService,
private jobServiceService: JobserviceService,
private messageHandlerService: MessageHandlerService,
private eventService: EventService,
private operationService: OperationService
private operationService: OperationService,
private jobServiceDashboardSharedDataService: JobServiceDashboardSharedDataService
) {}
ngOnInit(): void {
@ -78,9 +75,9 @@ export class WorkerCardComponent implements OnInit, OnDestroy {
}
}
loopWorkerStatus() {
this.jobServiceService
.getWorkers({ poolId: All.ALL_WORKERS.toString() })
loopWorkerStatus(isAutoRefresh?: boolean) {
this.jobServiceDashboardSharedDataService
.retrieveAllWorkers(isAutoRefresh)
.subscribe(res => {
if (res) {
this.denominator = res.length;
@ -91,7 +88,7 @@ export class WorkerCardComponent implements OnInit, OnDestroy {
}
});
this.statusTimeout = setTimeout(() => {
this.loopWorkerStatus();
this.loopWorkerStatus(true);
}, INTERVAL);
}
});
@ -123,9 +120,6 @@ export class WorkerCardComponent implements OnInit, OnDestroy {
'JOB_SERVICE_DASHBOARD.FREE_ALL_SUCCESS'
);
this.refreshNow();
this.eventService.publish(
HarborEvent.REFRESH_JOB_SERVICE_DASHBOARD
);
operateChanges(operationMessage, OperationState.success);
},
error: err => {

View File

@ -2,7 +2,7 @@
<clr-datagrid
[clrDgLoading]="loadingPools"
(clrDgRefresh)="clrLoadPool($event)"
(clrDgSelectedChange)="selectionChanged()"
(clrDgSelectedChange)="refreshWorkers()"
[(clrDgSingleSelected)]="selectedPool">
<clr-dg-action-bar class="action-bar mt-0">
<span class="refresh-btn" (click)="getPools()">
@ -86,7 +86,7 @@
</clr-tooltip-content>
</clr-tooltip>
</div>
<span class="refresh-btn" (click)="selectionChanged()">
<span class="refresh-btn" (click)="refreshWorkers()">
<clr-icon shape="refresh"></clr-icon>
</span>
</clr-dg-action-bar>

View File

@ -5,14 +5,16 @@ import { delay } from 'rxjs/operators';
import { SharedTestingModule } from '../../../../shared/shared.module';
import { JobserviceService } from '../../../../../../ng-swagger-gen/services/jobservice.service';
import { Worker, WorkerPool } from 'ng-swagger-gen/models';
import { ScheduleListResponse } from '../job-service-dashboard.interface';
import { JobServiceDashboardSharedDataService } from '../job-service-dashboard-shared-data.service';
describe('WorkerListComponent', () => {
let component: WorkerListComponent;
let fixture: ComponentFixture<WorkerListComponent>;
const mockedWorkers: Worker[] = [
{ id: '1', job_id: '1', job_name: 'test1' },
{ id: '2', job_id: '2', job_name: 'test2' },
{ id: '1', job_id: '1', job_name: 'test1', pool_id: '1' },
{ id: '2', job_id: '2', job_name: 'test2', pool_id: '1' },
];
const mockedPools: WorkerPool[] = [
@ -20,13 +22,19 @@ describe('WorkerListComponent', () => {
];
const fakedJobserviceService = {
getWorkers() {
return of(mockedWorkers).pipe(delay(0));
},
getWorkerPools() {
return of(mockedPools).pipe(delay(0));
},
};
const fakedJobServiceDashboardSharedDataService = {
_allWorkers: mockedWorkers,
getAllWorkers(): ScheduleListResponse {
return this._allWorkers;
},
retrieveAllWorkers() {
return of([]);
},
};
beforeEach(async () => {
await TestBed.configureTestingModule({
@ -37,6 +45,10 @@ describe('WorkerListComponent', () => {
provide: JobserviceService,
useValue: fakedJobserviceService,
},
{
provide: JobServiceDashboardSharedDataService,
useValue: fakedJobServiceDashboardSharedDataService,
},
],
}).compileComponents();
fixture = TestBed.createComponent(WorkerListComponent);
@ -49,10 +61,10 @@ describe('WorkerListComponent', () => {
});
it('should render worker list', async () => {
component.selectionChanged();
await fixture.whenStable();
component.loadingPools = false;
component.loadingWorkers = false;
component.selectedPool = mockedPools[0];
fixture.detectChanges();
await fixture.whenStable();
const rows = fixture.nativeElement.querySelectorAll('clr-dg-row');

View File

@ -17,10 +17,6 @@ import {
ConfirmationTargets,
} from '../../../../shared/entities/shared.const';
import { ConfirmationDialogService } from '../../../global-confirmation-dialog/confirmation-dialog.service';
import {
EventService,
HarborEvent,
} from '../../../../services/event-service/event.service';
import { OperationService } from '../../../../shared/components/operation/operation.service';
import {
operateChanges,
@ -28,6 +24,7 @@ import {
OperationState,
} from '../../../../shared/components/operation/operate';
import { errorHandler } from '../../../../shared/units/shared.utils';
import { JobServiceDashboardSharedDataService } from '../job-service-dashboard-shared-data.service';
@Component({
selector: 'app-worker-list',
@ -39,7 +36,6 @@ export class WorkerListComponent implements OnInit, OnDestroy {
selectedPool: WorkerPool;
pools: WorkerPool[] = [];
loadingWorkers: boolean = false;
workers: Worker[] = [];
selected: Worker[] = [];
poolPageSize: number = getPageSizeFromLocalStorage(
@ -52,29 +48,29 @@ export class WorkerListComponent implements OnInit, OnDestroy {
);
loadingFree: boolean = false;
confirmSub: Subscription;
eventSub: Subscription;
constructor(
private jobServiceService: JobserviceService,
private messageHandlerService: MessageHandlerService,
private operateDialogService: ConfirmationDialogService,
private eventService: EventService,
private operationService: OperationService
private operationService: OperationService,
private jobServiceDashboardSharedDataService: JobServiceDashboardSharedDataService
) {}
ngOnInit(): void {
this.getPools();
this.initSub();
this.initEventSub();
}
ngOnDestroy() {
if (this.confirmSub) {
this.confirmSub.unsubscribe();
this.confirmSub = null;
}
if (this.eventSub) {
this.eventSub.unsubscribe();
this.eventSub = null;
}
}
get workers(): Worker[] {
return this.jobServiceDashboardSharedDataService
.getAllWorkers()
.filter(item => item.pool_id === this.selectedPool?.worker_pool_id);
}
initSub() {
@ -98,17 +94,6 @@ export class WorkerListComponent implements OnInit, OnDestroy {
}
}
initEventSub() {
if (!this.eventSub) {
this.eventSub = this.eventService.subscribe(
HarborEvent.REFRESH_JOB_SERVICE_DASHBOARD,
() => {
this.selectionChanged();
}
);
}
}
getPools() {
this.loadingPools = true;
this.jobServiceService
@ -119,7 +104,6 @@ export class WorkerListComponent implements OnInit, OnDestroy {
this.pools = res;
if (res?.length) {
this.selectedPool = res[0];
this.selectionChanged();
}
},
error: err => {
@ -128,21 +112,6 @@ export class WorkerListComponent implements OnInit, OnDestroy {
});
}
selectionChanged() {
this.loadingWorkers = true;
this.jobServiceService
.getWorkers({ poolId: this.selectedPool?.worker_pool_id })
.pipe(finalize(() => (this.loadingWorkers = false)))
.subscribe({
next: res => {
this.workers = res;
},
error: err => {
this.messageHandlerService.error(err);
},
});
}
string(v: any) {
if (v) {
return JSON.stringify(v);
@ -196,6 +165,14 @@ export class WorkerListComponent implements OnInit, OnDestroy {
this.operateDialogService.openComfirmDialog(deletionMessage);
}
refreshWorkers() {
this.loadingWorkers = true;
this.jobServiceDashboardSharedDataService
.retrieveAllWorkers()
.pipe(finalize(() => (this.loadingWorkers = false)))
.subscribe();
}
executeFreeWorkers() {
this.loadingFree = true;
const operationMessage = new OperateInfo();
@ -219,7 +196,7 @@ export class WorkerListComponent implements OnInit, OnDestroy {
this.messageHandlerService.info(
'JOB_SERVICE_DASHBOARD.FREE_WORKER_SUCCESS'
);
this.selectionChanged();
this.refreshWorkers();
operateChanges(operationMessage, OperationState.success);
},
error: err => {

View File

@ -79,5 +79,4 @@ export enum HarborEvent {
STOP_SCAN_ARTIFACT = 'stopScanArtifact',
UPDATE_VULNERABILITY_INFO = 'UpdateVulnerabilityInfo',
REFRESH_EXPORT_JOBS = 'refreshExportJobs',
REFRESH_JOB_SERVICE_DASHBOARD = 'refreshJobServiceDashboard',
}