#56 step into layout revamp

This commit is contained in:
Fabio Formosa
2022-08-04 00:43:53 +02:00
parent 09df7795a9
commit f1c9fba68e
32 changed files with 503 additions and 162 deletions

View File

@@ -1,5 +1,8 @@
<app-header></app-header>
<div class="content">
<router-outlet></router-outlet>
<div fxLayout="column" fxLayoutAlign="space-between stretch" fxFill>
<app-header fxFlex="0 0 auto"></app-header>
<div class="content" fxFlex="100" fxFill>
<router-outlet></router-outlet>
</div>
<app-footer fxFlex="0 0 auto"></app-footer>
</div>
<app-footer></app-footer>

View File

@@ -2,20 +2,9 @@
display: block;
color: rgba(0,0,0,.54);
font-family: Roboto,"Helvetica Neue";
height: 100%;
}
.content {
margin: 50px 70px;
}
@media screen and (min-width: 600px) and (max-width: 1279px) {
.content {
margin: 20px 30px;
}
}
@media screen and (max-width: 599px) {
.content {
margin: 8px 12px;
}
padding: 20px;
}

View File

@@ -18,6 +18,8 @@ import {MatIconModule} from '@angular/material/icon';
import {MatButtonModule} from '@angular/material/button';
import {MatCardModule} from '@angular/material/card';
import {MatDatepickerModule} from '@angular/material/datepicker';
import {MatListModule} from '@angular/material/list';
import {MatSidenavModule} from '@angular/material/sidenav';
import {MatNativeDateModule} from '@angular/material/core';
import { NgxMatTimepickerModule, NgxMatDatetimePickerModule} from '@angular-material-components/datetime-picker';
@@ -40,7 +42,8 @@ import {
SchedulerConfigComponent,
SchedulerControlComponent,
LogsPanelComponent,
ProgressPanelComponent
ProgressPanelComponent,
TriggerListComponent
} from './components';
import {
@@ -51,7 +54,8 @@ import {
ConfigService,
ProgressWebsocketService,
LogsWebsocketService,
getHtmlBaseUrl
getHtmlBaseUrl,
TriggerService
} from './services';
import { ChangePasswordComponent } from './views/change-password/change-password.component';
import { ForbiddenComponent } from './views/forbidden/forbidden.component';
@@ -113,7 +117,8 @@ export function jwtOptionsFactory(apiService: ApiService) {
LogsPanelComponent,
ProgressPanelComponent,
ChangePasswordComponent,
ForbiddenComponent
ForbiddenComponent,
TriggerListComponent
],
imports: [
BrowserAnimationsModule,
@@ -137,11 +142,13 @@ export function jwtOptionsFactory(apiService: ApiService) {
MatInputModule,
MatToolbarModule,
MatCardModule,
MatListModule,
MatProgressSpinnerModule,
MatProgressBarModule,
MatDatepickerModule, MatNativeDateModule,
NgxMatMomentModule,
NgxMatDatetimePickerModule,
MatSidenavModule,
FlexLayoutModule
],
providers: [
@@ -159,6 +166,7 @@ export function jwtOptionsFactory(apiService: ApiService) {
GuestGuard,
AdminGuard,
SchedulerService,
TriggerService,
ProgressWebsocketService,
LogsWebsocketService,
AuthService,

View File

@@ -1,7 +1,8 @@
<p style="margin: 0px auto; padding: 0px; color: rgba(255, 255, 255, 0.541176); max-width: 356px;">
Hand crafted with love by
<a href="https://github.com/fabioformosa" style="color: rgba(255, 255, 255, 0.870588);">Fabio Formosa</a>
</p>
<a style="margin-top: 22px;" mat-icon-button href="https://github.com/fabioformosa/quartz-manager">
<img src="assets/image/github.png"/>
</a>
<mat-toolbar id="footer" style="color: rgba(255, 255, 255, 0.541176);" fxLayout="row" fxLayoutAlign="center center">
<a mat-icon-button href="https://github.com/fabioformosa/quartz-manager">
<img src="assets/image/github.png"/>
&nbsp; Quartz Manager
</a>
<!-- Hand crafted with love by &nbsp;-->
<!-- <a href="https://github.com/fabioformosa" style="color: rgba(255, 255, 255, 0.870588);">Fabio Formosa</a>-->
</mat-toolbar>

View File

@@ -1,18 +1,20 @@
:host {
display: block;
font-weight: 300;
font-size: 15px;
display: block;
:host{
//position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 64px;
}
#footer{
background-color: rgb(33, 33, 33);
height: 236px;
padding: 72px 24px;
font-size: 15px;
box-sizing: border-box;
text-align: center;
a {
text-decoration: none;
cursor: auto;
cursor: pointer;
color: #FFFFFF;
margin-top: 32px;
}
h3 {
@@ -21,7 +23,5 @@
font-weight: 300;
font-size: 22px;
}
}

View File

@@ -1,6 +1,6 @@
<mat-toolbar color="primary" class="app-navbar">
<button mat-button mat-ripple routerLink="/">
<img alt="Quartz Manager" class="app-angular-logo" src="assets/image/angular-white-transparent.svg">
<!-- <img alt="Quartz Manager" class="app-angular-logo" src="assets/image/angular-white-transparent.svg">-->
<span>Quartz Manager</span>
</button>

View File

@@ -5,3 +5,4 @@ export * from './logs-panel';
export * from './scheduler-config';
export * from './scheduler-control';
export * from './progress-panel';
export * from './trigger-list';

View File

@@ -1,6 +1,6 @@
<!-- <div class="progress" [hidden]="progress.percentage < 0">
<div class="progress-bar"
role="progressbar"
<div class="progress-bar"
role="progressbar"
[ngStyle]="{width: percentageStr}">
{{percentageStr}}
</div>
@@ -8,29 +8,29 @@
<mat-card>
<mat-card-header>
<mat-card-title><b>JOB PROGRESS</b></mat-card-title>
<mat-card-subtitle><b>JOB PROGRESS</b></mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<div>
<mat-progress-bar mode="determinate" value="{{progress.percentage}}"></mat-progress-bar>
{{percentageStr}}
</div>
<br>
<div>
<mat-chip>counter</mat-chip>&nbsp;
<mat-chip>counter</mat-chip>&nbsp;
<span class="animated pulse">{{progress.timesTriggered}}</span> <span ng-show="progress.repeatCount > 0">/ {{progress.repeatCount}} </span>
<br/><br/>
<mat-chip>job key</mat-chip> <span class="animated pulse">{{progress.jobKey}}</span><br>
<mat-chip>job class</mat-chip> <span class="animated pulse">{{progress.jobClass}}</span><br/>
<br/>
<mat-chip>prev fire time</mat-chip> <span class="animated pulse">{{progress.previousFireTime|date:'dd-MM-yyyy HH:mm:ss'}}</span><br/>
<mat-chip>next fire time</mat-chip> <span class="animated pulse">{{progress.nextFireTime|date:'dd-MM-yyyy HH:mm:ss'}}</span><br/>
<mat-chip>final fire time</mat-chip> <span class="animated pulse">{{progress.finalFireTime|date:'dd-MM-yyyy HH:mm:ss'}}</span><br/>
</div>
</mat-card-content>
</mat-card>
</mat-card>

View File

@@ -1,15 +1,26 @@
<mat-card>
<mat-card-header>
<mat-card-title><b>SCHEDULER CONTROLLER</b></mat-card-title>
</mat-card-header>
<mat-card-content>
<button id="schedulerControllerBtn1" mat-raised-button class="btn btn-default large-btn" (click)="startOrPause()">
<span *ngIf = "schedulerState === 'running'">
<i class="fas fa-pause red"></i>
</span>
<span *ngIf = "schedulerState === 'stopped' || schedulerState === 'paused'">
<i class="fas fa-play green"></i>
</span>
</button>
<div fxLayout="row" fxLayoutAlign="left stretch" fxLayoutGap="30px">
<button id="schedulerControllerBtn" mat-raised-button class="btn btn-default large-btn" (click)="startOrPause()">
<span *ngIf = "scheduler?.status === 'RUNNING'">
<i class="fas fa-pause red"></i>
</span>
<span *ngIf = "scheduler?.status === 'STOPPED' || scheduler?.status === 'PAUSED'">
<i class="fas fa-play green"></i>
</span>
</button>
<div fxLayout="column center">
<mat-card-subtitle style="margin: auto;"><b>SCHEDULER</b></mat-card-subtitle>
</div>
<mat-divider [vertical]="true"></mat-divider>
<div fxLayout="column">
<div><label>Name</label></div>
<div><span id="scheduler-name">{{scheduler?.name}}</span></div>
</div>
<div fxLayout="column">
<div><label>Instance ID</label></div>
<div><span id="scheduler-instance">{{scheduler?.instanceId}}</span></div>
</div>
</div>
</mat-card-content>
</mat-card>
</mat-card>

View File

@@ -3,4 +3,15 @@
}
.green{
color: green;
}
}
label{
color: grey;
font-variant: small-caps;
font-size: smaller;
}
#scheduler-name{
text-transform: capitalize;
font-size: larger;
}

View File

@@ -0,0 +1,105 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {SchedulerControlComponent} from './scheduler-control.component';
import {ApiService, ConfigService, SchedulerService, UserService} from '../../services';
import {HttpClient} from '@angular/common/http';
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
import {RouterTestingModule} from '@angular/router/testing';
import {DebugElement} from '@angular/core';
import {By} from '@angular/platform-browser';
import {Scheduler} from '../../model/scheduler.model';
import {MatCardModule} from '@angular/material/card';
import {MatIconModule} from '@angular/material/icon';
import {MatDividerModule} from '@angular/material/divider';
describe('SchedulerControlComponent', () => {
let component: SchedulerControlComponent;
let fixture: ComponentFixture<SchedulerControlComponent>;
let httpClient: HttpClient;
let httpTestingController: HttpTestingController;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [MatCardModule, MatDividerModule, MatIconModule, HttpClientTestingModule, RouterTestingModule],
declarations: [SchedulerControlComponent],
providers: [UserService, SchedulerService, ApiService, ConfigService]
}).compileComponents();
httpClient = TestBed.inject(HttpClient);
httpTestingController = TestBed.inject(HttpTestingController);
}));
beforeEach(() => {
fixture = TestBed.createComponent(SchedulerControlComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should display the play button at the beginning since the scheduler is stopped', () => {
expect(component).toBeDefined();
const getSchedulerReq = httpTestingController.expectOne('/quartz-manager/scheduler');
const mockScheduler = new Scheduler('test-scheduler', 'test-id', 'STOPPED', []);
getSchedulerReq.flush(mockScheduler);
expect(component.scheduler).toEqual(mockScheduler);
expect(component.scheduler.status).toEqual('STOPPED');
fixture.detectChanges();
const schedulerControlComponentDe: DebugElement = fixture.debugElement;
const schedulerBtnDe = schedulerControlComponentDe.query(By.css('#schedulerControllerBtn'));
expect(schedulerBtnDe).toBeTruthy();
const playIconDe = schedulerBtnDe.query(By.css('.fa-play'));
expect(playIconDe).toBeTruthy();
});
it('should switch the button to pause when the scheduler is started', () => {
expect(component).toBeDefined();
const getSchedulerReq = httpTestingController.expectOne('/quartz-manager/scheduler');
const mockScheduler = new Scheduler('test-scheduler', 'test-id', 'STOPPED', []);
getSchedulerReq.flush(mockScheduler);
fixture.detectChanges();
const schedulerControlComponentDe: DebugElement = fixture.debugElement;
let schedulerBtnDe = schedulerControlComponentDe.query(By.css('#schedulerControllerBtn'));
expect(schedulerBtnDe).toBeTruthy();
const playIconDe = schedulerBtnDe.query(By.css('.fa-play'));
expect(playIconDe).toBeTruthy();
schedulerBtnDe.nativeElement.click();
const startSchedulerReq = httpTestingController.expectOne('/quartz-manager/scheduler/run');
startSchedulerReq.flush(null);
fixture.detectChanges();
schedulerBtnDe = schedulerControlComponentDe.query(By.css('#schedulerControllerBtn'));
const pauseIconDe = schedulerBtnDe.query(By.css('.fa-pause'));
expect(pauseIconDe).toBeTruthy();
})
it('should switch the button to play when the scheduler is stopped', () => {
expect(component).toBeDefined();
const getSchedulerReq = httpTestingController.expectOne('/quartz-manager/scheduler');
const mockScheduler = new Scheduler('test-scheduler', 'test-id', 'RUNNING', []);
getSchedulerReq.flush(mockScheduler);
fixture.detectChanges();
const schedulerControlComponentDe: DebugElement = fixture.debugElement;
let schedulerBtnDe = schedulerControlComponentDe.query(By.css('#schedulerControllerBtn'));
expect(schedulerBtnDe).toBeTruthy();
const pauseIconDe = schedulerBtnDe.query(By.css('.fa-pause'));
expect(pauseIconDe).toBeTruthy();
schedulerBtnDe.nativeElement.click();
const startSchedulerReq = httpTestingController.expectOne('/quartz-manager/scheduler/pause');
startSchedulerReq.flush(null);
fixture.detectChanges();
schedulerBtnDe = schedulerControlComponentDe.query(By.css('#schedulerControllerBtn'));
const playIconDe = schedulerBtnDe.query(By.css('.fa-play'));
expect(playIconDe).toBeTruthy();
})
});

View File

@@ -1,54 +1,80 @@
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { UserService, SchedulerService } from '../../services';
import {Component, OnInit} from '@angular/core';
import {SchedulerService, UserService} from '../../services';
import {Scheduler} from '../../model/scheduler.model';
@Component({
selector: 'scheduler-control',
selector: 'qrzmng-scheduler-control',
templateUrl: './scheduler-control.component.html',
styleUrls: ['./scheduler-control.component.scss']
})
export class SchedulerControlComponent implements OnInit {
schedulerState;
scheduler: Scheduler;
constructor(
private userService: UserService,
private schedulerService: SchedulerService
) { }
) {
}
ngOnInit() {
this.schedulerService.getStatus().subscribe(res => {this.schedulerState = res.data}, err => {console.log(err)});
this._getScheduler();
}
startScheduler = function(){
this.schedulerService.startScheduler().subscribe((res) => {this.schedulerState = 'running'}, (res) => {console.log(JSON.stringify(res))});
private _getScheduler() {
this.schedulerService.getScheduler()
.subscribe(resp => this.scheduler = resp);
}
startScheduler = function () {
this.schedulerService.startScheduler().subscribe((res) => {
this.scheduler.status = 'RUNNING'
}, (res) => {
console.log(JSON.stringify(res))
});
};
stopScheduler = function(){
this.schedulerService.stopScheduler().subscribe((res) => {this.schedulerState = 'stopped'}, (res) => {console.log(JSON.stringify(res))});
stopScheduler = function () {
this.schedulerService.stopScheduler().subscribe((res) => {
this.scheduler.status = 'STOPPED'
}, (res) => {
console.log(JSON.stringify(res))
});
};
pauseScheduler = function(){
this.schedulerService.pauseScheduler().subscribe((res) => {this.schedulerState = 'paused'}, (res) => {console.log(JSON.stringify(res))});
pauseScheduler = function () {
this.schedulerService.pauseScheduler().subscribe((res) => {
this.scheduler.status = 'PAUSED'
}, (res) => {
console.log(JSON.stringify(res))
});
};
resumeScheduler = function(){
this.schedulerService.resumeScheduler().subscribe((res) => {this.schedulerState = 'running'}, (res) => {console.log(JSON.stringify(res))});
resumeScheduler = function () {
this.schedulerService.resumeScheduler().subscribe((res) => {
this.scheduler.status = 'RUNNING'
}, (res) => {
console.log(JSON.stringify(res))
});
};
stop = function(){
if(this.schedulerState != 'stopped')
stop = function () {
if (this.scheduler.status !== 'STOPPED') {
this.stopScheduler();
}
}
startOrPause = function(){
switch (this.schedulerState) {
case 'running': this.pauseScheduler();
break;
case 'paused': this.resumeScheduler();
break;
default:
this.startScheduler();
break;
startOrPause = function () {
switch (this.scheduler.status) {
case 'RUNNING':
this.pauseScheduler();
break;
case 'PAUSED':
this.resumeScheduler();
break;
default:
this.startScheduler();
break;
}
};

View File

@@ -1,6 +1,6 @@
<mat-card>
<mat-card-header>
<mat-card-title><b>SCHEDULER CONFIG</b></mat-card-title>
<mat-card-subtitle><b>TRIGGER DETAILS</b></mat-card-subtitle>
</mat-card-header>
<!-- ADD BUTTON -->

View File

@@ -0,0 +1 @@
export * from './trigger-list.component'

View File

@@ -0,0 +1,19 @@
<mat-card fxFlex="1 1 auto" style="padding-left: 0; padding-right: 0">
<mat-card-header fxLayout="row" fxLayoutAlign="space-between none" style="padding-right: 1em;" >
<mat-card-subtitle><b>TRIGGERS</b></mat-card-subtitle>
<button mat-raised-button style="top: -0.5em" color="primary" (click)="openNewTriggerForm()">
new
</button>
</mat-card-header>
<mat-divider></mat-divider>
<mat-card-content style="position: relative; height: 100%">
<mat-nav-list style="overflow-y: auto; position: absolute; left: 0; right: 0; top: 0; bottom: 0; overflow: auto; height: calc(100% - 3em)">
<mat-list-item *ngFor="let triggerKey of triggerKeys">
<a matLine href="...">{{ triggerKey.name }}</a>
<!-- <button mat-icon-button (click)="showInfo(link)">-->
<!-- <mat-icon>info</mat-icon>-->
<!-- </button>-->
</mat-list-item>
</mat-nav-list>
</mat-card-content>
</mat-card>

View File

@@ -0,0 +1,21 @@
/* ===== Scrollbar CSS ===== */
/* Firefox */
* {
scrollbar-width: auto;
scrollbar-color: #b8b8b8 #ffffff;
}
/* Chrome, Edge, and Safari */
*::-webkit-scrollbar {
width: 16px;
}
*::-webkit-scrollbar-track {
background: #ffffff;
}
*::-webkit-scrollbar-thumb {
background-color: #b8b8b8;
border-radius: 10px;
border: 3px solid #ffffff;
}

View File

@@ -0,0 +1,36 @@
import {Component, EventEmitter, OnInit, Output} from '@angular/core';
import {TriggerService} from '../../services/trigger.service';
import {TriggerKey} from '../../model/triggerKey.model';
@Component({
selector: 'qrzmng-trigger-list',
templateUrl: './trigger-list.component.html',
styleUrls: ['./trigger-list.component.scss']
})
export class TriggerListComponent implements OnInit {
loading = true;
triggerKeys: Array<TriggerKey> = [];
@Output() openedNewTriggerFormEvent = new EventEmitter<boolean>();
constructor(
private triggerService: TriggerService
) { }
ngOnInit() {
this.loading = true;
this.fetchTriggers();
}
private fetchTriggers() {
this.triggerService.fetchTriggers()
.subscribe((triggerKeys: Array<TriggerKey>) => {
this.triggerKeys = triggerKeys;
})
}
openNewTriggerForm() {
this.openedNewTriggerFormEvent.emit(true);
}
}

View File

@@ -3,10 +3,12 @@ import {TriggerKey} from './triggerKey.model';
export class Scheduler {
name: string;
instanceId: string;
status: string;
triggerKeys: TriggerKey[];
constructor(name: string, instanceId: string, triggerKeys: TriggerKey[]) {
constructor(name: string, instanceId: string, status: string, triggerKeys: TriggerKey[]) {
this.name = name;
this.status = status;
this.instanceId = instanceId;
this.triggerKeys = triggerKeys;
}

View File

@@ -6,4 +6,5 @@ export * from './scheduler.service';
export * from './websocket.service';
export * from './progress.websocket.service';
export * from './logs.websocket.service';
export * from './trigger.service'

View File

@@ -5,6 +5,7 @@ import {Trigger} from '../model/trigger.model';
import {Observable} from 'rxjs';
import {SimpleTriggerCommand} from '../model/simple-trigger.command';
import {SchedulerConfig} from '../model/schedulerConfig.model';
import {Scheduler} from '../model/scheduler.model';
@Injectable()
export class SchedulerService {
@@ -13,19 +14,19 @@ export class SchedulerService {
private apiService: ApiService
) { }
startScheduler = () => {
startScheduler = (): Observable<void> => {
return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler/run')
}
stopScheduler = () => {
stopScheduler = (): Observable<void> => {
return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler/stop')
}
pauseScheduler = () => {
pauseScheduler = (): Observable<void> => {
return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler/pause')
}
resumeScheduler = () => {
resumeScheduler = (): Observable<void> => {
return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler/resume')
}
@@ -33,7 +34,7 @@ export class SchedulerService {
return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler/status')
}
getScheduler = () => {
getScheduler = (): Observable<Scheduler> => {
return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler')
}

View File

@@ -0,0 +1,20 @@
import {ApiService} from './api.service';
import {Injectable} from '@angular/core';
import {Observable} from 'rxjs';
import {Trigger} from '../model/trigger.model';
import {TriggerKey} from '../model/triggerKey.model';
import {getBaseUrl} from './config.service';
@Injectable()
export class TriggerService {
constructor(
private apiService: ApiService) {
}
fetchTriggers = (): Observable<Array<TriggerKey>> => {
return this.apiService.get(getBaseUrl() + 'quartz-manager/triggers');
}
}

View File

@@ -1,19 +1,32 @@
<div class="content" fxLayout="row" fxLayoutAlign="center none">
<div id="managerViewContainer" class="contenttt" fxLayout="column" fxLayoutAlign="left stretch" fxLayoutGap="10px" fxFill>
<div fxFlex="1 1 30%" fxFill>
<div fxLayout="column">
<scheduler-control></scheduler-control>
<br/>
<qrzmng-simple-trigger-config></qrzmng-simple-trigger-config>
</div>
<div id="schedulerBarContainer" class="contentttt" fxLayout="column" fxLayoutAlign="left stretch">
<qrzmng-scheduler-control></qrzmng-scheduler-control>
</div>
<div fxFlex="1 1 70%" style="margin-left: 20px">
<div fxLayout="column">
<div fxFlex="1 1 100%"><progress-panel></progress-panel></div>
<br/>
<div fxFlex="1 1 100%"><logs-panel></logs-panel></div>
</div>
</div>
<div fxLayout="row" fxLayoutGap="20px" fxLayoutAlign="center stretch" fxFlex="1 1 auto">
<div fxFlex="0 0 300px">
<div fxLayout="row" fxLayoutAlign="stretch" fxFill>
<qrzmng-trigger-list (openedNewTriggerFormEvent)="setnewTriggerFormOpened($event)" fxFill></qrzmng-trigger-list>
</div>
</div>
<div fxFlex="1 1 500px" >
<div fxLayout="column">
<qrzmng-simple-trigger-config></qrzmng-simple-trigger-config>
</div>
</div>
<div fxFlex="1 1 auto" style="margin-left: 20px">
<div fxLayout="column">
<div fxFlex="1 1 100%"><progress-panel></progress-panel></div>
<br/>
<div fxFlex="1 1 100%"><logs-panel></logs-panel></div>
</div>
</div>
</div>
</div>

View File

@@ -11,8 +11,8 @@ import {
})
export class ManagerComponent implements OnInit {
whoamIResponse = {};
allUserResponse = {};
newTriggerFormOpened = false;
constructor(
private config: ConfigService,
private userService: UserService
@@ -21,4 +21,8 @@ export class ManagerComponent implements OnInit {
ngOnInit() {
}
setnewTriggerFormOpened(opened: boolean){
this.newTriggerFormOpened = opened;
}
}

View File

@@ -1,6 +1,13 @@
/* You can add global styles to this file, and also import other style files */
@import '~@angular/material/prebuilt-themes/deeppurple-amber.css';
html {
display: flex;
flex-direction: column;
height: 100%;
}
body {
margin: 0;
flex:1;
background-color: #f1f1f1;
}

View File

@@ -9,8 +9,8 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam;
import it.fabioformosa.quartzmanager.dto.SchedulerDTO;
import it.fabioformosa.quartzmanager.dto.TriggerStatus;
import it.fabioformosa.quartzmanager.enums.SchedulerStates;
import it.fabioformosa.quartzmanager.services.LegacySchedulerService;
import it.fabioformosa.quartzmanager.services.SchedulerService;
import org.quartz.SchedulerException;
import org.quartz.SimpleTrigger;
import org.quartz.impl.triggers.SimpleTriggerImpl;
@@ -24,8 +24,6 @@ import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.Map;
/**
* This controller provides scheduler info about config and status. It provides
@@ -42,8 +40,11 @@ public class SchedulerController {
private LegacySchedulerService legacySchedulerService;
public SchedulerController(LegacySchedulerService legacySchedulerService, ConversionService conversionService) {
private SchedulerService schedulerService;
public SchedulerController(LegacySchedulerService legacySchedulerService, SchedulerService schedulerService, ConversionService conversionService) {
this.legacySchedulerService = legacySchedulerService;
this.schedulerService = schedulerService;
this.conversionService = conversionService;
}
@@ -82,9 +83,8 @@ public class SchedulerController {
schema = @Schema(implementation = SchedulerDTO.class)) })
})
public SchedulerDTO getScheduler() {
log.debug("SCHEDULER - GET Scheduler...");
SchedulerDTO schedulerDTO = conversionService.convert(legacySchedulerService.getScheduler(), SchedulerDTO.class);
return schedulerDTO;
log.trace("SCHEDULER - GET Scheduler...");
return schedulerService.getScheduler();
}
//TODO move this to the Trigger Controller
@@ -113,24 +113,26 @@ public class SchedulerController {
return progress;
}
@GetMapping(value = "/status", produces = "application/json")
@Operation(summary = "Get the scheduler status")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "Return the scheduler status",
content = { @Content(mediaType = "application/json",
schema = @Schema(implementation = SchedulerStates.class)) })
})
public Map<String, String> getStatus() throws SchedulerException {
log.trace("SCHEDULER - GET STATUS");
String schedulerState = "";
if (legacySchedulerService.getScheduler().isShutdown() || !legacySchedulerService.getScheduler().isStarted())
schedulerState = SchedulerStates.STOPPED.toString();
else if (legacySchedulerService.getScheduler().isStarted() && legacySchedulerService.getScheduler().isInStandbyMode())
schedulerState = SchedulerStates.PAUSED.toString();
else
schedulerState = SchedulerStates.RUNNING.toString();
return Collections.singletonMap("data", schedulerState.toLowerCase());
}
//REMOVEME
// @GetMapping(value = "/status", produces = "application/json")
// @Operation(summary = "Get the scheduler status")
// @ApiResponses(value = {
// @ApiResponse(responseCode = "200", description = "Return the scheduler status",
// content = { @Content(mediaType = "application/json",
// schema = @Schema(implementation = SchedulerStates.class)) })
// })
// public Map<String, String> getStatus() throws SchedulerException {
// log.trace("SCHEDULER - GET STATUS");
// String schedulerState = "";
// if (legacySchedulerService.getScheduler().isShutdown() || !legacySchedulerService.getScheduler().isStarted())
// schedulerState = SchedulerStates.STOPPED.toString();
// else if (legacySchedulerService.getScheduler().isStarted() && legacySchedulerService.getScheduler().isInStandbyMode())
// schedulerState = SchedulerStates.PAUSED.toString();
// else
// schedulerState = SchedulerStates.RUNNING.toString();
// return Collections.singletonMap("data", schedulerState.toLowerCase());
// }
@GetMapping("/pause")
@Operation(summary = "Get paused the scheduler")

View File

@@ -10,12 +10,14 @@ import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam;
import it.fabioformosa.quartzmanager.dto.TriggerDTO;
import it.fabioformosa.quartzmanager.exceptions.TriggerNotFoundException;
import it.fabioformosa.quartzmanager.services.LegacySchedulerService;
import it.fabioformosa.quartzmanager.services.TriggerService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.SchedulerException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
@Slf4j
@RequestMapping(TriggerController.TRIGGER_CONTROLLER_BASE_URL)
@@ -26,11 +28,19 @@ public class TriggerController extends AbstractTriggerController {
static public final String TRIGGER_CONTROLLER_BASE_URL = "/quartz-manager/triggers";
private LegacySchedulerService schedulerService;
private TriggerService triggerService;
public TriggerController(LegacySchedulerService schedulerService) {
public TriggerController(LegacySchedulerService schedulerService, TriggerService triggerService) {
this.schedulerService = schedulerService;
this.triggerService = triggerService;
}
@GetMapping
public List<TriggerDTO> listTriggers() throws SchedulerException {
return triggerService.fetchTriggers();
}
@GetMapping("/{name}")
public TriggerDTO getTrigger(@PathVariable String name) throws SchedulerException, TriggerNotFoundException {
return schedulerService.getLegacyTriggerByName(name);

View File

@@ -2,8 +2,10 @@ package it.fabioformosa.quartzmanager.converters;
import it.fabioformosa.metamorphosis.core.converters.AbstractBaseConverterToDTO;
import it.fabioformosa.quartzmanager.dto.SchedulerDTO;
import it.fabioformosa.quartzmanager.enums.SchedulerStatus;
import lombok.SneakyThrows;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.stereotype.Component;
@@ -16,6 +18,15 @@ public class SchedulerToSchedulerDTO extends AbstractBaseConverterToDTO<Schedule
target.setName(source.getSchedulerName());
target.setInstanceId(source.getSchedulerInstanceId());
target.setTriggerKeys(source.getTriggerKeys(GroupMatcher.anyTriggerGroup()));
target.setStatus(buildTheSchedulerStatus(source));
}
private SchedulerStatus buildTheSchedulerStatus(Scheduler scheduler) throws SchedulerException {
if (scheduler.isShutdown() || !scheduler.isStarted())
return SchedulerStatus.STOPPED;
else if (scheduler.isStarted() && scheduler.isInStandbyMode())
return SchedulerStatus.PAUSED;
return SchedulerStatus.RUNNING;
}
}

View File

@@ -1,35 +1,19 @@
package it.fabioformosa.quartzmanager.dto;
import it.fabioformosa.quartzmanager.enums.SchedulerStatus;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.quartz.TriggerKey;
import java.util.Set;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class SchedulerDTO {
private String name;
private String instanceId;
private SchedulerStatus status;
private Set<TriggerKey> triggerKeys;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setInstanceId(String instanceId) {
this.instanceId = instanceId;
}
public String getInstanceId() {
return instanceId;
}
public void setTriggerKeys(Set<TriggerKey> triggerKeys) {
this.triggerKeys = triggerKeys;
}
public Set<TriggerKey> getTriggerKeys() {
return triggerKeys;
}
}

View File

@@ -1,5 +1,5 @@
package it.fabioformosa.quartzmanager.enums;
public enum SchedulerStates {
public enum SchedulerStatus {
RUNNING, STOPPED, PAUSED
}
}

View File

@@ -0,0 +1,19 @@
package it.fabioformosa.quartzmanager.services;
import it.fabioformosa.quartzmanager.dto.SchedulerDTO;
import org.quartz.Scheduler;
import org.springframework.core.convert.ConversionService;
import org.springframework.stereotype.Service;
@Service
public class SchedulerService extends AbstractSchedulerService{
public SchedulerService(Scheduler scheduler, ConversionService conversionService) {
super(scheduler, conversionService);
}
public SchedulerDTO getScheduler() {
return conversionService.convert(scheduler, SchedulerDTO.class);
}
}

View File

@@ -0,0 +1,34 @@
package it.fabioformosa.quartzmanager.services;
import it.fabioformosa.quartzmanager.dto.TriggerDTO;
import it.fabioformosa.quartzmanager.dto.TriggerKeyDTO;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerKey;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Set;
@Service
public class TriggerService {
private Scheduler scheduler;
private ConversionService conversionService;
public TriggerService(Scheduler scheduler, ConversionService conversionService) {
this.scheduler = scheduler;
this.conversionService = conversionService;
}
public List<TriggerDTO> fetchTriggers() throws SchedulerException {
Set<TriggerKey> triggerKeys = scheduler.getTriggerKeys(GroupMatcher.anyTriggerGroup());
return (List<TriggerDTO>) conversionService.convert(triggerKeys,
TypeDescriptor.collection(Set.class, TypeDescriptor.valueOf(TriggerKey.class)),
TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(TriggerKeyDTO.class)));
}
}