mirror of
https://github.com/fabioformosa/quartz-manager.git
synced 2026-05-15 14:20:30 +09:00
Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e1e1bdbd01 | ||
|
|
6757511de3 | ||
|
|
f5b717ec36 | ||
|
|
6b491d9949 | ||
|
|
cf382db49f | ||
|
|
ee2f80e582 | ||
|
|
4453d62515 | ||
|
|
72068818d7 | ||
|
|
92bb94b9fa | ||
|
|
7a2098e9ce | ||
|
|
56b81f4f48 | ||
|
|
82c060c2a7 | ||
|
|
fba35b796d | ||
|
|
5f6fc1fa6f | ||
|
|
a9c227cd05 | ||
|
|
a8330e062f | ||
|
|
30d0cbf6de | ||
|
|
3063e08eb3 | ||
|
|
f7054b160f | ||
|
|
98b5d0e37a | ||
|
|
6348bac11a | ||
|
|
c2a26c97a8 | ||
|
|
cd6e01109b | ||
|
|
7553efdc3b | ||
|
|
6578dc312a | ||
|
|
c490a7ab28 | ||
|
|
0969a406c6 | ||
|
|
eed3021373 | ||
|
|
5b33bd4dca | ||
|
|
6d22207e27 | ||
|
|
249cf49873 | ||
|
|
b17d487c8b | ||
|
|
fedb2b50b6 | ||
|
|
4b18313e2d | ||
|
|
8387f587b3 | ||
|
|
c7b64dbdf3 | ||
|
|
085d61cf29 | ||
|
|
e6cf2e9390 | ||
|
|
4bcea96789 | ||
|
|
866062bdcb | ||
|
|
53a54ddbda | ||
|
|
8bdb85b878 | ||
|
|
9a26be41a8 | ||
|
|
5cf39b3861 | ||
|
|
99d87636d2 | ||
|
|
bccd50ac4a | ||
|
|
3bb30accfd | ||
|
|
5d2b71652c | ||
|
|
b70af4dafe | ||
|
|
a95cf20c6b | ||
|
|
b58a5dbe9f | ||
|
|
f7222d65ae |
39
README.md
39
README.md
@@ -1,6 +1,23 @@
|
||||
[](https://github.com/fabioformosa/quartz-manager/actions/workflows/maven.yml)
|
||||
[](https://github.com/fabioformosa/quartz-manager/actions/workflows/npm.yml)
|
||||
[](https://maven-badges.herokuapp.com/maven-central/it.fabioformosa.quartz-manager/quartz-manager-starter-api)
|
||||
[](https://maven-badges.herokuapp.com/maven-central/it.fabioformosa.quartz-manager/quartz-manager-starter-api)
|
||||
[](https://sonarcloud.io/summary/new_code?id=fabioformosa_quartz-manager) [](https://sonarcloud.io/summary/new_code?id=fabioformosa_quartz-manager) [](https://sonarcloud.io/summary/new_code?id=fabioformosa_quartz-manager) [](https://sonarcloud.io/summary/new_code?id=fabioformosa_quartz-manager)
|
||||
|
||||
|
||||
[QUARTZ MANAGER](https://github.com/fabioformosa/quartz-manager#quartz-manager)
|
||||
[Quartz Manager UI](https://github.com/fabioformosa/quartz-manager#quartz-manager-ui)
|
||||
[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)
|
||||
[Quartz Manager Starter API Lib](https://github.com/fabioformosa/quartz-manager#quartz-manager-starter-api-lib)
|
||||
[Quartz Manager Starter UI Lib](https://github.com/fabioformosa/quartz-manager#quartz-manager-starter-ui-lib)
|
||||
[Quartz Manager Starter Security Lib](https://github.com/fabioformosa/quartz-manager#quartz-manager-starter-security-lib)
|
||||
[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.3</version>
|
||||
<version>4.0.5</version>
|
||||
</dependency>
|
||||
```
|
||||
#### Gradle
|
||||
```
|
||||
implementation group: 'it.fabioformosa.quartz-manager', name: 'quartz-manager-starter-api', version: '4.0.3'
|
||||
implementation group: 'it.fabioformosa.quartz-manager', name: 'quartz-manager-starter-api', version: '4.0.5'
|
||||
```
|
||||
|
||||
### 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.3</version>
|
||||
<version>4.0.5</version>
|
||||
</dependency>
|
||||
```
|
||||
#### Gradle
|
||||
```
|
||||
implementation group: 'it.fabioformosa.quartz-manager', name: 'quartz-manager-starter-ui', version: '4.0.3'
|
||||
implementation group: 'it.fabioformosa.quartz-manager', name: 'quartz-manager-starter-ui', version: '4.0.5'
|
||||
```
|
||||
|
||||
### 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.3</version>
|
||||
<version>4.0.5</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
#### Gradle
|
||||
|
||||
```
|
||||
compile group: 'it.fabioformosa.quartz-manager', name: 'quartz-manager-starter-security', version: '4.0.3'
|
||||
compile group: 'it.fabioformosa.quartz-manager', name: 'quartz-manager-starter-security', version: '4.0.5'
|
||||
```
|
||||
|
||||
|
||||
@@ -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.3</version>
|
||||
<version>4.0.5</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
#### Gradle
|
||||
|
||||
```
|
||||
compile group: 'it.fabioformosa.quartz-manager', name: 'quartz-manager-starter-persistence', version: '4.0.3'
|
||||
compile group: 'it.fabioformosa.quartz-manager', name: 'quartz-manager-starter-persistence', version: '4.0.5'
|
||||
```
|
||||
|
||||
### Quartz Manager Persistence Lib - App Props
|
||||
@@ -245,7 +262,7 @@ compile group: 'it.fabioformosa.quartz-manager', name: 'quartz-manager-starter-p
|
||||
|
||||
## Examples
|
||||
|
||||
You can find some examples of different scenarios of projects which import Quartz Manager at the repository [quartz-manager-use-cases] (https://github.com/fabioformosa/quartz-manager-use-cases)
|
||||
You can find some examples of different scenarios of projects which import Quartz Manager at the repository [quartz-manager-use-cases](https://github.com/fabioformosa/quartz-manager-use-cases)
|
||||
* *simply-spring* - tipical scenario in which you create a minimal spring project from scratch dedicated to launch your scheduled jobs. Imported libraries: Quartz Manager API, Quartz Manager UI and Quartz Manager Security.
|
||||
* *simply-spring-no-security* - as simple-spring, without the security. Imported libraries: Quartz Manager API, Quartz Manager UI.
|
||||
* *existing-security-cookie-based* - It demonstrates how Quartz Manager stays under the security of your project, in case of an auth model based on cookies. Imported libraries: Quartz Manager API, Quartz Manager UI.
|
||||
|
||||
2
quartz-manager-parent/lombok.config
Normal file
2
quartz-manager-parent/lombok.config
Normal file
@@ -0,0 +1,2 @@
|
||||
config.stopBubbling = true
|
||||
lombok.addLombokGeneratedAnnotation = true
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-parent</artifactId>
|
||||
<version>4.0.4</version>
|
||||
<version>4.0.6</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.4</version>
|
||||
<version>4.0.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-starter-api</artifactId>
|
||||
<version>4.0.4</version>
|
||||
<version>4.0.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-starter-security</artifactId>
|
||||
<version>4.0.4</version>
|
||||
<version>4.0.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-starter-persistence</artifactId>
|
||||
<version>4.0.4</version>
|
||||
<version>4.0.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-starter-ui</artifactId>
|
||||
<version>4.0.4</version>
|
||||
<version>4.0.6</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>
|
||||
|
||||
@@ -179,17 +208,6 @@
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.sonatype.plugins</groupId>
|
||||
<artifactId>nexus-staging-maven-plugin</artifactId>
|
||||
<version>${nexus-staging-maven-plugin.version}</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<serverId>ossrh</serverId>
|
||||
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
|
||||
<autoReleaseAfterClose>true</autoReleaseAfterClose>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
@@ -233,6 +251,17 @@
|
||||
</distributionManagement>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.sonatype.plugins</groupId>
|
||||
<artifactId>nexus-staging-maven-plugin</artifactId>
|
||||
<version>${nexus-staging-maven-plugin.version}</version>
|
||||
<extensions>true</extensions>
|
||||
<configuration>
|
||||
<serverId>ossrh</serverId>
|
||||
<nexusUrl>https://oss.sonatype.org/</nexusUrl>
|
||||
<autoReleaseAfterClose>true</autoReleaseAfterClose>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-gpg-plugin</artifactId>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-parent</artifactId>
|
||||
<version>4.0.4</version>
|
||||
<version>4.0.6</version>
|
||||
</parent>
|
||||
<artifactId>quartz-manager-common</artifactId>
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-parent</artifactId>
|
||||
<version>4.0.4</version>
|
||||
<version>4.0.6</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>
|
||||
@@ -106,6 +106,10 @@
|
||||
<artifactId>reflections</artifactId>
|
||||
<version>0.10.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- QUARTZ -->
|
||||
<dependency>
|
||||
@@ -118,34 +122,6 @@
|
||||
<version>1.3.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Reactor -->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>io.projectreactor</groupId>-->
|
||||
<!-- <artifactId>reactor-core</artifactId>-->
|
||||
<!-- </dependency>-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>io.projectreactor</groupId>-->
|
||||
<!-- <artifactId>reactor-net</artifactId>-->
|
||||
<!-- <version>2.0.8.RELEASE</version>-->
|
||||
<!-- </dependency>-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>io.projectreactor.spring</groupId>-->
|
||||
<!-- <artifactId>reactor-spring-context</artifactId>-->
|
||||
<!-- <version>2.0.7.RELEASE</version>-->
|
||||
<!-- </dependency>-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>io.netty</groupId>-->
|
||||
<!-- <artifactId>netty-all</artifactId>-->
|
||||
<!-- </dependency>-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>org.springframework.boot</groupId>-->
|
||||
<!-- <artifactId>spring-boot-starter-aop</artifactId>-->
|
||||
<!-- </dependency>-->
|
||||
<dependency>
|
||||
<groupId>org.yaml</groupId>
|
||||
<artifactId>snakeyaml</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- OAS -->
|
||||
<dependency>
|
||||
<groupId>org.springdoc</groupId>
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -23,6 +23,5 @@ public class JobKeyToJobDetailDTO extends AbstractBaseConverterToDTO<JobKey, Job
|
||||
JobDetail jobDetail = scheduler.getJobDetail(jobKey);
|
||||
jobDetailDTO.setJobClassName(jobDetail.getJobClass().getName());
|
||||
jobDetailDTO.setDescription(jobDetail.getDescription());
|
||||
//jobDetail.getJobDataMap();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 + "]";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
mock-maker-inline
|
||||
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-parent</artifactId>
|
||||
<version>4.0.4</version>
|
||||
<version>4.0.6</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>quartz-manager-starter-persistence</artifactId>
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-parent</artifactId>
|
||||
<version>4.0.4</version>
|
||||
<version>4.0.6</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>quartz-manager-starter-security</artifactId>
|
||||
|
||||
@@ -34,9 +34,6 @@ import org.springframework.security.web.AuthenticationEntryPoint;
|
||||
import org.springframework.security.web.SecurityFilterChain;
|
||||
import org.springframework.security.web.authentication.HttpStatusEntryPoint;
|
||||
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
|
||||
import org.springframework.web.cors.CorsConfiguration;
|
||||
import org.springframework.web.cors.CorsConfigurationSource;
|
||||
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -101,7 +98,9 @@ public class QuartzManagerSecurityConfig {
|
||||
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
@Bean(name = "quartzManagerFilterChain")
|
||||
public SecurityFilterChain filterChain(HttpSecurity http, @Qualifier("quartzManagerInMemoryAuthentication") InMemoryUserDetailsManager userDetailsService, AuthenticationManager authenticationManager) throws Exception {
|
||||
public SecurityFilterChain filterChain(HttpSecurity http,
|
||||
@Qualifier("quartzManagerInMemoryAuthentication") InMemoryUserDetailsManager userDetailsService,
|
||||
AuthenticationManager authenticationManager) throws Exception {
|
||||
http.antMatcher(QUARTZ_MANAGER_API_ANT_MATCHER).csrf().disable() //
|
||||
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() //
|
||||
.exceptionHandling().authenticationEntryPoint(restAuthEntryPoint()).and() //
|
||||
@@ -128,14 +127,6 @@ public class QuartzManagerSecurityConfig {
|
||||
};
|
||||
}
|
||||
|
||||
@Bean(name = "quartzManagerCorsConfigurationSource")
|
||||
public CorsConfigurationSource corsConfigurationSource() {
|
||||
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
|
||||
source.registerCorsConfiguration(QUARTZ_MANAGER_API_ANT_MATCHER, new CorsConfiguration().applyPermitDefaultValues());
|
||||
source.registerCorsConfiguration(QUARTZ_MANAGER_UI_ANT_MATCHER, new CorsConfiguration().applyPermitDefaultValues());
|
||||
return source;
|
||||
}
|
||||
|
||||
public LoginConfigurer formLoginConfigurer() {
|
||||
JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler = jwtAuthenticationSuccessHandler();
|
||||
AuthenticationSuccessHandler authenticationSuccessHandler = new AuthenticationSuccessHandler(jwtAuthenticationSuccessHandler);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -20,13 +20,14 @@ import static it.fabioformosa.quartzmanager.api.common.config.QuartzManagerPaths
|
||||
@RequestMapping(value = QUARTZ_MANAGER_AUTH_PATH, produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
public class UserController {
|
||||
|
||||
public static final String WHOAMI_URL = "/whoami";
|
||||
|
||||
@GetMapping("/whoami")
|
||||
@GetMapping(WHOAMI_URL)
|
||||
public ResponseEntity<Object> getLoggedUser() {
|
||||
SecurityContext context = SecurityContextHolder.getContext();
|
||||
if (context != null && context.getAuthentication() != null)
|
||||
return new ResponseEntity<>(context.getAuthentication().getPrincipal(), HttpStatus.OK);
|
||||
return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
|
||||
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -1,24 +1,38 @@
|
||||
package it.fabioformosa.quartzmanager.api.security.properties;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Base64;
|
||||
|
||||
|
||||
@Configuration
|
||||
@ConfigurationProperties(prefix = "quartz-manager.security.jwt")
|
||||
@Data
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
public class JwtSecurityProperties {
|
||||
private String secret = RandomStringUtils.randomAlphabetic(10);
|
||||
private String secret;
|
||||
|
||||
private long expirationInSec = 28800;
|
||||
|
||||
private CookieStrategy cookieStrategy = new CookieStrategy();
|
||||
private HeaderStrategy headerStrategy = new HeaderStrategy();
|
||||
|
||||
public JwtSecurityProperties() {
|
||||
SecureRandom random = new SecureRandom();
|
||||
byte[] bytes = new byte[20];
|
||||
random.nextBytes(bytes);
|
||||
Base64.Encoder encoder = Base64.getUrlEncoder().withoutPadding();
|
||||
secret = encoder.encodeToString(bytes);
|
||||
}
|
||||
|
||||
@Data
|
||||
public static class CookieStrategy {
|
||||
private boolean enabled = false;
|
||||
|
||||
@@ -2,6 +2,8 @@ package it.fabioformosa.quartzmanager.api.security;
|
||||
|
||||
import it.fabioformosa.quartzmanager.api.common.config.QuartzManagerPaths;
|
||||
import it.fabioformosa.quartzmanager.api.security.controllers.TestController;
|
||||
import it.fabioformosa.quartzmanager.api.security.properties.JwtSecurityProperties;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.hamcrest.core.IsNot;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
@@ -19,9 +21,8 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@TestPropertySource(properties = {
|
||||
"quartz-manager.security.jwt.enabled=true",
|
||||
"quartz-manager.security.jwt.secret=bibidibobidiboo",
|
||||
"quartz-manager.security.jwt.expiration-in-sec=28800",
|
||||
"quartz-manager.security.jwt.expiration-in-sec=36000",
|
||||
"quartz-manager.security.jwt.header-strategy.enabled=false",
|
||||
"quartz-manager.security.jwt.header-strategy.header=Authorization",
|
||||
"quartz-manager.security.jwt.cookie-strategy.enabled=true",
|
||||
@@ -31,11 +32,14 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||
"quartz-manager.security.accounts.in-memory.users[0].password=bar",
|
||||
"quartz-manager.security.accounts.in-memory.users[0].roles[0]=admin",
|
||||
})
|
||||
public class SecurityControllerTest {
|
||||
class SecurityControllerTest {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Autowired
|
||||
private JwtSecurityProperties jwtSecurityProperties;
|
||||
|
||||
@Test
|
||||
void givenAnAnonymousUser_whenCalledADMZController_thenShouldRaiseForbidden() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.get("/dmz"))
|
||||
@@ -72,4 +76,10 @@ public class SecurityControllerTest {
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenSecurityProps_whenTheBootstrapHasCompleted_thenJWTPropertiesShouldBeSetAccordingly() throws Exception {
|
||||
Assertions.assertThat(jwtSecurityProperties.getExpirationInSec()).isEqualTo(36000);
|
||||
Assertions.assertThat(jwtSecurityProperties.getSecret()).isEqualTo("bibidibobidiboo");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@ import org.springframework.test.context.TestPropertySource;
|
||||
@TestPropertySource(properties = {
|
||||
"quartz-manager.security.login-model.form-login-enabled = false",
|
||||
"quartz-manager.security.login-model.userpwd-filter-enabled = true",
|
||||
"quartz-manager.security.jwt.enabled=true",
|
||||
"quartz-manager.security.jwt.secret=bibidibobidiboo",
|
||||
"quartz-manager.security.jwt.expiration-in-sec=28800",
|
||||
"quartz-manager.security.jwt.header-strategy.enabled=true",
|
||||
|
||||
@@ -12,7 +12,6 @@ import org.springframework.test.context.TestPropertySource;
|
||||
@TestPropertySource(properties = {
|
||||
"quartz-manager.security.login-model.form-login-enabled = true",
|
||||
"quartz-manager.security.login-model.userpwd-filter-enabled = false",
|
||||
"quartz-manager.security.jwt.enabled=true",
|
||||
"quartz-manager.security.jwt.secret=bibidibobidiboo",
|
||||
"quartz-manager.security.jwt.expiration-in-sec=28800",
|
||||
"quartz-manager.security.jwt.header-strategy.enabled=true",
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package it.fabioformosa.quartzmanager.api.security.controllers;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.security.test.context.support.WithMockUser;
|
||||
import org.springframework.test.context.TestPropertySource;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
|
||||
|
||||
import static it.fabioformosa.quartzmanager.api.common.config.QuartzManagerPaths.QUARTZ_MANAGER_AUTH_PATH;
|
||||
import static it.fabioformosa.quartzmanager.api.security.controllers.UserController.WHOAMI_URL;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@SpringBootTest
|
||||
@AutoConfigureMockMvc
|
||||
@TestPropertySource(properties = {
|
||||
"quartz-manager.security.accounts.in-memory.enabled=true",
|
||||
"quartz-manager.security.accounts.in-memory.users[0].username=admin",
|
||||
"quartz-manager.security.accounts.in-memory.users[0].password=admin",
|
||||
"quartz-manager.security.accounts.in-memory.users[0].roles[0]=admin",
|
||||
})
|
||||
class UserControllerTest {
|
||||
|
||||
@Autowired
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@Test
|
||||
@WithMockUser("admin")
|
||||
void givenAnUser_whenCalledTheWhoamiEndpoint_thenShouldReturn2xx() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.get(QUARTZ_MANAGER_AUTH_PATH + WHOAMI_URL))
|
||||
.andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenAnAnonymousUser_whenCalledTheWhoamiEndpoint_thenShouldReturnNotFound() throws Exception {
|
||||
mockMvc.perform(MockMvcRequestBuilders.get(QUARTZ_MANAGER_AUTH_PATH + WHOAMI_URL))
|
||||
.andExpect(status().isUnauthorized());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package it.fabioformosa.quartzmanager.api.security.properties;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.springframework.boot.context.properties.bind.BindResult;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
|
||||
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
|
||||
|
||||
import javax.validation.Validation;
|
||||
import javax.validation.Validator;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class AbstractPropertyValidatorTest {
|
||||
protected static Validator propertyValidator;
|
||||
|
||||
@BeforeAll
|
||||
public static void setup() {
|
||||
propertyValidator = Validation.buildDefaultValidatorFactory().getValidator();
|
||||
}
|
||||
|
||||
protected static <T> T inflateConfigurationPropertyFromAMap(Map<String, String> properties, String configurationPropName, Class<T> propClass) {
|
||||
ConfigurationPropertySource source = new MapConfigurationPropertySource(properties);
|
||||
Binder binder = new Binder(source);
|
||||
BindResult<T> result = binder.bind(configurationPropName, propClass);
|
||||
if (properties != null && !properties.isEmpty()) {
|
||||
Assertions.assertThat(result.isBound()).isTrue();
|
||||
T configPropObject = result.get();
|
||||
return configPropObject;
|
||||
} else {
|
||||
try {
|
||||
return propClass.getConstructor().newInstance();
|
||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,16 @@
|
||||
package it.fabioformosa.quartzmanager.api.security.properties;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
import org.springframework.boot.context.properties.bind.BindResult;
|
||||
import org.springframework.boot.context.properties.bind.Binder;
|
||||
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
|
||||
import org.springframework.boot.context.properties.source.MapConfigurationPropertySource;
|
||||
|
||||
import javax.validation.Validation;
|
||||
import javax.validation.Validator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class InMemoryUsersValidationControllerTest {
|
||||
|
||||
private static Validator propertyValidator;
|
||||
class InMemoryUsersValidationControllerTest extends AbstractPropertyValidatorTest {
|
||||
|
||||
static Stream<Arguments> notValidInMemoryProps = Stream.of(
|
||||
Arguments.of(
|
||||
@@ -34,26 +25,15 @@ public class InMemoryUsersValidationControllerTest {
|
||||
);
|
||||
|
||||
|
||||
@BeforeAll
|
||||
public static void setup() {
|
||||
propertyValidator = Validation.buildDefaultValidatorFactory().getValidator();
|
||||
}
|
||||
|
||||
static Stream<Arguments> getNotValidInMemoryProps(){
|
||||
static Stream<Arguments> getNotValidInMemoryProps() {
|
||||
return notValidInMemoryProps;
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@MethodSource("it.fabioformosa.quartzmanager.api.security.properties.InMemoryUsersValidationControllerTest#getNotValidInMemoryProps")
|
||||
void givenAMissingUsername_whenThePropertyValidationIsApplied_thenShouldRaiseValidationError(Map<String, String> properties) {
|
||||
ConfigurationPropertySource source = new MapConfigurationPropertySource(properties);
|
||||
|
||||
Binder binder = new Binder(source);
|
||||
BindResult<InMemoryAccountProperties> result = binder.bind("quartz-manager.security.accounts.in-memory", InMemoryAccountProperties.class);
|
||||
|
||||
Assertions.assertThat(result.isBound()).isTrue();
|
||||
|
||||
InMemoryAccountProperties inMemoryAccountProperties = result.get();
|
||||
InMemoryAccountProperties inMemoryAccountProperties = inflateConfigurationPropertyFromAMap(properties,
|
||||
"quartz-manager.security.accounts.in-memory", InMemoryAccountProperties.class);
|
||||
Assertions.assertThat(propertyValidator.validate(inMemoryAccountProperties)).isNotEmpty();
|
||||
|
||||
}
|
||||
@@ -65,14 +45,9 @@ public class InMemoryUsersValidationControllerTest {
|
||||
properties.put("quartz-manager.security.accounts.in-memory.users[0].password", "bar");
|
||||
properties.put("quartz-manager.security.accounts.in-memory.users[0].roles[0]", "admin");
|
||||
|
||||
ConfigurationPropertySource source = new MapConfigurationPropertySource(properties);
|
||||
InMemoryAccountProperties inMemoryAccountProperties = inflateConfigurationPropertyFromAMap(properties,
|
||||
"quartz-manager.security.accounts.in-memory", InMemoryAccountProperties.class);
|
||||
|
||||
Binder binder = new Binder(source);
|
||||
BindResult<InMemoryAccountProperties> result = binder.bind("quartz-manager.security.accounts.in-memory", InMemoryAccountProperties.class);
|
||||
|
||||
Assertions.assertThat(result.isBound()).isTrue();
|
||||
|
||||
InMemoryAccountProperties inMemoryAccountProperties = result.get();
|
||||
Assertions.assertThat(propertyValidator.validate(inMemoryAccountProperties)).isEmpty();
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package it.fabioformosa.quartzmanager.api.security.properties;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
class JwtSecurityPropertiesTest extends AbstractPropertyValidatorTest {
|
||||
|
||||
@Test
|
||||
void givenAllJWTSecurityPropSet_whenThePropertyValidationIsApplied_thenShouldBeValid() {
|
||||
Map<String, String> properties = new HashMap<>();
|
||||
String secret = "helloworld";
|
||||
properties.put("quartz-manager.security.jwt.secret", secret);
|
||||
String expirationInSec = "36000";
|
||||
properties.put("quartz-manager.security.jwt.expirationInSec", expirationInSec);
|
||||
|
||||
JwtSecurityProperties jwtSecurityProperties = inflateConfigurationPropertyFromAMap(properties,
|
||||
"quartz-manager.security.jwt", JwtSecurityProperties.class);
|
||||
|
||||
Assertions.assertThat(propertyValidator.validate(jwtSecurityProperties)).isEmpty();
|
||||
|
||||
Assertions.assertThat(jwtSecurityProperties.getExpirationInSec()).isEqualTo(Long.valueOf(expirationInSec));
|
||||
Assertions.assertThat(jwtSecurityProperties.getSecret()).isEqualTo(secret);
|
||||
}
|
||||
|
||||
@Test
|
||||
void givenTheMandatoryJWTSecurityPropUnset_whenThePropertyValidationIsApplied_thenShouldBeSetWithDefault() {
|
||||
Map<String, String> properties = new HashMap<>();
|
||||
|
||||
JwtSecurityProperties jwtSecurityProperties = inflateConfigurationPropertyFromAMap(properties,
|
||||
"quartz-manager.security.jwt", JwtSecurityProperties.class);
|
||||
|
||||
Assertions.assertThat(jwtSecurityProperties.getExpirationInSec()).isEqualTo(28800L);
|
||||
Assertions.assertThat(jwtSecurityProperties.getSecret()).isNotBlank();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-parent</artifactId>
|
||||
<version>4.0.4</version>
|
||||
<version>4.0.6</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>quartz-manager-starter-ui</artifactId>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>it.fabioformosa.quartz-manager</groupId>
|
||||
<artifactId>quartz-manager-parent</artifactId>
|
||||
<version>4.0.4</version>
|
||||
<version>4.0.6</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>quartz-manager-web-showcase</artifactId>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -2,9 +2,10 @@ 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;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* This job can be used to test the misfire policy. It pretends to be a long
|
||||
@@ -13,20 +14,27 @@ import org.slf4j.LoggerFactory;
|
||||
* @author Fabio.Formosa
|
||||
*
|
||||
*/
|
||||
@Slf4j
|
||||
@NoArgsConstructor
|
||||
@Generated
|
||||
public class MisfireTestJob extends AbstractQuartzManagerJob {
|
||||
|
||||
private Logger log = LoggerFactory.getLogger(MisfireTestJob.class);
|
||||
private long sleepPeriodInMs = 10 * 1000L;
|
||||
|
||||
public MisfireTestJob(long sleepPeriodInMs) {
|
||||
this.sleepPeriodInMs = sleepPeriodInMs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LogRecord doIt(JobExecutionContext jobExecutionContext) {
|
||||
try {
|
||||
log.info("{} is going to sleep...", Thread.currentThread().getName());
|
||||
|
||||
Thread.sleep(10 * 1000);
|
||||
Thread.sleep(sleepPeriodInMs);
|
||||
|
||||
log.info("{} woke up!", Thread.currentThread().getName());
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
|
||||
return new LogRecord(LogRecord.LogType.INFO, "Hello!");
|
||||
|
||||
@@ -4,12 +4,12 @@ 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
|
||||
public void contextLoads() {
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package it.fabioformosa.quartzmanager.jobs.tests;
|
||||
|
||||
import it.fabioformosa.quartzmanager.api.jobs.entities.LogRecord;
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class MisfireTestJobTest {
|
||||
|
||||
@Test
|
||||
void givenAMisfireTestJob_whenIsExecuted_shoulReturnALogRecord() {
|
||||
MisfireTestJob misfireTestJob = new MisfireTestJob(10L);
|
||||
LogRecord logRecord = misfireTestJob.doIt(null);
|
||||
Assertions.assertThat(logRecord.getMessage()).isEqualTo("Hello!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user