Compare commits

...

78 Commits

Author SHA1 Message Date
dependabot[bot]
113b18a93d Bump ua-parser-js from 0.7.32 to 0.7.33 in /quartz-manager-frontend
Bumps [ua-parser-js](https://github.com/faisalman/ua-parser-js) from 0.7.32 to 0.7.33.
- [Release notes](https://github.com/faisalman/ua-parser-js/releases)
- [Changelog](https://github.com/faisalman/ua-parser-js/blob/master/changelog.md)
- [Commits](https://github.com/faisalman/ua-parser-js/compare/0.7.32...0.7.33)

---
updated-dependencies:
- dependency-name: ua-parser-js
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-27 18:18:56 +00:00
Fabio Formosa
82a60eb651 Update README.md 2022-12-10 16:59:31 +01:00
Fabio Formosa
7c910196e1 upgraded README and CHANGELOG 2022-12-10 16:54:50 +01:00
Fabio Formosa
554e7e5e25 #80 bump version to 4.0.8 2022-12-10 16:39:26 +01:00
Fabio Formosa
8b70319436 #80 upgraded node and npm into the quartz-manager-starter-ui 2022-12-10 16:38:34 +01:00
Fabio Formosa
053f196b6b Merge pull request #100 from fabioformosa/develop
v4.0.7
2022-12-10 16:29:16 +01:00
Fabio Formosa
86f8cc8347 bump version to 4.0.7 2022-12-10 16:25:17 +01:00
Fabio Formosa
e91d02ba9f #80 added moment to the allowed commonJS module 2022-12-10 16:19:39 +01:00
Fabio Formosa
109d2868d9 #80 upgraded to the new linter and added prettier 2022-12-10 16:15:00 +01:00
Fabio Formosa
4673e41fc5 #80 removed unnecessary asterisks 2022-12-10 14:53:35 +01:00
Fabio Formosa
a2122351d6 #80 removed extractCss 2022-12-10 13:31:45 +01:00
Fabio Formosa
375aaf71d3 #80 fixed the build command as angular v14 2022-12-10 13:25:14 +01:00
Fabio Formosa
b752af948d #80 downgraded jest to v28 2022-12-10 13:21:18 +01:00
Fabio Formosa
52ed016073 #80 fixed tests 2022-12-10 12:20:57 +01:00
Fabio Formosa
632288726b #80 upgraded some deps 2022-12-10 11:55:00 +01:00
Fabio Formosa
1dc9bee987 #80 upgraded to angular v14 2022-12-10 11:27:14 +01:00
Fabio Formosa
387c440e06 #80 step into angular v14 2022-12-10 11:20:01 +01:00
Fabio Formosa
136afa0fca #80 step into angular v14 2022-12-10 11:17:39 +01:00
Fabio Formosa
8ba3daa2f0 #80 upgraded angular to v13 2022-12-08 00:15:48 +01:00
Fabio Formosa
b3fe337203 #80 upgraded angular to v13 2022-12-08 00:15:39 +01:00
Fabio Formosa
da7375ea0d #80 first step to angular v13 2022-12-07 00:38:30 +01:00
Fabio Formosa
d9487d1106 #80 completed the migration to angular12 2022-12-07 00:37:25 +01:00
Fabio Formosa
3110496630 #80 completed the migration to angular12 2022-12-07 00:36:59 +01:00
Fabio Formosa
22e90dc7d1 #80 step into angular v12 2022-12-07 00:29:20 +01:00
Fabio Formosa
5943243e62 #80 step into angular v12 2022-12-07 00:27:20 +01:00
Fabio Formosa
1244a64d34 #80 fixes for angular v11 2022-12-07 00:25:50 +01:00
Fabio Formosa
40a6ecd159 #80 fixes for angular v11 2022-12-07 00:24:56 +01:00
Fabio Formosa
9df68dccac #80 first step to angular v11 2022-12-06 23:35:35 +01:00
Fabio Formosa
72bba6f80c #80 reverted deps to angular 10 2022-12-06 23:29:29 +01:00
Fabio Formosa
cf803bb1dd #80 reverted deps to angular 10 2022-12-06 23:29:13 +01:00
Fabio Formosa
13c78ed5d3 #80 removed the package-lock.json 2022-12-06 23:19:08 +01:00
Fabio Formosa
3bd878a978 #80 reverted deps to angular 10 2022-12-06 23:14:45 +01:00
Fabio Formosa
c9d3528f4b #80 reverted deps to angular 10 2022-12-06 23:14:32 +01:00
Fabio Formosa
3b2f8fc780 #80 reverted deps to angular 10 2022-12-06 23:08:17 +01:00
Fabio Formosa
bc3da09d60 #80 step into angular upgrade to v11 2022-12-06 19:33:42 +01:00
Fabio Formosa
284f56302c #80 fixed tests 2022-12-06 01:08:17 +01:00
Fabio Formosa
cdd5047bbc #80 jest setup fix for angular 10 2022-12-06 00:39:45 +01:00
Fabio Formosa
5d3ba95bd9 #80 upgraded angular to v10 2022-12-05 23:42:36 +01:00
Fabio Formosa
ec384113eb angular material upgrade to v4 2022-12-05 23:36:36 +01:00
Fabio Formosa
8b2651876c new package-lock.json 2022-12-05 23:31:55 +01:00
Fabio Formosa
a3852c421e bump version to 4.0.7-SNAPSHOT 2022-12-05 23:09:51 +01:00
Fabio Formosa
e1e1bdbd01 bump version to 4.0.6 2022-12-04 12:15:51 +01:00
Fabio Formosa
6757511de3 #62 added a missing test 2022-12-04 12:07:20 +01:00
Fabio Formosa
f5b717ec36 Update README.md 2022-12-04 11:54:06 +01:00
Fabio Formosa
6b491d9949 Update README.md 2022-12-04 11:52:54 +01:00
Fabio Formosa
cf382db49f Update README.md 2022-12-04 11:52:24 +01:00
Fabio Formosa
ee2f80e582 Update README.md
added a table of contents
2022-12-04 11:49:26 +01:00
Fabio Formosa
4453d62515 Update README.md
Added sonar quality badges
2022-12-04 11:33:47 +01:00
Fabio Formosa
72068818d7 #62 fixed sonar smells 2022-12-04 11:25:21 +01:00
Fabio Formosa
92bb94b9fa #62 added coverage exclusion 2022-12-04 11:15:53 +01:00
Fabio Formosa
7a2098e9ce #62 added coverage exclusion 2022-12-04 11:08:48 +01:00
Fabio Formosa
56b81f4f48 #62 added some exclusion from coverage 2022-12-03 19:54:07 +01:00
Fabio Formosa
82c060c2a7 #62 set jacoco properties 2022-12-03 19:46:14 +01:00
Fabio Formosa
fba35b796d #62 set jacoco properties 2022-12-03 19:42:12 +01:00
Fabio Formosa
5f6fc1fa6f #62 set jacoco properties 2022-12-03 19:16:28 +01:00
Fabio Formosa
a9c227cd05 Merge branch 'develop' 2022-12-03 19:11:49 +01:00
Fabio Formosa
a8330e062f #62 set jacoco properties 2022-12-03 19:07:29 +01:00
Fabio Formosa
30d0cbf6de Merge pull request #97 from fabioformosa/develop
v4.0.6
2022-12-03 17:17:00 +01:00
Fabio Formosa
3063e08eb3 #62 solved a couple of sonar smells 2022-12-03 17:10:34 +01:00
Fabio Formosa
f7054b160f #62 added missing tests 2022-12-03 16:46:27 +01:00
Fabio Formosa
98b5d0e37a #62 fixed the job class name in the TriggerFiredBundleDTO 2022-12-03 16:21:13 +01:00
Fabio Formosa
6348bac11a #62 added missing tests 2022-12-03 12:19:41 +01:00
Fabio Formosa
c2a26c97a8 #62 added some coverage exclusion 2022-12-02 00:37:56 +01:00
Fabio Formosa
cd6e01109b #62 added some coverage exclusion 2022-12-02 00:32:52 +01:00
Fabio Formosa
7553efdc3b #62 added missing tests 2022-12-02 00:21:23 +01:00
Fabio Formosa
6578dc312a #62 added missing tests 2022-12-01 23:52:48 +01:00
Fabio Formosa
c490a7ab28 #62 added missing tests 2022-12-01 23:48:16 +01:00
Fabio Formosa
0969a406c6 #62 fixed a failing test 2022-12-01 00:55:38 +01:00
Fabio Formosa
eed3021373 #62 fixed a failing test 2022-12-01 00:35:40 +01:00
Fabio Formosa
5b33bd4dca #62 added missing tests 2022-12-01 00:30:47 +01:00
Fabio Formosa
6d22207e27 #62 added missing tests 2022-11-29 23:39:14 +01:00
Fabio Formosa
249cf49873 #62 added missing tests 2022-11-29 23:12:54 +01:00
Fabio Formosa
b17d487c8b #62 added missing tests 2022-11-29 20:29:55 +01:00
Fabio Formosa
fedb2b50b6 #62 clean up 2022-11-29 18:44:46 +01:00
Fabio Formosa
4b18313e2d #62 removed unused security filter 2022-11-23 20:54:37 +01:00
Fabio Formosa
8387f587b3 bump version to 4.0.6-SNAPSHOT 2022-11-20 14:02:45 +01:00
Fabio Formosa
c7b64dbdf3 Update README.md 2022-11-20 14:01:24 +01:00
Fabio Formosa
085d61cf29 Merge pull request #95 from fabioformosa/develop
v4.0.5
2022-11-20 14:00:34 +01:00
71 changed files with 28653 additions and 12706 deletions

View File

@@ -1,3 +1,20 @@
## **v4.0.8**
Upgraded the frontend to angular v14
## **v4.0.6**
Minor bug fixes
## **v4.0.5**
Fixed potential security issues
## **v4.0.4**
* Conformed the trigger configuration to the Simple Trigger of Quartz
* **BREAKING CHANGE** Changed accordingly the API and the UI
* Made Quartz Manager embeddable in projects with existing quartz instance, security layer, swagger ui.
## **v3.1.0**
* Added a new persistence module to persist the quartz triggers in a postgresql database
## **v3.0.1**
Quartz-Manager is now publicly available into the maven central repo into 3 different packages.

View File

@@ -1,6 +1,23 @@
[![Java CI with Maven](https://github.com/fabioformosa/quartz-manager/actions/workflows/maven.yml/badge.svg)](https://github.com/fabioformosa/quartz-manager/actions/workflows/maven.yml)
[![npm CI](https://github.com/fabioformosa/quartz-manager/actions/workflows/npm.yml/badge.svg)](https://github.com/fabioformosa/quartz-manager/actions/workflows/npm.yml)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/it.fabioformosa.quartz-manager/quartz-manager-starter-api/badge.svg)](https://maven-badges.herokuapp.com/maven-central/it.fabioformosa.quartz-manager/quartz-manager-starter-api)
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/it.fabioformosa.quartz-manager/quartz-manager-starter-api/badge.svg)](https://maven-badges.herokuapp.com/maven-central/it.fabioformosa.quartz-manager/quartz-manager-starter-api)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=fabioformosa_quartz-manager&metric=coverage)](https://sonarcloud.io/summary/new_code?id=fabioformosa_quartz-manager) [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=fabioformosa_quartz-manager&metric=bugs)](https://sonarcloud.io/summary/new_code?id=fabioformosa_quartz-manager) [![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=fabioformosa_quartz-manager&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=fabioformosa_quartz-manager) [![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=fabioformosa_quartz-manager&metric=sqale_rating)](https://sonarcloud.io/summary/new_code?id=fabioformosa_quartz-manager)
[QUARTZ MANAGER](https://github.com/fabioformosa/quartz-manager#quartz-manager)
&nbsp;&nbsp;&nbsp;&nbsp;[Quartz Manager UI](https://github.com/fabioformosa/quartz-manager#quartz-manager-ui)
&nbsp;&nbsp;&nbsp;&nbsp;[Quartz Manager API](https://github.com/fabioformosa/quartz-manager#quartz-manager-api)
[HOW IT WORKS](https://github.com/fabioformosa/quartz-managerhttps://github.com/fabioformosa/quartz-manager#get-started)
&nbsp;&nbsp;&nbsp;&nbsp;[Quartz Manager Starter API Lib](https://github.com/fabioformosa/quartz-manager#quartz-manager-starter-api-lib)
&nbsp;&nbsp;&nbsp;&nbsp;[Quartz Manager Starter UI Lib](https://github.com/fabioformosa/quartz-manager#quartz-manager-starter-ui-lib)
&nbsp;&nbsp;&nbsp;&nbsp;[Quartz Manager Starter Security Lib](https://github.com/fabioformosa/quartz-manager#quartz-manager-starter-security-lib)
&nbsp;&nbsp;&nbsp;&nbsp;[Quartz Manager Persistence Lib](https://github.com/fabioformosa/quartz-manager#quartz-manager-starter-persistence-lib)
[EXAMPLES](https://github.com/fabioformosa/quartz-manager#examples)
[LIMITATIONS](https://github.com/fabioformosa/quartz-manager#limitations)
[ROADMAP](https://github.com/fabioformosa/quartz-manager#roadmap)
[REPOSITORY](https://github.com/fabioformosa/quartz-manager#repository)
[HOW TO CONTRIBUTE](https://github.com/fabioformosa/quartz-manager#how-to-contribute)
[SUPPORT THE PROJECT](https://github.com/fabioformosa/quartz-manager#%EF%B8%8F-support-the-project-%EF%B8%8F)
# QUARTZ MANAGER
In the last decade, the [Quartz Scheduler](http://www.quartz-scheduler.org/) has become the most adopted opensource job scheduling library for Java applications.
@@ -37,7 +54,7 @@ In the latter case, in which there isn't an existing quartz instance, you can re
* You can enable a secure layer, if your project doesn't have any, to give access at the API and the UI only to authenticated users.
* You can enable a persistent layer, to persist the config and the progress of your trigger, in a postgresql database.
# QUICK START
# GET STARTED
**Requirements**
Java 9+, Spring Framework 5+ (Spring Boot 2.x)
@@ -63,12 +80,12 @@ Add the dependency, make eligible for Quart Manager the job classes and set the
<dependency>
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-starter-api</artifactId>
<version>4.0.4</version>
<version>4.0.8</version>
</dependency>
```
#### Gradle
```
implementation group: 'it.fabioformosa.quartz-manager', name: 'quartz-manager-starter-api', version: '4.0.4'
implementation group: 'it.fabioformosa.quartz-manager', name: 'quartz-manager-starter-api', version: '4.0.8'
```
### Step 2. Quartz Manager Job Classes
@@ -151,12 +168,12 @@ You can optionally import the following dependency to have the UI Dashboard to i
<dependency>
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-starter-ui</artifactId>
<version>4.0.4</version>
<version>4.0.8</version>
</dependency>
```
#### Gradle
```
implementation group: 'it.fabioformosa.quartz-manager', name: 'quartz-manager-starter-ui', version: '4.0.4'
implementation group: 'it.fabioformosa.quartz-manager', name: 'quartz-manager-starter-ui', version: '4.0.8'
```
### Reach out the UI Console at URL
@@ -186,14 +203,14 @@ Future development: the Quart Manager Security OAuth2 client.
<dependency>
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-starter-security</artifactId>
<version>4.0.4</version>
<version>4.0.8</version>
</dependency>
```
#### Gradle
```
compile group: 'it.fabioformosa.quartz-manager', name: 'quartz-manager-starter-security', version: '4.0.4'
compile group: 'it.fabioformosa.quartz-manager', name: 'quartz-manager-starter-security', version: '4.0.8'
```
@@ -223,14 +240,14 @@ The pre-requesite is the availability of Postgresql database where Quartz Manage
<dependency>
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-starter-persistence</artifactId>
<version>4.0.4</version>
<version>4.0.8</version>
</dependency>
```
#### Gradle
```
compile group: 'it.fabioformosa.quartz-manager', name: 'quartz-manager-starter-persistence', version: '4.0.4'
compile group: 'it.fabioformosa.quartz-manager', name: 'quartz-manager-starter-persistence', version: '4.0.8'
```
### Quartz Manager Persistence Lib - App Props

View File

@@ -0,0 +1,214 @@
/*
👋 Hi! This file was autogenerated by tslint-to-eslint-config.
https://github.com/typescript-eslint/tslint-to-eslint-config
It represents the closest reasonable ESLint configuration to this
project's original TSLint configuration.
We recommend eventually switching this configuration to extend from
the recommended rulesets in typescript-eslint.
https://github.com/typescript-eslint/tslint-to-eslint-config/blob/master/docs/FAQs.md
Happy linting! 💖
*/
{
"env": {
"browser": true,
"es6": true,
"node": true
},
"extends": [
"prettier"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "tsconfig.json",
"sourceType": "module"
},
"plugins": [
"eslint-plugin-import",
"@angular-eslint/eslint-plugin",
"@typescript-eslint",
"@typescript-eslint/tslint"
],
"root": true,
"rules": {
"@angular-eslint/component-class-suffix": "error",
"@angular-eslint/component-selector": [
"error",
{
"type": "element",
"prefix": "app",
"style": "kebab-case"
}
],
"@angular-eslint/directive-class-suffix": "error",
"@angular-eslint/directive-selector": [
"error",
{
"type": "attribute",
"prefix": "app",
"style": "camelCase"
}
],
"@angular-eslint/no-host-metadata-property": "error",
"@angular-eslint/no-input-rename": "error",
"@angular-eslint/no-inputs-metadata-property": "error",
"@angular-eslint/no-output-rename": "error",
"@angular-eslint/no-outputs-metadata-property": "error",
"@angular-eslint/use-lifecycle-interface": "error",
"@angular-eslint/use-pipe-transform-interface": "error",
"@typescript-eslint/consistent-type-definitions": "error",
"@typescript-eslint/dot-notation": "off",
"@typescript-eslint/explicit-member-accessibility": [
"off",
{
"accessibility": "explicit"
}
],
"@typescript-eslint/indent": "off",
"@typescript-eslint/member-delimiter-style": [
"off",
{
"multiline": {
"delimiter": "none",
"requireLast": true
},
"singleline": {
"delimiter": "semi",
"requireLast": false
}
}
],
"@typescript-eslint/member-ordering": "error",
"@typescript-eslint/naming-convention": [
"error",
{
"selector": "variable",
"format": [
"camelCase",
"UPPER_CASE"
],
"leadingUnderscore": "forbid",
"trailingUnderscore": "forbid"
}
],
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/no-empty-interface": "error",
"@typescript-eslint/no-inferrable-types": [
"error",
{
"ignoreParameters": true
}
],
"@typescript-eslint/no-shadow": [
"error",
{
"hoist": "all"
}
],
"@typescript-eslint/no-unused-expressions": "error",
"@typescript-eslint/prefer-function-type": "error",
"@typescript-eslint/quotes": [
"error",
"single"
],
"@typescript-eslint/semi": [
"off",
null
],
"@typescript-eslint/tslint/config": [
"error",
{
"rules": {
"import-spacing": true,
"invoke-injectable": true,
"no-access-missing-member": true,
"templates-use-public": true,
"whitespace": true
}
}
],
"@typescript-eslint/type-annotation-spacing": "off",
"@typescript-eslint/unified-signatures": "error",
"brace-style": [
"error",
"1tbs"
],
"curly": "error",
"dot-notation": "off",
"eol-last": "off",
"eqeqeq": [
"error",
"smart"
],
"guard-for-in": "error",
"id-denylist": "off",
"id-match": "off",
"import/no-deprecated": "warn",
"indent": "off",
"max-len": [
"error",
{
"code": 140
}
],
"no-bitwise": "error",
"no-caller": "error",
"no-console": [
"error",
{
"allow": [
"log",
"warn",
"dir",
"timeLog",
"assert",
"clear",
"count",
"countReset",
"group",
"groupEnd",
"table",
"dirxml",
"error",
"groupCollapsed",
"Console",
"profile",
"profileEnd",
"timeStamp",
"context"
]
}
],
"no-debugger": "error",
"no-empty": "off",
"no-empty-function": "off",
"no-eval": "error",
"no-fallthrough": "error",
"no-new-wrappers": "error",
"no-redeclare": "error",
"no-restricted-imports": "error",
"no-shadow": "off",
"no-throw-literal": "error",
"no-trailing-spaces": "off",
"no-underscore-dangle": "off",
"no-unused-expressions": "off",
"no-unused-labels": "error",
"no-var": "error",
"prefer-const": "error",
"quotes": "off",
"radix": "error",
"semi": "off",
"spaced-comment": [
"error",
"always",
{
"markers": [
"/"
]
}
],
"valid-typeof": "error"
}
}

View File

@@ -40,3 +40,4 @@ testem.log
# System Files
.DS_Store
Thumbs.db
/.angular/

View File

@@ -0,0 +1,47 @@
src/polyfills.ts
src/typings.d.ts
_test.ts
# See http://help.github.com/ignore-files/ for more about ignoring files.
# compiled output
/dist
/tmp
/out-tsc
# dependencies
/node_modules
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
# misc
/.sass-cache
/connect.lock
/coverage
/libpeerconnection.log
npm-debug.log
testem.log
/typings
# e2e
/e2e/*.js
/e2e/*.map
# System Files
.DS_Store
Thumbs.db
/.angular/

View File

@@ -0,0 +1,11 @@
{
"tabWidth": 2,
"useTabs": false,
"singleQuote": true,
"semi": true,
"bracketSpacing": true,
"arrowParens": "avoid",
"trailingComma": "es5",
"bracketSameLine": true,
"printWidth": 80
}

View File

@@ -18,6 +18,9 @@
"main": "src/main.ts",
"tsConfig": "src/tsconfig.app.json",
"polyfills": "src/polyfills.ts",
"allowedCommonJsDependencies": [
"stompjs", "sockjs-client", "moment"
],
"assets": [
"src/assets",
"src/favicon.ico"
@@ -38,7 +41,6 @@
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"aot": true,
"extractLicenses": true,
@@ -71,13 +73,10 @@
}
},
"lint": {
"builder": "@angular-devkit/build-angular:tslint",
"builder": "@angular-eslint/builder:lint",
"options": {
"tsConfig": [
"src/tsconfig.app.json",
"src/tsconfig.spec.json"
],
"exclude": []
"eslintConfig": ".eslintrc.json",
"lintFilePatterns": ["**/*.spec.ts", "**/*.ts"]
}
}
}
@@ -106,7 +105,6 @@
}
}
},
"defaultProject": "angular-spring-starter",
"schematics": {
"@schematics/angular:component": {
"prefix": "qrzmng",
@@ -115,5 +113,8 @@
"@schematics/angular:directive": {
"prefix": "qrzmng"
}
},
"cli": {
"analytics": false
}
}

View File

@@ -1 +1 @@
import 'jest-preset-angular';
import 'jest-preset-angular/setup-jest';

File diff suppressed because it is too large Load Diff

View File

@@ -5,67 +5,81 @@
"scripts": {
"ng": "ng",
"start": "ng serve --proxy-config proxy.conf.json",
"build": "ng build --prod",
"build": "ng build --configuration production",
"test": "jest",
"lint": "ng lint",
"e2e": "ng e2e"
},
"private": true,
"dependencies": {
"@angular-material-components/datetime-picker": "2.0.4",
"@angular-material-components/moment-adapter": "2.0.2",
"@angular/animations": "9.1.4",
"@angular/cdk": "9.2.1",
"@angular/common": "9.1.4",
"@angular/compiler": "9.1.4",
"@angular/core": "9.1.4",
"@angular/flex-layout": "9.0.0-beta.29",
"@angular/forms": "9.1.4",
"@angular/material": "9.2.1",
"@angular/platform-browser": "9.1.4",
"@angular/platform-browser-dynamic": "9.1.4",
"@angular/platform-server": "9.1.4",
"@angular/router": "9.1.4",
"@auth0/angular-jwt": "^4.0.0",
"@angular-material-components/datetime-picker": "8.0.0",
"@angular-material-components/moment-adapter": "8.0.0",
"@angular/animations": "14.2.12",
"@angular/cdk": "^14.0.1",
"@angular/common": "14.2.12",
"@angular/compiler": "14.2.12",
"@angular/core": "14.2.12",
"@angular/flex-layout": "14.0.0-beta.41",
"@angular/forms": "14.2.12",
"@angular/material": "^14.0.1",
"@angular/platform-browser": "14.2.12",
"@angular/platform-browser-dynamic": "14.2.12",
"@angular/platform-server": "14.2.12",
"@angular/router": "14.2.12",
"@auth0/angular-jwt": "5.1.0",
"@fortawesome/fontawesome": "^1.1.4",
"@fortawesome/fontawesome-free-regular": "^5.0.8",
"@fortawesome/fontawesome-free-solid": "^5.0.8",
"@stomp/ng2-stompjs": "^0.6.3",
"@types/jest": "^27.0.2",
"core-js": "2.5.1",
"hammerjs": "2.0.8",
"moment": "^2.29.1",
"net": "^1.0.2",
"rxjs": "6.5.5",
"sockjs-client": "^1.1.1",
"stompjs": "^2.3.3",
"tslib": "^1.10.0",
"zone.js": "~0.10.2"
"tslib": "~2.4.1",
"zone.js": "~0.12.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.901.4",
"@angular-devkit/core": "^9.1.4",
"@angular/cli": "9.1.4",
"@angular/compiler-cli": "9.1.4",
"@angular/language-service": "9.1.4",
"@angular-devkit/build-angular": "14.2.10",
"@angular-devkit/core": "^14.2.10",
"@angular-eslint/builder": "14.4.0",
"@angular-eslint/eslint-plugin": "14.4.0",
"@angular-eslint/eslint-plugin-template": "14.4.0",
"@angular-eslint/schematics": "14.4.0",
"@angular-eslint/template-parser": "14.4.0",
"@angular/cli": "14.2.10",
"@angular/compiler-cli": "14.2.12",
"@angular/language-service": "14.2.12",
"@types/hammerjs": "2.0.34",
"@types/jasmine": "2.5.54",
"@types/jasminewd2": "2.0.3",
"@types/jest": "28.1.1",
"@types/node": "^12.11.1",
"codelyzer": "^5.1.2",
"jasmine-core": "2.6.4",
"jasmine-spec-reporter": "4.1.1",
"jest": "^26.0.1",
"jest-preset-angular": "^8.2.0",
"karma": "1.7.1",
"karma-chrome-launcher": "2.1.1",
"karma-cli": "1.0.1",
"karma-coverage-istanbul-reporter": "1.3.0",
"karma-jasmine": "1.1.0",
"karma-jasmine-html-reporter": "0.2.2",
"protractor": "5.1.2",
"ts-node": "3.0.6",
"tslint": "5.7.0",
"typescript": "3.8.3"
"@typescript-eslint/eslint-plugin": "5.43.0",
"@typescript-eslint/eslint-plugin-tslint": "^5.46.0",
"@typescript-eslint/parser": "5.43.0",
"codelyzer": "~6.0.2",
"eslint": "^8.28.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-prettier": "^4.2.1",
"jasmine-core": "~4.5.0",
"jasmine-spec-reporter": "~7.0.0",
"jest": "28.1.3",
"jest-preset-angular": "~12.2.3",
"karma": "~6.4.1",
"karma-chrome-launcher": "~3.1.1",
"karma-cli": "2.0.0",
"karma-coverage-istanbul-reporter": "~3.0.3",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.0.0",
"prettier": "^2.8.1",
"prettier-eslint": "^15.0.1",
"protractor": "~7.0.0",
"ts-node": "10.9.1",
"typescript": "4.6.4"
},
"jest": {
"preset": "jest-preset-angular",

View File

@@ -46,7 +46,7 @@ export const routes: Routes = [
@NgModule({
imports: [RouterModule.forRoot(routes, {
initialNavigation: false
initialNavigation: 'disabled'
})],
exports: [RouterModule],
providers: []

View File

@@ -1,4 +1,4 @@
import { TestBed, async } from '@angular/core/testing';
import {TestBed, async, waitForAsync} from '@angular/core/testing';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
@@ -18,7 +18,7 @@ import {
} from './services';
describe('AppComponent', () => {
beforeEach(async(() => {
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [
AppComponent,

View File

@@ -1,4 +1,4 @@
import { async, ComponentFixture, TestBed, inject } from '@angular/core/testing';
import { ComponentFixture, TestBed, inject, waitForAsync} from '@angular/core/testing';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { RouterTestingModule } from '@angular/router/testing';
@@ -18,7 +18,7 @@ describe('AccountMenuComponent', () => {
let component: AccountMenuComponent;
let fixture: ComponentFixture<AccountMenuComponent>;
beforeEach(async(() => {
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
RouterTestingModule

View File

@@ -1,4 +1,4 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
import {SchedulerControlComponent} from './scheduler-control.component';
import {ApiService, ConfigService, SchedulerService, UserService} from '../../services';
import {HttpClient} from '@angular/common/http';
@@ -19,7 +19,7 @@ describe('SchedulerControlComponent', () => {
let httpClient: HttpClient;
let httpTestingController: HttpTestingController;
beforeEach(async(() => {
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [MatCardModule, MatDividerModule, MatIconModule, HttpClientTestingModule, RouterTestingModule],
declarations: [SchedulerControlComponent],

View File

@@ -20,7 +20,7 @@
<mat-form-field
[appearance]="enabledTriggerForm && !trigger ? 'standard': 'none'"
class="full-size-input">
<mat-label>Trigger Name *</mat-label>
<mat-label>Trigger Name</mat-label>
<input id="triggerName"
[readonly]="!enabledTriggerForm || trigger"
matInput placeholder="name of the trigger (unique)"
@@ -36,7 +36,7 @@
[appearance]="enabledTriggerForm ? 'standard': 'none'"
class="full-size-input"
>
<mat-label>Job Class *</mat-label>
<mat-label>Job Class</mat-label>
<mat-select id="jobClass" name="jobClass" formControlName="jobClass" [disabled]="!enabledTriggerForm">
<mat-option *ngFor="let job of jobs" [value]="job" style="font-size: 0.8em">
{{job}}
@@ -53,7 +53,7 @@
[appearance]="enabledTriggerForm ? 'standard': 'none'"
class="full-size-input"
>
<mat-label>Misfire Instruction *</mat-label>
<mat-label>Misfire Instruction</mat-label>
<mat-select id="misfireInstruction" name="misfireInstruction" formControlName="misfireInstruction"
[disabled]="!enabledTriggerForm" style="font-size: 0.8em">
<mat-option value="MISFIRE_INSTRUCTION_FIRE_NOW">FIRE NOW</mat-option>
@@ -93,9 +93,6 @@
formControlName="startDate" name="startDate">
<mat-datepicker-toggle matSuffix [for]="startDatePicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #startDatePicker showSpinners="true" showSeconds="true">
<!-- [stepHour]="stepHour"-->
<!-- [stepMinute]="stepMinute" [stepSecond]="stepSecond" [touchUi]="touchUi" [color]="color"-->
<!-- [enableMeridian]="enableMeridian" [disableMinute]="disableMinute" [hideTime]="hideTime">-->
</ngx-mat-datetime-picker>
</mat-form-field>
</div>
@@ -114,9 +111,6 @@
>
<mat-datepicker-toggle matSuffix [for]="endDatePicker"></mat-datepicker-toggle>
<ngx-mat-datetime-picker #endDatePicker showSpinners="true" showSeconds="true">
<!-- [stepHour]="stepHour"-->
<!-- [stepMinute]="stepMinute" [stepSecond]="stepSecond" [touchUi]="touchUi" [color]="color"-->
<!-- [enableMeridian]="enableMeridian" [disableMinute]="disableMinute" [hideTime]="hideTime">-->
</ngx-mat-datetime-picker>
</mat-form-field>
<mat-error *ngIf="simpleTriggerReactiveForm.controls.triggerPeriod.errors?.invalidTriggerPeriod" style="font-size: small">
@@ -141,9 +135,6 @@
repeatCount and repeatInterval must be <strong>both</strong> set or unset
</mat-error>
</mat-form-field>
<!-- <mat-error *ngIf="simpleTriggerReactiveForm.controls.triggerRecurrence.errors?.invalidTriggerRecurrence" style="font-size: small">-->
<!-- repeatCount and repeatInterval must be <strong>both</strong> set or unset-->
<!-- </mat-error>-->
</div>
<div>
<mat-form-field

View File

@@ -1,4 +1,4 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
import {MatCardModule} from '@angular/material/card';
import {SimpleTriggerConfigComponent} from './simple-trigger-config.component';
import {ApiService, ConfigService, CONTEXT_PATH, SchedulerService} from '../../services';
@@ -12,7 +12,7 @@ import {FormBuilder, FormsModule, ReactiveFormsModule} from '@angular/forms';
import {MatFormFieldModule} from '@angular/material/form-field';
import {MatNativeDateModule} from '@angular/material/core';
import {MatInputModule} from '@angular/material/input';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {NoopAnimationsModule} from '@angular/platform-browser/animations';
import {TriggerKey} from '../../model/triggerKey.model';
import {Trigger} from '../../model/trigger.model';
import {JobDetail} from '../../model/jobDetail.model';
@@ -29,9 +29,9 @@ describe('SimpleTriggerConfig', () => {
let httpClient: HttpClient;
let httpTestingController: HttpTestingController;
beforeEach(async( () => {
beforeEach(waitForAsync( () => {
TestBed.configureTestingModule({
imports: [FormsModule, MatFormFieldModule, MatFormFieldModule, MatSelectModule, MatInputModule, BrowserAnimationsModule,
imports: [FormsModule, MatFormFieldModule, MatFormFieldModule, MatSelectModule, MatInputModule, NoopAnimationsModule,
MatNativeDateModule, ReactiveFormsModule,
MatCardModule, MatIconModule, HttpClientTestingModule, RouterTestingModule],
declarations: [SimpleTriggerConfigComponent],
@@ -198,6 +198,10 @@ describe('SimpleTriggerConfig', () => {
it('should fetch and display the trigger when the triggerKey is passed as input', () => {
const mockTriggerKey = new TriggerKey('my-simple-trigger', null);
component.triggerKey = mockTriggerKey;
component.trigger = new SimpleTrigger();
component.trigger.triggerKeyDTO = mockTriggerKey;
fixture.detectChanges();
const mockTrigger = new Trigger();
@@ -226,6 +230,9 @@ describe('SimpleTriggerConfig', () => {
getJobsReq.flush([]);
fixture.detectChanges();
component.openTriggerForm();
fixture.detectChanges();
const componentDe: DebugElement = fixture.debugElement;
const warningCard = componentDe.query(By.css('#noEligibleJobsAlert'));
expect(warningCard).toBeTruthy();
@@ -237,6 +244,9 @@ describe('SimpleTriggerConfig', () => {
getJobsReq.flush(['sampleJob']);
fixture.detectChanges();
component.openTriggerForm();
fixture.detectChanges();
const componentDe: DebugElement = fixture.debugElement;
const warningCard = componentDe.query(By.css('#noEligibleJobsAlert'));
expect(warningCard).toBeFalsy();

View File

@@ -3,12 +3,11 @@ import {SchedulerService} from '../../services';
import {Scheduler} from '../../model/scheduler.model';
import {SimpleTriggerCommand} from '../../model/simple-trigger.command';
import {SimpleTrigger} from '../../model/simple-trigger.model';
import {SimpleTriggerForm} from '../../model/simple-trigger.form';
import * as moment from 'moment';
import {TriggerKey} from '../../model/triggerKey.model';
import JobService from '../../services/job.service';
import {MisfireInstruction, MisfireInstructionCaption} from '../../model/misfire-instruction.model';
import {AbstractControl, FormBuilder, FormControl, FormGroup, ValidationErrors, ValidatorFn, Validators} from '@angular/forms';
import {AbstractControl, UntypedFormBuilder, UntypedFormGroup, ValidationErrors, Validators} from '@angular/forms';
@Component({
selector: 'qrzmng-simple-trigger-config',
@@ -17,9 +16,9 @@ import {AbstractControl, FormBuilder, FormControl, FormGroup, ValidationErrors,
})
export class SimpleTriggerConfigComponent implements OnInit {
trigger: SimpleTrigger;
trigger: SimpleTrigger = null;
simpleTriggerReactiveForm: FormGroup = this.formBuilder.group({
simpleTriggerReactiveForm: UntypedFormGroup = this.formBuilder.group({
triggerName: [this.trigger?.triggerKeyDTO.name, Validators.required],
jobClass: [this.trigger?.jobDetailDTO.jobClassName, Validators.required],
triggerPeriod: this.formBuilder.group({
@@ -50,7 +49,7 @@ export class SimpleTriggerConfigComponent implements OnInit {
onNewTrigger = new EventEmitter<SimpleTrigger>();
constructor(
private formBuilder: FormBuilder,
private formBuilder: UntypedFormBuilder,
private schedulerService: SchedulerService,
private jobService: JobService
) {

View File

@@ -1,4 +1,4 @@
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
import {ApiService, ConfigService, CONTEXT_PATH, TriggerService} from '../../services';
import {HttpClient} from '@angular/common/http';
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
@@ -21,7 +21,7 @@ describe('TriggerListComponent', () => {
let httpClient: HttpClient;
let httpTestingController: HttpTestingController;
beforeEach(async(() => {
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [MatCardModule, MatDialogModule, MatDividerModule,
MatIconModule, MatListModule, HttpClientTestingModule, RouterTestingModule],

View File

@@ -1,4 +1,4 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
import {GenericErrorComponent} from './genericError.component';
@@ -6,7 +6,7 @@ describe('GenericComponent', () => {
let component: GenericErrorComponent;
let fixture: ComponentFixture<GenericErrorComponent>;
beforeEach(async(() => {
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ GenericErrorComponent ]
})

View File

@@ -1,4 +1,4 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
import { ForbiddenComponent } from './forbidden.component';
@@ -6,7 +6,7 @@ describe('ForbiddenComponent', () => {
let component: ForbiddenComponent;
let fixture: ComponentFixture<ForbiddenComponent>;
beforeEach(async(() => {
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [ ForbiddenComponent ]
})

View File

@@ -1,4 +1,4 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
import { LoginComponent } from './login.component';
import { RouterTestingModule } from '@angular/router/testing';
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
@@ -16,7 +16,7 @@ describe('LoginComponent', () => {
let component: LoginComponent;
let fixture: ComponentFixture<LoginComponent>;
beforeEach(async(() => {
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [LoginComponent],
imports: [

View File

@@ -1,5 +1,5 @@
import {Component, OnDestroy, OnInit} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
import {UntypedFormBuilder, UntypedFormGroup, Validators} from '@angular/forms';
import {ActivatedRoute, Router} from '@angular/router';
import {DisplayMessage} from '../../shared/models/display-message';
import {Subject} from 'rxjs';
@@ -15,7 +15,7 @@ import {AuthService, UserService} from '../../services';
export class LoginComponent implements OnInit, OnDestroy {
title = 'Login';
githubLink = 'https://github.com/fabioformosa/quartz-manager';
form: FormGroup;
form: UntypedFormGroup;
submitted = false;
@@ -29,7 +29,7 @@ export class LoginComponent implements OnInit, OnDestroy {
private authService: AuthService,
private router: Router,
private route: ActivatedRoute,
private formBuilder: FormBuilder
private formBuilder: UntypedFormBuilder
) {
}

View File

@@ -1,4 +1,4 @@
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import {ComponentFixture, TestBed, waitForAsync} from '@angular/core/testing';
import { NotFoundComponent } from './not-found.component';
@@ -6,7 +6,7 @@ describe('NotFoundComponent', () => {
let component: NotFoundComponent;
let fixture: ComponentFixture<NotFoundComponent>;
beforeEach(async(() => {
beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [NotFoundComponent]
})

View File

@@ -10,7 +10,7 @@
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"target": "es2015",
"target": "es2020",
"typeRoots": [
"node_modules/@types"
],
@@ -18,6 +18,6 @@
"es2016",
"dom"
],
"module": "esnext"
"module": "es2020"
}
}

View File

@@ -12,6 +12,9 @@
"curly": true,
"eofline": true,
"forin": true,
"deprecation": {
"severity": "warning"
},
"import-blacklist": [true],
"import-spacing": true,
"indent": [
@@ -53,7 +56,6 @@
"no-switch-case-fall-through": true,
"no-trailing-whitespace": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-var-keyword": true,
"object-literal-sort-keys": false,
"one-line": [

View File

@@ -58,13 +58,13 @@ Replace the dummy job (class: `it.fabioformosa.quartzmanager.jobs.SampleJob`) wi
**Backend Stack** Java 9, Spring Boot 2.5.6 (Spring MVC 5.3.12, Spring Security 5.5.3), Quartz Scheduler 2.3.2
**Frontend** Angular 9.1.4, Web-Socket (stompjs 2.3.3)
**Frontend** Angular 14.2.12, Web-Socket (stompjs 2.3.3)
**Style** Angular Material 9, FontAwesome 5
**Style** Angular Material 14, FontAwesome 5
Starting from Quartz Manager v2.x.x, the new structure of project is:
* Multi-module maven project: REST API backend
* Angular 9: Single Page Application frontend
* Angular 14: Single Page Application frontend
(The first version of quartz manager was a monolithic backend that provided also frontend developed with angularjs 1.6.x. You can find it at the branch 1.x.x)

View File

@@ -10,7 +10,7 @@
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-parent</artifactId>
<version>4.0.5</version>
<version>4.0.8</version>
<packaging>pom</packaging>
@@ -41,7 +41,6 @@
</developers>
<properties>
<sonar.organization>fabioformosa</sonar.organization>
<java.version>9</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven-surefire-plugin.version>2.22.0</maven-surefire-plugin.version>
@@ -51,8 +50,18 @@
<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.organization>fabioformosa</sonar.organization>
<sonar.host.url>https://sonarcloud.io</sonar.host.url>
<sonar.exclusions>**/SpringApplicationTest.java, **/QuartManagerApplicationTests.java</sonar.exclusions>
<sonar.exclusions>
**/SpringApplicationTest.java, **/QuartManagerApplicationTests.java, **/MisfireTestJob.java
</sonar.exclusions>
<sonar.coverage.exclusions>
OpenApiConfig.java,
**/SecurityOpenApiConfig.java, **/QuartzModuleProperties.java, **/QuartzManagerDemoApplication.java,
**/ServletInitializer.java, **/SessionController.java, **/HealthCheckController.java,
**/WebShowcaseOpenApiConfig.java, **/MisfireTestJob.java, **/PersistenceConfig.java,
**/QuartzManagerSecurityConfig.java
</sonar.coverage.exclusions>
</properties>
<modules>
@@ -69,27 +78,27 @@
<dependency>
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-common</artifactId>
<version>4.0.5</version>
<version>4.0.8</version>
</dependency>
<dependency>
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-starter-api</artifactId>
<version>4.0.5</version>
<version>4.0.8</version>
</dependency>
<dependency>
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-starter-security</artifactId>
<version>4.0.5</version>
<version>4.0.8</version>
</dependency>
<dependency>
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-starter-persistence</artifactId>
<version>4.0.5</version>
<version>4.0.8</version>
</dependency>
<dependency>
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-starter-ui</artifactId>
<version>4.0.5</version>
<version>4.0.8</version>
</dependency>
</dependencies>
</dependencyManagement>
@@ -128,6 +137,19 @@
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco-maven-plugin.version}</version>
<configuration>
<excludes>
<exclude>**/OpenApiConfig.class</exclude>
<exclude>**/SecurityOpenApiConfig.class</exclude>
<exclude>**/QuartzModuleProperties.class</exclude>
<exclude>**/QuartzManagerDemoApplication.class</exclude>
<exclude>**/ServletInitializer.class</exclude>
<exclude>**/SessionController.class</exclude>
<exclude>**/HealthCheckController.class</exclude>
<exclude>**/WebShowcaseOpenApiConfig.class</exclude>
<exclude>**/MisfireTestJob.class</exclude>
</excludes>
</configuration>
<executions>
<execution>
<goals>
@@ -141,6 +163,13 @@
<goal>report</goal>
</goals>
</execution>
<execution>
<id>report-aggregate</id>
<goals>
<goal>report-aggregate</goal>
</goals>
<phase>verify</phase>
</execution>
</executions>
</plugin>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-parent</artifactId>
<version>4.0.5</version>
<version>4.0.8</version>
</parent>
<artifactId>quartz-manager-common</artifactId>

View File

@@ -1,10 +1,12 @@
package it.fabioformosa.quartzmanager.api.common.properties;
import lombok.Data;
import lombok.Generated;
import java.util.Properties;
@Data
@Generated
public class QuartzModuleProperties {
private Properties properties = new Properties();

View File

@@ -1,13 +1,20 @@
package it.fabioformosa.quartzmanager.api.common.utils;
import lombok.Getter;
import java.util.function.Function;
/**
*
* @param <R> success type
*/
@Getter
public class Try<R> {
private final Throwable failure;
private final R success;
public Try(Throwable failure, R success) {
private Try(Throwable failure, R success) {
this.failure = failure;
this.success = success;
}
@@ -16,11 +23,11 @@ public class Try<R> {
return success;
}
public static <R> Try<R> success(R r){
private static <R> Try<R> success(R r){
return new Try<>(null, r);
}
public static <R> Try<R> failure(Throwable e){
private static <R> Try<R> failure(Throwable e){
return new Try<>(e, null);
}
@@ -38,14 +45,6 @@ public class Try<R> {
return t -> Try.with(checkedFunction).apply(t).getSuccess();
}
public boolean isSuccess(){
return this.failure == null;
}
public boolean isFailure(){
return this.failure != null;
}
@FunctionalInterface
public static interface CheckedFunction<T, R> {
R apply(T t) throws java.lang.Exception;

View File

@@ -5,6 +5,7 @@ import org.junit.jupiter.api.Test;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Calendar;
import java.util.Date;
class DateUtilsTest {
@@ -17,4 +18,20 @@ class DateUtilsTest {
Assertions.assertThat(convertedLocalDateTime).isEqualTo(originalLocalDateTime);
}
@Test
void givenALocalDatetime_whenTheAddHoursToNowIsCalled_shouldReturnAFutureDate(){
Calendar calendar = Calendar.getInstance();
Date futureDate = DateUtils.addHoursToNow(1);
calendar.add(Calendar.HOUR_OF_DAY, 1);
calendar.add(Calendar.MINUTE, -1);
Date hourStartingAround = calendar.getTime();
calendar.add(Calendar.HOUR_OF_DAY, 1);
calendar.add(Calendar.MINUTE, 2);
Date hourEndingAround = calendar.getTime();
Assertions.assertThat(futureDate).isBetween(hourStartingAround, hourEndingAround);
}
}

View File

@@ -0,0 +1,42 @@
package it.fabioformosa.quartzmanager.api.common.utils;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.Optional;
class TryTest {
String raiseExceptionIfHello(String greetings) throws Exception {
if("hello".equals(greetings))
throw new Exception("hello");
return greetings;
}
@Test
void givenAFunctionWhichRaisesAnException_whenSneakyThrowIsCalled_thenItReturnsNull(){
String hello = Optional.of("hello").map(Try.sneakyThrow(this::raiseExceptionIfHello)).orElse(null);
Assertions.assertThat(hello).isNull();
}
@Test
void givenAFunctionWhichDoesntRaiseAnException_whenSneakyThrowIsCalled_thenItReturnsTheValue(){
String hello = Optional.of("not hello").map(Try.sneakyThrow(this::raiseExceptionIfHello)).orElse(null);
Assertions.assertThat(hello).isEqualTo("not hello");
}
@Test
void givenAFunctionWhichRaisesAnException_whenTryWithIsCalled_thenItReturnsAFailureObj(){
Try<String> aTry = Optional.of("hello").map(greet -> Try.with(this::raiseExceptionIfHello).apply(greet)).get();
Assertions.assertThat(aTry.getFailure()).isNotNull();
Assertions.assertThat(aTry.getFailure().getMessage()).isEqualTo("hello");
}
@Test
void givenAFunctionWhichDoesntRaiseAnException_whenTryWithIsCalled_thenItReturnsTheValue(){
Try<String> aTry = Optional.of("not hello").map(greet -> Try.with(this::raiseExceptionIfHello).apply(greet)).get();
Assertions.assertThat(aTry.getFailure()).isNull();
Assertions.assertThat(aTry.getSuccess()).isEqualTo("not hello");
}
}

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-parent</artifactId>
<version>4.0.5</version>
<version>4.0.8</version>
</parent>
<artifactId>quartz-manager-starter-api</artifactId>
@@ -20,7 +20,7 @@
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<springdoc-openapi.version>1.5.12</springdoc-openapi.version>
<java.version>9</java.version>
<sonar.exclusions>**/QuartManagerApplicationTests.java</sonar.exclusions>
<sonar.exclusions>**/QuartManagerApplicationTests.java, **/OpenApiConfig.java</sonar.exclusions>
</properties>
<dependencies>

View File

@@ -4,6 +4,7 @@ import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import it.fabioformosa.quartzmanager.api.common.config.QuartzManagerPaths;
import lombok.Generated;
import lombok.extern.slf4j.Slf4j;
import org.springdoc.core.GroupedOpenApi;
import org.springdoc.core.customizers.OpenApiCustomiser;
@@ -19,6 +20,7 @@ import java.util.Optional;
@Slf4j
@Configuration
@Generated
public class OpenApiConfig {
@ConditionalOnProperty(name = "quartz-manager.oas.enabled")

View File

@@ -16,10 +16,11 @@ import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.stream.Collectors;
@RequestMapping(QuartzManagerPaths.QUARTZ_MANAGER_BASE_CONTEXT_PATH + "/jobs")
@RequestMapping(JobController.JOB_CONTROLLER_BASE_URL)
@SecurityRequirement(name = OpenAPIConfigConsts.QUARTZ_MANAGER_SEC_OAS_SCHEMA)
@RestController
public class JobController {
public static final String JOB_CONTROLLER_BASE_URL = QuartzManagerPaths.QUARTZ_MANAGER_BASE_CONTEXT_PATH + "/jobs";
private final JobService jobService;
public JobController(JobService jobService) {

View File

@@ -17,7 +17,8 @@ public class SchedulerToSchedulerDTO extends AbstractBaseConverterToDTO<Schedule
protected void convert(Scheduler source, SchedulerDTO target) {
target.setName(source.getSchedulerName());
target.setInstanceId(source.getSchedulerInstanceId());
target.setTriggerKeys(source.getTriggerKeys(GroupMatcher.anyTriggerGroup()));
if(!source.isShutdown())
target.setTriggerKeys(source.getTriggerKeys(GroupMatcher.anyTriggerGroup()));
target.setStatus(buildTheSchedulerStatus(source));
}

View File

@@ -2,6 +2,7 @@ package it.fabioformosa.quartzmanager.api.dto;
import it.fabioformosa.quartzmanager.api.enums.SchedulerStatus;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.quartz.TriggerKey;
@@ -11,6 +12,7 @@ import java.util.Set;
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public class SchedulerDTO {
private String name;
private String instanceId;

View File

@@ -1,7 +1,13 @@
package it.fabioformosa.quartzmanager.api.dto;
import lombok.*;
import java.util.Date;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TriggerFiredBundleDTO {
private int timesTriggered;
@@ -18,22 +24,6 @@ public class TriggerFiredBundleDTO {
private String jobClass;
public Date getFinalFireTime() {
return finalFireTime;
}
public String getJobClass() {
return jobClass;
}
public String getJobKey() {
return jobKey;
}
public Date getNextFireTime() {
return nextFireTime;
}
public int getPercentage() {
if (this.repeatCount <= 0)
return -1;
@@ -41,44 +31,4 @@ public class TriggerFiredBundleDTO {
* 100);
}
public Date getPreviousFireTime() {
return previousFireTime;
}
public int getRepeatCount() {
return repeatCount;
}
public int getTimesTriggered() {
return timesTriggered;
}
public void setFinalFireTime(Date finalFireTime) {
this.finalFireTime = finalFireTime;
}
public void setJobClass(String jobClass) {
this.jobClass = jobClass;
}
public void setJobKey(String jobKey) {
this.jobKey = jobKey;
}
public void setNextFireTime(Date nextFireTime) {
this.nextFireTime = nextFireTime;
}
public void setPreviousFireTime(Date previousFireTime) {
this.previousFireTime = previousFireTime;
}
public void setRepeatCount(int repeatCount) {
this.repeatCount = repeatCount;
}
public void setTimesTriggered(int timesTriggered) {
this.timesTriggered = timesTriggered;
}
}

View File

@@ -8,11 +8,8 @@ public class ResourceConflictException extends RuntimeException {
private static final long serialVersionUID = 1791564636123821405L;
private final Long resourceId;
public ResourceConflictException(Long resourceId, String message) {
super(message);
this.resourceId = resourceId;
super("Conflict on resourceID " + resourceId + " " + message);
}
}

View File

@@ -1,16 +1,7 @@
package it.fabioformosa.quartzmanager.api.exceptions;
import lombok.Getter;
import lombok.ToString;
@ToString
@Getter
public class TriggerNotFoundException extends Exception {
private final String name;
public TriggerNotFoundException(String name) {
super("Trigger with name " + name + " not found!");
this.name = name;
}
}

View File

@@ -1,5 +1,8 @@
package it.fabioformosa.quartzmanager.api.jobs.entities;
import lombok.Data;
import lombok.ToString;
import java.util.Date;
/**
@@ -8,6 +11,8 @@ import java.util.Date;
* @author Fabio.Formosa
*
*/
@Data
@ToString
public class LogRecord {
public enum LogType {
@@ -27,41 +32,4 @@ public class LogRecord {
date = new Date();
}
public Date getDate() {
return date;
}
public String getMessage() {
return message;
}
public String getThreadName() {
return threadName;
}
public LogType getType() {
return type;
}
public void setDate(Date date) {
this.date = date;
}
public void setMessage(String msg) {
message = msg;
}
public void setThreadName(String threadName) {
this.threadName = threadName;
}
public void setType(LogType type) {
this.type = type;
}
@Override
public String toString() {
return "LogRecord [date=" + date + ", type=" + type + ", message=" + message + "]";
}
}

View File

@@ -1,7 +1,6 @@
package it.fabioformosa.quartzmanager.api.services;
import it.fabioformosa.quartzmanager.api.dto.SimpleTriggerCommandDTO;
import it.fabioformosa.quartzmanager.api.dto.TriggerDTO;
import it.fabioformosa.quartzmanager.api.dto.SimpleTriggerDTO;
import it.fabioformosa.quartzmanager.api.exceptions.TriggerNotFoundException;
import org.quartz.*;
@@ -34,7 +33,7 @@ public class SimpleTriggerService extends AbstractSchedulerService {
return conversionService.convert(newSimpleTrigger, SimpleTriggerDTO.class);
}
public TriggerDTO rescheduleSimpleTrigger(SimpleTriggerCommandDTO triggerCommandDTO) throws SchedulerException {
public SimpleTriggerDTO rescheduleSimpleTrigger(SimpleTriggerCommandDTO triggerCommandDTO) throws SchedulerException {
SimpleTrigger newSimpleTrigger = conversionService.convert(triggerCommandDTO, SimpleTrigger.class);
TriggerKey triggerKey = TriggerKey.triggerKey(triggerCommandDTO.getTriggerName());

View File

@@ -43,7 +43,7 @@ public class WebSocketProgressNotifier implements WebhookSender<TriggerFiredBund
JobDetail jobDetail = jobExecutionContext.getJobDetail();
triggerFiredBundleDTO.setJobKey(jobDetail.getKey().getName());
triggerFiredBundleDTO.setJobClass(trigger.getClass().getSimpleName());
triggerFiredBundleDTO.setJobClass(jobDetail.getJobClass().getName());
return triggerFiredBundleDTO;
}

View File

@@ -7,8 +7,10 @@ import org.quartz.Scheduler;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
class SchedulerConfigTest {
@@ -16,11 +18,8 @@ class SchedulerConfigTest {
public static final String QUARTZ_SCHEDULER_DEFAULT_NAME = "QuartzScheduler";
@Test
void givenASchedulerName_whenTheSchedulerIsInstatiated_thenTheSchedulerHasThatName() throws Exception {
QuartzModuleProperties quartzModuleProperties = new QuartzModuleProperties();
quartzModuleProperties.getProperties().put("org.quartz.scheduler.instanceName", TEST_SCHEDULER_NAME);
List<QuartzModuleProperties> quartzModulePropertiesList = new ArrayList<>();
quartzModulePropertiesList.add(quartzModuleProperties);
void givenASchedulerName_whenTheSchedulerIsInstantiated_thenTheSchedulerHasThatName() throws Exception {
List<QuartzModuleProperties> quartzModulePropertiesList = getQuartzModulePropertiesWithASchedulerName(TEST_SCHEDULER_NAME);
SchedulerConfig schedulerConfig = new SchedulerConfig(quartzModulePropertiesList);
GenericApplicationContext applicationContext = new GenericApplicationContext();
applicationContext.refresh();
@@ -31,8 +30,16 @@ class SchedulerConfigTest {
Assertions.assertThat(scheduler.getSchedulerName()).isEqualTo(TEST_SCHEDULER_NAME);
}
private static List<QuartzModuleProperties> getQuartzModulePropertiesWithASchedulerName(String schedulerName) {
QuartzModuleProperties quartzModuleProperties = new QuartzModuleProperties();
quartzModuleProperties.getProperties().put("org.quartz.scheduler.instanceName", schedulerName);
List<QuartzModuleProperties> quartzModulePropertiesList = new ArrayList<>();
quartzModulePropertiesList.add(quartzModuleProperties);
return quartzModulePropertiesList;
}
@Test
void givenNoSchedulerName_whenTheSchedulerIsInstatiated_thenTheSchedulerHasTheDefaultName() throws Exception {
void givenNoSchedulerName_whenTheSchedulerIsInstantiated_thenTheSchedulerHasTheDefaultName() throws Exception {
QuartzModuleProperties quartzModuleProperties = new QuartzModuleProperties();
List<QuartzModuleProperties> quartzModulePropertiesList = new ArrayList<>();
quartzModulePropertiesList.add(quartzModuleProperties);
@@ -46,4 +53,32 @@ class SchedulerConfigTest {
Assertions.assertThat(scheduler.getSchedulerName()).isEqualTo(QUARTZ_SCHEDULER_DEFAULT_NAME);
}
@Test
void givenAManagedProperties_whenTheSchedulerIsInstantiated_thenTheManagedPropsHavePriority() throws Exception {
List<QuartzModuleProperties> quartzModulePropertiesList = getQuartzModulePropertiesWithASchedulerName(TEST_SCHEDULER_NAME);
SchedulerConfig schedulerConfig = new SchedulerConfig(quartzModulePropertiesList);
GenericApplicationContext applicationContext = new GenericApplicationContext();
applicationContext.refresh();
Properties managedProps = new Properties();
String overridden_scheduler_name = "OVERRIDDEN_SCHEDULER_NAME";
managedProps.put("org.quartz.scheduler.instanceName", overridden_scheduler_name);
SchedulerFactoryBean schedulerFactoryBean = schedulerConfig.schedulerFactoryBean(schedulerConfig.jobFactory(applicationContext), managedProps);
schedulerFactoryBean.afterPropertiesSet();
Scheduler scheduler = schedulerFactoryBean.getScheduler();
Assertions.assertThat(scheduler.getSchedulerName()).isEqualTo(overridden_scheduler_name);
}
@Test
void givenAnEmptyManagedPropFile_whenSchedulerConfigRuns_thenItReturnsAnEmptyPropList() throws IOException {
List<QuartzModuleProperties> quartzModulePropertiesList = getQuartzModulePropertiesWithASchedulerName(TEST_SCHEDULER_NAME);
SchedulerConfig schedulerConfig = new SchedulerConfig(quartzModulePropertiesList);
GenericApplicationContext applicationContext = new GenericApplicationContext();
applicationContext.refresh();
Properties properties = schedulerConfig.quartzProperties();
Assertions.assertThat(properties).isEmpty();
}
}

View File

@@ -0,0 +1,46 @@
package it.fabioformosa.quartzmanager.api.controllers;
import it.fabioformosa.quartzmanager.api.QuartManagerApplicationTests;
import it.fabioformosa.quartzmanager.api.controllers.utils.TestUtils;
import it.fabioformosa.quartzmanager.api.jobs.SampleJob;
import it.fabioformosa.quartzmanager.api.services.JobService;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
@ContextConfiguration(classes = {QuartManagerApplicationTests.class})
@WebMvcTest(controllers = SimpleTriggerController.class, properties = {
"quartz-manager.jobClassPackages=it.fabioformosa.quartzmanager.jobs"
})
class JobControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private JobService jobService;
@Test
void whenGetListIsCalled_thenTheSimpleJobIsReturned() throws Exception {
Mockito.when(jobService.getJobClasses()).thenReturn(List.of(SampleJob.class));
List<String> expectedJobs = List.of(SampleJob.class.getName());
mockMvc.perform(get(JobController.JOB_CONTROLLER_BASE_URL)
.contentType(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().json(TestUtils.toJson(expectedJobs)));
Mockito.verify(jobService, Mockito.times(1)).getJobClasses();
}
}

View File

@@ -0,0 +1,28 @@
package it.fabioformosa.quartzmanager.api.controllers;
import it.fabioformosa.quartzmanager.api.QuartManagerApplicationTests;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
@ContextConfiguration(classes = {QuartManagerApplicationTests.class})
@WebMvcTest(controllers = SimpleTriggerController.class, properties = {
"quartz-manager.jobClassPackages=it.fabioformosa.quartzmanager.jobs"
})
class ResourceConflictControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void whenAResourceConflictExceptionIsRaised_thenTheExceptionHandlerReturns409() throws Exception {
mockMvc.perform(
MockMvcRequestBuilders.post(TestController.TEST_CONTROLLER_BASE_URL + "/test-conflict")
.contentType(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isConflict());
}
}

View File

@@ -0,0 +1,89 @@
package it.fabioformosa.quartzmanager.api.controllers;
import it.fabioformosa.quartzmanager.api.QuartManagerApplicationTests;
import it.fabioformosa.quartzmanager.api.controllers.utils.TestUtils;
import it.fabioformosa.quartzmanager.api.dto.SchedulerDTO;
import it.fabioformosa.quartzmanager.api.enums.SchedulerStatus;
import it.fabioformosa.quartzmanager.api.services.SchedulerService;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
@ContextConfiguration(classes = {QuartManagerApplicationTests.class})
@WebMvcTest(controllers = SimpleTriggerController.class, properties = {
"quartz-manager.jobClassPackages=it.fabioformosa.quartzmanager.jobs"
})
class SchedulerControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private SchedulerService schedulerService;
@Test
void whenTheGetIsCalled_thenTheSchedulerServiceIsReturned() throws Exception {
SchedulerDTO schedulerDTO = SchedulerDTO.builder()
.name("TEST_SCHEDULER")
.instanceId("testSchedulerId")
.status(SchedulerStatus.STOPPED)
.build();
Mockito.when(schedulerService.getScheduler()).thenReturn(schedulerDTO);
mockMvc.perform(get(SchedulerController.SCHEDULER_CONTROLLER_BASE_URL)
.contentType(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.status().isOk())
.andExpect(MockMvcResultMatchers.content().json(TestUtils.toJson(schedulerDTO)));
Mockito.verify(schedulerService).getScheduler();
}
@Test
void givenAScheduler_whenTheGetPausedIsCalled_then2xxReturned() throws Exception {
mockMvc.perform(get(SchedulerController.SCHEDULER_CONTROLLER_BASE_URL + "/pause")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.status().isNoContent())
.andExpect(MockMvcResultMatchers.content().string(""));
Mockito.verify(schedulerService).standby();
}
@Test
void givenAScheduler_whenTheGetResumedIsCalled_then2xxReturned() throws Exception {
mockMvc.perform(get(SchedulerController.SCHEDULER_CONTROLLER_BASE_URL + "/resume")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.status().isNoContent())
.andExpect(MockMvcResultMatchers.content().string(""));
Mockito.verify(schedulerService).start();
}
@Test
void givenAScheduler_whenTheGetRunIsCalled_then2xxReturned() throws Exception {
mockMvc.perform(get(SchedulerController.SCHEDULER_CONTROLLER_BASE_URL + "/run")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.status().isNoContent())
.andExpect(MockMvcResultMatchers.content().string(""));
Mockito.verify(schedulerService).start();
}
@Test
void givenAScheduler_whenTheGetStoppedIsCalled_then2xxReturned() throws Exception {
mockMvc.perform(get(SchedulerController.SCHEDULER_CONTROLLER_BASE_URL + "/stop")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(MockMvcResultMatchers.status().isNoContent())
.andExpect(MockMvcResultMatchers.content().string(""));
Mockito.verify(schedulerService).shutdown();
}
}

View File

@@ -0,0 +1,19 @@
package it.fabioformosa.quartzmanager.api.controllers;
import it.fabioformosa.quartzmanager.api.exceptions.ResourceConflictException;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RequestMapping(TestController.TEST_CONTROLLER_BASE_URL)
@RestController
public class TestController {
public static final String TEST_CONTROLLER_BASE_URL = "/test-controller";
@PostMapping("/test-conflict")
public void raiseConflictException(){
throw new ResourceConflictException(1000L, "another entity has found with the same ID");
}
}

View File

@@ -0,0 +1,71 @@
package it.fabioformosa.quartzmanager.api.converters;
import it.fabioformosa.quartzmanager.api.dto.SchedulerDTO;
import it.fabioformosa.quartzmanager.api.enums.SchedulerStatus;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.convert.ConversionService;
import org.springframework.test.annotation.DirtiesContext;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@SpringBootTest
class SchedulerToSchedulerDTOTest {
@Autowired
private Scheduler scheduler;
@Autowired
private ConversionService conversionService;
@Order(1)
@Test
void givenAnActiveScheduler_whenItIsConverted_thenADtoIsReturned () throws SchedulerException {
Assertions.assertThat(scheduler.isShutdown()).isFalse();
SchedulerDTO schedulerDTO = conversionService.convert(scheduler, SchedulerDTO.class);
Assertions.assertThat(schedulerDTO).isNotNull();
Assertions.assertThat(schedulerDTO.getName()).isEqualTo(scheduler.getSchedulerName());
Assertions.assertThat(schedulerDTO.getInstanceId()).isEqualTo(scheduler.getSchedulerInstanceId());
}
@Order(2)
@Test
void givenAnActiveScheduler_whenItIsConverted_thenADtoHasAStatus () throws SchedulerException {
scheduler.start();
Assertions.assertThat(scheduler.isStarted()).isTrue();
SchedulerDTO schedulerDTO = conversionService.convert(scheduler, SchedulerDTO.class);
Assertions.assertThat(schedulerDTO.getStatus()).isEqualTo(SchedulerStatus.RUNNING);
scheduler.standby();
Assertions.assertThat(scheduler.isInStandbyMode()).isTrue();
schedulerDTO = conversionService.convert(scheduler, SchedulerDTO.class);
Assertions.assertThat(schedulerDTO.getStatus()).isEqualTo(SchedulerStatus.PAUSED);
}
@DirtiesContext
@Order(3)
@Test
void givenASchedulerInShutdown_whenItIsConverted_thenADtoIsReturnedWithNoTriggers () throws SchedulerException {
Assertions.assertThat(scheduler.isShutdown()).isFalse();
scheduler.shutdown(false);
Assertions.assertThat(scheduler.isShutdown()).isTrue();
SchedulerDTO schedulerDTO = conversionService.convert(scheduler, SchedulerDTO.class);
Assertions.assertThat(schedulerDTO).isNotNull();
Assertions.assertThat(schedulerDTO.getName()).isEqualTo(scheduler.getSchedulerName());
Assertions.assertThat(schedulerDTO.getInstanceId()).isEqualTo(scheduler.getSchedulerInstanceId());
Assertions.assertThat(schedulerDTO.getTriggerKeys()).isNull();
Assertions.assertThat(schedulerDTO.getStatus()).isEqualTo(SchedulerStatus.STOPPED);
}
}

View File

@@ -0,0 +1,33 @@
package it.fabioformosa.quartzmanager.api.dto;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import static org.junit.jupiter.api.Assertions.*;
class TriggerFiredBundleDTOTest {
@CsvSource({
"10, 100, 10",
"23, 1000, 2",
"26, 1000, 3"
})
@ParameterizedTest
void givenARepeatCount_whenTheTriggerHasFiredXTimes_thenThePercentageIsCalculatedAccordingly(int timesTriggered, int repeatCount, int expectedPercentage){
TriggerFiredBundleDTO triggerFiredBundleDTO = TriggerFiredBundleDTO.builder().build();
triggerFiredBundleDTO.setTimesTriggered(timesTriggered);
triggerFiredBundleDTO.setRepeatCount(repeatCount);
Assertions.assertThat(triggerFiredBundleDTO.getPercentage()).isEqualTo(expectedPercentage);
}
@Test
void givenAnInfiniteRecursion_whenTheTriggerHasFired10_thenThePercentageIsMinus1(){
TriggerFiredBundleDTO triggerFiredBundleDTO = TriggerFiredBundleDTO.builder().build();
triggerFiredBundleDTO.setTimesTriggered(10);
triggerFiredBundleDTO.setRepeatCount(-1);
Assertions.assertThat(triggerFiredBundleDTO.getPercentage()).isEqualTo(-1);
}
}

View File

@@ -0,0 +1,79 @@
package it.fabioformosa.quartzmanager.api.jobs;
import it.fabioformosa.quartzmanager.api.dto.TriggerFiredBundleDTO;
import it.fabioformosa.quartzmanager.api.jobs.entities.LogRecord;
import it.fabioformosa.quartzmanager.api.websockets.WebhookSender;
import org.assertj.core.api.Assertions;
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.mockito.MockitoAnnotations;
import org.quartz.*;
import static org.mockito.ArgumentMatchers.argThat;
class SampleJobTest {
@InjectMocks
private SampleJob sampleJob;
@Mock
private WebhookSender<TriggerFiredBundleDTO> webSocketProgressNotifier;
@Mock
private WebhookSender<LogRecord> webSocketLogsNotifier;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}
@Test
void givenASampleJob_whenTheJobIsExecuted_thenTheWebhookSendersAreCalled() {
JobExecutionContext jobExecutionContext = Mockito.mock(JobExecutionContext.class);
ScheduleBuilder schedulerBuilder = SimpleScheduleBuilder.simpleSchedule()
.withRepeatCount(5)
.withIntervalInMilliseconds(1000L);
JobDetail jobDetail = JobBuilder
.newJob(SampleJob.class).withIdentity(JobKey.jobKey("test-job"))
.build();
Trigger trigger = TriggerBuilder.newTrigger()
.forJob(jobDetail)
.withSchedule(schedulerBuilder)
.build();
Mockito.when(jobExecutionContext.getTrigger()).thenReturn(trigger);
Mockito.when(jobExecutionContext.getJobDetail()).thenReturn(jobDetail);
sampleJob.execute(jobExecutionContext);
Mockito.verify(webSocketLogsNotifier).send(argThat(actualLogRecord -> {
Assertions.assertThat(actualLogRecord.getMessage()).isEqualTo("Hello!");
Assertions.assertThat(actualLogRecord.getType()).isEqualTo(LogRecord.LogType.INFO);
Assertions.assertThat(actualLogRecord.getDate()).isNotNull();
Assertions.assertThat(actualLogRecord.getThreadName()).isNotNull();
return true;
}));
Mockito.verify(webSocketProgressNotifier).send(argThat(triggerFiredBundleDTO -> {
Assertions.assertThat(triggerFiredBundleDTO.getJobKey()).isEqualTo("test-job");
Assertions.assertThat(triggerFiredBundleDTO.getRepeatCount()).isEqualTo(6);
Assertions.assertThat(triggerFiredBundleDTO.getJobClass()).isEqualTo(SampleJob.class.getName());
Assertions.assertThat(triggerFiredBundleDTO.getTimesTriggered()).isZero();
Assertions.assertThat(triggerFiredBundleDTO.getNextFireTime()).isNull();
Assertions.assertThat(triggerFiredBundleDTO.getPercentage()).isZero();
Assertions.assertThat(triggerFiredBundleDTO.getFinalFireTime()).isNotNull();
Assertions.assertThat(triggerFiredBundleDTO.getPreviousFireTime()).isNull();
return true;
}));
}
@Test
void givenASampleJob_whenTheDoItMethodIsCalled_thenALogRecordIsReturned() {
JobExecutionContext jobExecutionContext = Mockito.mock(JobExecutionContext.class);
LogRecord logRecord = sampleJob.doIt(jobExecutionContext);
Assertions.assertThat(logRecord.getMessage()).isEqualTo("Hello!");
Assertions.assertThat(logRecord.getType()).isEqualTo(LogRecord.LogType.INFO);
Assertions.assertThat(logRecord.getDate()).isNotNull();
}
}

View File

@@ -0,0 +1,57 @@
package it.fabioformosa.quartzmanager.api.services;
import it.fabioformosa.quartzmanager.api.dto.SchedulerDTO;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.annotation.DirtiesContext;
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
@SpringBootTest
class SchedulerServiceIntegrationTest {
@Autowired
private SchedulerService schedulerService;
@Autowired
private Scheduler scheduler;
@Test
void givenASchedulerService_whenGetSchedulerIsCalled_thenReturnIt() throws SchedulerException {
SchedulerDTO schedulerDTO = schedulerService.getScheduler();
Assertions.assertThat(schedulerDTO).isNotNull();
Assertions.assertThat(schedulerDTO.getName()).isEqualTo(scheduler.getSchedulerName());
}
@Order(1)
@Test
void givenASchedulerService_whenTheStatusIsChange_thenTheSchedulerReflectsTheSame() throws SchedulerException {
Assertions.assertThat(scheduler.isStarted()).isFalse();
schedulerService.start();
Assertions.assertThat(scheduler.isStarted()).isTrue();
}
@Order(2)
@Test
void givenASchedulerService_whenStandByIsCalled_thenTheStandByIsPropagated() throws SchedulerException {
Assertions.assertThat(scheduler.isInStandbyMode()).isFalse();
schedulerService.standby();
Assertions.assertThat(scheduler.isInStandbyMode()).isTrue();
}
@Order(3)
@DirtiesContext(methodMode = DirtiesContext.MethodMode.AFTER_METHOD)
@Test
void givenASchedulerService_whenShutdownIsCalled_thenTheShutdownIsPropagated() throws SchedulerException {
Assertions.assertThat(scheduler.isShutdown()).isFalse();
schedulerService.start();
schedulerService.shutdown();
Assertions.assertThat(scheduler.isShutdown()).isTrue();
}
}

View File

@@ -0,0 +1,63 @@
package it.fabioformosa.quartzmanager.api.services;
import it.fabioformosa.quartzmanager.api.dto.SchedulerDTO;
import org.assertj.core.api.Assertions;
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.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.core.convert.ConversionService;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.MockitoAnnotations.openMocks;
class SchedulerServiceTest {
@InjectMocks
private SchedulerService schedulerService;
@Mock
private Scheduler scheduler;
@Mock
private ConversionService conversionService;
@BeforeEach
void setUp() {
openMocks(this);
}
@Test
void givenASchedulerService_whenGetSchedulerIsCalled_thenReturnIt(){
Mockito.when(conversionService.convert(any(Scheduler.class), eq(SchedulerDTO.class))).thenReturn(SchedulerDTO.builder()
.name("testScheduler")
.build());
SchedulerDTO schedulerDTO = schedulerService.getScheduler();
Assertions.assertThat(schedulerDTO).isNotNull();
Assertions.assertThat(schedulerDTO.getName()).isEqualTo("testScheduler");
}
@Test
void givenASchedulerService_whenStandByIsCalled_thenTheStandByIsPropagated() throws SchedulerException {
schedulerService.standby();
Mockito.verify(scheduler).standby();
}
@Test
void givenASchedulerService_whenShutdownIsCalled_thenTheShutdownIsPropagated() throws SchedulerException {
schedulerService.shutdown();
Mockito.verify(scheduler).shutdown(true);
}
@Test
void givenASchedulerService_whenStarted_thenTheSchedulerIsStarted() throws SchedulerException {
schedulerService.start();
Mockito.verify(scheduler).start();
}
}

View File

@@ -9,9 +9,7 @@ import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleTrigger;
import org.quartz.*;
import org.springframework.core.convert.ConversionService;
import java.util.Date;
@@ -45,6 +43,21 @@ class SimpleTriggerServiceTest {
Assertions.assertThat(throwable).isInstanceOf(TriggerNotFoundException.class);
}
@Test
void givenAnExistingTrigger_whenGetSimplerTriggerByNameIsCalled_thenTheDtoIsReturned() throws SchedulerException, TriggerNotFoundException {
String existing_trigger = "existing_trigger";
Mockito.when(scheduler.getTrigger(any(TriggerKey.class)))
.thenReturn(TriggerBuilder.newTrigger().withIdentity(existing_trigger).build());
Mockito.when(conversionService.convert(any(SimpleTrigger.class), eq(SimpleTriggerDTO.class)))
.thenReturn(SimpleTriggerDTO.builder()
.triggerKeyDTO(TriggerKeyDTO.builder().name(existing_trigger).build())
.build());
SimpleTriggerDTO simpleTriggerByName = simpleSchedulerService.getSimpleTriggerByName(existing_trigger);
Assertions.assertThat(simpleTriggerByName.getTriggerKeyDTO().getName()).isEqualTo(existing_trigger);
}
@Test
void givenASimpleTriggerCommandDTO_whenASimpleTriggerIsScheduled_thenATriggerDTOIsReturned() throws SchedulerException, ClassNotFoundException {
SimpleTriggerInputDTO triggerInputDTO = SimpleTriggerInputDTO.builder()
@@ -79,4 +92,41 @@ class SimpleTriggerServiceTest {
Assertions.assertThat(simpleTrigger).isEqualTo(expectedTriggerDTO);
}
@Test
void givenASimpleTriggerCommandDTO_whenASimpleTriggerIsRecheduled_thenATriggerDTOIsReturned() throws SchedulerException, ClassNotFoundException {
SimpleTriggerInputDTO triggerInputDTO = SimpleTriggerInputDTO.builder()
.jobClass("it.fabioformosa.quartzmanager.api.jobs.SampleJob")
.startDate(new Date())
.repeatInterval(5000L).repeatCount(5)
.endDate(DateUtils.addHoursToNow(1))
.build();
String simpleTriggerName = "simpleTrigger";
SimpleTriggerDTO expectedTriggerDTO = SimpleTriggerDTO.builder()
.startTime(triggerInputDTO.getStartDate())
.repeatInterval(1000)
.repeatCount(10)
.mayFireAgain(true)
.finalFireTime(triggerInputDTO.getEndDate())
.jobKeyDTO(JobKeyDTO.builder().name("MyJob").build())
.misfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_FIRE_NOW)
.triggerKeyDTO(TriggerKeyDTO.builder().name(simpleTriggerName).build())
.build();
Mockito.when(scheduler.rescheduleJob(any(), any())).thenReturn(new Date());
Mockito.when(conversionService.convert(any(), eq(SimpleTriggerDTO.class))).thenReturn(expectedTriggerDTO);
SimpleTriggerCommandDTO simpleTriggerCommandDTO = SimpleTriggerCommandDTO.builder()
.triggerName(simpleTriggerName)
.simpleTriggerInputDTO(triggerInputDTO)
.build();
SimpleTriggerDTO simpleTrigger = simpleSchedulerService.rescheduleSimpleTrigger(simpleTriggerCommandDTO);
Assertions.assertThat(simpleTrigger).isEqualTo(expectedTriggerDTO);
Mockito.verify(scheduler).rescheduleJob(any(), any());
Mockito.verify(conversionService).convert(any(), eq(SimpleTriggerDTO.class));
}
}

View File

@@ -0,0 +1,50 @@
package it.fabioformosa.quartzmanager.api.services;
import it.fabioformosa.quartzmanager.api.dto.TriggerKeyDTO;
import org.assertj.core.api.Assertions;
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.mockito.MockitoAnnotations;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.TriggerKey;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import java.util.List;
import java.util.Set;
import static org.mockito.ArgumentMatchers.any;
class TriggerServiceTest {
@InjectMocks
private TriggerService triggerService;
@Mock
private Scheduler scheduler;
@Mock
private ConversionService conversionService;
@BeforeEach
void setUp(){
MockitoAnnotations.openMocks(this);
}
@Test
void givenATrigger_whenTheyAreFecthed_TheServiceReturnsTheDtos() throws SchedulerException {
String triggerTestName = "triggerTest";
Mockito.when(scheduler.getTriggerKeys(any())).thenReturn(Set.of(TriggerKey.triggerKey(triggerTestName)));
Mockito.when(conversionService.convert(any(Set.class), any(TypeDescriptor.class), any(TypeDescriptor.class)))
.thenReturn(List.of(TriggerKeyDTO.builder().name(triggerTestName).build()));
List<TriggerKeyDTO> triggerKeyDTOs = triggerService.fetchTriggers();
Assertions.assertThat(triggerKeyDTOs).hasSize(1);
Assertions.assertThat(triggerKeyDTOs.get(0).getName()).isEqualTo(triggerTestName);
}
}

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-parent</artifactId>
<version>4.0.5</version>
<version>4.0.8</version>
</parent>
<artifactId>quartz-manager-starter-persistence</artifactId>

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-parent</artifactId>
<version>4.0.5</version>
<version>4.0.8</version>
</parent>
<artifactId>quartz-manager-starter-security</artifactId>

View File

@@ -11,6 +11,7 @@ import io.swagger.v3.oas.models.security.SecurityScheme;
import it.fabioformosa.quartzmanager.api.common.config.OpenAPIConfigConsts;
import it.fabioformosa.quartzmanager.api.common.config.QuartzManagerPaths;
import it.fabioformosa.quartzmanager.api.security.properties.JwtSecurityProperties;
import lombok.Generated;
import lombok.extern.slf4j.Slf4j;
import org.springdoc.core.customizers.OpenApiCustomiser;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -24,6 +25,7 @@ import java.util.Arrays;
@Slf4j
@ConditionalOnProperty(name = "quartz-manager.oas.enabled")
@Configuration
@Generated
public class SecurityOpenApiConfig {
@Order(Ordered.HIGHEST_PRECEDENCE)

View File

@@ -1,51 +0,0 @@
package it.fabioformosa.quartzmanager.api.security.helpers.impl;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class AjaxAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
public class AjaxLoginAuthSuccessHandler extends SimpleUrlAuthenticationSuccessHandler
implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) {
response.setStatus(HttpServletResponse.SC_OK);
super.clearAuthenticationAttributes(request);
}
}
public AjaxAuthenticationFilter(AuthenticationManager authenticationManager) {
setAuthenticationManager(authenticationManager);
setUsernameParameter("ajaxUsername");
setPasswordParameter("ajaxPassword");
setPostOnly(true);
setFilterProcessesUrl("/ajaxLogin");
setAuthenticationSuccessHandler(new AjaxLoginAuthSuccessHandler());
}
/**
* Removes temporary authentication-related data which may have been stored
* in the session during the authentication process.
*/
protected final void clearAuthenticationAttributes(HttpServletRequest request) {
HttpSession session = request.getSession(false);
if (session == null)
return;
session.removeAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
}
}

View File

@@ -1,7 +1,9 @@
package it.fabioformosa.quartzmanager.api.security.helpers.impl;
import lombok.EqualsAndHashCode;
import org.springframework.security.authentication.AbstractAuthenticationToken;
@EqualsAndHashCode
public class AnonAuthentication extends AbstractAuthenticationToken {
private static final long serialVersionUID = 1L;
@@ -9,15 +11,6 @@ public class AnonAuthentication extends AbstractAuthenticationToken {
super( null );
}
@Override
public boolean equals( Object obj ) {
if ( this == obj )
return true;
if ( obj == null )
return false;
return getClass() == obj.getClass();
}
@Override
public Object getCredentials() {
return null;
@@ -28,11 +21,6 @@ public class AnonAuthentication extends AbstractAuthenticationToken {
return null;
}
@Override
public int hashCode() {
return 7;
}
@Override
public boolean isAuthenticated() {
return true;

View File

@@ -4,7 +4,7 @@
<parent>
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-parent</artifactId>
<version>4.0.5</version>
<version>4.0.8</version>
</parent>
<artifactId>quartz-manager-starter-ui</artifactId>
@@ -19,8 +19,8 @@
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>9</java.version>
<frontend.folderName>quartz-manager-frontend</frontend.folderName>
<node.version>v10.16.3</node.version>
<npm.version>6.9.0</npm.version>
<node.version>v16.14.1</node.version>
<npm.version>8.19.3</npm.version>
</properties>
<dependencies>

View File

@@ -5,7 +5,7 @@
<parent>
<groupId>it.fabioformosa.quartz-manager</groupId>
<artifactId>quartz-manager-parent</artifactId>
<version>4.0.5</version>
<version>4.0.8</version>
</parent>
<artifactId>quartz-manager-web-showcase</artifactId>

View File

@@ -1,12 +1,14 @@
package it.fabioformosa;
import lombok.Generated;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@Generated
@SpringBootApplication
public class QuartManagerDemoApplication {
public class QuartzManagerDemoApplication {
public static void main(String[] args) {
SpringApplication.run(QuartManagerDemoApplication.class, args);
SpringApplication.run(QuartzManagerDemoApplication.class, args);
}
}

View File

@@ -1,5 +1,6 @@
package it.fabioformosa;
import lombok.Generated;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@@ -9,11 +10,12 @@ import org.springframework.boot.web.servlet.support.SpringBootServletInitializer
* @author Fabio Formosa
*
*/
@Generated
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(QuartManagerDemoApplication.class);
return application.sources(QuartzManagerDemoApplication.class);
}
}

View File

@@ -3,11 +3,13 @@ package it.fabioformosa;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import lombok.Generated;
import org.springdoc.core.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@Generated
public class WebShowcaseOpenApiConfig {
@Bean

View File

@@ -2,6 +2,7 @@ package it.fabioformosa.quartzmanager.controllers;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Operation;
import lombok.Generated;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
@@ -13,6 +14,7 @@ import org.springframework.web.bind.annotation.RestController;
@Hidden
@RestController
@RequestMapping
@Generated
public class HealthCheckController {
@ResponseStatus(code = HttpStatus.OK)

View File

@@ -1,6 +1,7 @@
package it.fabioformosa.quartzmanager.controllers;
import io.swagger.v3.oas.annotations.Operation;
import lombok.Generated;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
@@ -15,12 +16,12 @@ import javax.servlet.http.HttpSession;
@Controller
@RequestMapping("/session")
@Generated
public class SessionController {
private final Logger log = LoggerFactory.getLogger(SessionController.class);
@GetMapping("/invalidate")
//@PreAuthorize("hasAuthority('ADMIN')") TODO
@ResponseStatus(HttpStatus.NO_CONTENT)
@Operation(hidden = true)
public void invalidateSession(HttpSession session) {
@@ -29,7 +30,6 @@ public class SessionController {
}
@GetMapping("/refresh")
// @PreAuthorize("hasAuthority('ADMIN')") TODO
@Operation(hidden = true)
public HttpEntity<Void> refreshSession(HttpSession session) {
return new ResponseEntity<>(HttpStatus.OK);

View File

@@ -2,6 +2,7 @@ package it.fabioformosa.quartzmanager.jobs.tests;
import it.fabioformosa.quartzmanager.api.jobs.AbstractQuartzManagerJob;
import it.fabioformosa.quartzmanager.api.jobs.entities.LogRecord;
import lombok.Generated;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobExecutionContext;
@@ -15,6 +16,7 @@ import org.quartz.JobExecutionContext;
*/
@Slf4j
@NoArgsConstructor
@Generated
public class MisfireTestJob extends AbstractQuartzManagerJob {
private long sleepPeriodInMs = 10 * 1000L;

View File

@@ -4,9 +4,9 @@ import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.web.WebAppConfiguration;
@SpringBootTest(classes = QuartManagerDemoApplication.class)
@SpringBootTest(classes = QuartzManagerDemoApplication.class)
@WebAppConfiguration
public class QuartManagerApplicationTests {
class QuartManagerApplicationTests {
@Test
void contextLoads() {