mirror of
https://github.com/goharbor/harbor.git
synced 2024-12-26 10:38:00 +01:00
Add Jobservice UI (#17722)
Signed-off-by: AllForNothing <sshijun@vmware.com> Signed-off-by: AllForNothing <sshijun@vmware.com>
This commit is contained in:
parent
321a9abfb3
commit
ea812c9f16
@ -123,6 +123,14 @@ const routes: Routes = [
|
||||
'./left-side-nav/clearing-job/clearing-job.module'
|
||||
).then(m => m.ClearingJobModule),
|
||||
},
|
||||
{
|
||||
path: 'job-service-dashboard',
|
||||
canActivate: [SystemAdminGuard],
|
||||
loadChildren: () =>
|
||||
import(
|
||||
'./left-side-nav/job-service-dashboard/job-service-dashboard.module'
|
||||
).then(m => m.JobServiceDashboardModule),
|
||||
},
|
||||
{
|
||||
path: 'configs',
|
||||
canActivate: [SystemAdminGuard],
|
||||
|
@ -167,6 +167,19 @@
|
||||
clrVerticalNavIcon></clr-icon>
|
||||
{{ 'CLEARANCES.CLEARANCES' | translate }}
|
||||
</a>
|
||||
<a
|
||||
clrVerticalNavLink
|
||||
*ngIf="hasAdminRole"
|
||||
routerLink="/harbor/job-service-dashboard"
|
||||
routerLinkActive="active">
|
||||
<clr-icon
|
||||
shape="dashboard"
|
||||
clrVerticalNavIcon></clr-icon>
|
||||
{{
|
||||
'JOB_SERVICE_DASHBOARD.JOB_SERVICE_DASHBOARD'
|
||||
| translate
|
||||
}}
|
||||
</a>
|
||||
<a
|
||||
clrVerticalNavLink
|
||||
routerLinkActive="active"
|
||||
|
@ -44,6 +44,7 @@
|
||||
</div>
|
||||
<div class="cron-selection">
|
||||
<cron-selection
|
||||
class="w-100"
|
||||
[externalValidation]="isValid()"
|
||||
[labelCurrent]="getLabelCurrent"
|
||||
[labelEdit]="getLabelCurrent"
|
||||
|
@ -44,6 +44,7 @@
|
||||
</div>
|
||||
<div class="cron-selection">
|
||||
<cron-selection
|
||||
class="w-100"
|
||||
[labelCurrent]="getLabelCurrent"
|
||||
[labelEdit]="getLabelCurrent"
|
||||
[originCron]="originCron"
|
||||
|
@ -11,6 +11,7 @@
|
||||
</div>
|
||||
<div class="button-group">
|
||||
<cron-selection
|
||||
class="w-100"
|
||||
[disabled]="!hasDefaultScanner"
|
||||
[labelCurrent]="getLabelCurrent"
|
||||
[labelEdit]="getLabelCurrent"
|
||||
|
@ -0,0 +1,49 @@
|
||||
<h2 class="title">
|
||||
<clr-icon class="dashboard" size="24" shape="dashboard"></clr-icon>
|
||||
<span>{{ 'JOB_SERVICE_DASHBOARD.JOB_SERVICE_DASHBOARD' | translate }}</span>
|
||||
</h2>
|
||||
|
||||
<div class="clr-row pr-10 mb-2">
|
||||
<div class="clr-col">
|
||||
<app-pending-job-card></app-pending-job-card>
|
||||
</div>
|
||||
<div class="clr-col">
|
||||
<app-schedule-card></app-schedule-card>
|
||||
</div>
|
||||
<div class="clr-col">
|
||||
<app-worker-card></app-worker-card>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="nav" role="tablist">
|
||||
<li role="presentation" class="nav-item">
|
||||
<button
|
||||
id="pending-jobs"
|
||||
class="btn btn-link nav-link"
|
||||
type="button"
|
||||
routerLink="pending-jobs"
|
||||
routerLinkActive="active">
|
||||
{{ 'JOB_SERVICE_DASHBOARD.JOB_QUEUE' | translate }}
|
||||
</button>
|
||||
</li>
|
||||
<li role="presentation" class="nav-item">
|
||||
<button
|
||||
id="schedules"
|
||||
class="btn btn-link nav-link"
|
||||
type="button"
|
||||
routerLink="schedules"
|
||||
routerLinkActive="active">
|
||||
{{ 'JOB_SERVICE_DASHBOARD.SCHEDULES' | translate }}
|
||||
</button>
|
||||
</li>
|
||||
<li role="presentation" class="nav-item">
|
||||
<button
|
||||
id="workers"
|
||||
class="btn btn-link nav-link"
|
||||
type="button"
|
||||
routerLink="workers"
|
||||
routerLinkActive="active">
|
||||
{{ 'JOB_SERVICE_DASHBOARD.WORKERS' | translate }}
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
<router-outlet></router-outlet>
|
@ -0,0 +1,25 @@
|
||||
@mixin v-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.dashboard {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
.pr-10 {
|
||||
padding-right: 2rem;
|
||||
}
|
||||
|
||||
.card-block {
|
||||
height: 9rem;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,27 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { SharedTestingModule } from 'src/app/shared/shared.module';
|
||||
import { JobServiceDashboardComponent } from './job-service-dashboard.component';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
|
||||
describe('JobServiceDashboardComponent', () => {
|
||||
let component: JobServiceDashboardComponent;
|
||||
let fixture: ComponentFixture<JobServiceDashboardComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
schemas: [NO_ERRORS_SCHEMA],
|
||||
imports: [SharedTestingModule],
|
||||
declarations: [JobServiceDashboardComponent],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(JobServiceDashboardComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,21 @@
|
||||
// Copyright Project Harbor Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'job-service-dashboard',
|
||||
templateUrl: 'job-service-dashboard.component.html',
|
||||
styleUrls: ['job-service-dashboard.component.scss'],
|
||||
})
|
||||
export class JobServiceDashboardComponent {}
|
@ -0,0 +1,26 @@
|
||||
export enum All {
|
||||
ALL_WORKERS = 'all',
|
||||
}
|
||||
|
||||
export enum PendingJobsActions {
|
||||
PAUSE = 'pause',
|
||||
RESUME = 'resume',
|
||||
STOP = 'stop',
|
||||
}
|
||||
|
||||
export const INTERVAL: number = 10000;
|
||||
|
||||
export enum ScheduleStatusString {
|
||||
PAUSED = 'JOB_SERVICE_DASHBOARD.PAUSED',
|
||||
RUNNING = 'JOB_SERVICE_DASHBOARD.RUNNING_STATUS',
|
||||
}
|
||||
|
||||
export enum ScheduleExecuteBtnString {
|
||||
RESUME_ALL = 'JOB_SERVICE_DASHBOARD.RESUME_ALL_BTN_TEXT',
|
||||
PAUSE_ALL = 'JOB_SERVICE_DASHBOARD.PAUSE_ALL_BTN_TEXT',
|
||||
}
|
||||
|
||||
export enum JobType {
|
||||
SCHEDULER = 'scheduler',
|
||||
ALL = 'all',
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { SharedModule } from 'src/app/shared/shared.module';
|
||||
import { JobServiceDashboardComponent } from './job-service-dashboard.component';
|
||||
import { PendingCardComponent } from './pending-job-card/pending-job-card.component';
|
||||
import { PendingListComponent } from './pending-job-list/pending-job-list.component';
|
||||
import { ScheduleCardComponent } from './schedule-card/schedule-card.component';
|
||||
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';
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: '',
|
||||
component: JobServiceDashboardComponent,
|
||||
children: [
|
||||
{
|
||||
path: 'pending-jobs',
|
||||
component: PendingListComponent,
|
||||
},
|
||||
{
|
||||
path: 'schedules',
|
||||
component: ScheduleListComponent,
|
||||
},
|
||||
{
|
||||
path: 'workers',
|
||||
component: WorkerListComponent,
|
||||
},
|
||||
{
|
||||
path: '',
|
||||
redirectTo: 'pending-jobs',
|
||||
pathMatch: 'full',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@NgModule({
|
||||
imports: [SharedModule, RouterModule.forChild(routes)],
|
||||
declarations: [
|
||||
JobServiceDashboardComponent,
|
||||
DonutChartComponent,
|
||||
PendingCardComponent,
|
||||
ScheduleCardComponent,
|
||||
WorkerCardComponent,
|
||||
PendingListComponent,
|
||||
ScheduleListComponent,
|
||||
WorkerListComponent,
|
||||
],
|
||||
})
|
||||
export class JobServiceDashboardModule {}
|
@ -0,0 +1,49 @@
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
{{ 'JOB_SERVICE_DASHBOARD.PENDING_JOBS' | translate }}
|
||||
</div>
|
||||
<div class="card-block">
|
||||
<div class="duration">
|
||||
{{ '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">
|
||||
{{ jobQueue[0]?.job_type }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="clr-col-7">{{ 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">
|
||||
{{ jobQueue[1]?.job_type }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="clr-col-7">{{ 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">{{
|
||||
'JOB_SERVICE_DASHBOARD.OTHERS' | translate
|
||||
}}</label>
|
||||
</div>
|
||||
<div class="clr-col-7">{{ otherCount() }}</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<div *ngIf="loading" class="loading">
|
||||
<span class="spinner spinner-inline"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<button
|
||||
[disabled]="loadingStopAll || total() <= 0"
|
||||
[clrLoading]="loadingStopAll"
|
||||
(click)="stopAll()"
|
||||
type="button"
|
||||
class="btn btn-link">
|
||||
{{ 'JOB_SERVICE_DASHBOARD.STOP_ALL' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,23 @@
|
||||
.duration {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 50px;
|
||||
margin: 1.5rem 0 1rem;
|
||||
}
|
||||
|
||||
.card-block {
|
||||
height: 9rem;
|
||||
}
|
||||
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.loading {
|
||||
display: flex;
|
||||
height: 4rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
@ -0,0 +1,62 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { PendingCardComponent } from './pending-job-card.component';
|
||||
import { SharedTestingModule } from '../../../../shared/shared.module';
|
||||
import { JobQueue } from '../../../../../../ng-swagger-gen/models/job-queue';
|
||||
import { of } from 'rxjs';
|
||||
import { delay } from 'rxjs/operators';
|
||||
|
||||
describe('PendingCardComponent', () => {
|
||||
let component: PendingCardComponent;
|
||||
let fixture: ComponentFixture<PendingCardComponent>;
|
||||
|
||||
const mockedJobs: JobQueue[] = [
|
||||
{
|
||||
job_type: 'test1',
|
||||
count: 1,
|
||||
},
|
||||
{
|
||||
job_type: 'test2',
|
||||
count: 2,
|
||||
},
|
||||
{
|
||||
job_type: 'test3',
|
||||
count: 3,
|
||||
},
|
||||
];
|
||||
|
||||
const fakedJobserviceService = {
|
||||
listJobQueues() {
|
||||
return of(mockedJobs).pipe(delay(0));
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [PendingCardComponent],
|
||||
imports: [SharedTestingModule],
|
||||
}).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;
|
||||
});
|
||||
});
|
||||
});
|
||||
fixture.autoDetectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render data', async () => {
|
||||
await fixture.whenStable();
|
||||
expect(component.total()).toEqual(6);
|
||||
});
|
||||
});
|
@ -0,0 +1,185 @@
|
||||
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,
|
||||
JobType,
|
||||
PendingJobsActions,
|
||||
} from '../job-service-dashboard.interface';
|
||||
import {
|
||||
ConfirmationButtons,
|
||||
ConfirmationState,
|
||||
ConfirmationTargets,
|
||||
} from '../../../../shared/entities/shared.const';
|
||||
import { of, Subscription } from 'rxjs';
|
||||
import {
|
||||
EventService,
|
||||
HarborEvent,
|
||||
} from '../../../../services/event-service/event.service';
|
||||
import {
|
||||
operateChanges,
|
||||
OperateInfo,
|
||||
OperationState,
|
||||
} from '../../../../shared/components/operation/operate';
|
||||
import { errorHandler } from '../../../../shared/units/shared.utils';
|
||||
import { OperationService } from '../../../../shared/components/operation/operation.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-pending-job-card',
|
||||
templateUrl: './pending-job-card.component.html',
|
||||
styleUrls: ['./pending-job-card.component.scss'],
|
||||
})
|
||||
export class PendingCardComponent implements OnInit, OnDestroy {
|
||||
loading: boolean = false;
|
||||
jobQueue: JobQueue[] = [];
|
||||
timeout: any;
|
||||
loadingStopAll: boolean = false;
|
||||
confirmSub: Subscription;
|
||||
constructor(
|
||||
private operateDialogService: ConfirmationDialogService,
|
||||
private jobServiceService: JobserviceService,
|
||||
private messageHandlerService: MessageHandlerService,
|
||||
private eventService: EventService,
|
||||
private operationService: OperationService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.loopGetPendingJobs(true);
|
||||
this.initSub();
|
||||
}
|
||||
ngOnDestroy() {
|
||||
if (this.timeout) {
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = null;
|
||||
}
|
||||
if (this.confirmSub) {
|
||||
this.confirmSub.unsubscribe();
|
||||
this.confirmSub = null;
|
||||
}
|
||||
}
|
||||
|
||||
initSub() {
|
||||
if (!this.confirmSub) {
|
||||
this.confirmSub =
|
||||
this.operateDialogService.confirmationConfirm$.subscribe(
|
||||
message => {
|
||||
if (
|
||||
message &&
|
||||
message.state === ConfirmationState.CONFIRMED
|
||||
) {
|
||||
if (
|
||||
message.source ===
|
||||
ConfirmationTargets.STOP_ALL_PENDING_JOBS
|
||||
) {
|
||||
this.executeStopAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
loopGetPendingJobs(withLoading?: boolean) {
|
||||
if (withLoading) {
|
||||
this.loading = true;
|
||||
}
|
||||
this.jobServiceService
|
||||
.listJobQueues()
|
||||
.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);
|
||||
});
|
||||
}
|
||||
|
||||
total(): number {
|
||||
if (this.jobQueue?.length) {
|
||||
let count: number = 0;
|
||||
this.jobQueue.forEach((item, index) => {
|
||||
count += item?.count | 0;
|
||||
});
|
||||
return count;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
otherCount(): number {
|
||||
if (this.jobQueue?.length) {
|
||||
let count: number = 0;
|
||||
this.jobQueue.forEach((item, index) => {
|
||||
if (index > 1) {
|
||||
count += item?.count | 0;
|
||||
}
|
||||
});
|
||||
return count;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
stopAll() {
|
||||
this.operateDialogService.openComfirmDialog({
|
||||
data: undefined,
|
||||
param: null,
|
||||
title: 'JOB_SERVICE_DASHBOARD.CONFIRM_STOP_ALL',
|
||||
message: 'JOB_SERVICE_DASHBOARD.CONFIRM_STOP_ALL_CONTENT',
|
||||
targetId: ConfirmationTargets.STOP_ALL_PENDING_JOBS,
|
||||
buttons: ConfirmationButtons.CONFIRM_CANCEL,
|
||||
});
|
||||
}
|
||||
|
||||
executeStopAll() {
|
||||
this.loadingStopAll = true;
|
||||
const operationMessage = new OperateInfo();
|
||||
operationMessage.name =
|
||||
'JOB_SERVICE_DASHBOARD.OPERATION_STOP_ALL_QUEUES';
|
||||
operationMessage.state = OperationState.progressing;
|
||||
this.operationService.publishInfo(operationMessage);
|
||||
this.jobServiceService
|
||||
.actionPendingJobs({
|
||||
jobType: JobType.ALL,
|
||||
actionRequest: {
|
||||
action: PendingJobsActions.STOP,
|
||||
},
|
||||
})
|
||||
.pipe(finalize(() => (this.loadingStopAll = false)))
|
||||
.subscribe({
|
||||
next: res => {
|
||||
this.messageHandlerService.info(
|
||||
'JOB_SERVICE_DASHBOARD.STOP_ALL_SUCCESS'
|
||||
);
|
||||
this.refreshNow();
|
||||
this.eventService.publish(
|
||||
HarborEvent.REFRESH_JOB_SERVICE_DASHBOARD
|
||||
);
|
||||
operateChanges(operationMessage, OperationState.success);
|
||||
},
|
||||
error: err => {
|
||||
this.messageHandlerService.error(err);
|
||||
operateChanges(
|
||||
operationMessage,
|
||||
OperationState.failure,
|
||||
errorHandler(err)
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
refreshNow() {
|
||||
if (this.timeout) {
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = null;
|
||||
}
|
||||
this.loopGetPendingJobs(true);
|
||||
}
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
<clr-datagrid
|
||||
[(clrDgSelected)]="selectedRows"
|
||||
[clrDgLoading]="loading"
|
||||
(clrDgRefresh)="clrLoad($event)">
|
||||
<clr-dg-action-bar>
|
||||
<clr-dg-action-bar class="action-bar mt-0">
|
||||
<div>
|
||||
<button
|
||||
[clrLoading]="loadingStop"
|
||||
[disabled]="loadingStop || !selectedRows?.length"
|
||||
type="button"
|
||||
class="btn btn-secondary"
|
||||
(click)="stop()">
|
||||
{{ 'JOB_SERVICE_DASHBOARD.STOP_BTN' | translate }}
|
||||
</button>
|
||||
<button
|
||||
(click)="pause()"
|
||||
[disabled]="!canPause()"
|
||||
type="button"
|
||||
class="btn btn-secondary">
|
||||
{{ 'JOB_SERVICE_DASHBOARD.PAUSE_BTN' | translate }}
|
||||
</button>
|
||||
<button
|
||||
(click)="resume()"
|
||||
[disabled]="!canResume()"
|
||||
type="button"
|
||||
class="btn btn-secondary">
|
||||
{{ 'JOB_SERVICE_DASHBOARD.RESUME_BTN' | translate }}
|
||||
</button>
|
||||
<clr-tooltip>
|
||||
<clr-icon
|
||||
clrTooltipTrigger
|
||||
shape="info-circle"
|
||||
size="24"></clr-icon>
|
||||
<clr-tooltip-content
|
||||
clrPosition="top-right"
|
||||
clrSize="lg"
|
||||
*clrIfOpen>
|
||||
<div>
|
||||
{{
|
||||
'JOB_SERVICE_DASHBOARD.QUEUE_STOP_BTN_INFO'
|
||||
| translate
|
||||
}}
|
||||
</div>
|
||||
<div>
|
||||
{{
|
||||
'JOB_SERVICE_DASHBOARD.QUEUE_PAUSE_BTN_INFO'
|
||||
| translate
|
||||
}}
|
||||
</div>
|
||||
<div>
|
||||
{{
|
||||
'JOB_SERVICE_DASHBOARD.QUEUE_RESUME_BTN_INFO'
|
||||
| translate
|
||||
}}
|
||||
</div>
|
||||
</clr-tooltip-content>
|
||||
</clr-tooltip>
|
||||
</div>
|
||||
<span class="refresh-btn">
|
||||
<clr-icon shape="refresh" (click)="getJobs()"></clr-icon>
|
||||
</span>
|
||||
</clr-dg-action-bar>
|
||||
</clr-dg-action-bar>
|
||||
<clr-dg-column [clrDgField]="'job_type'">{{
|
||||
'JOB_SERVICE_DASHBOARD.JOB_TYPE' | translate
|
||||
}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'count'" [clrDgColType]="'number'">{{
|
||||
'JOB_SERVICE_DASHBOARD.PENDING_COUNT' | translate
|
||||
}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'latency'" [clrDgColType]="'number'">{{
|
||||
'JOB_SERVICE_DASHBOARD.LATENCY' | translate
|
||||
}}</clr-dg-column>
|
||||
<clr-dg-column>{{
|
||||
'JOB_SERVICE_DASHBOARD.PAUSED' | translate
|
||||
}}</clr-dg-column>
|
||||
<clr-dg-placeholder>{{
|
||||
'JOB_SERVICE_DASHBOARD.NO_JOB_QUEUE' | translate
|
||||
}}</clr-dg-placeholder>
|
||||
<clr-dg-row *clrDgItems="let j of jobQueue" [clrDgItem]="j">
|
||||
<clr-dg-cell>{{ j.job_type }}</clr-dg-cell>
|
||||
<clr-dg-cell>{{ j.count || 0 }}</clr-dg-cell>
|
||||
<clr-dg-cell>{{ getDuration(j?.latency) || 0 }}</clr-dg-cell>
|
||||
<clr-dg-cell>{{ isPaused(j?.paused) | translate }}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
<clr-dg-pagination
|
||||
#pagination
|
||||
[clrDgPageSize]="pageSize"
|
||||
[clrDgTotalItems]="jobQueue?.length">
|
||||
<clr-dg-page-size [clrPageSizeOptions]="[15, 25, 50]">{{
|
||||
'PAGINATION.PAGE_SIZE' | translate
|
||||
}}</clr-dg-page-size>
|
||||
<span *ngIf="jobQueue?.length">
|
||||
{{ pagination.firstItem + 1 }} -
|
||||
{{ pagination.lastItem + 1 }}
|
||||
{{ 'GROUP.OF' | translate }}
|
||||
</span>
|
||||
{{ jobQueue?.length }} {{ 'GROUP.ITEMS' | translate }}
|
||||
</clr-dg-pagination>
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
@ -0,0 +1,9 @@
|
||||
.action-bar {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
margin-right: 1rem;
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
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 { SharedTestingModule } from '../../../../shared/shared.module';
|
||||
import { JobserviceService } from '../../../../../../ng-swagger-gen/services/jobservice.service';
|
||||
|
||||
describe('PendingListComponent', () => {
|
||||
let component: PendingListComponent;
|
||||
let fixture: ComponentFixture<PendingListComponent>;
|
||||
|
||||
const mockedJobs: JobQueue[] = [
|
||||
{
|
||||
job_type: 'test1',
|
||||
count: 1,
|
||||
},
|
||||
{
|
||||
job_type: 'test2',
|
||||
count: 2,
|
||||
},
|
||||
{
|
||||
job_type: 'test3',
|
||||
count: 3,
|
||||
},
|
||||
];
|
||||
|
||||
const fakedJobserviceService = {
|
||||
listJobQueues() {
|
||||
return of(mockedJobs).pipe(delay(0));
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [PendingListComponent],
|
||||
imports: [SharedTestingModule],
|
||||
providers: [
|
||||
{
|
||||
provide: JobserviceService,
|
||||
useValue: fakedJobserviceService,
|
||||
},
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(PendingListComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render list', async () => {
|
||||
await fixture.whenStable();
|
||||
component.loading = false;
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const rows = fixture.nativeElement.querySelectorAll('clr-dg-row');
|
||||
expect(rows.length).toEqual(3);
|
||||
});
|
||||
});
|
@ -0,0 +1,332 @@
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ClrDatagridStateInterface } from '@clr/angular/data/datagrid/interfaces/state.interface';
|
||||
import {
|
||||
durationStr,
|
||||
getPageSizeFromLocalStorage,
|
||||
PageSizeMapKeys,
|
||||
setPageSizeToLocalStorage,
|
||||
} from '../../../../shared/units/utils';
|
||||
import { JobserviceService } from '../../../../../../ng-swagger-gen/services/jobservice.service';
|
||||
import { MessageHandlerService } from '../../../../shared/services/message-handler.service';
|
||||
import { finalize } from 'rxjs/operators';
|
||||
import { JobQueue } from '../../../../../../ng-swagger-gen/models/job-queue';
|
||||
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,
|
||||
ConfirmationTargets,
|
||||
} from 'src/app/shared/entities/shared.const';
|
||||
import {
|
||||
operateChanges,
|
||||
OperateInfo,
|
||||
OperationState,
|
||||
} from '../../../../shared/components/operation/operate';
|
||||
import { OperationService } from '../../../../shared/components/operation/operation.service';
|
||||
import { errorHandler } from '../../../../shared/units/shared.utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-pending-job-list',
|
||||
templateUrl: './pending-job-list.component.html',
|
||||
styleUrls: ['./pending-job-list.component.scss'],
|
||||
})
|
||||
export class PendingListComponent implements OnInit, OnDestroy {
|
||||
loading: boolean = false;
|
||||
selectedRows: JobQueue[] = [];
|
||||
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
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.getJobs();
|
||||
this.initEventSub();
|
||||
this.initSub();
|
||||
}
|
||||
ngOnDestroy() {
|
||||
if (this.confirmSub) {
|
||||
this.confirmSub.unsubscribe();
|
||||
this.confirmSub = null;
|
||||
}
|
||||
if (this.eventSub) {
|
||||
this.eventSub.unsubscribe();
|
||||
this.eventSub = null;
|
||||
}
|
||||
}
|
||||
|
||||
initSub() {
|
||||
if (!this.confirmSub) {
|
||||
this.confirmSub =
|
||||
this.operateDialogService.confirmationConfirm$.subscribe(
|
||||
message => {
|
||||
if (
|
||||
message &&
|
||||
message.state === ConfirmationState.CONFIRMED
|
||||
) {
|
||||
if (
|
||||
message.source ===
|
||||
ConfirmationTargets.STOPS_JOBS
|
||||
) {
|
||||
this.executeStop();
|
||||
}
|
||||
if (
|
||||
message.source ===
|
||||
ConfirmationTargets.PAUSE_JOBS
|
||||
) {
|
||||
this.executePause();
|
||||
}
|
||||
if (
|
||||
message.source ===
|
||||
ConfirmationTargets.RESUME_JOBS
|
||||
) {
|
||||
this.executeResume();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
.pipe(finalize(() => (this.loading = false)))
|
||||
.subscribe({
|
||||
next: res => {
|
||||
this.jobQueue = res;
|
||||
},
|
||||
error: err => {
|
||||
this.messageHandlerService.error(err);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
clrLoad(state: ClrDatagridStateInterface): void {
|
||||
if (state?.page?.size) {
|
||||
this.pageSize = state.page.size;
|
||||
setPageSizeToLocalStorage(
|
||||
PageSizeMapKeys.PENDING_LIST_COMPONENT,
|
||||
this.pageSize
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
isPaused(paused: boolean): string {
|
||||
if (paused) {
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
}
|
||||
getDuration(v: number): string {
|
||||
if (v) {
|
||||
return durationStr(v * 1000);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
canPause(): boolean {
|
||||
if (this.selectedRows?.length) {
|
||||
return !this.selectedRows.some(item => item.paused);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
canResume(): boolean {
|
||||
if (this.selectedRows?.length) {
|
||||
return !this.selectedRows.some(item => !item.paused);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
stop() {
|
||||
const jobs: string = this.selectedRows
|
||||
.map(item => item.job_type)
|
||||
.join(',');
|
||||
this.operateDialogService.openComfirmDialog({
|
||||
data: undefined,
|
||||
param: jobs,
|
||||
title: 'JOB_SERVICE_DASHBOARD.CONFIRM_STOPPING_JOBS',
|
||||
message: 'JOB_SERVICE_DASHBOARD.CONFIRM_STOPPING_JOBS_CONTENT',
|
||||
targetId: ConfirmationTargets.STOPS_JOBS,
|
||||
buttons: ConfirmationButtons.CONFIRM_CANCEL,
|
||||
});
|
||||
}
|
||||
|
||||
pause() {
|
||||
const jobs: string = this.selectedRows
|
||||
.map(item => item.job_type)
|
||||
.join(',');
|
||||
this.operateDialogService.openComfirmDialog({
|
||||
data: undefined,
|
||||
param: jobs,
|
||||
title: 'JOB_SERVICE_DASHBOARD.CONFIRM_PAUSING_JOBS',
|
||||
message: 'JOB_SERVICE_DASHBOARD.CONFIRM_PAUSING_JOBS_CONTENT',
|
||||
targetId: ConfirmationTargets.PAUSE_JOBS,
|
||||
buttons: ConfirmationButtons.CONFIRM_CANCEL,
|
||||
});
|
||||
}
|
||||
|
||||
resume() {
|
||||
const jobs: string = this.selectedRows
|
||||
.map(item => item.job_type)
|
||||
.join(',');
|
||||
this.operateDialogService.openComfirmDialog({
|
||||
data: undefined,
|
||||
param: jobs,
|
||||
title: 'JOB_SERVICE_DASHBOARD.CONFIRM_RESUMING_JOBS',
|
||||
message: 'JOB_SERVICE_DASHBOARD.CONFIRM_RESUMING_JOBS_CONTENT',
|
||||
targetId: ConfirmationTargets.RESUME_JOBS,
|
||||
buttons: ConfirmationButtons.CONFIRM_CANCEL,
|
||||
});
|
||||
}
|
||||
|
||||
executeStop() {
|
||||
this.loadingStop = true;
|
||||
const operationMessage = new OperateInfo();
|
||||
operationMessage.name =
|
||||
'JOB_SERVICE_DASHBOARD.OPERATION_STOP_SPECIFIED_QUEUES';
|
||||
operationMessage.state = OperationState.progressing;
|
||||
operationMessage.data.name = this.selectedRows
|
||||
.map(item => item.job_type)
|
||||
.join(',');
|
||||
this.operationService.publishInfo(operationMessage);
|
||||
forkJoin(
|
||||
this.selectedRows.map(item => {
|
||||
return this.jobServiceService.actionPendingJobs({
|
||||
jobType: item.job_type,
|
||||
actionRequest: {
|
||||
action: PendingJobsActions.STOP,
|
||||
},
|
||||
});
|
||||
})
|
||||
)
|
||||
.pipe(finalize(() => (this.loadingStop = false)))
|
||||
.subscribe({
|
||||
next: res => {
|
||||
this.messageHandlerService.info(
|
||||
'JOB_SERVICE_DASHBOARD.STOP_SUCCESS'
|
||||
);
|
||||
this.getJobs();
|
||||
operateChanges(operationMessage, OperationState.success);
|
||||
},
|
||||
error: err => {
|
||||
this.messageHandlerService.error(err);
|
||||
operateChanges(
|
||||
operationMessage,
|
||||
OperationState.failure,
|
||||
errorHandler(err)
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
executePause() {
|
||||
this.loadingPause = true;
|
||||
const operationMessage = new OperateInfo();
|
||||
operationMessage.name =
|
||||
'JOB_SERVICE_DASHBOARD.OPERATION_PAUSE_SPECIFIED_QUEUES';
|
||||
operationMessage.state = OperationState.progressing;
|
||||
operationMessage.data.name = this.selectedRows
|
||||
.map(item => item.job_type)
|
||||
.join(',');
|
||||
this.operationService.publishInfo(operationMessage);
|
||||
forkJoin(
|
||||
this.selectedRows.map(item => {
|
||||
return this.jobServiceService.actionPendingJobs({
|
||||
jobType: item.job_type,
|
||||
actionRequest: {
|
||||
action: PendingJobsActions.PAUSE,
|
||||
},
|
||||
});
|
||||
})
|
||||
)
|
||||
.pipe(finalize(() => (this.loadingPause = false)))
|
||||
.subscribe({
|
||||
next: res => {
|
||||
operateChanges(operationMessage, OperationState.success);
|
||||
this.messageHandlerService.info(
|
||||
'JOB_SERVICE_DASHBOARD.PAUSE_SUCCESS'
|
||||
);
|
||||
this.getJobs();
|
||||
},
|
||||
error: err => {
|
||||
this.messageHandlerService.error(err);
|
||||
operateChanges(
|
||||
operationMessage,
|
||||
OperationState.failure,
|
||||
errorHandler(err)
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
executeResume() {
|
||||
this.loadingResume = true;
|
||||
const operationMessage = new OperateInfo();
|
||||
operationMessage.name =
|
||||
'JOB_SERVICE_DASHBOARD.OPERATION_RESUME_SPECIFIED_QUEUES';
|
||||
operationMessage.state = OperationState.progressing;
|
||||
operationMessage.data.name = this.selectedRows
|
||||
.map(item => item.job_type)
|
||||
.join(',');
|
||||
this.operationService.publishInfo(operationMessage);
|
||||
forkJoin(
|
||||
this.selectedRows.map(item => {
|
||||
return this.jobServiceService.actionPendingJobs({
|
||||
jobType: item.job_type,
|
||||
actionRequest: {
|
||||
action: PendingJobsActions.RESUME,
|
||||
},
|
||||
});
|
||||
})
|
||||
)
|
||||
.pipe(finalize(() => (this.loadingResume = false)))
|
||||
.subscribe({
|
||||
next: res => {
|
||||
operateChanges(operationMessage, OperationState.success);
|
||||
this.messageHandlerService.info(
|
||||
'JOB_SERVICE_DASHBOARD.RESUME_SUCCESS'
|
||||
);
|
||||
this.getJobs();
|
||||
},
|
||||
error: err => {
|
||||
this.messageHandlerService.error(err);
|
||||
operateChanges(
|
||||
operationMessage,
|
||||
OperationState.failure,
|
||||
errorHandler(err)
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
{{ 'JOB_SERVICE_DASHBOARD.SCHEDULES' | translate }}
|
||||
</div>
|
||||
<div class="card-block">
|
||||
<div class="duration">
|
||||
{{ 'REPLICATION.TOTAL' | translate }}: {{ scheduleCount }}
|
||||
</div>
|
||||
<div class="clr-row">
|
||||
<div class="clr-col-5 center">
|
||||
<label class="clr-control-label">{{
|
||||
'WEBHOOK.STATUS' | translate
|
||||
}}</label>
|
||||
</div>
|
||||
<div class="clr-col-7">
|
||||
<span class="status" *ngIf="!loadingStatus">{{
|
||||
statusStr() | translate
|
||||
}}</span>
|
||||
<span
|
||||
*ngIf="loadingStatus"
|
||||
class="spinner spinner-inline"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<button
|
||||
[disabled]="loadingStatus || clickOnGoing"
|
||||
[clrLoading]="clickOnGoing"
|
||||
(click)="pauseOrResume()"
|
||||
type="button"
|
||||
class="btn btn-link">
|
||||
{{ btnText() | translate }}
|
||||
</button>
|
||||
<clr-tooltip>
|
||||
<clr-icon
|
||||
clrTooltipTrigger
|
||||
shape="info-circle"
|
||||
size="24"></clr-icon>
|
||||
<clr-tooltip-content
|
||||
clrPosition="top-right"
|
||||
clrSize="lg"
|
||||
*clrIfOpen>
|
||||
<div>
|
||||
{{
|
||||
'JOB_SERVICE_DASHBOARD.SCHEDULE_PAUSE_BTN_INFO'
|
||||
| translate
|
||||
}}
|
||||
</div>
|
||||
<div>
|
||||
{{
|
||||
'JOB_SERVICE_DASHBOARD.SCHEDULE_RESUME_BTN_INFO'
|
||||
| translate
|
||||
}}
|
||||
</div>
|
||||
</clr-tooltip-content>
|
||||
</clr-tooltip>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,15 @@
|
||||
.duration {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 50px;
|
||||
margin: 1.5rem 0 1rem;
|
||||
}
|
||||
|
||||
.card-block {
|
||||
height: 9rem;
|
||||
}
|
||||
|
||||
.center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ScheduleCardComponent } from './schedule-card.component';
|
||||
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 { HttpHeaders, HttpResponse } from '@angular/common/http';
|
||||
import { ScheduleTask } from '../../../../../../ng-swagger-gen/models/schedule-task';
|
||||
import { ScheduleService } from '../../../../../../ng-swagger-gen/services/schedule.service';
|
||||
|
||||
describe('ScheduleCardComponent', () => {
|
||||
let component: ScheduleCardComponent;
|
||||
let fixture: ComponentFixture<ScheduleCardComponent>;
|
||||
|
||||
const fakedJobserviceService = {};
|
||||
|
||||
const fakedScheduleService = {
|
||||
getSchedulePaused() {
|
||||
return of({}).pipe(delay(0));
|
||||
},
|
||||
listSchedulesResponse() {
|
||||
const res: HttpResponse<Array<ScheduleTask>> = new HttpResponse<
|
||||
Array<ScheduleTask>
|
||||
>({
|
||||
headers: new HttpHeaders({ 'x-total-count': '0' }),
|
||||
body: [],
|
||||
});
|
||||
return of(res).pipe(delay(0));
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ScheduleCardComponent],
|
||||
imports: [SharedTestingModule],
|
||||
providers: [
|
||||
{
|
||||
provide: JobserviceService,
|
||||
useValue: fakedJobserviceService,
|
||||
},
|
||||
{
|
||||
provide: ScheduleService,
|
||||
useValue: fakedScheduleService,
|
||||
},
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ScheduleCardComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should show right status and right total count', () => {
|
||||
component.loadingStatus = false;
|
||||
fixture.detectChanges();
|
||||
const totalDiv: HTMLDivElement =
|
||||
fixture.nativeElement.querySelector('.duration');
|
||||
expect(totalDiv.innerText).toContain('0');
|
||||
const statusSpan: HTMLSpanElement =
|
||||
fixture.nativeElement.querySelector('.status');
|
||||
expect(statusSpan.innerText).toEqual(ScheduleStatusString.RUNNING);
|
||||
});
|
||||
});
|
@ -0,0 +1,224 @@
|
||||
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 { finalize } from 'rxjs/operators';
|
||||
import {
|
||||
INTERVAL,
|
||||
JobType,
|
||||
PendingJobsActions,
|
||||
ScheduleExecuteBtnString,
|
||||
ScheduleStatusString,
|
||||
} from '../job-service-dashboard.interface';
|
||||
import {
|
||||
ConfirmationButtons,
|
||||
ConfirmationState,
|
||||
ConfirmationTargets,
|
||||
} from '../../../../shared/entities/shared.const';
|
||||
import { Subscription } from 'rxjs';
|
||||
import {
|
||||
EventService,
|
||||
HarborEvent,
|
||||
} from '../../../../services/event-service/event.service';
|
||||
import { OperationService } from '../../../../shared/components/operation/operation.service';
|
||||
import {
|
||||
operateChanges,
|
||||
OperateInfo,
|
||||
OperationState,
|
||||
} from '../../../../shared/components/operation/operate';
|
||||
import { errorHandler } from '../../../../shared/units/shared.utils';
|
||||
import { ScheduleService } from '../../../../../../ng-swagger-gen/services/schedule.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-schedule-card',
|
||||
templateUrl: './schedule-card.component.html',
|
||||
styleUrls: ['./schedule-card.component.scss'],
|
||||
})
|
||||
export class ScheduleCardComponent implements OnInit, OnDestroy {
|
||||
scheduleCount: number = 0;
|
||||
isPaused: boolean = false;
|
||||
loadingStatus: boolean = false;
|
||||
confirmSub: Subscription;
|
||||
statusTimeout: any;
|
||||
clickOnGoing: boolean = false;
|
||||
constructor(
|
||||
private operateDialogService: ConfirmationDialogService,
|
||||
private jobServiceService: JobserviceService,
|
||||
private messageHandlerService: MessageHandlerService,
|
||||
private eventService: EventService,
|
||||
private operationService: OperationService,
|
||||
private scheduleService: ScheduleService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.initSub();
|
||||
this.loopGetStatus(true);
|
||||
this.getScheduleCount();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.statusTimeout) {
|
||||
clearTimeout(this.statusTimeout);
|
||||
this.statusTimeout = null;
|
||||
}
|
||||
if (this.confirmSub) {
|
||||
this.confirmSub.unsubscribe();
|
||||
this.confirmSub = null;
|
||||
}
|
||||
}
|
||||
|
||||
initSub() {
|
||||
if (!this.confirmSub) {
|
||||
this.confirmSub =
|
||||
this.operateDialogService.confirmationConfirm$.subscribe(
|
||||
message => {
|
||||
if (
|
||||
message &&
|
||||
message.state === ConfirmationState.CONFIRMED
|
||||
) {
|
||||
if (
|
||||
message.source ===
|
||||
ConfirmationTargets.RESUME_ALL_SCHEDULES
|
||||
) {
|
||||
this.execute(PendingJobsActions.RESUME);
|
||||
}
|
||||
if (
|
||||
message.source ===
|
||||
ConfirmationTargets.PAUSE_ALL_SCHEDULES
|
||||
) {
|
||||
this.execute(PendingJobsActions.PAUSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
loopGetStatus(withLoading?: boolean) {
|
||||
if (withLoading) {
|
||||
this.loadingStatus = true;
|
||||
}
|
||||
this.scheduleService
|
||||
.getSchedulePaused({
|
||||
jobType: JobType.ALL,
|
||||
})
|
||||
.pipe(finalize(() => (this.loadingStatus = false)))
|
||||
.subscribe(res => {
|
||||
this.isPaused = res?.paused;
|
||||
this.statusTimeout = setTimeout(() => {
|
||||
this.loopGetStatus();
|
||||
this.getScheduleCount();
|
||||
}, INTERVAL);
|
||||
});
|
||||
}
|
||||
|
||||
getScheduleCount() {
|
||||
this.scheduleService
|
||||
.listSchedulesResponse({
|
||||
page: 1,
|
||||
pageSize: 1,
|
||||
})
|
||||
.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);
|
||||
}
|
||||
}
|
||||
},
|
||||
error: err => {
|
||||
this.messageHandlerService.error(err);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
btnText(): string {
|
||||
if (this.isPaused) {
|
||||
return ScheduleExecuteBtnString.RESUME_ALL;
|
||||
}
|
||||
return ScheduleExecuteBtnString.PAUSE_ALL;
|
||||
}
|
||||
|
||||
statusStr(): string {
|
||||
if (this.isPaused) {
|
||||
return ScheduleStatusString.PAUSED;
|
||||
}
|
||||
return ScheduleStatusString.RUNNING;
|
||||
}
|
||||
|
||||
pauseOrResume() {
|
||||
if (this.isPaused) {
|
||||
this.operateDialogService.openComfirmDialog({
|
||||
data: undefined,
|
||||
param: null,
|
||||
title: 'JOB_SERVICE_DASHBOARD.CONFIRM_RESUMING_ALL',
|
||||
message: 'JOB_SERVICE_DASHBOARD.CONFIRM_RESUMING_ALL_CONTENT',
|
||||
targetId: ConfirmationTargets.RESUME_ALL_SCHEDULES,
|
||||
buttons: ConfirmationButtons.CONFIRM_CANCEL,
|
||||
});
|
||||
} else {
|
||||
this.operateDialogService.openComfirmDialog({
|
||||
data: undefined,
|
||||
param: null,
|
||||
title: 'JOB_SERVICE_DASHBOARD.CONFIRM_PAUSING_ALL',
|
||||
message: 'JOB_SERVICE_DASHBOARD.CONFIRM_PAUSING_ALL_CONTENT',
|
||||
targetId: ConfirmationTargets.PAUSE_ALL_SCHEDULES,
|
||||
buttons: ConfirmationButtons.CONFIRM_CANCEL,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
execute(action: PendingJobsActions) {
|
||||
this.clickOnGoing = true;
|
||||
const operationMessage = new OperateInfo();
|
||||
operationMessage.name = this.isPaused
|
||||
? 'JOB_SERVICE_DASHBOARD.OPERATION_RESUME_SCHEDULE'
|
||||
: 'JOB_SERVICE_DASHBOARD.OPERATION_PAUSE_SCHEDULE';
|
||||
operationMessage.state = OperationState.progressing;
|
||||
this.operationService.publishInfo(operationMessage);
|
||||
this.jobServiceService
|
||||
.actionPendingJobs({
|
||||
jobType: JobType.SCHEDULER,
|
||||
actionRequest: {
|
||||
action: action,
|
||||
},
|
||||
})
|
||||
.pipe(finalize(() => (this.clickOnGoing = false)))
|
||||
.subscribe({
|
||||
next: res => {
|
||||
if (this.isPaused) {
|
||||
this.messageHandlerService.info(
|
||||
'JOB_SERVICE_DASHBOARD.RESUME_ALL_SUCCESS'
|
||||
);
|
||||
} else {
|
||||
this.messageHandlerService.info(
|
||||
'JOB_SERVICE_DASHBOARD.PAUSE_ALL_SUCCESS'
|
||||
);
|
||||
}
|
||||
this.refreshNow();
|
||||
this.eventService.publish(
|
||||
HarborEvent.REFRESH_JOB_SERVICE_DASHBOARD
|
||||
);
|
||||
operateChanges(operationMessage, OperationState.success);
|
||||
},
|
||||
error: err => {
|
||||
this.messageHandlerService.error(err);
|
||||
operateChanges(
|
||||
operationMessage,
|
||||
OperationState.failure,
|
||||
errorHandler(err)
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
refreshNow() {
|
||||
if (this.statusTimeout) {
|
||||
clearTimeout(this.statusTimeout);
|
||||
this.statusTimeout = null;
|
||||
}
|
||||
this.loopGetStatus(true);
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
<clr-datagrid
|
||||
[clrDgLoading]="loadingSchedules"
|
||||
(clrDgRefresh)="clrLoad($event)">
|
||||
<clr-dg-action-bar class="action-bar">
|
||||
<span class="refresh-btn" (click)="clrLoad()">
|
||||
<clr-icon shape="refresh"></clr-icon>
|
||||
</span>
|
||||
</clr-dg-action-bar>
|
||||
<clr-dg-column [clrDgSortBy]="'id'">{{
|
||||
'TAG_RETENTION.SERIAL' | translate
|
||||
}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="'vendor_type'">{{
|
||||
'JOB_SERVICE_DASHBOARD.VENDOR_TYPE' | translate
|
||||
}}</clr-dg-column>
|
||||
<clr-dg-column>{{
|
||||
'JOB_SERVICE_DASHBOARD.VENDOR_ID' | translate
|
||||
}}</clr-dg-column>
|
||||
<clr-dg-column>{{
|
||||
'JOB_SERVICE_DASHBOARD.EXTRA_ATTR' | translate
|
||||
}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgSortBy]="'creation_time'">{{
|
||||
'PROJECT.CREATION_TIME' | translate
|
||||
}}</clr-dg-column>
|
||||
<clr-dg-placeholder>{{
|
||||
'JOB_SERVICE_DASHBOARD.NO_SCHEDULE' | translate
|
||||
}}</clr-dg-placeholder>
|
||||
<clr-dg-row *ngFor="let p of schedules" [clrDgItem]="p">
|
||||
<clr-dg-cell>{{ p.id }}</clr-dg-cell>
|
||||
<clr-dg-cell>{{ p.vendor_type }}</clr-dg-cell>
|
||||
<clr-dg-cell>{{ p.vendor_id }}</clr-dg-cell>
|
||||
<clr-dg-cell class="flex">
|
||||
<clr-signpost>
|
||||
<a class="btn btn-link link-normal" clrSignpostTrigger>{{
|
||||
p.extra_attrs
|
||||
}}</a>
|
||||
<clr-signpost-content
|
||||
class="pre"
|
||||
[clrPosition]="'top-middle'"
|
||||
*clrIfOpen>
|
||||
<pre
|
||||
[innerHTML]="
|
||||
json(p.extra_attrs) | json | markdown
|
||||
"></pre>
|
||||
</clr-signpost-content>
|
||||
</clr-signpost>
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell>{{ p.creation_time | harborDatetime }}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
<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>
|
||||
<span *ngIf="total">
|
||||
{{ pagination.firstItem + 1 }} -
|
||||
{{ pagination.lastItem + 1 }}
|
||||
{{ 'GROUP.OF' | translate }}
|
||||
</span>
|
||||
{{ total }} {{ 'GROUP.ITEMS' | translate }}
|
||||
</clr-dg-pagination>
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
@ -0,0 +1,24 @@
|
||||
.action-bar {
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.link-normal {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
max-width: 10rem;
|
||||
text-transform: unset;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.pre {
|
||||
min-width: 25rem;
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
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';
|
||||
|
||||
describe('ScheduleListComponent', () => {
|
||||
let component: ScheduleListComponent;
|
||||
let fixture: ComponentFixture<ScheduleListComponent>;
|
||||
|
||||
const mockedSchedules: ScheduleTask[] = [
|
||||
{ id: 1, vendor_type: 'test1' },
|
||||
{ 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));
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ScheduleListComponent],
|
||||
imports: [SharedTestingModule],
|
||||
providers: [
|
||||
{
|
||||
provide: ScheduleService,
|
||||
useValue: fakedScheduleService,
|
||||
},
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(ScheduleListComponent);
|
||||
component = fixture.componentInstance;
|
||||
component.loadingSchedules = true;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render list', async () => {
|
||||
await fixture.whenStable();
|
||||
component.loadingSchedules = false;
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const rows = fixture.nativeElement.querySelectorAll('clr-dg-row');
|
||||
expect(rows.length).toEqual(2);
|
||||
});
|
||||
});
|
@ -0,0 +1,99 @@
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ClrDatagridStateInterface } from '@clr/angular/data/datagrid/interfaces/state.interface';
|
||||
import {
|
||||
doSorting,
|
||||
getPageSizeFromLocalStorage,
|
||||
PageSizeMapKeys,
|
||||
setPageSizeToLocalStorage,
|
||||
} from '../../../../shared/units/utils';
|
||||
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';
|
||||
|
||||
@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[] = [];
|
||||
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
|
||||
) {}
|
||||
|
||||
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();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
clrLoad(state?: ClrDatagridStateInterface): void {
|
||||
if (state?.page?.size) {
|
||||
this.pageSize = state.page.size;
|
||||
setPageSizeToLocalStorage(
|
||||
PageSizeMapKeys.SCHEDULE_LIST_COMPONENT,
|
||||
this.pageSize
|
||||
);
|
||||
}
|
||||
this.loadingSchedules = true;
|
||||
this.scheduleService
|
||||
.listSchedulesResponse({
|
||||
page: this.page,
|
||||
pageSize: this.pageSize,
|
||||
})
|
||||
.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);
|
||||
},
|
||||
error: err => {
|
||||
this.messageHandlerService.error(err);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
json(v: string): object {
|
||||
if (v) {
|
||||
return JSON.parse(v);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
<div class="loading" [style.background-color]="getBigBGColor()">
|
||||
<div
|
||||
class="left"
|
||||
[style.background-color]="getLeftBGColor()"
|
||||
[style.transform]="getLeftRotate()"></div>
|
||||
<div
|
||||
class="right"
|
||||
[style.background-color]="getRightBGColor()"
|
||||
[style.transform]="getRightRotate()"></div>
|
||||
<div
|
||||
class="center"
|
||||
[style.color]="getBigBGColor()"
|
||||
[style.background-color]="getSmallBGColor()">
|
||||
{{ numerator }}/{{ denominator }}
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,44 @@
|
||||
$big: 150px;
|
||||
$small: 100px;
|
||||
|
||||
.loading {
|
||||
display: flex;
|
||||
width: $big;
|
||||
height: $big;
|
||||
position: relative;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.center {
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: auto;
|
||||
width: $small;
|
||||
height: $small;
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
line-height: $small;
|
||||
text-align: center;
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.left,
|
||||
.right {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.left {
|
||||
border-radius: $big 0 0 $big;
|
||||
transform-origin: right center;
|
||||
}
|
||||
|
||||
.right {
|
||||
border-radius: 0 $big $big 0;
|
||||
transform-origin: left center;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,23 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { NO_ERRORS_SCHEMA } from '@angular/core';
|
||||
import { DonutChartComponent } from './donut-chart.component';
|
||||
|
||||
describe('DonutChartComponent', () => {
|
||||
let component: DonutChartComponent;
|
||||
let fixture: ComponentFixture<DonutChartComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
schemas: [NO_ERRORS_SCHEMA],
|
||||
declarations: [DonutChartComponent],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(DonutChartComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,91 @@
|
||||
import { Component, Input } from '@angular/core';
|
||||
import { HAS_STYLE_MODE, StyleMode } from '../../../../../services/theme';
|
||||
|
||||
// this donut chart is created based on css border-radius and transform: rotate()
|
||||
// as we have two themes, so need to set different colors for different theme
|
||||
// for dark theme: .center bg-color #21333b; denominator #405C6B; numerator #49aeda
|
||||
// for light theme: .center bg-color #fff; denominator #ccc; numerator #0072a3
|
||||
|
||||
enum DarkColors {
|
||||
CENTER_BG_COLOR = '#21333b',
|
||||
DEN_COLOR = '#405C6B',
|
||||
NUM_COLOR = '#49aeda',
|
||||
}
|
||||
|
||||
enum LightColors {
|
||||
CENTER_BG_COLOR = '#fff',
|
||||
DEN_COLOR = '#ccc',
|
||||
NUM_COLOR = '#0072a3',
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-donut-chart',
|
||||
templateUrl: './donut-chart.component.html',
|
||||
styleUrls: ['./donut-chart.component.scss'],
|
||||
})
|
||||
export class DonutChartComponent {
|
||||
@Input()
|
||||
denominator: number;
|
||||
|
||||
@Input()
|
||||
numerator: number;
|
||||
|
||||
isDarkTheme(): boolean {
|
||||
return localStorage?.getItem(HAS_STYLE_MODE) === StyleMode.DARK;
|
||||
}
|
||||
|
||||
getSmallBGColor(): string {
|
||||
if (this.isDarkTheme()) {
|
||||
return DarkColors.CENTER_BG_COLOR;
|
||||
}
|
||||
return LightColors.CENTER_BG_COLOR;
|
||||
}
|
||||
|
||||
getBigBGColor(): string {
|
||||
if (this.isDarkTheme()) {
|
||||
return DarkColors.NUM_COLOR;
|
||||
}
|
||||
return LightColors.NUM_COLOR;
|
||||
}
|
||||
|
||||
getLeftBGColor() {
|
||||
if (this.isDarkTheme()) {
|
||||
return DarkColors.DEN_COLOR;
|
||||
}
|
||||
return LightColors.DEN_COLOR;
|
||||
}
|
||||
|
||||
getRightBGColor(): string {
|
||||
if (this.getDegree() > 180) {
|
||||
if (this.isDarkTheme()) {
|
||||
return DarkColors.NUM_COLOR;
|
||||
}
|
||||
return LightColors.NUM_COLOR;
|
||||
}
|
||||
if (this.isDarkTheme()) {
|
||||
return DarkColors.DEN_COLOR;
|
||||
}
|
||||
return LightColors.DEN_COLOR;
|
||||
}
|
||||
|
||||
getDegree(): number {
|
||||
if (this.numerator && this.denominator) {
|
||||
return (this.numerator / this.denominator) * 360;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
getRightRotate() {
|
||||
if (this.getDegree() > 0 && this.getDegree() <= 180) {
|
||||
return `rotate(${this.getDegree()}deg)`;
|
||||
}
|
||||
return `rotate(0)`;
|
||||
}
|
||||
|
||||
getLeftRotate() {
|
||||
if (this.getDegree() > 180) {
|
||||
return `rotate(${this.getDegree() - 180}deg)`;
|
||||
}
|
||||
return `rotate(0)`;
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
{{ 'JOB_SERVICE_DASHBOARD.WORKERS' | translate }}
|
||||
</div>
|
||||
<div class="card-block donut-chart">
|
||||
<app-donut-chart
|
||||
[denominator]="denominator"
|
||||
[numerator]="busyWorkers?.length"></app-donut-chart>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<button
|
||||
[disabled]="loadingFreeAll || busyWorkers?.length === 0"
|
||||
[clrLoading]="loadingFreeAll"
|
||||
(click)="freeAll()"
|
||||
type="button"
|
||||
class="btn btn-link">
|
||||
{{ 'JOB_SERVICE_DASHBOARD.FREE_ALL' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,9 @@
|
||||
.donut-chart {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.card-block {
|
||||
height: 9rem;
|
||||
}
|
@ -0,0 +1,53 @@
|
||||
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';
|
||||
|
||||
describe('WorkerCardComponent', () => {
|
||||
let component: WorkerCardComponent;
|
||||
let fixture: ComponentFixture<WorkerCardComponent>;
|
||||
|
||||
const mockedWorkers: Worker[] = [
|
||||
{ id: '1', job_id: '1', job_name: 'test1' },
|
||||
{ id: '2', job_id: '2', job_name: 'test2' },
|
||||
];
|
||||
|
||||
const fakedJobserviceService = {
|
||||
getWorkers() {
|
||||
return of(mockedWorkers);
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
schemas: [NO_ERRORS_SCHEMA],
|
||||
declarations: [WorkerCardComponent],
|
||||
imports: [SharedTestingModule],
|
||||
providers: [
|
||||
{
|
||||
provide: JobserviceService,
|
||||
useValue: fakedJobserviceService,
|
||||
},
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(WorkerCardComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should init timeout', () => {
|
||||
expect(component.statusTimeout).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should get workers', () => {
|
||||
expect(component.busyWorkers.length).toEqual(2);
|
||||
});
|
||||
});
|
@ -0,0 +1,149 @@
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Worker } from 'ng-swagger-gen/models';
|
||||
import { JobserviceService } from 'ng-swagger-gen/services';
|
||||
import { finalize, Subscription } from 'rxjs';
|
||||
import { ConfirmationDialogService } from 'src/app/base/global-confirmation-dialog/confirmation-dialog.service';
|
||||
import {
|
||||
ConfirmationButtons,
|
||||
ConfirmationState,
|
||||
ConfirmationTargets,
|
||||
} 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,
|
||||
OperateInfo,
|
||||
OperationState,
|
||||
} from '../../../../shared/components/operation/operate';
|
||||
import { errorHandler } from '../../../../shared/units/shared.utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-worker-card',
|
||||
templateUrl: './worker-card.component.html',
|
||||
styleUrls: ['./worker-card.component.scss'],
|
||||
})
|
||||
export class WorkerCardComponent implements OnInit, OnDestroy {
|
||||
denominator: number = 0;
|
||||
statusTimeout: any;
|
||||
loadingFreeAll: boolean = false;
|
||||
confirmSub: Subscription;
|
||||
busyWorkers: Worker[] = [];
|
||||
|
||||
constructor(
|
||||
private operateDialogService: ConfirmationDialogService,
|
||||
private jobServiceService: JobserviceService,
|
||||
private messageHandlerService: MessageHandlerService,
|
||||
private eventService: EventService,
|
||||
private operationService: OperationService
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loopWorkerStatus();
|
||||
this.initSub();
|
||||
}
|
||||
ngOnDestroy(): void {
|
||||
if (this.statusTimeout) {
|
||||
clearTimeout(this.statusTimeout);
|
||||
this.statusTimeout = null;
|
||||
}
|
||||
if (this.confirmSub) {
|
||||
this.confirmSub.unsubscribe();
|
||||
this.confirmSub = null;
|
||||
}
|
||||
}
|
||||
|
||||
initSub() {
|
||||
if (!this.confirmSub) {
|
||||
this.confirmSub =
|
||||
this.operateDialogService.confirmationConfirm$.subscribe(
|
||||
message => {
|
||||
if (
|
||||
message &&
|
||||
message.state === ConfirmationState.CONFIRMED
|
||||
) {
|
||||
if (
|
||||
message.source ===
|
||||
ConfirmationTargets.FREE_ALL_WORKERS
|
||||
) {
|
||||
this.executeFreeAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
loopWorkerStatus() {
|
||||
this.jobServiceService
|
||||
.getWorkers({ poolId: All.ALL_WORKERS.toString() })
|
||||
.subscribe(res => {
|
||||
if (res) {
|
||||
this.denominator = res.length;
|
||||
this.busyWorkers = [];
|
||||
res.forEach(item => {
|
||||
if (item.job_id) {
|
||||
this.busyWorkers.push(item);
|
||||
}
|
||||
});
|
||||
this.statusTimeout = setTimeout(() => {
|
||||
this.loopWorkerStatus();
|
||||
}, INTERVAL);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
freeAll() {
|
||||
this.operateDialogService.openComfirmDialog({
|
||||
data: undefined,
|
||||
param: null,
|
||||
title: 'JOB_SERVICE_DASHBOARD.CONFIRM_FREE_ALL',
|
||||
message: 'JOB_SERVICE_DASHBOARD.CONFIRM_FREE_ALL_CONTENT',
|
||||
targetId: ConfirmationTargets.FREE_ALL_WORKERS,
|
||||
buttons: ConfirmationButtons.CONFIRM_CANCEL,
|
||||
});
|
||||
}
|
||||
|
||||
executeFreeAll() {
|
||||
this.loadingFreeAll = true;
|
||||
const operationMessage = new OperateInfo();
|
||||
operationMessage.name = 'JOB_SERVICE_DASHBOARD.OPERATION_FREE_ALL';
|
||||
operationMessage.state = OperationState.progressing;
|
||||
this.operationService.publishInfo(operationMessage);
|
||||
this.jobServiceService
|
||||
.stopRunningJob({ jobId: All.ALL_WORKERS })
|
||||
.pipe(finalize(() => (this.loadingFreeAll = false)))
|
||||
.subscribe({
|
||||
next: res => {
|
||||
this.messageHandlerService.info(
|
||||
'JOB_SERVICE_DASHBOARD.FREE_ALL_SUCCESS'
|
||||
);
|
||||
this.refreshNow();
|
||||
this.eventService.publish(
|
||||
HarborEvent.REFRESH_JOB_SERVICE_DASHBOARD
|
||||
);
|
||||
operateChanges(operationMessage, OperationState.success);
|
||||
},
|
||||
error: err => {
|
||||
this.messageHandlerService.error(err);
|
||||
operateChanges(
|
||||
operationMessage,
|
||||
OperationState.failure,
|
||||
errorHandler(err)
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
refreshNow() {
|
||||
if (this.statusTimeout) {
|
||||
clearTimeout(this.statusTimeout);
|
||||
this.statusTimeout = null;
|
||||
}
|
||||
this.loopWorkerStatus();
|
||||
}
|
||||
}
|
@ -0,0 +1,151 @@
|
||||
<h2>{{ 'JOB_SERVICE_DASHBOARD.WORKER_POOL' | translate }}</h2>
|
||||
<clr-datagrid
|
||||
[clrDgLoading]="loadingPools"
|
||||
(clrDgRefresh)="clrLoadPool($event)"
|
||||
(clrDgSelectedChange)="selectionChanged()"
|
||||
[(clrDgSingleSelected)]="selectedPool">
|
||||
<clr-dg-action-bar class="action-bar mt-0">
|
||||
<span class="refresh-btn" (click)="getPools()">
|
||||
<clr-icon shape="refresh"></clr-icon>
|
||||
</span>
|
||||
</clr-dg-action-bar>
|
||||
<clr-dg-column [clrDgField]="'worker_pool_id'">{{
|
||||
'JOB_SERVICE_DASHBOARD.WORKER_POOL_ID' | translate
|
||||
}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'pid'">{{
|
||||
'JOB_SERVICE_DASHBOARD.PID' | translate
|
||||
}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'start_at'">{{
|
||||
'JOB_SERVICE_DASHBOARD.START_AT' | translate
|
||||
}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'heartbeat_at'">{{
|
||||
'JOB_SERVICE_DASHBOARD.HEARTBEAT_AT' | translate
|
||||
}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'concurrency'" [clrDgColType]="'number'">{{
|
||||
'JOB_SERVICE_DASHBOARD.CONCURRENCY' | translate
|
||||
}}</clr-dg-column>
|
||||
<clr-dg-placeholder>{{
|
||||
'JOB_SERVICE_DASHBOARD.NO_WORKER_POOL' | translate
|
||||
}}</clr-dg-placeholder>
|
||||
<clr-dg-row *clrDgItems="let p of pools" [clrDgItem]="p">
|
||||
<clr-dg-cell>{{ p.worker_pool_id }}</clr-dg-cell>
|
||||
<clr-dg-cell>{{ p.pid }}</clr-dg-cell>
|
||||
|
||||
<clr-dg-cell>{{ p.start_at | harborDatetime }}</clr-dg-cell>
|
||||
<clr-dg-cell>{{ p.heartbeat_at | harborDatetime }}</clr-dg-cell>
|
||||
<clr-dg-cell>{{ p.concurrency }}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
<clr-dg-pagination
|
||||
#pagination
|
||||
[clrDgPageSize]="5"
|
||||
[clrDgTotalItems]="pools?.length">
|
||||
<clr-dg-page-size [clrPageSizeOptions]="[5, 15, 30]">{{
|
||||
'PAGINATION.PAGE_SIZE' | translate
|
||||
}}</clr-dg-page-size>
|
||||
<span *ngIf="pools?.length">
|
||||
{{ pagination.firstItem + 1 }} -
|
||||
{{ pagination.lastItem + 1 }}
|
||||
{{ 'GROUP.OF' | translate }}
|
||||
</span>
|
||||
{{ pools?.length }} {{ 'GROUP.ITEMS' | translate }}
|
||||
</clr-dg-pagination>
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
<ng-container *ngIf="selectedPool">
|
||||
<h2>{{ 'JOB_SERVICE_DASHBOARD.WORKERS' | translate }}</h2>
|
||||
<clr-datagrid
|
||||
[clrDgLoading]="loadingWorkers"
|
||||
(clrDgRefresh)="clrLoadWorker($event)"
|
||||
[(clrDgSelected)]="selected">
|
||||
<clr-dg-action-bar class="action-bar-worker">
|
||||
<div>
|
||||
<button
|
||||
[clrLoading]="loadingFree"
|
||||
[disabled]="loadingFree || !canFree()"
|
||||
type="button"
|
||||
class="btn btn-secondary"
|
||||
(click)="freeWorker()">
|
||||
{{ 'JOB_SERVICE_DASHBOARD.FREE' | translate }}
|
||||
</button>
|
||||
<clr-tooltip>
|
||||
<clr-icon
|
||||
clrTooltipTrigger
|
||||
shape="info-circle"
|
||||
size="24"></clr-icon>
|
||||
<clr-tooltip-content
|
||||
clrPosition="top-right"
|
||||
clrSize="lg"
|
||||
*clrIfOpen>
|
||||
<span>
|
||||
{{
|
||||
'JOB_SERVICE_DASHBOARD.WORKER_FREE_BTN_INFO'
|
||||
| translate
|
||||
}}
|
||||
</span>
|
||||
</clr-tooltip-content>
|
||||
</clr-tooltip>
|
||||
</div>
|
||||
<span class="refresh-btn" (click)="selectionChanged()">
|
||||
<clr-icon shape="refresh"></clr-icon>
|
||||
</span>
|
||||
</clr-dg-action-bar>
|
||||
<clr-dg-column [clrDgField]="'id'">{{
|
||||
'JOB_SERVICE_DASHBOARD.WORKER_ID' | translate
|
||||
}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'job_name'">{{
|
||||
'CVE_EXPORT.JOB_NAME' | translate
|
||||
}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'job_id'">{{
|
||||
'JOB_SERVICE_DASHBOARD.JOB_ID' | translate
|
||||
}}</clr-dg-column>
|
||||
<clr-dg-column>{{
|
||||
'JOB_SERVICE_DASHBOARD.JOB_PARAM' | translate
|
||||
}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'start_at'">{{
|
||||
'JOB_SERVICE_DASHBOARD.START_AT' | translate
|
||||
}}</clr-dg-column>
|
||||
<clr-dg-column [clrDgField]="'checkin_at'">{{
|
||||
'JOB_SERVICE_DASHBOARD.CHECK_IN_AT' | translate
|
||||
}}</clr-dg-column>
|
||||
<clr-dg-placeholder>{{
|
||||
'JOB_SERVICE_DASHBOARD.NO_WORKER' | translate
|
||||
}}</clr-dg-placeholder>
|
||||
<clr-dg-row *clrDgItems="let w of workers" [clrDgItem]="w">
|
||||
<clr-dg-cell>{{ w.id }}</clr-dg-cell>
|
||||
<clr-dg-cell>{{ w.job_name }}</clr-dg-cell>
|
||||
<clr-dg-cell>{{ w.job_id }}</clr-dg-cell>
|
||||
<clr-dg-cell class="flex">
|
||||
<clr-signpost>
|
||||
<a class="btn btn-link link-normal" clrSignpostTrigger>{{
|
||||
w.args
|
||||
}}</a>
|
||||
<clr-signpost-content
|
||||
class="pre"
|
||||
[clrPosition]="'top-middle'"
|
||||
*clrIfOpen>
|
||||
<pre [innerHTML]="json(w.args) | json | markdown"></pre>
|
||||
</clr-signpost-content>
|
||||
</clr-signpost>
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell>{{ w.start_at | harborDatetime }}</clr-dg-cell>
|
||||
<clr-dg-cell>{{ w.checkin_at | harborDatetime }}</clr-dg-cell>
|
||||
</clr-dg-row>
|
||||
<clr-dg-footer>
|
||||
<clr-dg-pagination
|
||||
#pagination
|
||||
[clrDgPageSize]="workerPageSize"
|
||||
[clrDgTotalItems]="workers?.length">
|
||||
<clr-dg-page-size [clrPageSizeOptions]="[15, 25, 50]">{{
|
||||
'PAGINATION.PAGE_SIZE' | translate
|
||||
}}</clr-dg-page-size>
|
||||
<span *ngIf="workers?.length">
|
||||
{{ pagination.firstItem + 1 }} -
|
||||
{{ pagination.lastItem + 1 }}
|
||||
{{ 'GROUP.OF' | translate }}
|
||||
</span>
|
||||
{{ workers?.length }} {{ 'GROUP.ITEMS' | translate }}
|
||||
</clr-dg-pagination>
|
||||
</clr-dg-footer>
|
||||
</clr-datagrid>
|
||||
</ng-container>
|
@ -0,0 +1,30 @@
|
||||
.link-normal {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
max-width: 10rem;
|
||||
text-transform: unset;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.action-bar {
|
||||
display: flex;
|
||||
justify-content: right;
|
||||
}
|
||||
|
||||
.refresh-btn {
|
||||
margin-right: 1rem;
|
||||
}
|
||||
|
||||
.action-bar-worker {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.pre {
|
||||
min-width: 25rem;
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { WorkerListComponent } from './worker-list.component';
|
||||
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 { Worker, WorkerPool } from 'ng-swagger-gen/models';
|
||||
|
||||
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' },
|
||||
];
|
||||
|
||||
const mockedPools: WorkerPool[] = [
|
||||
{ pid: 1, concurrency: 10, worker_pool_id: '1' },
|
||||
];
|
||||
|
||||
const fakedJobserviceService = {
|
||||
getWorkers() {
|
||||
return of(mockedWorkers).pipe(delay(0));
|
||||
},
|
||||
getWorkerPools() {
|
||||
return of(mockedPools).pipe(delay(0));
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [WorkerListComponent],
|
||||
imports: [SharedTestingModule],
|
||||
providers: [
|
||||
{
|
||||
provide: JobserviceService,
|
||||
useValue: fakedJobserviceService,
|
||||
},
|
||||
],
|
||||
}).compileComponents();
|
||||
fixture = TestBed.createComponent(WorkerListComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should render worker list', async () => {
|
||||
component.selectionChanged();
|
||||
await fixture.whenStable();
|
||||
component.loadingPools = false;
|
||||
component.loadingWorkers = false;
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const rows = fixture.nativeElement.querySelectorAll('clr-dg-row');
|
||||
expect(rows.length).toEqual(3); // 1 + 2
|
||||
});
|
||||
});
|
@ -0,0 +1,242 @@
|
||||
import { Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ClrDatagridStateInterface } from '@clr/angular/data/datagrid/interfaces/state.interface';
|
||||
import { Worker } from 'ng-swagger-gen/models';
|
||||
import { WorkerPool } from 'ng-swagger-gen/models/worker-pool';
|
||||
import { JobserviceService } from 'ng-swagger-gen/services';
|
||||
import { finalize, forkJoin, Subscription } from 'rxjs';
|
||||
import { MessageHandlerService } from 'src/app/shared/services/message-handler.service';
|
||||
import {
|
||||
getPageSizeFromLocalStorage,
|
||||
PageSizeMapKeys,
|
||||
setPageSizeToLocalStorage,
|
||||
} from 'src/app/shared/units/utils';
|
||||
import { ConfirmationMessage } from '../../../global-confirmation-dialog/confirmation-message';
|
||||
import {
|
||||
ConfirmationButtons,
|
||||
ConfirmationState,
|
||||
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,
|
||||
OperateInfo,
|
||||
OperationState,
|
||||
} from '../../../../shared/components/operation/operate';
|
||||
import { errorHandler } from '../../../../shared/units/shared.utils';
|
||||
|
||||
@Component({
|
||||
selector: 'app-worker-list',
|
||||
templateUrl: './worker-list.component.html',
|
||||
styleUrls: ['./worker-list.component.scss'],
|
||||
})
|
||||
export class WorkerListComponent implements OnInit, OnDestroy {
|
||||
loadingPools: boolean = false;
|
||||
selectedPool: WorkerPool;
|
||||
pools: WorkerPool[] = [];
|
||||
loadingWorkers: boolean = false;
|
||||
workers: Worker[] = [];
|
||||
selected: Worker[] = [];
|
||||
|
||||
poolPageSize: number = getPageSizeFromLocalStorage(
|
||||
PageSizeMapKeys.WORKER_LIST_COMPONENT_POOL,
|
||||
5
|
||||
);
|
||||
|
||||
workerPageSize: number = getPageSizeFromLocalStorage(
|
||||
PageSizeMapKeys.WORKER_LIST_COMPONENT_WORKER
|
||||
);
|
||||
loadingFree: boolean = false;
|
||||
confirmSub: Subscription;
|
||||
eventSub: Subscription;
|
||||
constructor(
|
||||
private jobServiceService: JobserviceService,
|
||||
private messageHandlerService: MessageHandlerService,
|
||||
private operateDialogService: ConfirmationDialogService,
|
||||
private eventService: EventService,
|
||||
private operationService: OperationService
|
||||
) {}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
initSub() {
|
||||
if (!this.confirmSub) {
|
||||
this.confirmSub =
|
||||
this.operateDialogService.confirmationConfirm$.subscribe(
|
||||
message => {
|
||||
if (
|
||||
message &&
|
||||
message.state === ConfirmationState.CONFIRMED
|
||||
) {
|
||||
if (
|
||||
message.source ===
|
||||
ConfirmationTargets.FREE_SPECIFIED_WORKERS
|
||||
) {
|
||||
this.executeFreeWorkers();
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
initEventSub() {
|
||||
if (!this.eventSub) {
|
||||
this.eventSub = this.eventService.subscribe(
|
||||
HarborEvent.REFRESH_JOB_SERVICE_DASHBOARD,
|
||||
() => {
|
||||
this.selectionChanged();
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
getPools() {
|
||||
this.loadingPools = true;
|
||||
this.jobServiceService
|
||||
.getWorkerPools()
|
||||
.pipe(finalize(() => (this.loadingPools = false)))
|
||||
.subscribe({
|
||||
next: res => {
|
||||
this.pools = res;
|
||||
if (res?.length) {
|
||||
this.selectedPool = res[0];
|
||||
this.selectionChanged();
|
||||
}
|
||||
},
|
||||
error: err => {
|
||||
this.messageHandlerService.error(err);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
clrLoadPool(state: ClrDatagridStateInterface): void {
|
||||
if (state?.page?.size) {
|
||||
this.poolPageSize = state.page.size;
|
||||
setPageSizeToLocalStorage(
|
||||
PageSizeMapKeys.WORKER_LIST_COMPONENT_POOL,
|
||||
this.poolPageSize
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
clrLoadWorker(state: ClrDatagridStateInterface): void {
|
||||
if (state?.page?.size) {
|
||||
this.workerPageSize = state.page.size;
|
||||
setPageSizeToLocalStorage(
|
||||
PageSizeMapKeys.WORKER_LIST_COMPONENT_POOL,
|
||||
this.workerPageSize
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
canFree(): boolean {
|
||||
if (this.selected?.length) {
|
||||
let flag: boolean = true;
|
||||
this.selected.forEach(item => {
|
||||
if (!item.job_id) {
|
||||
flag = false;
|
||||
}
|
||||
});
|
||||
return flag;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
freeWorker() {
|
||||
const workers: string = this.selected.map(item => item.id).join(',');
|
||||
const deletionMessage = new ConfirmationMessage(
|
||||
'JOB_SERVICE_DASHBOARD.CONFIRM_FREE_WORKERS',
|
||||
'JOB_SERVICE_DASHBOARD.CONFIRM_FREE_WORKERS_CONTENT',
|
||||
workers,
|
||||
this.selected,
|
||||
ConfirmationTargets.FREE_SPECIFIED_WORKERS,
|
||||
ConfirmationButtons.CONFIRM_CANCEL
|
||||
);
|
||||
this.operateDialogService.openComfirmDialog(deletionMessage);
|
||||
}
|
||||
|
||||
executeFreeWorkers() {
|
||||
this.loadingFree = true;
|
||||
const operationMessage = new OperateInfo();
|
||||
operationMessage.name =
|
||||
'JOB_SERVICE_DASHBOARD.OPERATION_FREE_SPECIFIED_WORKERS';
|
||||
operationMessage.state = OperationState.progressing;
|
||||
operationMessage.data.name = this.selected
|
||||
.map(item => item.id)
|
||||
.join(',');
|
||||
this.operationService.publishInfo(operationMessage);
|
||||
forkJoin(
|
||||
this.selected.map(item => {
|
||||
return this.jobServiceService.stopRunningJob({
|
||||
jobId: item.job_id,
|
||||
});
|
||||
})
|
||||
)
|
||||
.pipe(finalize(() => (this.loadingFree = false)))
|
||||
.subscribe({
|
||||
next: res => {
|
||||
this.messageHandlerService.info(
|
||||
'JOB_SERVICE_DASHBOARD.FREE_WORKER_SUCCESS'
|
||||
);
|
||||
this.selectionChanged();
|
||||
operateChanges(operationMessage, OperationState.success);
|
||||
},
|
||||
error: err => {
|
||||
this.messageHandlerService.error(err);
|
||||
operateChanges(
|
||||
operationMessage,
|
||||
OperationState.failure,
|
||||
errorHandler(err)
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
json(v: string): object {
|
||||
if (v) {
|
||||
return JSON.parse(v);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -126,15 +126,15 @@
|
||||
<clr-icon
|
||||
shape="exclamation-triangle"
|
||||
class="is-warning text-alignment"
|
||||
size="18"></clr-icon
|
||||
>Deactivated
|
||||
size="18"></clr-icon>
|
||||
{{ 'WEBHOOK.DISABLED' | translate }}
|
||||
</div>
|
||||
<div *ngIf="p?.enabled">
|
||||
<clr-icon
|
||||
shape="success-standard"
|
||||
class="is-success text-alignment"
|
||||
size="18"></clr-icon>
|
||||
Enabled
|
||||
{{ 'WEBHOOK.ENABLED' | translate }}
|
||||
</div>
|
||||
</div>
|
||||
</clr-dg-cell>
|
||||
@ -172,7 +172,14 @@
|
||||
getFlattenLevelString(p.dest_namespace_replace_count)
|
||||
| translate
|
||||
}}</clr-dg-cell>
|
||||
<clr-dg-cell>{{ p.trigger ? p.trigger.type : '' }}</clr-dg-cell>
|
||||
<clr-dg-cell class="trigger">
|
||||
{{ getTriggerTypeI18n(p.trigger) | translate }}
|
||||
<clr-signpost *ngIf="p?.trigger?.trigger_settings?.cron">
|
||||
<clr-signpost-content *clrIfOpen>
|
||||
{{ p?.trigger?.trigger_settings?.cron }}
|
||||
</clr-signpost-content>
|
||||
</clr-signpost>
|
||||
</clr-dg-cell>
|
||||
<clr-dg-cell>{{
|
||||
getBandwidthStr(p.speed) | translate
|
||||
}}</clr-dg-cell>
|
||||
|
@ -25,3 +25,7 @@
|
||||
.margin-right-2px {
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.trigger {
|
||||
width: 180px;
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ import {
|
||||
Component,
|
||||
EventEmitter,
|
||||
Input,
|
||||
OnInit,
|
||||
Output,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
@ -51,13 +52,21 @@ import { BandwidthUnit, Flatten_I18n_MAP } from '../../replication';
|
||||
import { KB_TO_MB } from '../create-edit-rule/create-edit-rule.component';
|
||||
import { ReplicationService } from 'ng-swagger-gen/services/replication.service';
|
||||
import { ReplicationPolicy } from '../../../../../../../ng-swagger-gen/models/replication-policy';
|
||||
import { JobserviceService } from '../../../../../../../ng-swagger-gen/services/jobservice.service';
|
||||
import { ReplicationTrigger } from '../../../../../../../ng-swagger-gen/models/replication-trigger';
|
||||
import {
|
||||
TRIGGER,
|
||||
TRIGGER_I18N_MAP,
|
||||
} from '../../../../project/p2p-provider/p2p-provider.service';
|
||||
import { ScheduleService } from '../../../../../../../ng-swagger-gen/services/schedule.service';
|
||||
import { JobType } from '../../../job-service-dashboard/job-service-dashboard.interface';
|
||||
|
||||
@Component({
|
||||
selector: 'hbr-list-replication-rule',
|
||||
templateUrl: './list-replication-rule.component.html',
|
||||
styleUrls: ['./list-replication-rule.component.scss'],
|
||||
})
|
||||
export class ListReplicationRuleComponent {
|
||||
export class ListReplicationRuleComponent implements OnInit {
|
||||
@Input() selectedId: number | string;
|
||||
@Input() withReplicationJob: boolean;
|
||||
@Input() hasCreateReplicationPermission: boolean;
|
||||
@ -86,13 +95,34 @@ export class ListReplicationRuleComponent {
|
||||
totalCount: number = 0;
|
||||
loading: boolean = true;
|
||||
|
||||
paused: boolean = false;
|
||||
|
||||
constructor(
|
||||
private replicationService: ReplicationService,
|
||||
private translateService: TranslateService,
|
||||
private errorHandlerEntity: ErrorHandler,
|
||||
private operationService: OperationService
|
||||
private operationService: OperationService,
|
||||
private scheduleService: ScheduleService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.scheduleService
|
||||
.getSchedulePaused({ jobType: JobType.ALL })
|
||||
.subscribe(res => {
|
||||
this.paused = res?.paused;
|
||||
});
|
||||
}
|
||||
|
||||
getTriggerTypeI18n(t: ReplicationTrigger) {
|
||||
if (t) {
|
||||
if (this.paused && t?.type === TRIGGER.SCHEDULED) {
|
||||
return TRIGGER_I18N_MAP[TRIGGER.SCHEDULED_PAUSED];
|
||||
}
|
||||
return TRIGGER_I18N_MAP[t?.type];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
trancatedDescription(desc: string): string {
|
||||
if (desc.length > 35) {
|
||||
return desc.substr(0, 35);
|
||||
|
@ -66,6 +66,7 @@ export enum EXECUTION_STATUS {
|
||||
export enum TRIGGER {
|
||||
MANUAL = 'manual',
|
||||
SCHEDULED = 'scheduled',
|
||||
SCHEDULED_PAUSED = 'scheduled(paused)',
|
||||
EVENT_BASED = 'event_based',
|
||||
}
|
||||
|
||||
@ -73,6 +74,7 @@ export const TRIGGER_I18N_MAP = {
|
||||
manual: 'P2P_PROVIDER.MANUAL',
|
||||
scheduled: 'P2P_PROVIDER.SCHEDULED',
|
||||
event_based: 'P2P_PROVIDER.EVENT_BASED',
|
||||
'scheduled(paused)': 'JOB_SERVICE_DASHBOARD.SCHEDULE_PAUSED',
|
||||
};
|
||||
|
||||
export const TIME_OUT: number = 7000;
|
||||
|
@ -56,6 +56,9 @@ import {
|
||||
EventService,
|
||||
HarborEvent,
|
||||
} from '../../../../services/event-service/event.service';
|
||||
import { JobserviceService } from '../../../../../../ng-swagger-gen/services/jobservice.service';
|
||||
import { ScheduleService } from '../../../../../../ng-swagger-gen/services/schedule.service';
|
||||
import { JobType } from '../../../left-side-nav/job-service-dashboard/job-service-dashboard.interface';
|
||||
// The route path which will display this component
|
||||
const URL_TO_DISPLAY: RegExp =
|
||||
/\/harbor\/projects\/(\d+)\/p2p-provider\/policies/;
|
||||
@ -109,6 +112,7 @@ export class PolicyComponent implements OnInit, OnDestroy {
|
||||
policyPage: number = 1;
|
||||
totalPolicy: number = 0;
|
||||
state: ClrDatagridStateInterface;
|
||||
paused: boolean = false;
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private router: Router,
|
||||
@ -117,10 +121,18 @@ export class PolicyComponent implements OnInit, OnDestroy {
|
||||
private messageHandlerService: MessageHandlerService,
|
||||
private userPermissionService: UserPermissionService,
|
||||
private preheatService: PreheatService,
|
||||
private event: EventService
|
||||
private event: EventService,
|
||||
private scheduleService: ScheduleService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.scheduleService
|
||||
.getSchedulePaused({
|
||||
jobType: JobType.ALL,
|
||||
})
|
||||
.subscribe(res => {
|
||||
this.paused = res?.paused;
|
||||
});
|
||||
if (!this.scrollSub) {
|
||||
this.scrollSub = this.event.subscribe(HarborEvent.SCROLL, v => {
|
||||
if (v && URL_TO_DISPLAY.test(v.url)) {
|
||||
@ -638,6 +650,9 @@ export class PolicyComponent implements OnInit, OnDestroy {
|
||||
|
||||
getTriggerTypeI18n(trigger: string): string {
|
||||
if (JSON.parse(trigger).type) {
|
||||
if (this.paused && JSON.parse(trigger).type === TRIGGER.SCHEDULED) {
|
||||
return TRIGGER_I18N_MAP[TRIGGER.SCHEDULED_PAUSED];
|
||||
}
|
||||
return TRIGGER_I18N_MAP[JSON.parse(trigger).type];
|
||||
}
|
||||
return TRIGGER_I18N_MAP[TRIGGER.MANUAL];
|
||||
|
@ -168,6 +168,7 @@
|
||||
</div>
|
||||
<div class="cron-selection">
|
||||
<cron-selection
|
||||
class="w-100"
|
||||
[labelWidth]="'150px'"
|
||||
[disabled]="!(retention?.rules?.length > 0)"
|
||||
#cronScheduleComponent
|
||||
|
@ -79,4 +79,5 @@ export enum HarborEvent {
|
||||
STOP_SCAN_ARTIFACT = 'stopScanArtifact',
|
||||
UPDATE_VULNERABILITY_INFO = 'UpdateVulnerabilityInfo',
|
||||
REFRESH_EXPORT_JOBS = 'refreshExportJobs',
|
||||
REFRESH_JOB_SERVICE_DASHBOARD = 'refreshJobServiceDashboard',
|
||||
}
|
||||
|
@ -147,6 +147,27 @@
|
||||
{{ 'BUTTON.CANCEL' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
<div *ngIf="paused && oriCron" class="clr-row mb-1">
|
||||
<span class="font-style" [style.width]="labelWidth"></span>
|
||||
<div class="alert alert-warning" role="alert">
|
||||
<div class="alert-items p-0">
|
||||
<div class="alert-item static">
|
||||
<div class="alert-icon-wrapper">
|
||||
<cds-icon
|
||||
class="alert-icon"
|
||||
shape="exclamation-triangle"></cds-icon>
|
||||
</div>
|
||||
<span class="alert-text">{{
|
||||
'JOB_SERVICE_DASHBOARD.SCHEDULE_BEEN_PAUSED'
|
||||
| translate
|
||||
: {
|
||||
param: labelCurrent
|
||||
}
|
||||
}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="isInlineModel">
|
||||
<div class="normal-wrapper-box height-1rem" *ngIf="!isEditMode">
|
||||
|
@ -32,3 +32,9 @@ span.required {
|
||||
.mb-05 {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.alert-warning {
|
||||
flex-grow: 1;
|
||||
margin-right: 0.6rem;
|
||||
margin-left: 0.6rem;
|
||||
}
|
||||
|
@ -6,11 +6,15 @@ import {
|
||||
OnChanges,
|
||||
SimpleChanges,
|
||||
SimpleChange,
|
||||
OnInit,
|
||||
} from '@angular/core';
|
||||
import { OriginCron } from '../../services/interface';
|
||||
import { cronRegex } from '../../units/utils';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { ErrorHandler } from '../../units/error-handler/error-handler';
|
||||
import { JobserviceService } from '../../../../../ng-swagger-gen/services/jobservice.service';
|
||||
import { ScheduleService } from '../../../../../ng-swagger-gen/services/schedule.service';
|
||||
import { JobType } from '../../../base/left-side-nav/job-service-dashboard/job-service-dashboard.interface';
|
||||
const SCHEDULE_TYPE = {
|
||||
NONE: 'None',
|
||||
DAILY: 'Daily',
|
||||
@ -24,7 +28,7 @@ const PREFIX: string = '0 ';
|
||||
templateUrl: './cron-schedule.component.html',
|
||||
styleUrls: ['./cron-schedule.component.scss'],
|
||||
})
|
||||
export class CronScheduleComponent implements OnChanges {
|
||||
export class CronScheduleComponent implements OnChanges, OnInit {
|
||||
@Input() externalValidation: boolean = true; //extra check
|
||||
@Input() isInlineModel: boolean = false;
|
||||
@Input() originCron: OriginCron;
|
||||
@ -40,11 +44,26 @@ export class CronScheduleComponent implements OnChanges {
|
||||
SCHEDULE_TYPE = SCHEDULE_TYPE;
|
||||
scheduleType: string;
|
||||
@Output() inputvalue = new EventEmitter<string>();
|
||||
paused: boolean = false;
|
||||
constructor(
|
||||
private translate: TranslateService,
|
||||
private errorHandler: ErrorHandler
|
||||
private errorHandler: ErrorHandler,
|
||||
private scheduleService: ScheduleService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
if (this.labelCurrent) {
|
||||
this.translate
|
||||
.get(this.labelCurrent)
|
||||
.subscribe(res => (this.labelCurrent = res));
|
||||
}
|
||||
this.scheduleService
|
||||
.getSchedulePaused({ jobType: JobType.ALL })
|
||||
.subscribe(res => {
|
||||
this.paused = res?.paused;
|
||||
});
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
let cronChange: SimpleChange = changes['originCron'];
|
||||
if (cronChange?.currentValue) {
|
||||
|
@ -56,6 +56,14 @@ export const enum ConfirmationTargets {
|
||||
ALL_ACCESSORIES,
|
||||
STOP_GC,
|
||||
STOP_AUDIT_LOG_ROTATION,
|
||||
FREE_ALL_WORKERS,
|
||||
RESUME_ALL_SCHEDULES,
|
||||
PAUSE_ALL_SCHEDULES,
|
||||
STOP_ALL_PENDING_JOBS,
|
||||
FREE_SPECIFIED_WORKERS,
|
||||
STOPS_JOBS,
|
||||
PAUSE_JOBS,
|
||||
RESUME_JOBS,
|
||||
}
|
||||
|
||||
export const enum ActionType {
|
||||
|
@ -1,6 +1,7 @@
|
||||
import {
|
||||
DEFAULT_PAGE_SIZE,
|
||||
delUrlParam,
|
||||
durationStr,
|
||||
getPageSizeFromLocalStorage,
|
||||
getQueryString,
|
||||
getSizeNumber,
|
||||
@ -119,4 +120,11 @@ describe('functions in utils.ts should work', () => {
|
||||
setPageSizeToLocalStorage('test1', 10);
|
||||
expect(getPageSizeFromLocalStorage('test1')).toEqual(10);
|
||||
});
|
||||
|
||||
it('functions durationStr(distance: number) should work', () => {
|
||||
expect(durationStr(11)).toEqual('0');
|
||||
expect(durationStr(1111)).toEqual('1sec');
|
||||
expect(durationStr(61111)).toEqual('1min 1sec');
|
||||
expect(durationStr(3661111)).toEqual('1hrs 1min 1sec');
|
||||
});
|
||||
});
|
||||
|
@ -17,6 +17,8 @@ import {
|
||||
import { AbstractControl } from '@angular/forms';
|
||||
import { isValidCron } from 'cron-validator';
|
||||
import { ClrDatagridStateInterface } from '@clr/angular';
|
||||
import { ScheduleListComponent } from '../../base/left-side-nav/job-service-dashboard/schedule-list/schedule-list.component';
|
||||
import { PendingListComponent } from '../../base/left-side-nav/job-service-dashboard/pending-job-list/pending-job-list.component';
|
||||
|
||||
/**
|
||||
* Api levels
|
||||
@ -928,6 +930,29 @@ export function setPageSizeToLocalStorage(key: string, pageSize: number) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert seconds to xx hrs xx min xx sec
|
||||
* @param distance in milliseconds
|
||||
*/
|
||||
export function durationStr(distance: number): string {
|
||||
const hours = Math.floor(distance / 3600000);
|
||||
distance -= hours * 3600000;
|
||||
const minutes = Math.floor(distance / 60000);
|
||||
distance -= minutes * 60000;
|
||||
const seconds = Math.floor(distance / 1000);
|
||||
let result: string = '';
|
||||
if (seconds) {
|
||||
result = `${seconds}sec`;
|
||||
}
|
||||
if (minutes) {
|
||||
result = `${minutes}min ${seconds}sec`;
|
||||
}
|
||||
if (hours) {
|
||||
result = `${hours}hrs ${minutes}min ${seconds}sec`;
|
||||
}
|
||||
return result ? result : '0';
|
||||
}
|
||||
|
||||
export enum PageSizeMapKeys {
|
||||
LIST_PROJECT_COMPONENT = 'ListProjectComponent',
|
||||
REPOSITORY_GRIDVIEW_COMPONENT = 'RepositoryGridviewComponent',
|
||||
@ -957,4 +982,8 @@ export enum PageSizeMapKeys {
|
||||
SYSTEM_SCANNER_COMPONENT = 'ConfigurationScannerComponent',
|
||||
GC_HISTORY_COMPONENT = 'GcHistoryComponent',
|
||||
SYSTEM_GROUP_COMPONENT = 'SystemGroupComponent',
|
||||
WORKER_LIST_COMPONENT_POOL = 'WorkerListComponentPool',
|
||||
WORKER_LIST_COMPONENT_WORKER = 'WorkerListComponentWorker',
|
||||
SCHEDULE_LIST_COMPONENT = 'ScheduleListComponent',
|
||||
PENDING_LIST_COMPONENT = 'PendingListComponent',
|
||||
}
|
||||
|
@ -347,3 +347,13 @@ app-artifact-filter {
|
||||
color: $normal-border-color !important;;
|
||||
}
|
||||
}
|
||||
|
||||
job-service-dashboard {
|
||||
.duration {
|
||||
color: $text-color-job-service-dashboard;
|
||||
}
|
||||
}
|
||||
|
||||
.datagrid-numeric-filter-input {
|
||||
background-color: $datagrid-numeric-filter-input-bg-color;
|
||||
}
|
||||
|
@ -45,4 +45,6 @@ $pull-command-icon-color: #4aaed9;
|
||||
$pull-command-icon-hover-color: #007CBB;
|
||||
$select-all-for-dropdown-color: #4aaed9;
|
||||
$normal-border-color: #acbac3;
|
||||
$text-color-job-service-dashboard: #49aeda;
|
||||
$datagrid-numeric-filter-input-bg-color: #21333b;
|
||||
@import "./common.scss";
|
||||
|
@ -46,4 +46,6 @@ $pull-command-icon-color: #007CBB;
|
||||
$pull-command-icon-hover-color: #4aaed9;
|
||||
$select-all-for-dropdown-color: #0072a3;
|
||||
$normal-border-color: #6a7a81;
|
||||
$text-color-job-service-dashboard: #0072a3;
|
||||
$datagrid-numeric-filter-input-bg-color: unset;
|
||||
@import "./common.scss";
|
||||
|
@ -1765,5 +1765,75 @@
|
||||
"JOB_NAME_REQUIRED": "Job name is required",
|
||||
"JOB_NAME_EXISTING": "Job name already exists",
|
||||
"TRIGGER_EXPORT_SUCCESS": "Trigger exporting CVEs successfully!"
|
||||
},
|
||||
"JOB_SERVICE_DASHBOARD": {
|
||||
"SCHEDULE_PAUSED": "Scheduled(Paused)",
|
||||
"SCHEDULE_BEEN_PAUSED": "{{param}} has been paused",
|
||||
"PENDING_JOBS": "Pending Jobs In Queues",
|
||||
"OTHERS": "Others",
|
||||
"STOP_ALL": "STOP ALL",
|
||||
"CONFIRM_STOP_ALL": "Confirm Stopping All",
|
||||
"CONFIRM_STOP_ALL_CONTENT": "Do you want to stop all the job queues?",
|
||||
"STOP_ALL_SUCCESS": "Stopped all the job queues successfully",
|
||||
"STOP_BTN": "STOP",
|
||||
"PAUSE_BTN": "PAUSE",
|
||||
"RESUME_BTN": "RESUME",
|
||||
"JOB_TYPE": "Job Type",
|
||||
"PENDING_COUNT": "Pending Count",
|
||||
"LATENCY": "Latency",
|
||||
"PAUSED": "Paused",
|
||||
"NO_JOB_QUEUE": "We could not find any job queue",
|
||||
"CONFIRM_STOPPING_JOBS": "Confirm Stopping Jobs",
|
||||
"CONFIRM_STOPPING_JOBS_CONTENT": "Do you want to stop the jobs {{param}}?",
|
||||
"CONFIRM_PAUSING_JOBS": "Confirm Pausing Jobs",
|
||||
"CONFIRM_PAUSING_JOBS_CONTENT": "Do you want to pause the jobs {{param}}?",
|
||||
"CONFIRM_RESUMING_JOBS": "Confirm Resuming Jobs",
|
||||
"CONFIRM_RESUMING_JOBS_CONTENT": "Do you want to resume the jobs {{param}}?",
|
||||
"STOP_SUCCESS": "Stopped jobs Successfully",
|
||||
"PAUSE_SUCCESS": "Paused jobs Successfully",
|
||||
"RESUME_SUCCESS": "Resumed jobs Successfully",
|
||||
"SCHEDULES": "Schedules",
|
||||
"RUNNING_STATUS": "Running",
|
||||
"RESUME_ALL_BTN_TEXT": "RESUME ALL",
|
||||
"PAUSE_ALL_BTN_TEXT": "PAUSE ALL",
|
||||
"CONFIRM_PAUSING_ALL": "Confirm Pausing All",
|
||||
"CONFIRM_PAUSING_ALL_CONTENT": "Do you want to pause all the jobs schedules?",
|
||||
"CONFIRM_RESUMING_ALL": "Confirm Resuming All",
|
||||
"CONFIRM_RESUMING_ALL_CONTENT": "Do you want to resume all the jobs schedules?",
|
||||
"PAUSE_ALL_SUCCESS": "Paused all the schedules Successfully",
|
||||
"RESUME_ALL_SUCCESS": "Resumed all the schedules Successfully",
|
||||
"VENDOR_TYPE": "Vendor Type",
|
||||
"VENDOR_ID": "Vendor ID",
|
||||
"EXTRA_ATTR": "Extra Attribute",
|
||||
"NO_SCHEDULE": "We could not find any schedule",
|
||||
"WORKERS": "Workers",
|
||||
"FREE_ALL": "Free all",
|
||||
"CONFIRM_FREE_ALL": "Confirm Freeing All",
|
||||
"CONFIRM_FREE_ALL_CONTENT": "Do you want to free all the workers?",
|
||||
"CONFIRM_FREE_WORKERS": "Confirm Freeing Workers",
|
||||
"CONFIRM_FREE_WORKERS_CONTENT": "Do you want to free the workers {{param}}?",
|
||||
"FREE_WORKER_SUCCESS": "Freed workers successfully",
|
||||
"FREE_ALL_SUCCESS": "Freed all the workers successfully",
|
||||
"WORKER_POOL": "Worker Pools",
|
||||
"WORKER_POOL_ID": "Worker Pool ID",
|
||||
"PID": "Pid",
|
||||
"START_AT": "Started At",
|
||||
"HEARTBEAT_AT": "Heartbeat At",
|
||||
"CONCURRENCY": "Concurrency",
|
||||
"NO_WORKER_POOL": "We could not find any worker pool",
|
||||
"FREE": "Free",
|
||||
"WORKER_ID": "Worker ID",
|
||||
"JOB_ID": "Job ID",
|
||||
"JOB_PARAM": "Job Parameter",
|
||||
"CHECK_IN_AT": "Checked In At",
|
||||
"NO_WORKER": "We could not find any worker",
|
||||
"JOB_QUEUE": "Job Queues",
|
||||
"JOB_SERVICE_DASHBOARD": "Job Service Dashboard",
|
||||
"QUEUE_STOP_BTN_INFO": "STOP — Stop all jobs in the queue and remove them from the queue.",
|
||||
"QUEUE_PAUSE_BTN_INFO": "PAUSE — Pause to execute jobs in this type of job queue, jobs can be enqueued when the queue is paused.",
|
||||
"QUEUE_RESUME_BTN_INFO": "RESUME — Resume to execute jobs in this type of job queue.",
|
||||
"SCHEDULE_PAUSE_BTN_INFO": "PAUSE — Pause all schedules to execute.",
|
||||
"SCHEDULE_RESUME_BTN_INFO": "RESUME — Resume all schedules to execute.",
|
||||
"WORKER_FREE_BTN_INFO": "Stop the current running job to free the worker"
|
||||
}
|
||||
}
|
||||
|
@ -1765,5 +1765,83 @@
|
||||
"JOB_NAME_REQUIRED": "Job name is required",
|
||||
"JOB_NAME_EXISTING": "Job name already exists",
|
||||
"TRIGGER_EXPORT_SUCCESS": "Trigger exporting CVEs successfully!"
|
||||
},
|
||||
"JOB_SERVICE_DASHBOARD": {
|
||||
"SCHEDULE_PAUSED": "Scheduled(Paused)",
|
||||
"SCHEDULE_BEEN_PAUSED": "{{param}} has been paused",
|
||||
"PENDING_JOBS": "Pending Jobs In Queues",
|
||||
"OTHERS": "Others",
|
||||
"STOP_ALL": "STOP ALL",
|
||||
"CONFIRM_STOP_ALL": "Confirm Stopping All",
|
||||
"CONFIRM_STOP_ALL_CONTENT": "Do you want to stop all the job queues?",
|
||||
"STOP_ALL_SUCCESS": "Stopped all the job queues successfully",
|
||||
"STOP_BTN": "STOP",
|
||||
"PAUSE_BTN": "PAUSE",
|
||||
"RESUME_BTN": "RESUME",
|
||||
"JOB_TYPE": "Job Type",
|
||||
"PENDING_COUNT": "Pending Count",
|
||||
"LATENCY": "Latency",
|
||||
"PAUSED": "Paused",
|
||||
"NO_JOB_QUEUE": "We could not find any job queue",
|
||||
"CONFIRM_STOPPING_JOBS": "Confirm Stopping Jobs",
|
||||
"CONFIRM_STOPPING_JOBS_CONTENT": "Do you want to stop the jobs {{param}}?",
|
||||
"CONFIRM_PAUSING_JOBS": "Confirm Pausing Jobs",
|
||||
"CONFIRM_PAUSING_JOBS_CONTENT": "Do you want to pause the jobs {{param}}?",
|
||||
"CONFIRM_RESUMING_JOBS": "Confirm Resuming Jobs",
|
||||
"CONFIRM_RESUMING_JOBS_CONTENT": "Do you want to resume the jobs {{param}}?",
|
||||
"STOP_SUCCESS": "Stopped jobs successfully",
|
||||
"PAUSE_SUCCESS": "Paused jobs successfully",
|
||||
"RESUME_SUCCESS": "Resumed jobs successfully",
|
||||
"SCHEDULES": "Schedules",
|
||||
"RUNNING_STATUS": "Running",
|
||||
"RESUME_ALL_BTN_TEXT": "RESUME ALL",
|
||||
"PAUSE_ALL_BTN_TEXT": "PAUSE ALL",
|
||||
"CONFIRM_PAUSING_ALL": "Confirm Pausing All",
|
||||
"CONFIRM_PAUSING_ALL_CONTENT": "Do you want to pause all the jobs schedules?",
|
||||
"CONFIRM_RESUMING_ALL": "Confirm Resuming All",
|
||||
"CONFIRM_RESUMING_ALL_CONTENT": "Do you want to resume all the jobs schedules?",
|
||||
"PAUSE_ALL_SUCCESS": "Paused all the schedules successfully",
|
||||
"RESUME_ALL_SUCCESS": "Resumed all the schedules successfully",
|
||||
"VENDOR_TYPE": "Vendor Type",
|
||||
"VENDOR_ID": "Vendor ID",
|
||||
"EXTRA_ATTR": "Extra Attribute",
|
||||
"NO_SCHEDULE": "We could not find any schedule",
|
||||
"WORKERS": "Workers",
|
||||
"FREE_ALL": "Free all",
|
||||
"CONFIRM_FREE_ALL": "Confirm Freeing All",
|
||||
"CONFIRM_FREE_ALL_CONTENT": "Do you want to free all the workers?",
|
||||
"CONFIRM_FREE_WORKERS": "Confirm Freeing Workers",
|
||||
"CONFIRM_FREE_WORKERS_CONTENT": "Do you want to free the workers {{param}}?",
|
||||
"FREE_WORKER_SUCCESS": "Freed workers successfully",
|
||||
"FREE_ALL_SUCCESS": "Freed all the workers successfully",
|
||||
"WORKER_POOL": "Worker Pools",
|
||||
"WORKER_POOL_ID": "Worker Pool ID",
|
||||
"PID": "Pid",
|
||||
"START_AT": "Started At",
|
||||
"HEARTBEAT_AT": "Heartbeat At",
|
||||
"CONCURRENCY": "Concurrency",
|
||||
"NO_WORKER_POOL": "We could not find any worker pool",
|
||||
"FREE": "Free",
|
||||
"WORKER_ID": "Worker ID",
|
||||
"JOB_ID": "Job ID",
|
||||
"JOB_PARAM": "Job Parameter",
|
||||
"CHECK_IN_AT": "Checked In At",
|
||||
"NO_WORKER": "We could not find any worker",
|
||||
"JOB_QUEUE": "Job Queues",
|
||||
"JOB_SERVICE_DASHBOARD": "Job Service Dashboard",
|
||||
"OPERATION_STOP_ALL_QUEUES": "Stop all job queues",
|
||||
"OPERATION_STOP_SPECIFIED_QUEUES": "Stop specified job queues",
|
||||
"OPERATION_PAUSE_SPECIFIED_QUEUES": "Pause specified job queues",
|
||||
"OPERATION_RESUME_SPECIFIED_QUEUES": "Resume specified job queues",
|
||||
"OPERATION_PAUSE_SCHEDULE": "Pause all schedules",
|
||||
"OPERATION_RESUME_SCHEDULE": "Resume all schedules",
|
||||
"OPERATION_FREE_ALL": "Free all workers",
|
||||
"OPERATION_FREE_SPECIFIED_WORKERS": "Free specified workers",
|
||||
"QUEUE_STOP_BTN_INFO": "STOP — Stop all jobs in the queue and remove them from the queue.",
|
||||
"QUEUE_PAUSE_BTN_INFO": "PAUSE — Pause to execute jobs in this type of job queue, jobs can be enqueued when the queue is paused.",
|
||||
"QUEUE_RESUME_BTN_INFO": "RESUME — Resume to execute jobs in this type of job queue.",
|
||||
"SCHEDULE_PAUSE_BTN_INFO": "PAUSE — Pause all schedules to execute.",
|
||||
"SCHEDULE_RESUME_BTN_INFO": "RESUME — Resume all schedules to execute.",
|
||||
"WORKER_FREE_BTN_INFO": "Stop the current running job to free the worker"
|
||||
}
|
||||
}
|
||||
|
@ -1764,5 +1764,75 @@
|
||||
"JOB_NAME_REQUIRED": "Job name is required",
|
||||
"JOB_NAME_EXISTING": "Job name already exists",
|
||||
"TRIGGER_EXPORT_SUCCESS": "Trigger exporting CVEs successfully!"
|
||||
},
|
||||
"JOB_SERVICE_DASHBOARD": {
|
||||
"SCHEDULE_PAUSED": "Scheduled(Paused)",
|
||||
"SCHEDULE_BEEN_PAUSED": "{{param}} has been paused",
|
||||
"PENDING_JOBS": "Pending Jobs In Queues",
|
||||
"OTHERS": "Others",
|
||||
"STOP_ALL": "STOP ALL",
|
||||
"CONFIRM_STOP_ALL": "Confirm Stopping All",
|
||||
"CONFIRM_STOP_ALL_CONTENT": "Do you want to stop all the job queues?",
|
||||
"STOP_ALL_SUCCESS": "Stopped all the job queues successfully",
|
||||
"STOP_BTN": "STOP",
|
||||
"PAUSE_BTN": "PAUSE",
|
||||
"RESUME_BTN": "RESUME",
|
||||
"JOB_TYPE": "Job Type",
|
||||
"PENDING_COUNT": "Pending Count",
|
||||
"LATENCY": "Latency",
|
||||
"PAUSED": "Paused",
|
||||
"NO_JOB_QUEUE": "We could not find any job queue",
|
||||
"CONFIRM_STOPPING_JOBS": "Confirm Stopping Jobs",
|
||||
"CONFIRM_STOPPING_JOBS_CONTENT": "Do you want to stop the jobs {{param}}?",
|
||||
"CONFIRM_PAUSING_JOBS": "Confirm Pausing Jobs",
|
||||
"CONFIRM_PAUSING_JOBS_CONTENT": "Do you want to pause the jobs {{param}}?",
|
||||
"CONFIRM_RESUMING_JOBS": "Confirm Resuming Jobs",
|
||||
"CONFIRM_RESUMING_JOBS_CONTENT": "Do you want to resume the jobs {{param}}?",
|
||||
"STOP_SUCCESS": "Stopped jobs Successfully",
|
||||
"PAUSE_SUCCESS": "Paused jobs Successfully",
|
||||
"RESUME_SUCCESS": "Resumed jobs Successfully",
|
||||
"SCHEDULES": "Schedules",
|
||||
"RUNNING_STATUS": "Running",
|
||||
"RESUME_ALL_BTN_TEXT": "RESUME ALL",
|
||||
"PAUSE_ALL_BTN_TEXT": "PAUSE ALL",
|
||||
"CONFIRM_PAUSING_ALL": "Confirm Pausing All",
|
||||
"CONFIRM_PAUSING_ALL_CONTENT": "Do you want to pause all the jobs schedules?",
|
||||
"CONFIRM_RESUMING_ALL": "Confirm Resuming All",
|
||||
"CONFIRM_RESUMING_ALL_CONTENT": "Do you want to resume all the jobs schedules?",
|
||||
"PAUSE_ALL_SUCCESS": "Paused all the schedules Successfully",
|
||||
"RESUME_ALL_SUCCESS": "Resumed all the schedules Successfully",
|
||||
"VENDOR_TYPE": "Vendor Type",
|
||||
"VENDOR_ID": "Vendor ID",
|
||||
"EXTRA_ATTR": "Extra Attribute",
|
||||
"NO_SCHEDULE": "We could not find any schedule",
|
||||
"WORKERS": "Workers",
|
||||
"FREE_ALL": "Free all",
|
||||
"CONFIRM_FREE_ALL": "Confirm Freeing All",
|
||||
"CONFIRM_FREE_ALL_CONTENT": "Do you want to free all the workers?",
|
||||
"CONFIRM_FREE_WORKERS": "Confirm Freeing Workers",
|
||||
"CONFIRM_FREE_WORKERS_CONTENT": "Do you want to free the workers {{param}}?",
|
||||
"FREE_WORKER_SUCCESS": "Freed workers successfully",
|
||||
"FREE_ALL_SUCCESS": "Freed all the workers successfully",
|
||||
"WORKER_POOL": "Worker Pools",
|
||||
"WORKER_POOL_ID": "Worker Pool ID",
|
||||
"PID": "Pid",
|
||||
"START_AT": "Started At",
|
||||
"HEARTBEAT_AT": "Heartbeat At",
|
||||
"CONCURRENCY": "Concurrency",
|
||||
"NO_WORKER_POOL": "We could not find any worker pool",
|
||||
"FREE": "Free",
|
||||
"WORKER_ID": "Worker ID",
|
||||
"JOB_ID": "Job ID",
|
||||
"JOB_PARAM": "Job Parameter",
|
||||
"CHECK_IN_AT": "Checked In At",
|
||||
"NO_WORKER": "We could not find any worker",
|
||||
"JOB_QUEUE": "Job Queues",
|
||||
"JOB_SERVICE_DASHBOARD": "Job Service Dashboard",
|
||||
"QUEUE_STOP_BTN_INFO": "STOP — Stop all jobs in the queue and remove them from the queue.",
|
||||
"QUEUE_PAUSE_BTN_INFO": "PAUSE — Pause to execute jobs in this type of job queue, jobs can be enqueued when the queue is paused.",
|
||||
"QUEUE_RESUME_BTN_INFO": "RESUME — Resume to execute jobs in this type of job queue.",
|
||||
"SCHEDULE_PAUSE_BTN_INFO": "PAUSE — Pause all schedules to execute.",
|
||||
"SCHEDULE_RESUME_BTN_INFO": "RESUME — Resume all schedules to execute.",
|
||||
"WORKER_FREE_BTN_INFO": "Stop the current running job to free the worker"
|
||||
}
|
||||
}
|
||||
|
@ -1734,5 +1734,75 @@
|
||||
"JOB_NAME_REQUIRED": "Job name is required",
|
||||
"JOB_NAME_EXISTING": "Job name already exists",
|
||||
"TRIGGER_EXPORT_SUCCESS": "Trigger exporting CVEs successfully!"
|
||||
},
|
||||
"JOB_SERVICE_DASHBOARD": {
|
||||
"SCHEDULE_PAUSED": "Scheduled(Paused)",
|
||||
"SCHEDULE_BEEN_PAUSED": "{{param}} has been paused",
|
||||
"PENDING_JOBS": "Pending Jobs In Queues",
|
||||
"OTHERS": "Other",
|
||||
"STOP_ALL": "STOP ALL",
|
||||
"CONFIRM_STOP_ALL": "Confirm Stopping All",
|
||||
"CONFIRM_STOP_ALL_CONTENT": "Do you want to stop all the job queues?",
|
||||
"STOP_ALL_SUCCESS": "Stopped all the job queues successfully",
|
||||
"STOP_BTN": "STOP",
|
||||
"PAUSE_BTN": "PAUSE",
|
||||
"RESUME_BTN": "RESUME",
|
||||
"JOB_TYPE": "Job Type",
|
||||
"PENDING_COUNT": "Pending Count",
|
||||
"LATENCY": "Latency",
|
||||
"PAUSED": "Paused",
|
||||
"NO_JOB_QUEUE": "We could not find any job queue",
|
||||
"CONFIRM_STOPPING_JOBS": "Confirm Stopping Jobs",
|
||||
"CONFIRM_STOPPING_JOBS_CONTENT": "Do you want to stop the jobs {{param}}?",
|
||||
"CONFIRM_PAUSING_JOBS": "Confirm Pausing Jobs",
|
||||
"CONFIRM_PAUSING_JOBS_CONTENT": "Do you want to pause the jobs {{param}}?",
|
||||
"CONFIRM_RESUMING_JOBS": "Confirm Resuming Jobs",
|
||||
"CONFIRM_RESUMING_JOBS_CONTENT": "Do you want to resume the jobs {{param}}?",
|
||||
"STOP_SUCCESS": "Stopped jobs Successfully",
|
||||
"PAUSE_SUCCESS": "Paused jobs Successfully",
|
||||
"RESUME_SUCCESS": "Resumed jobs Successfully",
|
||||
"SCHEDULES": "Schedules",
|
||||
"RUNNING_STATUS": "Running",
|
||||
"RESUME_ALL_BTN_TEXT": "RESUME ALL",
|
||||
"PAUSE_ALL_BTN_TEXT": "PAUSE ALL",
|
||||
"CONFIRM_PAUSING_ALL": "Confirm Pausing All",
|
||||
"CONFIRM_PAUSING_ALL_CONTENT": "Do you want to pause all the jobs schedules?",
|
||||
"CONFIRM_RESUMING_ALL": "Confirm Resuming All",
|
||||
"CONFIRM_RESUMING_ALL_CONTENT": "Do you want to resume all the jobs schedules?",
|
||||
"PAUSE_ALL_SUCCESS": "Paused all the schedules Successfully",
|
||||
"RESUME_ALL_SUCCESS": "Resumed all the schedules Successfully",
|
||||
"VENDOR_TYPE": "Vendor Type",
|
||||
"VENDOR_ID": "Vendor ID",
|
||||
"EXTRA_ATTR": "Extra Attribute",
|
||||
"NO_SCHEDULE": "We could not find any schedule",
|
||||
"WORKERS": "Workers",
|
||||
"FREE_ALL": "Free all",
|
||||
"CONFIRM_FREE_ALL": "Confirm Freeing All",
|
||||
"CONFIRM_FREE_ALL_CONTENT": "Do you want to free all the workers?",
|
||||
"CONFIRM_FREE_WORKERS": "Confirm Freeing Workers",
|
||||
"CONFIRM_FREE_WORKERS_CONTENT": "Do you want to free the workers {{param}}?",
|
||||
"FREE_WORKER_SUCCESS": "Freed workers successfully",
|
||||
"FREE_ALL_SUCCESS": "Freed all the workers successfully",
|
||||
"WORKER_POOL": "Worker Pools",
|
||||
"WORKER_POOL_ID": "Worker Pool ID",
|
||||
"PID": "Pid",
|
||||
"START_AT": "Started At",
|
||||
"HEARTBEAT_AT": "Heartbeat At",
|
||||
"CONCURRENCY": "Concurrency",
|
||||
"NO_WORKER_POOL": "We could not find any worker pool",
|
||||
"FREE": "Free",
|
||||
"WORKER_ID": "Worker ID",
|
||||
"JOB_ID": "Job ID",
|
||||
"JOB_PARAM": "Job Parameter",
|
||||
"CHECK_IN_AT": "Checked In At",
|
||||
"NO_WORKER": "We could not find any worker",
|
||||
"JOB_QUEUE": "Job Queues",
|
||||
"JOB_SERVICE_DASHBOARD": "Job Service Dashboard",
|
||||
"QUEUE_STOP_BTN_INFO": "STOP — Stop all jobs in the queue and remove them from the queue.",
|
||||
"QUEUE_PAUSE_BTN_INFO": "PAUSE — Pause to execute jobs in this type of job queue, jobs can be enqueued when the queue is paused.",
|
||||
"QUEUE_RESUME_BTN_INFO": "RESUME — Resume to execute jobs in this type of job queue.",
|
||||
"SCHEDULE_PAUSE_BTN_INFO": "PAUSE — Pause all schedules to execute.",
|
||||
"SCHEDULE_RESUME_BTN_INFO": "RESUME — Resume all schedule to execute.",
|
||||
"WORKER_FREE_BTN_INFO": "Stop the current running job to free the worker"
|
||||
}
|
||||
}
|
||||
|
@ -1761,5 +1761,75 @@
|
||||
"JOB_NAME_REQUIRED": "Job name is required",
|
||||
"JOB_NAME_EXISTING": "Job name already exists",
|
||||
"TRIGGER_EXPORT_SUCCESS": "Trigger exporting CVEs successfully!"
|
||||
},
|
||||
"JOB_SERVICE_DASHBOARD": {
|
||||
"SCHEDULE_PAUSED": "Scheduled(Paused)",
|
||||
"SCHEDULE_BEEN_PAUSED": "{{param}} has been paused",
|
||||
"PENDING_JOBS": "Pending Jobs In Queues",
|
||||
"OTHERS": "Others",
|
||||
"STOP_ALL": "STOP ALL",
|
||||
"CONFIRM_STOP_ALL": "Confirm Stopping All",
|
||||
"CONFIRM_STOP_ALL_CONTENT": "Do you want to stop all the job queues?",
|
||||
"STOP_ALL_SUCCESS": "Stopped all the job queues successfully",
|
||||
"STOP_BTN": "STOP",
|
||||
"PAUSE_BTN": "PAUSE",
|
||||
"RESUME_BTN": "RESUME",
|
||||
"JOB_TYPE": "Job Type",
|
||||
"PENDING_COUNT": "Pending Count",
|
||||
"LATENCY": "Latency",
|
||||
"PAUSED": "Paused",
|
||||
"NO_JOB_QUEUE": "We could not find any job queue",
|
||||
"CONFIRM_STOPPING_JOBS": "Confirm Stopping Jobs",
|
||||
"CONFIRM_STOPPING_JOBS_CONTENT": "Do you want to stop the jobs {{param}}?",
|
||||
"CONFIRM_PAUSING_JOBS": "Confirm Pausing Jobs",
|
||||
"CONFIRM_PAUSING_JOBS_CONTENT": "Do you want to pause the jobs {{param}}?",
|
||||
"CONFIRM_RESUMING_JOBS": "Confirm Resuming Jobs",
|
||||
"CONFIRM_RESUMING_JOBS_CONTENT": "Do you want to resume the jobs {{param}}?",
|
||||
"STOP_SUCCESS": "Stopped jobs Successfully",
|
||||
"PAUSE_SUCCESS": "Paused jobs Successfully",
|
||||
"RESUME_SUCCESS": "Resumed jobs Successfully",
|
||||
"SCHEDULES": "Schedules",
|
||||
"RUNNING_STATUS": "Running",
|
||||
"RESUME_ALL_BTN_TEXT": "RESUME ALL",
|
||||
"PAUSE_ALL_BTN_TEXT": "PAUSE ALL",
|
||||
"CONFIRM_PAUSING_ALL": "Confirm Pausing All",
|
||||
"CONFIRM_PAUSING_ALL_CONTENT": "Do you want to pause all the jobs schedules?",
|
||||
"CONFIRM_RESUMING_ALL": "Confirm Resuming All",
|
||||
"CONFIRM_RESUMING_ALL_CONTENT": "Do you want to resume all the jobs schedules?",
|
||||
"PAUSE_ALL_SUCCESS": "Paused all the schedules Successfully",
|
||||
"RESUME_ALL_SUCCESS": "Resumed all the schedules Successfully",
|
||||
"VENDOR_TYPE": "Vendor Type",
|
||||
"VENDOR_ID": "Vendor ID",
|
||||
"EXTRA_ATTR": "Extra Attribute",
|
||||
"NO_SCHEDULE": "We could not find any schedule",
|
||||
"WORKERS": "Workers",
|
||||
"FREE_ALL": "Free all",
|
||||
"CONFIRM_FREE_ALL": "Confirm Freeing All",
|
||||
"CONFIRM_FREE_ALL_CONTENT": "Do you want to free all the workers?",
|
||||
"CONFIRM_FREE_WORKERS": "Confirm Freeing Workers",
|
||||
"CONFIRM_FREE_WORKERS_CONTENT": "Do you want to free the workers {{param}}?",
|
||||
"FREE_WORKER_SUCCESS": "Freed workers successfully",
|
||||
"FREE_ALL_SUCCESS": "Freed all the workers successfully",
|
||||
"WORKER_POOL": "Worker Pools",
|
||||
"WORKER_POOL_ID": "Worker Pool ID",
|
||||
"PID": "Pid",
|
||||
"START_AT": "Started At",
|
||||
"HEARTBEAT_AT": "Heartbeat At",
|
||||
"CONCURRENCY": "Concurrency",
|
||||
"NO_WORKER_POOL": "We could not find any worker pool",
|
||||
"FREE": "Free",
|
||||
"WORKER_ID": "Worker ID",
|
||||
"JOB_ID": "Job ID",
|
||||
"JOB_PARAM": "Job Parameter",
|
||||
"CHECK_IN_AT": "Checked In At",
|
||||
"NO_WORKER": "We could not find any worker",
|
||||
"JOB_QUEUE": "Job Queues",
|
||||
"JOB_SERVICE_DASHBOARD": "Job Service Dashboard",
|
||||
"QUEUE_STOP_BTN_INFO": "STOP — Stop all jobs in the queue and remove them from the queue.",
|
||||
"QUEUE_PAUSE_BTN_INFO": "PAUSE — Pause to execute jobs in this type of job queue, jobs can be enqueued when the queue is paused.",
|
||||
"QUEUE_RESUME_BTN_INFO": "RESUME — Resume to execute jobs in this type of job queue.",
|
||||
"SCHEDULE_PAUSE_BTN_INFO": "PAUSE — Pause all schedules to execute.",
|
||||
"SCHEDULE_RESUME_BTN_INFO": "RESUME — Resume all schedule to execute.",
|
||||
"WORKER_FREE_BTN_INFO": "Stop the current running job to free the worker"
|
||||
}
|
||||
}
|
||||
|
@ -1765,5 +1765,75 @@
|
||||
"JOB_NAME_REQUIRED": "Job name is required",
|
||||
"JOB_NAME_EXISTING": "Job name already exists",
|
||||
"TRIGGER_EXPORT_SUCCESS": "Trigger exporting CVEs successfully!"
|
||||
},
|
||||
"JOB_SERVICE_DASHBOARD": {
|
||||
"SCHEDULE_PAUSED": "Scheduled(Paused)",
|
||||
"SCHEDULE_BEEN_PAUSED": "{{param}} has been paused",
|
||||
"PENDING_JOBS": "Pending Jobs In Queues",
|
||||
"OTHERS": "Others",
|
||||
"STOP_ALL": "STOP ALL",
|
||||
"CONFIRM_STOP_ALL": "Confirm Stopping All",
|
||||
"CONFIRM_STOP_ALL_CONTENT": "Do you want to stop all the job queues?",
|
||||
"STOP_ALL_SUCCESS": "Stopped all the job queues successfully",
|
||||
"STOP_BTN": "STOP",
|
||||
"PAUSE_BTN": "PAUSE",
|
||||
"RESUME_BTN": "RESUME",
|
||||
"JOB_TYPE": "Job Type",
|
||||
"PENDING_COUNT": "Pending Count",
|
||||
"LATENCY": "Latency",
|
||||
"PAUSED": "Paused",
|
||||
"NO_JOB_QUEUE": "We could not find any job queue",
|
||||
"CONFIRM_STOPPING_JOBS": "Confirm Stopping Jobs",
|
||||
"CONFIRM_STOPPING_JOBS_CONTENT": "Do you want to stop the jobs {{param}}?",
|
||||
"CONFIRM_PAUSING_JOBS": "Confirm Pausing Jobs",
|
||||
"CONFIRM_PAUSING_JOBS_CONTENT": "Do you want to pause the jobs {{param}}?",
|
||||
"CONFIRM_RESUMING_JOBS": "Confirm Resuming Jobs",
|
||||
"CONFIRM_RESUMING_JOBS_CONTENT": "Do you want to resume the jobs {{param}}?",
|
||||
"STOP_SUCCESS": "Stopped jobs Successfully",
|
||||
"PAUSE_SUCCESS": "Paused jobs Successfully",
|
||||
"RESUME_SUCCESS": "Resumed jobs Successfully",
|
||||
"SCHEDULES": "Schedules",
|
||||
"RUNNING_STATUS": "Running",
|
||||
"RESUME_ALL_BTN_TEXT": "RESUME ALL",
|
||||
"PAUSE_ALL_BTN_TEXT": "PAUSE ALL",
|
||||
"CONFIRM_PAUSING_ALL": "Confirm Pausing All",
|
||||
"CONFIRM_PAUSING_ALL_CONTENT": "Do you want to pause all the jobs schedules?",
|
||||
"CONFIRM_RESUMING_ALL": "Confirm Resuming All",
|
||||
"CONFIRM_RESUMING_ALL_CONTENT": "Do you want to resume all the jobs schedules?",
|
||||
"PAUSE_ALL_SUCCESS": "Paused all the schedules Successfully",
|
||||
"RESUME_ALL_SUCCESS": "Resumed all the schedules Successfully",
|
||||
"VENDOR_TYPE": "Vendor Type",
|
||||
"VENDOR_ID": "Vendor ID",
|
||||
"EXTRA_ATTR": "Extra Attribute",
|
||||
"NO_SCHEDULE": "We could not find any schedule",
|
||||
"WORKERS": "Workers",
|
||||
"FREE_ALL": "Free all",
|
||||
"CONFIRM_FREE_ALL": "Confirm Freeing All",
|
||||
"CONFIRM_FREE_ALL_CONTENT": "Do you want to free all the workers?",
|
||||
"CONFIRM_FREE_WORKERS": "Confirm Freeing Workers",
|
||||
"CONFIRM_FREE_WORKERS_CONTENT": "Do you want to free the workers {{param}}?",
|
||||
"FREE_WORKER_SUCCESS": "Freed workers successfully",
|
||||
"FREE_ALL_SUCCESS": "Freed all the workers successfully",
|
||||
"WORKER_POOL": "Worker Pools",
|
||||
"WORKER_POOL_ID": "Worker Pool ID",
|
||||
"PID": "Pid",
|
||||
"START_AT": "Started At",
|
||||
"HEARTBEAT_AT": "Heartbeat At",
|
||||
"CONCURRENCY": "Concurrency",
|
||||
"NO_WORKER_POOL": "We could not find any worker pool",
|
||||
"FREE": "Free",
|
||||
"WORKER_ID": "Worker ID",
|
||||
"JOB_ID": "Job ID",
|
||||
"JOB_PARAM": "Job Parameter",
|
||||
"CHECK_IN_AT": "Checked In At",
|
||||
"NO_WORKER": "We could not find any worker",
|
||||
"JOB_QUEUE": "Job Queues",
|
||||
"JOB_SERVICE_DASHBOARD": "Job Service Dashboard",
|
||||
"QUEUE_STOP_BTN_INFO": "STOP — Stop all jobs in the queue and remove them from the queue.",
|
||||
"QUEUE_PAUSE_BTN_INFO": "PAUSE — Pause to execute jobs in this type of job queue, jobs can be enqueued when the queue is paused.",
|
||||
"QUEUE_RESUME_BTN_INFO": "RESUME — Resume to execute jobs in this type of job queue.",
|
||||
"SCHEDULE_PAUSE_BTN_INFO": "PAUSE — Pause all schedules to execute.",
|
||||
"SCHEDULE_RESUME_BTN_INFO": "RESUME — Resume all schedule to execute.",
|
||||
"WORKER_FREE_BTN_INFO": "Stop the current running job to free the worker"
|
||||
}
|
||||
}
|
||||
|
@ -1763,5 +1763,75 @@
|
||||
"JOB_NAME_REQUIRED": "任务名称为必填项",
|
||||
"JOB_NAME_EXISTING": "任务名称已存在",
|
||||
"TRIGGER_EXPORT_SUCCESS": "触发导出 CVEs 任务成功!"
|
||||
},
|
||||
"JOB_SERVICE_DASHBOARD": {
|
||||
"SCHEDULE_PAUSED": "定时(已暂停)",
|
||||
"SCHEDULE_BEEN_PAUSED": "{{param}} 已被暂停",
|
||||
"PENDING_JOBS": "待执行任务",
|
||||
"OTHERS": "其他",
|
||||
"STOP_ALL": "停止全部",
|
||||
"CONFIRM_STOP_ALL": "确认停止全部",
|
||||
"CONFIRM_STOP_ALL_CONTENT": "您想停止全部任务栈吗?",
|
||||
"STOP_ALL_SUCCESS": "停止全部任务栈成功",
|
||||
"STOP_BTN": "停止",
|
||||
"PAUSE_BTN": "暂停",
|
||||
"RESUME_BTN": "重启",
|
||||
"JOB_TYPE": "任务类型",
|
||||
"PENDING_COUNT": "待执行数",
|
||||
"LATENCY": "等待时间",
|
||||
"PAUSED": "已暂停",
|
||||
"NO_JOB_QUEUE": "未发现任何任务栈",
|
||||
"CONFIRM_STOPPING_JOBS": "确认停止任务栈",
|
||||
"CONFIRM_STOPPING_JOBS_CONTENT": "您想停止这些任务栈吗 {{param}}?",
|
||||
"CONFIRM_PAUSING_JOBS": "确认暂停任务栈",
|
||||
"CONFIRM_PAUSING_JOBS_CONTENT": "您想暂停这些任务栈吗 {{param}}?",
|
||||
"CONFIRM_RESUMING_JOBS": "确认重启任务栈",
|
||||
"CONFIRM_RESUMING_JOBS_CONTENT": "您想重启这些任务栈吗 {{param}}?",
|
||||
"STOP_SUCCESS": "停止任务栈成功",
|
||||
"PAUSE_SUCCESS": "暂停任务栈成功",
|
||||
"RESUME_SUCCESS": "R重启任务栈成功",
|
||||
"SCHEDULES": "定时任务",
|
||||
"RUNNING_STATUS": "运行中",
|
||||
"RESUME_ALL_BTN_TEXT": "重启全部",
|
||||
"PAUSE_ALL_BTN_TEXT": "暂停全部",
|
||||
"CONFIRM_PAUSING_ALL": "确认暂停全部",
|
||||
"CONFIRM_PAUSING_ALL_CONTENT": "您想暂停所有定时任务吗?",
|
||||
"CONFIRM_RESUMING_ALL": "确认重启全部",
|
||||
"CONFIRM_RESUMING_ALL_CONTENT": "您想重启所有定时任务吗?",
|
||||
"PAUSE_ALL_SUCCESS": "暂停所有定时任务成功",
|
||||
"RESUME_ALL_SUCCESS": "重启所有定时任务成功",
|
||||
"VENDOR_TYPE": "供应商类型",
|
||||
"VENDOR_ID": "供应商 ID",
|
||||
"EXTRA_ATTR": "其他属性",
|
||||
"NO_SCHEDULE": "未发现任何定时任务",
|
||||
"WORKERS": "工作者",
|
||||
"FREE_ALL": "停下全部",
|
||||
"CONFIRM_FREE_ALL": "确认停下全部",
|
||||
"CONFIRM_FREE_ALL_CONTENT": "您想停下全部工作者吗?",
|
||||
"CONFIRM_FREE_WORKERS": "确认停下工作者",
|
||||
"CONFIRM_FREE_WORKERS_CONTENT": "您想停下这些工作者吗 {{param}}?",
|
||||
"FREE_WORKER_SUCCESS": "停下全部工作者成功",
|
||||
"FREE_ALL_SUCCESS": "停下全部工作者成功",
|
||||
"WORKER_POOL": "工作者池",
|
||||
"WORKER_POOL_ID": "工作者池 ID",
|
||||
"PID": "Pid",
|
||||
"START_AT": "开始时间",
|
||||
"HEARTBEAT_AT": "上次心跳检测",
|
||||
"CONCURRENCY": "并行数",
|
||||
"NO_WORKER_POOL": "未发现任何工作者池",
|
||||
"FREE": "停下",
|
||||
"WORKER_ID": "工作者 ID",
|
||||
"JOB_ID": "任务 ID",
|
||||
"JOB_PARAM": "任务参数",
|
||||
"CHECK_IN_AT": "检查时间",
|
||||
"NO_WORKER": "未发现任何工作者",
|
||||
"JOB_QUEUE": "任务栈",
|
||||
"JOB_SERVICE_DASHBOARD": "任务中心",
|
||||
"QUEUE_STOP_BTN_INFO": "停止 — 停止选中的任务栈中所有正在执行的任务,并清空任务栈。",
|
||||
"QUEUE_PAUSE_BTN_INFO": "暂停 — 暂停执行选中的任务栈中的任务,当任务栈处于暂停状态时,进入该栈的任务会进入待执行状态。",
|
||||
"QUEUE_RESUME_BTN_INFO": "重启 — 重启选中的任务栈并开始执行栈中的任务。",
|
||||
"SCHEDULE_PAUSE_BTN_INFO": "暂停 — 暂停所有定时任务,暂停中的定时任务将不会被执行。",
|
||||
"SCHEDULE_RESUME_BTN_INFO": "重启 — 重启所有定时任务,定时任务在触发时会正常执行。",
|
||||
"WORKER_FREE_BTN_INFO": "停下选中的工作者当前正在执行的任务以便释放该工作者,被释放的工作会继续执行其他任务。"
|
||||
}
|
||||
}
|
||||
|
@ -1756,5 +1756,75 @@
|
||||
"JOB_NAME_REQUIRED": "Job name is required",
|
||||
"JOB_NAME_EXISTING": "Job name already exists",
|
||||
"TRIGGER_EXPORT_SUCCESS": "Trigger exporting CVEs successfully!"
|
||||
},
|
||||
"JOB_SERVICE_DASHBOARD": {
|
||||
"SCHEDULE_PAUSED": "Scheduled(Paused)",
|
||||
"SCHEDULE_BEEN_PAUSED": "{{param}} has been paused",
|
||||
"PENDING_JOBS": "Pending Jobs In Queues",
|
||||
"OTHERS": "Others",
|
||||
"STOP_ALL": "STOP ALL",
|
||||
"CONFIRM_STOP_ALL": "Confirm Stopping All",
|
||||
"CONFIRM_STOP_ALL_CONTENT": "Do you want to stop all the job queues?",
|
||||
"STOP_ALL_SUCCESS": "Stopped all the job queues successfully",
|
||||
"STOP_BTN": "STOP",
|
||||
"PAUSE_BTN": "PAUSE",
|
||||
"RESUME_BTN": "RESUME",
|
||||
"JOB_TYPE": "Job Type",
|
||||
"PENDING_COUNT": "Pending Count",
|
||||
"LATENCY": "Latency",
|
||||
"PAUSED": "Paused",
|
||||
"NO_JOB_QUEUE": "We could not find any job queue",
|
||||
"CONFIRM_STOPPING_JOBS": "Confirm Stopping Jobs",
|
||||
"CONFIRM_STOPPING_JOBS_CONTENT": "Do you want to stop the jobs {{param}}?",
|
||||
"CONFIRM_PAUSING_JOBS": "Confirm Pausing Jobs",
|
||||
"CONFIRM_PAUSING_JOBS_CONTENT": "Do you want to pause the jobs {{param}}?",
|
||||
"CONFIRM_RESUMING_JOBS": "Confirm Resuming Jobs",
|
||||
"CONFIRM_RESUMING_JOBS_CONTENT": "Do you want to resume the jobs {{param}}?",
|
||||
"STOP_SUCCESS": "Stopped jobs Successfully",
|
||||
"PAUSE_SUCCESS": "Paused jobs Successfully",
|
||||
"RESUME_SUCCESS": "Resumed jobs Successfully",
|
||||
"SCHEDULES": "Schedules",
|
||||
"RUNNING_STATUS": "Running",
|
||||
"RESUME_ALL_BTN_TEXT": "RESUME ALL",
|
||||
"PAUSE_ALL_BTN_TEXT": "PAUSE ALL",
|
||||
"CONFIRM_PAUSING_ALL": "Confirm Pausing All",
|
||||
"CONFIRM_PAUSING_ALL_CONTENT": "Do you want to pause all the jobs schedules?",
|
||||
"CONFIRM_RESUMING_ALL": "Confirm Resuming All",
|
||||
"CONFIRM_RESUMING_ALL_CONTENT": "Do you want to resume all the jobs schedules?",
|
||||
"PAUSE_ALL_SUCCESS": "Paused all the schedules Successfully",
|
||||
"RESUME_ALL_SUCCESS": "Resumed all the schedules Successfully",
|
||||
"VENDOR_TYPE": "Vendor Type",
|
||||
"VENDOR_ID": "Vendor ID",
|
||||
"EXTRA_ATTR": "Extra Attribute",
|
||||
"NO_SCHEDULE": "We could not find any schedule",
|
||||
"WORKERS": "Workers",
|
||||
"FREE_ALL": "Free all",
|
||||
"CONFIRM_FREE_ALL": "Confirm Freeing All",
|
||||
"CONFIRM_FREE_ALL_CONTENT": "Do you want to free all the workers?",
|
||||
"CONFIRM_FREE_WORKERS": "Confirm Freeing Workers",
|
||||
"CONFIRM_FREE_WORKERS_CONTENT": "Do you want to free the workers {{param}}?",
|
||||
"FREE_WORKER_SUCCESS": "Freed workers successfully",
|
||||
"FREE_ALL_SUCCESS": "Freed all the workers successfully",
|
||||
"WORKER_POOL": "Worker Pools",
|
||||
"WORKER_POOL_ID": "Worker Pool ID",
|
||||
"PID": "Pid",
|
||||
"START_AT": "Started At",
|
||||
"HEARTBEAT_AT": "Heartbeat At",
|
||||
"CONCURRENCY": "Concurrency",
|
||||
"NO_WORKER_POOL": "We could not find any worker pool",
|
||||
"FREE": "Free",
|
||||
"WORKER_ID": "Worker ID",
|
||||
"JOB_ID": "Job ID",
|
||||
"JOB_PARAM": "Job Parameter",
|
||||
"CHECK_IN_AT": "Checked In At",
|
||||
"NO_WORKER": "We could not find any worker",
|
||||
"JOB_QUEUE": "Job Queues",
|
||||
"JOB_SERVICE_DASHBOARD": "Job Service Dashboard",
|
||||
"QUEUE_STOP_BTN_INFO": "STOP — Stop all jobs in the queue and remove them from the queue.",
|
||||
"QUEUE_PAUSE_BTN_INFO": "PAUSE — Pause to execute jobs in this type of job queue, jobs can be enqueued when the queue is paused.",
|
||||
"QUEUE_RESUME_BTN_INFO": "RESUME — Resume to execute jobs in this type of job queue.",
|
||||
"SCHEDULE_PAUSE_BTN_INFO": "PAUSE — Pause all schedules to execute.",
|
||||
"SCHEDULE_RESUME_BTN_INFO": "RESUME — Resume all schedule to execute.",
|
||||
"WORKER_FREE_BTN_INFO": "Stop the current running job to free the worker"
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user