mirror of
https://github.com/fabioformosa/quartz-manager.git
synced 2026-05-14 22:00:30 +09:00
#103 final step into multi-trigger feature
This commit is contained in:
19
quartz-manager-frontend/.eslintrc.sonar.json
Normal file
19
quartz-manager-frontend/.eslintrc.sonar.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es6": true,
|
||||
"node": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:sonarjs/recommended"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"project": "tsconfig.json",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [
|
||||
"sonarjs"
|
||||
],
|
||||
"root": true
|
||||
}
|
||||
20
quartz-manager-frontend/package-lock.json
generated
20
quartz-manager-frontend/package-lock.json
generated
@@ -62,6 +62,7 @@
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-sonarjs": "^0.16.0",
|
||||
"jasmine-core": "~4.5.0",
|
||||
"jasmine-spec-reporter": "~7.0.0",
|
||||
"jest": "28.1.3",
|
||||
@@ -8604,6 +8605,18 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-sonarjs": {
|
||||
"version": "0.16.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.16.0.tgz",
|
||||
"integrity": "sha512-al8ojAzcQW8Eu0tWn841ldhPpPcjrJ59TzzTfAVWR45bWvdAASCmrGl8vK0MWHyKVDdC0i17IGbtQQ1KgxLlVA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"eslint": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-scope": {
|
||||
"version": "5.1.1",
|
||||
"dev": true,
|
||||
@@ -26048,6 +26061,13 @@
|
||||
"prettier-linter-helpers": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"eslint-plugin-sonarjs": {
|
||||
"version": "0.16.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-sonarjs/-/eslint-plugin-sonarjs-0.16.0.tgz",
|
||||
"integrity": "sha512-al8ojAzcQW8Eu0tWn841ldhPpPcjrJ59TzzTfAVWR45bWvdAASCmrGl8vK0MWHyKVDdC0i17IGbtQQ1KgxLlVA==",
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"eslint-scope": {
|
||||
"version": "5.1.1",
|
||||
"dev": true,
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
"build": "ng build --configuration production",
|
||||
"test": "jest",
|
||||
"lint": "ng lint",
|
||||
"lint:sonar": "eslint --no-eslintrc -c .eslintrc.sonar.json \"src/**/*.ts\"",
|
||||
"lint:sonar:fix": "eslint --no-eslintrc -c .eslintrc.sonar.json \"src/**/*.ts\" --fix",
|
||||
"e2e": "ng e2e"
|
||||
},
|
||||
"private": true,
|
||||
@@ -65,6 +67,7 @@
|
||||
"eslint-config-prettier": "^8.5.0",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-sonarjs": "^0.16.0",
|
||||
"jasmine-core": "~4.5.0",
|
||||
"jasmine-spec-reporter": "~7.0.0",
|
||||
"jest": "28.1.3",
|
||||
|
||||
@@ -13,8 +13,8 @@ import {
|
||||
} from '@angular/platform-browser-dynamic/testing';
|
||||
|
||||
// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
|
||||
declare var __karma__: any;
|
||||
declare var require: any;
|
||||
declare let __karma__: any;
|
||||
declare let require: any;
|
||||
|
||||
// Prevent Karma from running prematurely.
|
||||
__karma__.loaded = function () {};
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import {Subject} from 'rxjs';
|
||||
import {LogsPanelComponent} from './logs-panel.component';
|
||||
import {TriggerKey} from '../../model/triggerKey.model';
|
||||
import {jest} from '@jest/globals';
|
||||
|
||||
describe('LogsPanelComponent', () => {
|
||||
|
||||
it('should subscribe to the selected trigger logs topic', () => {
|
||||
const messages = new Subject<any>();
|
||||
const logsRxWebsocketService = {
|
||||
watch: jest.fn(() => messages.asObservable())
|
||||
};
|
||||
const component = new LogsPanelComponent(logsRxWebsocketService as any, null);
|
||||
|
||||
component.triggerKey = new TriggerKey('trigger-1', null);
|
||||
|
||||
expect(logsRxWebsocketService.watch).toHaveBeenCalledWith('/topic/logs/trigger-1');
|
||||
|
||||
const logRecord = {
|
||||
date: new Date(),
|
||||
type: 'INFO',
|
||||
message: 'job completed',
|
||||
threadName: 'worker-1'
|
||||
};
|
||||
messages.next({body: JSON.stringify(logRecord)});
|
||||
|
||||
expect(component.logs[0]).toEqual({
|
||||
time: logRecord.date.toISOString(),
|
||||
type: 'INFO',
|
||||
msg: 'job completed',
|
||||
threadName: 'worker-1'
|
||||
});
|
||||
});
|
||||
|
||||
it('should unsubscribe from the previous topic when the trigger changes', () => {
|
||||
const firstMessages = new Subject<any>();
|
||||
const secondMessages = new Subject<any>();
|
||||
const logsRxWebsocketService = {
|
||||
watch: jest.fn()
|
||||
.mockReturnValueOnce(firstMessages.asObservable())
|
||||
.mockReturnValueOnce(secondMessages.asObservable())
|
||||
};
|
||||
const component = new LogsPanelComponent(logsRxWebsocketService as any, null);
|
||||
|
||||
component.triggerKey = new TriggerKey('trigger-1', null);
|
||||
const firstSubscription = component.topicSubscription;
|
||||
jest.spyOn(firstSubscription, 'unsubscribe');
|
||||
|
||||
component.triggerKey = new TriggerKey('trigger-2', null);
|
||||
|
||||
expect(firstSubscription.unsubscribe).toHaveBeenCalled();
|
||||
expect(logsRxWebsocketService.watch).toHaveBeenCalledWith('/topic/logs/trigger-2');
|
||||
});
|
||||
|
||||
it('should ignore destroy when no topic was selected', () => {
|
||||
const logsRxWebsocketService = {
|
||||
watch: jest.fn()
|
||||
};
|
||||
const component = new LogsPanelComponent(logsRxWebsocketService as any, null);
|
||||
|
||||
expect(() => component.ngOnDestroy()).not.toThrow();
|
||||
});
|
||||
|
||||
});
|
||||
@@ -27,37 +27,42 @@ export class LogsPanelComponent implements OnInit, OnDestroy {
|
||||
) {
|
||||
}
|
||||
|
||||
@Input()
|
||||
set triggerKey(triggerKey: TriggerKey) {
|
||||
this.selectedTriggerKey = {...triggerKey} as TriggerKey;
|
||||
if (this.selectedTriggerKey && this.selectedTriggerKey.name) {
|
||||
this._subscribeToTheTopic(this.selectedTriggerKey);
|
||||
}
|
||||
}
|
||||
@Input()
|
||||
set triggerKey(triggerKey: TriggerKey) {
|
||||
if (!triggerKey || !triggerKey.name) {
|
||||
this._unsubscribeFromTopic();
|
||||
this.selectedTriggerKey = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectedTriggerKey = {...triggerKey} as TriggerKey;
|
||||
this._subscribeToTheTopic(this.selectedTriggerKey);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
private _subscribeToTheTopic = (triggerKey: TriggerKey) => {
|
||||
if (this.topicSubscription) {
|
||||
this.topicSubscription.unsubscribe();
|
||||
}
|
||||
this.topicSubscription = this.logsRxWebsocketService.watch(`/topic/logs/${triggerKey.name}`)
|
||||
.pipe(map(msg => JSON.parse(msg.body)))
|
||||
private _subscribeToTheTopic = (triggerKey: TriggerKey) => {
|
||||
this._unsubscribeFromTopic();
|
||||
this.topicSubscription = this.logsRxWebsocketService.watch(`/topic/logs/${triggerKey.name}`)
|
||||
.pipe(map((msg: any) => JSON.parse(msg.body)))
|
||||
.subscribe(this._showNewLog, (err) => {
|
||||
console.log(err);
|
||||
// TODO in case of 401
|
||||
// this.apiService.get('/quartz-manager/session/refresh');
|
||||
});
|
||||
};
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.topicSubscription) {
|
||||
this.topicSubscription.unsubscribe();
|
||||
}
|
||||
this.topicSubscription.unsubscribe();
|
||||
this.topicSubscription = null;
|
||||
}
|
||||
};
|
||||
|
||||
ngOnDestroy() {
|
||||
this._unsubscribeFromTopic();
|
||||
}
|
||||
|
||||
private _unsubscribeFromTopic() {
|
||||
if (this.topicSubscription) {
|
||||
this.topicSubscription.unsubscribe();
|
||||
this.topicSubscription = null;
|
||||
}
|
||||
}
|
||||
|
||||
_showNewLog = (logRecord) => {
|
||||
if (this.logs.length > this.MAX_LOGS) {
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import {Subject} from 'rxjs';
|
||||
import {ProgressPanelComponent} from './progress-panel.component';
|
||||
import {TriggerKey} from '../../model/triggerKey.model';
|
||||
import {jest} from '@jest/globals';
|
||||
|
||||
describe('ProgressPanelComponent', () => {
|
||||
|
||||
it('should subscribe to the selected trigger progress topic', () => {
|
||||
const messages = new Subject<any>();
|
||||
const progressRxWebsocketService = {
|
||||
watch: jest.fn(() => messages.asObservable())
|
||||
};
|
||||
const component = new ProgressPanelComponent(progressRxWebsocketService as any);
|
||||
|
||||
component.triggerKey = new TriggerKey('trigger-1', null);
|
||||
|
||||
expect(progressRxWebsocketService.watch).toHaveBeenCalledWith('/topic/progress/trigger-1');
|
||||
|
||||
messages.next({body: JSON.stringify({percentage: 75, timesTriggered: 3})});
|
||||
|
||||
expect(component.progress.percentage).toEqual(75);
|
||||
expect(component.percentageStr).toEqual('75%');
|
||||
});
|
||||
|
||||
it('should unsubscribe from the previous topic when the trigger changes', () => {
|
||||
const firstMessages = new Subject<any>();
|
||||
const secondMessages = new Subject<any>();
|
||||
const progressRxWebsocketService = {
|
||||
watch: jest.fn()
|
||||
.mockReturnValueOnce(firstMessages.asObservable())
|
||||
.mockReturnValueOnce(secondMessages.asObservable())
|
||||
};
|
||||
const component = new ProgressPanelComponent(progressRxWebsocketService as any);
|
||||
|
||||
component.triggerKey = new TriggerKey('trigger-1', null);
|
||||
const firstSubscription = component.topicSubscription;
|
||||
jest.spyOn(firstSubscription, 'unsubscribe');
|
||||
|
||||
component.triggerKey = new TriggerKey('trigger-2', null);
|
||||
|
||||
expect(firstSubscription.unsubscribe).toHaveBeenCalled();
|
||||
expect(progressRxWebsocketService.watch).toHaveBeenCalledWith('/topic/progress/trigger-2');
|
||||
});
|
||||
|
||||
it('should ignore destroy when no topic was selected', () => {
|
||||
const progressRxWebsocketService = {
|
||||
watch: jest.fn()
|
||||
};
|
||||
const component = new ProgressPanelComponent(progressRxWebsocketService as any);
|
||||
|
||||
expect(() => component.ngOnDestroy()).not.toThrow();
|
||||
});
|
||||
|
||||
});
|
||||
@@ -21,20 +21,22 @@ export class ProgressPanelComponent implements OnInit, OnDestroy {
|
||||
private progressRxWebsocketService: ProgressRxWebsocketService
|
||||
) { }
|
||||
|
||||
@Input()
|
||||
set triggerKey(triggerKey: TriggerKey) {
|
||||
this.selectedTriggerKey = {...triggerKey} as TriggerKey;
|
||||
if (this.selectedTriggerKey && this.selectedTriggerKey.name) {
|
||||
this._subscribeToTheTopic(this.selectedTriggerKey);
|
||||
}
|
||||
}
|
||||
|
||||
private _subscribeToTheTopic = (triggerKey: TriggerKey) => {
|
||||
if (this.topicSubscription) {
|
||||
this.topicSubscription.unsubscribe();
|
||||
}
|
||||
this.topicSubscription = this.progressRxWebsocketService.watch(`/topic/progress/${triggerKey.name}`)
|
||||
.pipe(map(msg => JSON.parse(msg.body)))
|
||||
@Input()
|
||||
set triggerKey(triggerKey: TriggerKey) {
|
||||
if (!triggerKey || !triggerKey.name) {
|
||||
this._unsubscribeFromTopic();
|
||||
this.selectedTriggerKey = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.selectedTriggerKey = {...triggerKey} as TriggerKey;
|
||||
this._subscribeToTheTopic(this.selectedTriggerKey);
|
||||
}
|
||||
|
||||
private _subscribeToTheTopic = (triggerKey: TriggerKey) => {
|
||||
this._unsubscribeFromTopic();
|
||||
this.topicSubscription = this.progressRxWebsocketService.watch(`/topic/progress/${triggerKey.name}`)
|
||||
.pipe(map((msg: any) => JSON.parse(msg.body)))
|
||||
.subscribe(this.onNewProgressMsg, (err) => {
|
||||
console.log(err);
|
||||
// TODO in case of 401
|
||||
@@ -48,14 +50,17 @@ export class ProgressPanelComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.topicSubscription) {
|
||||
this.topicSubscription.unsubscribe();
|
||||
}
|
||||
this.topicSubscription.unsubscribe();
|
||||
this.topicSubscription = null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this._unsubscribeFromTopic();
|
||||
}
|
||||
|
||||
private _unsubscribeFromTopic() {
|
||||
if (this.topicSubscription) {
|
||||
this.topicSubscription.unsubscribe();
|
||||
this.topicSubscription = null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,6 +13,12 @@ import {MatDividerModule} from '@angular/material/divider';
|
||||
|
||||
describe('SchedulerControlComponent', () => {
|
||||
|
||||
const schedulerUrl = '/quartz-manager/scheduler';
|
||||
const schedulerButtonSelector = '#schedulerControllerBtn';
|
||||
const schedulerName = 'test-scheduler';
|
||||
const schedulerId = 'test-id';
|
||||
const stoppedStatus = 'STOPPED';
|
||||
|
||||
let component: SchedulerControlComponent;
|
||||
let fixture: ComponentFixture<SchedulerControlComponent>;
|
||||
|
||||
@@ -38,16 +44,16 @@ describe('SchedulerControlComponent', () => {
|
||||
|
||||
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', []);
|
||||
const getSchedulerReq = httpTestingController.expectOne(schedulerUrl);
|
||||
const mockScheduler = new Scheduler(schedulerName, schedulerId, stoppedStatus, []);
|
||||
getSchedulerReq.flush(mockScheduler);
|
||||
|
||||
expect(component.scheduler).toEqual(mockScheduler);
|
||||
expect(component.scheduler.status).toEqual('STOPPED');
|
||||
expect(component.scheduler.status).toEqual(stoppedStatus);
|
||||
fixture.detectChanges();
|
||||
|
||||
const schedulerControlComponentDe: DebugElement = fixture.debugElement;
|
||||
const schedulerBtnDe = schedulerControlComponentDe.query(By.css('#schedulerControllerBtn'));
|
||||
const schedulerBtnDe = schedulerControlComponentDe.query(By.css(schedulerButtonSelector));
|
||||
expect(schedulerBtnDe).toBeTruthy();
|
||||
|
||||
const playIconDe = schedulerBtnDe.query(By.css('.fa-play'));
|
||||
@@ -56,13 +62,13 @@ describe('SchedulerControlComponent', () => {
|
||||
|
||||
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', []);
|
||||
const getSchedulerReq = httpTestingController.expectOne(schedulerUrl);
|
||||
const mockScheduler = new Scheduler(schedulerName, schedulerId, stoppedStatus, []);
|
||||
getSchedulerReq.flush(mockScheduler);
|
||||
fixture.detectChanges();
|
||||
|
||||
const schedulerControlComponentDe: DebugElement = fixture.debugElement;
|
||||
let schedulerBtnDe = schedulerControlComponentDe.query(By.css('#schedulerControllerBtn'));
|
||||
let schedulerBtnDe = schedulerControlComponentDe.query(By.css(schedulerButtonSelector));
|
||||
expect(schedulerBtnDe).toBeTruthy();
|
||||
const playIconDe = schedulerBtnDe.query(By.css('.fa-play'));
|
||||
expect(playIconDe).toBeTruthy();
|
||||
@@ -72,7 +78,7 @@ describe('SchedulerControlComponent', () => {
|
||||
startSchedulerReq.flush(null);
|
||||
fixture.detectChanges();
|
||||
|
||||
schedulerBtnDe = schedulerControlComponentDe.query(By.css('#schedulerControllerBtn'));
|
||||
schedulerBtnDe = schedulerControlComponentDe.query(By.css(schedulerButtonSelector));
|
||||
const pauseIconDe = schedulerBtnDe.query(By.css('.fa-pause'));
|
||||
expect(pauseIconDe).toBeTruthy();
|
||||
|
||||
@@ -80,13 +86,13 @@ describe('SchedulerControlComponent', () => {
|
||||
|
||||
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', []);
|
||||
const getSchedulerReq = httpTestingController.expectOne(schedulerUrl);
|
||||
const mockScheduler = new Scheduler(schedulerName, schedulerId, 'RUNNING', []);
|
||||
getSchedulerReq.flush(mockScheduler);
|
||||
fixture.detectChanges();
|
||||
|
||||
const schedulerControlComponentDe: DebugElement = fixture.debugElement;
|
||||
let schedulerBtnDe = schedulerControlComponentDe.query(By.css('#schedulerControllerBtn'));
|
||||
let schedulerBtnDe = schedulerControlComponentDe.query(By.css(schedulerButtonSelector));
|
||||
expect(schedulerBtnDe).toBeTruthy();
|
||||
const pauseIconDe = schedulerBtnDe.query(By.css('.fa-pause'));
|
||||
expect(pauseIconDe).toBeTruthy();
|
||||
@@ -96,7 +102,7 @@ describe('SchedulerControlComponent', () => {
|
||||
startSchedulerReq.flush(null);
|
||||
fixture.detectChanges();
|
||||
|
||||
schedulerBtnDe = schedulerControlComponentDe.query(By.css('#schedulerControllerBtn'));
|
||||
schedulerBtnDe = schedulerControlComponentDe.query(By.css(schedulerButtonSelector));
|
||||
const playIconDe = schedulerBtnDe.query(By.css('.fa-play'));
|
||||
expect(playIconDe).toBeTruthy();
|
||||
|
||||
|
||||
@@ -23,6 +23,11 @@ import {MisfireInstruction} from '../../model/misfire-instruction.model';
|
||||
|
||||
describe('SimpleTriggerConfig', () => {
|
||||
|
||||
const submitButtonSelector = 'form button[color="primary"]';
|
||||
const repeatIntervalSelector = '#repeatInterval';
|
||||
const testTriggerName = 'test-trigger';
|
||||
const testJobName = 'TestJob';
|
||||
|
||||
let component: SimpleTriggerConfigComponent;
|
||||
let fixture: ComponentFixture<SimpleTriggerConfigComponent>;
|
||||
|
||||
@@ -91,16 +96,16 @@ describe('SimpleTriggerConfig', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const getJobsReq = httpTestingController.expectOne(`${CONTEXT_PATH}/jobs`);
|
||||
getJobsReq.flush(['TestJob']);
|
||||
getJobsReq.flush([testJobName]);
|
||||
|
||||
const componentDe: DebugElement = fixture.debugElement;
|
||||
|
||||
const submitButton = componentDe.query(By.css('form button[color="primary"]'));
|
||||
const submitButton = componentDe.query(By.css(submitButtonSelector));
|
||||
expect(submitButton.nativeElement.textContent.trim()).toEqual('Submit');
|
||||
expect(submitButton.nativeElement.getAttribute('disabled')).toEqual('');
|
||||
|
||||
setInputValue(componentDe, '#triggerName', 'test-trigger');
|
||||
expect(component.simpleTriggerReactiveForm.controls.triggerName.value).toEqual('test-trigger');
|
||||
setInputValue(componentDe, '#triggerName', testTriggerName);
|
||||
expect(component.simpleTriggerReactiveForm.controls.triggerName.value).toEqual(testTriggerName);
|
||||
expect(submitButton.nativeElement.getAttribute('disabled')).toEqual('');
|
||||
setMatSelectValueByIndex(componentDe, '#misfireInstruction', 0);
|
||||
expect(component.simpleTriggerReactiveForm.controls.misfireInstruction.value).toEqual('MISFIRE_INSTRUCTION_FIRE_NOW');
|
||||
@@ -111,7 +116,7 @@ describe('SimpleTriggerConfig', () => {
|
||||
setInputValue(componentDe, '#repeatCount', '1000');
|
||||
expect(submitButton.nativeElement.getAttribute('disabled')).toEqual('');
|
||||
|
||||
setInputValue(componentDe, '#repeatInterval', '2000');
|
||||
setInputValue(componentDe, repeatIntervalSelector, '2000');
|
||||
expect(submitButton.nativeElement.getAttribute('disabled')).toEqual(null);
|
||||
}
|
||||
|
||||
@@ -122,18 +127,18 @@ describe('SimpleTriggerConfig', () => {
|
||||
it('should emit an event when a new trigger is submitted', () => {
|
||||
const componentDe: DebugElement = fixture.debugElement;
|
||||
const mockTrigger = new Trigger();
|
||||
mockTrigger.triggerKeyDTO = new TriggerKey('test-trigger', null);
|
||||
mockTrigger.jobDetailDTO = <JobDetail>{jobClassName: 'TestJob', description: null};
|
||||
mockTrigger.triggerKeyDTO = new TriggerKey(testTriggerName, null);
|
||||
mockTrigger.jobDetailDTO = <JobDetail>{jobClassName: testJobName, description: null};
|
||||
mockTrigger.misfireInstruction = MisfireInstruction.MISFIRE_INSTRUCTION_FIRE_NOW;
|
||||
|
||||
openFormAndFillAllMandatoryFields();
|
||||
|
||||
setInputValue(componentDe, '#repeatInterval', '2000');
|
||||
setInputValue(componentDe, repeatIntervalSelector, '2000');
|
||||
expect(component.simpleTriggerReactiveForm.controls.triggerRecurrence.value.repeatInterval).toEqual(2000);
|
||||
setInputValue(componentDe, '#repeatCount', '100');
|
||||
expect(component.simpleTriggerReactiveForm.controls.triggerRecurrence.value.repeatCount).toEqual(100);
|
||||
|
||||
const submitButton = componentDe.query(By.css('form button[color="primary"]'));
|
||||
const submitButton = componentDe.query(By.css(submitButtonSelector));
|
||||
expect(submitButton.nativeElement.textContent.trim()).toEqual('Submit');
|
||||
|
||||
let actualNewTrigger;
|
||||
@@ -141,28 +146,28 @@ describe('SimpleTriggerConfig', () => {
|
||||
|
||||
submitButton.nativeElement.click();
|
||||
|
||||
const postSimpleTriggerReq = httpTestingController.expectOne(`${CONTEXT_PATH}/simple-triggers/test-trigger`);
|
||||
const postSimpleTriggerReq = httpTestingController.expectOne(`${CONTEXT_PATH}/simple-triggers/${testTriggerName}`);
|
||||
postSimpleTriggerReq.flush(mockTrigger);
|
||||
|
||||
expect(actualNewTrigger).toEqual(mockTrigger);
|
||||
});
|
||||
|
||||
it('should not emit an event when an existing trigger is edited', () => {
|
||||
const mockTriggerKey = new TriggerKey('test-trigger', null);
|
||||
const mockTriggerKey = new TriggerKey(testTriggerName, null);
|
||||
component.triggerKey = mockTriggerKey;
|
||||
fixture.detectChanges();
|
||||
|
||||
const mockTrigger = new SimpleTrigger();
|
||||
mockTrigger.triggerKeyDTO = new TriggerKey('test-trigger', null);
|
||||
mockTrigger.jobDetailDTO = <JobDetail>{jobClassName: 'TestJob', description: null};
|
||||
mockTrigger.triggerKeyDTO = new TriggerKey(testTriggerName, null);
|
||||
mockTrigger.jobDetailDTO = <JobDetail>{jobClassName: testJobName, description: null};
|
||||
mockTrigger.mayFireAgain = true;
|
||||
mockTrigger.misfireInstruction = MisfireInstruction.MISFIRE_INSTRUCTION_FIRE_NOW;
|
||||
const getSimpleTriggerReq = httpTestingController.expectOne(`${CONTEXT_PATH}/simple-triggers/test-trigger`);
|
||||
const getSimpleTriggerReq = httpTestingController.expectOne(`${CONTEXT_PATH}/simple-triggers/${testTriggerName}`);
|
||||
getSimpleTriggerReq.flush(mockTrigger);
|
||||
|
||||
component.simpleTriggerReactiveForm.setValue({
|
||||
triggerName: 'test-trigger',
|
||||
jobClass: 'TestJob',
|
||||
triggerName: testTriggerName,
|
||||
jobClass: testJobName,
|
||||
triggerRecurrence: {
|
||||
repeatInterval: 2000,
|
||||
repeatCount: 100,
|
||||
@@ -178,10 +183,10 @@ describe('SimpleTriggerConfig', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const componentDe: DebugElement = fixture.debugElement;
|
||||
setInputValue(componentDe, '#repeatInterval', '4000');
|
||||
setInputValue(componentDe, repeatIntervalSelector, '4000');
|
||||
expect(component.simpleTriggerReactiveForm.controls.triggerRecurrence.value.repeatInterval).toEqual(4000);
|
||||
|
||||
const submitButton = componentDe.query(By.css('form button[color="primary"]'));
|
||||
const submitButton = componentDe.query(By.css(submitButtonSelector));
|
||||
expect(submitButton.nativeElement.textContent.trim()).toEqual('Submit');
|
||||
|
||||
let actualNewTrigger;
|
||||
@@ -189,7 +194,7 @@ describe('SimpleTriggerConfig', () => {
|
||||
|
||||
submitButton.nativeElement.click();
|
||||
|
||||
const putSimpleTriggerReq = httpTestingController.expectOne(`${CONTEXT_PATH}/simple-triggers/test-trigger`);
|
||||
const putSimpleTriggerReq = httpTestingController.expectOne(`${CONTEXT_PATH}/simple-triggers/${testTriggerName}`);
|
||||
putSimpleTriggerReq.flush(mockTrigger);
|
||||
|
||||
expect(actualNewTrigger).toBeUndefined();
|
||||
@@ -220,7 +225,7 @@ describe('SimpleTriggerConfig', () => {
|
||||
fixture.detectChanges();
|
||||
|
||||
const componentDe: DebugElement = fixture.debugElement;
|
||||
const submitButton = componentDe.query(By.css('form button[color="primary"]'));
|
||||
const submitButton = componentDe.query(By.css(submitButtonSelector));
|
||||
expect(submitButton.nativeElement.textContent.trim()).toEqual('Submit');
|
||||
|
||||
expect(component.simpleTriggerReactiveForm.value.triggerName).toBeNull();
|
||||
@@ -228,6 +233,25 @@ describe('SimpleTriggerConfig', () => {
|
||||
});
|
||||
|
||||
it('should reset the form when a new trigger is selected', () => {
|
||||
const mockTriggerKey = new TriggerKey(testTriggerName, null);
|
||||
component.triggerKey = mockTriggerKey;
|
||||
|
||||
const mockTrigger = new SimpleTrigger();
|
||||
mockTrigger.triggerKeyDTO = mockTriggerKey;
|
||||
mockTrigger.jobDetailDTO = <JobDetail>{jobClassName: testJobName, description: null};
|
||||
mockTrigger.mayFireAgain = true;
|
||||
mockTrigger.misfireInstruction = MisfireInstruction.MISFIRE_INSTRUCTION_FIRE_NOW;
|
||||
|
||||
const getSimpleTriggerReq = httpTestingController.expectOne(`${CONTEXT_PATH}/simple-triggers/${testTriggerName}`);
|
||||
getSimpleTriggerReq.flush(mockTrigger);
|
||||
|
||||
expect(component.simpleTriggerReactiveForm.value.triggerName).toEqual(testTriggerName);
|
||||
|
||||
component.triggerKey = null;
|
||||
|
||||
expect(component.simpleTriggerReactiveForm.value.triggerName).toBeNull();
|
||||
expect(component.simpleTriggerReactiveForm.value.jobClass).toBeNull();
|
||||
expect(component.shouldShowTheTriggerCardContent()).toBeTruthy();
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -47,6 +47,9 @@ export class SimpleTriggerConfigComponent implements OnInit {
|
||||
@Output()
|
||||
onNewTrigger = new EventEmitter<SimpleTrigger>();
|
||||
|
||||
@Output()
|
||||
triggerFormOpenChange = new EventEmitter<boolean>();
|
||||
|
||||
constructor(
|
||||
private formBuilder: UntypedFormBuilder,
|
||||
private schedulerService: SchedulerService,
|
||||
@@ -63,30 +66,31 @@ export class SimpleTriggerConfigComponent implements OnInit {
|
||||
}
|
||||
|
||||
openTriggerForm() {
|
||||
// this.selectedTriggerKey = null;
|
||||
// this.trigger = null;
|
||||
// this.simpleTriggerReactiveForm.setValue(new SimpleTriggerReactiveForm());
|
||||
this.enabledTriggerForm = true;
|
||||
this.triggerFormOpenChange.emit(this.enabledTriggerForm);
|
||||
}
|
||||
|
||||
private closeTriggerForm() {
|
||||
this.enabledTriggerForm = false;
|
||||
this.triggerFormOpenChange.emit(this.enabledTriggerForm);
|
||||
}
|
||||
|
||||
@Input()
|
||||
set triggerKey(triggerKey: TriggerKey) {
|
||||
if (!triggerKey) {
|
||||
this.selectedTriggerKey = null;
|
||||
this.trigger = null;
|
||||
this.simpleTriggerReactiveForm.reset(new SimpleTriggerReactiveForm());
|
||||
this.openNewTriggerForm();
|
||||
} else if (!this.selectedTriggerKey || this.selectedTriggerKey.name !== triggerKey.name) {
|
||||
this._resetTheTrigger();
|
||||
this.selectedTriggerKey = {...triggerKey} as TriggerKey;
|
||||
this.fetchSelectedTrigger();
|
||||
this.closeTriggerForm();
|
||||
}
|
||||
this.openTriggerForm();
|
||||
}
|
||||
|
||||
openNewTriggerForm() {
|
||||
this._resetTheTrigger();
|
||||
this.openTriggerForm();
|
||||
}
|
||||
|
||||
private _resetTheTrigger() {
|
||||
this.trigger = null;
|
||||
@@ -111,7 +115,11 @@ export class SimpleTriggerConfigComponent implements OnInit {
|
||||
existsATriggerInProgress = (): boolean => this.trigger && this.triggerInProgress;
|
||||
|
||||
onResetReactiveForm = () => {
|
||||
this.simpleTriggerReactiveForm.setValue(this._fromTriggerToReactiveForm(this.trigger));
|
||||
if (this.trigger) {
|
||||
this.simpleTriggerReactiveForm.setValue(this._fromTriggerToReactiveForm(this.trigger));
|
||||
} else {
|
||||
this.simpleTriggerReactiveForm.reset(new SimpleTriggerReactiveForm());
|
||||
}
|
||||
this.closeTriggerForm();
|
||||
};
|
||||
|
||||
@@ -135,8 +143,13 @@ export class SimpleTriggerConfigComponent implements OnInit {
|
||||
|
||||
this.closeTriggerForm();
|
||||
}, error => {
|
||||
this.simpleTriggerReactiveForm.setValue(this._fromTriggerToReactiveForm(this.trigger));
|
||||
}, () => {this.triggerLoading = true});
|
||||
if (this.trigger) {
|
||||
this.simpleTriggerReactiveForm.setValue(this._fromTriggerToReactiveForm(this.trigger));
|
||||
}
|
||||
this.triggerLoading = false;
|
||||
}, () => {
|
||||
this.triggerLoading = false
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
|
||||
*/
|
||||
|
||||
/***************************************************************************************************
|
||||
/** *************************************************************************************************
|
||||
* BROWSER POLYFILLS
|
||||
*/
|
||||
|
||||
@@ -51,14 +51,14 @@ import 'core-js/es7/reflect';
|
||||
|
||||
|
||||
|
||||
/***************************************************************************************************
|
||||
/** *************************************************************************************************
|
||||
* Zone JS is required by Angular itself.
|
||||
*/
|
||||
import 'zone.js/dist/zone'; // Included with Angular CLI.
|
||||
|
||||
|
||||
|
||||
/***************************************************************************************************
|
||||
/** *************************************************************************************************
|
||||
* APPLICATION IMPORTS
|
||||
*/
|
||||
|
||||
@@ -68,7 +68,7 @@ import 'zone.js/dist/zone'; // Included with Angular CLI.
|
||||
*/
|
||||
// import 'intl'; // Run `npm install --save intl`.
|
||||
|
||||
/***************************************************************************************************
|
||||
/** *************************************************************************************************
|
||||
* MATERIAL 2
|
||||
*/
|
||||
import 'hammerjs/hammer';
|
||||
|
||||
@@ -2,15 +2,22 @@ import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { LogsRxWebsocketService } from './logs.rx-websocket.service';
|
||||
import {ApiService} from './api.service';
|
||||
import {HttpClientTestingModule} from '@angular/common/http/testing';
|
||||
import {RxStomp} from '@stomp/rx-stomp';
|
||||
import {jest} from '@jest/globals';
|
||||
|
||||
describe('LogsRxWebsocketService', () => {
|
||||
let service: LogsRxWebsocketService;
|
||||
let configureSpy;
|
||||
let activateSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
configureSpy = jest.spyOn(RxStomp.prototype, 'configure');
|
||||
activateSpy = jest.spyOn(RxStomp.prototype, 'activate').mockImplementation(() => undefined);
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
imports: [HttpClientTestingModule],
|
||||
providers: [ApiService]
|
||||
providers: [
|
||||
{provide: ApiService, useValue: {getToken: () => 'test-token'}}
|
||||
]
|
||||
});
|
||||
service = TestBed.inject(LogsRxWebsocketService);
|
||||
});
|
||||
@@ -18,4 +25,15 @@ describe('LogsRxWebsocketService', () => {
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should configure rx-stomp with the logs websocket endpoint', () => {
|
||||
expect(configureSpy).toHaveBeenCalled();
|
||||
expect(activateSpy).toHaveBeenCalled();
|
||||
|
||||
const config = configureSpy.mock.calls[configureSpy.mock.calls.length - 1][0];
|
||||
expect(config.heartbeatIncoming).toEqual(0);
|
||||
expect(config.heartbeatOutgoing).toEqual(20000);
|
||||
expect(config.reconnectDelay).toEqual(200);
|
||||
expect(config.webSocketFactory.toString()).toContain('/logs?access_token=');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import {RxStomp} from '@stomp/rx-stomp';
|
||||
import {jest} from '@jest/globals';
|
||||
|
||||
import { ProgressRxWebsocketService } from './progress.rx-websocket.service';
|
||||
import {ApiService} from './api.service';
|
||||
|
||||
describe('ProgressRxWebsocketService', () => {
|
||||
let service: ProgressRxWebsocketService;
|
||||
let configureSpy;
|
||||
let activateSpy;
|
||||
|
||||
beforeEach(() => {
|
||||
configureSpy = jest.spyOn(RxStomp.prototype, 'configure');
|
||||
activateSpy = jest.spyOn(RxStomp.prototype, 'activate').mockImplementation(() => undefined);
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
{provide: ApiService, useValue: {getToken: () => 'test-token'}}
|
||||
]
|
||||
});
|
||||
service = TestBed.inject(ProgressRxWebsocketService);
|
||||
});
|
||||
|
||||
it('should be created', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should configure rx-stomp with the progress websocket endpoint', () => {
|
||||
expect(configureSpy).toHaveBeenCalled();
|
||||
expect(activateSpy).toHaveBeenCalled();
|
||||
|
||||
const config = configureSpy.mock.calls[configureSpy.mock.calls.length - 1][0];
|
||||
expect(config.heartbeatIncoming).toEqual(0);
|
||||
expect(config.heartbeatOutgoing).toEqual(20000);
|
||||
expect(config.reconnectDelay).toEqual(200);
|
||||
expect(config.webSocketFactory.toString()).toContain('/progress?access_token=');
|
||||
});
|
||||
});
|
||||
@@ -35,7 +35,7 @@ export class UserService {
|
||||
this.currentUser = user;
|
||||
this.router.initialNavigation();
|
||||
}, err => {
|
||||
console.log(`error retrieving current user due to ` + JSON.stringify(err));
|
||||
console.log('error retrieving current user due to ' + JSON.stringify(err));
|
||||
const httpErrorResponse = err as HttpErrorResponse;
|
||||
if (httpErrorResponse.status === 404) {
|
||||
this.isAnAnonymousUser = true;
|
||||
|
||||
@@ -19,10 +19,11 @@
|
||||
<div fxFlex="1 1 350px">
|
||||
<div fxLayout="row" fxFill>
|
||||
<div fxLayout="column" fxFill>
|
||||
<qrzmng-simple-trigger-config fxFill
|
||||
[triggerKey]="selectedTriggerKey"
|
||||
(onNewTrigger)="onNewTriggerCreated($event)">
|
||||
</qrzmng-simple-trigger-config>
|
||||
<qrzmng-simple-trigger-config fxFill
|
||||
[triggerKey]="selectedTriggerKey"
|
||||
(triggerFormOpenChange)="setNewTriggerFormOpened($event)"
|
||||
(onNewTrigger)="onNewTriggerCreated($event)">
|
||||
</qrzmng-simple-trigger-config>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import {Component, OnInit, ViewChild} from '@angular/core';
|
||||
import {
|
||||
ConfigService,
|
||||
UserService
|
||||
} from '../../services';
|
||||
import {SimpleTrigger} from '../../model/simple-trigger.model';
|
||||
import {TriggerKey} from '../../model/triggerKey.model';
|
||||
import {SimpleTriggerConfigComponent} from '../../components/simple-trigger-config';
|
||||
@@ -33,15 +29,24 @@ export class ManagerComponent implements OnInit {
|
||||
|
||||
onNewTriggerRequested() {
|
||||
this.selectedTriggerKey = null;
|
||||
// this.triggerConfigComponent.openTriggerForm();
|
||||
this.newTriggerFormOpened = true;
|
||||
if (this.triggerConfigComponent) {
|
||||
this.triggerConfigComponent.openNewTriggerForm();
|
||||
}
|
||||
}
|
||||
|
||||
onNewTriggerCreated(newTrigger: SimpleTrigger) {
|
||||
this.triggerListComponent.onNewTrigger(newTrigger);
|
||||
this.newTriggerFormOpened = false;
|
||||
}
|
||||
|
||||
setSelectedTrigger(triggerKey: TriggerKey) {
|
||||
this.selectedTriggerKey = triggerKey;
|
||||
this.newTriggerFormOpened = false;
|
||||
}
|
||||
|
||||
setNewTriggerFormOpened(opened: boolean) {
|
||||
this.newTriggerFormOpened = opened;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
|
||||
*/
|
||||
|
||||
/***************************************************************************************************
|
||||
/** *************************************************************************************************
|
||||
* BROWSER POLYFILLS
|
||||
*/
|
||||
|
||||
@@ -50,7 +50,7 @@ import 'core-js/es6/reflect';
|
||||
|
||||
|
||||
|
||||
/***************************************************************************************************
|
||||
/** *************************************************************************************************
|
||||
* Zone JS is required by Angular itself.
|
||||
*/
|
||||
import 'zone.js/dist/zone'; // Included with Angular CLI.
|
||||
@@ -59,7 +59,7 @@ import 'zone.js/dist/zone'; // Included with Angular CLI.
|
||||
|
||||
|
||||
|
||||
/***************************************************************************************************
|
||||
/** *************************************************************************************************
|
||||
* APPLICATION IMPORTS
|
||||
*/
|
||||
|
||||
@@ -69,7 +69,7 @@ import 'zone.js/dist/zone'; // Included with Angular CLI.
|
||||
*/
|
||||
// import 'intl'; // Run `npm install --save intl`.
|
||||
|
||||
/***************************************************************************************************
|
||||
/** *************************************************************************************************
|
||||
* MATERIAL 2
|
||||
*/
|
||||
import 'hammerjs/hammer';
|
||||
|
||||
2
quartz-manager-frontend/src/typings.d.ts
vendored
2
quartz-manager-frontend/src/typings.d.ts
vendored
@@ -1,5 +1,5 @@
|
||||
/* SystemJS module definition */
|
||||
declare var module: NodeModule;
|
||||
declare let module: NodeModule;
|
||||
interface NodeModule {
|
||||
id: string;
|
||||
}
|
||||
|
||||
1
quartz-manager-parent/.gitignore
vendored
1
quartz-manager-parent/.gitignore
vendored
@@ -4,3 +4,4 @@
|
||||
.classpath
|
||||
.project
|
||||
.idea
|
||||
/**/*.iml
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
<nexus-staging-maven-plugin.version>1.6.7</nexus-staging-maven-plugin.version>
|
||||
<maven-release-plugin.version>2.5.3</maven-release-plugin.version>
|
||||
<maven-gpg-plugin.version>3.0.1</maven-gpg-plugin.version>
|
||||
<sonar-maven-plugin.version>3.11.0.3922</sonar-maven-plugin.version>
|
||||
<sonar.organization>fabioformosa</sonar.organization>
|
||||
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
|
||||
<sonar.exclusions>
|
||||
@@ -133,6 +134,11 @@
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<version>${maven-failsafe-plugin.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.sonarsource.scanner.maven</groupId>
|
||||
<artifactId>sonar-maven-plugin</artifactId>
|
||||
<version>${sonar-maven-plugin.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
|
||||
@@ -18,13 +18,17 @@ import java.util.Date;
|
||||
@SpringBootTest
|
||||
class SimpleTriggerServiceIntegrationTest {
|
||||
|
||||
private static final String SAMPLE_JOB_CLASS = "it.fabioformosa.quartzmanager.api.jobs.SampleJob";
|
||||
private static final String SAMPLE_EXTRA_JOB_CLASS = "it.fabioformosa.samplepackage.SampleExtraJob";
|
||||
private static final String FIRST_TRIGGER_SUFFIX = "A";
|
||||
private static final String SECOND_TRIGGER_SUFFIX = "B";
|
||||
|
||||
@Autowired
|
||||
private SimpleTriggerService simpleTriggerService;
|
||||
|
||||
@Test
|
||||
void givenASimpleTriggerCommandDTOWithAllData_whenANewSimpleTriggerIsScheduled_thenShouldGetATriggertDTO() throws SchedulerException, ClassNotFoundException {
|
||||
String simpleTriggerTestName = "simpleTriggerWithAllData";
|
||||
String jobClass = "it.fabioformosa.quartzmanager.api.jobs.SampleJob";
|
||||
Date startDate = new Date();
|
||||
Date endDate = DateUtils.addHoursToNow(5);
|
||||
int repeatCount = 3;
|
||||
@@ -41,7 +45,7 @@ class SimpleTriggerServiceIntegrationTest {
|
||||
.repeatCount(repeatCount)
|
||||
.repeatInterval(repeatInterval)
|
||||
.misfireInstruction(misfireInstructionFireNow)
|
||||
.jobClass(jobClass)
|
||||
.jobClass(SAMPLE_JOB_CLASS)
|
||||
.build())
|
||||
.build();
|
||||
SimpleTriggerDTO simpleTriggerDTO = simpleTriggerService.scheduleSimpleTrigger(simpleTriggerCommand);
|
||||
@@ -61,12 +65,11 @@ class SimpleTriggerServiceIntegrationTest {
|
||||
@Test
|
||||
void givenASimpleTriggerCommandDTOWithMissingOptionalField_whenANewSimpleTriggerIsScheduled_thenShouldGetATriggertDTO() throws SchedulerException, ClassNotFoundException {
|
||||
String simpleTriggerTestName = "simpleTriggerWithoutOptionalData";
|
||||
String jobClass = "it.fabioformosa.quartzmanager.api.jobs.SampleJob";
|
||||
|
||||
SimpleTriggerCommandDTO simpleTriggerCommand = SimpleTriggerCommandDTO.builder()
|
||||
.triggerName(simpleTriggerTestName)
|
||||
.simpleTriggerInputDTO(SimpleTriggerInputDTO.builder()
|
||||
.jobClass(jobClass)
|
||||
.jobClass(SAMPLE_JOB_CLASS)
|
||||
.build())
|
||||
.build();
|
||||
SimpleTriggerDTO simpleTriggerDTO = simpleTriggerService.scheduleSimpleTrigger(simpleTriggerCommand);
|
||||
@@ -81,4 +84,49 @@ class SimpleTriggerServiceIntegrationTest {
|
||||
Assertions.assertThat(simpleTriggerDTO.getRepeatInterval()).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenTwoSimpleTriggerCommandDTOsForTheSameJob_whenScheduled_thenShouldCreateTwoTriggers() throws SchedulerException, ClassNotFoundException {
|
||||
String triggerNamePrefix = "sameJobTrigger" + System.nanoTime();
|
||||
|
||||
SimpleTriggerDTO firstTrigger = simpleTriggerService.scheduleSimpleTrigger(
|
||||
buildSimpleTriggerCommand(triggerNamePrefix + FIRST_TRIGGER_SUFFIX, SAMPLE_JOB_CLASS)
|
||||
);
|
||||
SimpleTriggerDTO secondTrigger = simpleTriggerService.scheduleSimpleTrigger(
|
||||
buildSimpleTriggerCommand(triggerNamePrefix + SECOND_TRIGGER_SUFFIX, SAMPLE_JOB_CLASS)
|
||||
);
|
||||
|
||||
Assertions.assertThat(firstTrigger.getTriggerKeyDTO().getName()).isEqualTo(triggerNamePrefix + FIRST_TRIGGER_SUFFIX);
|
||||
Assertions.assertThat(secondTrigger.getTriggerKeyDTO().getName()).isEqualTo(triggerNamePrefix + SECOND_TRIGGER_SUFFIX);
|
||||
Assertions.assertThat(firstTrigger.getJobDetailDTO().getJobClassName()).isEqualTo(SAMPLE_JOB_CLASS);
|
||||
Assertions.assertThat(secondTrigger.getJobDetailDTO().getJobClassName()).isEqualTo(SAMPLE_JOB_CLASS);
|
||||
Assertions.assertThat(firstTrigger.getJobKeyDTO().getName()).isNotEqualTo(secondTrigger.getJobKeyDTO().getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenTwoSimpleTriggerCommandDTOsForDifferentJobs_whenScheduled_thenShouldCreateTwoTriggers() throws SchedulerException, ClassNotFoundException {
|
||||
String triggerNamePrefix = "differentJobTrigger" + System.nanoTime();
|
||||
|
||||
SimpleTriggerDTO firstTrigger = simpleTriggerService.scheduleSimpleTrigger(
|
||||
buildSimpleTriggerCommand(triggerNamePrefix + FIRST_TRIGGER_SUFFIX, SAMPLE_JOB_CLASS)
|
||||
);
|
||||
SimpleTriggerDTO secondTrigger = simpleTriggerService.scheduleSimpleTrigger(
|
||||
buildSimpleTriggerCommand(triggerNamePrefix + SECOND_TRIGGER_SUFFIX, SAMPLE_EXTRA_JOB_CLASS)
|
||||
);
|
||||
|
||||
Assertions.assertThat(firstTrigger.getTriggerKeyDTO().getName()).isEqualTo(triggerNamePrefix + FIRST_TRIGGER_SUFFIX);
|
||||
Assertions.assertThat(secondTrigger.getTriggerKeyDTO().getName()).isEqualTo(triggerNamePrefix + SECOND_TRIGGER_SUFFIX);
|
||||
Assertions.assertThat(firstTrigger.getJobDetailDTO().getJobClassName()).isEqualTo(SAMPLE_JOB_CLASS);
|
||||
Assertions.assertThat(secondTrigger.getJobDetailDTO().getJobClassName()).isEqualTo(SAMPLE_EXTRA_JOB_CLASS);
|
||||
}
|
||||
|
||||
private static SimpleTriggerCommandDTO buildSimpleTriggerCommand(String triggerName, String jobClass) {
|
||||
return SimpleTriggerCommandDTO.builder()
|
||||
.triggerName(triggerName)
|
||||
.simpleTriggerInputDTO(SimpleTriggerInputDTO.builder()
|
||||
.jobClass(jobClass)
|
||||
.startDate(DateUtils.addHoursToNow(1))
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package it.fabioformosa.quartzmanager.api.websockets;
|
||||
|
||||
import it.fabioformosa.quartzmanager.api.dto.TriggerFiredBundleDTO;
|
||||
import it.fabioformosa.quartzmanager.api.jobs.entities.LogRecord;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.Mockito;
|
||||
import org.springframework.messaging.simp.SimpMessageSendingOperations;
|
||||
|
||||
import static org.mockito.MockitoAnnotations.openMocks;
|
||||
|
||||
class WebSocketNotifierTest {
|
||||
|
||||
@InjectMocks
|
||||
private WebSocketLogsNotifier webSocketLogsNotifier;
|
||||
|
||||
@InjectMocks
|
||||
private WebSocketProgressNotifier webSocketProgressNotifier;
|
||||
|
||||
@Mock
|
||||
private SimpMessageSendingOperations messagingTemplate;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
openMocks(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenATriggerName_whenALogIsSent_thenShouldSendItToTheTriggerLogsTopic() {
|
||||
LogRecord logRecord = new LogRecord(LogRecord.LogType.INFO, "Hello!");
|
||||
|
||||
webSocketLogsNotifier.send("trigger-1", logRecord);
|
||||
|
||||
Mockito.verify(messagingTemplate).convertAndSend("/topic/logs/trigger-1", logRecord);
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenATriggerName_whenProgressIsSent_thenShouldSendItToTheTriggerProgressTopic() {
|
||||
TriggerFiredBundleDTO triggerFiredBundleDTO = new TriggerFiredBundleDTO();
|
||||
|
||||
webSocketProgressNotifier.send("trigger-1", triggerFiredBundleDTO);
|
||||
|
||||
Mockito.verify(messagingTemplate).convertAndSend("/topic/progress/trigger-1", triggerFiredBundleDTO);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user