From 4746ba9489a3b2774c3883eec4e87692e8188af8 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Mon, 1 Nov 2021 23:18:00 +0100 Subject: [PATCH 001/184] [maven-release-plugin] prepare release quartz-manager-parent-3.1.0 --- quartz-manager-parent/pom.xml | 14 +++++++------- .../quartz-manager-common/pom.xml | 2 +- .../quartz-manager-starter-api/pom.xml | 2 +- .../quartz-manager-starter-persistence/pom.xml | 2 +- .../quartz-manager-starter-security/pom.xml | 2 +- .../quartz-manager-starter-ui/pom.xml | 2 +- .../quartz-manager-web-showcase/pom.xml | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/quartz-manager-parent/pom.xml b/quartz-manager-parent/pom.xml index f6f2065..71039c3 100644 --- a/quartz-manager-parent/pom.xml +++ b/quartz-manager-parent/pom.xml @@ -9,7 +9,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.0.2-SNAPSHOT + 3.1.0 pom @@ -29,7 +29,7 @@ scm:git:git://github.com/fabioformosa/quartz-manager.git scm:git:git@github.com:fabioformosa/quartz-manager.git https://github.com/fabioformosa/quartz-manager - HEAD + quartz-manager-parent-3.1.0 @@ -53,27 +53,27 @@ it.fabioformosa.quartz-manager quartz-manager-common - 3.0.2-SNAPSHOT + 3.1.0 it.fabioformosa.quartz-manager quartz-manager-starter-api - 3.0.2-SNAPSHOT + 3.1.0 it.fabioformosa.quartz-manager quartz-manager-starter-security - 3.0.2-SNAPSHOT + 3.1.0 it.fabioformosa.quartz-manager quartz-manager-starter-persistence - 3.0.2-SNAPSHOT + 3.1.0 it.fabioformosa.quartz-manager quartz-manager-starter-ui - 3.0.2-SNAPSHOT + 3.1.0 diff --git a/quartz-manager-parent/quartz-manager-common/pom.xml b/quartz-manager-parent/quartz-manager-common/pom.xml index a51d260..100223e 100644 --- a/quartz-manager-parent/quartz-manager-common/pom.xml +++ b/quartz-manager-parent/quartz-manager-common/pom.xml @@ -3,7 +3,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.0.2-SNAPSHOT + 3.1.0 quartz-manager-common diff --git a/quartz-manager-parent/quartz-manager-starter-api/pom.xml b/quartz-manager-parent/quartz-manager-starter-api/pom.xml index 395b3f4..26609ad 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-api/pom.xml @@ -4,7 +4,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.0.2-SNAPSHOT + 3.1.0 quartz-manager-starter-api diff --git a/quartz-manager-parent/quartz-manager-starter-persistence/pom.xml b/quartz-manager-parent/quartz-manager-starter-persistence/pom.xml index 8f6908f..646529e 100644 --- a/quartz-manager-parent/quartz-manager-starter-persistence/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-persistence/pom.xml @@ -3,7 +3,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.0.2-SNAPSHOT + 3.1.0 quartz-manager-starter-persistence diff --git a/quartz-manager-parent/quartz-manager-starter-security/pom.xml b/quartz-manager-parent/quartz-manager-starter-security/pom.xml index 69533ba..b098dd5 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-security/pom.xml @@ -3,7 +3,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.0.2-SNAPSHOT + 3.1.0 quartz-manager-starter-security diff --git a/quartz-manager-parent/quartz-manager-starter-ui/pom.xml b/quartz-manager-parent/quartz-manager-starter-ui/pom.xml index 586e136..152d9e5 100644 --- a/quartz-manager-parent/quartz-manager-starter-ui/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-ui/pom.xml @@ -4,7 +4,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.0.2-SNAPSHOT + 3.1.0 quartz-manager-starter-ui diff --git a/quartz-manager-parent/quartz-manager-web-showcase/pom.xml b/quartz-manager-parent/quartz-manager-web-showcase/pom.xml index 73755dd..8920b82 100644 --- a/quartz-manager-parent/quartz-manager-web-showcase/pom.xml +++ b/quartz-manager-parent/quartz-manager-web-showcase/pom.xml @@ -5,7 +5,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.0.2-SNAPSHOT + 3.1.0 quartz-manager-web-showcase From 085d2f1706f685a267c98fbda7abe28fe7683a61 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Mon, 1 Nov 2021 23:24:43 +0100 Subject: [PATCH 002/184] [maven-release-plugin] rollback the release of quartz-manager-parent-3.1.0 --- quartz-manager-parent/pom.xml | 14 +++++++------- .../quartz-manager-common/pom.xml | 2 +- .../quartz-manager-starter-api/pom.xml | 2 +- .../quartz-manager-starter-persistence/pom.xml | 2 +- .../quartz-manager-starter-security/pom.xml | 2 +- .../quartz-manager-starter-ui/pom.xml | 2 +- .../quartz-manager-web-showcase/pom.xml | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/quartz-manager-parent/pom.xml b/quartz-manager-parent/pom.xml index 71039c3..f6f2065 100644 --- a/quartz-manager-parent/pom.xml +++ b/quartz-manager-parent/pom.xml @@ -9,7 +9,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.1.0 + 3.0.2-SNAPSHOT pom @@ -29,7 +29,7 @@ scm:git:git://github.com/fabioformosa/quartz-manager.git scm:git:git@github.com:fabioformosa/quartz-manager.git https://github.com/fabioformosa/quartz-manager - quartz-manager-parent-3.1.0 + HEAD @@ -53,27 +53,27 @@ it.fabioformosa.quartz-manager quartz-manager-common - 3.1.0 + 3.0.2-SNAPSHOT it.fabioformosa.quartz-manager quartz-manager-starter-api - 3.1.0 + 3.0.2-SNAPSHOT it.fabioformosa.quartz-manager quartz-manager-starter-security - 3.1.0 + 3.0.2-SNAPSHOT it.fabioformosa.quartz-manager quartz-manager-starter-persistence - 3.1.0 + 3.0.2-SNAPSHOT it.fabioformosa.quartz-manager quartz-manager-starter-ui - 3.1.0 + 3.0.2-SNAPSHOT diff --git a/quartz-manager-parent/quartz-manager-common/pom.xml b/quartz-manager-parent/quartz-manager-common/pom.xml index 100223e..a51d260 100644 --- a/quartz-manager-parent/quartz-manager-common/pom.xml +++ b/quartz-manager-parent/quartz-manager-common/pom.xml @@ -3,7 +3,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.1.0 + 3.0.2-SNAPSHOT quartz-manager-common diff --git a/quartz-manager-parent/quartz-manager-starter-api/pom.xml b/quartz-manager-parent/quartz-manager-starter-api/pom.xml index 26609ad..395b3f4 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-api/pom.xml @@ -4,7 +4,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.1.0 + 3.0.2-SNAPSHOT quartz-manager-starter-api diff --git a/quartz-manager-parent/quartz-manager-starter-persistence/pom.xml b/quartz-manager-parent/quartz-manager-starter-persistence/pom.xml index 646529e..8f6908f 100644 --- a/quartz-manager-parent/quartz-manager-starter-persistence/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-persistence/pom.xml @@ -3,7 +3,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.1.0 + 3.0.2-SNAPSHOT quartz-manager-starter-persistence diff --git a/quartz-manager-parent/quartz-manager-starter-security/pom.xml b/quartz-manager-parent/quartz-manager-starter-security/pom.xml index b098dd5..69533ba 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-security/pom.xml @@ -3,7 +3,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.1.0 + 3.0.2-SNAPSHOT quartz-manager-starter-security diff --git a/quartz-manager-parent/quartz-manager-starter-ui/pom.xml b/quartz-manager-parent/quartz-manager-starter-ui/pom.xml index 152d9e5..586e136 100644 --- a/quartz-manager-parent/quartz-manager-starter-ui/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-ui/pom.xml @@ -4,7 +4,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.1.0 + 3.0.2-SNAPSHOT quartz-manager-starter-ui diff --git a/quartz-manager-parent/quartz-manager-web-showcase/pom.xml b/quartz-manager-parent/quartz-manager-web-showcase/pom.xml index 8920b82..73755dd 100644 --- a/quartz-manager-parent/quartz-manager-web-showcase/pom.xml +++ b/quartz-manager-parent/quartz-manager-web-showcase/pom.xml @@ -5,7 +5,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.1.0 + 3.0.2-SNAPSHOT quartz-manager-web-showcase From e70bbcff62cdbbb0bf8f36b679180a1d6369d676 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Mon, 1 Nov 2021 23:25:44 +0100 Subject: [PATCH 003/184] [maven-release-plugin] prepare release quartz-manager-parent-3.1.0 --- quartz-manager-parent/pom.xml | 14 +++++++------- .../quartz-manager-common/pom.xml | 2 +- .../quartz-manager-starter-api/pom.xml | 2 +- .../quartz-manager-starter-persistence/pom.xml | 2 +- .../quartz-manager-starter-security/pom.xml | 2 +- .../quartz-manager-starter-ui/pom.xml | 2 +- .../quartz-manager-web-showcase/pom.xml | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/quartz-manager-parent/pom.xml b/quartz-manager-parent/pom.xml index f6f2065..71039c3 100644 --- a/quartz-manager-parent/pom.xml +++ b/quartz-manager-parent/pom.xml @@ -9,7 +9,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.0.2-SNAPSHOT + 3.1.0 pom @@ -29,7 +29,7 @@ scm:git:git://github.com/fabioformosa/quartz-manager.git scm:git:git@github.com:fabioformosa/quartz-manager.git https://github.com/fabioformosa/quartz-manager - HEAD + quartz-manager-parent-3.1.0 @@ -53,27 +53,27 @@ it.fabioformosa.quartz-manager quartz-manager-common - 3.0.2-SNAPSHOT + 3.1.0 it.fabioformosa.quartz-manager quartz-manager-starter-api - 3.0.2-SNAPSHOT + 3.1.0 it.fabioformosa.quartz-manager quartz-manager-starter-security - 3.0.2-SNAPSHOT + 3.1.0 it.fabioformosa.quartz-manager quartz-manager-starter-persistence - 3.0.2-SNAPSHOT + 3.1.0 it.fabioformosa.quartz-manager quartz-manager-starter-ui - 3.0.2-SNAPSHOT + 3.1.0 diff --git a/quartz-manager-parent/quartz-manager-common/pom.xml b/quartz-manager-parent/quartz-manager-common/pom.xml index a51d260..100223e 100644 --- a/quartz-manager-parent/quartz-manager-common/pom.xml +++ b/quartz-manager-parent/quartz-manager-common/pom.xml @@ -3,7 +3,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.0.2-SNAPSHOT + 3.1.0 quartz-manager-common diff --git a/quartz-manager-parent/quartz-manager-starter-api/pom.xml b/quartz-manager-parent/quartz-manager-starter-api/pom.xml index 395b3f4..26609ad 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-api/pom.xml @@ -4,7 +4,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.0.2-SNAPSHOT + 3.1.0 quartz-manager-starter-api diff --git a/quartz-manager-parent/quartz-manager-starter-persistence/pom.xml b/quartz-manager-parent/quartz-manager-starter-persistence/pom.xml index 8f6908f..646529e 100644 --- a/quartz-manager-parent/quartz-manager-starter-persistence/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-persistence/pom.xml @@ -3,7 +3,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.0.2-SNAPSHOT + 3.1.0 quartz-manager-starter-persistence diff --git a/quartz-manager-parent/quartz-manager-starter-security/pom.xml b/quartz-manager-parent/quartz-manager-starter-security/pom.xml index 69533ba..b098dd5 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-security/pom.xml @@ -3,7 +3,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.0.2-SNAPSHOT + 3.1.0 quartz-manager-starter-security diff --git a/quartz-manager-parent/quartz-manager-starter-ui/pom.xml b/quartz-manager-parent/quartz-manager-starter-ui/pom.xml index 586e136..152d9e5 100644 --- a/quartz-manager-parent/quartz-manager-starter-ui/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-ui/pom.xml @@ -4,7 +4,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.0.2-SNAPSHOT + 3.1.0 quartz-manager-starter-ui diff --git a/quartz-manager-parent/quartz-manager-web-showcase/pom.xml b/quartz-manager-parent/quartz-manager-web-showcase/pom.xml index 73755dd..8920b82 100644 --- a/quartz-manager-parent/quartz-manager-web-showcase/pom.xml +++ b/quartz-manager-parent/quartz-manager-web-showcase/pom.xml @@ -5,7 +5,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.0.2-SNAPSHOT + 3.1.0 quartz-manager-web-showcase From 09e9b18f96728109d87fade9400d658aaf188760 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Mon, 1 Nov 2021 23:26:06 +0100 Subject: [PATCH 004/184] [maven-release-plugin] rollback the release of quartz-manager-parent-3.1.0 --- quartz-manager-parent/pom.xml | 14 +++++++------- .../quartz-manager-common/pom.xml | 2 +- .../quartz-manager-starter-api/pom.xml | 2 +- .../quartz-manager-starter-persistence/pom.xml | 2 +- .../quartz-manager-starter-security/pom.xml | 2 +- .../quartz-manager-starter-ui/pom.xml | 2 +- .../quartz-manager-web-showcase/pom.xml | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/quartz-manager-parent/pom.xml b/quartz-manager-parent/pom.xml index 71039c3..f6f2065 100644 --- a/quartz-manager-parent/pom.xml +++ b/quartz-manager-parent/pom.xml @@ -9,7 +9,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.1.0 + 3.0.2-SNAPSHOT pom @@ -29,7 +29,7 @@ scm:git:git://github.com/fabioformosa/quartz-manager.git scm:git:git@github.com:fabioformosa/quartz-manager.git https://github.com/fabioformosa/quartz-manager - quartz-manager-parent-3.1.0 + HEAD @@ -53,27 +53,27 @@ it.fabioformosa.quartz-manager quartz-manager-common - 3.1.0 + 3.0.2-SNAPSHOT it.fabioformosa.quartz-manager quartz-manager-starter-api - 3.1.0 + 3.0.2-SNAPSHOT it.fabioformosa.quartz-manager quartz-manager-starter-security - 3.1.0 + 3.0.2-SNAPSHOT it.fabioformosa.quartz-manager quartz-manager-starter-persistence - 3.1.0 + 3.0.2-SNAPSHOT it.fabioformosa.quartz-manager quartz-manager-starter-ui - 3.1.0 + 3.0.2-SNAPSHOT diff --git a/quartz-manager-parent/quartz-manager-common/pom.xml b/quartz-manager-parent/quartz-manager-common/pom.xml index 100223e..a51d260 100644 --- a/quartz-manager-parent/quartz-manager-common/pom.xml +++ b/quartz-manager-parent/quartz-manager-common/pom.xml @@ -3,7 +3,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.1.0 + 3.0.2-SNAPSHOT quartz-manager-common diff --git a/quartz-manager-parent/quartz-manager-starter-api/pom.xml b/quartz-manager-parent/quartz-manager-starter-api/pom.xml index 26609ad..395b3f4 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-api/pom.xml @@ -4,7 +4,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.1.0 + 3.0.2-SNAPSHOT quartz-manager-starter-api diff --git a/quartz-manager-parent/quartz-manager-starter-persistence/pom.xml b/quartz-manager-parent/quartz-manager-starter-persistence/pom.xml index 646529e..8f6908f 100644 --- a/quartz-manager-parent/quartz-manager-starter-persistence/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-persistence/pom.xml @@ -3,7 +3,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.1.0 + 3.0.2-SNAPSHOT quartz-manager-starter-persistence diff --git a/quartz-manager-parent/quartz-manager-starter-security/pom.xml b/quartz-manager-parent/quartz-manager-starter-security/pom.xml index b098dd5..69533ba 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-security/pom.xml @@ -3,7 +3,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.1.0 + 3.0.2-SNAPSHOT quartz-manager-starter-security diff --git a/quartz-manager-parent/quartz-manager-starter-ui/pom.xml b/quartz-manager-parent/quartz-manager-starter-ui/pom.xml index 152d9e5..586e136 100644 --- a/quartz-manager-parent/quartz-manager-starter-ui/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-ui/pom.xml @@ -4,7 +4,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.1.0 + 3.0.2-SNAPSHOT quartz-manager-starter-ui diff --git a/quartz-manager-parent/quartz-manager-web-showcase/pom.xml b/quartz-manager-parent/quartz-manager-web-showcase/pom.xml index 8920b82..73755dd 100644 --- a/quartz-manager-parent/quartz-manager-web-showcase/pom.xml +++ b/quartz-manager-parent/quartz-manager-web-showcase/pom.xml @@ -5,7 +5,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.1.0 + 3.0.2-SNAPSHOT quartz-manager-web-showcase From d89a2af1aa022a151d5564a059fd087cb29cbc32 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Mon, 1 Nov 2021 23:28:04 +0100 Subject: [PATCH 005/184] [maven-release-plugin] prepare release 3.1.0 --- quartz-manager-parent/pom.xml | 14 +++++++------- .../quartz-manager-common/pom.xml | 2 +- .../quartz-manager-starter-api/pom.xml | 2 +- .../quartz-manager-starter-persistence/pom.xml | 2 +- .../quartz-manager-starter-security/pom.xml | 2 +- .../quartz-manager-starter-ui/pom.xml | 2 +- .../quartz-manager-web-showcase/pom.xml | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/quartz-manager-parent/pom.xml b/quartz-manager-parent/pom.xml index f6f2065..47b62ff 100644 --- a/quartz-manager-parent/pom.xml +++ b/quartz-manager-parent/pom.xml @@ -9,7 +9,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.0.2-SNAPSHOT + 3.1.0 pom @@ -29,7 +29,7 @@ scm:git:git://github.com/fabioformosa/quartz-manager.git scm:git:git@github.com:fabioformosa/quartz-manager.git https://github.com/fabioformosa/quartz-manager - HEAD + 3.1.0 @@ -53,27 +53,27 @@ it.fabioformosa.quartz-manager quartz-manager-common - 3.0.2-SNAPSHOT + 3.1.0 it.fabioformosa.quartz-manager quartz-manager-starter-api - 3.0.2-SNAPSHOT + 3.1.0 it.fabioformosa.quartz-manager quartz-manager-starter-security - 3.0.2-SNAPSHOT + 3.1.0 it.fabioformosa.quartz-manager quartz-manager-starter-persistence - 3.0.2-SNAPSHOT + 3.1.0 it.fabioformosa.quartz-manager quartz-manager-starter-ui - 3.0.2-SNAPSHOT + 3.1.0 diff --git a/quartz-manager-parent/quartz-manager-common/pom.xml b/quartz-manager-parent/quartz-manager-common/pom.xml index a51d260..100223e 100644 --- a/quartz-manager-parent/quartz-manager-common/pom.xml +++ b/quartz-manager-parent/quartz-manager-common/pom.xml @@ -3,7 +3,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.0.2-SNAPSHOT + 3.1.0 quartz-manager-common diff --git a/quartz-manager-parent/quartz-manager-starter-api/pom.xml b/quartz-manager-parent/quartz-manager-starter-api/pom.xml index 395b3f4..26609ad 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-api/pom.xml @@ -4,7 +4,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.0.2-SNAPSHOT + 3.1.0 quartz-manager-starter-api diff --git a/quartz-manager-parent/quartz-manager-starter-persistence/pom.xml b/quartz-manager-parent/quartz-manager-starter-persistence/pom.xml index 8f6908f..646529e 100644 --- a/quartz-manager-parent/quartz-manager-starter-persistence/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-persistence/pom.xml @@ -3,7 +3,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.0.2-SNAPSHOT + 3.1.0 quartz-manager-starter-persistence diff --git a/quartz-manager-parent/quartz-manager-starter-security/pom.xml b/quartz-manager-parent/quartz-manager-starter-security/pom.xml index 69533ba..b098dd5 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-security/pom.xml @@ -3,7 +3,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.0.2-SNAPSHOT + 3.1.0 quartz-manager-starter-security diff --git a/quartz-manager-parent/quartz-manager-starter-ui/pom.xml b/quartz-manager-parent/quartz-manager-starter-ui/pom.xml index 586e136..152d9e5 100644 --- a/quartz-manager-parent/quartz-manager-starter-ui/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-ui/pom.xml @@ -4,7 +4,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.0.2-SNAPSHOT + 3.1.0 quartz-manager-starter-ui diff --git a/quartz-manager-parent/quartz-manager-web-showcase/pom.xml b/quartz-manager-parent/quartz-manager-web-showcase/pom.xml index 73755dd..8920b82 100644 --- a/quartz-manager-parent/quartz-manager-web-showcase/pom.xml +++ b/quartz-manager-parent/quartz-manager-web-showcase/pom.xml @@ -5,7 +5,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.0.2-SNAPSHOT + 3.1.0 quartz-manager-web-showcase From 8218c63bbae703d48cc064b54216671ac22512a8 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Mon, 1 Nov 2021 23:28:05 +0100 Subject: [PATCH 006/184] [maven-release-plugin] prepare for next development iteration --- quartz-manager-parent/pom.xml | 14 +++++++------- .../quartz-manager-common/pom.xml | 2 +- .../quartz-manager-starter-api/pom.xml | 2 +- .../quartz-manager-starter-persistence/pom.xml | 2 +- .../quartz-manager-starter-security/pom.xml | 2 +- .../quartz-manager-starter-ui/pom.xml | 2 +- .../quartz-manager-web-showcase/pom.xml | 2 +- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/quartz-manager-parent/pom.xml b/quartz-manager-parent/pom.xml index 47b62ff..511d5c5 100644 --- a/quartz-manager-parent/pom.xml +++ b/quartz-manager-parent/pom.xml @@ -9,7 +9,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.1.0 + 3.1.1-SNAPSHOT pom @@ -29,7 +29,7 @@ scm:git:git://github.com/fabioformosa/quartz-manager.git scm:git:git@github.com:fabioformosa/quartz-manager.git https://github.com/fabioformosa/quartz-manager - 3.1.0 + HEAD @@ -53,27 +53,27 @@ it.fabioformosa.quartz-manager quartz-manager-common - 3.1.0 + 3.1.1-SNAPSHOT it.fabioformosa.quartz-manager quartz-manager-starter-api - 3.1.0 + 3.1.1-SNAPSHOT it.fabioformosa.quartz-manager quartz-manager-starter-security - 3.1.0 + 3.1.1-SNAPSHOT it.fabioformosa.quartz-manager quartz-manager-starter-persistence - 3.1.0 + 3.1.1-SNAPSHOT it.fabioformosa.quartz-manager quartz-manager-starter-ui - 3.1.0 + 3.1.1-SNAPSHOT diff --git a/quartz-manager-parent/quartz-manager-common/pom.xml b/quartz-manager-parent/quartz-manager-common/pom.xml index 100223e..0a7ecb7 100644 --- a/quartz-manager-parent/quartz-manager-common/pom.xml +++ b/quartz-manager-parent/quartz-manager-common/pom.xml @@ -3,7 +3,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.1.0 + 3.1.1-SNAPSHOT quartz-manager-common diff --git a/quartz-manager-parent/quartz-manager-starter-api/pom.xml b/quartz-manager-parent/quartz-manager-starter-api/pom.xml index 26609ad..47bf449 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-api/pom.xml @@ -4,7 +4,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.1.0 + 3.1.1-SNAPSHOT quartz-manager-starter-api diff --git a/quartz-manager-parent/quartz-manager-starter-persistence/pom.xml b/quartz-manager-parent/quartz-manager-starter-persistence/pom.xml index 646529e..9023faa 100644 --- a/quartz-manager-parent/quartz-manager-starter-persistence/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-persistence/pom.xml @@ -3,7 +3,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.1.0 + 3.1.1-SNAPSHOT quartz-manager-starter-persistence diff --git a/quartz-manager-parent/quartz-manager-starter-security/pom.xml b/quartz-manager-parent/quartz-manager-starter-security/pom.xml index b098dd5..fcd5ed0 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-security/pom.xml @@ -3,7 +3,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.1.0 + 3.1.1-SNAPSHOT quartz-manager-starter-security diff --git a/quartz-manager-parent/quartz-manager-starter-ui/pom.xml b/quartz-manager-parent/quartz-manager-starter-ui/pom.xml index 152d9e5..c17cbd8 100644 --- a/quartz-manager-parent/quartz-manager-starter-ui/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-ui/pom.xml @@ -4,7 +4,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.1.0 + 3.1.1-SNAPSHOT quartz-manager-starter-ui diff --git a/quartz-manager-parent/quartz-manager-web-showcase/pom.xml b/quartz-manager-parent/quartz-manager-web-showcase/pom.xml index 8920b82..16a5b8b 100644 --- a/quartz-manager-parent/quartz-manager-web-showcase/pom.xml +++ b/quartz-manager-parent/quartz-manager-web-showcase/pom.xml @@ -5,7 +5,7 @@ it.fabioformosa.quartz-manager quartz-manager-parent - 3.1.0 + 3.1.1-SNAPSHOT quartz-manager-web-showcase From 1cd7f605e3cc92d1fa48325a83bbeb5e14652e22 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Sat, 6 Nov 2021 01:17:10 +0100 Subject: [PATCH 007/184] upgraded spring boot version to 2.5.6 --- quartz-manager-parent/pom.xml | 390 +++++++++--------- .../quartz-manager-starter-security/pom.xml | 114 ++--- .../quartz-manager-starter-ui/pom.xml | 278 ++++++------- .../quartz-manager-web-showcase/pom.xml | 8 +- 4 files changed, 395 insertions(+), 395 deletions(-) diff --git a/quartz-manager-parent/pom.xml b/quartz-manager-parent/pom.xml index 511d5c5..01b22d5 100644 --- a/quartz-manager-parent/pom.xml +++ b/quartz-manager-parent/pom.xml @@ -1,195 +1,195 @@ - - 4.0.0 - - - org.springframework.boot - spring-boot-starter-parent - 2.3.4.RELEASE - - - it.fabioformosa.quartz-manager - quartz-manager-parent - 3.1.1-SNAPSHOT - - pom - - Quartz Manager - API and UI Manager for Quartz Scheduler - - https://github.com/fabioformosa/quartz-manager - - - - Apache License 2.0 - https://github.com/fabioformosa/quartz-manager/blob/master/LICENSE - - - - - scm:git:git://github.com/fabioformosa/quartz-manager.git - scm:git:git@github.com:fabioformosa/quartz-manager.git - https://github.com/fabioformosa/quartz-manager - HEAD - - - - - Fabio Formosa - https://github.com/fabioformosa - - - - - quartz-manager-starter-api - quartz-manager-starter-ui - quartz-manager-starter-security - quartz-manager-web-showcase - quartz-manager-starter-persistence - quartz-manager-common - - - - - - it.fabioformosa.quartz-manager - quartz-manager-common - 3.1.1-SNAPSHOT - - - it.fabioformosa.quartz-manager - quartz-manager-starter-api - 3.1.1-SNAPSHOT - - - it.fabioformosa.quartz-manager - quartz-manager-starter-security - 3.1.1-SNAPSHOT - - - it.fabioformosa.quartz-manager - quartz-manager-starter-persistence - 3.1.1-SNAPSHOT - - - it.fabioformosa.quartz-manager - quartz-manager-starter-ui - 3.1.1-SNAPSHOT - - - - - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - 1.8 - 1.8 - - - - org.apache.maven.plugins - maven-source-plugin - - - attach-sources - - jar - - - - - - org.apache.maven.plugins - maven-release-plugin - 2.5.3 - - clean - build-webjar - true - false - forked-path - -Dgpg.passphrase=${gpg.passphrase} - - - - org.apache.maven.scm - maven-scm-provider-gitexe - 1.9.5 - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.7 - true - - ossrh - https://oss.sonatype.org/ - true - - - - org.apache.maven.plugins - maven-javadoc-plugin - - - attach-javadocs - - jar - - - none - - - - - - - - - - - release-sign-artifacts - - - performRelease - true - - - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.6 - - - sign-artifacts - verify - - sign - - - - - - - - - - \ No newline at end of file + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.5.6 + + + it.fabioformosa.quartz-manager + quartz-manager-parent + 3.1.1-SNAPSHOT + + pom + + Quartz Manager + API and UI Manager for Quartz Scheduler + + https://github.com/fabioformosa/quartz-manager + + + + Apache License 2.0 + https://github.com/fabioformosa/quartz-manager/blob/master/LICENSE + + + + + scm:git:git://github.com/fabioformosa/quartz-manager.git + scm:git:git@github.com:fabioformosa/quartz-manager.git + https://github.com/fabioformosa/quartz-manager + HEAD + + + + + Fabio Formosa + https://github.com/fabioformosa + + + + + quartz-manager-starter-api + quartz-manager-starter-ui + quartz-manager-starter-security + quartz-manager-web-showcase + quartz-manager-starter-persistence + quartz-manager-common + + + + + + it.fabioformosa.quartz-manager + quartz-manager-common + 3.1.1-SNAPSHOT + + + it.fabioformosa.quartz-manager + quartz-manager-starter-api + 3.1.1-SNAPSHOT + + + it.fabioformosa.quartz-manager + quartz-manager-starter-security + 3.1.1-SNAPSHOT + + + it.fabioformosa.quartz-manager + quartz-manager-starter-persistence + 3.1.1-SNAPSHOT + + + it.fabioformosa.quartz-manager + quartz-manager-starter-ui + 3.1.1-SNAPSHOT + + + + + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar + + + + + + org.apache.maven.plugins + maven-release-plugin + 2.5.3 + + clean + build-webjar + true + false + forked-path + -Dgpg.passphrase=${gpg.passphrase} + + + + org.apache.maven.scm + maven-scm-provider-gitexe + 1.9.5 + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + 1.6.7 + true + + ossrh + https://oss.sonatype.org/ + true + + + + org.apache.maven.plugins + maven-javadoc-plugin + + + attach-javadocs + + jar + + + none + + + + + + + + + + + release-sign-artifacts + + + performRelease + true + + + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.6 + + + sign-artifacts + verify + + sign + + + + + + + + + + diff --git a/quartz-manager-parent/quartz-manager-starter-security/pom.xml b/quartz-manager-parent/quartz-manager-starter-security/pom.xml index fcd5ed0..5d80594 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-security/pom.xml @@ -1,57 +1,57 @@ - - 4.0.0 - - it.fabioformosa.quartz-manager - quartz-manager-parent - 3.1.1-SNAPSHOT - - - quartz-manager-starter-security - - Quartz Manager Starter Security - Security Layer for Quartz Manager. Import it in your spring webapp - - https://github.com/fabioformosa/quartz-manager - - ${basedir}/../.. - UTF-8 - UTF-8 - 1.8 - - - - - org.springframework.boot - spring-boot-starter-security - - - org.springframework.boot - spring-boot-starter-data-jpa - - - org.springframework.boot - spring-boot-configuration-processor - true - - - io.jsonwebtoken - jjwt - 0.9.0 - - - org.apache.commons - commons-lang3 - - - org.projectlombok - lombok - provided - - - javax.servlet - javax.servlet-api - provided - - - - \ No newline at end of file + + 4.0.0 + + it.fabioformosa.quartz-manager + quartz-manager-parent + 3.1.1-SNAPSHOT + + + quartz-manager-starter-security + + Quartz Manager Starter Security + Security Layer for Quartz Manager. Import it in your spring webapp + + https://github.com/fabioformosa/quartz-manager + + ${basedir}/../.. + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.boot + spring-boot-configuration-processor + true + + + io.jsonwebtoken + jjwt + 0.9.0 + + + org.apache.commons + commons-lang3 + + + org.projectlombok + lombok + provided + + + javax.servlet + javax.servlet-api + provided + + + + diff --git a/quartz-manager-parent/quartz-manager-starter-ui/pom.xml b/quartz-manager-parent/quartz-manager-starter-ui/pom.xml index c17cbd8..168ccb5 100644 --- a/quartz-manager-parent/quartz-manager-starter-ui/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-ui/pom.xml @@ -1,139 +1,139 @@ - - - 4.0.0 - - it.fabioformosa.quartz-manager - quartz-manager-parent - 3.1.1-SNAPSHOT - - - quartz-manager-starter-ui - - Quartz Manager UI webjar - Webjar to import the quartz-manager frontend in your spring webapp - - https://github.com/fabioformosa/quartz-manager - - ${basedir}/../.. - UTF-8 - UTF-8 - 1.8 - quartz-manager-frontend - v10.16.3 - 6.9.0 - - - - - - - - build-webjar - - - - - - org.apache.maven.plugins - maven-resources-plugin - 2.6 - - - copy-resources - generate-resources - - copy-resources - - - ${basedir}/target/tmp - - - ../../${frontend.folderName} - - static/** - dist/** - node_modules/** - - - - - - - - - - - com.github.eirslett - frontend-maven-plugin - 1.11.0 - - target/tmp - - - - - install node and npm - - install-node-and-npm - - generate-resources - - ${node.version} - ${npm.version} - - - - - npm install - - npm - - process-resources - - install - - - - - npm run build - - npm - - process-resources - - run build - - - - - - - - - maven-antrun-plugin - 1.8 - - - clean build files - process-resources - - - - - - - - - - - run - - - - - - - - - - + + + 4.0.0 + + it.fabioformosa.quartz-manager + quartz-manager-parent + 3.1.1-SNAPSHOT + + + quartz-manager-starter-ui + + Quartz Manager UI webjar + Webjar to import the quartz-manager frontend in your spring webapp + + https://github.com/fabioformosa/quartz-manager + + ${basedir}/../.. + UTF-8 + UTF-8 + 1.8 + quartz-manager-frontend + v10.16.3 + 6.9.0 + + + + + + + + build-webjar + + + + + + org.apache.maven.plugins + maven-resources-plugin + 2.6 + + + copy-resources + generate-resources + + copy-resources + + + ${basedir}/target/tmp + + + ../../${frontend.folderName} + + static/** + dist/** + node_modules/** + + + + + + + + + + + com.github.eirslett + frontend-maven-plugin + 1.11.0 + + target/tmp + + + + + install node and npm + + install-node-and-npm + + generate-resources + + ${node.version} + ${npm.version} + + + + + npm install + + npm + + process-resources + + install + + + + + npm run build + + npm + + process-resources + + run build + + + + + + + + + maven-antrun-plugin + 1.8 + + + clean build files + process-resources + + + + + + + + + + + run + + + + + + + + + + diff --git a/quartz-manager-parent/quartz-manager-web-showcase/pom.xml b/quartz-manager-parent/quartz-manager-web-showcase/pom.xml index 16a5b8b..85b85d7 100644 --- a/quartz-manager-parent/quartz-manager-web-showcase/pom.xml +++ b/quartz-manager-parent/quartz-manager-web-showcase/pom.xml @@ -35,10 +35,10 @@ it.fabioformosa.quartz-manager quartz-manager-starter-security - - it.fabioformosa.quartz-manager - quartz-manager-starter-persistence - + + + + From 233b56f282d1100eff93a1dab90630ef28cd3587 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Sat, 6 Nov 2021 01:17:50 +0100 Subject: [PATCH 008/184] #37 added some mockMvcTest to TriggerController --- .../QuartManagerApplicationTests.java | 8 +++++--- .../controllers/TriggerControllerTest.java | 19 ++++++++++++++++--- .../QuartManagerApplicationTests.java | 5 +---- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/QuartManagerApplicationTests.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/QuartManagerApplicationTests.java index 97f14ef..de9ad97 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/QuartManagerApplicationTests.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/QuartManagerApplicationTests.java @@ -1,9 +1,11 @@ package it.fabioformosa.quartzmanager; -import org.junit.Test; -import org.springframework.boot.test.context.SpringBootTest; +import org.junit.jupiter.api.Test; +import org.springframework.boot.SpringBootConfiguration; +import org.springframework.context.annotation.ComponentScan; -@SpringBootTest +@ComponentScan("it.fabioformosa.quartzmanager") +@SpringBootConfiguration public class QuartManagerApplicationTests { @Test diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java index aabdc42..0eec481 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java @@ -1,25 +1,27 @@ package it.fabioformosa.quartzmanager.controllers; +import it.fabioformosa.quartzmanager.QuartManagerApplicationTests; import it.fabioformosa.quartzmanager.controllers.utils.TestUtils; import it.fabioformosa.quartzmanager.controllers.utils.TriggerUtils; import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam; import it.fabioformosa.quartzmanager.dto.TriggerDTO; import it.fabioformosa.quartzmanager.services.SchedulerService; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Disabled; 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.mockito.ArgumentMatchers.any; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -@Disabled +@ContextConfiguration(classes = {QuartManagerApplicationTests.class}) @WebMvcTest(controllers = TriggerController.class, properties = { "quartz-manager.jobClass=it.fabioformosa.quartzmanager.jobs.myjobs.SampleJob" }) @@ -43,7 +45,7 @@ class TriggerControllerTest { SchedulerConfigParam configParamToPost = SchedulerConfigParam.builder().maxCount(20).triggerPerDay(20000L).build(); mockMvc.perform( - post(TriggerController.TRIGGER_CONTROLLER_BASE_URL + "mytrigger") + post(TriggerController.TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") .contentType(MediaType.APPLICATION_JSON) .content(TestUtils.toJson(configParamToPost)) ) @@ -51,4 +53,15 @@ class TriggerControllerTest { .andExpect(MockMvcResultMatchers.content().json(TestUtils.toJson(expectedTriggerDTO))) ; } + + @Test + void whenGetIsCalled_thenATriggerIsReturned() throws Exception { + TriggerDTO expectedTriggerDTO = TriggerUtils.getTriggerInstance(); + Mockito.when(schedulerService.getTriggerByName("mytrigger")).thenReturn(expectedTriggerDTO); + + mockMvc.perform(get(TriggerController.TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") + .contentType(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.content().json(TestUtils.toJson(expectedTriggerDTO))); + } + } diff --git a/quartz-manager-parent/quartz-manager-web-showcase/src/test/java/it/fabioformosa/QuartManagerApplicationTests.java b/quartz-manager-parent/quartz-manager-web-showcase/src/test/java/it/fabioformosa/QuartManagerApplicationTests.java index 199dc21..b6c30b1 100644 --- a/quartz-manager-parent/quartz-manager-web-showcase/src/test/java/it/fabioformosa/QuartManagerApplicationTests.java +++ b/quartz-manager-parent/quartz-manager-web-showcase/src/test/java/it/fabioformosa/QuartManagerApplicationTests.java @@ -1,12 +1,9 @@ package it.fabioformosa; -import org.junit.Test; -import org.junit.runner.RunWith; +import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; -@RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = QuartManagerApplication.class) @WebAppConfiguration public class QuartManagerApplicationTests { From 34f21a58c964bb09c4cd13e6aa615879e6ddaa30 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Tue, 9 Nov 2021 00:11:47 +0100 Subject: [PATCH 009/184] #37 added input validation to the trigger creation --- .../quartz-manager-starter-api/pom.xml | 20 +++++++++- .../controllers/SchedulerController.java | 2 +- .../controllers/TriggerController.java | 4 +- .../dto/SchedulerConfigParam.java | 8 +++- .../controllers/TriggerControllerTest.java | 39 ++++++++++++++++--- .../InvalidSchedulerConfigParamProvider.java | 19 +++++++++ .../controllers/utils/TriggerUtils.java | 4 +- 7 files changed, 83 insertions(+), 13 deletions(-) create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/InvalidSchedulerConfigParamProvider.java diff --git a/quartz-manager-parent/quartz-manager-starter-api/pom.xml b/quartz-manager-parent/quartz-manager-starter-api/pom.xml index 47bf449..1156d07 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-api/pom.xml @@ -48,6 +48,10 @@ org.springframework.security spring-security-core + + org.springframework.boot + spring-boot-starter-validation + org.springframework.boot spring-boot-starter-test @@ -78,7 +82,21 @@ metamorphosis-core 3.0.0 - + + javax.validation + validation-api + 2.0.1.Final + + + org.hibernate.validator + hibernate-validator + 6.0.2.Final + + + org.glassfish + javax.el + 3.0.0 + diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java index ebd4887..dba69cb 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java @@ -46,7 +46,7 @@ public class SchedulerController { log.debug("SCHEDULER - GET CONFIG params"); SchedulerConfigParam schedulerConfigParam = schedulerService.getOneSimpleTrigger() .map(SchedulerController::fromSimpleTriggerToSchedulerConfigParam) - .orElse(new SchedulerConfigParam(0, 0, 0)); + .orElse(new SchedulerConfigParam(0L, 0, 0)); return schedulerConfigParam; } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java index 7330769..d2ea4f1 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java @@ -10,6 +10,8 @@ import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; +import javax.validation.Valid; + @Slf4j @RequestMapping(TriggerController.TRIGGER_CONTROLLER_BASE_URL) @RestController @@ -34,7 +36,7 @@ public class TriggerController { @ResponseStatus(HttpStatus.CREATED) @PostMapping("/{name}") - public TriggerDTO postTrigger(@PathVariable String name, @RequestBody SchedulerConfigParam config) throws SchedulerException, ClassNotFoundException { + public TriggerDTO postTrigger(@PathVariable String name, @Valid @RequestBody SchedulerConfigParam config) throws SchedulerException, ClassNotFoundException { log.info("TRIGGER - CREATING a trigger {} {}", name, config); TriggerDTO newTriggerDTO = schedulerService.scheduleNewTrigger(name, jobClassname, config); log.info("TRIGGER - CREATED a trigger {}", newTriggerDTO); diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/dto/SchedulerConfigParam.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/dto/SchedulerConfigParam.java index f259a96..4b78d62 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/dto/SchedulerConfigParam.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/dto/SchedulerConfigParam.java @@ -5,12 +5,16 @@ import lombok.Builder; import lombok.Data; import lombok.NoArgsConstructor; +import javax.validation.constraints.NotNull; + @NoArgsConstructor @AllArgsConstructor @Builder @Data public class SchedulerConfigParam { - public long triggerPerDay; - public int maxCount; + @NotNull + public Long triggerPerDay; + @NotNull + public Integer maxCount; public int timesTriggered; } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java index 0eec481..c0e33ec 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java @@ -1,6 +1,7 @@ package it.fabioformosa.quartzmanager.controllers; import it.fabioformosa.quartzmanager.QuartManagerApplicationTests; +import it.fabioformosa.quartzmanager.controllers.utils.InvalidSchedulerConfigParamProvider; import it.fabioformosa.quartzmanager.controllers.utils.TestUtils; import it.fabioformosa.quartzmanager.controllers.utils.TriggerUtils; import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam; @@ -8,6 +9,8 @@ import it.fabioformosa.quartzmanager.dto.TriggerDTO; import it.fabioformosa.quartzmanager.services.SchedulerService; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; import org.mockito.Mockito; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; @@ -18,8 +21,7 @@ import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import static org.mockito.ArgumentMatchers.any; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; @ContextConfiguration(classes = {QuartManagerApplicationTests.class}) @WebMvcTest(controllers = TriggerController.class, properties = { @@ -40,10 +42,9 @@ class TriggerControllerTest { @Test void givenASchedulerConfigParam_whenPosted_thenANewTriggerIsCreated() throws Exception { - TriggerDTO expectedTriggerDTO = TriggerUtils.getTriggerInstance(); + SchedulerConfigParam configParamToPost = buildSimpleSchedulerConfigParam(); + TriggerDTO expectedTriggerDTO = TriggerUtils.getTriggerInstance("mytrigger"); Mockito.when(schedulerService.scheduleNewTrigger(any(), any(), any())).thenReturn(expectedTriggerDTO); - - SchedulerConfigParam configParamToPost = SchedulerConfigParam.builder().maxCount(20).triggerPerDay(20000L).build(); mockMvc.perform( post(TriggerController.TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") .contentType(MediaType.APPLICATION_JSON) @@ -54,9 +55,18 @@ class TriggerControllerTest { ; } + @ParameterizedTest + @ArgumentsSource(InvalidSchedulerConfigParamProvider.class) + void givenAnInvalidSchedulerConfigParam_whenPosted_thenAnErrorIsReturned(SchedulerConfigParam invalidSchedulerConfigParam) throws Exception { + mockMvc.perform(post(TriggerController.TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtils.toJson(invalidSchedulerConfigParam))) + .andExpect(MockMvcResultMatchers.status().is4xxClientError()); + } + @Test void whenGetIsCalled_thenATriggerIsReturned() throws Exception { - TriggerDTO expectedTriggerDTO = TriggerUtils.getTriggerInstance(); + TriggerDTO expectedTriggerDTO = TriggerUtils.getTriggerInstance("mytrigger"); Mockito.when(schedulerService.getTriggerByName("mytrigger")).thenReturn(expectedTriggerDTO); mockMvc.perform(get(TriggerController.TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") @@ -64,4 +74,21 @@ class TriggerControllerTest { .andExpect(MockMvcResultMatchers.content().json(TestUtils.toJson(expectedTriggerDTO))); } + @Test + void givenATriggerName_whenPutSchedulerConfigParam_thenTheTriggerIsRescheduled() throws Exception { + SchedulerConfigParam expectedConfigParam = buildSimpleSchedulerConfigParam(); + TriggerDTO expectedTriggerDTO = TriggerUtils.getTriggerInstance("mytrigger"); + Mockito.when(schedulerService.rescheduleTrigger("mytrigger", buildSimpleSchedulerConfigParam())).thenReturn(expectedTriggerDTO); + + mockMvc.perform(put(TriggerController.TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtils.toJson(expectedConfigParam))) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.content().json(TestUtils.toJson(expectedTriggerDTO))); + } + + private SchedulerConfigParam buildSimpleSchedulerConfigParam() { + return SchedulerConfigParam.builder().maxCount(20).triggerPerDay(20000L).build(); + } + } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/InvalidSchedulerConfigParamProvider.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/InvalidSchedulerConfigParamProvider.java new file mode 100644 index 0000000..486f29e --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/InvalidSchedulerConfigParamProvider.java @@ -0,0 +1,19 @@ +package it.fabioformosa.quartzmanager.controllers.utils; + +import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; + +import java.util.stream.Stream; + +public class InvalidSchedulerConfigParamProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext extensionContext) throws Exception { + return Stream.of( + Arguments.of(SchedulerConfigParam.builder().build()), + Arguments.of(SchedulerConfigParam.builder().maxCount(1).build()), + Arguments.of(SchedulerConfigParam.builder().triggerPerDay(1L).build()) + ); + } +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/TriggerUtils.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/TriggerUtils.java index 6c04827..2fb9080 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/TriggerUtils.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/TriggerUtils.java @@ -9,7 +9,7 @@ import java.time.LocalDateTime; public class TriggerUtils { - static public TriggerDTO getTriggerInstance(){ + static public TriggerDTO getTriggerInstance(String triggerName){ return TriggerDTO.builder() .description("sample trigger") .endTime(DateUtils.getHoursFromNow(2L)) @@ -21,7 +21,7 @@ public class TriggerUtils { .mayFireAgain(true) .triggerKeyDTO(TriggerKeyDTO.builder() .group("defaultTriggerGroup") - .name("sampleTrigger") + .name(triggerName) .build()) .misfireInstruction(1) .nextFireTime(DateUtils.getHoursFromNow(1L)) From 3df1abd46edd5683c031e2e98bdcc76428ff2672 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Tue, 9 Nov 2021 00:14:49 +0100 Subject: [PATCH 010/184] #37 added input validation to the trigger rescheduling --- .../quartzmanager/controllers/TriggerController.java | 2 +- .../controllers/TriggerControllerTest.java | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java index d2ea4f1..03583ce 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java @@ -44,7 +44,7 @@ public class TriggerController { } @PutMapping("/{name}") - public TriggerDTO rescheduleTrigger(@PathVariable String name, @RequestBody SchedulerConfigParam config) throws SchedulerException { + public TriggerDTO rescheduleTrigger(@PathVariable String name, @Valid @RequestBody SchedulerConfigParam config) throws SchedulerException { log.info("TRIGGER - RESCHEDULING the trigger {} {}", name, config); TriggerDTO triggerDTO = schedulerService.rescheduleTrigger(name, config); log.info("TRIGGER - RESCHEDULED the trigger {}", triggerDTO); diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java index c0e33ec..85ec109 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java @@ -57,7 +57,7 @@ class TriggerControllerTest { @ParameterizedTest @ArgumentsSource(InvalidSchedulerConfigParamProvider.class) - void givenAnInvalidSchedulerConfigParam_whenPosted_thenAnErrorIsReturned(SchedulerConfigParam invalidSchedulerConfigParam) throws Exception { + void givenAnInvalidSchedulerConfigParam_whenRequestedANewTrigger_thenAnErrorIsReturned(SchedulerConfigParam invalidSchedulerConfigParam) throws Exception { mockMvc.perform(post(TriggerController.TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") .contentType(MediaType.APPLICATION_JSON) .content(TestUtils.toJson(invalidSchedulerConfigParam))) @@ -87,6 +87,15 @@ class TriggerControllerTest { .andExpect(MockMvcResultMatchers.content().json(TestUtils.toJson(expectedTriggerDTO))); } + @ParameterizedTest + @ArgumentsSource(InvalidSchedulerConfigParamProvider.class) + void givenAnInvalidSchedulerConfigParam_whenATriggerIsRescheduled_thenAnErrorIsReturned(SchedulerConfigParam invalidSchedulerConfigParam) throws Exception { + mockMvc.perform(put(TriggerController.TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtils.toJson(invalidSchedulerConfigParam))) + .andExpect(MockMvcResultMatchers.status().is4xxClientError()); + } + private SchedulerConfigParam buildSimpleSchedulerConfigParam() { return SchedulerConfigParam.builder().maxCount(20).triggerPerDay(20000L).build(); } From 2ca2ba7ffc5794a9fffdadf6cf23e2ec8080030a Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Tue, 9 Nov 2021 23:39:38 +0100 Subject: [PATCH 011/184] #55 migrated swagger2 to openApi specification 3 --- .../quartz-manager-starter-api/pom.xml | 14 ++--- .../configuration/OpenApiConfig.java | 36 ++++++++++++ .../controllers/SchedulerController.java | 57 +++++++++++++++++-- .../controllers/TriggerController.java | 25 +++++++- 4 files changed, 118 insertions(+), 14 deletions(-) create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/OpenApiConfig.java diff --git a/quartz-manager-parent/quartz-manager-starter-api/pom.xml b/quartz-manager-parent/quartz-manager-starter-api/pom.xml index 1156d07..d30e3b1 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-api/pom.xml @@ -137,16 +137,16 @@ snakeyaml - + - io.springfox - springfox-swagger2 - ${springfox.version} + org.springdoc + springdoc-openapi-ui + 1.5.12 - io.springfox - springfox-swagger-ui - ${springfox.version} + io.swagger.core.v3 + swagger-annotations + 2.1.11 diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/OpenApiConfig.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/OpenApiConfig.java new file mode 100644 index 0000000..387aa8e --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/OpenApiConfig.java @@ -0,0 +1,36 @@ +package it.fabioformosa.quartzmanager.configuration; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.info.License; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class OpenApiConfig { + + @Bean + public OpenAPI customOpenAPI() { + return new OpenAPI().info(apiInfo()); + } + + private Info apiInfo() { + return new Info() + .title("QUARTZ MANAGER API") + .description("Quartz Manager - REST API") + .version("1.0.0") + .license(new License() + .name("Apache License 2.0") + .url("https://github.com/fabioformosa/quartz-manager/blob/master/LICENSE")); + } + +// private SecurityContext securityContext() { +// return SecurityContext.builder().forPaths(PathSelectors.any()).build(); +// } + +// @Override +// protected void addResourceHandlers(ResourceHandlerRegistry registry) { +// registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/"); +// registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); +// } +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java index dba69cb..8d5cb21 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java @@ -1,18 +1,26 @@ package it.fabioformosa.quartzmanager.controllers; -import io.swagger.annotations.Api; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam; import it.fabioformosa.quartzmanager.dto.SchedulerDTO; import it.fabioformosa.quartzmanager.dto.TriggerStatus; import it.fabioformosa.quartzmanager.enums.SchedulerStates; import it.fabioformosa.quartzmanager.services.SchedulerService; -import org.quartz.*; +import org.quartz.SchedulerException; +import org.quartz.SimpleTrigger; import org.quartz.impl.triggers.SimpleTriggerImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.convert.ConversionService; import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.util.Collections; @@ -26,7 +34,6 @@ import java.util.Map; */ @RestController @RequestMapping("/quartz-manager/scheduler") -@Api(value = "scheduler") public class SchedulerController { private final Logger log = LoggerFactory.getLogger(SchedulerController.class); @@ -41,7 +48,14 @@ public class SchedulerController { @Resource private ConversionService conversionService; + //TODO replace this a list of trigger @GetMapping("/config") + @Operation(summary = "Get the config of the trigger") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Return the trigger config", + content = { @Content(mediaType = "application/json", + schema = @Schema(implementation = SchedulerConfigParam.class)) }) + }) public SchedulerConfigParam getConfig() throws SchedulerException { log.debug("SCHEDULER - GET CONFIG params"); SchedulerConfigParam schedulerConfigParam = schedulerService.getOneSimpleTrigger() @@ -58,13 +72,26 @@ public class SchedulerController { } @GetMapping + @Operation(summary = "Get the scheduler details") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Return the scheduler config", + content = { @Content(mediaType = "application/json", + schema = @Schema(implementation = SchedulerDTO.class)) }) + }) public SchedulerDTO getScheduler() { log.debug("SCHEDULER - GET Scheduler..."); SchedulerDTO schedulerDTO = conversionService.convert(schedulerService.getScheduler(), SchedulerDTO.class); return schedulerDTO; } + //TODO move this to the Trigger Controller @GetMapping("/progress") + @Operation(summary = "Get the trigger status") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Return the trigger status", + content = { @Content(mediaType = "application/json", + schema = @Schema(implementation = TriggerStatus.class)) }) + }) public TriggerStatus getProgressInfo() throws SchedulerException { log.trace("SCHEDULER - GET PROGRESS INFO"); TriggerStatus progress = new TriggerStatus(); @@ -84,6 +111,12 @@ public class SchedulerController { } @GetMapping(value = "/status", produces = "application/json") + @Operation(summary = "Get the scheduler status") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Return the scheduler status", + content = { @Content(mediaType = "application/json", + schema = @Schema(implementation = SchedulerStates.class)) }) + }) public Map getStatus() throws SchedulerException { log.trace("SCHEDULER - GET STATUS"); String schedulerState = ""; @@ -97,6 +130,10 @@ public class SchedulerController { } @GetMapping("/pause") + @Operation(summary = "Get paused the scheduler") + @ApiResponses(value = { + @ApiResponse(responseCode = "204", description = "Got paused successfully") + }) @ResponseStatus(HttpStatus.NO_CONTENT) public void pause() throws SchedulerException { log.info("SCHEDULER - PAUSE COMMAND"); @@ -104,6 +141,10 @@ public class SchedulerController { } @GetMapping("/resume") + @Operation(summary = "Get resumed the scheduler") + @ApiResponses(value = { + @ApiResponse(responseCode = "204", description = "Got resumed successfully") + }) @ResponseStatus(HttpStatus.NO_CONTENT) public void resume() throws SchedulerException { log.info("SCHEDULER - RESUME COMMAND"); @@ -111,6 +152,10 @@ public class SchedulerController { } @GetMapping("/run") + @Operation(summary = "Start the scheduler") + @ApiResponses(value = { + @ApiResponse(responseCode = "204", description = "Got started successfully") + }) @ResponseStatus(HttpStatus.NO_CONTENT) public void run() throws SchedulerException { log.info("SCHEDULER - START COMMAND"); @@ -118,6 +163,10 @@ public class SchedulerController { } @GetMapping("/stop") + @Operation(summary = "Stop the scheduler") + @ApiResponses(value = { + @ApiResponse(responseCode = "204", description = "Got stopped successfully") + }) @ResponseStatus(HttpStatus.NO_CONTENT) public void stop() throws SchedulerException { log.info("SCHEDULER - STOP COMMAND"); diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java index 03583ce..99d846e 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java @@ -1,6 +1,10 @@ package it.fabioformosa.quartzmanager.controllers; -import io.swagger.annotations.Api; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam; import it.fabioformosa.quartzmanager.dto.TriggerDTO; import it.fabioformosa.quartzmanager.services.SchedulerService; @@ -15,7 +19,6 @@ import javax.validation.Valid; @Slf4j @RequestMapping(TriggerController.TRIGGER_CONTROLLER_BASE_URL) @RestController -@Api(value = "triggers") public class TriggerController { static public final String TRIGGER_CONTROLLER_BASE_URL = "/quartz-manager/triggers"; @@ -34,8 +37,16 @@ public class TriggerController { return schedulerService.getTriggerByName(name); } - @ResponseStatus(HttpStatus.CREATED) @PostMapping("/{name}") + @ResponseStatus(HttpStatus.CREATED) + @Operation(summary = "Create a new trigger") + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "Created the new trigger", + content = { @Content(mediaType = "application/json", + schema = @Schema(implementation = TriggerDTO.class)) }), + @ApiResponse(responseCode = "400", description = "Invalid config supplied", + content = @Content) + }) public TriggerDTO postTrigger(@PathVariable String name, @Valid @RequestBody SchedulerConfigParam config) throws SchedulerException, ClassNotFoundException { log.info("TRIGGER - CREATING a trigger {} {}", name, config); TriggerDTO newTriggerDTO = schedulerService.scheduleNewTrigger(name, jobClassname, config); @@ -44,6 +55,14 @@ public class TriggerController { } @PutMapping("/{name}") + @Operation(summary = "Reschedule the trigger") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Rescheduled the trigger", + content = { @Content(mediaType = "application/json", + schema = @Schema(implementation = TriggerDTO.class)) }), + @ApiResponse(responseCode = "400", description = "Invalid config supplied", + content = @Content) + }) public TriggerDTO rescheduleTrigger(@PathVariable String name, @Valid @RequestBody SchedulerConfigParam config) throws SchedulerException { log.info("TRIGGER - RESCHEDULING the trigger {} {}", name, config); TriggerDTO triggerDTO = schedulerService.rescheduleTrigger(name, config); From c9b90478dd2f757bb96fd46b458b182e32fc65d7 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Wed, 10 Nov 2021 01:16:36 +0100 Subject: [PATCH 012/184] #55 migrated swagger2 to openApi specification 3 --- .../configuration/SwaggerConfig.java | 59 ------------------- .../main/resources/META-INF/spring.factories | 8 +-- .../configuration/WebSecurityConfigJWT.java | 2 +- .../controllers/QuartzManagerController.java | 50 ++++++++-------- .../controllers/SessionController.java | 8 +-- 5 files changed, 34 insertions(+), 93 deletions(-) delete mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/SwaggerConfig.java diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/SwaggerConfig.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/SwaggerConfig.java deleted file mode 100644 index c52b9d9..0000000 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/SwaggerConfig.java +++ /dev/null @@ -1,59 +0,0 @@ -package it.fabioformosa.quartzmanager.configuration; - -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; - -import springfox.documentation.builders.PathSelectors; -import springfox.documentation.builders.RequestHandlerSelectors; -import springfox.documentation.service.ApiInfo; -import springfox.documentation.service.BasicAuth; -import springfox.documentation.service.Contact; -import springfox.documentation.service.VendorExtension; -import springfox.documentation.spi.DocumentationType; -import springfox.documentation.spi.service.contexts.SecurityContext; -import springfox.documentation.spring.web.plugins.Docket; -import springfox.documentation.swagger2.annotations.EnableSwagger2; - -@Configuration -@EnableSwagger2 -public class SwaggerConfig extends WebMvcConfigurationSupport { - - @Bean - public Docket api() { - return new Docket(DocumentationType.SWAGGER_2).select() - .apis(RequestHandlerSelectors.basePackage("it.fabioformosa.quartzmanager.controllers")) // - .build() // - .apiInfo(apiInfo()) // - .securitySchemes(Arrays.asList(new BasicAuth("basicAuth"))) - .securityContexts(Collections.singletonList(securityContext())); - } - - @SuppressWarnings("rawtypes") - private ApiInfo apiInfo() { - String title = "QUARTZ MANAGER API"; - String description = "Quartz Manager - REST API"; - String version = "1.0.0"; - String termsOfServiceUrl = null; - Contact contact = null; - String license = "Apache License 2.0"; - String licenseUrl = "https://github.com/fabioformosa/quartz-manager/blob/master/LICENSE"; - List vendorExtension = Collections.emptyList(); - return new ApiInfo(title, description, version, termsOfServiceUrl, contact, license, licenseUrl, vendorExtension); - } - - private SecurityContext securityContext() { - return SecurityContext.builder().forPaths(PathSelectors.any()).build(); - } - - @Override - protected void addResourceHandlers(ResourceHandlerRegistry registry) { - registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/"); - registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); - } -} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/resources/META-INF/spring.factories b/quartz-manager-parent/quartz-manager-starter-api/src/main/resources/META-INF/spring.factories index 19f951b..f608554 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/resources/META-INF/spring.factories +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/resources/META-INF/spring.factories @@ -1,4 +1,4 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -it.fabioformosa.quartzmanager.configuration.SchedulerConfig,\ -it.fabioformosa.quartzmanager.configuration.SwaggerConfig,\ -it.fabioformosa.quartzmanager.configuration.WebsocketConfig \ No newline at end of file +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +it.fabioformosa.quartzmanager.configuration.SchedulerConfig,\ +it.fabioformosa.quartzmanager.configuration.OpenApiConfig,\ +it.fabioformosa.quartzmanager.configuration.WebsocketConfig diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/configuration/WebSecurityConfigJWT.java b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/configuration/WebSecurityConfigJWT.java index c5bcb07..429ecb6 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/configuration/WebSecurityConfigJWT.java +++ b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/configuration/WebSecurityConfigJWT.java @@ -51,7 +51,7 @@ import it.fabioformosa.quartzmanager.security.helpers.impl.QuartzManagerHttpSecu @EnableGlobalMethodSecurity(prePostEnabled = true) public class WebSecurityConfigJWT extends WebSecurityConfigurerAdapter { - private static final String[] PATTERNS_SWAGGER_UI = {"/swagger-ui.html", "/v2/api-docs", "/swagger-resources/**", "/webjars/**"}; + private static final String[] PATTERNS_SWAGGER_UI = {"/swagger-ui.html", "/v3/api-docs/**", "/swagger-resources/**", "/webjars/**"}; private static final String LOGIN_PATH = "/quartz-manager/api/login"; private static final String LOGOUT_PATH = "/quartz-manager/api/logout"; diff --git a/quartz-manager-parent/quartz-manager-web-showcase/src/main/java/it/fabioformosa/quartzmanager/controllers/QuartzManagerController.java b/quartz-manager-parent/quartz-manager-web-showcase/src/main/java/it/fabioformosa/quartzmanager/controllers/QuartzManagerController.java index f1a2ac0..a4b21c8 100644 --- a/quartz-manager-parent/quartz-manager-web-showcase/src/main/java/it/fabioformosa/quartzmanager/controllers/QuartzManagerController.java +++ b/quartz-manager-parent/quartz-manager-web-showcase/src/main/java/it/fabioformosa/quartzmanager/controllers/QuartzManagerController.java @@ -1,25 +1,25 @@ -package it.fabioformosa.quartzmanager.controllers; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestController; - -import io.swagger.annotations.Api; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@RestController -@RequestMapping -@Api(value = "Healthy Check") -public class QuartzManagerController { - - @ResponseStatus(code = HttpStatus.OK) - @GetMapping("/") - public void healthyCheck() { - log.debug("Healthy check called"); - } - - -} +package it.fabioformosa.quartzmanager.controllers; + +import io.swagger.v3.oas.annotations.Operation; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +@RestController +@RequestMapping +public class QuartzManagerController { + + @ResponseStatus(code = HttpStatus.OK) + @GetMapping("/") + @Operation(description = "Healthy Check") + public void healthyCheck() { + log.debug("Healthy check called"); + } + + +} diff --git a/quartz-manager-parent/quartz-manager-web-showcase/src/main/java/it/fabioformosa/quartzmanager/controllers/SessionController.java b/quartz-manager-parent/quartz-manager-web-showcase/src/main/java/it/fabioformosa/quartzmanager/controllers/SessionController.java index ebfbe12..5990624 100644 --- a/quartz-manager-parent/quartz-manager-web-showcase/src/main/java/it/fabioformosa/quartzmanager/controllers/SessionController.java +++ b/quartz-manager-parent/quartz-manager-web-showcase/src/main/java/it/fabioformosa/quartzmanager/controllers/SessionController.java @@ -1,7 +1,6 @@ package it.fabioformosa.quartzmanager.controllers; -import javax.servlet.http.HttpSession; - +import io.swagger.v3.oas.annotations.Operation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.HttpEntity; @@ -13,10 +12,9 @@ import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseStatus; -import springfox.documentation.annotations.ApiIgnore; +import javax.servlet.http.HttpSession; @Controller -@ApiIgnore @RequestMapping("/session") public class SessionController { @@ -25,6 +23,7 @@ public class SessionController { @GetMapping("/invalidate") @PreAuthorize("hasAuthority('ADMIN')") @ResponseStatus(HttpStatus.NO_CONTENT) + @Operation(hidden = true) public void invalidateSession(HttpSession session) { session.invalidate(); log.info("Invalidated current session!"); @@ -32,6 +31,7 @@ public class SessionController { @GetMapping("/refresh") @PreAuthorize("hasAuthority('ADMIN')") + @Operation(hidden = true) public HttpEntity refreshSession(HttpSession session) { return new ResponseEntity<>(HttpStatus.OK); } From f3506304d9e41175a2a9546e21d888b1cf213196 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Sat, 4 Dec 2021 17:11:10 +0100 Subject: [PATCH 013/184] #44 added the login operation to the OpenApi doc --- .../configuration/OpenApiConfig.java | 48 ++++++++++++++----- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/OpenApiConfig.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/OpenApiConfig.java index 387aa8e..d2d4bab 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/OpenApiConfig.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/OpenApiConfig.java @@ -1,20 +1,55 @@ package it.fabioformosa.quartzmanager.configuration; +import io.swagger.v3.oas.models.Components; import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.PathItem; import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.info.License; +import io.swagger.v3.oas.models.media.*; +import io.swagger.v3.oas.models.parameters.RequestBody; +import io.swagger.v3.oas.models.responses.ApiResponse; +import io.swagger.v3.oas.models.responses.ApiResponses; +import io.swagger.v3.oas.models.security.SecurityScheme; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import java.util.Arrays; + @Configuration public class OpenApiConfig { + static private String BASIC_AUTH_SEC_SCHEME = "basic-auth"; + @Bean public OpenAPI customOpenAPI() { - return new OpenAPI().info(apiInfo()); + return new OpenAPI() + .info(apiInfo()) + .components(new Components().addSecuritySchemes(BASIC_AUTH_SEC_SCHEME, buildBasicAuthScheme())) + .path("/quartz-manager/api/login", + new PathItem().post(new Operation() + .operationId("login") + .tags(Arrays.asList("auth")) + .requestBody(new RequestBody().content( + new Content().addMediaType("application/x-www-form-urlencoded", new MediaType().schema(new Schema().type("object") + .addProperties("username", new StringSchema()) + .addProperties("password", new PasswordSchema()) + .required(Arrays.asList("username", "password")) + )))) + .responses(new ApiResponses().addApiResponse("200", new ApiResponse().description("JWT Token to authenticate the next requests"))) + .responses(new ApiResponses().addApiResponse("401", new ApiResponse().description("Unauthorized - Username or password are incorrect!"))) + )); } - private Info apiInfo() { + private SecurityScheme buildBasicAuthScheme() { + return new SecurityScheme() + .type(SecurityScheme.Type.HTTP) + .scheme("bearer") + .bearerFormat("JWT"); + } + + + private Info apiInfo() { return new Info() .title("QUARTZ MANAGER API") .description("Quartz Manager - REST API") @@ -24,13 +59,4 @@ public class OpenApiConfig { .url("https://github.com/fabioformosa/quartz-manager/blob/master/LICENSE")); } -// private SecurityContext securityContext() { -// return SecurityContext.builder().forPaths(PathSelectors.any()).build(); -// } - -// @Override -// protected void addResourceHandlers(ResourceHandlerRegistry registry) { -// registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/"); -// registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); -// } } From cbd3066f57f8ebed273e229404dba1dbe24a9591 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Sat, 4 Dec 2021 17:14:06 +0100 Subject: [PATCH 014/184] #55 migrated the API doc from swagger2 to OpenAPI 3 --- .../quartz-manager-starter-api/pom.xml | 9 +- .../controllers/SchedulerController.java | 2 + .../controllers/TriggerController.java | 2 + .../configuration/WebSecurityConfigJWT.java | 4 +- .../impl/JwtTokenAuthenticationFilter.java | 208 +++++++++--------- .../security/helpers/impl/JwtTokenHelper.java | 35 ++- 6 files changed, 132 insertions(+), 128 deletions(-) diff --git a/quartz-manager-parent/quartz-manager-starter-api/pom.xml b/quartz-manager-parent/quartz-manager-starter-api/pom.xml index d30e3b1..63d327f 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-api/pom.xml @@ -17,7 +17,7 @@ ${basedir}/../.. UTF-8 UTF-8 - 2.9.2 + 1.5.12 1.8 @@ -141,7 +141,12 @@ org.springdoc springdoc-openapi-ui - 1.5.12 + ${openapi.version} + + + org.springdoc + springdoc-openapi-security + ${openapi.version} io.swagger.core.v3 diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java index 8d5cb21..5798662 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java @@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam; import it.fabioformosa.quartzmanager.dto.SchedulerDTO; import it.fabioformosa.quartzmanager.dto.TriggerStatus; @@ -33,6 +34,7 @@ import java.util.Map; * @author Fabio.Formosa */ @RestController +@SecurityRequirement(name = "basic-auth") @RequestMapping("/quartz-manager/scheduler") public class SchedulerController { diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java index 99d846e..0c93ccd 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java @@ -5,6 +5,7 @@ import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam; import it.fabioformosa.quartzmanager.dto.TriggerDTO; import it.fabioformosa.quartzmanager.services.SchedulerService; @@ -18,6 +19,7 @@ import javax.validation.Valid; @Slf4j @RequestMapping(TriggerController.TRIGGER_CONTROLLER_BASE_URL) +@SecurityRequirement(name = "basic-auth") @RestController public class TriggerController { diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/configuration/WebSecurityConfigJWT.java b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/configuration/WebSecurityConfigJWT.java index 429ecb6..b56dffe 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/configuration/WebSecurityConfigJWT.java +++ b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/configuration/WebSecurityConfigJWT.java @@ -104,7 +104,7 @@ public class WebSecurityConfigJWT extends WebSecurityConfigurerAdapter { } @Override - public void configure(WebSecurity web) throws Exception { + public void configure(WebSecurity web) { web.ignoring()// .antMatchers(HttpMethod.GET, PATTERNS_SWAGGER_UI) // .antMatchers(HttpMethod.GET, WEBJAR_PATH + "/css/**", WEBJAR_PATH + "/js/**", WEBJAR_PATH + "/img/**", WEBJAR_PATH + "/lib/**", WEBJAR_PATH + "/assets/**"); @@ -146,7 +146,7 @@ public class WebSecurityConfigJWT extends WebSecurityConfigurerAdapter { } @Bean - public JwtTokenAuthenticationFilter jwtAuthenticationTokenFilter() throws Exception { + public JwtTokenAuthenticationFilter jwtAuthenticationTokenFilter() { return new JwtTokenAuthenticationFilter(jwtTokenHelper(), userDetailsService); } diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtTokenAuthenticationFilter.java b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtTokenAuthenticationFilter.java index 346bb2f..c84ca6f 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtTokenAuthenticationFilter.java +++ b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtTokenAuthenticationFilter.java @@ -1,105 +1,103 @@ -package it.fabioformosa.quartzmanager.security.helpers.impl; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; -import org.springframework.security.web.util.matcher.OrRequestMatcher; -import org.springframework.security.web.util.matcher.RequestMatcher; -import org.springframework.web.filter.OncePerRequestFilter; - - -/** - * It finds the jwtToken into the request, it validates it and sets an @Authentication into the @SecurityContextHolder. - * If the request has a path included into the paths that must be skipped, it sets an anonymous authentication - * - * It delegates the jwtToken retrieve to the @JwtTokenHelper that applies several strategies. - * - */ -public class JwtTokenAuthenticationFilter extends OncePerRequestFilter { - - private static final Logger log = LoggerFactory.getLogger(JwtTokenAuthenticationFilter.class); - - private static final String ROOT_MATCHER = "/"; - private static final String FAVICON_MATCHER = "/favicon.ico"; - private static final String HTML_MATCHER = "/**/*.html"; - private static final String CSS_MATCHER = "/**/*.css"; - private static final String JS_MATCHER = "/**/*.js"; - private static final String IMG_MATCHER = "/images/*"; - private static final String LOGIN_MATCHER = "/api/login"; - private static final String LOGOUT_MATCHER = "/api/logout"; - - private static List PATH_TO_SKIP = Arrays.asList( - ROOT_MATCHER, - HTML_MATCHER, - FAVICON_MATCHER, - CSS_MATCHER, - JS_MATCHER, - IMG_MATCHER, - LOGIN_MATCHER, - LOGOUT_MATCHER - ); - - private final JwtTokenHelper jwtTokenHelper; - private final UserDetailsService userDetailsService; - - - public JwtTokenAuthenticationFilter(JwtTokenHelper jwtTokenHelper, UserDetailsService userDetailsService) { - super(); - this.jwtTokenHelper = jwtTokenHelper; - this.userDetailsService = userDetailsService; - } - - @Override - public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { - - String jwtToken = jwtTokenHelper.retrieveToken(request); - if (jwtToken != null) { - log.debug("Found a jwtToken into the request {}", request.getPathInfo()); - try { - String username = jwtTokenHelper.getUsernameFromToken(jwtToken); - UserDetails userDetails = userDetailsService.loadUserByUsername(username); - - JwtTokenBasedAuthentication authentication = new JwtTokenBasedAuthentication(userDetails); - authentication.setToken(jwtToken); - - SecurityContextHolder.getContext().setAuthentication(authentication); - } catch (Exception e) { - log.error("Authentication failed! an expected error occurred authenticating the request {}", request.getRequestURL()); - // SecurityContextHolder.getContext().setAuthentication(new AnonAuthentication()); - // log.error("Switched to Anonymous Authentication, " - // + "because an error occurred setting authentication in security context holder due to " + e.getMessage(), e); - } - } - else if(skipPathRequest(request, PATH_TO_SKIP)) { - log.debug("Detected a path to be skipped from authentication, so activated anonymous auth for {}", request.getRequestURL()); - SecurityContextHolder.getContext().setAuthentication(new AnonAuthentication()); - } - else - log.debug("Not found any jwtToken and the request hasn't a path to be skipped from auth. Path: {}", request.getRequestURL()); - - chain.doFilter(request, response); - } - - private boolean skipPathRequest(HttpServletRequest request, List pathsToSkip ) { - if(pathsToSkip == null) - pathsToSkip = new ArrayList(); - List matchers = pathsToSkip.stream().map(path -> new AntPathRequestMatcher(path)).collect(Collectors.toList()); - OrRequestMatcher compositeMatchers = new OrRequestMatcher(matchers); - return compositeMatchers.matches(request); - } - -} \ No newline at end of file +package it.fabioformosa.quartzmanager.security.helpers.impl; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; +import org.springframework.security.web.util.matcher.OrRequestMatcher; +import org.springframework.security.web.util.matcher.RequestMatcher; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + + +/** + * It finds the jwtToken into the request, it validates it and sets an @Authentication into the @SecurityContextHolder. + * If the request has a path included into the paths that must be skipped, it sets an anonymous authentication + * + * It delegates the jwtToken retrieve to the @JwtTokenHelper that applies several strategies. + * + */ +public class JwtTokenAuthenticationFilter extends OncePerRequestFilter { + + private static final Logger log = LoggerFactory.getLogger(JwtTokenAuthenticationFilter.class); + + private static final String ROOT_MATCHER = "/"; + private static final String FAVICON_MATCHER = "/favicon.ico"; + private static final String HTML_MATCHER = "/**/*.html"; + private static final String CSS_MATCHER = "/**/*.css"; + private static final String JS_MATCHER = "/**/*.js"; + private static final String IMG_MATCHER = "/images/*"; + private static final String LOGIN_MATCHER = "/api/login"; + private static final String LOGOUT_MATCHER = "/api/logout"; + + private static List PATH_TO_SKIP = Arrays.asList( + ROOT_MATCHER, + HTML_MATCHER, + FAVICON_MATCHER, + CSS_MATCHER, + JS_MATCHER, + IMG_MATCHER, + LOGIN_MATCHER, + LOGOUT_MATCHER + ); + + private final JwtTokenHelper jwtTokenHelper; + private final UserDetailsService userDetailsService; + + + public JwtTokenAuthenticationFilter(JwtTokenHelper jwtTokenHelper, UserDetailsService userDetailsService) { + super(); + this.jwtTokenHelper = jwtTokenHelper; + this.userDetailsService = userDetailsService; + } + + @Override + public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { + String jwtToken = jwtTokenHelper.retrieveToken(request); + if (jwtToken != null) { + log.debug("Found a jwtToken into the request {}", request.getPathInfo()); + try { + String username = jwtTokenHelper.verifyTokenAndExtractUsername(jwtToken); + UserDetails userDetails = userDetailsService.loadUserByUsername(username); + + JwtTokenBasedAuthentication authentication = new JwtTokenBasedAuthentication(userDetails); + authentication.setToken(jwtToken); + + SecurityContextHolder.getContext().setAuthentication(authentication); + } catch (Exception e) { + log.error("Authentication failed! an expected error occurred authenticating the request {} due to {}", request.getRequestURL(), e.getMessage(), e); + // SecurityContextHolder.getContext().setAuthentication(new AnonAuthentication()); + // log.error("Switched to Anonymous Authentication, " + // + "because an error occurred setting authentication in security context holder due to " + e.getMessage(), e); + } + } + else if(skipPathRequest(request, PATH_TO_SKIP)) { + log.debug("Detected a path to be skipped from authentication, so activated anonymous auth for {}", request.getRequestURL()); + SecurityContextHolder.getContext().setAuthentication(new AnonAuthentication()); + } + else + log.debug("Not found any jwtToken and the request hasn't a path to be skipped from auth. Path: {}", request.getRequestURL()); + + chain.doFilter(request, response); + } + + private boolean skipPathRequest(HttpServletRequest request, List pathsToSkip ) { + if(pathsToSkip == null) + pathsToSkip = new ArrayList(); + List matchers = pathsToSkip.stream().map(path -> new AntPathRequestMatcher(path)).collect(Collectors.toList()); + OrRequestMatcher compositeMatchers = new OrRequestMatcher(matchers); + return compositeMatchers.matches(request); + } + +} diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtTokenHelper.java b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtTokenHelper.java index b796f0b..fdaaf0c 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtTokenHelper.java +++ b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtTokenHelper.java @@ -1,5 +1,15 @@ package it.fabioformosa.quartzmanager.security.helpers.impl; +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import it.fabioformosa.quartzmanager.security.configuration.properties.JwtSecurityProperties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; import java.time.ZoneId; @@ -7,18 +17,6 @@ import java.util.Base64; import java.util.Date; import java.util.Map; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import io.jsonwebtoken.Claims; -import io.jsonwebtoken.Jwts; -import io.jsonwebtoken.SignatureAlgorithm; -import it.fabioformosa.quartzmanager.security.configuration.properties.JwtSecurityProperties; - /** * * @author Fabio.Formosa @@ -47,7 +45,7 @@ public class JwtTokenHelper { public Boolean canTokenBeRefreshed(String token) { try { - final Date expirationDate = getClaimsFromToken(token).getExpiration(); + final Date expirationDate = verifyAndGetClaimsFromToken(token).getExpiration(); // String username = getUsernameFromToken(token); // UserDetails userDetails = userDetailsService.loadUserByUsername(username); return expirationDate.compareTo(generateCurrentDate()) > 0; @@ -75,7 +73,7 @@ public class JwtTokenHelper { .signWith(SIGNATURE_ALGORITHM, base64EncodeSecretKey(jwtSecurityProps.getSecret())).compact(); } - private Claims getClaimsFromToken(String token) { + private Claims verifyAndGetClaimsFromToken(String token) { Claims claims; try { claims = Jwts.parser().setSigningKey(base64EncodeSecretKey(jwtSecurityProps.getSecret())) @@ -109,13 +107,12 @@ public class JwtTokenHelper { return LocalDateTime.now().atZone(ZoneId.of("Europe/Rome")).toInstant().toEpochMilli(); } - public String getUsernameFromToken(String token) { + public String verifyTokenAndExtractUsername(String token) { String username; try { - final Claims claims = getClaimsFromToken(token); + final Claims claims = verifyAndGetClaimsFromToken(token); username = claims.getSubject(); } catch (Exception e) { - username = null; log.error("Error getting claims from jwt token due to " + e.getMessage(), e); throw e; } @@ -125,7 +122,7 @@ public class JwtTokenHelper { public String refreshToken(String token) { String refreshedToken; try { - final Claims claims = getClaimsFromToken(token); + final Claims claims = verifyAndGetClaimsFromToken(token); claims.setIssuedAt(generateCurrentDate()); refreshedToken = generateToken(claims); } catch (Exception e) { @@ -136,7 +133,7 @@ public class JwtTokenHelper { } public String retrieveToken(HttpServletRequest request) { - if (jwtSecurityProps.getCookieStrategy().isEnabled() == true) { + if (jwtSecurityProps.getCookieStrategy().isEnabled()) { Cookie authCookie = getCookieValueByName(request, jwtSecurityProps.getCookieStrategy().getCookie()); if (authCookie != null) return authCookie.getValue(); From 3ba2bafc55ef0bfd072af38168169f13666157f9 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Tue, 7 Dec 2021 23:56:50 +0100 Subject: [PATCH 015/184] #55 fixed mockMvcTests --- .../quartzmanager/controllers/utils/TestUtils.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/TestUtils.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/TestUtils.java index 34333cf..86b10c4 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/TestUtils.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/TestUtils.java @@ -1,11 +1,17 @@ package it.fabioformosa.quartzmanager.controllers.utils; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.util.StdDateFormat; import lombok.SneakyThrows; public class TestUtils { - static public ObjectMapper objectMapper = new ObjectMapper(); + static public ObjectMapper objectMapper = new ObjectMapper(); + static{ + objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + objectMapper.setDateFormat(new StdDateFormat().withColonInTimeZone(true)); // StdDateFormat is ISO8601 since jackson 2.9 + } @SneakyThrows static public String toJson(Object object){ From 851100b774e58a8a61a55e83defc176f4c010abb Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Sat, 5 Mar 2022 20:44:04 +0100 Subject: [PATCH 016/184] added a .editorconfig file --- quartz-manager-parent/.editorconfig | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 quartz-manager-parent/.editorconfig diff --git a/quartz-manager-parent/.editorconfig b/quartz-manager-parent/.editorconfig new file mode 100644 index 0000000..74b2bdd --- /dev/null +++ b/quartz-manager-parent/.editorconfig @@ -0,0 +1,12 @@ +# This file is for unifying the coding style for different editors and IDEs +# See editorconfig.org + +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true From 54999ce735980d5d969ab36f22ffc5e13b3db6fc Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Sat, 5 Mar 2022 20:48:53 +0100 Subject: [PATCH 017/184] #56 added the simpleTrigger to the API --- .../aspects/ProgressNotifier.java | 3 +- .../aspects/WebSocketProgressNotifier.java | 36 ++--- .../AbstractTriggerController.java | 10 ++ .../controllers/SchedulerController.java | 28 ++-- .../controllers/SimpleTriggerController.java | 91 +++++++++++++ .../controllers/TriggerController.java | 21 +-- .../advices/ExceptionHandlingController.java | 31 +++++ ...impleTriggerCommandDTOToSimpleTrigger.java | 32 +++++ .../SimpleTriggerToSimpleTriggerDTO.java | 22 ++++ .../converters/TriggerToTriggerDTO.java | 10 +- .../dto/SimpleTriggerCommandDTO.java | 13 ++ .../quartzmanager/dto/SimpleTriggerDTO.java | 16 +++ .../dto/SimpleTriggerInputDTO.java | 21 +++ .../quartzmanager/dto/TriggerCommandDTO.java | 22 ++++ .../quartzmanager/dto/TriggerDTO.java | 4 +- .../ExceptionHandlingController.java | 18 --- .../exceptions/ExceptionResponse.java | 28 ++-- .../exceptions/ResourceConflictException.java | 13 +- .../exceptions/TriggerNotFoundException.java | 16 +++ .../jobs/AbstractLoggingJob.java | 13 +- .../services/AbstractSchedulerService.java | 26 ++++ ...rvice.java => LegacySchedulerService.java} | 37 +++--- .../SimpleTriggerSchedulerService.java | 48 +++++++ .../SimpleTriggerControllerTest.java | 123 ++++++++++++++++++ .../controllers/TriggerControllerTest.java | 8 +- ...nvalidSimpleTriggerCommandDTOProvider.java | 19 +++ .../controllers/utils/TriggerUtils.java | 50 ++++++- .../quartzmanager/jobs/SampleJob.java | 14 ++ .../SimpleTriggerSchedulerServiceTest.java | 81 ++++++++++++ .../src/test/resources/application.properties | 1 - .../src/test/resources/application.yml | 18 +++ 31 files changed, 741 insertions(+), 132 deletions(-) create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AbstractTriggerController.java create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerController.java create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/advices/ExceptionHandlingController.java create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/SimpleTriggerCommandDTOToSimpleTrigger.java create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/SimpleTriggerToSimpleTriggerDTO.java create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/dto/SimpleTriggerCommandDTO.java create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/dto/SimpleTriggerDTO.java create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/dto/SimpleTriggerInputDTO.java create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/dto/TriggerCommandDTO.java delete mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/exceptions/ExceptionHandlingController.java create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/exceptions/TriggerNotFoundException.java create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/AbstractSchedulerService.java rename quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/{SchedulerService.java => LegacySchedulerService.java} (75%) create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerService.java create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerControllerTest.java create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/InvalidSimpleTriggerCommandDTOProvider.java create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/jobs/SampleJob.java create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerServiceTest.java delete mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/test/resources/application.properties create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/test/resources/application.yml diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/aspects/ProgressNotifier.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/aspects/ProgressNotifier.java index 117251e..242cfd3 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/aspects/ProgressNotifier.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/aspects/ProgressNotifier.java @@ -1,5 +1,6 @@ package it.fabioformosa.quartzmanager.aspects; +import org.quartz.JobExecutionContext; import org.quartz.SchedulerException; /** @@ -11,6 +12,6 @@ import org.quartz.SchedulerException; */ public interface ProgressNotifier { - void send() throws SchedulerException; + void send(JobExecutionContext jobExecutionContext) throws SchedulerException; } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/aspects/WebSocketProgressNotifier.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/aspects/WebSocketProgressNotifier.java index b0ac383..b60fab1 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/aspects/WebSocketProgressNotifier.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/aspects/WebSocketProgressNotifier.java @@ -1,17 +1,11 @@ package it.fabioformosa.quartzmanager.aspects; import it.fabioformosa.quartzmanager.dto.TriggerStatus; -import it.fabioformosa.quartzmanager.services.SchedulerService; -import org.quartz.DailyTimeIntervalTrigger; -import org.quartz.SchedulerException; -import org.quartz.SimpleTrigger; -import org.quartz.Trigger; +import org.quartz.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.simp.SimpMessageSendingOperations; import org.springframework.stereotype.Component; -import javax.annotation.Resource; - /** * * Notify the progress of the trigger through websocket @@ -29,8 +23,8 @@ public class WebSocketProgressNotifier implements ProgressNotifier { // @Resource // private Scheduler scheduler; - @Resource - private SchedulerService schedulerService; +// @Resource +// private LegacySchedulerService schedulerService; // @Resource // private TriggerMonitor triggerMonitor; @@ -42,34 +36,26 @@ public class WebSocketProgressNotifier implements ProgressNotifier { // } @Override - public void send() throws SchedulerException { + public void send(JobExecutionContext jobExecutionContext) throws SchedulerException { TriggerStatus currTriggerStatus = new TriggerStatus(); - Trigger trigger = schedulerService.getOneSimpleTrigger().get(); + Trigger trigger = jobExecutionContext.getTrigger(); currTriggerStatus.setFinalFireTime(trigger.getFinalFireTime()); currTriggerStatus.setNextFireTime(trigger.getNextFireTime()); currTriggerStatus.setPreviousFireTime(trigger.getPreviousFireTime()); - int timesTriggered = 0; - int repeatCount = 0; - if (trigger instanceof SimpleTrigger) { SimpleTrigger simpleTrigger = (SimpleTrigger) trigger; - timesTriggered = simpleTrigger.getTimesTriggered(); - repeatCount = simpleTrigger.getRepeatCount(); + currTriggerStatus.setRepeatCount(simpleTrigger.getRepeatCount() + 1); + currTriggerStatus.setTimesTriggered(simpleTrigger.getTimesTriggered()); } else if (trigger instanceof DailyTimeIntervalTrigger) { DailyTimeIntervalTrigger dailyTrigger = (DailyTimeIntervalTrigger) trigger; - timesTriggered = dailyTrigger.getTimesTriggered(); - repeatCount = dailyTrigger.getRepeatCount(); + currTriggerStatus.setRepeatCount(dailyTrigger.getRepeatCount() + 1); } - Trigger jobTrigger = schedulerService.getOneSimpleTrigger().get(); - if (jobTrigger != null && jobTrigger.getJobKey() != null) { - currTriggerStatus.setJobKey(jobTrigger.getJobKey().getName()); - currTriggerStatus.setJobClass(jobTrigger.getClass().getSimpleName()); - currTriggerStatus.setTimesTriggered(timesTriggered); - currTriggerStatus.setRepeatCount(repeatCount + 1); - } + JobDetail jobDetail = jobExecutionContext.getJobDetail(); + currTriggerStatus.setJobKey(jobDetail.getKey().getName()); + currTriggerStatus.setJobClass(trigger.getClass().getSimpleName()); messagingTemplate.convertAndSend("/topic/progress", currTriggerStatus); } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AbstractTriggerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AbstractTriggerController.java new file mode 100644 index 0000000..ba76182 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AbstractTriggerController.java @@ -0,0 +1,10 @@ +package it.fabioformosa.quartzmanager.controllers; + +import org.springframework.beans.factory.annotation.Value; + +public class AbstractTriggerController { + + @Value("${quartz-manager.jobClass}") + protected String jobClassname; + +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java index 5798662..065a601 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java @@ -10,7 +10,7 @@ import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam; import it.fabioformosa.quartzmanager.dto.SchedulerDTO; import it.fabioformosa.quartzmanager.dto.TriggerStatus; import it.fabioformosa.quartzmanager.enums.SchedulerStates; -import it.fabioformosa.quartzmanager.services.SchedulerService; +import it.fabioformosa.quartzmanager.services.LegacySchedulerService; import org.quartz.SchedulerException; import org.quartz.SimpleTrigger; import org.quartz.impl.triggers.SimpleTriggerImpl; @@ -40,10 +40,10 @@ public class SchedulerController { private final Logger log = LoggerFactory.getLogger(SchedulerController.class); - private SchedulerService schedulerService; + private LegacySchedulerService legacySchedulerService; - public SchedulerController(SchedulerService schedulerService, ConversionService conversionService) { - this.schedulerService = schedulerService; + public SchedulerController(LegacySchedulerService legacySchedulerService, ConversionService conversionService) { + this.legacySchedulerService = legacySchedulerService; this.conversionService = conversionService; } @@ -60,7 +60,7 @@ public class SchedulerController { }) public SchedulerConfigParam getConfig() throws SchedulerException { log.debug("SCHEDULER - GET CONFIG params"); - SchedulerConfigParam schedulerConfigParam = schedulerService.getOneSimpleTrigger() + SchedulerConfigParam schedulerConfigParam = legacySchedulerService.getOneSimpleTrigger() .map(SchedulerController::fromSimpleTriggerToSchedulerConfigParam) .orElse(new SchedulerConfigParam(0L, 0, 0)); return schedulerConfigParam; @@ -69,7 +69,7 @@ public class SchedulerController { public static SchedulerConfigParam fromSimpleTriggerToSchedulerConfigParam(SimpleTrigger simpleTrigger){ int timesTriggered = simpleTrigger.getTimesTriggered(); int maxCount = simpleTrigger.getRepeatCount() + 1; - long triggersPerDay = SchedulerService.fromMillsIntervalToTriggerPerDay(simpleTrigger.getRepeatInterval()); + long triggersPerDay = LegacySchedulerService.fromMillsIntervalToTriggerPerDay(simpleTrigger.getRepeatInterval()); return new SchedulerConfigParam(triggersPerDay, maxCount, timesTriggered); } @@ -82,7 +82,7 @@ public class SchedulerController { }) public SchedulerDTO getScheduler() { log.debug("SCHEDULER - GET Scheduler..."); - SchedulerDTO schedulerDTO = conversionService.convert(schedulerService.getScheduler(), SchedulerDTO.class); + SchedulerDTO schedulerDTO = conversionService.convert(legacySchedulerService.getScheduler(), SchedulerDTO.class); return schedulerDTO; } @@ -98,7 +98,7 @@ public class SchedulerController { log.trace("SCHEDULER - GET PROGRESS INFO"); TriggerStatus progress = new TriggerStatus(); - SimpleTriggerImpl jobTrigger = (SimpleTriggerImpl) schedulerService.getOneSimpleTrigger().get(); + SimpleTriggerImpl jobTrigger = (SimpleTriggerImpl) legacySchedulerService.getOneSimpleTrigger().get(); if (jobTrigger != null && jobTrigger.getJobKey() != null) { progress.setJobKey(jobTrigger.getJobKey().getName()); progress.setJobClass(jobTrigger.getClass().getSimpleName()); @@ -122,9 +122,9 @@ public class SchedulerController { public Map getStatus() throws SchedulerException { log.trace("SCHEDULER - GET STATUS"); String schedulerState = ""; - if (schedulerService.getScheduler().isShutdown() || !schedulerService.getScheduler().isStarted()) + if (legacySchedulerService.getScheduler().isShutdown() || !legacySchedulerService.getScheduler().isStarted()) schedulerState = SchedulerStates.STOPPED.toString(); - else if (schedulerService.getScheduler().isStarted() && schedulerService.getScheduler().isInStandbyMode()) + else if (legacySchedulerService.getScheduler().isStarted() && legacySchedulerService.getScheduler().isInStandbyMode()) schedulerState = SchedulerStates.PAUSED.toString(); else schedulerState = SchedulerStates.RUNNING.toString(); @@ -139,7 +139,7 @@ public class SchedulerController { @ResponseStatus(HttpStatus.NO_CONTENT) public void pause() throws SchedulerException { log.info("SCHEDULER - PAUSE COMMAND"); - schedulerService.getScheduler().standby(); + legacySchedulerService.getScheduler().standby(); } @GetMapping("/resume") @@ -150,7 +150,7 @@ public class SchedulerController { @ResponseStatus(HttpStatus.NO_CONTENT) public void resume() throws SchedulerException { log.info("SCHEDULER - RESUME COMMAND"); - schedulerService.getScheduler().start(); + legacySchedulerService.getScheduler().start(); } @GetMapping("/run") @@ -161,7 +161,7 @@ public class SchedulerController { @ResponseStatus(HttpStatus.NO_CONTENT) public void run() throws SchedulerException { log.info("SCHEDULER - START COMMAND"); - schedulerService.getScheduler().start(); + legacySchedulerService.getScheduler().start(); } @GetMapping("/stop") @@ -172,7 +172,7 @@ public class SchedulerController { @ResponseStatus(HttpStatus.NO_CONTENT) public void stop() throws SchedulerException { log.info("SCHEDULER - STOP COMMAND"); - schedulerService.getScheduler().shutdown(true); + legacySchedulerService.getScheduler().shutdown(true); } } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerController.java new file mode 100644 index 0000000..e8c5909 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerController.java @@ -0,0 +1,91 @@ +package it.fabioformosa.quartzmanager.controllers; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import it.fabioformosa.quartzmanager.dto.SimpleTriggerCommandDTO; +import it.fabioformosa.quartzmanager.dto.SimpleTriggerInputDTO; +import it.fabioformosa.quartzmanager.dto.SimpleTriggerDTO; +import it.fabioformosa.quartzmanager.dto.TriggerDTO; +import it.fabioformosa.quartzmanager.exceptions.TriggerNotFoundException; +import it.fabioformosa.quartzmanager.services.SimpleTriggerSchedulerService; +import lombok.extern.slf4j.Slf4j; +import org.quartz.SchedulerException; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +@Slf4j +@RequestMapping(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL) +@SecurityRequirement(name = "basic-auth") +@RestController +public class SimpleTriggerController extends AbstractTriggerController { + + static public final String SIMPLE_TRIGGER_CONTROLLER_BASE_URL = "/quartz-manager/simple-triggers"; + + private SimpleTriggerSchedulerService simpleSchedulerService; + + public SimpleTriggerController(SimpleTriggerSchedulerService simpleSchedulerService) { + this.simpleSchedulerService = simpleSchedulerService; + } + + @GetMapping("/{name}") + @Operation(summary = "Get a simple trigger by name") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Got the trigger by its name", + content = { @Content(mediaType = "application/json", + schema = @Schema(implementation = SimpleTriggerDTO.class)) }), + @ApiResponse(responseCode = "404", description = "Trigger not found", + content = @Content) + }) + public SimpleTriggerDTO getSimpleTrigger(@PathVariable String name) throws SchedulerException, TriggerNotFoundException { + return simpleSchedulerService.getSimpleTriggerByName(name); + } + + @PostMapping("/{name}") + @ResponseStatus(HttpStatus.CREATED) + @Operation(summary = "Create a new simple trigger") + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "Created a new simple trigger", + content = { @Content(mediaType = "application/json", + schema = @Schema(implementation = TriggerDTO.class)) }), + @ApiResponse(responseCode = "400", description = "Invalid config supplied", + content = @Content) + }) + public SimpleTriggerDTO postSimpleTrigger(@PathVariable String name, @Valid @RequestBody SimpleTriggerInputDTO simpleTriggerInputDTO) throws SchedulerException, ClassNotFoundException { + log.info("SIMPLE TRIGGER - CREATING a SimpleTrigger {} {}", name, simpleTriggerInputDTO); + SimpleTriggerCommandDTO simpleTriggerCommandDTO = SimpleTriggerCommandDTO.builder() + .triggerName(name) + .simpleTriggerInputDTO(simpleTriggerInputDTO) + .build(); + SimpleTriggerDTO newTriggerDTO = simpleSchedulerService.scheduleSimpleTrigger(jobClassname, simpleTriggerCommandDTO); + log.info("SIMPLE TRIGGER - CREATED a SimpleTrigger {}", newTriggerDTO); + return newTriggerDTO; + } + + @PutMapping("/{name}") + @Operation(summary = "Reschedule a simple trigger") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Rescheduled a simple trigger", + content = { @Content(mediaType = "application/json", + schema = @Schema(implementation = TriggerDTO.class)) }), + @ApiResponse(responseCode = "400", description = "Invalid config supplied", + content = @Content) + }) + public TriggerDTO rescheduleSimpleTrigger(@PathVariable String name, @Valid @RequestBody SimpleTriggerInputDTO simpleTriggerInputDTO) throws SchedulerException { + log.info("SIMPLE TRIGGER - RESCHEDULING the trigger {} {}", name, simpleTriggerInputDTO); + SimpleTriggerCommandDTO simpleTriggerCommandDTO = SimpleTriggerCommandDTO.builder() + .triggerName(name) + .simpleTriggerInputDTO(simpleTriggerInputDTO) + .build(); + TriggerDTO triggerDTO = simpleSchedulerService.rescheduleSimpleTrigger(simpleTriggerCommandDTO); + log.info("SIMPLE TRIGGER - RESCHEDULED the trigger {}", triggerDTO); + return triggerDTO; + } + + +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java index 0c93ccd..47e60c5 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java @@ -8,10 +8,10 @@ import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam; import it.fabioformosa.quartzmanager.dto.TriggerDTO; -import it.fabioformosa.quartzmanager.services.SchedulerService; +import it.fabioformosa.quartzmanager.exceptions.TriggerNotFoundException; +import it.fabioformosa.quartzmanager.services.LegacySchedulerService; import lombok.extern.slf4j.Slf4j; import org.quartz.SchedulerException; -import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; @@ -21,24 +21,23 @@ import javax.validation.Valid; @RequestMapping(TriggerController.TRIGGER_CONTROLLER_BASE_URL) @SecurityRequirement(name = "basic-auth") @RestController -public class TriggerController { +public class TriggerController extends AbstractTriggerController { static public final String TRIGGER_CONTROLLER_BASE_URL = "/quartz-manager/triggers"; + static public final String SIMPLE_TRIGGER_BASE_URL = "/simple-triggers"; - @Value("${quartz-manager.jobClass}") - private String jobClassname; + private LegacySchedulerService schedulerService; - private SchedulerService schedulerService; - - public TriggerController(SchedulerService schedulerService) { + public TriggerController(LegacySchedulerService schedulerService) { this.schedulerService = schedulerService; } @GetMapping("/{name}") - public TriggerDTO getTrigger(@PathVariable String name) throws SchedulerException { - return schedulerService.getTriggerByName(name); + public TriggerDTO getTrigger(@PathVariable String name) throws SchedulerException, TriggerNotFoundException { + return schedulerService.getLegacyTriggerByName(name); } + @Deprecated @PostMapping("/{name}") @ResponseStatus(HttpStatus.CREATED) @Operation(summary = "Create a new trigger") @@ -56,6 +55,8 @@ public class TriggerController { return newTriggerDTO; } + + @PutMapping("/{name}") @Operation(summary = "Reschedule the trigger") @ApiResponses(value = { diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/advices/ExceptionHandlingController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/advices/ExceptionHandlingController.java new file mode 100644 index 0000000..4f386a0 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/advices/ExceptionHandlingController.java @@ -0,0 +1,31 @@ +package it.fabioformosa.quartzmanager.controllers.advices; + +import it.fabioformosa.quartzmanager.exceptions.ExceptionResponse; +import it.fabioformosa.quartzmanager.exceptions.ResourceConflictException; +import it.fabioformosa.quartzmanager.exceptions.TriggerNotFoundException; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ControllerAdvice +public class ExceptionHandlingController { + + @ExceptionHandler(ResourceConflictException.class) + public ResponseEntity resourceConflict(ResourceConflictException ex) { + ExceptionResponse response = new ExceptionResponse(); + response.setErrorCode("Conflict"); + response.setErrorMessage(ex.getMessage()); + return new ResponseEntity(response, HttpStatus.CONFLICT); + } + + @ExceptionHandler(TriggerNotFoundException.class) + @ResponseStatus(HttpStatus.NOT_FOUND) + @ResponseBody + public ExceptionResponse triggerNotFound(TriggerNotFoundException ex){ + return ExceptionResponse.builder().errorCode(HttpStatus.NOT_FOUND.toString()).errorMessage(ex.getMessage()).build(); + } + +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/SimpleTriggerCommandDTOToSimpleTrigger.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/SimpleTriggerCommandDTOToSimpleTrigger.java new file mode 100644 index 0000000..ba2d0c8 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/SimpleTriggerCommandDTOToSimpleTrigger.java @@ -0,0 +1,32 @@ +package it.fabioformosa.quartzmanager.converters; + + +import it.fabioformosa.quartzmanager.dto.SimpleTriggerCommandDTO; +import org.quartz.SimpleScheduleBuilder; +import org.quartz.SimpleTrigger; +import org.quartz.Trigger; +import org.quartz.TriggerBuilder; +import org.springframework.core.convert.converter.Converter; +import org.springframework.stereotype.Component; + +@Component +public class SimpleTriggerCommandDTOToSimpleTrigger implements Converter { + @Override + public SimpleTrigger convert(SimpleTriggerCommandDTO triggerCommandDTO) { + TriggerBuilder triggerTriggerBuilder = TriggerBuilder.newTrigger(); + if(triggerCommandDTO.getSimpleTriggerInputDTO().getStartDate() != null) + triggerTriggerBuilder.startAt(triggerCommandDTO.getSimpleTriggerInputDTO().getStartDate()); + if(triggerCommandDTO.getSimpleTriggerInputDTO().getEndDate() != null) + triggerTriggerBuilder.endAt(triggerCommandDTO.getSimpleTriggerInputDTO().getEndDate()); + + SimpleTrigger newSimpleTrigger = triggerTriggerBuilder.withSchedule( + SimpleScheduleBuilder.simpleSchedule() + .withIntervalInMilliseconds(triggerCommandDTO.getSimpleTriggerInputDTO().getRepeatInterval()) + .withRepeatCount(triggerCommandDTO.getSimpleTriggerInputDTO().getRepeatCount()) + .withMisfireHandlingInstructionNextWithRemainingCount() + ) + .withIdentity(triggerCommandDTO.getTriggerName()) + .build(); + return newSimpleTrigger; + } +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/SimpleTriggerToSimpleTriggerDTO.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/SimpleTriggerToSimpleTriggerDTO.java new file mode 100644 index 0000000..c691b7a --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/SimpleTriggerToSimpleTriggerDTO.java @@ -0,0 +1,22 @@ +package it.fabioformosa.quartzmanager.converters; + +import it.fabioformosa.quartzmanager.dto.SimpleTriggerDTO; +import org.quartz.SimpleTrigger; +import org.springframework.stereotype.Component; + +@Component +public class SimpleTriggerToSimpleTriggerDTO extends TriggerToTriggerDTO { + + @Override + protected void convert(SimpleTrigger source, SimpleTriggerDTO target) { + super.convert(source, target); + target.setTimesTriggered(source.getTimesTriggered()); + target.setRepeatCount(source.getRepeatCount()); + target.setRepeatInterval(source.getRepeatInterval()); + } + + @Override + protected SimpleTriggerDTO createOrRetrieveTarget(SimpleTrigger source) { + return new SimpleTriggerDTO(); + } +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/TriggerToTriggerDTO.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/TriggerToTriggerDTO.java index 479be88..3c35a2e 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/TriggerToTriggerDTO.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/TriggerToTriggerDTO.java @@ -1,6 +1,6 @@ package it.fabioformosa.quartzmanager.converters; -import it.fabioformosa.metamorphosis.core.converters.AbstractBaseConverterToDTO; +import it.fabioformosa.metamorphosis.core.converters.AbstractBaseConverter; import it.fabioformosa.quartzmanager.dto.JobKeyDTO; import it.fabioformosa.quartzmanager.dto.TriggerDTO; import it.fabioformosa.quartzmanager.dto.TriggerKeyDTO; @@ -10,10 +10,10 @@ import org.quartz.TriggerKey; import org.springframework.stereotype.Component; @Component -public class TriggerToTriggerDTO extends AbstractBaseConverterToDTO { +public class TriggerToTriggerDTO extends AbstractBaseConverter { @Override - protected void convert(Trigger source, TriggerDTO target) { + protected void convert(S source, T target) { TriggerKey triggerKey = source.getKey(); TriggerKeyDTO triggerKeyDTO = conversionService.convert(triggerKey, TriggerKeyDTO.class); target.setTriggerKeyDTO(triggerKeyDTO); @@ -30,7 +30,11 @@ public class TriggerToTriggerDTO extends AbstractBaseConverterToDTO resourceConflict(ResourceConflictException ex) { - ExceptionResponse response = new ExceptionResponse(); - response.setErrorCode("Conflict"); - response.setErrorMessage(ex.getMessage()); - return new ResponseEntity(response, HttpStatus.CONFLICT); - } -} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/exceptions/ExceptionResponse.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/exceptions/ExceptionResponse.java index e642282..cc591c1 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/exceptions/ExceptionResponse.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/exceptions/ExceptionResponse.java @@ -1,25 +1,15 @@ package it.fabioformosa.quartzmanager.exceptions; -public class ExceptionResponse { +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +@NoArgsConstructor +@AllArgsConstructor +@Builder +@Data +public class ExceptionResponse { private String errorCode; private String errorMessage; - - public ExceptionResponse() {} - - public String getErrorCode() { - return errorCode; - } - - public void setErrorCode(String errorCode) { - this.errorCode = errorCode; - } - - public String getErrorMessage() { - return errorMessage; - } - - public void setErrorMessage(String errorMessage) { - this.errorMessage = errorMessage; - } } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/exceptions/ResourceConflictException.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/exceptions/ResourceConflictException.java index a5af1fe..d3ed391 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/exceptions/ResourceConflictException.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/exceptions/ResourceConflictException.java @@ -1,5 +1,9 @@ package it.fabioformosa.quartzmanager.exceptions; +import lombok.Getter; +import lombok.Setter; + +@Getter @Setter public class ResourceConflictException extends RuntimeException { private static final long serialVersionUID = 1791564636123821405L; @@ -8,14 +12,7 @@ public class ResourceConflictException extends RuntimeException { public ResourceConflictException(Long resourceId, String message) { super(message); - setResourceId(resourceId); - } - - public Long getResourceId() { - return resourceId; - } - - public void setResourceId(Long resourceId) { this.resourceId = resourceId; } + } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/exceptions/TriggerNotFoundException.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/exceptions/TriggerNotFoundException.java new file mode 100644 index 0000000..26d3933 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/exceptions/TriggerNotFoundException.java @@ -0,0 +1,16 @@ +package it.fabioformosa.quartzmanager.exceptions; + +import lombok.Getter; +import lombok.ToString; + +@ToString +@Getter +public class TriggerNotFoundException extends Exception { + + private String name; + + public TriggerNotFoundException(String name) { + super("Trigger with name " + name + " not found!"); + this.name = name; + } +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/jobs/AbstractLoggingJob.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/jobs/AbstractLoggingJob.java index a55378b..b2bf316 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/jobs/AbstractLoggingJob.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/jobs/AbstractLoggingJob.java @@ -1,7 +1,7 @@ package it.fabioformosa.quartzmanager.jobs; -import javax.annotation.Resource; - +import it.fabioformosa.quartzmanager.aspects.ProgressNotifier; +import it.fabioformosa.quartzmanager.jobs.entities.LogRecord; import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.SchedulerException; @@ -10,13 +10,12 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.simp.SimpMessageSendingOperations; -import it.fabioformosa.quartzmanager.aspects.ProgressNotifier; -import it.fabioformosa.quartzmanager.jobs.entities.LogRecord; +import javax.annotation.Resource; /** * Extends this class to create a job that produces LogRecord to be displayed * into the GUI panel - * + * * @author Fabio.Formosa * */ @@ -42,7 +41,7 @@ public abstract class AbstractLoggingJob implements Job { try { LogRecord logMsg = doIt(jobExecutionContext); logAndSend(logMsg); - progressNotifier.send(); + progressNotifier.send(jobExecutionContext); } catch (SchedulerException e) { log.error("Error updating progress " + e.getMessage()); } @@ -54,4 +53,4 @@ public abstract class AbstractLoggingJob implements Job { messagingTemplate.convertAndSend("/topic/logs", logRecord); } -} \ No newline at end of file +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/AbstractSchedulerService.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/AbstractSchedulerService.java new file mode 100644 index 0000000..26c2ed2 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/AbstractSchedulerService.java @@ -0,0 +1,26 @@ +package it.fabioformosa.quartzmanager.services; + +import it.fabioformosa.quartzmanager.exceptions.TriggerNotFoundException; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.Trigger; +import org.quartz.TriggerKey; +import org.springframework.core.convert.ConversionService; + +public class AbstractSchedulerService { + + protected Scheduler scheduler; + protected ConversionService conversionService; + + public AbstractSchedulerService(Scheduler scheduler, ConversionService conversionService) { + this.scheduler = scheduler; + this.conversionService = conversionService; + } + + protected Trigger getTriggerByName(String name) throws SchedulerException, TriggerNotFoundException { + Trigger trigger = scheduler.getTrigger(new TriggerKey(name)); + if(trigger == null) + throw new TriggerNotFoundException(name); + return trigger; + } +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SchedulerService.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/LegacySchedulerService.java similarity index 75% rename from quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SchedulerService.java rename to quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/LegacySchedulerService.java index 7900aee..bea3ef0 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SchedulerService.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/LegacySchedulerService.java @@ -3,6 +3,7 @@ package it.fabioformosa.quartzmanager.services; import it.fabioformosa.quartzmanager.common.utils.Try; import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam; import it.fabioformosa.quartzmanager.dto.TriggerDTO; +import it.fabioformosa.quartzmanager.exceptions.TriggerNotFoundException; import org.quartz.*; import org.quartz.impl.matchers.GroupMatcher; import org.springframework.core.convert.ConversionService; @@ -10,26 +11,24 @@ import org.springframework.stereotype.Service; import java.util.Optional; +import static org.quartz.TriggerBuilder.newTrigger; + @Service -public class SchedulerService { +public class LegacySchedulerService extends AbstractSchedulerService { public static final int MILLS_IN_A_DAY = 1000 * 60 * 60 * 24; public static final int SEC_IN_A_DAY = 60 * 60 * 24; - private Scheduler scheduler; - private ConversionService conversionService; - - public SchedulerService(Scheduler scheduler, ConversionService conversionService) { - this.scheduler = scheduler; - this.conversionService = conversionService; + public LegacySchedulerService(Scheduler scheduler, ConversionService conversionService) { + super(scheduler, conversionService); } public static int fromTriggerPerDayToMillsInterval(long triggerPerDay) { - return (int) Math.ceil(Long.valueOf(SchedulerService.MILLS_IN_A_DAY) / triggerPerDay); // with ceil the triggerPerDay is a max value + return (int) Math.ceil(Long.valueOf(LegacySchedulerService.MILLS_IN_A_DAY) / triggerPerDay); // with ceil the triggerPerDay is a max value } public static int fromTriggerPerDayToSecInterval(long triggerPerDay) { - return (int) Math.ceil(Long.valueOf(SchedulerService.SEC_IN_A_DAY) / triggerPerDay); + return (int) Math.ceil(Long.valueOf(LegacySchedulerService.SEC_IN_A_DAY) / triggerPerDay); } public static long fromMillsIntervalToTriggerPerDay(long repeatIntervalInMills) { @@ -59,10 +58,12 @@ public class SchedulerService { .findFirst(); } - public TriggerDTO getTriggerByName(String name) throws SchedulerException { - Trigger trigger = scheduler.getTrigger(new TriggerKey(name)); - return conversionService.convert(trigger, TriggerDTO.class); - } + public TriggerDTO getLegacyTriggerByName(String name) throws SchedulerException, TriggerNotFoundException { + Trigger trigger = getTriggerByName(name); + return conversionService.convert(trigger, TriggerDTO.class); + } + + public TriggerDTO scheduleNewTrigger(String name, String jobClassname, SchedulerConfigParam config) throws SchedulerException, ClassNotFoundException { Class jobClass = (Class) Class.forName(jobClassname); @@ -71,9 +72,9 @@ public class SchedulerService { .storeDurably(false) .build(); - int intervalInMills = SchedulerService.fromTriggerPerDayToMillsInterval(config.getTriggerPerDay()); + int intervalInMills = LegacySchedulerService.fromTriggerPerDayToMillsInterval(config.getTriggerPerDay()); - Trigger newTrigger = TriggerBuilder.newTrigger() + Trigger newTrigger = newTrigger() .withSchedule( SimpleScheduleBuilder.simpleSchedule() .withIntervalInMilliseconds(intervalInMills) @@ -88,14 +89,16 @@ public class SchedulerService { return conversionService.convert(newTrigger, TriggerDTO.class); } + + public TriggerDTO rescheduleTrigger(String name, SchedulerConfigParam config) throws SchedulerException { - int intervalInMills = SchedulerService.fromTriggerPerDayToMillsInterval(config.getTriggerPerDay()); + int intervalInMills = LegacySchedulerService.fromTriggerPerDayToMillsInterval(config.getTriggerPerDay()); Optional optionalTriggerKey = getTriggerByKey(name); TriggerKey triggerKey = optionalTriggerKey.orElse(TriggerKey.triggerKey(name)); Trigger trigger = scheduler.getTrigger(triggerKey); - Trigger newTrigger = TriggerBuilder.newTrigger() + Trigger newTrigger = newTrigger() .withSchedule( SimpleScheduleBuilder.simpleSchedule() .withIntervalInMilliseconds(intervalInMills) diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerService.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerService.java new file mode 100644 index 0000000..95ec237 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerService.java @@ -0,0 +1,48 @@ +package it.fabioformosa.quartzmanager.services; + +import it.fabioformosa.quartzmanager.dto.SimpleTriggerCommandDTO; +import it.fabioformosa.quartzmanager.dto.SimpleTriggerDTO; +import it.fabioformosa.quartzmanager.dto.TriggerDTO; +import it.fabioformosa.quartzmanager.exceptions.TriggerNotFoundException; +import org.quartz.*; +import org.springframework.core.convert.ConversionService; +import org.springframework.stereotype.Service; + +@Service +public class SimpleTriggerSchedulerService extends AbstractSchedulerService { + + public SimpleTriggerSchedulerService(Scheduler scheduler, ConversionService conversionService) { + super(scheduler, conversionService); + } + + public SimpleTriggerDTO getSimpleTriggerByName(String name) throws SchedulerException, TriggerNotFoundException { + Trigger trigger = getTriggerByName(name); + return conversionService.convert(trigger, SimpleTriggerDTO.class); + } + + public SimpleTriggerDTO scheduleSimpleTrigger(String jobClassname, SimpleTriggerCommandDTO triggerCommandDTO) throws SchedulerException, ClassNotFoundException { + Class jobClass = (Class) Class.forName(jobClassname); + JobDetail jobDetail = JobBuilder.newJob() + .ofType(jobClass) + .storeDurably(false) + .build(); + + SimpleTrigger newSimpleTrigger = conversionService.convert(triggerCommandDTO, SimpleTrigger.class); + scheduler.scheduleJob(jobDetail, newSimpleTrigger); + + return conversionService.convert(newSimpleTrigger, SimpleTriggerDTO.class); + } + + public TriggerDTO rescheduleSimpleTrigger(SimpleTriggerCommandDTO triggerCommandDTO) throws SchedulerException { + //Optional optionalTriggerKey = getTriggerByKey(name); +// TriggerKey triggerKey = optionalTriggerKey.orElse(TriggerKey.triggerKey(name)); + + SimpleTrigger newSimpleTrigger = conversionService.convert(triggerCommandDTO, SimpleTrigger.class); + + TriggerKey triggerKey = TriggerKey.triggerKey(triggerCommandDTO.getTriggerName()); + scheduler.rescheduleJob(triggerKey, newSimpleTrigger); + + return conversionService.convert(newSimpleTrigger, SimpleTriggerDTO.class); + } + +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerControllerTest.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerControllerTest.java new file mode 100644 index 0000000..b0e9a54 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerControllerTest.java @@ -0,0 +1,123 @@ +package it.fabioformosa.quartzmanager.controllers; + +import it.fabioformosa.quartzmanager.QuartManagerApplicationTests; +import it.fabioformosa.quartzmanager.controllers.utils.InvalidSimpleTriggerCommandDTOProvider; +import it.fabioformosa.quartzmanager.controllers.utils.TestUtils; +import it.fabioformosa.quartzmanager.controllers.utils.TriggerUtils; +import it.fabioformosa.quartzmanager.dto.SimpleTriggerCommandDTO; +import it.fabioformosa.quartzmanager.dto.SimpleTriggerDTO; +import it.fabioformosa.quartzmanager.dto.SimpleTriggerInputDTO; +import it.fabioformosa.quartzmanager.exceptions.TriggerNotFoundException; +import it.fabioformosa.quartzmanager.services.SimpleTriggerSchedulerService; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ArgumentsSource; +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.Date; + +import static org.mockito.ArgumentMatchers.any; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; + +@ContextConfiguration(classes = {QuartManagerApplicationTests.class}) +@WebMvcTest(controllers = SimpleTriggerController.class, properties = { + "quartz-manager.jobClass=it.fabioformosa.quartzmanager.jobs.SampleJob" +}) +class SimpleTriggerControllerTest { + + @Autowired + private MockMvc mockMvc; + + @MockBean + private SimpleTriggerSchedulerService simpleTriggerSchedulerService; + + @AfterEach + void cleanUp(){ + Mockito.reset(simpleTriggerSchedulerService); + } + + @Test + void whenGetIsCalled_thenASimpleTriggerIsReturned() throws Exception { + SimpleTriggerDTO expectedSimpleTriggerDTO = TriggerUtils.getSimpleTriggerInstance("mytrigger"); + Mockito.when(simpleTriggerSchedulerService.getSimpleTriggerByName("mytrigger")).thenReturn(expectedSimpleTriggerDTO); + + mockMvc.perform(get(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") + .contentType(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.content().json(TestUtils.toJson(expectedSimpleTriggerDTO))); + } + + @Test + void givenAnExistingTrigger_whenGetIsCalled_then404IsReturned() throws Exception { + Mockito.when(simpleTriggerSchedulerService.getSimpleTriggerByName("not_existing_trigger_name")).thenThrow(new TriggerNotFoundException("not_existing_trigger_name")); + + mockMvc.perform(get(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL + "/not_existing_trigger_name") + .contentType(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isNotFound()); + } + + @Test + void givenASimpleTriggerCommandDTO_whenPosted_thenANewSimpleTriggerIsCreated() throws Exception { + SimpleTriggerInputDTO simpleTriggerInputDTO = buildSimpleTriggerCommandDTO(); + SimpleTriggerDTO expectedSimpleTriggerDTO = TriggerUtils.getSimpleTriggerInstance("mytrigger", simpleTriggerInputDTO); + Mockito.when(simpleTriggerSchedulerService.scheduleSimpleTrigger(any(), any())).thenReturn(expectedSimpleTriggerDTO); + mockMvc.perform( + post(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtils.toJson(simpleTriggerInputDTO)) + ) + .andExpect(MockMvcResultMatchers.status().isCreated()) + .andExpect(MockMvcResultMatchers.content().json(TestUtils.toJson(expectedSimpleTriggerDTO))) + ; + } + + private SimpleTriggerInputDTO buildSimpleTriggerCommandDTO() { + return SimpleTriggerInputDTO.builder() + .startDate(new Date()) + .repeatCount(20) + .repeatInterval(20000L) + .build(); + } + + @ParameterizedTest + @ArgumentsSource(InvalidSimpleTriggerCommandDTOProvider.class) + void givenAnInvalidSimpleTriggerCommandDTO_whenPostedANewTrigger_thenAnErrorIsReturned(SimpleTriggerInputDTO invalidSimpleTriggerComandDTO) throws Exception { + mockMvc.perform(post(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtils.toJson(invalidSimpleTriggerComandDTO))) + .andExpect(MockMvcResultMatchers.status().is4xxClientError()); + } + + @Test + void givenATriggerName_whenPutSimpleTriggerCommandDTO_thenTheSimpleTriggerIsRescheduled() throws Exception { + SimpleTriggerInputDTO simpleTriggerInputDTO = buildSimpleTriggerCommandDTO(); + SimpleTriggerDTO expectedSimpleTriggerDTO = TriggerUtils.getSimpleTriggerInstance("mytrigger", simpleTriggerInputDTO); + SimpleTriggerCommandDTO simpleTriggerCommandDTO = SimpleTriggerCommandDTO.builder() + .triggerName("mytrigger") + .simpleTriggerInputDTO(simpleTriggerInputDTO) + .build(); + Mockito.when(simpleTriggerSchedulerService.rescheduleSimpleTrigger(simpleTriggerCommandDTO)).thenReturn(expectedSimpleTriggerDTO); + + mockMvc.perform(put(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtils.toJson(simpleTriggerInputDTO))) + .andExpect(MockMvcResultMatchers.status().isOk()) + .andExpect(MockMvcResultMatchers.content().json(TestUtils.toJson(expectedSimpleTriggerDTO))); + } + + @ParameterizedTest + @ArgumentsSource(InvalidSimpleTriggerCommandDTOProvider.class) + void givenAnInvalidSimpleTriggerCommandDTO_whenATriggerIsRescheduled_thenAnErrorIsReturned(SimpleTriggerInputDTO invalidSimpleTriggerCommandTO) throws Exception { + mockMvc.perform(put(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtils.toJson(invalidSimpleTriggerCommandTO))) + .andExpect(MockMvcResultMatchers.status().is4xxClientError()); + } + +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java index 85ec109..6f741ba 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java @@ -6,7 +6,7 @@ import it.fabioformosa.quartzmanager.controllers.utils.TestUtils; import it.fabioformosa.quartzmanager.controllers.utils.TriggerUtils; import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam; import it.fabioformosa.quartzmanager.dto.TriggerDTO; -import it.fabioformosa.quartzmanager.services.SchedulerService; +import it.fabioformosa.quartzmanager.services.LegacySchedulerService; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -25,7 +25,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder @ContextConfiguration(classes = {QuartManagerApplicationTests.class}) @WebMvcTest(controllers = TriggerController.class, properties = { - "quartz-manager.jobClass=it.fabioformosa.quartzmanager.jobs.myjobs.SampleJob" + "quartz-manager.jobClass=it.fabioformosa.quartzmanager.jobs.SampleJob" }) class TriggerControllerTest { @@ -33,7 +33,7 @@ class TriggerControllerTest { private MockMvc mockMvc; @MockBean - private SchedulerService schedulerService; + private LegacySchedulerService schedulerService; @AfterEach void cleanUp(){ @@ -67,7 +67,7 @@ class TriggerControllerTest { @Test void whenGetIsCalled_thenATriggerIsReturned() throws Exception { TriggerDTO expectedTriggerDTO = TriggerUtils.getTriggerInstance("mytrigger"); - Mockito.when(schedulerService.getTriggerByName("mytrigger")).thenReturn(expectedTriggerDTO); + Mockito.when(schedulerService.getLegacyTriggerByName("mytrigger")).thenReturn(expectedTriggerDTO); mockMvc.perform(get(TriggerController.TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") .contentType(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk()) diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/InvalidSimpleTriggerCommandDTOProvider.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/InvalidSimpleTriggerCommandDTOProvider.java new file mode 100644 index 0000000..9cdd068 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/InvalidSimpleTriggerCommandDTOProvider.java @@ -0,0 +1,19 @@ +package it.fabioformosa.quartzmanager.controllers.utils; + +import it.fabioformosa.quartzmanager.dto.SimpleTriggerInputDTO; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; + +import java.util.stream.Stream; + +public class InvalidSimpleTriggerCommandDTOProvider implements ArgumentsProvider { + @Override + public Stream provideArguments(ExtensionContext extensionContext) throws Exception { + return Stream.of( + Arguments.of(SimpleTriggerInputDTO.builder().build()), + Arguments.of(SimpleTriggerInputDTO.builder().repeatCount(1).build()), + Arguments.of(SimpleTriggerInputDTO.builder().repeatInterval(1L).build()) + ); + } +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/TriggerUtils.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/TriggerUtils.java index 2fb9080..7ee9335 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/TriggerUtils.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/TriggerUtils.java @@ -1,9 +1,7 @@ package it.fabioformosa.quartzmanager.controllers.utils; import it.fabioformosa.quartzmanager.common.utils.DateUtils; -import it.fabioformosa.quartzmanager.dto.JobKeyDTO; -import it.fabioformosa.quartzmanager.dto.TriggerDTO; -import it.fabioformosa.quartzmanager.dto.TriggerKeyDTO; +import it.fabioformosa.quartzmanager.dto.*; import java.time.LocalDateTime; @@ -30,4 +28,50 @@ public class TriggerUtils { .build(); } + static public SimpleTriggerDTO getSimpleTriggerInstance(String triggerName, SimpleTriggerInputDTO simpleTriggerInputDTO){ + return SimpleTriggerDTO.builder() + .description("simple trigger") + .repeatCount(simpleTriggerInputDTO.getRepeatCount()) + .repeatInterval(simpleTriggerInputDTO.getRepeatInterval()) + .endTime(DateUtils.getHoursFromNow(2L)) + .finalFireTime(DateUtils.getHoursFromNow(2L)) + .jobKeyDTO(JobKeyDTO.builder() + .group("defaultJobGroup") + .name("sampleJob") + .build()) + .mayFireAgain(true) + .triggerKeyDTO(TriggerKeyDTO.builder() + .group("defaultTriggerGroup") + .name(triggerName) + .build()) + .misfireInstruction(1) + .nextFireTime(DateUtils.getHoursFromNow(1L)) + .priority(1) + .startTime(DateUtils.fromLocaleDateTimeToDate(LocalDateTime.now())) + .build(); + } + + static public SimpleTriggerDTO getSimpleTriggerInstance(String triggerName){ + return SimpleTriggerDTO.builder() + .description("simple trigger") + .repeatCount(2) + .repeatInterval(1000L) + .endTime(DateUtils.getHoursFromNow(2L)) + .finalFireTime(DateUtils.getHoursFromNow(2L)) + .jobKeyDTO(JobKeyDTO.builder() + .group("defaultJobGroup") + .name("sampleJob") + .build()) + .mayFireAgain(true) + .triggerKeyDTO(TriggerKeyDTO.builder() + .group("defaultTriggerGroup") + .name(triggerName) + .build()) + .misfireInstruction(1) + .nextFireTime(DateUtils.getHoursFromNow(1L)) + .priority(1) + .startTime(DateUtils.fromLocaleDateTimeToDate(LocalDateTime.now())) + .build(); + } + } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/jobs/SampleJob.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/jobs/SampleJob.java new file mode 100644 index 0000000..f8fb8d3 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/jobs/SampleJob.java @@ -0,0 +1,14 @@ +package it.fabioformosa.quartzmanager.jobs; + +import it.fabioformosa.quartzmanager.jobs.entities.LogRecord; +import it.fabioformosa.quartzmanager.jobs.entities.LogRecord.LogType; +import org.quartz.JobExecutionContext; + +public class SampleJob extends AbstractLoggingJob { + + @Override + public LogRecord doIt(JobExecutionContext jobExecutionContext) { + return new LogRecord(LogType.INFO, "Hello!"); + } + +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerServiceTest.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerServiceTest.java new file mode 100644 index 0000000..ecf8bb9 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerServiceTest.java @@ -0,0 +1,81 @@ +package it.fabioformosa.quartzmanager.services; + +import it.fabioformosa.quartzmanager.common.utils.DateUtils; +import it.fabioformosa.quartzmanager.dto.*; +import it.fabioformosa.quartzmanager.exceptions.TriggerNotFoundException; +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.quartz.SimpleTrigger; +import org.springframework.core.convert.ConversionService; + +import java.util.Date; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.MockitoAnnotations.openMocks; + +class SimpleTriggerSchedulerServiceTest { + + @InjectMocks + private SimpleTriggerSchedulerService simpleSchedulerService; + + @Mock + private Scheduler scheduler; + + @Mock + private ConversionService conversionService; + + @BeforeEach + void setUp() { + openMocks(this); + } + + @Test + void givenANotExistingTrigger_whenGetSimplerTriggerByNameIsCalled_thenThrowException() throws SchedulerException { + String not_existing_trigger = "not_existing_trigger"; + Mockito.when(scheduler.getTrigger(any())).thenReturn(null); + + Throwable throwable = Assertions.catchThrowable(() -> simpleSchedulerService.getSimpleTriggerByName(not_existing_trigger)); + Assertions.assertThat(throwable).isInstanceOf(TriggerNotFoundException.class); + } + + @Test + void givenASimpleTriggerCommandDTO_whenASimpleTriggerIsScheduled_thenATriggerDTOIsReturned() throws SchedulerException, ClassNotFoundException { + SimpleTriggerInputDTO triggerInputDTO = SimpleTriggerInputDTO.builder() + .startDate(new Date()) + .repeatInterval(5000L).repeatCount(5) + .endDate(DateUtils.getHoursFromNow(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.scheduleJob(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.scheduleSimpleTrigger("it.fabioformosa.quartzmanager.jobs.SampleJob", simpleTriggerCommandDTO); + + Assertions.assertThat(simpleTrigger).isEqualTo(expectedTriggerDTO); + } + +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/resources/application.properties b/quartz-manager-parent/quartz-manager-starter-api/src/test/resources/application.properties deleted file mode 100644 index 4dfe84c..0000000 --- a/quartz-manager-parent/quartz-manager-starter-api/src/test/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -name: Phil \ No newline at end of file diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/resources/application.yml b/quartz-manager-parent/quartz-manager-starter-api/src/test/resources/application.yml new file mode 100644 index 0000000..88a3041 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/resources/application.yml @@ -0,0 +1,18 @@ +app: + name: quartz-manager + +quartz: + enabled: true + +job: + frequency: 4000 + repeatCount: 19 + +logging: + level: + org.springframework.boot.autoconfigure.security: INFO + it.fabioformosa: DEBUG + org.quartz: INFO + +quartz-manager: + jobClass: it.fabioformosa.quartzmanager.jobs.SampleJob From 86badb8f416c41bb963cbd40d4c2a70a04bfe0e2 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Sat, 5 Mar 2022 20:49:24 +0100 Subject: [PATCH 018/184] #56 added the simpleTrigger to the API --- .../quartz-manager-starter-api/pom.xml | 20 +++++++++++-------- .../src/test/resources/quartz.properties | 2 ++ 2 files changed, 14 insertions(+), 8 deletions(-) create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/test/resources/quartz.properties diff --git a/quartz-manager-parent/quartz-manager-starter-api/pom.xml b/quartz-manager-parent/quartz-manager-starter-api/pom.xml index 63d327f..4b0b6ab 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-api/pom.xml @@ -6,12 +6,12 @@ quartz-manager-parent 3.1.1-SNAPSHOT - + quartz-manager-starter-api - + Quartz Manager Starter API REST API layer for your scheduler and triggered jobs, to be included in your spring webapp - + https://github.com/fabioformosa/quartz-manager ${basedir}/../.. @@ -20,7 +20,7 @@ 1.5.12 1.8 - + it.fabioformosa.quartz-manager @@ -57,7 +57,7 @@ spring-boot-starter-test test - + com.fasterxml.jackson.core @@ -108,7 +108,7 @@ commons-io 1.3.2 - + io.projectreactor @@ -136,7 +136,7 @@ org.yaml snakeyaml - + org.springdoc @@ -160,6 +160,10 @@ junit-platform-launcher test + + org.springframework + spring-tx + - + diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/resources/quartz.properties b/quartz-manager-parent/quartz-manager-starter-api/src/test/resources/quartz.properties new file mode 100644 index 0000000..cdcc9e4 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/resources/quartz.properties @@ -0,0 +1,2 @@ +org.quartz.scheduler.instanceName=example +org.quartz.threadPool.threadCount=1 From 6bb768de59dd5718ef9fea70f20e8fd6f5de0ea5 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Sat, 5 Mar 2022 20:50:23 +0100 Subject: [PATCH 019/184] #56 added a new FE component to schedule a simple trigger --- quartz-manager-frontend/package.json | 2 + quartz-manager-frontend/src/app/app.module.ts | 10 ++ .../scheduler-config.component.html | 34 +++--- .../components/simple-trigger-config/index.ts | 2 + .../simple-trigger-config.component.html | 103 ++++++++++++++++++ .../simple-trigger-config.component.scss | 3 + .../simple-trigger-config.component.ts | 99 +++++++++++++++++ .../src/app/model/jobKey.model.ts | 4 + .../src/app/model/simple-trigger.command.ts | 6 + .../src/app/model/simple-trigger.form.ts | 8 ++ .../src/app/model/simple-trigger.model.ts | 7 ++ .../src/app/model/trigger.model.ts | 15 +++ .../src/app/services/scheduler.service.ts | 19 +++- .../app/views/manager/manager.component.html | 2 +- 14 files changed, 296 insertions(+), 18 deletions(-) create mode 100644 quartz-manager-frontend/src/app/components/simple-trigger-config/index.ts create mode 100644 quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html create mode 100644 quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.scss create mode 100644 quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts create mode 100644 quartz-manager-frontend/src/app/model/jobKey.model.ts create mode 100644 quartz-manager-frontend/src/app/model/simple-trigger.command.ts create mode 100644 quartz-manager-frontend/src/app/model/simple-trigger.form.ts create mode 100644 quartz-manager-frontend/src/app/model/simple-trigger.model.ts create mode 100644 quartz-manager-frontend/src/app/model/trigger.model.ts diff --git a/quartz-manager-frontend/package.json b/quartz-manager-frontend/package.json index 5e33306..7608d51 100644 --- a/quartz-manager-frontend/package.json +++ b/quartz-manager-frontend/package.json @@ -12,6 +12,7 @@ }, "private": true, "dependencies": { + "@angular-material-components/datetime-picker": "2.0.4", "@angular/animations": "9.1.4", "@angular/cdk": "9.2.1", "@angular/common": "9.1.4", @@ -32,6 +33,7 @@ "@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", "stompjs": "^2.3.3", diff --git a/quartz-manager-frontend/src/app/app.module.ts b/quartz-manager-frontend/src/app/app.module.ts index 5274d28..57b3a54 100644 --- a/quartz-manager-frontend/src/app/app.module.ts +++ b/quartz-manager-frontend/src/app/app.module.ts @@ -17,6 +17,11 @@ import {MatToolbarModule} from '@angular/material/toolbar'; import {MatIconModule} from '@angular/material/icon'; import {MatButtonModule} from '@angular/material/button'; import {MatCardModule} from '@angular/material/card'; +import {MatDatepickerModule} from '@angular/material/datepicker'; + +import {MatNativeDateModule} from '@angular/material/core'; +import { NgxMatTimepickerModule, NgxMatDatetimePickerModule} from '@angular-material-components/datetime-picker'; +import { NgxMatMomentModule } from '@angular-material-components/moment-adapter'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { FlexLayoutModule } from '@angular/flex-layout'; @@ -52,6 +57,7 @@ import { ChangePasswordComponent } from './views/change-password/change-password import { ForbiddenComponent } from './views/forbidden/forbidden.component'; import { APP_BASE_HREF } from '@angular/common'; import { environment } from '../environments/environment'; +import {SimpleTriggerConfigComponent} from './components/simple-trigger-config'; export function initUserFactory(userService: UserService) { return () => userService.jsessionInitUser(); @@ -102,6 +108,7 @@ export function jwtOptionsFactory(apiService: ApiService) { NotFoundComponent, AccountMenuComponent, SchedulerConfigComponent, + SimpleTriggerConfigComponent, SchedulerControlComponent, LogsPanelComponent, ProgressPanelComponent, @@ -132,6 +139,9 @@ export function jwtOptionsFactory(apiService: ApiService) { MatCardModule, MatProgressSpinnerModule, MatProgressBarModule, + MatDatepickerModule, MatNativeDateModule, + NgxMatMomentModule, + NgxMatDatetimePickerModule, FlexLayoutModule ], providers: [ diff --git a/quartz-manager-frontend/src/app/components/scheduler-config/scheduler-config.component.html b/quartz-manager-frontend/src/app/components/scheduler-config/scheduler-config.component.html index cf39730..fe87e40 100644 --- a/quartz-manager-frontend/src/app/components/scheduler-config/scheduler-config.component.html +++ b/quartz-manager-frontend/src/app/components/scheduler-config/scheduler-config.component.html @@ -14,23 +14,25 @@
- - Freq [Num per day] - - - - Max Occurrences - - - +
+ + Freq [Num per day] + + +
+
+ + Max Occurrences + + +

-
Misfire Policy
RESCHEDULE NEXT WITH REMAINING COUNT
diff --git a/quartz-manager-frontend/src/app/components/simple-trigger-config/index.ts b/quartz-manager-frontend/src/app/components/simple-trigger-config/index.ts new file mode 100644 index 0000000..8612d8b --- /dev/null +++ b/quartz-manager-frontend/src/app/components/simple-trigger-config/index.ts @@ -0,0 +1,2 @@ +export * from './simple-trigger-config.component'; +export {SimpleTriggerCommand} from '../../model/simple-trigger.command'; diff --git a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html new file mode 100644 index 0000000..40793a3 --- /dev/null +++ b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html @@ -0,0 +1,103 @@ + + + SCHEDULER CONFIG + + + + + + + + + + +
+ + +
+ + Start Date (optional) + + + + + + + + +
+ +
+ + End Date (optional) + + + + + + + + +
+ +
+ + Repeat Interval [in mills] + + +
+
+ + Repeat Count + + +
+
+
+
Misfire Policy
+
RESCHEDULE NEXT WITH REMAINING COUNT
+
+ In case of misfire event, the trigger is re-scheduled to the next scheduled time after 'now' with the repeat count set to what it would be, if it had not missed any firings. +
+ Warning: This policy could cause the Trigger to go directly to the 'COMPLETE' state if all fire-times where missed. +
+
+ +
+ + + + + +
+
+
diff --git a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.scss b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.scss new file mode 100644 index 0000000..e4cf5d5 --- /dev/null +++ b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.scss @@ -0,0 +1,3 @@ +.small{ + font-size: 0.8em; +} diff --git a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts new file mode 100644 index 0000000..8efd796 --- /dev/null +++ b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts @@ -0,0 +1,99 @@ +import {Component, OnInit} from '@angular/core'; +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'; + +@Component({ + selector: 'qrzmng-simple-trigger-config', + templateUrl: './simple-trigger-config.component.html', + styleUrls: ['./simple-trigger-config.component.scss'] +}) +export class SimpleTriggerConfigComponent implements OnInit { + + simpleTriggerForm: SimpleTriggerForm = new SimpleTriggerForm(); + formBackup: SimpleTriggerForm = new SimpleTriggerForm(); + + trigger: SimpleTrigger; + scheduler: Scheduler; + + triggerLoading = true; + enabledTriggerForm = false; + private fetchedTriggers = false; + private triggerInProgress = false; + + constructor( + private schedulerService: SchedulerService + ) { } + + ngOnInit() { + this.triggerLoading = true; + this.retrieveConfiguredTriggerIfExists(); + } + + retrieveConfiguredTriggerIfExists = () => { + this.schedulerService.getSimpleTriggerConfig() + .subscribe((retTrigger: SimpleTrigger) => { + this.trigger = retTrigger; + this.formBackup = this.simpleTriggerForm; + this.simpleTriggerForm = this._fromTriggerToForm(retTrigger); + + this.triggerLoading = false; + this.triggerInProgress = this.trigger.mayFireAgain; + }) + } + + existsATriggerInProgress = (): boolean => this.trigger && this.triggerInProgress; + + cancelConfigForm = () => this.enabledTriggerForm = false; + + submitConfig = () => { + const schedulerServiceCall = this.existsATriggerInProgress() ? + this.schedulerService.updateSimpleTriggerConfig : this.schedulerService.saveSimpleTriggerConfig; + + const simpleTriggerCommand = this._fromFormToCommand(this.simpleTriggerForm); + schedulerServiceCall(simpleTriggerCommand) + .subscribe((retTrigger: SimpleTrigger) => { + this.trigger = retTrigger; + this.formBackup = this.simpleTriggerForm; + this.simpleTriggerForm = this._fromTriggerToForm(retTrigger); + this.enabledTriggerForm = false; + this.fetchedTriggers = true; + this.triggerInProgress = this.trigger.mayFireAgain; + }, error => { + this.simpleTriggerForm = this.formBackup; + }); + }; + + enableTriggerForm = () => this.enabledTriggerForm = true; + + private _fromTriggerToCommand = (simpleTrigger: SimpleTrigger) => { + const command = new SimpleTriggerCommand(); + command.repeatCount = simpleTrigger.repeatCount; + command.repeatInterval = simpleTrigger.repeatInterval; + command.startDate = simpleTrigger.startTime; + command.endDate = simpleTrigger.endTime; + return command; + } + + private _fromTriggerToForm = (simpleTrigger: SimpleTrigger): SimpleTriggerForm => { + const command = new SimpleTriggerForm(); + command.repeatCount = simpleTrigger.repeatCount; + command.repeatInterval = simpleTrigger.repeatInterval; + command.startDate = moment(simpleTrigger.startTime); + command.endDate = moment(simpleTrigger.endTime); + return command; + } + + private _fromFormToCommand = (simpleTriggerForm: SimpleTriggerForm): SimpleTriggerCommand => { + const simpleTriggerCommand = new SimpleTriggerCommand(); + simpleTriggerCommand.repeatCount = simpleTriggerForm.repeatCount; + simpleTriggerCommand.repeatInterval = simpleTriggerForm.repeatInterval; + simpleTriggerCommand.startDate = simpleTriggerForm.startDate.toDate(); + simpleTriggerCommand.endDate = simpleTriggerForm.endDate.toDate(); + return simpleTriggerCommand; + } + +} diff --git a/quartz-manager-frontend/src/app/model/jobKey.model.ts b/quartz-manager-frontend/src/app/model/jobKey.model.ts new file mode 100644 index 0000000..afc7171 --- /dev/null +++ b/quartz-manager-frontend/src/app/model/jobKey.model.ts @@ -0,0 +1,4 @@ +export class JobKeyModel { + name: string; + group: string; +} diff --git a/quartz-manager-frontend/src/app/model/simple-trigger.command.ts b/quartz-manager-frontend/src/app/model/simple-trigger.command.ts new file mode 100644 index 0000000..3d9db64 --- /dev/null +++ b/quartz-manager-frontend/src/app/model/simple-trigger.command.ts @@ -0,0 +1,6 @@ +export class SimpleTriggerCommand { + startDate: Date; + endDate: Date; + repeatCount: number; + repeatInterval: number; +} diff --git a/quartz-manager-frontend/src/app/model/simple-trigger.form.ts b/quartz-manager-frontend/src/app/model/simple-trigger.form.ts new file mode 100644 index 0000000..98a0210 --- /dev/null +++ b/quartz-manager-frontend/src/app/model/simple-trigger.form.ts @@ -0,0 +1,8 @@ +import {Moment} from 'moment/moment'; + +export class SimpleTriggerForm { + startDate: Moment; + endDate: Moment; + repeatCount: number; + repeatInterval: number; +} diff --git a/quartz-manager-frontend/src/app/model/simple-trigger.model.ts b/quartz-manager-frontend/src/app/model/simple-trigger.model.ts new file mode 100644 index 0000000..d5e2537 --- /dev/null +++ b/quartz-manager-frontend/src/app/model/simple-trigger.model.ts @@ -0,0 +1,7 @@ +import {Trigger} from './trigger.model'; + +export class SimpleTrigger extends Trigger { + repeatCount: number; + repeatInterval: number; + timesTriggered: number; +} diff --git a/quartz-manager-frontend/src/app/model/trigger.model.ts b/quartz-manager-frontend/src/app/model/trigger.model.ts new file mode 100644 index 0000000..5b67f47 --- /dev/null +++ b/quartz-manager-frontend/src/app/model/trigger.model.ts @@ -0,0 +1,15 @@ +import {TriggerKey} from './triggerKey.model'; +import {JobKeyModel} from './jobKey.model'; + +export class Trigger { + triggerKeyDTO: TriggerKey; + priority: number; + startTime: Date; + description: string; + endTime: Date; + finalFireTime: Date; + misfireInstruction: number; + nextFireTime: Date; + jobKeyDTO: JobKeyModel; + mayFireAgain: boolean; +} diff --git a/quartz-manager-frontend/src/app/services/scheduler.service.ts b/quartz-manager-frontend/src/app/services/scheduler.service.ts index ead76f3..53df2bb 100644 --- a/quartz-manager-frontend/src/app/services/scheduler.service.ts +++ b/quartz-manager-frontend/src/app/services/scheduler.service.ts @@ -1,6 +1,10 @@ import { Injectable } from '@angular/core'; import { getBaseUrl } from '.'; import { ApiService } from './api.service'; +import {Trigger} from '../model/trigger.model'; +import {Observable} from 'rxjs'; +import {SimpleTriggerCommand} from '../model/simple-trigger.command'; +import {SchedulerConfig} from '../model/schedulerConfig.model'; @Injectable() export class SchedulerService { @@ -33,17 +37,30 @@ export class SchedulerService { return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler') } + // deprecated getConfig = () => { return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler/config') } + getSimpleTriggerConfig = (): Observable => { + return this.apiService.get(getBaseUrl() + '/quartz-manager/simple-triggers/my-simple-trigger'); + } + saveConfig = (config: Object) => { return this.apiService.post(getBaseUrl() + '/quartz-manager/triggers/mytrigger', config) } - updateConfig = (config: Object) => { + saveSimpleTriggerConfig = (config: SimpleTriggerCommand) => { + return this.apiService.post(getBaseUrl() + '/quartz-manager/simple-triggers/mytrigger', config) + } + + updateConfig = (config: SchedulerConfig) => { return this.apiService.put(getBaseUrl() + '/quartz-manager/triggers/mytrigger', config) } + updateSimpleTriggerConfig = (config: SimpleTriggerCommand) => { + return this.apiService.put(getBaseUrl() + '/quartz-manager/simple-triggers/mytrigger', config) + } + } diff --git a/quartz-manager-frontend/src/app/views/manager/manager.component.html b/quartz-manager-frontend/src/app/views/manager/manager.component.html index ac239e1..7c5e377 100644 --- a/quartz-manager-frontend/src/app/views/manager/manager.component.html +++ b/quartz-manager-frontend/src/app/views/manager/manager.component.html @@ -4,7 +4,7 @@

- +
From 09df7795a9bf470116cb84c37efd9c5107d3f8ed Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Tue, 5 Jul 2022 19:06:21 +0200 Subject: [PATCH 020/184] #56 minor commit --- .../scheduler-config/scheduler-config.component.ts | 3 +++ .../src/app/services/scheduler.service.ts | 6 ++++-- .../controllers/SchedulerController.java | 3 ++- .../quartzmanager/controllers/TriggerController.java | 1 - .../src/main/resources/application.yml | 12 +++++++----- 5 files changed, 16 insertions(+), 9 deletions(-) diff --git a/quartz-manager-frontend/src/app/components/scheduler-config/scheduler-config.component.ts b/quartz-manager-frontend/src/app/components/scheduler-config/scheduler-config.component.ts index 7da523a..17b404e 100644 --- a/quartz-manager-frontend/src/app/components/scheduler-config/scheduler-config.component.ts +++ b/quartz-manager-frontend/src/app/components/scheduler-config/scheduler-config.component.ts @@ -3,6 +3,9 @@ import { SchedulerService } from '../../services'; import { SchedulerConfig } from '../../model/schedulerConfig.model' import {Scheduler} from '../../model/scheduler.model'; +/** + * DEPRECATED! Not more used + */ @Component({ selector: 'qrzmng-scheduler-config', templateUrl: './scheduler-config.component.html', diff --git a/quartz-manager-frontend/src/app/services/scheduler.service.ts b/quartz-manager-frontend/src/app/services/scheduler.service.ts index 53df2bb..7320dc9 100644 --- a/quartz-manager-frontend/src/app/services/scheduler.service.ts +++ b/quartz-manager-frontend/src/app/services/scheduler.service.ts @@ -46,20 +46,22 @@ export class SchedulerService { return this.apiService.get(getBaseUrl() + '/quartz-manager/simple-triggers/my-simple-trigger'); } + // deprecated saveConfig = (config: Object) => { return this.apiService.post(getBaseUrl() + '/quartz-manager/triggers/mytrigger', config) } saveSimpleTriggerConfig = (config: SimpleTriggerCommand) => { - return this.apiService.post(getBaseUrl() + '/quartz-manager/simple-triggers/mytrigger', config) + return this.apiService.post(getBaseUrl() + '/quartz-manager/simple-triggers/my-simple-trigger', config) } + // deprecated updateConfig = (config: SchedulerConfig) => { return this.apiService.put(getBaseUrl() + '/quartz-manager/triggers/mytrigger', config) } updateSimpleTriggerConfig = (config: SimpleTriggerCommand) => { - return this.apiService.put(getBaseUrl() + '/quartz-manager/simple-triggers/mytrigger', config) + return this.apiService.put(getBaseUrl() + '/quartz-manager/simple-triggers/my-simple-trigger', config) } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java index 065a601..6799df1 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java @@ -50,7 +50,8 @@ public class SchedulerController { @Resource private ConversionService conversionService; - //TODO replace this a list of trigger + @Deprecated + //TODO to be removed when the legacy trigger is removed @GetMapping("/config") @Operation(summary = "Get the config of the trigger") @ApiResponses(value = { diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java index 47e60c5..47ea4a9 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java @@ -24,7 +24,6 @@ import javax.validation.Valid; public class TriggerController extends AbstractTriggerController { static public final String TRIGGER_CONTROLLER_BASE_URL = "/quartz-manager/triggers"; - static public final String SIMPLE_TRIGGER_BASE_URL = "/simple-triggers"; private LegacySchedulerService schedulerService; diff --git a/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml b/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml index 0b9e144..66b3ad5 100644 --- a/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml +++ b/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml @@ -6,9 +6,9 @@ server: app: name: quartz-manager - + spring: - thymeleaf: + thymeleaf: cache: false mode: LEGACYHTML5 jpa.open-in-view: false @@ -16,7 +16,7 @@ spring: quartz: enabled: true -job: +job: frequency: 4000 repeatCount: 19 @@ -27,8 +27,10 @@ logging: org.springframework.boot.autoconfigure.security: INFO it.fabioformosa: DEBUG org.quartz: INFO - + quartz-manager: + trigger: + name: "sampletrigger" persistence: quartz: datasource: @@ -57,4 +59,4 @@ quartz-manager: - name: admin password: admin roles: - - ADMIN + - ADMIN From f1c9fba68ef3d0028e15aedb3f82b95d80056ea5 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Thu, 4 Aug 2022 00:43:53 +0200 Subject: [PATCH 021/184] #56 step into layout revamp --- .../src/app/app.component.html | 11 +- .../src/app/app.component.scss | 15 +-- quartz-manager-frontend/src/app/app.module.ts | 14 ++- .../components/footer/footer.component.html | 15 +-- .../components/footer/footer.component.scss | 22 ++-- .../components/header/header.component.html | 2 +- .../src/app/components/index.ts | 1 + .../progress-panel.component.html | 18 +-- .../scheduler-control.component.html | 35 ++++-- .../scheduler-control.component.scss | 13 ++- .../scheduler-control.component.spec.ts | 105 ++++++++++++++++++ .../scheduler-control.component.ts | 78 ++++++++----- .../simple-trigger-config.component.html | 2 +- .../src/app/components/trigger-list/index.ts | 1 + .../trigger-list/trigger-list.component.html | 19 ++++ .../trigger-list/trigger-list.component.scss | 21 ++++ .../trigger-list/trigger-list.component.ts | 36 ++++++ .../src/app/model/scheduler.model.ts | 4 +- .../src/app/services/index.ts | 1 + .../src/app/services/scheduler.service.ts | 11 +- .../src/app/services/trigger.service.ts | 20 ++++ .../app/views/manager/manager.component.html | 41 ++++--- .../app/views/manager/manager.component.scss | 1 + .../app/views/manager/manager.component.ts | 8 +- quartz-manager-frontend/src/styles.css | 7 ++ .../controllers/SchedulerController.java | 52 ++++----- .../controllers/TriggerController.java | 12 +- .../converters/SchedulerToSchedulerDTO.java | 11 ++ .../quartzmanager/dto/SchedulerDTO.java | 32 ++---- ...edulerStates.java => SchedulerStatus.java} | 4 +- .../services/SchedulerService.java | 19 ++++ .../services/TriggerService.java | 34 ++++++ 32 files changed, 503 insertions(+), 162 deletions(-) create mode 100644 quartz-manager-frontend/src/app/components/scheduler-control/scheduler-control.component.spec.ts create mode 100644 quartz-manager-frontend/src/app/components/trigger-list/index.ts create mode 100644 quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.html create mode 100644 quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.scss create mode 100644 quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.ts create mode 100644 quartz-manager-frontend/src/app/services/trigger.service.ts rename quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/enums/{SchedulerStates.java => SchedulerStatus.java} (69%) create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SchedulerService.java create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/TriggerService.java diff --git a/quartz-manager-frontend/src/app/app.component.html b/quartz-manager-frontend/src/app/app.component.html index d2bb06e..91994a2 100644 --- a/quartz-manager-frontend/src/app/app.component.html +++ b/quartz-manager-frontend/src/app/app.component.html @@ -1,5 +1,8 @@ - -
- +
+ +
+ +
+
- + diff --git a/quartz-manager-frontend/src/app/app.component.scss b/quartz-manager-frontend/src/app/app.component.scss index 5293b8d..6e1eb96 100644 --- a/quartz-manager-frontend/src/app/app.component.scss +++ b/quartz-manager-frontend/src/app/app.component.scss @@ -2,20 +2,9 @@ display: block; color: rgba(0,0,0,.54); font-family: Roboto,"Helvetica Neue"; + height: 100%; } .content { - margin: 50px 70px; -} - -@media screen and (min-width: 600px) and (max-width: 1279px) { - .content { - margin: 20px 30px; - } -} - -@media screen and (max-width: 599px) { - .content { - margin: 8px 12px; - } + padding: 20px; } diff --git a/quartz-manager-frontend/src/app/app.module.ts b/quartz-manager-frontend/src/app/app.module.ts index 57b3a54..15dd847 100644 --- a/quartz-manager-frontend/src/app/app.module.ts +++ b/quartz-manager-frontend/src/app/app.module.ts @@ -18,6 +18,8 @@ import {MatIconModule} from '@angular/material/icon'; import {MatButtonModule} from '@angular/material/button'; import {MatCardModule} from '@angular/material/card'; import {MatDatepickerModule} from '@angular/material/datepicker'; +import {MatListModule} from '@angular/material/list'; +import {MatSidenavModule} from '@angular/material/sidenav'; import {MatNativeDateModule} from '@angular/material/core'; import { NgxMatTimepickerModule, NgxMatDatetimePickerModule} from '@angular-material-components/datetime-picker'; @@ -40,7 +42,8 @@ import { SchedulerConfigComponent, SchedulerControlComponent, LogsPanelComponent, - ProgressPanelComponent + ProgressPanelComponent, + TriggerListComponent } from './components'; import { @@ -51,7 +54,8 @@ import { ConfigService, ProgressWebsocketService, LogsWebsocketService, - getHtmlBaseUrl + getHtmlBaseUrl, + TriggerService } from './services'; import { ChangePasswordComponent } from './views/change-password/change-password.component'; import { ForbiddenComponent } from './views/forbidden/forbidden.component'; @@ -113,7 +117,8 @@ export function jwtOptionsFactory(apiService: ApiService) { LogsPanelComponent, ProgressPanelComponent, ChangePasswordComponent, - ForbiddenComponent + ForbiddenComponent, + TriggerListComponent ], imports: [ BrowserAnimationsModule, @@ -137,11 +142,13 @@ export function jwtOptionsFactory(apiService: ApiService) { MatInputModule, MatToolbarModule, MatCardModule, + MatListModule, MatProgressSpinnerModule, MatProgressBarModule, MatDatepickerModule, MatNativeDateModule, NgxMatMomentModule, NgxMatDatetimePickerModule, + MatSidenavModule, FlexLayoutModule ], providers: [ @@ -159,6 +166,7 @@ export function jwtOptionsFactory(apiService: ApiService) { GuestGuard, AdminGuard, SchedulerService, + TriggerService, ProgressWebsocketService, LogsWebsocketService, AuthService, diff --git a/quartz-manager-frontend/src/app/components/footer/footer.component.html b/quartz-manager-frontend/src/app/components/footer/footer.component.html index 43d36df..ddc9c70 100644 --- a/quartz-manager-frontend/src/app/components/footer/footer.component.html +++ b/quartz-manager-frontend/src/app/components/footer/footer.component.html @@ -1,7 +1,8 @@ -

- Hand crafted with love by - Fabio Formosa -

- - - + + + +   Quartz Manager + + + + diff --git a/quartz-manager-frontend/src/app/components/footer/footer.component.scss b/quartz-manager-frontend/src/app/components/footer/footer.component.scss index a5f4583..786b1b1 100644 --- a/quartz-manager-frontend/src/app/components/footer/footer.component.scss +++ b/quartz-manager-frontend/src/app/components/footer/footer.component.scss @@ -1,18 +1,20 @@ -:host { - display: block; - font-weight: 300; - font-size: 15px; - display: block; +:host{ + //position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 64px; +} + +#footer{ background-color: rgb(33, 33, 33); - height: 236px; - padding: 72px 24px; + font-size: 15px; box-sizing: border-box; text-align: center; a { text-decoration: none; - cursor: auto; + cursor: pointer; color: #FFFFFF; - margin-top: 32px; } h3 { @@ -21,7 +23,5 @@ font-weight: 300; font-size: 22px; } - } - diff --git a/quartz-manager-frontend/src/app/components/header/header.component.html b/quartz-manager-frontend/src/app/components/header/header.component.html index 7d8b4b4..b44ce6a 100644 --- a/quartz-manager-frontend/src/app/components/header/header.component.html +++ b/quartz-manager-frontend/src/app/components/header/header.component.html @@ -1,6 +1,6 @@ diff --git a/quartz-manager-frontend/src/app/components/index.ts b/quartz-manager-frontend/src/app/components/index.ts index 54bec53..fe73207 100644 --- a/quartz-manager-frontend/src/app/components/index.ts +++ b/quartz-manager-frontend/src/app/components/index.ts @@ -5,3 +5,4 @@ export * from './logs-panel'; export * from './scheduler-config'; export * from './scheduler-control'; export * from './progress-panel'; +export * from './trigger-list'; diff --git a/quartz-manager-frontend/src/app/components/progress-panel/progress-panel.component.html b/quartz-manager-frontend/src/app/components/progress-panel/progress-panel.component.html index a5da15c..479abc6 100644 --- a/quartz-manager-frontend/src/app/components/progress-panel/progress-panel.component.html +++ b/quartz-manager-frontend/src/app/components/progress-panel/progress-panel.component.html @@ -1,6 +1,6 @@ diff --git a/quartz-manager-frontend/src/app/components/trigger-list/index.ts b/quartz-manager-frontend/src/app/components/trigger-list/index.ts new file mode 100644 index 0000000..2dadc65 --- /dev/null +++ b/quartz-manager-frontend/src/app/components/trigger-list/index.ts @@ -0,0 +1 @@ +export * from './trigger-list.component' diff --git a/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.html b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.html new file mode 100644 index 0000000..36e76a9 --- /dev/null +++ b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.html @@ -0,0 +1,19 @@ + + + TRIGGERS + + + + + + + {{ triggerKey.name }} + + + + + + + diff --git a/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.scss b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.scss new file mode 100644 index 0000000..8507fa3 --- /dev/null +++ b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.scss @@ -0,0 +1,21 @@ +/* ===== Scrollbar CSS ===== */ +/* Firefox */ +* { + scrollbar-width: auto; + scrollbar-color: #b8b8b8 #ffffff; +} + +/* Chrome, Edge, and Safari */ +*::-webkit-scrollbar { + width: 16px; +} + +*::-webkit-scrollbar-track { + background: #ffffff; +} + +*::-webkit-scrollbar-thumb { + background-color: #b8b8b8; + border-radius: 10px; + border: 3px solid #ffffff; +} diff --git a/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.ts b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.ts new file mode 100644 index 0000000..490f68b --- /dev/null +++ b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.ts @@ -0,0 +1,36 @@ +import {Component, EventEmitter, OnInit, Output} from '@angular/core'; +import {TriggerService} from '../../services/trigger.service'; +import {TriggerKey} from '../../model/triggerKey.model'; + +@Component({ + selector: 'qrzmng-trigger-list', + templateUrl: './trigger-list.component.html', + styleUrls: ['./trigger-list.component.scss'] +}) +export class TriggerListComponent implements OnInit { + + loading = true; + triggerKeys: Array = []; + + @Output() openedNewTriggerFormEvent = new EventEmitter(); + + constructor( + private triggerService: TriggerService + ) { } + + ngOnInit() { + this.loading = true; + this.fetchTriggers(); + } + + private fetchTriggers() { + this.triggerService.fetchTriggers() + .subscribe((triggerKeys: Array) => { + this.triggerKeys = triggerKeys; + }) + } + + openNewTriggerForm() { + this.openedNewTriggerFormEvent.emit(true); + } +} diff --git a/quartz-manager-frontend/src/app/model/scheduler.model.ts b/quartz-manager-frontend/src/app/model/scheduler.model.ts index adad75f..e7df13a 100644 --- a/quartz-manager-frontend/src/app/model/scheduler.model.ts +++ b/quartz-manager-frontend/src/app/model/scheduler.model.ts @@ -3,10 +3,12 @@ import {TriggerKey} from './triggerKey.model'; export class Scheduler { name: string; instanceId: string; + status: string; triggerKeys: TriggerKey[]; - constructor(name: string, instanceId: string, triggerKeys: TriggerKey[]) { + constructor(name: string, instanceId: string, status: string, triggerKeys: TriggerKey[]) { this.name = name; + this.status = status; this.instanceId = instanceId; this.triggerKeys = triggerKeys; } diff --git a/quartz-manager-frontend/src/app/services/index.ts b/quartz-manager-frontend/src/app/services/index.ts index db5951e..1352120 100644 --- a/quartz-manager-frontend/src/app/services/index.ts +++ b/quartz-manager-frontend/src/app/services/index.ts @@ -6,4 +6,5 @@ export * from './scheduler.service'; export * from './websocket.service'; export * from './progress.websocket.service'; export * from './logs.websocket.service'; +export * from './trigger.service' diff --git a/quartz-manager-frontend/src/app/services/scheduler.service.ts b/quartz-manager-frontend/src/app/services/scheduler.service.ts index 7320dc9..afc94ec 100644 --- a/quartz-manager-frontend/src/app/services/scheduler.service.ts +++ b/quartz-manager-frontend/src/app/services/scheduler.service.ts @@ -5,6 +5,7 @@ import {Trigger} from '../model/trigger.model'; import {Observable} from 'rxjs'; import {SimpleTriggerCommand} from '../model/simple-trigger.command'; import {SchedulerConfig} from '../model/schedulerConfig.model'; +import {Scheduler} from '../model/scheduler.model'; @Injectable() export class SchedulerService { @@ -13,19 +14,19 @@ export class SchedulerService { private apiService: ApiService ) { } - startScheduler = () => { + startScheduler = (): Observable => { return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler/run') } - stopScheduler = () => { + stopScheduler = (): Observable => { return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler/stop') } - pauseScheduler = () => { + pauseScheduler = (): Observable => { return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler/pause') } - resumeScheduler = () => { + resumeScheduler = (): Observable => { return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler/resume') } @@ -33,7 +34,7 @@ export class SchedulerService { return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler/status') } - getScheduler = () => { + getScheduler = (): Observable => { return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler') } diff --git a/quartz-manager-frontend/src/app/services/trigger.service.ts b/quartz-manager-frontend/src/app/services/trigger.service.ts new file mode 100644 index 0000000..55d5bf2 --- /dev/null +++ b/quartz-manager-frontend/src/app/services/trigger.service.ts @@ -0,0 +1,20 @@ +import {ApiService} from './api.service'; +import {Injectable} from '@angular/core'; +import {Observable} from 'rxjs'; +import {Trigger} from '../model/trigger.model'; +import {TriggerKey} from '../model/triggerKey.model'; +import {getBaseUrl} from './config.service'; + +@Injectable() +export class TriggerService { + + constructor( + private apiService: ApiService) { + } + + fetchTriggers = (): Observable> => { + return this.apiService.get(getBaseUrl() + 'quartz-manager/triggers'); + } + + +} diff --git a/quartz-manager-frontend/src/app/views/manager/manager.component.html b/quartz-manager-frontend/src/app/views/manager/manager.component.html index 7c5e377..df44e6d 100644 --- a/quartz-manager-frontend/src/app/views/manager/manager.component.html +++ b/quartz-manager-frontend/src/app/views/manager/manager.component.html @@ -1,19 +1,32 @@ -
+
-
-
- -
- -
+
+
-
-
-
-
-
-
-
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+
+
+ +
+ + diff --git a/quartz-manager-frontend/src/app/views/manager/manager.component.scss b/quartz-manager-frontend/src/app/views/manager/manager.component.scss index e69de29..8b13789 100644 --- a/quartz-manager-frontend/src/app/views/manager/manager.component.scss +++ b/quartz-manager-frontend/src/app/views/manager/manager.component.scss @@ -0,0 +1 @@ + diff --git a/quartz-manager-frontend/src/app/views/manager/manager.component.ts b/quartz-manager-frontend/src/app/views/manager/manager.component.ts index a780d49..839c3e7 100644 --- a/quartz-manager-frontend/src/app/views/manager/manager.component.ts +++ b/quartz-manager-frontend/src/app/views/manager/manager.component.ts @@ -11,8 +11,8 @@ import { }) export class ManagerComponent implements OnInit { - whoamIResponse = {}; - allUserResponse = {}; + newTriggerFormOpened = false; + constructor( private config: ConfigService, private userService: UserService @@ -21,4 +21,8 @@ export class ManagerComponent implements OnInit { ngOnInit() { } + setnewTriggerFormOpened(opened: boolean){ + this.newTriggerFormOpened = opened; + } + } diff --git a/quartz-manager-frontend/src/styles.css b/quartz-manager-frontend/src/styles.css index dcbd1da..e6babad 100644 --- a/quartz-manager-frontend/src/styles.css +++ b/quartz-manager-frontend/src/styles.css @@ -1,6 +1,13 @@ /* You can add global styles to this file, and also import other style files */ @import '~@angular/material/prebuilt-themes/deeppurple-amber.css'; +html { + display: flex; + flex-direction: column; + height: 100%; +} body { margin: 0; + flex:1; + background-color: #f1f1f1; } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java index 6799df1..b8a6a1a 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java @@ -9,8 +9,8 @@ import io.swagger.v3.oas.annotations.security.SecurityRequirement; import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam; import it.fabioformosa.quartzmanager.dto.SchedulerDTO; import it.fabioformosa.quartzmanager.dto.TriggerStatus; -import it.fabioformosa.quartzmanager.enums.SchedulerStates; import it.fabioformosa.quartzmanager.services.LegacySchedulerService; +import it.fabioformosa.quartzmanager.services.SchedulerService; import org.quartz.SchedulerException; import org.quartz.SimpleTrigger; import org.quartz.impl.triggers.SimpleTriggerImpl; @@ -24,8 +24,6 @@ import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; -import java.util.Collections; -import java.util.Map; /** * This controller provides scheduler info about config and status. It provides @@ -42,8 +40,11 @@ public class SchedulerController { private LegacySchedulerService legacySchedulerService; - public SchedulerController(LegacySchedulerService legacySchedulerService, ConversionService conversionService) { + private SchedulerService schedulerService; + + public SchedulerController(LegacySchedulerService legacySchedulerService, SchedulerService schedulerService, ConversionService conversionService) { this.legacySchedulerService = legacySchedulerService; + this.schedulerService = schedulerService; this.conversionService = conversionService; } @@ -82,9 +83,8 @@ public class SchedulerController { schema = @Schema(implementation = SchedulerDTO.class)) }) }) public SchedulerDTO getScheduler() { - log.debug("SCHEDULER - GET Scheduler..."); - SchedulerDTO schedulerDTO = conversionService.convert(legacySchedulerService.getScheduler(), SchedulerDTO.class); - return schedulerDTO; + log.trace("SCHEDULER - GET Scheduler..."); + return schedulerService.getScheduler(); } //TODO move this to the Trigger Controller @@ -113,24 +113,26 @@ public class SchedulerController { return progress; } - @GetMapping(value = "/status", produces = "application/json") - @Operation(summary = "Get the scheduler status") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Return the scheduler status", - content = { @Content(mediaType = "application/json", - schema = @Schema(implementation = SchedulerStates.class)) }) - }) - public Map getStatus() throws SchedulerException { - log.trace("SCHEDULER - GET STATUS"); - String schedulerState = ""; - if (legacySchedulerService.getScheduler().isShutdown() || !legacySchedulerService.getScheduler().isStarted()) - schedulerState = SchedulerStates.STOPPED.toString(); - else if (legacySchedulerService.getScheduler().isStarted() && legacySchedulerService.getScheduler().isInStandbyMode()) - schedulerState = SchedulerStates.PAUSED.toString(); - else - schedulerState = SchedulerStates.RUNNING.toString(); - return Collections.singletonMap("data", schedulerState.toLowerCase()); - } + + //REMOVEME +// @GetMapping(value = "/status", produces = "application/json") +// @Operation(summary = "Get the scheduler status") +// @ApiResponses(value = { +// @ApiResponse(responseCode = "200", description = "Return the scheduler status", +// content = { @Content(mediaType = "application/json", +// schema = @Schema(implementation = SchedulerStates.class)) }) +// }) +// public Map getStatus() throws SchedulerException { +// log.trace("SCHEDULER - GET STATUS"); +// String schedulerState = ""; +// if (legacySchedulerService.getScheduler().isShutdown() || !legacySchedulerService.getScheduler().isStarted()) +// schedulerState = SchedulerStates.STOPPED.toString(); +// else if (legacySchedulerService.getScheduler().isStarted() && legacySchedulerService.getScheduler().isInStandbyMode()) +// schedulerState = SchedulerStates.PAUSED.toString(); +// else +// schedulerState = SchedulerStates.RUNNING.toString(); +// return Collections.singletonMap("data", schedulerState.toLowerCase()); +// } @GetMapping("/pause") @Operation(summary = "Get paused the scheduler") diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java index 47ea4a9..edff08d 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java @@ -10,12 +10,14 @@ import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam; import it.fabioformosa.quartzmanager.dto.TriggerDTO; import it.fabioformosa.quartzmanager.exceptions.TriggerNotFoundException; import it.fabioformosa.quartzmanager.services.LegacySchedulerService; +import it.fabioformosa.quartzmanager.services.TriggerService; import lombok.extern.slf4j.Slf4j; import org.quartz.SchedulerException; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; +import java.util.List; @Slf4j @RequestMapping(TriggerController.TRIGGER_CONTROLLER_BASE_URL) @@ -26,11 +28,19 @@ public class TriggerController extends AbstractTriggerController { static public final String TRIGGER_CONTROLLER_BASE_URL = "/quartz-manager/triggers"; private LegacySchedulerService schedulerService; + private TriggerService triggerService; - public TriggerController(LegacySchedulerService schedulerService) { + public TriggerController(LegacySchedulerService schedulerService, TriggerService triggerService) { this.schedulerService = schedulerService; + this.triggerService = triggerService; } + @GetMapping + public List listTriggers() throws SchedulerException { + return triggerService.fetchTriggers(); + } + + @GetMapping("/{name}") public TriggerDTO getTrigger(@PathVariable String name) throws SchedulerException, TriggerNotFoundException { return schedulerService.getLegacyTriggerByName(name); diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/SchedulerToSchedulerDTO.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/SchedulerToSchedulerDTO.java index fcc01a7..ad4515c 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/SchedulerToSchedulerDTO.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/SchedulerToSchedulerDTO.java @@ -2,8 +2,10 @@ package it.fabioformosa.quartzmanager.converters; import it.fabioformosa.metamorphosis.core.converters.AbstractBaseConverterToDTO; import it.fabioformosa.quartzmanager.dto.SchedulerDTO; +import it.fabioformosa.quartzmanager.enums.SchedulerStatus; import lombok.SneakyThrows; import org.quartz.Scheduler; +import org.quartz.SchedulerException; import org.quartz.impl.matchers.GroupMatcher; import org.springframework.stereotype.Component; @@ -16,6 +18,15 @@ public class SchedulerToSchedulerDTO extends AbstractBaseConverterToDTO triggerKeys; - - public void setName(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - public void setInstanceId(String instanceId) { - this.instanceId = instanceId; - } - - public String getInstanceId() { - return instanceId; - } - - public void setTriggerKeys(Set triggerKeys) { - this.triggerKeys = triggerKeys; - } - - public Set getTriggerKeys() { - return triggerKeys; - } } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/enums/SchedulerStates.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/enums/SchedulerStatus.java similarity index 69% rename from quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/enums/SchedulerStates.java rename to quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/enums/SchedulerStatus.java index f248106..f37d113 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/enums/SchedulerStates.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/enums/SchedulerStatus.java @@ -1,5 +1,5 @@ package it.fabioformosa.quartzmanager.enums; -public enum SchedulerStates { +public enum SchedulerStatus { RUNNING, STOPPED, PAUSED -} \ No newline at end of file +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SchedulerService.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SchedulerService.java new file mode 100644 index 0000000..40e4cd4 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SchedulerService.java @@ -0,0 +1,19 @@ +package it.fabioformosa.quartzmanager.services; + +import it.fabioformosa.quartzmanager.dto.SchedulerDTO; +import org.quartz.Scheduler; +import org.springframework.core.convert.ConversionService; +import org.springframework.stereotype.Service; + +@Service +public class SchedulerService extends AbstractSchedulerService{ + + public SchedulerService(Scheduler scheduler, ConversionService conversionService) { + super(scheduler, conversionService); + } + + public SchedulerDTO getScheduler() { + return conversionService.convert(scheduler, SchedulerDTO.class); + } + +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/TriggerService.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/TriggerService.java new file mode 100644 index 0000000..d71aced --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/TriggerService.java @@ -0,0 +1,34 @@ +package it.fabioformosa.quartzmanager.services; + +import it.fabioformosa.quartzmanager.dto.TriggerDTO; +import it.fabioformosa.quartzmanager.dto.TriggerKeyDTO; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; +import org.quartz.TriggerKey; +import org.quartz.impl.matchers.GroupMatcher; +import org.springframework.core.convert.ConversionService; +import org.springframework.core.convert.TypeDescriptor; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Set; + +@Service +public class TriggerService { + + private Scheduler scheduler; + private ConversionService conversionService; + + public TriggerService(Scheduler scheduler, ConversionService conversionService) { + this.scheduler = scheduler; + this.conversionService = conversionService; + } + + public List fetchTriggers() throws SchedulerException { + Set triggerKeys = scheduler.getTriggerKeys(GroupMatcher.anyTriggerGroup()); + return (List) conversionService.convert(triggerKeys, + TypeDescriptor.collection(Set.class, TypeDescriptor.valueOf(TriggerKey.class)), + TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(TriggerKeyDTO.class))); + } + +} From b4bb16130c4ed173d41d9d8361f54c1d543b6526 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Wed, 31 Aug 2022 23:46:53 +0200 Subject: [PATCH 022/184] #59 added unit tests for the new trigger --- .../simple-trigger-config.component.html | 13 +-- .../simple-trigger-config.component.spec.ts | 80 ++++++++++++++++++ .../simple-trigger-config.component.ts | 52 +++++++----- .../trigger-list/trigger-list.component.html | 4 +- .../trigger-list.component.spec.ts | 84 +++++++++++++++++++ .../trigger-list/trigger-list.component.ts | 42 ++++++++-- .../src/app/services/scheduler.service.ts | 4 +- .../app/views/manager/manager.component.html | 16 +++- .../app/views/manager/manager.component.ts | 26 +++++- 9 files changed, 274 insertions(+), 47 deletions(-) create mode 100644 quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.spec.ts create mode 100644 quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.spec.ts diff --git a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html index 7a4e788..ebfe195 100644 --- a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html +++ b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html @@ -3,16 +3,7 @@ TRIGGER DETAILS - - - - - - - - +
@@ -82,7 +73,7 @@ diff --git a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.spec.ts b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.spec.ts new file mode 100644 index 0000000..d25ff65 --- /dev/null +++ b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.spec.ts @@ -0,0 +1,80 @@ +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; +import {MatCardModule} from '@angular/material/card'; +import {SimpleTriggerConfigComponent} from './simple-trigger-config.component'; +import {ApiService, ConfigService, SchedulerService} from '../../services'; +import {HttpClient} from '@angular/common/http'; +import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing'; +import {DebugElement, NO_ERRORS_SCHEMA} from '@angular/core'; +import {By} from '@angular/platform-browser'; +import {RouterTestingModule} from '@angular/router/testing'; +import {MatIconModule} from '@angular/material/icon'; +import {FormsModule} from '@angular/forms'; +import {MatFormFieldModule} from '@angular/material/form-field'; +import {MatNativeDateModule} from '@angular/material/core'; +import {MatDatepickerModule} from '@angular/material/datepicker'; +import {MatInputModule} from '@angular/material/input'; +import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; +import {TriggerKey} from '../../model/triggerKey.model'; +import {Trigger} from '../../model/trigger.model'; +import {NgxMatDatetimePickerModule} from '@angular-material-components/datetime-picker'; +import { NgxMatMomentModule } from '@angular-material-components/moment-adapter'; + +describe('SimpleTriggerConfig', () => { + + let component: SimpleTriggerConfigComponent; + let fixture: ComponentFixture; + + let httpClient: HttpClient; + let httpTestingController: HttpTestingController; + + beforeEach(async( () => { + TestBed.configureTestingModule({ + imports: [FormsModule, MatFormFieldModule, MatFormFieldModule, MatInputModule, BrowserAnimationsModule, + MatNativeDateModule, + MatCardModule, MatIconModule, HttpClientTestingModule, RouterTestingModule], + declarations: [SimpleTriggerConfigComponent], + providers: [SchedulerService, ApiService, ConfigService], + schemas: [NO_ERRORS_SCHEMA] + }).compileComponents(); + + httpClient = TestBed.inject(HttpClient); + httpTestingController = TestBed.inject(HttpTestingController); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SimpleTriggerConfigComponent); + component = fixture.componentInstance; + }); + + it('should fetch no triggers at the init', () => { + expect(component).toBeTruthy(); + httpTestingController.expectNone('/quartz-manager/simple-triggers/my-simple-trigger'); + }); + + 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; + fixture.detectChanges(); + + const mockTrigger = new Trigger(); + mockTrigger.triggerKeyDTO = mockTriggerKey; + const getSimpleTriggerReq = httpTestingController.expectOne('/quartz-manager/simple-triggers/my-simple-trigger'); + getSimpleTriggerReq.flush(mockTrigger); + + const componentDe: DebugElement = fixture.debugElement; + const submitButton = componentDe.query(By.css('form > button')); + expect(submitButton.nativeElement.textContent.trim()).toEqual('Reschedule'); + }); + + it('should get display the form if the openTriggerForm method is called', () => { + component.openTriggerForm(); + fixture.detectChanges(); + + const componentDe: DebugElement = fixture.debugElement; + const submitButton = componentDe.query(By.css('form > button[color="primary"]')); + expect(submitButton.nativeElement.textContent.trim()).toEqual('Submit'); + }); + + + +}); diff --git a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts index 8efd796..3888801 100644 --- a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts +++ b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts @@ -1,10 +1,11 @@ -import {Component, OnInit} from '@angular/core'; +import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; 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'; @Component({ selector: 'qrzmng-simple-trigger-config', @@ -20,31 +21,53 @@ export class SimpleTriggerConfigComponent implements OnInit { scheduler: Scheduler; triggerLoading = true; - enabledTriggerForm = false; + private fetchedTriggers = false; private triggerInProgress = false; + private selectedTriggerKey: TriggerKey; + + enabledTriggerForm = false; + + @Output() + onNewTrigger = new EventEmitter(); + constructor( private schedulerService: SchedulerService ) { } ngOnInit() { - this.triggerLoading = true; - this.retrieveConfiguredTriggerIfExists(); } - retrieveConfiguredTriggerIfExists = () => { - this.schedulerService.getSimpleTriggerConfig() + openTriggerForm() { + this.enabledTriggerForm = true; + } + + private closeTriggerForm() { + this.enabledTriggerForm = false; + } + + @Input() + set triggerKey(triggerKey: TriggerKey){ + this.selectedTriggerKey = {...triggerKey} as TriggerKey; + this.fetchSelectedTrigger(); + } + + + fetchSelectedTrigger = () => { + this.triggerLoading = true; + this.schedulerService.getSimpleTriggerConfig(this.selectedTriggerKey.name) .subscribe((retTrigger: SimpleTrigger) => { this.trigger = retTrigger; this.formBackup = this.simpleTriggerForm; this.simpleTriggerForm = this._fromTriggerToForm(retTrigger); - this.triggerLoading = false; this.triggerInProgress = this.trigger.mayFireAgain; }) } + shouldShowTheTriggerCardContent = (): boolean => this.trigger !== null || this.enabledTriggerForm; + existsATriggerInProgress = (): boolean => this.trigger && this.triggerInProgress; cancelConfigForm = () => this.enabledTriggerForm = false; @@ -59,25 +82,16 @@ export class SimpleTriggerConfigComponent implements OnInit { this.trigger = retTrigger; this.formBackup = this.simpleTriggerForm; this.simpleTriggerForm = this._fromTriggerToForm(retTrigger); - this.enabledTriggerForm = false; this.fetchedTriggers = true; this.triggerInProgress = this.trigger.mayFireAgain; + + this.onNewTrigger.emit(retTrigger); + this.closeTriggerForm(); }, error => { this.simpleTriggerForm = this.formBackup; }); }; - enableTriggerForm = () => this.enabledTriggerForm = true; - - private _fromTriggerToCommand = (simpleTrigger: SimpleTrigger) => { - const command = new SimpleTriggerCommand(); - command.repeatCount = simpleTrigger.repeatCount; - command.repeatInterval = simpleTrigger.repeatInterval; - command.startDate = simpleTrigger.startTime; - command.endDate = simpleTrigger.endTime; - return command; - } - private _fromTriggerToForm = (simpleTrigger: SimpleTrigger): SimpleTriggerForm => { const command = new SimpleTriggerForm(); command.repeatCount = simpleTrigger.repeatCount; diff --git a/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.html b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.html index 36e76a9..0c722c8 100644 --- a/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.html +++ b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.html @@ -1,14 +1,14 @@ TRIGGERS - - + {{ triggerKey.name }} diff --git a/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.spec.ts b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.spec.ts new file mode 100644 index 0000000..2f61fd0 --- /dev/null +++ b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.spec.ts @@ -0,0 +1,84 @@ +import {async, ComponentFixture, TestBed} from '@angular/core/testing'; +import {ApiService, ConfigService, TriggerService} from '../../services'; +import {HttpClient} from '@angular/common/http'; +import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing'; +import {RouterTestingModule} from '@angular/router/testing'; +import {DebugElement} from '@angular/core'; +import {By} from '@angular/platform-browser'; +import {MatCardModule} from '@angular/material/card'; +import {MatIconModule} from '@angular/material/icon'; +import {MatDividerModule} from '@angular/material/divider'; +import {TriggerListComponent} from './trigger-list.component'; +import {MatListModule} from '@angular/material/list'; +import {TriggerKey} from '../../model/triggerKey.model'; + +describe('TriggerListComponent', () => { + + let component: TriggerListComponent; + let fixture: ComponentFixture; + + let httpClient: HttpClient; + let httpTestingController: HttpTestingController; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [MatCardModule, MatDividerModule, MatIconModule, MatListModule, HttpClientTestingModule, RouterTestingModule], + declarations: [TriggerListComponent], + providers: [TriggerService, ApiService, ConfigService] + }).compileComponents(); + + httpClient = TestBed.inject(HttpClient); + httpTestingController = TestBed.inject(HttpTestingController); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(TriggerListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should select the first trigger of the list', () => { + expect(component).toBeDefined(); + + let actualSelectedTrigger: TriggerKey; + component.onSelectedTrigger.subscribe(selectedTrigger => actualSelectedTrigger = selectedTrigger); + + const getTriggerListReq = httpTestingController.expectOne('quartz-manager/triggers'); + const mockExistingTriggers = new Array(); + const firstTriggerKey = new TriggerKey('trigger1', 'group1'); + mockExistingTriggers.push(firstTriggerKey); + const secondTriggerKey = new TriggerKey('trigger2', 'group2'); + mockExistingTriggers.push(secondTriggerKey); + getTriggerListReq.flush(mockExistingTriggers); + fixture.detectChanges(); + + const triggerListComponentDe: DebugElement = fixture.debugElement; + const triggerItemList = triggerListComponentDe.queryAll(By.css('.triggerItemList')); + expect(triggerItemList.length).toEqual(2); + + expect(actualSelectedTrigger).toEqual(firstTriggerKey); + + }); + + it('should open the trigger form if the trigger list is empty', () => { + expect(component).toBeDefined(); + + let actualSelectedTrigger: TriggerKey; + component.onSelectedTrigger.subscribe(selectedTrigger => actualSelectedTrigger = selectedTrigger); + + let expectedOpenedNewTriggerFormEvent: boolean; + component.onNewTriggerClicked.subscribe(() => expectedOpenedNewTriggerFormEvent = true); + + const getTriggerListReq = httpTestingController.expectOne('quartz-manager/triggers'); + getTriggerListReq.flush(new Array()); + fixture.detectChanges(); + + const triggerListComponentDe: DebugElement = fixture.debugElement; + const triggerItemList = triggerListComponentDe.queryAll(By.css('.triggerItemList')); + expect(triggerItemList.length).toEqual(0); + + expect(expectedOpenedNewTriggerFormEvent).toBeTruthy(); + expect(actualSelectedTrigger).toBeUndefined(); + }); + +}); diff --git a/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.ts b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.ts index 490f68b..b6428ae 100644 --- a/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.ts +++ b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.ts @@ -1,6 +1,7 @@ -import {Component, EventEmitter, OnInit, Output} from '@angular/core'; +import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; import {TriggerService} from '../../services/trigger.service'; import {TriggerKey} from '../../model/triggerKey.model'; +import {SimpleTrigger} from '../../model/simple-trigger.model'; @Component({ selector: 'qrzmng-trigger-list', @@ -9,10 +10,18 @@ import {TriggerKey} from '../../model/triggerKey.model'; }) export class TriggerListComponent implements OnInit { - loading = true; - triggerKeys: Array = []; + @Input() + newTriggers: Array = new Array(); - @Output() openedNewTriggerFormEvent = new EventEmitter(); + loading = true; + + triggerKeys: Array = new Array(); + + @Output() onNewTriggerClicked = new EventEmitter(); + triggerFormIsOpen = false; + + selectedTrigger: TriggerKey; + @Output() onSelectedTrigger = new EventEmitter(); constructor( private triggerService: TriggerService @@ -23,14 +32,35 @@ export class TriggerListComponent implements OnInit { this.fetchTriggers(); } + @Input() + set openedNewTriggerForm(triggerFormIsOpen: boolean){ + this.triggerFormIsOpen = triggerFormIsOpen; + } + + getTriggerKeyList = () => { + const newTriggerKeys = this.newTriggers.map(simpleTrigger => simpleTrigger.triggerKeyDTO); + return newTriggerKeys.concat(this.triggerKeys); + } + private fetchTriggers() { this.triggerService.fetchTriggers() .subscribe((triggerKeys: Array) => { this.triggerKeys = triggerKeys; + if (!triggerKeys || triggerKeys.length === 0) { + this.onNewTriggerBtnClicked(); + } + else { + this.selectTrigger(this.triggerKeys[0]); + } }) } - openNewTriggerForm() { - this.openedNewTriggerFormEvent.emit(true); + selectTrigger(triggerKey: TriggerKey) { + this.selectedTrigger = triggerKey; + this.onSelectedTrigger.emit(triggerKey); + } + + onNewTriggerBtnClicked() { + this.onNewTriggerClicked.emit(); } } diff --git a/quartz-manager-frontend/src/app/services/scheduler.service.ts b/quartz-manager-frontend/src/app/services/scheduler.service.ts index afc94ec..9a26d1b 100644 --- a/quartz-manager-frontend/src/app/services/scheduler.service.ts +++ b/quartz-manager-frontend/src/app/services/scheduler.service.ts @@ -43,8 +43,8 @@ export class SchedulerService { return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler/config') } - getSimpleTriggerConfig = (): Observable => { - return this.apiService.get(getBaseUrl() + '/quartz-manager/simple-triggers/my-simple-trigger'); + getSimpleTriggerConfig = (triggerName: string): Observable => { + return this.apiService.get(getBaseUrl() + `/quartz-manager/simple-triggers/${triggerName}`); } // deprecated diff --git a/quartz-manager-frontend/src/app/views/manager/manager.component.html b/quartz-manager-frontend/src/app/views/manager/manager.component.html index df44e6d..6131d71 100644 --- a/quartz-manager-frontend/src/app/views/manager/manager.component.html +++ b/quartz-manager-frontend/src/app/views/manager/manager.component.html @@ -1,6 +1,6 @@ -
+
-
+
@@ -8,13 +8,21 @@
- +
- + +
diff --git a/quartz-manager-frontend/src/app/views/manager/manager.component.ts b/quartz-manager-frontend/src/app/views/manager/manager.component.ts index 839c3e7..f8c51fd 100644 --- a/quartz-manager-frontend/src/app/views/manager/manager.component.ts +++ b/quartz-manager-frontend/src/app/views/manager/manager.component.ts @@ -1,8 +1,11 @@ -import { Component, OnInit } from '@angular/core'; +import {Component, OnInit, ViewChild} from '@angular/core'; import { ConfigService, UserService } from '../../services'; +import {SimpleTrigger} from '../../model/simple-trigger.model'; +import {TriggerKey} from '../../model/triggerKey.model'; +import {SimpleTriggerConfigComponent} from '../../components/simple-trigger-config'; @Component({ selector: 'manager', @@ -11,8 +14,14 @@ import { }) export class ManagerComponent implements OnInit { + @ViewChild(SimpleTriggerConfigComponent) + private triggerConfigComponent!: SimpleTriggerConfigComponent; + newTriggerFormOpened = false; + newTriggers = new Array(); + selectedTriggerKey: TriggerKey; + constructor( private config: ConfigService, private userService: UserService @@ -21,8 +30,19 @@ export class ManagerComponent implements OnInit { ngOnInit() { } - setnewTriggerFormOpened(opened: boolean){ - this.newTriggerFormOpened = opened; + onNewTriggerRequested() { + this.triggerConfigComponent.openTriggerForm(); } + onNewTrigger(newTrigger: SimpleTrigger) { + this.newTriggers.push(newTrigger); + } + + setSelectedTrigger(triggerKey: TriggerKey) { + this.selectedTriggerKey = triggerKey; + } + + onTriggerFormToggled(formOpened: boolean) { + this.newTriggerFormOpened = formOpened; + } } From 21f3f7dca2e2599cc9c901c331fc0c9ba64a6e46 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Fri, 2 Sep 2022 00:19:23 +0200 Subject: [PATCH 023/184] #59 highlighted the selected trigger into the list --- .../src/app/app-routing.module.ts | 19 ++++++++----------- .../simple-trigger-config.component.spec.ts | 2 +- .../trigger-list/trigger-list.component.html | 6 ++++-- .../trigger-list/trigger-list.component.scss | 4 ++++ .../trigger-list/trigger-list.component.ts | 14 ++++++++++++-- 5 files changed, 29 insertions(+), 16 deletions(-) diff --git a/quartz-manager-frontend/src/app/app-routing.module.ts b/quartz-manager-frontend/src/app/app-routing.module.ts index 7d2d1c6..b955e6f 100644 --- a/quartz-manager-frontend/src/app/app-routing.module.ts +++ b/quartz-manager-frontend/src/app/app-routing.module.ts @@ -1,15 +1,12 @@ -import { NgModule } from '@angular/core'; -import { Routes, RouterModule } from '@angular/router'; -import { AppComponent } from './app.component'; -import { LoginComponent } from './views/login'; -import { LoginGuard } from './guards'; -import { GuestGuard, AdminGuard } from './guards'; -import { NotFoundComponent } from './views/not-found'; -import { ChangePasswordComponent } from './views/change-password'; -import { ForbiddenComponent } from './views/forbidden'; +import {NgModule} from '@angular/core'; +import {RouterModule, Routes} from '@angular/router'; +import {LoginComponent} from './views/login'; +import {AdminGuard, GuestGuard} from './guards'; +import {NotFoundComponent} from './views/not-found'; +import {ForbiddenComponent} from './views/forbidden'; + +import {ManagerComponent} from './views/manager'; -import { ManagerComponent } from './views/manager'; - export const routes: Routes = [ { path: '', diff --git a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.spec.ts b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.spec.ts index d25ff65..0ebd2cc 100644 --- a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.spec.ts +++ b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.spec.ts @@ -66,7 +66,7 @@ describe('SimpleTriggerConfig', () => { expect(submitButton.nativeElement.textContent.trim()).toEqual('Reschedule'); }); - it('should get display the form if the openTriggerForm method is called', () => { + it('should display the form if the openTriggerForm method is called', () => { component.openTriggerForm(); fixture.detectChanges(); diff --git a/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.html b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.html index 0c722c8..ce1ebd0 100644 --- a/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.html +++ b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.html @@ -8,8 +8,10 @@ - - {{ triggerKey.name }} + + + {{ triggerKey.name }} diff --git a/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.scss b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.scss index 8507fa3..ccfdc3d 100644 --- a/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.scss +++ b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.scss @@ -19,3 +19,7 @@ border-radius: 10px; border: 3px solid #ffffff; } + +.selectedTrigger{ + background-color: #dddddd; +} diff --git a/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.ts b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.ts index b6428ae..124321b 100644 --- a/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.ts +++ b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.ts @@ -2,6 +2,7 @@ import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; import {TriggerService} from '../../services/trigger.service'; import {TriggerKey} from '../../model/triggerKey.model'; import {SimpleTrigger} from '../../model/simple-trigger.model'; +import {MatDialog} from '@angular/material/dialog'; @Component({ selector: 'qrzmng-trigger-list', @@ -24,7 +25,8 @@ export class TriggerListComponent implements OnInit { @Output() onSelectedTrigger = new EventEmitter(); constructor( - private triggerService: TriggerService + private triggerService: TriggerService, + public dialog: MatDialog ) { } ngOnInit() { @@ -61,6 +63,14 @@ export class TriggerListComponent implements OnInit { } onNewTriggerBtnClicked() { - this.onNewTriggerClicked.emit(); + if (this.triggerKeys && this.triggerKeys.length > 0) + this.dialog.open(UnsupportedMultipleJobsDialog) + else + this.onNewTriggerClicked.emit(); } } + +@Component({ + template: 'Multiple jobs not supported yet - Coming Soon...', +}) +export class UnsupportedMultipleJobsDialog {} From 963866736845227ff93bde559b2b1f34c3988345 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Sat, 3 Sep 2022 15:50:34 +0200 Subject: [PATCH 024/184] #52 added the trigger name to the trigger form --- quartz-manager-frontend/src/app/app.module.ts | 2 - .../src/app/components/index.ts | 1 - .../app/components/scheduler-config/index.ts | 1 - .../scheduler-config.component.html | 68 ------------------ .../scheduler-config.component.scss | 3 - .../scheduler-config.component.ts | 72 ------------------- .../simple-trigger-config.component.html | 10 +++ .../simple-trigger-config.component.ts | 2 + .../trigger-list/trigger-list.component.html | 4 -- .../src/app/model/simple-trigger.command.ts | 1 + .../src/app/model/simple-trigger.form.ts | 1 + .../src/app/services/scheduler.service.ts | 36 ++++------ 12 files changed, 26 insertions(+), 175 deletions(-) delete mode 100644 quartz-manager-frontend/src/app/components/scheduler-config/index.ts delete mode 100644 quartz-manager-frontend/src/app/components/scheduler-config/scheduler-config.component.html delete mode 100644 quartz-manager-frontend/src/app/components/scheduler-config/scheduler-config.component.scss delete mode 100644 quartz-manager-frontend/src/app/components/scheduler-config/scheduler-config.component.ts diff --git a/quartz-manager-frontend/src/app/app.module.ts b/quartz-manager-frontend/src/app/app.module.ts index 15dd847..081bb21 100644 --- a/quartz-manager-frontend/src/app/app.module.ts +++ b/quartz-manager-frontend/src/app/app.module.ts @@ -39,7 +39,6 @@ import { HeaderComponent, FooterComponent, GithubComponent, - SchedulerConfigComponent, SchedulerControlComponent, LogsPanelComponent, ProgressPanelComponent, @@ -111,7 +110,6 @@ export function jwtOptionsFactory(apiService: ApiService) { LoginComponent, NotFoundComponent, AccountMenuComponent, - SchedulerConfigComponent, SimpleTriggerConfigComponent, SchedulerControlComponent, LogsPanelComponent, diff --git a/quartz-manager-frontend/src/app/components/index.ts b/quartz-manager-frontend/src/app/components/index.ts index fe73207..2a77c4a 100644 --- a/quartz-manager-frontend/src/app/components/index.ts +++ b/quartz-manager-frontend/src/app/components/index.ts @@ -2,7 +2,6 @@ export * from './header'; export * from './github'; export * from './footer'; export * from './logs-panel'; -export * from './scheduler-config'; export * from './scheduler-control'; export * from './progress-panel'; export * from './trigger-list'; diff --git a/quartz-manager-frontend/src/app/components/scheduler-config/index.ts b/quartz-manager-frontend/src/app/components/scheduler-config/index.ts deleted file mode 100644 index d8d3e69..0000000 --- a/quartz-manager-frontend/src/app/components/scheduler-config/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './scheduler-config.component'; diff --git a/quartz-manager-frontend/src/app/components/scheduler-config/scheduler-config.component.html b/quartz-manager-frontend/src/app/components/scheduler-config/scheduler-config.component.html deleted file mode 100644 index fe87e40..0000000 --- a/quartz-manager-frontend/src/app/components/scheduler-config/scheduler-config.component.html +++ /dev/null @@ -1,68 +0,0 @@ - - - SCHEDULER CONFIG - - - - - - - - - -
- -
- - Freq [Num per day] - - -
-
- - Max Occurrences - - -
-
-
-
Misfire Policy
-
RESCHEDULE NEXT WITH REMAINING COUNT
-
- In case of misfire event, the trigger is re-scheduled to the next scheduled time after 'now' with the repeat count set to what it would be, if it had not missed any firings. -
- Warning: This policy could cause the Trigger to go directly to the 'COMPLETE' state if all fire-times where missed. -
-
- -
- - - - - -
-
-
diff --git a/quartz-manager-frontend/src/app/components/scheduler-config/scheduler-config.component.scss b/quartz-manager-frontend/src/app/components/scheduler-config/scheduler-config.component.scss deleted file mode 100644 index 3176491..0000000 --- a/quartz-manager-frontend/src/app/components/scheduler-config/scheduler-config.component.scss +++ /dev/null @@ -1,3 +0,0 @@ -.small{ - font-size: 0.8em; -} diff --git a/quartz-manager-frontend/src/app/components/scheduler-config/scheduler-config.component.ts b/quartz-manager-frontend/src/app/components/scheduler-config/scheduler-config.component.ts deleted file mode 100644 index 17b404e..0000000 --- a/quartz-manager-frontend/src/app/components/scheduler-config/scheduler-config.component.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core'; -import { SchedulerService } from '../../services'; -import { SchedulerConfig } from '../../model/schedulerConfig.model' -import {Scheduler} from '../../model/scheduler.model'; - -/** - * DEPRECATED! Not more used - */ -@Component({ - selector: 'qrzmng-scheduler-config', - templateUrl: './scheduler-config.component.html', - styleUrls: ['./scheduler-config.component.scss'] -}) -export class SchedulerConfigComponent implements OnInit { - - config: SchedulerConfig = new SchedulerConfig() - configBackup: SchedulerConfig = new SchedulerConfig() - scheduler: Scheduler; - - triggerLoading = true; - enabledTriggerForm = false; - private fetchedTriggers = false; - private triggerInProgress = false; - - constructor( - private schedulerService: SchedulerService - ) { } - - ngOnInit() { - this.triggerLoading = true; - this._getScheduler(); - this.retrieveConfig(); - } - - retrieveConfig = () => { - this.schedulerService.getConfig() - .subscribe(res => { - this.config = new SchedulerConfig(res.triggerPerDay, res.maxCount, res.timesTriggered) - this.configBackup = res - this.triggerLoading = false; - this.triggerInProgress = res.timesTriggered < res.maxCount; - }) - } - - private _getScheduler() { - this.schedulerService.getScheduler() - .subscribe( res => { - this.scheduler = res; - this.fetchedTriggers = this.scheduler.triggerKeys.length > 0 - }) - } - - existsATriggerInProgress = (): boolean => this.fetchedTriggers && this.triggerInProgress; - - cancelConfigForm = () => this.enabledTriggerForm = false; - - submitConfig = () => { - const schedulerServiceCall = this.existsATriggerInProgress() ? this.schedulerService.updateConfig : this.schedulerService.saveConfig; - - schedulerServiceCall(this.config) - .subscribe(res => { - this.configBackup = this.config; - this.enabledTriggerForm = false; - this.fetchedTriggers = true; - this.triggerInProgress = true; - }, error => { - this.config = this.configBackup; - }); - }; - - enableTriggerForm = () => this.enabledTriggerForm = true; -} diff --git a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html index ebfe195..886f35a 100644 --- a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html +++ b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html @@ -7,6 +7,16 @@
+
+ + Trigger Name + + + +
+
Start Date (optional) diff --git a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts index 3888801..a9c2587 100644 --- a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts +++ b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts @@ -94,6 +94,7 @@ export class SimpleTriggerConfigComponent implements OnInit { private _fromTriggerToForm = (simpleTrigger: SimpleTrigger): SimpleTriggerForm => { const command = new SimpleTriggerForm(); + command.triggerName = simpleTrigger.triggerKeyDTO.name; command.repeatCount = simpleTrigger.repeatCount; command.repeatInterval = simpleTrigger.repeatInterval; command.startDate = moment(simpleTrigger.startTime); @@ -103,6 +104,7 @@ export class SimpleTriggerConfigComponent implements OnInit { private _fromFormToCommand = (simpleTriggerForm: SimpleTriggerForm): SimpleTriggerCommand => { const simpleTriggerCommand = new SimpleTriggerCommand(); + simpleTriggerCommand.triggerName = simpleTriggerForm.triggerName; simpleTriggerCommand.repeatCount = simpleTriggerForm.repeatCount; simpleTriggerCommand.repeatInterval = simpleTriggerForm.repeatInterval; simpleTriggerCommand.startDate = simpleTriggerForm.startDate.toDate(); diff --git a/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.html b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.html index ce1ebd0..67cac7d 100644 --- a/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.html +++ b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.html @@ -10,11 +10,7 @@ - {{ triggerKey.name }} - - - diff --git a/quartz-manager-frontend/src/app/model/simple-trigger.command.ts b/quartz-manager-frontend/src/app/model/simple-trigger.command.ts index 3d9db64..f6dc70b 100644 --- a/quartz-manager-frontend/src/app/model/simple-trigger.command.ts +++ b/quartz-manager-frontend/src/app/model/simple-trigger.command.ts @@ -1,4 +1,5 @@ export class SimpleTriggerCommand { + triggerName: string; startDate: Date; endDate: Date; repeatCount: number; diff --git a/quartz-manager-frontend/src/app/model/simple-trigger.form.ts b/quartz-manager-frontend/src/app/model/simple-trigger.form.ts index 98a0210..165f531 100644 --- a/quartz-manager-frontend/src/app/model/simple-trigger.form.ts +++ b/quartz-manager-frontend/src/app/model/simple-trigger.form.ts @@ -1,6 +1,7 @@ import {Moment} from 'moment/moment'; export class SimpleTriggerForm { + triggerName: string; startDate: Moment; endDate: Moment; repeatCount: number; diff --git a/quartz-manager-frontend/src/app/services/scheduler.service.ts b/quartz-manager-frontend/src/app/services/scheduler.service.ts index 9a26d1b..3f7642c 100644 --- a/quartz-manager-frontend/src/app/services/scheduler.service.ts +++ b/quartz-manager-frontend/src/app/services/scheduler.service.ts @@ -7,62 +7,50 @@ import {SimpleTriggerCommand} from '../model/simple-trigger.command'; import {SchedulerConfig} from '../model/schedulerConfig.model'; import {Scheduler} from '../model/scheduler.model'; +const CONTEXT_PATH = '/quartz-manager'; + @Injectable() export class SchedulerService { + constructor( private apiService: ApiService ) { } startScheduler = (): Observable => { - return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler/run') + return this.apiService.get(getBaseUrl() + `${CONTEXT_PATH}/scheduler/run`); } stopScheduler = (): Observable => { - return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler/stop') + return this.apiService.get(getBaseUrl() + `${CONTEXT_PATH}/scheduler/stop`); } pauseScheduler = (): Observable => { - return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler/pause') + return this.apiService.get(getBaseUrl() + `${CONTEXT_PATH}/scheduler/pause`); } resumeScheduler = (): Observable => { - return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler/resume') + return this.apiService.get(getBaseUrl() + `${CONTEXT_PATH}/scheduler/resume`); } getStatus = () => { - return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler/status') + return this.apiService.get(getBaseUrl() + `${CONTEXT_PATH}/scheduler/status`); } getScheduler = (): Observable => { - return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler') - } - - // deprecated - getConfig = () => { - return this.apiService.get(getBaseUrl() + '/quartz-manager/scheduler/config') + return this.apiService.get(getBaseUrl() + `${CONTEXT_PATH}/scheduler`); } getSimpleTriggerConfig = (triggerName: string): Observable => { - return this.apiService.get(getBaseUrl() + `/quartz-manager/simple-triggers/${triggerName}`); - } - - // deprecated - saveConfig = (config: Object) => { - return this.apiService.post(getBaseUrl() + '/quartz-manager/triggers/mytrigger', config) + return this.apiService.get(getBaseUrl() + `${CONTEXT_PATH}/simple-triggers/${triggerName}`); } saveSimpleTriggerConfig = (config: SimpleTriggerCommand) => { - return this.apiService.post(getBaseUrl() + '/quartz-manager/simple-triggers/my-simple-trigger', config) - } - - // deprecated - updateConfig = (config: SchedulerConfig) => { - return this.apiService.put(getBaseUrl() + '/quartz-manager/triggers/mytrigger', config) + return this.apiService.post(getBaseUrl() + `${CONTEXT_PATH}/simple-triggers/${config.triggerName}`, config) } updateSimpleTriggerConfig = (config: SimpleTriggerCommand) => { - return this.apiService.put(getBaseUrl() + '/quartz-manager/simple-triggers/my-simple-trigger', config) + return this.apiService.put(getBaseUrl() + `${CONTEXT_PATH}/simple-triggers/${config.triggerName}`, config) } From 599b6fb0b4546e337406e9b47e09a4025bae24c4 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Sat, 3 Sep 2022 16:30:51 +0200 Subject: [PATCH 025/184] #52 added the job className to the trigger detail view --- .../simple-trigger-config.component.html | 8 ++++++ .../simple-trigger-config.component.ts | 1 + .../src/app/model/jobDetail.model.ts | 4 +++ .../src/app/model/simple-trigger.form.ts | 1 + .../src/app/model/trigger.model.ts | 2 ++ .../controllers/JobController.java | 21 +++++++++++++++ .../controllers/TriggerController.java | 2 -- .../converters/JobKeyToJobDetailDTO.java | 26 +++++++++++++++++++ .../converters/TriggerToTriggerDTO.java | 4 +++ .../quartzmanager/dto/JobDetailDTO.java | 14 ++++++++++ .../quartzmanager/dto/TriggerDTO.java | 1 + .../services/AbstractSchedulerService.java | 10 ++++--- 12 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 quartz-manager-frontend/src/app/model/jobDetail.model.ts create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/JobController.java create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/JobKeyToJobDetailDTO.java create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/dto/JobDetailDTO.java diff --git a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html index 886f35a..09f615d 100644 --- a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html +++ b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html @@ -14,7 +14,15 @@ matInput placeholder="Repeat Interval [in mills]" name="triggerName" [(ngModel)]="simpleTriggerForm.triggerName"> +
+
+ + Job Class + +
diff --git a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts index a9c2587..8c751ed 100644 --- a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts +++ b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts @@ -95,6 +95,7 @@ export class SimpleTriggerConfigComponent implements OnInit { private _fromTriggerToForm = (simpleTrigger: SimpleTrigger): SimpleTriggerForm => { const command = new SimpleTriggerForm(); command.triggerName = simpleTrigger.triggerKeyDTO.name; + command.jobClass = simpleTrigger.jobDetailDTO.jobClassName; command.repeatCount = simpleTrigger.repeatCount; command.repeatInterval = simpleTrigger.repeatInterval; command.startDate = moment(simpleTrigger.startTime); diff --git a/quartz-manager-frontend/src/app/model/jobDetail.model.ts b/quartz-manager-frontend/src/app/model/jobDetail.model.ts new file mode 100644 index 0000000..2fd533f --- /dev/null +++ b/quartz-manager-frontend/src/app/model/jobDetail.model.ts @@ -0,0 +1,4 @@ +export class JobDetail { + jobClassName: string; + description: string; +} diff --git a/quartz-manager-frontend/src/app/model/simple-trigger.form.ts b/quartz-manager-frontend/src/app/model/simple-trigger.form.ts index 165f531..91e4966 100644 --- a/quartz-manager-frontend/src/app/model/simple-trigger.form.ts +++ b/quartz-manager-frontend/src/app/model/simple-trigger.form.ts @@ -2,6 +2,7 @@ import {Moment} from 'moment/moment'; export class SimpleTriggerForm { triggerName: string; + jobClass: string; startDate: Moment; endDate: Moment; repeatCount: number; diff --git a/quartz-manager-frontend/src/app/model/trigger.model.ts b/quartz-manager-frontend/src/app/model/trigger.model.ts index 5b67f47..d49906d 100644 --- a/quartz-manager-frontend/src/app/model/trigger.model.ts +++ b/quartz-manager-frontend/src/app/model/trigger.model.ts @@ -1,5 +1,6 @@ import {TriggerKey} from './triggerKey.model'; import {JobKeyModel} from './jobKey.model'; +import {JobDetail} from './jobDetail.model'; export class Trigger { triggerKeyDTO: TriggerKey; @@ -11,5 +12,6 @@ export class Trigger { misfireInstruction: number; nextFireTime: Date; jobKeyDTO: JobKeyModel; + jobDetailDTO: JobDetail; mayFireAgain: boolean; } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/JobController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/JobController.java new file mode 100644 index 0000000..5a37549 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/JobController.java @@ -0,0 +1,21 @@ +package it.fabioformosa.quartzmanager.controllers; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.ArrayList; +import java.util.List; + +@RequestMapping("/quartz-manager/jobs") +@RestController +public class JobController extends AbstractTriggerController { + + @GetMapping + public List listJobs(){ + List jobClasses = new ArrayList(); + jobClasses.add(jobClassname); + return jobClasses; + } + +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java index edff08d..1982df7 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java @@ -64,8 +64,6 @@ public class TriggerController extends AbstractTriggerController { return newTriggerDTO; } - - @PutMapping("/{name}") @Operation(summary = "Reschedule the trigger") @ApiResponses(value = { diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/JobKeyToJobDetailDTO.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/JobKeyToJobDetailDTO.java new file mode 100644 index 0000000..dbb7a9d --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/JobKeyToJobDetailDTO.java @@ -0,0 +1,26 @@ +package it.fabioformosa.quartzmanager.converters; + +import it.fabioformosa.metamorphosis.core.converters.AbstractBaseConverterToDTO; +import it.fabioformosa.quartzmanager.dto.JobDetailDTO; +import lombok.SneakyThrows; +import org.quartz.JobDetail; +import org.quartz.JobKey; +import org.quartz.Scheduler; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +public class JobKeyToJobDetailDTO extends AbstractBaseConverterToDTO { + + @Autowired + private Scheduler scheduler; + + @SneakyThrows + @Override + protected void convert(JobKey jobKey, JobDetailDTO jobDetailDTO) { + JobDetail jobDetail = scheduler.getJobDetail(jobKey); + jobDetailDTO.setJobClassName(jobDetail.getJobClass().getName()); + jobDetailDTO.setDescription(jobDetail.getDescription()); + //jobDetail.getJobDataMap(); + } +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/TriggerToTriggerDTO.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/TriggerToTriggerDTO.java index 3c35a2e..576b453 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/TriggerToTriggerDTO.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/converters/TriggerToTriggerDTO.java @@ -1,6 +1,7 @@ package it.fabioformosa.quartzmanager.converters; import it.fabioformosa.metamorphosis.core.converters.AbstractBaseConverter; +import it.fabioformosa.quartzmanager.dto.JobDetailDTO; import it.fabioformosa.quartzmanager.dto.JobKeyDTO; import it.fabioformosa.quartzmanager.dto.TriggerDTO; import it.fabioformosa.quartzmanager.dto.TriggerKeyDTO; @@ -30,6 +31,9 @@ public class TriggerToTriggerDTO extend JobKey jobKey = source.getJobKey(); JobKeyDTO jobKeyDTO = conversionService.convert(jobKey, JobKeyDTO.class); target.setJobKeyDTO(jobKeyDTO); + + JobDetailDTO jobDetailDTO = conversionService.convert(jobKey, JobDetailDTO.class); + target.setJobDetailDTO(jobDetailDTO); } @Override diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/dto/JobDetailDTO.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/dto/JobDetailDTO.java new file mode 100644 index 0000000..84998c2 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/dto/JobDetailDTO.java @@ -0,0 +1,14 @@ +package it.fabioformosa.quartzmanager.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@NoArgsConstructor +@AllArgsConstructor +@Data +public class JobDetailDTO { + private String jobClassName; + private String description; + +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/dto/TriggerDTO.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/dto/TriggerDTO.java index 4dd0f26..84fe33f 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/dto/TriggerDTO.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/dto/TriggerDTO.java @@ -21,5 +21,6 @@ public class TriggerDTO { private int misfireInstruction; private Date nextFireTime; private JobKeyDTO jobKeyDTO; + private JobDetailDTO jobDetailDTO; private boolean mayFireAgain; } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/AbstractSchedulerService.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/AbstractSchedulerService.java index 26c2ed2..2caa12d 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/AbstractSchedulerService.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/AbstractSchedulerService.java @@ -1,10 +1,7 @@ package it.fabioformosa.quartzmanager.services; import it.fabioformosa.quartzmanager.exceptions.TriggerNotFoundException; -import org.quartz.Scheduler; -import org.quartz.SchedulerException; -import org.quartz.Trigger; -import org.quartz.TriggerKey; +import org.quartz.*; import org.springframework.core.convert.ConversionService; public class AbstractSchedulerService { @@ -23,4 +20,9 @@ public class AbstractSchedulerService { throw new TriggerNotFoundException(name); return trigger; } + + protected JobDetail getJobDetailByKey(JobKey jobKey) throws SchedulerException { + return scheduler.getJobDetail(jobKey); + } + } From a693e2aa0cc49221f19439ee624eea5ca000ef81 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Wed, 7 Sep 2022 00:26:34 +0200 Subject: [PATCH 026/184] #52 no changes at the trigger list in case of rescheduling of an existing trigger --- .../simple-trigger-config.component.html | 20 +++-- .../simple-trigger-config.component.spec.ts | 89 ++++++++++++++++++- .../simple-trigger-config.component.ts | 9 +- .../trigger-list/trigger-list.component.html | 2 +- .../trigger-list/trigger-list.component.ts | 3 +- 5 files changed, 107 insertions(+), 16 deletions(-) diff --git a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html index 09f615d..797648c 100644 --- a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html +++ b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html @@ -10,7 +10,8 @@
Trigger Name - @@ -19,7 +20,8 @@
Job Class - @@ -28,7 +30,8 @@
Start Date (optional) - @@ -44,7 +47,8 @@
End Date (optional) - Repeat Interval [in mills] - @@ -70,7 +75,8 @@
Repeat Count - @@ -91,7 +97,7 @@ diff --git a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.spec.ts b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.spec.ts index 0ebd2cc..e44882b 100644 --- a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.spec.ts +++ b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.spec.ts @@ -11,13 +11,13 @@ import {MatIconModule} from '@angular/material/icon'; import {FormsModule} from '@angular/forms'; import {MatFormFieldModule} from '@angular/material/form-field'; import {MatNativeDateModule} from '@angular/material/core'; -import {MatDatepickerModule} from '@angular/material/datepicker'; import {MatInputModule} from '@angular/material/input'; import {BrowserAnimationsModule} from '@angular/platform-browser/animations'; import {TriggerKey} from '../../model/triggerKey.model'; import {Trigger} from '../../model/trigger.model'; -import {NgxMatDatetimePickerModule} from '@angular-material-components/datetime-picker'; -import { NgxMatMomentModule } from '@angular-material-components/moment-adapter'; +import {JobDetail} from '../../model/jobDetail.model'; +import {SimpleTriggerForm} from '../../model/simple-trigger.form'; +import {SimpleTrigger} from '../../model/simple-trigger.model'; describe('SimpleTriggerConfig', () => { @@ -44,6 +44,7 @@ describe('SimpleTriggerConfig', () => { beforeEach(() => { fixture = TestBed.createComponent(SimpleTriggerConfigComponent); component = fixture.componentInstance; + fixture.detectChanges(); }); it('should fetch no triggers at the init', () => { @@ -51,6 +52,87 @@ describe('SimpleTriggerConfig', () => { httpTestingController.expectNone('/quartz-manager/simple-triggers/my-simple-trigger'); }); + function setInputValue(componentDe: DebugElement, inputSelector: string, value: string) { + const inputDe = componentDe.query(By.css(inputSelector)); + const inputEl = inputDe.nativeElement; + inputEl.value = value; + inputEl.dispatchEvent(new Event('input')); + fixture.detectChanges(); + } + + it('should emit an event when a new trigger is submitted', () => { + const mockTrigger = new Trigger(); + mockTrigger.triggerKeyDTO = new TriggerKey('test-trigger', null); + mockTrigger.jobDetailDTO = {jobClassName: 'TestJob', description: null}; + + component.openTriggerForm(); + fixture.detectChanges(); + + const componentDe: DebugElement = fixture.debugElement; + setInputValue(componentDe, '#triggerName', 'test-trigger'); + expect(component.simpleTriggerForm.triggerName).toEqual('test-trigger'); + setInputValue(componentDe, '#jobClass', 'TestJob'); + // setInputValue(componentDe, '#startDate', '19/11/2022, 10:34:00 PM'); + // setInputValue(componentDe, '#endDate', '21/11/2022, 10:34:00 PM'); + setInputValue(componentDe, '#repeatInterval', '2000'); + expect(component.simpleTriggerForm.repeatInterval).toEqual(2000); + setInputValue(componentDe, '#repeatCount', '100'); + expect(component.simpleTriggerForm.repeatCount).toEqual(100); + + const submitButton = componentDe.query(By.css('form > button[color="primary"]')); + expect(submitButton.nativeElement.textContent.trim()).toEqual('Submit'); + + let actualNewTrigger; + component.onNewTrigger.subscribe(simpleTrigger => actualNewTrigger = simpleTrigger); + + submitButton.nativeElement.click(); + + const postSimpleTriggerReq = httpTestingController.expectOne('/quartz-manager/simple-triggers/test-trigger'); + postSimpleTriggerReq.flush(mockTrigger); + + expect(actualNewTrigger).toEqual(mockTrigger); + }); + + it('should not emit an event when an existing trigger is edited', () => { + const mockTriggerKey = new TriggerKey('test-trigger', null); + component.triggerKey = mockTriggerKey; + fixture.detectChanges(); + + const mockTrigger = new SimpleTrigger(); + mockTrigger.triggerKeyDTO = new TriggerKey('test-trigger', null); + mockTrigger.jobDetailDTO = {jobClassName: 'TestJob', description: null}; + mockTrigger.mayFireAgain = true; + const getSimpleTriggerReq = httpTestingController.expectOne('/quartz-manager/simple-triggers/test-trigger'); + getSimpleTriggerReq.flush(mockTrigger); + + component.simpleTriggerForm = { + triggerName: 'test-trigger', + jobClass: 'TestJob', + repeatInterval: 2000, + repeatCount: 100 + }; + + component.openTriggerForm(); + fixture.detectChanges(); + + const componentDe: DebugElement = fixture.debugElement; + setInputValue(componentDe, '#repeatInterval', '4000'); + expect(component.simpleTriggerForm.repeatInterval).toEqual(4000); + + const submitButton = componentDe.query(By.css('form > button[color="primary"]')); + expect(submitButton.nativeElement.textContent.trim()).toEqual('Submit'); + + let actualNewTrigger; + component.onNewTrigger.subscribe(simpleTrigger => actualNewTrigger = simpleTrigger); + + submitButton.nativeElement.click(); + + const putSimpleTriggerReq = httpTestingController.expectOne('/quartz-manager/simple-triggers/test-trigger'); + putSimpleTriggerReq.flush(mockTrigger); + + expect(actualNewTrigger).toBeUndefined(); + }); + 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; @@ -58,6 +140,7 @@ describe('SimpleTriggerConfig', () => { const mockTrigger = new Trigger(); mockTrigger.triggerKeyDTO = mockTriggerKey; + mockTrigger.jobDetailDTO = {jobClassName: 'TestJob', description: null}; const getSimpleTriggerReq = httpTestingController.expectOne('/quartz-manager/simple-triggers/my-simple-trigger'); getSimpleTriggerReq.flush(mockTrigger); diff --git a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts index 8c751ed..1af359e 100644 --- a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts +++ b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts @@ -85,7 +85,10 @@ export class SimpleTriggerConfigComponent implements OnInit { this.fetchedTriggers = true; this.triggerInProgress = this.trigger.mayFireAgain; - this.onNewTrigger.emit(retTrigger); + if (schedulerServiceCall === this.schedulerService.saveSimpleTriggerConfig) { + this.onNewTrigger.emit(retTrigger); + } + this.closeTriggerForm(); }, error => { this.simpleTriggerForm = this.formBackup; @@ -108,8 +111,8 @@ export class SimpleTriggerConfigComponent implements OnInit { simpleTriggerCommand.triggerName = simpleTriggerForm.triggerName; simpleTriggerCommand.repeatCount = simpleTriggerForm.repeatCount; simpleTriggerCommand.repeatInterval = simpleTriggerForm.repeatInterval; - simpleTriggerCommand.startDate = simpleTriggerForm.startDate.toDate(); - simpleTriggerCommand.endDate = simpleTriggerForm.endDate.toDate(); + simpleTriggerCommand.startDate = simpleTriggerForm.startDate?.toDate(); + simpleTriggerCommand.endDate = simpleTriggerForm.endDate?.toDate(); return simpleTriggerCommand; } diff --git a/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.html b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.html index 67cac7d..88677dd 100644 --- a/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.html +++ b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.html @@ -9,7 +9,7 @@ + [ngClass]="{'selectedTrigger': selectedTrigger && selectedTrigger.name==triggerKey.name}"> {{ triggerKey.name }} diff --git a/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.ts b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.ts index 124321b..2088bb3 100644 --- a/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.ts +++ b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.ts @@ -50,8 +50,7 @@ export class TriggerListComponent implements OnInit { this.triggerKeys = triggerKeys; if (!triggerKeys || triggerKeys.length === 0) { this.onNewTriggerBtnClicked(); - } - else { + } else { this.selectTrigger(this.triggerKeys[0]); } }) From 3e5b25b37a6cee5ebf2b20e81156591ee7452813 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Wed, 7 Sep 2022 20:36:27 +0200 Subject: [PATCH 027/184] #52 selected a new trigger once created --- .../trigger-list/trigger-list.component.ts | 7 ++++++- .../src/app/views/manager/manager.component.html | 3 +-- .../src/app/views/manager/manager.component.ts | 14 ++++++-------- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.ts b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.ts index 2088bb3..9d85156 100644 --- a/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.ts +++ b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.ts @@ -11,7 +11,6 @@ import {MatDialog} from '@angular/material/dialog'; }) export class TriggerListComponent implements OnInit { - @Input() newTriggers: Array = new Array(); loading = true; @@ -67,9 +66,15 @@ export class TriggerListComponent implements OnInit { else this.onNewTriggerClicked.emit(); } + + onNewTrigger(newTrigger: SimpleTrigger) { + this.newTriggers = [newTrigger, ...this.newTriggers]; + this.selectedTrigger = newTrigger.triggerKeyDTO; + } } @Component({ template: 'Multiple jobs not supported yet - Coming Soon...', }) +// tslint:disable-next-line:component-class-suffix export class UnsupportedMultipleJobsDialog {} diff --git a/quartz-manager-frontend/src/app/views/manager/manager.component.html b/quartz-manager-frontend/src/app/views/manager/manager.component.html index 6131d71..eeea662 100644 --- a/quartz-manager-frontend/src/app/views/manager/manager.component.html +++ b/quartz-manager-frontend/src/app/views/manager/manager.component.html @@ -9,7 +9,6 @@
+ (onNewTrigger)="onNewTriggerCreated($event)">
diff --git a/quartz-manager-frontend/src/app/views/manager/manager.component.ts b/quartz-manager-frontend/src/app/views/manager/manager.component.ts index f8c51fd..01ce9f1 100644 --- a/quartz-manager-frontend/src/app/views/manager/manager.component.ts +++ b/quartz-manager-frontend/src/app/views/manager/manager.component.ts @@ -6,6 +6,7 @@ import { import {SimpleTrigger} from '../../model/simple-trigger.model'; import {TriggerKey} from '../../model/triggerKey.model'; import {SimpleTriggerConfigComponent} from '../../components/simple-trigger-config'; +import {TriggerListComponent} from '../../components'; @Component({ selector: 'manager', @@ -17,14 +18,14 @@ export class ManagerComponent implements OnInit { @ViewChild(SimpleTriggerConfigComponent) private triggerConfigComponent!: SimpleTriggerConfigComponent; + @ViewChild(TriggerListComponent) + private triggerListComponent: TriggerListComponent; + newTriggerFormOpened = false; - newTriggers = new Array(); selectedTriggerKey: TriggerKey; constructor( - private config: ConfigService, - private userService: UserService ) { } ngOnInit() { @@ -34,15 +35,12 @@ export class ManagerComponent implements OnInit { this.triggerConfigComponent.openTriggerForm(); } - onNewTrigger(newTrigger: SimpleTrigger) { - this.newTriggers.push(newTrigger); + onNewTriggerCreated(newTrigger: SimpleTrigger) { + this.triggerListComponent.onNewTrigger(newTrigger); } setSelectedTrigger(triggerKey: TriggerKey) { this.selectedTriggerKey = triggerKey; } - onTriggerFormToggled(formOpened: boolean) { - this.newTriggerFormOpened = formOpened; - } } From a644dd60523dc36204ce8d5293e960e3253af062 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Wed, 7 Sep 2022 20:45:34 +0200 Subject: [PATCH 028/184] #52 made read-only the trigger name of an existing trigger --- .../simple-trigger-config.component.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html index 797648c..734f7eb 100644 --- a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html +++ b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html @@ -8,11 +8,11 @@
- + Trigger Name
From d9f9ee96afbb54626b82d87bafbc371d4191b386 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Tue, 13 Sep 2022 23:38:31 +0200 Subject: [PATCH 029/184] #52 added the jobClass selection --- quartz-manager-frontend/src/app/app.module.ts | 4 ++ .../simple-trigger-config.component.html | 9 ++-- .../simple-trigger-config.component.spec.ts | 3 +- .../simple-trigger-config.component.ts | 13 ++++- .../src/app/model/simple-trigger.command.ts | 1 + .../src/app/services/config.service.ts | 54 ++++++++++--------- .../src/app/services/index.ts | 2 + .../src/app/services/job.service.ts | 18 +++++++ .../src/app/services/scheduler.service.ts | 9 ++-- .../quartz-manager-starter-api/pom.xml | 5 ++ .../AbstractTriggerController.java | 6 +-- .../controllers/JobController.java | 13 +++-- .../controllers/SimpleTriggerController.java | 2 +- .../controllers/TriggerController.java | 35 ++++++------ .../quartzmanager/dto/TriggerCommandDTO.java | 4 ++ .../quartzmanager/services/JobService.java | 43 +++++++++++++++ .../SimpleTriggerSchedulerService.java | 4 +- .../SimpleTriggerControllerTest.java | 5 +- .../controllers/TriggerControllerTest.java | 18 +------ .../services/JobServiceTest.java | 45 ++++++++++++++++ .../SimpleTriggerSchedulerServiceTest.java | 3 +- .../samplepackage/SampleExtraJob.java | 15 ++++++ .../quartzmanager/jobs/myjobs/SampleJob.java | 2 +- .../src/main/resources/application.yml | 1 + 24 files changed, 226 insertions(+), 88 deletions(-) create mode 100644 quartz-manager-frontend/src/app/services/job.service.ts create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/JobService.java create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/services/JobServiceTest.java create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/samplepackage/SampleExtraJob.java diff --git a/quartz-manager-frontend/src/app/app.module.ts b/quartz-manager-frontend/src/app/app.module.ts index 081bb21..939bdf1 100644 --- a/quartz-manager-frontend/src/app/app.module.ts +++ b/quartz-manager-frontend/src/app/app.module.ts @@ -18,6 +18,7 @@ import {MatIconModule} from '@angular/material/icon'; import {MatButtonModule} from '@angular/material/button'; import {MatCardModule} from '@angular/material/card'; import {MatDatepickerModule} from '@angular/material/datepicker'; +import {MatSelectModule} from '@angular/material/select'; import {MatListModule} from '@angular/material/list'; import {MatSidenavModule} from '@angular/material/sidenav'; @@ -50,6 +51,7 @@ import { AuthService, UserService, SchedulerService, + JobService, ConfigService, ProgressWebsocketService, LogsWebsocketService, @@ -138,6 +140,7 @@ export function jwtOptionsFactory(apiService: ApiService) { MatChipsModule, MatIconModule, MatInputModule, + MatSelectModule, MatToolbarModule, MatCardModule, MatListModule, @@ -164,6 +167,7 @@ export function jwtOptionsFactory(apiService: ApiService) { GuestGuard, AdminGuard, SchedulerService, + JobService, TriggerService, ProgressWebsocketService, LogsWebsocketService, diff --git a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html index 734f7eb..63d7fb1 100644 --- a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html +++ b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html @@ -20,10 +20,11 @@
Job Class - + + + {{job}} + +
diff --git a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.spec.ts b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.spec.ts index e44882b..cce311a 100644 --- a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.spec.ts +++ b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.spec.ts @@ -18,6 +18,7 @@ import {Trigger} from '../../model/trigger.model'; import {JobDetail} from '../../model/jobDetail.model'; import {SimpleTriggerForm} from '../../model/simple-trigger.form'; import {SimpleTrigger} from '../../model/simple-trigger.model'; +import JobService from '../../services/job.service'; describe('SimpleTriggerConfig', () => { @@ -33,7 +34,7 @@ describe('SimpleTriggerConfig', () => { MatNativeDateModule, MatCardModule, MatIconModule, HttpClientTestingModule, RouterTestingModule], declarations: [SimpleTriggerConfigComponent], - providers: [SchedulerService, ApiService, ConfigService], + providers: [SchedulerService, ApiService, ConfigService, JobService], schemas: [NO_ERRORS_SCHEMA] }).compileComponents(); diff --git a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts index 1af359e..b304226 100644 --- a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts +++ b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts @@ -1,5 +1,5 @@ import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; -import {SchedulerService} from '../../services'; +import {JobService, SchedulerService} from '../../services'; import {Scheduler} from '../../model/scheduler.model'; import {SimpleTriggerCommand} from '../../model/simple-trigger.command'; import {SimpleTrigger} from '../../model/simple-trigger.model'; @@ -27,16 +27,24 @@ export class SimpleTriggerConfigComponent implements OnInit { private selectedTriggerKey: TriggerKey; + private jobs: Array; + enabledTriggerForm = false; @Output() onNewTrigger = new EventEmitter(); constructor( - private schedulerService: SchedulerService + private schedulerService: SchedulerService, + private jobService: JobService ) { } ngOnInit() { + this.fetchJobs(); + } + + private fetchJobs() { + this.jobService.fetchJobs().subscribe(jobs => this.jobs = jobs); } openTriggerForm() { @@ -109,6 +117,7 @@ export class SimpleTriggerConfigComponent implements OnInit { private _fromFormToCommand = (simpleTriggerForm: SimpleTriggerForm): SimpleTriggerCommand => { const simpleTriggerCommand = new SimpleTriggerCommand(); simpleTriggerCommand.triggerName = simpleTriggerForm.triggerName; + simpleTriggerCommand.jobClass = simpleTriggerForm.jobClass; simpleTriggerCommand.repeatCount = simpleTriggerForm.repeatCount; simpleTriggerCommand.repeatInterval = simpleTriggerForm.repeatInterval; simpleTriggerCommand.startDate = simpleTriggerForm.startDate?.toDate(); diff --git a/quartz-manager-frontend/src/app/model/simple-trigger.command.ts b/quartz-manager-frontend/src/app/model/simple-trigger.command.ts index f6dc70b..03f1231 100644 --- a/quartz-manager-frontend/src/app/model/simple-trigger.command.ts +++ b/quartz-manager-frontend/src/app/model/simple-trigger.command.ts @@ -1,5 +1,6 @@ export class SimpleTriggerCommand { triggerName: string; + jobClass: string; startDate: Date; endDate: Date; repeatCount: number; diff --git a/quartz-manager-frontend/src/app/services/config.service.ts b/quartz-manager-frontend/src/app/services/config.service.ts index 9294bec..9a2d7e4 100644 --- a/quartz-manager-frontend/src/app/services/config.service.ts +++ b/quartz-manager-frontend/src/app/services/config.service.ts @@ -1,24 +1,28 @@ -import { Injectable } from '@angular/core'; -import { environment } from '../../environments/environment'; +import {Injectable} from '@angular/core'; +import {environment} from '../../environments/environment'; const WEBJAR_PATH = '/quartz-manager-ui/'; -export function getHtmlBaseUrl(){ - const baseUrl = getBaseUrl() || '/'; - return environment.production ? getBaseUrl() + WEBJAR_PATH: '/'; - } +export const CONTEXT_PATH = '/quartz-manager'; -export function getBaseUrl(){ - if(environment.production){ - let contextPath: string = window.location.pathname.split('/')[1] || ''; - if(contextPath && ('/' + contextPath + '/') === WEBJAR_PATH) - return ''; - if(contextPath) - contextPath = '/' + contextPath; - return contextPath; - } - return ''; +export function getHtmlBaseUrl() { + const baseUrl = getBaseUrl() || '/'; + return environment.production ? getBaseUrl() + WEBJAR_PATH : '/'; +} + +export function getBaseUrl() { + if (environment.production) { + let contextPath: string = window.location.pathname.split('/')[1] || ''; + if (contextPath && ('/' + contextPath + '/') === WEBJAR_PATH) { + return ''; + } + if (contextPath) { + contextPath = '/' + contextPath; + } + return contextPath; + } + return ''; } @Injectable() @@ -45,35 +49,35 @@ export class ConfigService { private _signup_url = this._api_url + '/signup'; get reset_credentials_url(): string { - return this._reset_credentials_url; + return this._reset_credentials_url; } get refresh_token_url(): string { - return this._refresh_token_url; + return this._refresh_token_url; } get whoami_url(): string { - return this._whoami_url; + return this._whoami_url; } get users_url(): string { - return this._users_url; + return this._users_url; } get login_url(): string { - return this._login_url; + return this._login_url; } get logout_url(): string { - return this._logout_url; + return this._logout_url; } get change_password_url(): string { - return this._change_password_url; + return this._change_password_url; } - get signup_url():string { - return this._signup_url; + get signup_url(): string { + return this._signup_url; } } diff --git a/quartz-manager-frontend/src/app/services/index.ts b/quartz-manager-frontend/src/app/services/index.ts index 1352120..a3e5802 100644 --- a/quartz-manager-frontend/src/app/services/index.ts +++ b/quartz-manager-frontend/src/app/services/index.ts @@ -7,4 +7,6 @@ export * from './websocket.service'; export * from './progress.websocket.service'; export * from './logs.websocket.service'; export * from './trigger.service' +export * from './job.service' + diff --git a/quartz-manager-frontend/src/app/services/job.service.ts b/quartz-manager-frontend/src/app/services/job.service.ts new file mode 100644 index 0000000..c5cbccc --- /dev/null +++ b/quartz-manager-frontend/src/app/services/job.service.ts @@ -0,0 +1,18 @@ +import {Injectable} from '@angular/core'; +import {ApiService} from './api.service'; +import {CONTEXT_PATH, getBaseUrl} from './config.service'; +import {Observable} from 'rxjs'; + +@Injectable() +export class JobService { + + constructor( + private apiService: ApiService + ) { + } + + fetchJobs = (): Observable => { + return this.apiService.get(getBaseUrl() + `${CONTEXT_PATH}/jobs`) + } + +} diff --git a/quartz-manager-frontend/src/app/services/scheduler.service.ts b/quartz-manager-frontend/src/app/services/scheduler.service.ts index 3f7642c..e675862 100644 --- a/quartz-manager-frontend/src/app/services/scheduler.service.ts +++ b/quartz-manager-frontend/src/app/services/scheduler.service.ts @@ -1,18 +1,15 @@ -import { Injectable } from '@angular/core'; -import { getBaseUrl } from '.'; -import { ApiService } from './api.service'; +import {Injectable} from '@angular/core'; +import {CONTEXT_PATH, getBaseUrl} from '.'; +import {ApiService} from './api.service'; import {Trigger} from '../model/trigger.model'; import {Observable} from 'rxjs'; import {SimpleTriggerCommand} from '../model/simple-trigger.command'; -import {SchedulerConfig} from '../model/schedulerConfig.model'; import {Scheduler} from '../model/scheduler.model'; -const CONTEXT_PATH = '/quartz-manager'; @Injectable() export class SchedulerService { - constructor( private apiService: ApiService ) { } diff --git a/quartz-manager-parent/quartz-manager-starter-api/pom.xml b/quartz-manager-parent/quartz-manager-starter-api/pom.xml index 4b0b6ab..62ba738 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-api/pom.xml @@ -97,6 +97,11 @@ javax.el 3.0.0 + + org.reflections + reflections + 0.10.2 + diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AbstractTriggerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AbstractTriggerController.java index ba76182..3c6a786 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AbstractTriggerController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AbstractTriggerController.java @@ -1,10 +1,8 @@ package it.fabioformosa.quartzmanager.controllers; -import org.springframework.beans.factory.annotation.Value; - public class AbstractTriggerController { - @Value("${quartz-manager.jobClass}") - protected String jobClassname; +// @Value("${quartz-manager.jobClass}") +// protected String jobClassname; } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/JobController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/JobController.java index 5a37549..5361818 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/JobController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/JobController.java @@ -1,21 +1,26 @@ package it.fabioformosa.quartzmanager.controllers; +import it.fabioformosa.quartzmanager.services.JobService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; -import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; @RequestMapping("/quartz-manager/jobs") @RestController public class JobController extends AbstractTriggerController { + private JobService jobService; + + public JobController(JobService jobService) { + this.jobService = jobService; + } + @GetMapping public List listJobs(){ - List jobClasses = new ArrayList(); - jobClasses.add(jobClassname); - return jobClasses; + return jobService.getJobClasses().stream().map(Class::getName).collect(Collectors.toList()); } } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerController.java index e8c5909..f1b0177 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerController.java @@ -62,7 +62,7 @@ public class SimpleTriggerController extends AbstractTriggerController { .triggerName(name) .simpleTriggerInputDTO(simpleTriggerInputDTO) .build(); - SimpleTriggerDTO newTriggerDTO = simpleSchedulerService.scheduleSimpleTrigger(jobClassname, simpleTriggerCommandDTO); + SimpleTriggerDTO newTriggerDTO = simpleSchedulerService.scheduleSimpleTrigger(simpleTriggerCommandDTO); log.info("SIMPLE TRIGGER - CREATED a SimpleTrigger {}", newTriggerDTO); return newTriggerDTO; } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java index 1982df7..58e95a9 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java @@ -13,7 +13,6 @@ import it.fabioformosa.quartzmanager.services.LegacySchedulerService; import it.fabioformosa.quartzmanager.services.TriggerService; import lombok.extern.slf4j.Slf4j; import org.quartz.SchedulerException; -import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; @@ -46,23 +45,23 @@ public class TriggerController extends AbstractTriggerController { return schedulerService.getLegacyTriggerByName(name); } - @Deprecated - @PostMapping("/{name}") - @ResponseStatus(HttpStatus.CREATED) - @Operation(summary = "Create a new trigger") - @ApiResponses(value = { - @ApiResponse(responseCode = "201", description = "Created the new trigger", - content = { @Content(mediaType = "application/json", - schema = @Schema(implementation = TriggerDTO.class)) }), - @ApiResponse(responseCode = "400", description = "Invalid config supplied", - content = @Content) - }) - public TriggerDTO postTrigger(@PathVariable String name, @Valid @RequestBody SchedulerConfigParam config) throws SchedulerException, ClassNotFoundException { - log.info("TRIGGER - CREATING a trigger {} {}", name, config); - TriggerDTO newTriggerDTO = schedulerService.scheduleNewTrigger(name, jobClassname, config); - log.info("TRIGGER - CREATED a trigger {}", newTriggerDTO); - return newTriggerDTO; - } +// @Deprecated +// @PostMapping("/{name}") +// @ResponseStatus(HttpStatus.CREATED) +// @Operation(summary = "Create a new trigger") +// @ApiResponses(value = { +// @ApiResponse(responseCode = "201", description = "Created the new trigger", +// content = { @Content(mediaType = "application/json", +// schema = @Schema(implementation = TriggerDTO.class)) }), +// @ApiResponse(responseCode = "400", description = "Invalid config supplied", +// content = @Content) +// }) +// public TriggerDTO postTrigger(@PathVariable String name, @Valid @RequestBody SchedulerConfigParam config) throws SchedulerException, ClassNotFoundException { +// log.info("TRIGGER - CREATING a trigger {} {}", name, config); +// TriggerDTO newTriggerDTO = schedulerService.scheduleNewTrigger(name, config); +// log.info("TRIGGER - CREATED a trigger {}", newTriggerDTO); +// return newTriggerDTO; +// } @PutMapping("/{name}") @Operation(summary = "Reschedule the trigger") diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/dto/TriggerCommandDTO.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/dto/TriggerCommandDTO.java index da58424..eb32405 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/dto/TriggerCommandDTO.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/dto/TriggerCommandDTO.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.annotation.JsonFormat; import lombok.*; import lombok.experimental.SuperBuilder; +import javax.validation.constraints.NotNull; import java.util.Date; @SuperBuilder @@ -14,6 +15,9 @@ import java.util.Date; @Data public class TriggerCommandDTO { + @NotNull + private String jobClass; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'") private Date startDate; diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/JobService.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/JobService.java new file mode 100644 index 0000000..6769018 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/JobService.java @@ -0,0 +1,43 @@ +package it.fabioformosa.quartzmanager.services; + +import it.fabioformosa.quartzmanager.jobs.AbstractLoggingJob; +import lombok.Getter; +import org.apache.commons.lang3.StringUtils; +import org.reflections.Reflections; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import javax.annotation.PostConstruct; +import java.util.*; +import java.util.stream.Collectors; + +@Service +public class JobService { + + @Getter + private List> jobClasses = new ArrayList<>(); + + private List jobClassPackages = new ArrayList<>(); + + public JobService(@Value("${quartz-manager.jobClassPackages}") String jobClassPackages) { + List splitPackages = Arrays.stream(Optional.of(jobClassPackages).map(str -> str.split(",")).get()) + .map(String::trim) + .filter(StringUtils::isNotBlank) + .collect(Collectors.toList()); + if (splitPackages.size() > 0) + this.jobClassPackages.addAll(splitPackages); + } + + @PostConstruct + public void initJobClassList() { + List> foundJobClasses = jobClassPackages.stream().flatMap(jobClassPackage -> findJobClassesInPackage(jobClassPackage).stream()).collect(Collectors.toList()); + if (foundJobClasses.size() > 0) + this.jobClasses.addAll(foundJobClasses); + } + + private static Set> findJobClassesInPackage(String packageStr) { + Reflections reflections = new Reflections(packageStr); + return reflections.getSubTypesOf(AbstractLoggingJob.class); + } + +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerService.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerService.java index 95ec237..158f811 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerService.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerService.java @@ -20,8 +20,8 @@ public class SimpleTriggerSchedulerService extends AbstractSchedulerService { return conversionService.convert(trigger, SimpleTriggerDTO.class); } - public SimpleTriggerDTO scheduleSimpleTrigger(String jobClassname, SimpleTriggerCommandDTO triggerCommandDTO) throws SchedulerException, ClassNotFoundException { - Class jobClass = (Class) Class.forName(jobClassname); + public SimpleTriggerDTO scheduleSimpleTrigger(SimpleTriggerCommandDTO triggerCommandDTO) throws SchedulerException, ClassNotFoundException { + Class jobClass = (Class) Class.forName(triggerCommandDTO.getSimpleTriggerInputDTO().getJobClass()); JobDetail jobDetail = JobBuilder.newJob() .ofType(jobClass) .storeDurably(false) diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerControllerTest.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerControllerTest.java index b0e9a54..fa72c6b 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerControllerTest.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerControllerTest.java @@ -29,7 +29,7 @@ import static org.springframework.test.web.servlet.request.MockMvcRequestBuilder @ContextConfiguration(classes = {QuartManagerApplicationTests.class}) @WebMvcTest(controllers = SimpleTriggerController.class, properties = { - "quartz-manager.jobClass=it.fabioformosa.quartzmanager.jobs.SampleJob" + "quartz-manager.jobClassPackages=it.fabioformosa.quartzmanager.jobs" }) class SimpleTriggerControllerTest { @@ -66,7 +66,7 @@ class SimpleTriggerControllerTest { void givenASimpleTriggerCommandDTO_whenPosted_thenANewSimpleTriggerIsCreated() throws Exception { SimpleTriggerInputDTO simpleTriggerInputDTO = buildSimpleTriggerCommandDTO(); SimpleTriggerDTO expectedSimpleTriggerDTO = TriggerUtils.getSimpleTriggerInstance("mytrigger", simpleTriggerInputDTO); - Mockito.when(simpleTriggerSchedulerService.scheduleSimpleTrigger(any(), any())).thenReturn(expectedSimpleTriggerDTO); + Mockito.when(simpleTriggerSchedulerService.scheduleSimpleTrigger(any())).thenReturn(expectedSimpleTriggerDTO); mockMvc.perform( post(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") .contentType(MediaType.APPLICATION_JSON) @@ -79,6 +79,7 @@ class SimpleTriggerControllerTest { private SimpleTriggerInputDTO buildSimpleTriggerCommandDTO() { return SimpleTriggerInputDTO.builder() + .jobClass("it.fabioformosa.quartzmanager.jobs.SampleJob") .startDate(new Date()) .repeatCount(20) .repeatInterval(20000L) diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java index 6f741ba..783a32d 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java @@ -20,12 +20,11 @@ import org.springframework.test.context.ContextConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; -import static org.mockito.ArgumentMatchers.any; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; @ContextConfiguration(classes = {QuartManagerApplicationTests.class}) @WebMvcTest(controllers = TriggerController.class, properties = { - "quartz-manager.jobClass=it.fabioformosa.quartzmanager.jobs.SampleJob" + "quartz-manager.jobClassPackages=it.fabioformosa.quartzmanager.jobs" }) class TriggerControllerTest { @@ -40,21 +39,6 @@ class TriggerControllerTest { Mockito.reset(schedulerService); } - @Test - void givenASchedulerConfigParam_whenPosted_thenANewTriggerIsCreated() throws Exception { - SchedulerConfigParam configParamToPost = buildSimpleSchedulerConfigParam(); - TriggerDTO expectedTriggerDTO = TriggerUtils.getTriggerInstance("mytrigger"); - Mockito.when(schedulerService.scheduleNewTrigger(any(), any(), any())).thenReturn(expectedTriggerDTO); - mockMvc.perform( - post(TriggerController.TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") - .contentType(MediaType.APPLICATION_JSON) - .content(TestUtils.toJson(configParamToPost)) - ) - .andExpect(MockMvcResultMatchers.status().isCreated()) - .andExpect(MockMvcResultMatchers.content().json(TestUtils.toJson(expectedTriggerDTO))) - ; - } - @ParameterizedTest @ArgumentsSource(InvalidSchedulerConfigParamProvider.class) void givenAnInvalidSchedulerConfigParam_whenRequestedANewTrigger_thenAnErrorIsReturned(SchedulerConfigParam invalidSchedulerConfigParam) throws Exception { diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/services/JobServiceTest.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/services/JobServiceTest.java new file mode 100644 index 0000000..daf9c20 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/services/JobServiceTest.java @@ -0,0 +1,45 @@ +package it.fabioformosa.quartzmanager.services; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + + +class JobServiceTest { + + @Test + void givenTwoJobClassesInTwoPackages_whenTheJobServiceIsCalled_shouldReturnTwoJobClasses(){ + JobService jobService = new JobService("it.fabioformosa.quartzmanager.jobs, it.fabioformosa.samplepackage"); + jobService.initJobClassList(); + Assertions.assertThat(jobService).isNotNull(); + Assertions.assertThat(jobService.getJobClasses().size()).isEqualTo(2); + } + + @ParameterizedTest + @ValueSource(strings = { + "it.fabioformosa.quartzmanager.jobs", + "it.fabioformosa.quartzmanager.jobs,", + ",it.fabioformosa.quartzmanager.jobs" + }) + void givenOnePackage_whenTheJobServiceIsCalled_shouldReturnOneJobClasses(String packageStr){ + JobService jobService = new JobService(packageStr); + jobService.initJobClassList(); + Assertions.assertThat(jobService).isNotNull(); + Assertions.assertThat(jobService.getJobClasses().size()).isEqualTo(1); + } + + @ParameterizedTest + @ValueSource(strings = { + "", + ",", + ", " + }) + void givenNoPackages_whenTheJobServiceIsCalled_shouldReturnNoJobClasses(String packageStr){ + JobService jobService = new JobService(packageStr); + jobService.initJobClassList(); + Assertions.assertThat(jobService).isNotNull(); + Assertions.assertThat(jobService.getJobClasses()).isEmpty(); + } + +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerServiceTest.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerServiceTest.java index ecf8bb9..332854a 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerServiceTest.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerServiceTest.java @@ -48,6 +48,7 @@ class SimpleTriggerSchedulerServiceTest { @Test void givenASimpleTriggerCommandDTO_whenASimpleTriggerIsScheduled_thenATriggerDTOIsReturned() throws SchedulerException, ClassNotFoundException { SimpleTriggerInputDTO triggerInputDTO = SimpleTriggerInputDTO.builder() + .jobClass("it.fabioformosa.quartzmanager.jobs.SampleJob") .startDate(new Date()) .repeatInterval(5000L).repeatCount(5) .endDate(DateUtils.getHoursFromNow(1)) @@ -73,7 +74,7 @@ class SimpleTriggerSchedulerServiceTest { .triggerName(simpleTriggerName) .simpleTriggerInputDTO(triggerInputDTO) .build(); - SimpleTriggerDTO simpleTrigger = simpleSchedulerService.scheduleSimpleTrigger("it.fabioformosa.quartzmanager.jobs.SampleJob", simpleTriggerCommandDTO); + SimpleTriggerDTO simpleTrigger = simpleSchedulerService.scheduleSimpleTrigger(simpleTriggerCommandDTO); Assertions.assertThat(simpleTrigger).isEqualTo(expectedTriggerDTO); } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/samplepackage/SampleExtraJob.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/samplepackage/SampleExtraJob.java new file mode 100644 index 0000000..41283bd --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/samplepackage/SampleExtraJob.java @@ -0,0 +1,15 @@ +package it.fabioformosa.samplepackage; + +import it.fabioformosa.quartzmanager.jobs.AbstractLoggingJob; +import it.fabioformosa.quartzmanager.jobs.entities.LogRecord; +import it.fabioformosa.quartzmanager.jobs.entities.LogRecord.LogType; +import org.quartz.JobExecutionContext; + +public class SampleExtraJob extends AbstractLoggingJob { + + @Override + public LogRecord doIt(JobExecutionContext jobExecutionContext) { + return new LogRecord(LogType.INFO, "Hello!"); + } + +} diff --git a/quartz-manager-parent/quartz-manager-web-showcase/src/main/java/it/fabioformosa/quartzmanager/jobs/myjobs/SampleJob.java b/quartz-manager-parent/quartz-manager-web-showcase/src/main/java/it/fabioformosa/quartzmanager/jobs/myjobs/SampleJob.java index 7c13981..b4606aa 100644 --- a/quartz-manager-parent/quartz-manager-web-showcase/src/main/java/it/fabioformosa/quartzmanager/jobs/myjobs/SampleJob.java +++ b/quartz-manager-parent/quartz-manager-web-showcase/src/main/java/it/fabioformosa/quartzmanager/jobs/myjobs/SampleJob.java @@ -10,7 +10,7 @@ import it.fabioformosa.quartzmanager.jobs.entities.LogRecord.LogType; public class SampleJob extends AbstractLoggingJob { @Override public LogRecord doIt(JobExecutionContext jobExecutionContext) { - return new LogRecord(LogType.INFO, "Hello!"); + return new LogRecord(LogType.INFO, "Hello World!"); } } diff --git a/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml b/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml index 66b3ad5..722ad7e 100644 --- a/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml +++ b/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml @@ -52,6 +52,7 @@ quartz-manager: enabled: true cookie: AUTH-TOKEN jobClass: it.fabioformosa.quartzmanager.jobs.myjobs.SampleJob + jobClassPackages: it.fabioformosa.quartzmanager.jobs accounts: in-memory: enabled: true From b14cf641240120c02f6cf52f0c3e348a6dc67b40 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Wed, 14 Sep 2022 20:26:10 +0200 Subject: [PATCH 030/184] #59 reduced the size of the trigger form --- .../simple-trigger-config.component.html | 29 +++++++++++++++---- .../simple-trigger-config.component.scss | 4 +++ .../app/views/manager/manager.component.html | 2 +- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html index 63d7fb1..f68dbdd 100644 --- a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html +++ b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html @@ -8,7 +8,9 @@
- + Trigger Name
- + Job Class @@ -29,7 +34,10 @@
- + Start Date (optional)
- + End Date (optional)
- + Repeat Interval [in mills]
- + Repeat Count
-
+
Date: Wed, 14 Sep 2022 20:26:31 +0200 Subject: [PATCH 031/184] #52 removed an unused app property --- .../configuration/SchedulerConfig.java | 19 ------------------- .../src/test/resources/application.yml | 2 +- .../src/main/resources/application.yml | 1 - 3 files changed, 1 insertion(+), 21 deletions(-) diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfig.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfig.java index 4d9749a..63b897c 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfig.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfig.java @@ -2,10 +2,8 @@ package it.fabioformosa.quartzmanager.configuration; import it.fabioformosa.quartzmanager.common.properties.QuartzModuleProperties; import it.fabioformosa.quartzmanager.scheduler.AutowiringSpringBeanJobFactory; -import org.quartz.Job; import org.quartz.spi.JobFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.config.PropertiesFactoryBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.ApplicationContext; @@ -13,7 +11,6 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; -import org.springframework.scheduling.quartz.JobDetailFactoryBean; import org.springframework.scheduling.quartz.SchedulerFactoryBean; import java.io.IOException; @@ -24,25 +21,9 @@ import java.util.Properties; @ConditionalOnProperty(name = "quartz.enabled") public class SchedulerConfig { - private static JobDetailFactoryBean createJobDetail(Class jobClass) { - JobDetailFactoryBean factoryBean = new JobDetailFactoryBean(); - factoryBean.setJobClass(jobClass); - factoryBean.setDurability(false); - return factoryBean; - } - - @Value("${quartz-manager.jobClass}") - private String jobClassname; - @Autowired(required = false) private QuartzModuleProperties quartzModuleProperties; - @Bean - public JobDetailFactoryBean jobDetail() throws ClassNotFoundException { - Class JobClass = (Class) Class.forName(jobClassname); - return createJobDetail(JobClass); - } - @Bean public JobFactory jobFactory(ApplicationContext applicationContext) { AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory(); diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/resources/application.yml b/quartz-manager-parent/quartz-manager-starter-api/src/test/resources/application.yml index 88a3041..1d3a7ac 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/test/resources/application.yml +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/resources/application.yml @@ -15,4 +15,4 @@ logging: org.quartz: INFO quartz-manager: - jobClass: it.fabioformosa.quartzmanager.jobs.SampleJob + jobClassPackages: it.fabioformosa.quartzmanager.jobs diff --git a/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml b/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml index 722ad7e..fde9f72 100644 --- a/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml +++ b/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml @@ -51,7 +51,6 @@ quartz-manager: cookie-strategy: enabled: true cookie: AUTH-TOKEN - jobClass: it.fabioformosa.quartzmanager.jobs.myjobs.SampleJob jobClassPackages: it.fabioformosa.quartzmanager.jobs accounts: in-memory: From e0b03783290d9b28dabd939b2f4530e5723bd6bf Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Thu, 15 Sep 2022 00:37:54 +0200 Subject: [PATCH 032/184] #59 changed the layout of the job progress card --- .../progress-panel.component.html | 46 +++++++++++-------- .../progress-panel.component.scss | 33 +++++++++++++ 2 files changed, 60 insertions(+), 19 deletions(-) diff --git a/quartz-manager-frontend/src/app/components/progress-panel/progress-panel.component.html b/quartz-manager-frontend/src/app/components/progress-panel/progress-panel.component.html index 479abc6..d9976db 100644 --- a/quartz-manager-frontend/src/app/components/progress-panel/progress-panel.component.html +++ b/quartz-manager-frontend/src/app/components/progress-panel/progress-panel.component.html @@ -6,31 +6,39 @@
--> - + JOB PROGRESS -
- - {{percentageStr}} -
+
+ + {{percentageStr}} +
-
+
+ {{progress.timesTriggered}} +  / {{progress.repeatCount}} +
-
- counter  - {{progress.timesTriggered}} / {{progress.repeatCount}} + -

- - job key {{progress.jobKey}}
- job class {{progress.jobClass}}
- -
- prev fire time {{progress.previousFireTime|date:'dd-MM-yyyy HH:mm:ss'}}
- next fire time {{progress.nextFireTime|date:'dd-MM-yyyy HH:mm:ss'}}
- final fire time {{progress.finalFireTime|date:'dd-MM-yyyy HH:mm:ss'}}
-
+
+
+
prev fire time
+
{{progress.previousFireTime|date:'dd-MM-yyyy HH:mm:ss'}}
+
-
+
+
+
next fire time
+
{{progress.nextFireTime|date:'dd-MM-yyyy HH:mm:ss'}}
+
-
+
+
+
final fire time
+
{{progress.finalFireTime|date:'dd-MM-yyyy HH:mm:ss'}}
+
-
+
+
diff --git a/quartz-manager-frontend/src/app/components/progress-panel/progress-panel.component.scss b/quartz-manager-frontend/src/app/components/progress-panel/progress-panel.component.scss index e69de29..7aa05ec 100644 --- a/quartz-manager-frontend/src/app/components/progress-panel/progress-panel.component.scss +++ b/quartz-manager-frontend/src/app/components/progress-panel/progress-panel.component.scss @@ -0,0 +1,33 @@ +#timesTriggeredCounter{ + font-size: 2em; +} +#totCounter{ + margin-top: auto; + text-align: center; + margin-bottom: 5px; + font-size: 0.8em; +} +#counterBox{ + margin-bottom: 1em; +} + +.fireBox{ + width: 100%; + border-right: 1px solid rgba(0,0,0,.12); + margin: 8px; +} + +.fireBox:last-child { + border-right: 0; +} + +.fireBoxHeader{ + font-size: 0.8em; + text-align: center; + color: grey; + margin-bottom: 0.5em; +} + +.fireBoxContent{ + text-align: center; +} From 5be49a4090a27d4c3d4bba22917556ba06b2ddaa Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Fri, 16 Sep 2022 23:39:49 +0200 Subject: [PATCH 033/184] #59 fixed animation on logs --- quartz-manager-frontend/src/animate.css | 7 +++ .../logs-panel/logs-panel.component.html | 46 +++++++++---------- .../logs-panel/logs-panel.component.scss | 6 ++- .../logs-panel/logs-panel.component.ts | 4 +- .../progress-panel.component.html | 4 +- .../simple-trigger-config.component.html | 4 +- .../app/views/manager/manager.component.html | 11 ++--- quartz-manager-frontend/src/styles.css | 1 + 8 files changed, 47 insertions(+), 36 deletions(-) create mode 100644 quartz-manager-frontend/src/animate.css diff --git a/quartz-manager-frontend/src/animate.css b/quartz-manager-frontend/src/animate.css new file mode 100644 index 0000000..cd6869d --- /dev/null +++ b/quartz-manager-frontend/src/animate.css @@ -0,0 +1,7 @@ +@charset "UTF-8";/*! + * animate.css - https://animate.style/ + * Version - 4.1.1 + * Licensed under the MIT license - http://opensource.org/licenses/MIT + * + * Copyright (c) 2020 Animate.css + */:root{--animate-duration:1s;--animate-delay:1s;--animate-repeat:1}.animate__animated{-webkit-animation-duration:1s;animation-duration:1s;-webkit-animation-duration:var(--animate-duration);animation-duration:var(--animate-duration);-webkit-animation-fill-mode:both;animation-fill-mode:both}.animate__animated.animate__infinite{-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite}.animate__animated.animate__repeat-1{-webkit-animation-iteration-count:1;animation-iteration-count:1;-webkit-animation-iteration-count:var(--animate-repeat);animation-iteration-count:var(--animate-repeat)}.animate__animated.animate__repeat-2{-webkit-animation-iteration-count:2;animation-iteration-count:2;-webkit-animation-iteration-count:calc(var(--animate-repeat)*2);animation-iteration-count:calc(var(--animate-repeat)*2)}.animate__animated.animate__repeat-3{-webkit-animation-iteration-count:3;animation-iteration-count:3;-webkit-animation-iteration-count:calc(var(--animate-repeat)*3);animation-iteration-count:calc(var(--animate-repeat)*3)}.animate__animated.animate__delay-1s{-webkit-animation-delay:1s;animation-delay:1s;-webkit-animation-delay:var(--animate-delay);animation-delay:var(--animate-delay)}.animate__animated.animate__delay-2s{-webkit-animation-delay:2s;animation-delay:2s;-webkit-animation-delay:calc(var(--animate-delay)*2);animation-delay:calc(var(--animate-delay)*2)}.animate__animated.animate__delay-3s{-webkit-animation-delay:3s;animation-delay:3s;-webkit-animation-delay:calc(var(--animate-delay)*3);animation-delay:calc(var(--animate-delay)*3)}.animate__animated.animate__delay-4s{-webkit-animation-delay:4s;animation-delay:4s;-webkit-animation-delay:calc(var(--animate-delay)*4);animation-delay:calc(var(--animate-delay)*4)}.animate__animated.animate__delay-5s{-webkit-animation-delay:5s;animation-delay:5s;-webkit-animation-delay:calc(var(--animate-delay)*5);animation-delay:calc(var(--animate-delay)*5)}.animate__animated.animate__faster{-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-duration:calc(var(--animate-duration)/2);animation-duration:calc(var(--animate-duration)/2)}.animate__animated.animate__fast{-webkit-animation-duration:.8s;animation-duration:.8s;-webkit-animation-duration:calc(var(--animate-duration)*0.8);animation-duration:calc(var(--animate-duration)*0.8)}.animate__animated.animate__slow{-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-duration:calc(var(--animate-duration)*2);animation-duration:calc(var(--animate-duration)*2)}.animate__animated.animate__slower{-webkit-animation-duration:3s;animation-duration:3s;-webkit-animation-duration:calc(var(--animate-duration)*3);animation-duration:calc(var(--animate-duration)*3)}@media (prefers-reduced-motion:reduce),print{.animate__animated{-webkit-animation-duration:1ms!important;animation-duration:1ms!important;-webkit-transition-duration:1ms!important;transition-duration:1ms!important;-webkit-animation-iteration-count:1!important;animation-iteration-count:1!important}.animate__animated[class*=Out]{opacity:0}}@-webkit-keyframes bounce{0%,20%,53%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1);-webkit-transform:translateZ(0);transform:translateZ(0)}40%,43%{-webkit-animation-timing-function:cubic-bezier(.755,.05,.855,.06);animation-timing-function:cubic-bezier(.755,.05,.855,.06);-webkit-transform:translate3d(0,-30px,0) scaleY(1.1);transform:translate3d(0,-30px,0) scaleY(1.1)}70%{-webkit-animation-timing-function:cubic-bezier(.755,.05,.855,.06);animation-timing-function:cubic-bezier(.755,.05,.855,.06);-webkit-transform:translate3d(0,-15px,0) scaleY(1.05);transform:translate3d(0,-15px,0) scaleY(1.05)}80%{-webkit-transition-timing-function:cubic-bezier(.215,.61,.355,1);transition-timing-function:cubic-bezier(.215,.61,.355,1);-webkit-transform:translateZ(0) scaleY(.95);transform:translateZ(0) scaleY(.95)}90%{-webkit-transform:translate3d(0,-4px,0) scaleY(1.02);transform:translate3d(0,-4px,0) scaleY(1.02)}}@keyframes bounce{0%,20%,53%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1);-webkit-transform:translateZ(0);transform:translateZ(0)}40%,43%{-webkit-animation-timing-function:cubic-bezier(.755,.05,.855,.06);animation-timing-function:cubic-bezier(.755,.05,.855,.06);-webkit-transform:translate3d(0,-30px,0) scaleY(1.1);transform:translate3d(0,-30px,0) scaleY(1.1)}70%{-webkit-animation-timing-function:cubic-bezier(.755,.05,.855,.06);animation-timing-function:cubic-bezier(.755,.05,.855,.06);-webkit-transform:translate3d(0,-15px,0) scaleY(1.05);transform:translate3d(0,-15px,0) scaleY(1.05)}80%{-webkit-transition-timing-function:cubic-bezier(.215,.61,.355,1);transition-timing-function:cubic-bezier(.215,.61,.355,1);-webkit-transform:translateZ(0) scaleY(.95);transform:translateZ(0) scaleY(.95)}90%{-webkit-transform:translate3d(0,-4px,0) scaleY(1.02);transform:translate3d(0,-4px,0) scaleY(1.02)}}.animate__bounce{-webkit-animation-name:bounce;animation-name:bounce;-webkit-transform-origin:center bottom;transform-origin:center bottom}@-webkit-keyframes flash{0%,50%,to{opacity:1}25%,75%{opacity:0}}@keyframes flash{0%,50%,to{opacity:1}25%,75%{opacity:0}}.animate__flash{-webkit-animation-name:flash;animation-name:flash}@-webkit-keyframes pulse{0%{-webkit-transform:scaleX(1);transform:scaleX(1)}50%{-webkit-transform:scale3d(1.05,1.05,1.05);transform:scale3d(1.05,1.05,1.05)}to{-webkit-transform:scaleX(1);transform:scaleX(1)}}@keyframes pulse{0%{-webkit-transform:scaleX(1);transform:scaleX(1)}50%{-webkit-transform:scale3d(1.05,1.05,1.05);transform:scale3d(1.05,1.05,1.05)}to{-webkit-transform:scaleX(1);transform:scaleX(1)}}.animate__pulse{-webkit-animation-name:pulse;animation-name:pulse;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}@-webkit-keyframes rubberBand{0%{-webkit-transform:scaleX(1);transform:scaleX(1)}30%{-webkit-transform:scale3d(1.25,.75,1);transform:scale3d(1.25,.75,1)}40%{-webkit-transform:scale3d(.75,1.25,1);transform:scale3d(.75,1.25,1)}50%{-webkit-transform:scale3d(1.15,.85,1);transform:scale3d(1.15,.85,1)}65%{-webkit-transform:scale3d(.95,1.05,1);transform:scale3d(.95,1.05,1)}75%{-webkit-transform:scale3d(1.05,.95,1);transform:scale3d(1.05,.95,1)}to{-webkit-transform:scaleX(1);transform:scaleX(1)}}@keyframes rubberBand{0%{-webkit-transform:scaleX(1);transform:scaleX(1)}30%{-webkit-transform:scale3d(1.25,.75,1);transform:scale3d(1.25,.75,1)}40%{-webkit-transform:scale3d(.75,1.25,1);transform:scale3d(.75,1.25,1)}50%{-webkit-transform:scale3d(1.15,.85,1);transform:scale3d(1.15,.85,1)}65%{-webkit-transform:scale3d(.95,1.05,1);transform:scale3d(.95,1.05,1)}75%{-webkit-transform:scale3d(1.05,.95,1);transform:scale3d(1.05,.95,1)}to{-webkit-transform:scaleX(1);transform:scaleX(1)}}.animate__rubberBand{-webkit-animation-name:rubberBand;animation-name:rubberBand}@-webkit-keyframes shakeX{0%,to{-webkit-transform:translateZ(0);transform:translateZ(0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}20%,40%,60%,80%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}}@keyframes shakeX{0%,to{-webkit-transform:translateZ(0);transform:translateZ(0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(-10px,0,0);transform:translate3d(-10px,0,0)}20%,40%,60%,80%{-webkit-transform:translate3d(10px,0,0);transform:translate3d(10px,0,0)}}.animate__shakeX{-webkit-animation-name:shakeX;animation-name:shakeX}@-webkit-keyframes shakeY{0%,to{-webkit-transform:translateZ(0);transform:translateZ(0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}20%,40%,60%,80%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}}@keyframes shakeY{0%,to{-webkit-transform:translateZ(0);transform:translateZ(0)}10%,30%,50%,70%,90%{-webkit-transform:translate3d(0,-10px,0);transform:translate3d(0,-10px,0)}20%,40%,60%,80%{-webkit-transform:translate3d(0,10px,0);transform:translate3d(0,10px,0)}}.animate__shakeY{-webkit-animation-name:shakeY;animation-name:shakeY}@-webkit-keyframes headShake{0%{-webkit-transform:translateX(0);transform:translateX(0)}6.5%{-webkit-transform:translateX(-6px) rotateY(-9deg);transform:translateX(-6px) rotateY(-9deg)}18.5%{-webkit-transform:translateX(5px) rotateY(7deg);transform:translateX(5px) rotateY(7deg)}31.5%{-webkit-transform:translateX(-3px) rotateY(-5deg);transform:translateX(-3px) rotateY(-5deg)}43.5%{-webkit-transform:translateX(2px) rotateY(3deg);transform:translateX(2px) rotateY(3deg)}50%{-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes headShake{0%{-webkit-transform:translateX(0);transform:translateX(0)}6.5%{-webkit-transform:translateX(-6px) rotateY(-9deg);transform:translateX(-6px) rotateY(-9deg)}18.5%{-webkit-transform:translateX(5px) rotateY(7deg);transform:translateX(5px) rotateY(7deg)}31.5%{-webkit-transform:translateX(-3px) rotateY(-5deg);transform:translateX(-3px) rotateY(-5deg)}43.5%{-webkit-transform:translateX(2px) rotateY(3deg);transform:translateX(2px) rotateY(3deg)}50%{-webkit-transform:translateX(0);transform:translateX(0)}}.animate__headShake{-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;-webkit-animation-name:headShake;animation-name:headShake}@-webkit-keyframes swing{20%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}40%{-webkit-transform:rotate(-10deg);transform:rotate(-10deg)}60%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}80%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}@keyframes swing{20%{-webkit-transform:rotate(15deg);transform:rotate(15deg)}40%{-webkit-transform:rotate(-10deg);transform:rotate(-10deg)}60%{-webkit-transform:rotate(5deg);transform:rotate(5deg)}80%{-webkit-transform:rotate(-5deg);transform:rotate(-5deg)}to{-webkit-transform:rotate(0deg);transform:rotate(0deg)}}.animate__swing{-webkit-transform-origin:top center;transform-origin:top center;-webkit-animation-name:swing;animation-name:swing}@-webkit-keyframes tada{0%{-webkit-transform:scaleX(1);transform:scaleX(1)}10%,20%{-webkit-transform:scale3d(.9,.9,.9) rotate(-3deg);transform:scale3d(.9,.9,.9) rotate(-3deg)}30%,50%,70%,90%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate(3deg);transform:scale3d(1.1,1.1,1.1) rotate(3deg)}40%,60%,80%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate(-3deg);transform:scale3d(1.1,1.1,1.1) rotate(-3deg)}to{-webkit-transform:scaleX(1);transform:scaleX(1)}}@keyframes tada{0%{-webkit-transform:scaleX(1);transform:scaleX(1)}10%,20%{-webkit-transform:scale3d(.9,.9,.9) rotate(-3deg);transform:scale3d(.9,.9,.9) rotate(-3deg)}30%,50%,70%,90%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate(3deg);transform:scale3d(1.1,1.1,1.1) rotate(3deg)}40%,60%,80%{-webkit-transform:scale3d(1.1,1.1,1.1) rotate(-3deg);transform:scale3d(1.1,1.1,1.1) rotate(-3deg)}to{-webkit-transform:scaleX(1);transform:scaleX(1)}}.animate__tada{-webkit-animation-name:tada;animation-name:tada}@-webkit-keyframes wobble{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}15%{-webkit-transform:translate3d(-25%,0,0) rotate(-5deg);transform:translate3d(-25%,0,0) rotate(-5deg)}30%{-webkit-transform:translate3d(20%,0,0) rotate(3deg);transform:translate3d(20%,0,0) rotate(3deg)}45%{-webkit-transform:translate3d(-15%,0,0) rotate(-3deg);transform:translate3d(-15%,0,0) rotate(-3deg)}60%{-webkit-transform:translate3d(10%,0,0) rotate(2deg);transform:translate3d(10%,0,0) rotate(2deg)}75%{-webkit-transform:translate3d(-5%,0,0) rotate(-1deg);transform:translate3d(-5%,0,0) rotate(-1deg)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes wobble{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}15%{-webkit-transform:translate3d(-25%,0,0) rotate(-5deg);transform:translate3d(-25%,0,0) rotate(-5deg)}30%{-webkit-transform:translate3d(20%,0,0) rotate(3deg);transform:translate3d(20%,0,0) rotate(3deg)}45%{-webkit-transform:translate3d(-15%,0,0) rotate(-3deg);transform:translate3d(-15%,0,0) rotate(-3deg)}60%{-webkit-transform:translate3d(10%,0,0) rotate(2deg);transform:translate3d(10%,0,0) rotate(2deg)}75%{-webkit-transform:translate3d(-5%,0,0) rotate(-1deg);transform:translate3d(-5%,0,0) rotate(-1deg)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}.animate__wobble{-webkit-animation-name:wobble;animation-name:wobble}@-webkit-keyframes jello{0%,11.1%,to{-webkit-transform:translateZ(0);transform:translateZ(0)}22.2%{-webkit-transform:skewX(-12.5deg) skewY(-12.5deg);transform:skewX(-12.5deg) skewY(-12.5deg)}33.3%{-webkit-transform:skewX(6.25deg) skewY(6.25deg);transform:skewX(6.25deg) skewY(6.25deg)}44.4%{-webkit-transform:skewX(-3.125deg) skewY(-3.125deg);transform:skewX(-3.125deg) skewY(-3.125deg)}55.5%{-webkit-transform:skewX(1.5625deg) skewY(1.5625deg);transform:skewX(1.5625deg) skewY(1.5625deg)}66.6%{-webkit-transform:skewX(-.78125deg) skewY(-.78125deg);transform:skewX(-.78125deg) skewY(-.78125deg)}77.7%{-webkit-transform:skewX(.390625deg) skewY(.390625deg);transform:skewX(.390625deg) skewY(.390625deg)}88.8%{-webkit-transform:skewX(-.1953125deg) skewY(-.1953125deg);transform:skewX(-.1953125deg) skewY(-.1953125deg)}}@keyframes jello{0%,11.1%,to{-webkit-transform:translateZ(0);transform:translateZ(0)}22.2%{-webkit-transform:skewX(-12.5deg) skewY(-12.5deg);transform:skewX(-12.5deg) skewY(-12.5deg)}33.3%{-webkit-transform:skewX(6.25deg) skewY(6.25deg);transform:skewX(6.25deg) skewY(6.25deg)}44.4%{-webkit-transform:skewX(-3.125deg) skewY(-3.125deg);transform:skewX(-3.125deg) skewY(-3.125deg)}55.5%{-webkit-transform:skewX(1.5625deg) skewY(1.5625deg);transform:skewX(1.5625deg) skewY(1.5625deg)}66.6%{-webkit-transform:skewX(-.78125deg) skewY(-.78125deg);transform:skewX(-.78125deg) skewY(-.78125deg)}77.7%{-webkit-transform:skewX(.390625deg) skewY(.390625deg);transform:skewX(.390625deg) skewY(.390625deg)}88.8%{-webkit-transform:skewX(-.1953125deg) skewY(-.1953125deg);transform:skewX(-.1953125deg) skewY(-.1953125deg)}}.animate__jello{-webkit-animation-name:jello;animation-name:jello;-webkit-transform-origin:center;transform-origin:center}@-webkit-keyframes heartBeat{0%{-webkit-transform:scale(1);transform:scale(1)}14%{-webkit-transform:scale(1.3);transform:scale(1.3)}28%{-webkit-transform:scale(1);transform:scale(1)}42%{-webkit-transform:scale(1.3);transform:scale(1.3)}70%{-webkit-transform:scale(1);transform:scale(1)}}@keyframes heartBeat{0%{-webkit-transform:scale(1);transform:scale(1)}14%{-webkit-transform:scale(1.3);transform:scale(1.3)}28%{-webkit-transform:scale(1);transform:scale(1)}42%{-webkit-transform:scale(1.3);transform:scale(1.3)}70%{-webkit-transform:scale(1);transform:scale(1)}}.animate__heartBeat{-webkit-animation-name:heartBeat;animation-name:heartBeat;-webkit-animation-duration:1.3s;animation-duration:1.3s;-webkit-animation-duration:calc(var(--animate-duration)*1.3);animation-duration:calc(var(--animate-duration)*1.3);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}@-webkit-keyframes backInDown{0%{-webkit-transform:translateY(-1200px) scale(.7);transform:translateY(-1200px) scale(.7);opacity:.7}80%{-webkit-transform:translateY(0) scale(.7);transform:translateY(0) scale(.7);opacity:.7}to{-webkit-transform:scale(1);transform:scale(1);opacity:1}}@keyframes backInDown{0%{-webkit-transform:translateY(-1200px) scale(.7);transform:translateY(-1200px) scale(.7);opacity:.7}80%{-webkit-transform:translateY(0) scale(.7);transform:translateY(0) scale(.7);opacity:.7}to{-webkit-transform:scale(1);transform:scale(1);opacity:1}}.animate__backInDown{-webkit-animation-name:backInDown;animation-name:backInDown}@-webkit-keyframes backInLeft{0%{-webkit-transform:translateX(-2000px) scale(.7);transform:translateX(-2000px) scale(.7);opacity:.7}80%{-webkit-transform:translateX(0) scale(.7);transform:translateX(0) scale(.7);opacity:.7}to{-webkit-transform:scale(1);transform:scale(1);opacity:1}}@keyframes backInLeft{0%{-webkit-transform:translateX(-2000px) scale(.7);transform:translateX(-2000px) scale(.7);opacity:.7}80%{-webkit-transform:translateX(0) scale(.7);transform:translateX(0) scale(.7);opacity:.7}to{-webkit-transform:scale(1);transform:scale(1);opacity:1}}.animate__backInLeft{-webkit-animation-name:backInLeft;animation-name:backInLeft}@-webkit-keyframes backInRight{0%{-webkit-transform:translateX(2000px) scale(.7);transform:translateX(2000px) scale(.7);opacity:.7}80%{-webkit-transform:translateX(0) scale(.7);transform:translateX(0) scale(.7);opacity:.7}to{-webkit-transform:scale(1);transform:scale(1);opacity:1}}@keyframes backInRight{0%{-webkit-transform:translateX(2000px) scale(.7);transform:translateX(2000px) scale(.7);opacity:.7}80%{-webkit-transform:translateX(0) scale(.7);transform:translateX(0) scale(.7);opacity:.7}to{-webkit-transform:scale(1);transform:scale(1);opacity:1}}.animate__backInRight{-webkit-animation-name:backInRight;animation-name:backInRight}@-webkit-keyframes backInUp{0%{-webkit-transform:translateY(1200px) scale(.7);transform:translateY(1200px) scale(.7);opacity:.7}80%{-webkit-transform:translateY(0) scale(.7);transform:translateY(0) scale(.7);opacity:.7}to{-webkit-transform:scale(1);transform:scale(1);opacity:1}}@keyframes backInUp{0%{-webkit-transform:translateY(1200px) scale(.7);transform:translateY(1200px) scale(.7);opacity:.7}80%{-webkit-transform:translateY(0) scale(.7);transform:translateY(0) scale(.7);opacity:.7}to{-webkit-transform:scale(1);transform:scale(1);opacity:1}}.animate__backInUp{-webkit-animation-name:backInUp;animation-name:backInUp}@-webkit-keyframes backOutDown{0%{-webkit-transform:scale(1);transform:scale(1);opacity:1}20%{-webkit-transform:translateY(0) scale(.7);transform:translateY(0) scale(.7);opacity:.7}to{-webkit-transform:translateY(700px) scale(.7);transform:translateY(700px) scale(.7);opacity:.7}}@keyframes backOutDown{0%{-webkit-transform:scale(1);transform:scale(1);opacity:1}20%{-webkit-transform:translateY(0) scale(.7);transform:translateY(0) scale(.7);opacity:.7}to{-webkit-transform:translateY(700px) scale(.7);transform:translateY(700px) scale(.7);opacity:.7}}.animate__backOutDown{-webkit-animation-name:backOutDown;animation-name:backOutDown}@-webkit-keyframes backOutLeft{0%{-webkit-transform:scale(1);transform:scale(1);opacity:1}20%{-webkit-transform:translateX(0) scale(.7);transform:translateX(0) scale(.7);opacity:.7}to{-webkit-transform:translateX(-2000px) scale(.7);transform:translateX(-2000px) scale(.7);opacity:.7}}@keyframes backOutLeft{0%{-webkit-transform:scale(1);transform:scale(1);opacity:1}20%{-webkit-transform:translateX(0) scale(.7);transform:translateX(0) scale(.7);opacity:.7}to{-webkit-transform:translateX(-2000px) scale(.7);transform:translateX(-2000px) scale(.7);opacity:.7}}.animate__backOutLeft{-webkit-animation-name:backOutLeft;animation-name:backOutLeft}@-webkit-keyframes backOutRight{0%{-webkit-transform:scale(1);transform:scale(1);opacity:1}20%{-webkit-transform:translateX(0) scale(.7);transform:translateX(0) scale(.7);opacity:.7}to{-webkit-transform:translateX(2000px) scale(.7);transform:translateX(2000px) scale(.7);opacity:.7}}@keyframes backOutRight{0%{-webkit-transform:scale(1);transform:scale(1);opacity:1}20%{-webkit-transform:translateX(0) scale(.7);transform:translateX(0) scale(.7);opacity:.7}to{-webkit-transform:translateX(2000px) scale(.7);transform:translateX(2000px) scale(.7);opacity:.7}}.animate__backOutRight{-webkit-animation-name:backOutRight;animation-name:backOutRight}@-webkit-keyframes backOutUp{0%{-webkit-transform:scale(1);transform:scale(1);opacity:1}20%{-webkit-transform:translateY(0) scale(.7);transform:translateY(0) scale(.7);opacity:.7}to{-webkit-transform:translateY(-700px) scale(.7);transform:translateY(-700px) scale(.7);opacity:.7}}@keyframes backOutUp{0%{-webkit-transform:scale(1);transform:scale(1);opacity:1}20%{-webkit-transform:translateY(0) scale(.7);transform:translateY(0) scale(.7);opacity:.7}to{-webkit-transform:translateY(-700px) scale(.7);transform:translateY(-700px) scale(.7);opacity:.7}}.animate__backOutUp{-webkit-animation-name:backOutUp;animation-name:backOutUp}@-webkit-keyframes bounceIn{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}20%{-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}40%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}60%{opacity:1;-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}80%{-webkit-transform:scale3d(.97,.97,.97);transform:scale3d(.97,.97,.97)}to{opacity:1;-webkit-transform:scaleX(1);transform:scaleX(1)}}@keyframes bounceIn{0%,20%,40%,60%,80%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}20%{-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}40%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}60%{opacity:1;-webkit-transform:scale3d(1.03,1.03,1.03);transform:scale3d(1.03,1.03,1.03)}80%{-webkit-transform:scale3d(.97,.97,.97);transform:scale3d(.97,.97,.97)}to{opacity:1;-webkit-transform:scaleX(1);transform:scaleX(1)}}.animate__bounceIn{-webkit-animation-duration:.75s;animation-duration:.75s;-webkit-animation-duration:calc(var(--animate-duration)*0.75);animation-duration:calc(var(--animate-duration)*0.75);-webkit-animation-name:bounceIn;animation-name:bounceIn}@-webkit-keyframes bounceInDown{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0) scaleY(3);transform:translate3d(0,-3000px,0) scaleY(3)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0) scaleY(.9);transform:translate3d(0,25px,0) scaleY(.9)}75%{-webkit-transform:translate3d(0,-10px,0) scaleY(.95);transform:translate3d(0,-10px,0) scaleY(.95)}90%{-webkit-transform:translate3d(0,5px,0) scaleY(.985);transform:translate3d(0,5px,0) scaleY(.985)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes bounceInDown{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,-3000px,0) scaleY(3);transform:translate3d(0,-3000px,0) scaleY(3)}60%{opacity:1;-webkit-transform:translate3d(0,25px,0) scaleY(.9);transform:translate3d(0,25px,0) scaleY(.9)}75%{-webkit-transform:translate3d(0,-10px,0) scaleY(.95);transform:translate3d(0,-10px,0) scaleY(.95)}90%{-webkit-transform:translate3d(0,5px,0) scaleY(.985);transform:translate3d(0,5px,0) scaleY(.985)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}.animate__bounceInDown{-webkit-animation-name:bounceInDown;animation-name:bounceInDown}@-webkit-keyframes bounceInLeft{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0) scaleX(3);transform:translate3d(-3000px,0,0) scaleX(3)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0) scaleX(1);transform:translate3d(25px,0,0) scaleX(1)}75%{-webkit-transform:translate3d(-10px,0,0) scaleX(.98);transform:translate3d(-10px,0,0) scaleX(.98)}90%{-webkit-transform:translate3d(5px,0,0) scaleX(.995);transform:translate3d(5px,0,0) scaleX(.995)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes bounceInLeft{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(-3000px,0,0) scaleX(3);transform:translate3d(-3000px,0,0) scaleX(3)}60%{opacity:1;-webkit-transform:translate3d(25px,0,0) scaleX(1);transform:translate3d(25px,0,0) scaleX(1)}75%{-webkit-transform:translate3d(-10px,0,0) scaleX(.98);transform:translate3d(-10px,0,0) scaleX(.98)}90%{-webkit-transform:translate3d(5px,0,0) scaleX(.995);transform:translate3d(5px,0,0) scaleX(.995)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}.animate__bounceInLeft{-webkit-animation-name:bounceInLeft;animation-name:bounceInLeft}@-webkit-keyframes bounceInRight{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(3000px,0,0) scaleX(3);transform:translate3d(3000px,0,0) scaleX(3)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0) scaleX(1);transform:translate3d(-25px,0,0) scaleX(1)}75%{-webkit-transform:translate3d(10px,0,0) scaleX(.98);transform:translate3d(10px,0,0) scaleX(.98)}90%{-webkit-transform:translate3d(-5px,0,0) scaleX(.995);transform:translate3d(-5px,0,0) scaleX(.995)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes bounceInRight{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(3000px,0,0) scaleX(3);transform:translate3d(3000px,0,0) scaleX(3)}60%{opacity:1;-webkit-transform:translate3d(-25px,0,0) scaleX(1);transform:translate3d(-25px,0,0) scaleX(1)}75%{-webkit-transform:translate3d(10px,0,0) scaleX(.98);transform:translate3d(10px,0,0) scaleX(.98)}90%{-webkit-transform:translate3d(-5px,0,0) scaleX(.995);transform:translate3d(-5px,0,0) scaleX(.995)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}.animate__bounceInRight{-webkit-animation-name:bounceInRight;animation-name:bounceInRight}@-webkit-keyframes bounceInUp{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,3000px,0) scaleY(5);transform:translate3d(0,3000px,0) scaleY(5)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0) scaleY(.9);transform:translate3d(0,-20px,0) scaleY(.9)}75%{-webkit-transform:translate3d(0,10px,0) scaleY(.95);transform:translate3d(0,10px,0) scaleY(.95)}90%{-webkit-transform:translate3d(0,-5px,0) scaleY(.985);transform:translate3d(0,-5px,0) scaleY(.985)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes bounceInUp{0%,60%,75%,90%,to{-webkit-animation-timing-function:cubic-bezier(.215,.61,.355,1);animation-timing-function:cubic-bezier(.215,.61,.355,1)}0%{opacity:0;-webkit-transform:translate3d(0,3000px,0) scaleY(5);transform:translate3d(0,3000px,0) scaleY(5)}60%{opacity:1;-webkit-transform:translate3d(0,-20px,0) scaleY(.9);transform:translate3d(0,-20px,0) scaleY(.9)}75%{-webkit-transform:translate3d(0,10px,0) scaleY(.95);transform:translate3d(0,10px,0) scaleY(.95)}90%{-webkit-transform:translate3d(0,-5px,0) scaleY(.985);transform:translate3d(0,-5px,0) scaleY(.985)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}.animate__bounceInUp{-webkit-animation-name:bounceInUp;animation-name:bounceInUp}@-webkit-keyframes bounceOut{20%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}50%,55%{opacity:1;-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}to{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}}@keyframes bounceOut{20%{-webkit-transform:scale3d(.9,.9,.9);transform:scale3d(.9,.9,.9)}50%,55%{opacity:1;-webkit-transform:scale3d(1.1,1.1,1.1);transform:scale3d(1.1,1.1,1.1)}to{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}}.animate__bounceOut{-webkit-animation-duration:.75s;animation-duration:.75s;-webkit-animation-duration:calc(var(--animate-duration)*0.75);animation-duration:calc(var(--animate-duration)*0.75);-webkit-animation-name:bounceOut;animation-name:bounceOut}@-webkit-keyframes bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0) scaleY(.985);transform:translate3d(0,10px,0) scaleY(.985)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0) scaleY(.9);transform:translate3d(0,-20px,0) scaleY(.9)}to{opacity:0;-webkit-transform:translate3d(0,2000px,0) scaleY(3);transform:translate3d(0,2000px,0) scaleY(3)}}@keyframes bounceOutDown{20%{-webkit-transform:translate3d(0,10px,0) scaleY(.985);transform:translate3d(0,10px,0) scaleY(.985)}40%,45%{opacity:1;-webkit-transform:translate3d(0,-20px,0) scaleY(.9);transform:translate3d(0,-20px,0) scaleY(.9)}to{opacity:0;-webkit-transform:translate3d(0,2000px,0) scaleY(3);transform:translate3d(0,2000px,0) scaleY(3)}}.animate__bounceOutDown{-webkit-animation-name:bounceOutDown;animation-name:bounceOutDown}@-webkit-keyframes bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0) scaleX(.9);transform:translate3d(20px,0,0) scaleX(.9)}to{opacity:0;-webkit-transform:translate3d(-2000px,0,0) scaleX(2);transform:translate3d(-2000px,0,0) scaleX(2)}}@keyframes bounceOutLeft{20%{opacity:1;-webkit-transform:translate3d(20px,0,0) scaleX(.9);transform:translate3d(20px,0,0) scaleX(.9)}to{opacity:0;-webkit-transform:translate3d(-2000px,0,0) scaleX(2);transform:translate3d(-2000px,0,0) scaleX(2)}}.animate__bounceOutLeft{-webkit-animation-name:bounceOutLeft;animation-name:bounceOutLeft}@-webkit-keyframes bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0) scaleX(.9);transform:translate3d(-20px,0,0) scaleX(.9)}to{opacity:0;-webkit-transform:translate3d(2000px,0,0) scaleX(2);transform:translate3d(2000px,0,0) scaleX(2)}}@keyframes bounceOutRight{20%{opacity:1;-webkit-transform:translate3d(-20px,0,0) scaleX(.9);transform:translate3d(-20px,0,0) scaleX(.9)}to{opacity:0;-webkit-transform:translate3d(2000px,0,0) scaleX(2);transform:translate3d(2000px,0,0) scaleX(2)}}.animate__bounceOutRight{-webkit-animation-name:bounceOutRight;animation-name:bounceOutRight}@-webkit-keyframes bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0) scaleY(.985);transform:translate3d(0,-10px,0) scaleY(.985)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0) scaleY(.9);transform:translate3d(0,20px,0) scaleY(.9)}to{opacity:0;-webkit-transform:translate3d(0,-2000px,0) scaleY(3);transform:translate3d(0,-2000px,0) scaleY(3)}}@keyframes bounceOutUp{20%{-webkit-transform:translate3d(0,-10px,0) scaleY(.985);transform:translate3d(0,-10px,0) scaleY(.985)}40%,45%{opacity:1;-webkit-transform:translate3d(0,20px,0) scaleY(.9);transform:translate3d(0,20px,0) scaleY(.9)}to{opacity:0;-webkit-transform:translate3d(0,-2000px,0) scaleY(3);transform:translate3d(0,-2000px,0) scaleY(3)}}.animate__bounceOutUp{-webkit-animation-name:bounceOutUp;animation-name:bounceOutUp}@-webkit-keyframes fadeIn{0%{opacity:0}to{opacity:1}}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}.animate__fadeIn{-webkit-animation-name:fadeIn;animation-name:fadeIn}@-webkit-keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes fadeInDown{0%{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}.animate__fadeInDown{-webkit-animation-name:fadeInDown;animation-name:fadeInDown}@-webkit-keyframes fadeInDownBig{0%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes fadeInDownBig{0%{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}.animate__fadeInDownBig{-webkit-animation-name:fadeInDownBig;animation-name:fadeInDownBig}@-webkit-keyframes fadeInLeft{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes fadeInLeft{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}.animate__fadeInLeft{-webkit-animation-name:fadeInLeft;animation-name:fadeInLeft}@-webkit-keyframes fadeInLeftBig{0%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes fadeInLeftBig{0%{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}.animate__fadeInLeftBig{-webkit-animation-name:fadeInLeftBig;animation-name:fadeInLeftBig}@-webkit-keyframes fadeInRight{0%{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes fadeInRight{0%{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}.animate__fadeInRight{-webkit-animation-name:fadeInRight;animation-name:fadeInRight}@-webkit-keyframes fadeInRightBig{0%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes fadeInRightBig{0%{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}.animate__fadeInRightBig{-webkit-animation-name:fadeInRightBig;animation-name:fadeInRightBig}@-webkit-keyframes fadeInUp{0%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes fadeInUp{0%{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}.animate__fadeInUp{-webkit-animation-name:fadeInUp;animation-name:fadeInUp}@-webkit-keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes fadeInUpBig{0%{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}.animate__fadeInUpBig{-webkit-animation-name:fadeInUpBig;animation-name:fadeInUpBig}@-webkit-keyframes fadeInTopLeft{0%{opacity:0;-webkit-transform:translate3d(-100%,-100%,0);transform:translate3d(-100%,-100%,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes fadeInTopLeft{0%{opacity:0;-webkit-transform:translate3d(-100%,-100%,0);transform:translate3d(-100%,-100%,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}.animate__fadeInTopLeft{-webkit-animation-name:fadeInTopLeft;animation-name:fadeInTopLeft}@-webkit-keyframes fadeInTopRight{0%{opacity:0;-webkit-transform:translate3d(100%,-100%,0);transform:translate3d(100%,-100%,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes fadeInTopRight{0%{opacity:0;-webkit-transform:translate3d(100%,-100%,0);transform:translate3d(100%,-100%,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}.animate__fadeInTopRight{-webkit-animation-name:fadeInTopRight;animation-name:fadeInTopRight}@-webkit-keyframes fadeInBottomLeft{0%{opacity:0;-webkit-transform:translate3d(-100%,100%,0);transform:translate3d(-100%,100%,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes fadeInBottomLeft{0%{opacity:0;-webkit-transform:translate3d(-100%,100%,0);transform:translate3d(-100%,100%,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}.animate__fadeInBottomLeft{-webkit-animation-name:fadeInBottomLeft;animation-name:fadeInBottomLeft}@-webkit-keyframes fadeInBottomRight{0%{opacity:0;-webkit-transform:translate3d(100%,100%,0);transform:translate3d(100%,100%,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes fadeInBottomRight{0%{opacity:0;-webkit-transform:translate3d(100%,100%,0);transform:translate3d(100%,100%,0)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}.animate__fadeInBottomRight{-webkit-animation-name:fadeInBottomRight;animation-name:fadeInBottomRight}@-webkit-keyframes fadeOut{0%{opacity:1}to{opacity:0}}@keyframes fadeOut{0%{opacity:1}to{opacity:0}}.animate__fadeOut{-webkit-animation-name:fadeOut;animation-name:fadeOut}@-webkit-keyframes fadeOutDown{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}@keyframes fadeOutDown{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}.animate__fadeOutDown{-webkit-animation-name:fadeOutDown;animation-name:fadeOutDown}@-webkit-keyframes fadeOutDownBig{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}@keyframes fadeOutDownBig{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,2000px,0);transform:translate3d(0,2000px,0)}}.animate__fadeOutDownBig{-webkit-animation-name:fadeOutDownBig;animation-name:fadeOutDownBig}@-webkit-keyframes fadeOutLeft{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}@keyframes fadeOutLeft{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}.animate__fadeOutLeft{-webkit-animation-name:fadeOutLeft;animation-name:fadeOutLeft}@-webkit-keyframes fadeOutLeftBig{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}@keyframes fadeOutLeftBig{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(-2000px,0,0);transform:translate3d(-2000px,0,0)}}.animate__fadeOutLeftBig{-webkit-animation-name:fadeOutLeftBig;animation-name:fadeOutLeftBig}@-webkit-keyframes fadeOutRight{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}@keyframes fadeOutRight{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}.animate__fadeOutRight{-webkit-animation-name:fadeOutRight;animation-name:fadeOutRight}@-webkit-keyframes fadeOutRightBig{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}@keyframes fadeOutRightBig{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(2000px,0,0);transform:translate3d(2000px,0,0)}}.animate__fadeOutRightBig{-webkit-animation-name:fadeOutRightBig;animation-name:fadeOutRightBig}@-webkit-keyframes fadeOutUp{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}@keyframes fadeOutUp{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}.animate__fadeOutUp{-webkit-animation-name:fadeOutUp;animation-name:fadeOutUp}@-webkit-keyframes fadeOutUpBig{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}@keyframes fadeOutUpBig{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(0,-2000px,0);transform:translate3d(0,-2000px,0)}}.animate__fadeOutUpBig{-webkit-animation-name:fadeOutUpBig;animation-name:fadeOutUpBig}@-webkit-keyframes fadeOutTopLeft{0%{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}to{opacity:0;-webkit-transform:translate3d(-100%,-100%,0);transform:translate3d(-100%,-100%,0)}}@keyframes fadeOutTopLeft{0%{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}to{opacity:0;-webkit-transform:translate3d(-100%,-100%,0);transform:translate3d(-100%,-100%,0)}}.animate__fadeOutTopLeft{-webkit-animation-name:fadeOutTopLeft;animation-name:fadeOutTopLeft}@-webkit-keyframes fadeOutTopRight{0%{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}to{opacity:0;-webkit-transform:translate3d(100%,-100%,0);transform:translate3d(100%,-100%,0)}}@keyframes fadeOutTopRight{0%{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}to{opacity:0;-webkit-transform:translate3d(100%,-100%,0);transform:translate3d(100%,-100%,0)}}.animate__fadeOutTopRight{-webkit-animation-name:fadeOutTopRight;animation-name:fadeOutTopRight}@-webkit-keyframes fadeOutBottomRight{0%{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}to{opacity:0;-webkit-transform:translate3d(100%,100%,0);transform:translate3d(100%,100%,0)}}@keyframes fadeOutBottomRight{0%{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}to{opacity:0;-webkit-transform:translate3d(100%,100%,0);transform:translate3d(100%,100%,0)}}.animate__fadeOutBottomRight{-webkit-animation-name:fadeOutBottomRight;animation-name:fadeOutBottomRight}@-webkit-keyframes fadeOutBottomLeft{0%{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}to{opacity:0;-webkit-transform:translate3d(-100%,100%,0);transform:translate3d(-100%,100%,0)}}@keyframes fadeOutBottomLeft{0%{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}to{opacity:0;-webkit-transform:translate3d(-100%,100%,0);transform:translate3d(-100%,100%,0)}}.animate__fadeOutBottomLeft{-webkit-animation-name:fadeOutBottomLeft;animation-name:fadeOutBottomLeft}@-webkit-keyframes flip{0%{-webkit-transform:perspective(400px) scaleX(1) translateZ(0) rotateY(-1turn);transform:perspective(400px) scaleX(1) translateZ(0) rotateY(-1turn);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}40%{-webkit-transform:perspective(400px) scaleX(1) translateZ(150px) rotateY(-190deg);transform:perspective(400px) scaleX(1) translateZ(150px) rotateY(-190deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}50%{-webkit-transform:perspective(400px) scaleX(1) translateZ(150px) rotateY(-170deg);transform:perspective(400px) scaleX(1) translateZ(150px) rotateY(-170deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}80%{-webkit-transform:perspective(400px) scale3d(.95,.95,.95) translateZ(0) rotateY(0deg);transform:perspective(400px) scale3d(.95,.95,.95) translateZ(0) rotateY(0deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}to{-webkit-transform:perspective(400px) scaleX(1) translateZ(0) rotateY(0deg);transform:perspective(400px) scaleX(1) translateZ(0) rotateY(0deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}}@keyframes flip{0%{-webkit-transform:perspective(400px) scaleX(1) translateZ(0) rotateY(-1turn);transform:perspective(400px) scaleX(1) translateZ(0) rotateY(-1turn);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}40%{-webkit-transform:perspective(400px) scaleX(1) translateZ(150px) rotateY(-190deg);transform:perspective(400px) scaleX(1) translateZ(150px) rotateY(-190deg);-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}50%{-webkit-transform:perspective(400px) scaleX(1) translateZ(150px) rotateY(-170deg);transform:perspective(400px) scaleX(1) translateZ(150px) rotateY(-170deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}80%{-webkit-transform:perspective(400px) scale3d(.95,.95,.95) translateZ(0) rotateY(0deg);transform:perspective(400px) scale3d(.95,.95,.95) translateZ(0) rotateY(0deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}to{-webkit-transform:perspective(400px) scaleX(1) translateZ(0) rotateY(0deg);transform:perspective(400px) scaleX(1) translateZ(0) rotateY(0deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}}.animate__animated.animate__flip{-webkit-backface-visibility:visible;backface-visibility:visible;-webkit-animation-name:flip;animation-name:flip}@-webkit-keyframes flipInX{0%{-webkit-transform:perspective(400px) rotateX(90deg);transform:perspective(400px) rotateX(90deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotateX(-20deg);transform:perspective(400px) rotateX(-20deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotateX(10deg);transform:perspective(400px) rotateX(10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotateX(-5deg);transform:perspective(400px) rotateX(-5deg)}to{-webkit-transform:perspective(400px);transform:perspective(400px)}}@keyframes flipInX{0%{-webkit-transform:perspective(400px) rotateX(90deg);transform:perspective(400px) rotateX(90deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotateX(-20deg);transform:perspective(400px) rotateX(-20deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotateX(10deg);transform:perspective(400px) rotateX(10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotateX(-5deg);transform:perspective(400px) rotateX(-5deg)}to{-webkit-transform:perspective(400px);transform:perspective(400px)}}.animate__flipInX{-webkit-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipInX;animation-name:flipInX}@-webkit-keyframes flipInY{0%{-webkit-transform:perspective(400px) rotateY(90deg);transform:perspective(400px) rotateY(90deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotateY(-20deg);transform:perspective(400px) rotateY(-20deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotateY(10deg);transform:perspective(400px) rotateY(10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotateY(-5deg);transform:perspective(400px) rotateY(-5deg)}to{-webkit-transform:perspective(400px);transform:perspective(400px)}}@keyframes flipInY{0%{-webkit-transform:perspective(400px) rotateY(90deg);transform:perspective(400px) rotateY(90deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in;opacity:0}40%{-webkit-transform:perspective(400px) rotateY(-20deg);transform:perspective(400px) rotateY(-20deg);-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}60%{-webkit-transform:perspective(400px) rotateY(10deg);transform:perspective(400px) rotateY(10deg);opacity:1}80%{-webkit-transform:perspective(400px) rotateY(-5deg);transform:perspective(400px) rotateY(-5deg)}to{-webkit-transform:perspective(400px);transform:perspective(400px)}}.animate__flipInY{-webkit-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipInY;animation-name:flipInY}@-webkit-keyframes flipOutX{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotateX(-20deg);transform:perspective(400px) rotateX(-20deg);opacity:1}to{-webkit-transform:perspective(400px) rotateX(90deg);transform:perspective(400px) rotateX(90deg);opacity:0}}@keyframes flipOutX{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotateX(-20deg);transform:perspective(400px) rotateX(-20deg);opacity:1}to{-webkit-transform:perspective(400px) rotateX(90deg);transform:perspective(400px) rotateX(90deg);opacity:0}}.animate__flipOutX{-webkit-animation-duration:.75s;animation-duration:.75s;-webkit-animation-duration:calc(var(--animate-duration)*0.75);animation-duration:calc(var(--animate-duration)*0.75);-webkit-animation-name:flipOutX;animation-name:flipOutX;-webkit-backface-visibility:visible!important;backface-visibility:visible!important}@-webkit-keyframes flipOutY{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotateY(-15deg);transform:perspective(400px) rotateY(-15deg);opacity:1}to{-webkit-transform:perspective(400px) rotateY(90deg);transform:perspective(400px) rotateY(90deg);opacity:0}}@keyframes flipOutY{0%{-webkit-transform:perspective(400px);transform:perspective(400px)}30%{-webkit-transform:perspective(400px) rotateY(-15deg);transform:perspective(400px) rotateY(-15deg);opacity:1}to{-webkit-transform:perspective(400px) rotateY(90deg);transform:perspective(400px) rotateY(90deg);opacity:0}}.animate__flipOutY{-webkit-animation-duration:.75s;animation-duration:.75s;-webkit-animation-duration:calc(var(--animate-duration)*0.75);animation-duration:calc(var(--animate-duration)*0.75);-webkit-backface-visibility:visible!important;backface-visibility:visible!important;-webkit-animation-name:flipOutY;animation-name:flipOutY}@-webkit-keyframes lightSpeedInRight{0%{-webkit-transform:translate3d(100%,0,0) skewX(-30deg);transform:translate3d(100%,0,0) skewX(-30deg);opacity:0}60%{-webkit-transform:skewX(20deg);transform:skewX(20deg);opacity:1}80%{-webkit-transform:skewX(-5deg);transform:skewX(-5deg)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes lightSpeedInRight{0%{-webkit-transform:translate3d(100%,0,0) skewX(-30deg);transform:translate3d(100%,0,0) skewX(-30deg);opacity:0}60%{-webkit-transform:skewX(20deg);transform:skewX(20deg);opacity:1}80%{-webkit-transform:skewX(-5deg);transform:skewX(-5deg)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}.animate__lightSpeedInRight{-webkit-animation-name:lightSpeedInRight;animation-name:lightSpeedInRight;-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}@-webkit-keyframes lightSpeedInLeft{0%{-webkit-transform:translate3d(-100%,0,0) skewX(30deg);transform:translate3d(-100%,0,0) skewX(30deg);opacity:0}60%{-webkit-transform:skewX(-20deg);transform:skewX(-20deg);opacity:1}80%{-webkit-transform:skewX(5deg);transform:skewX(5deg)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes lightSpeedInLeft{0%{-webkit-transform:translate3d(-100%,0,0) skewX(30deg);transform:translate3d(-100%,0,0) skewX(30deg);opacity:0}60%{-webkit-transform:skewX(-20deg);transform:skewX(-20deg);opacity:1}80%{-webkit-transform:skewX(5deg);transform:skewX(5deg)}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}.animate__lightSpeedInLeft{-webkit-animation-name:lightSpeedInLeft;animation-name:lightSpeedInLeft;-webkit-animation-timing-function:ease-out;animation-timing-function:ease-out}@-webkit-keyframes lightSpeedOutRight{0%{opacity:1}to{-webkit-transform:translate3d(100%,0,0) skewX(30deg);transform:translate3d(100%,0,0) skewX(30deg);opacity:0}}@keyframes lightSpeedOutRight{0%{opacity:1}to{-webkit-transform:translate3d(100%,0,0) skewX(30deg);transform:translate3d(100%,0,0) skewX(30deg);opacity:0}}.animate__lightSpeedOutRight{-webkit-animation-name:lightSpeedOutRight;animation-name:lightSpeedOutRight;-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}@-webkit-keyframes lightSpeedOutLeft{0%{opacity:1}to{-webkit-transform:translate3d(-100%,0,0) skewX(-30deg);transform:translate3d(-100%,0,0) skewX(-30deg);opacity:0}}@keyframes lightSpeedOutLeft{0%{opacity:1}to{-webkit-transform:translate3d(-100%,0,0) skewX(-30deg);transform:translate3d(-100%,0,0) skewX(-30deg);opacity:0}}.animate__lightSpeedOutLeft{-webkit-animation-name:lightSpeedOutLeft;animation-name:lightSpeedOutLeft;-webkit-animation-timing-function:ease-in;animation-timing-function:ease-in}@-webkit-keyframes rotateIn{0%{-webkit-transform:rotate(-200deg);transform:rotate(-200deg);opacity:0}to{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}}@keyframes rotateIn{0%{-webkit-transform:rotate(-200deg);transform:rotate(-200deg);opacity:0}to{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}}.animate__rotateIn{-webkit-animation-name:rotateIn;animation-name:rotateIn;-webkit-transform-origin:center;transform-origin:center}@-webkit-keyframes rotateInDownLeft{0%{-webkit-transform:rotate(-45deg);transform:rotate(-45deg);opacity:0}to{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}}@keyframes rotateInDownLeft{0%{-webkit-transform:rotate(-45deg);transform:rotate(-45deg);opacity:0}to{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}}.animate__rotateInDownLeft{-webkit-animation-name:rotateInDownLeft;animation-name:rotateInDownLeft;-webkit-transform-origin:left bottom;transform-origin:left bottom}@-webkit-keyframes rotateInDownRight{0%{-webkit-transform:rotate(45deg);transform:rotate(45deg);opacity:0}to{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}}@keyframes rotateInDownRight{0%{-webkit-transform:rotate(45deg);transform:rotate(45deg);opacity:0}to{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}}.animate__rotateInDownRight{-webkit-animation-name:rotateInDownRight;animation-name:rotateInDownRight;-webkit-transform-origin:right bottom;transform-origin:right bottom}@-webkit-keyframes rotateInUpLeft{0%{-webkit-transform:rotate(45deg);transform:rotate(45deg);opacity:0}to{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}}@keyframes rotateInUpLeft{0%{-webkit-transform:rotate(45deg);transform:rotate(45deg);opacity:0}to{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}}.animate__rotateInUpLeft{-webkit-animation-name:rotateInUpLeft;animation-name:rotateInUpLeft;-webkit-transform-origin:left bottom;transform-origin:left bottom}@-webkit-keyframes rotateInUpRight{0%{-webkit-transform:rotate(-90deg);transform:rotate(-90deg);opacity:0}to{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}}@keyframes rotateInUpRight{0%{-webkit-transform:rotate(-90deg);transform:rotate(-90deg);opacity:0}to{-webkit-transform:translateZ(0);transform:translateZ(0);opacity:1}}.animate__rotateInUpRight{-webkit-animation-name:rotateInUpRight;animation-name:rotateInUpRight;-webkit-transform-origin:right bottom;transform-origin:right bottom}@-webkit-keyframes rotateOut{0%{opacity:1}to{-webkit-transform:rotate(200deg);transform:rotate(200deg);opacity:0}}@keyframes rotateOut{0%{opacity:1}to{-webkit-transform:rotate(200deg);transform:rotate(200deg);opacity:0}}.animate__rotateOut{-webkit-animation-name:rotateOut;animation-name:rotateOut;-webkit-transform-origin:center;transform-origin:center}@-webkit-keyframes rotateOutDownLeft{0%{opacity:1}to{-webkit-transform:rotate(45deg);transform:rotate(45deg);opacity:0}}@keyframes rotateOutDownLeft{0%{opacity:1}to{-webkit-transform:rotate(45deg);transform:rotate(45deg);opacity:0}}.animate__rotateOutDownLeft{-webkit-animation-name:rotateOutDownLeft;animation-name:rotateOutDownLeft;-webkit-transform-origin:left bottom;transform-origin:left bottom}@-webkit-keyframes rotateOutDownRight{0%{opacity:1}to{-webkit-transform:rotate(-45deg);transform:rotate(-45deg);opacity:0}}@keyframes rotateOutDownRight{0%{opacity:1}to{-webkit-transform:rotate(-45deg);transform:rotate(-45deg);opacity:0}}.animate__rotateOutDownRight{-webkit-animation-name:rotateOutDownRight;animation-name:rotateOutDownRight;-webkit-transform-origin:right bottom;transform-origin:right bottom}@-webkit-keyframes rotateOutUpLeft{0%{opacity:1}to{-webkit-transform:rotate(-45deg);transform:rotate(-45deg);opacity:0}}@keyframes rotateOutUpLeft{0%{opacity:1}to{-webkit-transform:rotate(-45deg);transform:rotate(-45deg);opacity:0}}.animate__rotateOutUpLeft{-webkit-animation-name:rotateOutUpLeft;animation-name:rotateOutUpLeft;-webkit-transform-origin:left bottom;transform-origin:left bottom}@-webkit-keyframes rotateOutUpRight{0%{opacity:1}to{-webkit-transform:rotate(90deg);transform:rotate(90deg);opacity:0}}@keyframes rotateOutUpRight{0%{opacity:1}to{-webkit-transform:rotate(90deg);transform:rotate(90deg);opacity:0}}.animate__rotateOutUpRight{-webkit-animation-name:rotateOutUpRight;animation-name:rotateOutUpRight;-webkit-transform-origin:right bottom;transform-origin:right bottom}@-webkit-keyframes hinge{0%{-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}20%,60%{-webkit-transform:rotate(80deg);transform:rotate(80deg);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}40%,80%{-webkit-transform:rotate(60deg);transform:rotate(60deg);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;opacity:1}to{-webkit-transform:translate3d(0,700px,0);transform:translate3d(0,700px,0);opacity:0}}@keyframes hinge{0%{-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}20%,60%{-webkit-transform:rotate(80deg);transform:rotate(80deg);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out}40%,80%{-webkit-transform:rotate(60deg);transform:rotate(60deg);-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;opacity:1}to{-webkit-transform:translate3d(0,700px,0);transform:translate3d(0,700px,0);opacity:0}}.animate__hinge{-webkit-animation-duration:2s;animation-duration:2s;-webkit-animation-duration:calc(var(--animate-duration)*2);animation-duration:calc(var(--animate-duration)*2);-webkit-animation-name:hinge;animation-name:hinge;-webkit-transform-origin:top left;transform-origin:top left}@-webkit-keyframes jackInTheBox{0%{opacity:0;-webkit-transform:scale(.1) rotate(30deg);transform:scale(.1) rotate(30deg);-webkit-transform-origin:center bottom;transform-origin:center bottom}50%{-webkit-transform:rotate(-10deg);transform:rotate(-10deg)}70%{-webkit-transform:rotate(3deg);transform:rotate(3deg)}to{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}@keyframes jackInTheBox{0%{opacity:0;-webkit-transform:scale(.1) rotate(30deg);transform:scale(.1) rotate(30deg);-webkit-transform-origin:center bottom;transform-origin:center bottom}50%{-webkit-transform:rotate(-10deg);transform:rotate(-10deg)}70%{-webkit-transform:rotate(3deg);transform:rotate(3deg)}to{opacity:1;-webkit-transform:scale(1);transform:scale(1)}}.animate__jackInTheBox{-webkit-animation-name:jackInTheBox;animation-name:jackInTheBox}@-webkit-keyframes rollIn{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0) rotate(-120deg);transform:translate3d(-100%,0,0) rotate(-120deg)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes rollIn{0%{opacity:0;-webkit-transform:translate3d(-100%,0,0) rotate(-120deg);transform:translate3d(-100%,0,0) rotate(-120deg)}to{opacity:1;-webkit-transform:translateZ(0);transform:translateZ(0)}}.animate__rollIn{-webkit-animation-name:rollIn;animation-name:rollIn}@-webkit-keyframes rollOut{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(100%,0,0) rotate(120deg);transform:translate3d(100%,0,0) rotate(120deg)}}@keyframes rollOut{0%{opacity:1}to{opacity:0;-webkit-transform:translate3d(100%,0,0) rotate(120deg);transform:translate3d(100%,0,0) rotate(120deg)}}.animate__rollOut{-webkit-animation-name:rollOut;animation-name:rollOut}@-webkit-keyframes zoomIn{0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%{opacity:1}}@keyframes zoomIn{0%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}50%{opacity:1}}.animate__zoomIn{-webkit-animation-name:zoomIn;animation-name:zoomIn}@-webkit-keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomInDown{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-1000px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.animate__zoomInDown{-webkit-animation-name:zoomInDown;animation-name:zoomInDown}@-webkit-keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(10px,0,0);transform:scale3d(.475,.475,.475) translate3d(10px,0,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomInLeft{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(-1000px,0,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(10px,0,0);transform:scale3d(.475,.475,.475) translate3d(10px,0,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.animate__zoomInLeft{-webkit-animation-name:zoomInLeft;animation-name:zoomInLeft}@-webkit-keyframes zoomInRight{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomInRight{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);transform:scale3d(.1,.1,.1) translate3d(1000px,0,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);transform:scale3d(.475,.475,.475) translate3d(-10px,0,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.animate__zoomInRight{-webkit-animation-name:zoomInRight;animation-name:zoomInRight}@-webkit-keyframes zoomInUp{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomInUp{0%{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);transform:scale3d(.1,.1,.1) translate3d(0,1000px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}60%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.animate__zoomInUp{-webkit-animation-name:zoomInUp;animation-name:zoomInUp}@-webkit-keyframes zoomOut{0%{opacity:1}50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}to{opacity:0}}@keyframes zoomOut{0%{opacity:1}50%{opacity:0;-webkit-transform:scale3d(.3,.3,.3);transform:scale3d(.3,.3,.3)}to{opacity:0}}.animate__zoomOut{-webkit-animation-name:zoomOut;animation-name:zoomOut}@-webkit-keyframes zoomOutDown{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}to{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomOutDown{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);transform:scale3d(.475,.475,.475) translate3d(0,-60px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}to{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,2000px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.animate__zoomOutDown{-webkit-animation-name:zoomOutDown;animation-name:zoomOutDown;-webkit-transform-origin:center bottom;transform-origin:center bottom}@-webkit-keyframes zoomOutLeft{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(42px,0,0);transform:scale3d(.475,.475,.475) translate3d(42px,0,0)}to{opacity:0;-webkit-transform:scale(.1) translate3d(-2000px,0,0);transform:scale(.1) translate3d(-2000px,0,0)}}@keyframes zoomOutLeft{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(42px,0,0);transform:scale3d(.475,.475,.475) translate3d(42px,0,0)}to{opacity:0;-webkit-transform:scale(.1) translate3d(-2000px,0,0);transform:scale(.1) translate3d(-2000px,0,0)}}.animate__zoomOutLeft{-webkit-animation-name:zoomOutLeft;animation-name:zoomOutLeft;-webkit-transform-origin:left center;transform-origin:left center}@-webkit-keyframes zoomOutRight{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-42px,0,0);transform:scale3d(.475,.475,.475) translate3d(-42px,0,0)}to{opacity:0;-webkit-transform:scale(.1) translate3d(2000px,0,0);transform:scale(.1) translate3d(2000px,0,0)}}@keyframes zoomOutRight{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(-42px,0,0);transform:scale3d(.475,.475,.475) translate3d(-42px,0,0)}to{opacity:0;-webkit-transform:scale(.1) translate3d(2000px,0,0);transform:scale(.1) translate3d(2000px,0,0)}}.animate__zoomOutRight{-webkit-animation-name:zoomOutRight;animation-name:zoomOutRight;-webkit-transform-origin:right center;transform-origin:right center}@-webkit-keyframes zoomOutUp{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}to{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}@keyframes zoomOutUp{40%{opacity:1;-webkit-transform:scale3d(.475,.475,.475) translate3d(0,60px,0);transform:scale3d(.475,.475,.475) translate3d(0,60px,0);-webkit-animation-timing-function:cubic-bezier(.55,.055,.675,.19);animation-timing-function:cubic-bezier(.55,.055,.675,.19)}to{opacity:0;-webkit-transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);transform:scale3d(.1,.1,.1) translate3d(0,-2000px,0);-webkit-animation-timing-function:cubic-bezier(.175,.885,.32,1);animation-timing-function:cubic-bezier(.175,.885,.32,1)}}.animate__zoomOutUp{-webkit-animation-name:zoomOutUp;animation-name:zoomOutUp;-webkit-transform-origin:center bottom;transform-origin:center bottom}@-webkit-keyframes slideInDown{0%{-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes slideInDown{0%{-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}.animate__slideInDown{-webkit-animation-name:slideInDown;animation-name:slideInDown}@-webkit-keyframes slideInLeft{0%{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes slideInLeft{0%{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}.animate__slideInLeft{-webkit-animation-name:slideInLeft;animation-name:slideInLeft}@-webkit-keyframes slideInRight{0%{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes slideInRight{0%{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}.animate__slideInRight{-webkit-animation-name:slideInRight;animation-name:slideInRight}@-webkit-keyframes slideInUp{0%{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}@keyframes slideInUp{0%{-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0);visibility:visible}to{-webkit-transform:translateZ(0);transform:translateZ(0)}}.animate__slideInUp{-webkit-animation-name:slideInUp;animation-name:slideInUp}@-webkit-keyframes slideOutDown{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}@keyframes slideOutDown{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(0,100%,0);transform:translate3d(0,100%,0)}}.animate__slideOutDown{-webkit-animation-name:slideOutDown;animation-name:slideOutDown}@-webkit-keyframes slideOutLeft{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}@keyframes slideOutLeft{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}.animate__slideOutLeft{-webkit-animation-name:slideOutLeft;animation-name:slideOutLeft}@-webkit-keyframes slideOutRight{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}@keyframes slideOutRight{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}.animate__slideOutRight{-webkit-animation-name:slideOutRight;animation-name:slideOutRight}@-webkit-keyframes slideOutUp{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}@keyframes slideOutUp{0%{-webkit-transform:translateZ(0);transform:translateZ(0)}to{visibility:hidden;-webkit-transform:translate3d(0,-100%,0);transform:translate3d(0,-100%,0)}}.animate__slideOutUp{-webkit-animation-name:slideOutUp;animation-name:slideOutUp} diff --git a/quartz-manager-frontend/src/app/components/logs-panel/logs-panel.component.html b/quartz-manager-frontend/src/app/components/logs-panel/logs-panel.component.html index 8adae09..71e6a56 100644 --- a/quartz-manager-frontend/src/app/components/logs-panel/logs-panel.component.html +++ b/quartz-manager-frontend/src/app/components/logs-panel/logs-panel.component.html @@ -1,29 +1,29 @@ - + - JOB LOGS + JOB LOGS -
-
-
- [{{log.time|date:'medium'}}] -
-
- - - -
-
- - {{log.threadName}} - -
-
- {{log.msg}} -
+
+
+
+ [{{log.time|date:'medium'}}] +
+
+ + + +
+
+ + {{log.threadName}} + +
+
+ {{log.msg}}
+
- \ No newline at end of file + diff --git a/quartz-manager-frontend/src/app/components/logs-panel/logs-panel.component.scss b/quartz-manager-frontend/src/app/components/logs-panel/logs-panel.component.scss index ce0212b..f0f6311 100644 --- a/quartz-manager-frontend/src/app/components/logs-panel/logs-panel.component.scss +++ b/quartz-manager-frontend/src/app/components/logs-panel/logs-panel.component.scss @@ -7,4 +7,8 @@ .yellow{ color: gold; -} \ No newline at end of file +} + +#logs{ + font-size: 0.9em; +} diff --git a/quartz-manager-frontend/src/app/components/logs-panel/logs-panel.component.ts b/quartz-manager-frontend/src/app/components/logs-panel/logs-panel.component.ts index 4d3d3c3..1d4f096 100644 --- a/quartz-manager-frontend/src/app/components/logs-panel/logs-panel.component.ts +++ b/quartz-manager-frontend/src/app/components/logs-panel/logs-panel.component.ts @@ -10,7 +10,7 @@ import { Observable } from 'rxjs'; }) export class LogsPanelComponent implements OnInit { - MAX_LOGS : number = 20; + MAX_LOGS : number = 30; logs : Array = new Array(); @@ -37,7 +37,7 @@ export class LogsPanelComponent implements OnInit { _showNewLog = (logRecord) => { if(this.logs.length > this.MAX_LOGS) this.logs.pop(); - + this.logs.unshift({ time : logRecord.date, type : logRecord.type, diff --git a/quartz-manager-frontend/src/app/components/progress-panel/progress-panel.component.html b/quartz-manager-frontend/src/app/components/progress-panel/progress-panel.component.html index d9976db..722c2f5 100644 --- a/quartz-manager-frontend/src/app/components/progress-panel/progress-panel.component.html +++ b/quartz-manager-frontend/src/app/components/progress-panel/progress-panel.component.html @@ -16,9 +16,9 @@ {{percentageStr}}
-
+
{{progress.timesTriggered}} -  / {{progress.repeatCount}} +  / {{progress.repeatCount}}
diff --git a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html index f68dbdd..b29c701 100644 --- a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html +++ b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html @@ -9,11 +9,11 @@
Trigger Name diff --git a/quartz-manager-frontend/src/app/views/manager/manager.component.html b/quartz-manager-frontend/src/app/views/manager/manager.component.html index ea70e3b..ed9164f 100644 --- a/quartz-manager-frontend/src/app/views/manager/manager.component.html +++ b/quartz-manager-frontend/src/app/views/manager/manager.component.html @@ -4,9 +4,9 @@
-
+
-
+
-
+
-
+
-
-
+
diff --git a/quartz-manager-frontend/src/styles.css b/quartz-manager-frontend/src/styles.css index e6babad..ee4c410 100644 --- a/quartz-manager-frontend/src/styles.css +++ b/quartz-manager-frontend/src/styles.css @@ -1,5 +1,6 @@ /* You can add global styles to this file, and also import other style files */ @import '~@angular/material/prebuilt-themes/deeppurple-amber.css'; +@import "animate.css"; html { display: flex; From bfba79448b0f13436db1fe8538c8c90b3e9193dd Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Sat, 24 Sep 2022 16:46:21 +0200 Subject: [PATCH 034/184] Update README.md --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index ca2d119..3af9196 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ [![Build Status](https://travis-ci.org/fabioformosa/quartz-manager.svg?branch=master)](https://travis-ci.org/fabioformosa/quartz-manager) [![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) -[![Gitter](https://badges.gitter.im/quartz-manager/community.svg)](https://gitter.im/quartz-manager/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) + # QUARTZ MANAGER Quartz Manager is a library you can import in your spring webapp to easily enable the [Quartz Scheduler](http://www.quartz-scheduler.org/) and to control it by REST APIs or by a UI Manager Panel (angular-based). @@ -205,6 +205,13 @@ Take a loot to the project [Quartz-Manager Demo](https://github.com/fabioformosa * add a secure layer to allow the API only to logged users * schedule a custom job (a dummy `hello world`) +## APPLICATION PROPERTIES + +| Property | Values | Description | +| :--- |:--- |:--- | +| quartz-manager.accounts.in-memory.enabled | boolean | Enable in memory users to login | +| quartz-manager.accounts.in-memory.users[0].name | string | | +| quartz-manager.accounts.in-memory.users[0].password | string | | ## ROADMAP Open the [Project Roadmap](https://github.com/fabioformosa/quartz-manager/projects) to take a look at the plan of Quartz Manager. From 7e21437dfccd4d3af8ab198707a81a416886db21 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Wed, 28 Sep 2022 23:10:19 +0200 Subject: [PATCH 035/184] #63 #66 clean up --- .../account-menu/account-menu.component.html | 3 +- .../logs-panel/logs-panel.component.ts | 2 +- .../app/guards/admin.guard.disabledspec.ts | 105 -------- .../src/app/model/schedulerConfig.model.ts | 13 - .../src/app/services/api.service.ts | 45 ++-- .../src/app/services/auth.service.ts | 35 +-- .../src/app/services/config.service.ts | 20 +- .../app/services/logs.websocket.service.ts | 14 +- .../services/progress.websocket.service.ts | 14 +- .../src/app/services/user.service.ts | 4 - .../src/app/services/websocket.service.ts | 224 +++++++++--------- .../change-password.component.html | 18 -- .../change-password.component.scss | 45 ---- .../change-password.component.spec.ts | 52 ---- .../change-password.component.ts | 64 ----- .../src/app/views/change-password/index.ts | 1 - .../src/app/views/login/login.component.ts | 33 +-- .../src/app/views/signup/index.ts | 1 - .../app/views/signup/signup.component.html | 56 ----- .../app/views/signup/signup.component.scss | 59 ----- .../app/views/signup/signup.component.spec.ts | 61 ----- .../src/app/views/signup/signup.component.ts | 104 -------- .../properties/QuartzModuleProperties.java | 2 +- .../aspects/WebSocketProgressNotifier.java | 9 - .../AbstractTriggerController.java | 3 - .../controllers/SchedulerController.java | 95 +------- .../controllers/SimpleTriggerController.java | 6 +- .../controllers/TriggerController.java | 58 +---- .../controllers/UserController.java | 43 ---- .../dto/SchedulerConfigParam.java | 20 -- .../scheduler/TriggerMonitor.java | 11 - .../scheduler/TriggerMonitorImpl.java | 19 -- .../services/LegacySchedulerService.java | 117 --------- .../services/SchedulerService.java | 11 + ...Service.java => SimpleTriggerService.java} | 4 +- .../SimpleTriggerControllerTest.java | 14 +- .../controllers/TriggerControllerTest.java | 61 +---- .../InvalidSchedulerConfigParamProvider.java | 19 -- ...est.java => SimpleTriggerServiceTest.java} | 4 +- .../configuration/WebSecurityConfigJWT.java | 4 +- .../impl/AuthenticationSuccessHandler.java | 62 +++-- .../helpers/impl/FormLoginConfig.java | 150 ++++++------ .../JwtAuthenticationSuccessHandlerImpl.java | 19 +- .../JwtUsernamePasswordFiterLoginConfig.java | 92 +++---- 44 files changed, 381 insertions(+), 1415 deletions(-) delete mode 100644 quartz-manager-frontend/src/app/guards/admin.guard.disabledspec.ts delete mode 100644 quartz-manager-frontend/src/app/model/schedulerConfig.model.ts delete mode 100644 quartz-manager-frontend/src/app/views/change-password/change-password.component.html delete mode 100644 quartz-manager-frontend/src/app/views/change-password/change-password.component.scss delete mode 100644 quartz-manager-frontend/src/app/views/change-password/change-password.component.spec.ts delete mode 100644 quartz-manager-frontend/src/app/views/change-password/change-password.component.ts delete mode 100644 quartz-manager-frontend/src/app/views/change-password/index.ts delete mode 100644 quartz-manager-frontend/src/app/views/signup/index.ts delete mode 100644 quartz-manager-frontend/src/app/views/signup/signup.component.html delete mode 100644 quartz-manager-frontend/src/app/views/signup/signup.component.scss delete mode 100644 quartz-manager-frontend/src/app/views/signup/signup.component.spec.ts delete mode 100644 quartz-manager-frontend/src/app/views/signup/signup.component.ts delete mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/dto/SchedulerConfigParam.java delete mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/scheduler/TriggerMonitor.java delete mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/scheduler/TriggerMonitorImpl.java delete mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/LegacySchedulerService.java rename quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/{SimpleTriggerSchedulerService.java => SimpleTriggerService.java} (91%) delete mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/InvalidSchedulerConfigParamProvider.java rename quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/services/{SimpleTriggerSchedulerServiceTest.java => SimpleTriggerServiceTest.java} (96%) diff --git a/quartz-manager-frontend/src/app/components/header/account-menu/account-menu.component.html b/quartz-manager-frontend/src/app/components/header/account-menu/account-menu.component.html index 8334975..1b235e1 100644 --- a/quartz-manager-frontend/src/app/components/header/account-menu/account-menu.component.html +++ b/quartz-manager-frontend/src/app/components/header/account-menu/account-menu.component.html @@ -1,2 +1 @@ - - + diff --git a/quartz-manager-frontend/src/app/components/logs-panel/logs-panel.component.ts b/quartz-manager-frontend/src/app/components/logs-panel/logs-panel.component.ts index 1d4f096..95dc4ee 100644 --- a/quartz-manager-frontend/src/app/components/logs-panel/logs-panel.component.ts +++ b/quartz-manager-frontend/src/app/components/logs-panel/logs-panel.component.ts @@ -20,7 +20,7 @@ export class LogsPanelComponent implements OnInit { ) { } ngOnInit() { - let obs = this.logsWebsocketService.getObservable() + const obs = this.logsWebsocketService.getObservable() obs.subscribe({ 'next' : this.onNewLogMsg, 'error' : (err) => {console.log(err)} diff --git a/quartz-manager-frontend/src/app/guards/admin.guard.disabledspec.ts b/quartz-manager-frontend/src/app/guards/admin.guard.disabledspec.ts deleted file mode 100644 index b196555..0000000 --- a/quartz-manager-frontend/src/app/guards/admin.guard.disabledspec.ts +++ /dev/null @@ -1,105 +0,0 @@ -import { TestBed, async, inject } from '@angular/core/testing'; -import { Router } from '@angular/router'; -import { NO_AUTH, UserService } from '../services'; -import { AdminGuard } from './admin.guard'; -import {jest} from '@jest/globals' - -export class RouterStub { - navigate(commands?: any[], extras?: any) {} -} - -const RouterSpy = jest.spyOn(RouterStub.prototype, 'navigate'); - -const MockUserServiceNoAuth = jest.fn(() => ({currentUser: NO_AUTH})); -const MockUserService = jest.fn(() => ({ - currentUser: { - authorities: ['ROLE_ADMIN'] - } -})); -const MockUserServiceForbidden = jest.fn(() => ({ - currentUser: { - authorities: ['ROLE_GUEST'] - } -})); - -// describe('AdminGuard NoAuth', () => { -// beforeEach(() => { -// TestBed.configureTestingModule({ -// providers: [ -// AdminGuard, -// { -// provide: Router, -// useClass: RouterStub -// }, -// { -// provide: UserService, -// useClass: MockUserServiceNoAuth -// } -// ] -// }); -// }); -// -// test.skip('should run', inject([AdminGuard], (guard: AdminGuard) => { -// expect(guard).toBeTruthy(); -// })); -// -// test.skip('returns true if user is NO_AUTH', inject([AdminGuard], (guard: AdminGuard) => { -// expect(guard.canActivate(null, null)).toBeTruthy(); -// })); -// -// }); - -// describe('AdminGuard activates the route', () => { -// beforeEach(() => { -// TestBed.configureTestingModule({ -// providers: [ -// AdminGuard, -// { -// provide: Router, -// useClass: RouterStub -// }, -// { -// provide: UserService, -// useClass: MockUserService -// } -// ] -// }); -// }); -// -// test.skip('should run', inject([AdminGuard], (guard: AdminGuard) => { -// expect(guard).toBeTruthy(); -// })); -// -// test.skip('returns true if user has admin role', inject([AdminGuard], (guard: AdminGuard) => { -// expect(guard.canActivate(null, null)).toBeTruthy(); -// })); -// -// }); - -// describe('AdminGuard redirects to 403', () => { -// beforeEach(() => { -// TestBed.configureTestingModule({ -// providers: [ -// AdminGuard, -// { -// provide: Router, -// useClass: RouterStub -// }, -// { -// provide: UserService, -// useClass: MockUserServiceForbidden -// } -// ] -// }); -// }); -// -// test.skip('should run', inject([AdminGuard], (guard: AdminGuard) => { -// expect(guard).toBeTruthy(); -// })); -// -// test.skip('returns false if user is not authorized', inject([AdminGuard], (guard: AdminGuard) => { -// expect(guard.canActivate(null, null)).toBeFalsy(); -// expect(RouterSpy).toHaveBeenCalledTimes(1); -// })); -// -// }); diff --git a/quartz-manager-frontend/src/app/model/schedulerConfig.model.ts b/quartz-manager-frontend/src/app/model/schedulerConfig.model.ts deleted file mode 100644 index 35601cb..0000000 --- a/quartz-manager-frontend/src/app/model/schedulerConfig.model.ts +++ /dev/null @@ -1,13 +0,0 @@ -export class SchedulerConfig { - - triggerPerDay = 0; - maxCount = 0; - timesTriggered = 0; - - constructor(triggerPerDay = 0, maxCount = 0, timesTriggered = 0) { - this.triggerPerDay = triggerPerDay; - this.maxCount = maxCount; - this.timesTriggered = timesTriggered; - } - - } diff --git a/quartz-manager-frontend/src/app/services/api.service.ts b/quartz-manager-frontend/src/app/services/api.service.ts index c7fde7c..742c550 100644 --- a/quartz-manager-frontend/src/app/services/api.service.ts +++ b/quartz-manager-frontend/src/app/services/api.service.ts @@ -1,9 +1,9 @@ -import { HttpClient, HttpHeaders, HttpResponse, HttpRequest, HttpEventType, HttpParams } from '@angular/common/http'; -import { Router} from '@angular/router'; -import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs'; -import { catchError, map, filter, tap } from 'rxjs/operators' -import { serialize } from '../shared/utilities/serialize'; +import {HttpClient, HttpHeaders, HttpResponse, HttpRequest, HttpEventType, HttpParams} from '@angular/common/http'; +import {Router} from '@angular/router'; +import {Injectable} from '@angular/core'; +import {Observable} from 'rxjs'; +import {catchError, map, filter, tap} from 'rxjs/operators' +import {serialize} from '../shared/utilities/serialize'; export enum RequestMethod { Get = 'GET', @@ -21,10 +21,11 @@ export class ApiService { private static extractTokenFromHttpResponse(res: HttpResponse): string { let authorization: string = null; let headers: HttpHeaders = res.headers; - if (headers && headers.has('Authorization')){ + if (headers && headers.has('Authorization')) { authorization = headers.get('Authorization'); - if(authorization.startsWith('Bearer ')) - authorization = authorization.substring(7); + if (authorization.startsWith('Bearer ')) { + authorization = authorization.substring(7); + } } return authorization; } @@ -36,7 +37,8 @@ export class ApiService { private jwtToken: string; - constructor( private http: HttpClient, private router: Router) { } + constructor(private http: HttpClient, private router: Router) { + } setToken(token: string) { this.jwtToken = token; @@ -50,8 +52,9 @@ export class ApiService { withCredentials: true }; - if (args) + if (args) { options['params'] = serialize(args); + } return this.http.get(path, options) .pipe(catchError(this.checkError.bind(this))); @@ -73,20 +76,21 @@ export class ApiService { const options = { headers: customHeaders || this.headers, withCredentials: true - } + } const req = new HttpRequest(method, path, body, options); return this.http.request(req) .pipe( - filter(response => response instanceof HttpResponse), - tap((resp: HttpResponse) => { - let jwtToken = ApiService.extractTokenFromHttpResponse(resp); - if(jwtToken) - this.setToken(jwtToken); - }), - map((response: HttpResponse) => response.body), - catchError(error => this.checkError(error)) + filter(response => response instanceof HttpResponse), + tap((resp: HttpResponse) => { + const jwtToken = ApiService.extractTokenFromHttpResponse(resp); + if (jwtToken) { + this.setToken(jwtToken); + } + }), + map((response: HttpResponse) => response.body), + catchError(error => this.checkError(error)) ) } @@ -100,6 +104,5 @@ export class ApiService { throw error; } - } diff --git a/quartz-manager-frontend/src/app/services/auth.service.ts b/quartz-manager-frontend/src/app/services/auth.service.ts index 07a643f..5520d6d 100644 --- a/quartz-manager-frontend/src/app/services/auth.service.ts +++ b/quartz-manager-frontend/src/app/services/auth.service.ts @@ -1,9 +1,9 @@ -import { Injectable } from '@angular/core'; -import { HttpHeaders, HttpResponse } from '@angular/common/http'; -import { ApiService } from './api.service'; -import { UserService } from './user.service'; -import { ConfigService } from './config.service'; -import { map } from 'rxjs/operators'; +import {Injectable} from '@angular/core'; +import {HttpHeaders, HttpResponse} from '@angular/common/http'; +import {ApiService} from './api.service'; +import {UserService} from './user.service'; +import {ConfigService} from './config.service'; +import {map} from 'rxjs/operators'; @Injectable() export class AuthService { @@ -12,7 +12,8 @@ export class AuthService { private apiService: ApiService, private userService: UserService, private config: ConfigService, - ) { } + ) { + } login(user) { const loginHeaders = new HttpHeaders({ @@ -21,24 +22,24 @@ export class AuthService { }); const body = `username=${user.username}&password=${user.password}`; return this.apiService.post(this.config.login_url, body, loginHeaders) - .pipe( - map(() => { - console.log("Login success"); - this.userService.getMyInfo().subscribe(); - }) - ); + .pipe( + map(() => { + console.log('Login success'); + this.userService.getMyInfo().subscribe(); + }) + ); } - signup(user){ + signup(user) { const signupHeaders = new HttpHeaders({ 'Accept': 'application/json', 'Content-Type': 'application/json' }); - return this.apiService.post(this.config.signup_url, JSON.stringify(user), signupHeaders).pipe(map(() =>{ - console.log("Sign up success"); + return this.apiService.post(this.config.signup_url, JSON.stringify(user), signupHeaders).pipe(map(() => { + console.log('Sign up success'); })); } - + logout() { return this.apiService.post(this.config.logout_url, {}) .pipe(map(() => { diff --git a/quartz-manager-frontend/src/app/services/config.service.ts b/quartz-manager-frontend/src/app/services/config.service.ts index 9a2d7e4..8236fc5 100644 --- a/quartz-manager-frontend/src/app/services/config.service.ts +++ b/quartz-manager-frontend/src/app/services/config.service.ts @@ -28,7 +28,7 @@ export function getBaseUrl() { @Injectable() export class ConfigService { - private _api_url = getBaseUrl() + '/quartz-manager/api' + private _api_url = getBaseUrl() + `${CONTEXT_PATH}/api` private _refresh_token_url = this._api_url + '/refresh'; @@ -36,22 +36,12 @@ export class ConfigService { private _logout_url = this._api_url + '/logout'; - private _change_password_url = this._api_url + '/changePassword'; - private _whoami_url = this._api_url + '/whoami'; private _user_url = this._api_url + '/user'; private _users_url = this._user_url + '/all'; - private _reset_credentials_url = this._user_url + '/reset-credentials'; - - private _signup_url = this._api_url + '/signup'; - - get reset_credentials_url(): string { - return this._reset_credentials_url; - } - get refresh_token_url(): string { return this._refresh_token_url; } @@ -72,12 +62,4 @@ export class ConfigService { return this._logout_url; } - get change_password_url(): string { - return this._change_password_url; - } - - get signup_url(): string { - return this._signup_url; - } - } diff --git a/quartz-manager-frontend/src/app/services/logs.websocket.service.ts b/quartz-manager-frontend/src/app/services/logs.websocket.service.ts index 989269c..97e77dd 100644 --- a/quartz-manager-frontend/src/app/services/logs.websocket.service.ts +++ b/quartz-manager-frontend/src/app/services/logs.websocket.service.ts @@ -1,12 +1,12 @@ -import { Injectable } from '@angular/core'; -import { WebsocketService, ApiService, getBaseUrl } from '.'; -import { SocketOption } from '../model/SocketOption.model'; +import {Injectable} from '@angular/core'; +import {WebsocketService, ApiService, getBaseUrl, CONTEXT_PATH} from '.'; +import {SocketOption} from '../model/SocketOption.model'; @Injectable() export class LogsWebsocketService extends WebsocketService { - constructor(private apiService: ApiService){ - super(new SocketOption( getBaseUrl() +'/quartz-manager/logs', '/topic/logs', apiService.getToken)) - } + constructor(private apiService: ApiService) { + super(new SocketOption(getBaseUrl() + `${CONTEXT_PATH}/logs`, '/topic/logs', apiService.getToken)) + } -} \ No newline at end of file +} diff --git a/quartz-manager-frontend/src/app/services/progress.websocket.service.ts b/quartz-manager-frontend/src/app/services/progress.websocket.service.ts index f81c627..7322e6f 100644 --- a/quartz-manager-frontend/src/app/services/progress.websocket.service.ts +++ b/quartz-manager-frontend/src/app/services/progress.websocket.service.ts @@ -1,12 +1,12 @@ -import { Injectable } from '@angular/core'; -import { WebsocketService, ApiService, getBaseUrl } from '.'; -import { SocketOption } from '../model/SocketOption.model'; +import {Injectable} from '@angular/core'; +import {WebsocketService, ApiService, getBaseUrl, CONTEXT_PATH} from '.'; +import {SocketOption} from '../model/SocketOption.model'; @Injectable() export class ProgressWebsocketService extends WebsocketService { - constructor(private apiService: ApiService){ - super(new SocketOption(getBaseUrl() + '/quartz-manager/progress', '/topic/progress', apiService.getToken)) - } + constructor(private apiService: ApiService) { + super(new SocketOption(getBaseUrl() + `${CONTEXT_PATH}/progress`, '/topic/progress', apiService.getToken)) + } -} \ No newline at end of file +} diff --git a/quartz-manager-frontend/src/app/services/user.service.ts b/quartz-manager-frontend/src/app/services/user.service.ts index f118f1c..6921820 100644 --- a/quartz-manager-frontend/src/app/services/user.service.ts +++ b/quartz-manager-frontend/src/app/services/user.service.ts @@ -40,10 +40,6 @@ export class UserService { }); } - resetCredentials() { - return this.apiService.get(this.config.reset_credentials_url); - } - getMyInfo() { return this.apiService.get(this.config.whoami_url).pipe(map(user => this.currentUser = user)); } diff --git a/quartz-manager-frontend/src/app/services/websocket.service.ts b/quartz-manager-frontend/src/app/services/websocket.service.ts index e1eda64..55450e4 100644 --- a/quartz-manager-frontend/src/app/services/websocket.service.ts +++ b/quartz-manager-frontend/src/app/services/websocket.service.ts @@ -1,125 +1,129 @@ -import { Observable } from 'rxjs'; -import { SocketEndpoint } from '../model/SocketEndpoint.model' +import {Observable} from 'rxjs'; +import {SocketEndpoint} from '../model/SocketEndpoint.model' import Stomp from 'stompjs'; import SockJS from 'sockjs-client'; -import { SocketOption } from '../model/SocketOption.model'; +import {SocketOption} from '../model/SocketOption.model'; export class WebsocketService { - _options : SocketOption; + _options: SocketOption; - _socket : SocketEndpoint = new SocketEndpoint(); - - observableStompConnection : Observable; - subscribers : Array = []; - subscriberIndex : number = 0; + _socket: SocketEndpoint = new SocketEndpoint(); - _messageIds : Array = []; + observableStompConnection: Observable; + subscribers: Array = []; + subscriberIndex: number = 0; - reconnectionPromise : any; + _messageIds: Array = []; - constructor(options : SocketOption){ - this._options = options - this.createObservableSocket(); - this.connect(); + reconnectionPromise: any; + + constructor(options: SocketOption) { + this._options = options + this.createObservableSocket(); + this.connect(); + } + + //TO BE OVERRIDEN + getOptions = () => { + return {} + } + + private createObservableSocket = () => { + this.observableStompConnection = new Observable((observer) => { + const subscriberIndex = this.subscriberIndex++; + this.addToSubscribers({index: subscriberIndex, observer}); + return () => this.removeFromSubscribers(subscriberIndex); + }); + } + + addToSubscribers = (subscriber) => { + this.subscribers.push(subscriber); + } + + removeFromSubscribers = (index) => { + if (index > this.subscribers.length) { + throw new Error(`Unexpected error removing subscriber from websocket, because index ${index} is greater than subscriber length ${this.subscribers.length}`); + } + this.subscribers.splice(index, 1); + } + + getObservable = () => { + return this.observableStompConnection; + }; + + getMessage = function (data) { + const out: any = {}; + out.type = 'SUCCESS'; + out.message = JSON.parse(data.body); + out.headers = {}; + out.headers.messageId = data.headers['message-id']; + + const messageIdIndex = this._messageIds.indexOf(out.headers.messageId); + if (messageIdIndex > -1) { + out.self = true; + this._messageIds = this._messageIds.splice(messageIdIndex, 1); + } + return out; + }; + + _socketListener = (frame) => { + console.log('Connected: ' + frame); + this._socket.stomp.subscribe( + this._options.topicName, + data => this.subscribers.forEach(subscriber => subscriber.observer.next(this.getMessage(data))) + ); + } + + _onSocketError = (errorMsg) => { + const out: any = {}; + out.type = 'ERROR'; + out.message = errorMsg; + this.subscribers.forEach(subscriber => subscriber.observer.error(out)); + this.scheduleReconnection(); + } + + scheduleReconnection = () => { + this.reconnectionPromise = setTimeout(() => { + console.log('Socket reconnecting... (if it fails, next attempt in ' + this._options.reconnectionTimeout + ' msec)'); + this.connect(); + }, this._options.reconnectionTimeout); + } + + reconnectNow = function () { + this._socket.stomp.disconnect(); + if (this.reconnectionPromise && this.reconnectionPromise.cancel) { + this.reconnectionPromise.cancel(); + } + this.connect(); + }; + + send = (message) => { + var id = Math.floor(Math.random() * 1000000); + this._socket.stomp.send(this._options.brokerName, { + priority: 9 + }, JSON.stringify({ + message: message, + id: id + })); + this._messageIds.push(id); + }; + + connect = () => { + const headers = {}; + + let socketUrl = this._options.socketUrl; + if (this._options.getAccessToken()) { + socketUrl += `?access_token=${this._options.getAccessToken()}` } - //TO BE OVERIDDEN - getOptions = () => {return {}} - - private createObservableSocket = () => { - this.observableStompConnection = new Observable((observer) => { - const subscriberIndex = this.subscriberIndex++; - this.addToSubscribers({ index: subscriberIndex, observer }); - return () => this.removeFromSubscribers(subscriberIndex); - }); - } + this._socket.client = new SockJS(socketUrl); + this._socket.stomp = Stomp.over(this._socket.client); + this._socket.stomp.connect(headers, this._socketListener, this._onSocketError); + this._socket.stomp.onclose = this.scheduleReconnection; + } - addToSubscribers = (subscriber) => { - this.subscribers.push(subscriber); - } - removeFromSubscribers = (index) => { - if(index > this.subscribers.length) - throw new Error(`Unexpected error removing subscriber from websocket, because index ${index} is greater than subscriber length ${this.subscribers.length}`); - this.subscribers.splice(index, 1); - } - - getObservable = () => { - return this.observableStompConnection; - }; - - getMessage = function(data) { - let out : any = {}; - out.type = 'SUCCESS'; - out.message = JSON.parse(data.body); - out.headers = {}; - out.headers.messageId = data.headers["message-id"]; - - let messageIdIndex = this._messageIds.indexOf(out.headers.messageId); - if ( messageIdIndex > -1) { - out.self = true; - this._messageIds = this._messageIds.splice(messageIdIndex, 1); - } - return out; - }; - - _socketListener = (frame) => { - console.log('Connected: ' + frame); - this._socket.stomp.subscribe( - this._options.topicName, - data => this.subscribers.forEach(subscriber => subscriber.observer.next(this.getMessage(data))) - ); - } - - _onSocketError = (errorMsg) => { - let out: any = {}; - out.type = 'ERROR'; - out.message = errorMsg; - this.subscribers.forEach(subscriber => subscriber.observer.error(out)); - this.scheduleReconnection(); - } - - scheduleReconnection = () => { - this.reconnectionPromise = setTimeout(() => { - console.log("Socket reconnecting... (if it fails, next attempt in " + this._options.reconnectionTimeout + " msec)"); - this.connect(); - }, this._options.reconnectionTimeout); - } - - reconnectNow = function(){ - this._socket.stomp.disconnect(); - if(this.reconnectionPromise && this.reconnectionPromise.cancel) - this.reconnectionPromise.cancel(); - this.connect(); - }; - - send = (message) => { - var id = Math.floor(Math.random() * 1000000); - this._socket.stomp.send(this._options.brokerName, { - priority: 9 - }, JSON.stringify({ - message: message, - id: id - })); - this._messageIds.push(id); - }; - - connect = () => { - const headers = {}; - - let socketUrl = this._options.socketUrl; - if(this._options.getAccessToken()) - socketUrl += `?access_token=${this._options.getAccessToken()}` - - this._socket.client = new SockJS(socketUrl); - this._socket.stomp = Stomp.over(this._socket.client); - this._socket.stomp.connect(headers, this._socketListener, this._onSocketError); - this._socket.stomp.onclose = this.scheduleReconnection; - } - - - -} \ No newline at end of file +} diff --git a/quartz-manager-frontend/src/app/views/change-password/change-password.component.html b/quartz-manager-frontend/src/app/views/change-password/change-password.component.html deleted file mode 100644 index 3ddd8c3..0000000 --- a/quartz-manager-frontend/src/app/views/change-password/change-password.component.html +++ /dev/null @@ -1,18 +0,0 @@ -
- - Change Your Password -

{{notification.msgBody}}

- - - - - - - - - - - - -
-
diff --git a/quartz-manager-frontend/src/app/views/change-password/change-password.component.scss b/quartz-manager-frontend/src/app/views/change-password/change-password.component.scss deleted file mode 100644 index 35b9ca9..0000000 --- a/quartz-manager-frontend/src/app/views/change-password/change-password.component.scss +++ /dev/null @@ -1,45 +0,0 @@ -.content { - width: 100%; -} - -mat-card { - max-width: 350px; - text-align: center; - animation: fadein 1s; - -o-animation: fadein 1s; /* Opera */ - -moz-animation: fadein 1s; /* Firefox */ - -webkit-animation: fadein 1s; /* Safari and Chrome */ -} - -mat-form-field { - width: 100%; -} - -mat-spinner { - width: 25px; - height: 25px; - margin: 20px auto 0 auto; -} - -.error { - color: #D50000; -} - -.success { - color: #8BC34A; -} - -@media screen and (max-width: 599px) { - - .content { - /* https://github.com/angular/flex-layout/issues/295 */ - display: block !important; - } - - mat-card { - /* https://github.com/angular/flex-layout/issues/295 */ - display: block !important; - max-width: 999px; - } - -} diff --git a/quartz-manager-frontend/src/app/views/change-password/change-password.component.spec.ts b/quartz-manager-frontend/src/app/views/change-password/change-password.component.spec.ts deleted file mode 100644 index e570558..0000000 --- a/quartz-manager-frontend/src/app/views/change-password/change-password.component.spec.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { FormsModule, ReactiveFormsModule } from '@angular/forms'; -import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { RouterTestingModule } from '@angular/router/testing'; -import { - ApiService, - AuthService, - UserService, - ConfigService -} from '../../services'; -import { MockApiService } from '../../services/mocks'; - -import { ChangePasswordComponent } from './change-password.component'; - -describe('ChangePasswordComponent', () => { - let component: ChangePasswordComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - imports: [ - RouterTestingModule, - FormsModule, - ReactiveFormsModule - ], - declarations: [ - ChangePasswordComponent - ], - providers: [ - { - provide: ApiService, - useClass: MockApiService - }, - AuthService, - UserService, - ConfigService - ], - schemas: [CUSTOM_ELEMENTS_SCHEMA] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(ChangePasswordComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should be created', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/quartz-manager-frontend/src/app/views/change-password/change-password.component.ts b/quartz-manager-frontend/src/app/views/change-password/change-password.component.ts deleted file mode 100644 index 36b13c3..0000000 --- a/quartz-manager-frontend/src/app/views/change-password/change-password.component.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { Component, OnInit } from '@angular/core'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { AuthService } from '../../services'; -import { Router } from '@angular/router'; -import { DisplayMessage } from '../../shared/models/display-message'; -import { delay, mergeMap } from 'rxjs/operators'; - -@Component({ - selector: 'app-change-password', - templateUrl: './change-password.component.html', - styleUrls: ['./change-password.component.scss'] -}) -export class ChangePasswordComponent implements OnInit { - - form: FormGroup; - /** - * Boolean used in telling the UI - * that the form has been submitted - * and is awaiting a response - */ - submitted = false; - - /** - * Diagnostic message from received - * form request error - */ - notification: DisplayMessage; - - constructor( - private authService: AuthService, - private router: Router, - private formBuilder: FormBuilder - ) { - } - - ngOnInit() { - - this.form = this.formBuilder.group({ - oldPassword: ['', Validators.compose([Validators.required, Validators.minLength(3), Validators.maxLength(64)])], - newPassword: ['', Validators.compose([Validators.required, Validators.minLength(3), Validators.maxLength(32)])] - }); - - } - - - onSubmit() { - /** - * Innocent until proven guilty - */ - this.notification = undefined; - this.submitted = true; - - this.authService.changePassword(this.form.value) - .pipe(delay(1000), mergeMap(() => this.authService.logout())) - .subscribe(() => { - this.router.navigate(['/login', { msgType: 'success', msgBody: 'Success! Please sign in with your new password.'}]); - }, error => { - this.submitted = false; - this.notification = { msgType: 'error', msgBody: 'Invalid old password.'}; - }); - - } - -} diff --git a/quartz-manager-frontend/src/app/views/change-password/index.ts b/quartz-manager-frontend/src/app/views/change-password/index.ts deleted file mode 100644 index d71e3de..0000000 --- a/quartz-manager-frontend/src/app/views/change-password/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './change-password.component'; diff --git a/quartz-manager-frontend/src/app/views/login/login.component.ts b/quartz-manager-frontend/src/app/views/login/login.component.ts index 201b259..8ef1c9d 100644 --- a/quartz-manager-frontend/src/app/views/login/login.component.ts +++ b/quartz-manager-frontend/src/app/views/login/login.component.ts @@ -1,18 +1,11 @@ -import { Inject } from '@angular/core'; -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { Router, ActivatedRoute } from '@angular/router'; -import { DisplayMessage } from '../../shared/models/display-message'; -import { Subscription } from 'rxjs'; -import { takeUntil, delay } from 'rxjs/operators' +import {Component, OnDestroy, OnInit} from '@angular/core'; +import {FormBuilder, FormGroup, Validators} from '@angular/forms'; +import {ActivatedRoute, Router} from '@angular/router'; +import {DisplayMessage} from '../../shared/models/display-message'; +import {Subject} from 'rxjs'; +import {delay, takeUntil} from 'rxjs/operators' -import { - UserService, - AuthService -} from '../../services'; - -import { Observable } from 'rxjs'; -import { Subject } from 'rxjs'; +import {AuthService, UserService} from '../../services'; @Component({ selector: 'app-login', @@ -59,18 +52,6 @@ export class LoginComponent implements OnInit, OnDestroy { this.ngUnsubscribe.complete(); } - // onResetCredentials() { - // this.userService.resetCredentials() - // .takeUntil(this.ngUnsubscribe) - // .subscribe(res => { - // if (res.result === 'success') { - // alert('Password has been reset to 123 for all accounts'); - // } else { - // alert('Server error'); - // } - // }); - // } - repository() { window.location.href = this.githubLink; } diff --git a/quartz-manager-frontend/src/app/views/signup/index.ts b/quartz-manager-frontend/src/app/views/signup/index.ts deleted file mode 100644 index 266b87d..0000000 --- a/quartz-manager-frontend/src/app/views/signup/index.ts +++ /dev/null @@ -1 +0,0 @@ -// export * from './signup.component'; \ No newline at end of file diff --git a/quartz-manager-frontend/src/app/views/signup/signup.component.html b/quartz-manager-frontend/src/app/views/signup/signup.component.html deleted file mode 100644 index c363ae5..0000000 --- a/quartz-manager-frontend/src/app/views/signup/signup.component.html +++ /dev/null @@ -1,56 +0,0 @@ -
- - - - -

Angular Spring Starter

-
- - -

{{ title }}

-
- - - -

{{notification.msgBody}}

- -
- - - - - - - - - - - - - - - - - -
-
- - -
-
- -

- Created by - Fan Jin - -

-

- Click below to go to repository -

- - -
- -
- -
\ No newline at end of file diff --git a/quartz-manager-frontend/src/app/views/signup/signup.component.scss b/quartz-manager-frontend/src/app/views/signup/signup.component.scss deleted file mode 100644 index 4de5604..0000000 --- a/quartz-manager-frontend/src/app/views/signup/signup.component.scss +++ /dev/null @@ -1,59 +0,0 @@ -.content { - width: 100%; - } - - mat-card { - max-width: 350px; - text-align: center; - animation: fadein 1s; - -o-animation: fadein 1s; /* Opera */ - -moz-animation: fadein 1s; /* Firefox */ - -webkit-animation: fadein 1s; /* Safari and Chrome */ - - } - - mat-form-field { - display: block; - } - - mat-spinner { - width: 25px; - height: 25px; - margin: 20px auto 0 auto; - } - - button { - display: block; - width: 100%; - } - - .error { - color: #D50000; - } - - .success { - color: #8BC34A; - } - - - @media screen and (max-width: 599px) { - - .content { - /* https://github.com/angular/flex-layout/issues/295 */ - display: block !important; - } - - mat-card { - /* https://github.com/angular/flex-layout/issues/295 */ - display: block !important; - max-width: 999px; - } - - } - - a { - text-decoration: none; - cursor: auto; - color: #FFFFFF; - } - \ No newline at end of file diff --git a/quartz-manager-frontend/src/app/views/signup/signup.component.spec.ts b/quartz-manager-frontend/src/app/views/signup/signup.component.spec.ts deleted file mode 100644 index 4c0da8f..0000000 --- a/quartz-manager-frontend/src/app/views/signup/signup.component.spec.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { async, ComponentFixture, TestBed } from '@angular/core/testing'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; - -import { MatCardModule} from '@angular/material/card'; -import { MatInputModule} from '@angular/material/input'; -import {MatProgressSpinnerModule} from '@angular/material/progress-spinner'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; - -import { SignupComponent } from './signup.component'; -import { ReactiveFormsModule } from '@angular/forms'; -import { RouterTestingModule } from '@angular/router/testing'; -import { - MockUserService, - MockApiService - } from '../../services/mocks'; -import { - UserService, - AuthService, - ApiService, - ConfigService - } from '../../services'; - -describe('SignupComponent', () => { - let component: SignupComponent; - let fixture: ComponentFixture; - - beforeEach(async(() => { - TestBed.configureTestingModule({ - declarations: [ SignupComponent ], - imports : [RouterTestingModule, - BrowserAnimationsModule, - MatCardModule, - MatInputModule, - MatProgressSpinnerModule, - MatProgressBarModule, - ReactiveFormsModule], - providers: [ - { - provide: UserService, - useClass: MockUserService - }, - { - provide: ApiService, - useClass: MockApiService - }, - AuthService, - ConfigService] - }) - .compileComponents(); - })); - - beforeEach(() => { - fixture = TestBed.createComponent(SignupComponent); - component = fixture.componentInstance; - fixture.detectChanges(); - }); - - it('should create', () => { - expect(component).toBeTruthy(); - }); -}); diff --git a/quartz-manager-frontend/src/app/views/signup/signup.component.ts b/quartz-manager-frontend/src/app/views/signup/signup.component.ts deleted file mode 100644 index 0a9c6fb..0000000 --- a/quartz-manager-frontend/src/app/views/signup/signup.component.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { Inject } from '@angular/core'; -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { Router, ActivatedRoute } from '@angular/router'; -import { DisplayMessage } from '../../shared/models/display-message'; -import { Subscription } from 'rxjs'; -import { takeUntil, delay } from 'rxjs/operators' -import { - UserService, - AuthService -} from '../../services'; - -import { Observable } from 'rxjs'; -import { Subject } from 'rxjs'; - -@Component({ - selector: 'app-signup', - templateUrl: './signup.component.html', - styleUrls: ['./signup.component.scss'] -}) -export class SignupComponent implements OnInit, OnDestroy { - title = 'Sign up'; - githubLink = 'https://github.com/bfwg/angular-spring-starter'; - form: FormGroup; - - /** - * Boolean used in telling the UI - * that the form has been submitted - * and is awaiting a response - */ - submitted = false; - - /** - * Notification message from received - * form request or router - */ - notification: DisplayMessage; - - returnUrl: string; - private ngUnsubscribe: Subject = new Subject(); - - constructor( - private userService: UserService, - private authService: AuthService, - private router: Router, - private route: ActivatedRoute, - private formBuilder: FormBuilder - ) { - - } - - ngOnInit() { - this.route.params.pipe( - takeUntil(this.ngUnsubscribe) - ) - .subscribe((params: DisplayMessage) => { - this.notification = params; - }); - // get return url from route parameters or default to '/' - this.returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/'; - this.form = this.formBuilder.group({ - username: ['', Validators.compose([Validators.required, Validators.minLength(3), Validators.maxLength(64)])], - password: ['', Validators.compose([Validators.required, Validators.minLength(3), Validators.maxLength(32)])], - firstname:[''], - lastname: [''] - }); - } - - ngOnDestroy() { - this.ngUnsubscribe.next(); - this.ngUnsubscribe.complete(); - } - - repository() { - window.location.href = this.githubLink; - } - - onSubmit() { - /** - * Innocent until proven guilty - */ - this.notification = undefined; - this.submitted = true; - - this.authService.signup(this.form.value) - // show me the animation - .pipe(delay(1000)) - .subscribe(data => { - console.log(data); - this.authService.login(this.form.value).subscribe(data =>{ - this.userService.getMyInfo().subscribe(); - }) - this.router.navigate([this.returnUrl]); - }, - error => { - this.submitted = false; - console.log("Sign up error" + JSON.stringify(error)); - this.notification = { msgType: 'error', msgBody: error['error'].errorMessage }; - }); - - } - - -} diff --git a/quartz-manager-parent/quartz-manager-common/src/main/java/it/fabioformosa/quartzmanager/common/properties/QuartzModuleProperties.java b/quartz-manager-parent/quartz-manager-common/src/main/java/it/fabioformosa/quartzmanager/common/properties/QuartzModuleProperties.java index 72e41c7..fced537 100644 --- a/quartz-manager-parent/quartz-manager-common/src/main/java/it/fabioformosa/quartzmanager/common/properties/QuartzModuleProperties.java +++ b/quartz-manager-parent/quartz-manager-common/src/main/java/it/fabioformosa/quartzmanager/common/properties/QuartzModuleProperties.java @@ -5,7 +5,7 @@ import lombok.Data; import java.util.Properties; @Data -public class QuartzModuleProperties{ +public class QuartzModuleProperties { private Properties properties = new Properties(); diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/aspects/WebSocketProgressNotifier.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/aspects/WebSocketProgressNotifier.java index b60fab1..73629dd 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/aspects/WebSocketProgressNotifier.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/aspects/WebSocketProgressNotifier.java @@ -20,15 +20,6 @@ public class WebSocketProgressNotifier implements ProgressNotifier { @Autowired private SimpMessageSendingOperations messagingTemplate; -// @Resource -// private Scheduler scheduler; - -// @Resource -// private LegacySchedulerService schedulerService; - -// @Resource -// private TriggerMonitor triggerMonitor; - //@AfterReturning("execution(* logAndSend(..))") // @Override // public void updateProgress(JoinPoint joinPoint) { diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AbstractTriggerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AbstractTriggerController.java index 3c6a786..2d0da31 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AbstractTriggerController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AbstractTriggerController.java @@ -2,7 +2,4 @@ package it.fabioformosa.quartzmanager.controllers; public class AbstractTriggerController { -// @Value("${quartz-manager.jobClass}") -// protected String jobClassname; - } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java index b8a6a1a..aa49f9f 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java @@ -6,16 +6,10 @@ import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.security.SecurityRequirement; -import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam; import it.fabioformosa.quartzmanager.dto.SchedulerDTO; -import it.fabioformosa.quartzmanager.dto.TriggerStatus; -import it.fabioformosa.quartzmanager.services.LegacySchedulerService; import it.fabioformosa.quartzmanager.services.SchedulerService; +import lombok.extern.slf4j.Slf4j; import org.quartz.SchedulerException; -import org.quartz.SimpleTrigger; -import org.quartz.impl.triggers.SimpleTriggerImpl; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.core.convert.ConversionService; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.GetMapping; @@ -31,19 +25,15 @@ import javax.annotation.Resource; * * @author Fabio.Formosa */ +@Slf4j @RestController @SecurityRequirement(name = "basic-auth") @RequestMapping("/quartz-manager/scheduler") public class SchedulerController { - private final Logger log = LoggerFactory.getLogger(SchedulerController.class); - - private LegacySchedulerService legacySchedulerService; - private SchedulerService schedulerService; - public SchedulerController(LegacySchedulerService legacySchedulerService, SchedulerService schedulerService, ConversionService conversionService) { - this.legacySchedulerService = legacySchedulerService; + public SchedulerController(SchedulerService schedulerService, ConversionService conversionService) { this.schedulerService = schedulerService; this.conversionService = conversionService; } @@ -51,30 +41,6 @@ public class SchedulerController { @Resource private ConversionService conversionService; - @Deprecated - //TODO to be removed when the legacy trigger is removed - @GetMapping("/config") - @Operation(summary = "Get the config of the trigger") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Return the trigger config", - content = { @Content(mediaType = "application/json", - schema = @Schema(implementation = SchedulerConfigParam.class)) }) - }) - public SchedulerConfigParam getConfig() throws SchedulerException { - log.debug("SCHEDULER - GET CONFIG params"); - SchedulerConfigParam schedulerConfigParam = legacySchedulerService.getOneSimpleTrigger() - .map(SchedulerController::fromSimpleTriggerToSchedulerConfigParam) - .orElse(new SchedulerConfigParam(0L, 0, 0)); - return schedulerConfigParam; - } - - public static SchedulerConfigParam fromSimpleTriggerToSchedulerConfigParam(SimpleTrigger simpleTrigger){ - int timesTriggered = simpleTrigger.getTimesTriggered(); - int maxCount = simpleTrigger.getRepeatCount() + 1; - long triggersPerDay = LegacySchedulerService.fromMillsIntervalToTriggerPerDay(simpleTrigger.getRepeatInterval()); - return new SchedulerConfigParam(triggersPerDay, maxCount, timesTriggered); - } - @GetMapping @Operation(summary = "Get the scheduler details") @ApiResponses(value = { @@ -87,53 +53,6 @@ public class SchedulerController { return schedulerService.getScheduler(); } - //TODO move this to the Trigger Controller - @GetMapping("/progress") - @Operation(summary = "Get the trigger status") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Return the trigger status", - content = { @Content(mediaType = "application/json", - schema = @Schema(implementation = TriggerStatus.class)) }) - }) - public TriggerStatus getProgressInfo() throws SchedulerException { - log.trace("SCHEDULER - GET PROGRESS INFO"); - TriggerStatus progress = new TriggerStatus(); - - SimpleTriggerImpl jobTrigger = (SimpleTriggerImpl) legacySchedulerService.getOneSimpleTrigger().get(); - if (jobTrigger != null && jobTrigger.getJobKey() != null) { - progress.setJobKey(jobTrigger.getJobKey().getName()); - progress.setJobClass(jobTrigger.getClass().getSimpleName()); - progress.setTimesTriggered(jobTrigger.getTimesTriggered()); - progress.setRepeatCount(jobTrigger.getRepeatCount()); - progress.setFinalFireTime(jobTrigger.getFinalFireTime()); - progress.setNextFireTime(jobTrigger.getNextFireTime()); - progress.setPreviousFireTime(jobTrigger.getPreviousFireTime()); - } - - return progress; - } - - - //REMOVEME -// @GetMapping(value = "/status", produces = "application/json") -// @Operation(summary = "Get the scheduler status") -// @ApiResponses(value = { -// @ApiResponse(responseCode = "200", description = "Return the scheduler status", -// content = { @Content(mediaType = "application/json", -// schema = @Schema(implementation = SchedulerStates.class)) }) -// }) -// public Map getStatus() throws SchedulerException { -// log.trace("SCHEDULER - GET STATUS"); -// String schedulerState = ""; -// if (legacySchedulerService.getScheduler().isShutdown() || !legacySchedulerService.getScheduler().isStarted()) -// schedulerState = SchedulerStates.STOPPED.toString(); -// else if (legacySchedulerService.getScheduler().isStarted() && legacySchedulerService.getScheduler().isInStandbyMode()) -// schedulerState = SchedulerStates.PAUSED.toString(); -// else -// schedulerState = SchedulerStates.RUNNING.toString(); -// return Collections.singletonMap("data", schedulerState.toLowerCase()); -// } - @GetMapping("/pause") @Operation(summary = "Get paused the scheduler") @ApiResponses(value = { @@ -142,7 +61,7 @@ public class SchedulerController { @ResponseStatus(HttpStatus.NO_CONTENT) public void pause() throws SchedulerException { log.info("SCHEDULER - PAUSE COMMAND"); - legacySchedulerService.getScheduler().standby(); + schedulerService.standby(); } @GetMapping("/resume") @@ -153,7 +72,7 @@ public class SchedulerController { @ResponseStatus(HttpStatus.NO_CONTENT) public void resume() throws SchedulerException { log.info("SCHEDULER - RESUME COMMAND"); - legacySchedulerService.getScheduler().start(); + schedulerService.start(); } @GetMapping("/run") @@ -164,7 +83,7 @@ public class SchedulerController { @ResponseStatus(HttpStatus.NO_CONTENT) public void run() throws SchedulerException { log.info("SCHEDULER - START COMMAND"); - legacySchedulerService.getScheduler().start(); + schedulerService.start(); } @GetMapping("/stop") @@ -175,7 +94,7 @@ public class SchedulerController { @ResponseStatus(HttpStatus.NO_CONTENT) public void stop() throws SchedulerException { log.info("SCHEDULER - STOP COMMAND"); - legacySchedulerService.getScheduler().shutdown(true); + schedulerService.shutdown(); } } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerController.java index f1b0177..cd39af6 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerController.java @@ -11,7 +11,7 @@ import it.fabioformosa.quartzmanager.dto.SimpleTriggerInputDTO; import it.fabioformosa.quartzmanager.dto.SimpleTriggerDTO; import it.fabioformosa.quartzmanager.dto.TriggerDTO; import it.fabioformosa.quartzmanager.exceptions.TriggerNotFoundException; -import it.fabioformosa.quartzmanager.services.SimpleTriggerSchedulerService; +import it.fabioformosa.quartzmanager.services.SimpleTriggerService; import lombok.extern.slf4j.Slf4j; import org.quartz.SchedulerException; import org.springframework.http.HttpStatus; @@ -27,9 +27,9 @@ public class SimpleTriggerController extends AbstractTriggerController { static public final String SIMPLE_TRIGGER_CONTROLLER_BASE_URL = "/quartz-manager/simple-triggers"; - private SimpleTriggerSchedulerService simpleSchedulerService; + private SimpleTriggerService simpleSchedulerService; - public SimpleTriggerController(SimpleTriggerSchedulerService simpleSchedulerService) { + public SimpleTriggerController(SimpleTriggerService simpleSchedulerService) { this.simpleSchedulerService = simpleSchedulerService; } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java index 58e95a9..04f4a54 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java @@ -1,21 +1,14 @@ package it.fabioformosa.quartzmanager.controllers; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.security.SecurityRequirement; -import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam; import it.fabioformosa.quartzmanager.dto.TriggerDTO; -import it.fabioformosa.quartzmanager.exceptions.TriggerNotFoundException; -import it.fabioformosa.quartzmanager.services.LegacySchedulerService; import it.fabioformosa.quartzmanager.services.TriggerService; import lombok.extern.slf4j.Slf4j; import org.quartz.SchedulerException; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; -import javax.validation.Valid; import java.util.List; @Slf4j @@ -26,11 +19,9 @@ public class TriggerController extends AbstractTriggerController { static public final String TRIGGER_CONTROLLER_BASE_URL = "/quartz-manager/triggers"; - private LegacySchedulerService schedulerService; private TriggerService triggerService; - public TriggerController(LegacySchedulerService schedulerService, TriggerService triggerService) { - this.schedulerService = schedulerService; + public TriggerController(TriggerService triggerService) { this.triggerService = triggerService; } @@ -39,45 +30,4 @@ public class TriggerController extends AbstractTriggerController { return triggerService.fetchTriggers(); } - - @GetMapping("/{name}") - public TriggerDTO getTrigger(@PathVariable String name) throws SchedulerException, TriggerNotFoundException { - return schedulerService.getLegacyTriggerByName(name); - } - -// @Deprecated -// @PostMapping("/{name}") -// @ResponseStatus(HttpStatus.CREATED) -// @Operation(summary = "Create a new trigger") -// @ApiResponses(value = { -// @ApiResponse(responseCode = "201", description = "Created the new trigger", -// content = { @Content(mediaType = "application/json", -// schema = @Schema(implementation = TriggerDTO.class)) }), -// @ApiResponse(responseCode = "400", description = "Invalid config supplied", -// content = @Content) -// }) -// public TriggerDTO postTrigger(@PathVariable String name, @Valid @RequestBody SchedulerConfigParam config) throws SchedulerException, ClassNotFoundException { -// log.info("TRIGGER - CREATING a trigger {} {}", name, config); -// TriggerDTO newTriggerDTO = schedulerService.scheduleNewTrigger(name, config); -// log.info("TRIGGER - CREATED a trigger {}", newTriggerDTO); -// return newTriggerDTO; -// } - - @PutMapping("/{name}") - @Operation(summary = "Reschedule the trigger") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Rescheduled the trigger", - content = { @Content(mediaType = "application/json", - schema = @Schema(implementation = TriggerDTO.class)) }), - @ApiResponse(responseCode = "400", description = "Invalid config supplied", - content = @Content) - }) - public TriggerDTO rescheduleTrigger(@PathVariable String name, @Valid @RequestBody SchedulerConfigParam config) throws SchedulerException { - log.info("TRIGGER - RESCHEDULING the trigger {} {}", name, config); - TriggerDTO triggerDTO = schedulerService.rescheduleTrigger(name, config); - log.info("TRIGGER - RESCHEDULED the trigger {}", triggerDTO); - return triggerDTO; - } - - } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/UserController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/UserController.java index 9e1b820..64ffb37 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/UserController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/UserController.java @@ -20,47 +20,4 @@ public class UserController { return "\"NO_AUTH\""; } -// /** -// * JWT Temporary disabled -// * -// * @author Fabio.Formosa -// * -// */ - - // @Autowired - // private UserService userService; - - - // @RequestMapping(method = POST, value = "/signup") - // public ResponseEntity addUser(@RequestBody UserRequest userRequest, - // UriComponentsBuilder ucBuilder) { - // - // User existUser = this.userService.findByUsername(userRequest.getUsername()); - // if (existUser != null) - // throw new ResourceConflictException(userRequest.getId(), "Username already exists"); - // User user = this.userService.save(userRequest); - // HttpHeaders headers = new HttpHeaders(); - // headers.setLocation(ucBuilder.path("/api/user/{userId}").buildAndExpand(user.getId()).toUri()); - // return new ResponseEntity<>(user, HttpStatus.CREATED); - // } - // - // @RequestMapping(method = GET, value = "/user/all") - // public List loadAll() { - // return this.userService.findAll(); - // } - // - // @RequestMapping(method = GET, value = "/user/{userId}") - // public User loadById(@PathVariable Long userId) { - // return this.userService.findById(userId); - // } - // - // - // @RequestMapping(method = GET, value = "/user/reset-credentials") - // public ResponseEntity resetCredentials() { - // this.userService.resetCredentials(); - // Map result = new HashMap<>(); - // result.put("result", "success"); - // return ResponseEntity.accepted().body(result); - // } - } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/dto/SchedulerConfigParam.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/dto/SchedulerConfigParam.java deleted file mode 100644 index 4b78d62..0000000 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/dto/SchedulerConfigParam.java +++ /dev/null @@ -1,20 +0,0 @@ -package it.fabioformosa.quartzmanager.dto; - -import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; -import lombok.NoArgsConstructor; - -import javax.validation.constraints.NotNull; - -@NoArgsConstructor -@AllArgsConstructor -@Builder -@Data -public class SchedulerConfigParam { - @NotNull - public Long triggerPerDay; - @NotNull - public Integer maxCount; - public int timesTriggered; -} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/scheduler/TriggerMonitor.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/scheduler/TriggerMonitor.java deleted file mode 100644 index cb0e62b..0000000 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/scheduler/TriggerMonitor.java +++ /dev/null @@ -1,11 +0,0 @@ -package it.fabioformosa.quartzmanager.scheduler; - -import org.quartz.Trigger; - -public interface TriggerMonitor { - - void setTrigger(Trigger trigger); - - Trigger getTrigger(); - -} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/scheduler/TriggerMonitorImpl.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/scheduler/TriggerMonitorImpl.java deleted file mode 100644 index 1e352ba..0000000 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/scheduler/TriggerMonitorImpl.java +++ /dev/null @@ -1,19 +0,0 @@ -package it.fabioformosa.quartzmanager.scheduler; - -import org.quartz.Trigger; - -public class TriggerMonitorImpl implements TriggerMonitor { - - private Trigger trigger; - - @Override - public Trigger getTrigger() { - return trigger; - } - - @Override - public void setTrigger(Trigger trigger) { - this.trigger = trigger; - } - -} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/LegacySchedulerService.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/LegacySchedulerService.java deleted file mode 100644 index bea3ef0..0000000 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/LegacySchedulerService.java +++ /dev/null @@ -1,117 +0,0 @@ -package it.fabioformosa.quartzmanager.services; - -import it.fabioformosa.quartzmanager.common.utils.Try; -import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam; -import it.fabioformosa.quartzmanager.dto.TriggerDTO; -import it.fabioformosa.quartzmanager.exceptions.TriggerNotFoundException; -import org.quartz.*; -import org.quartz.impl.matchers.GroupMatcher; -import org.springframework.core.convert.ConversionService; -import org.springframework.stereotype.Service; - -import java.util.Optional; - -import static org.quartz.TriggerBuilder.newTrigger; - -@Service -public class LegacySchedulerService extends AbstractSchedulerService { - - public static final int MILLS_IN_A_DAY = 1000 * 60 * 60 * 24; - public static final int SEC_IN_A_DAY = 60 * 60 * 24; - - public LegacySchedulerService(Scheduler scheduler, ConversionService conversionService) { - super(scheduler, conversionService); - } - - public static int fromTriggerPerDayToMillsInterval(long triggerPerDay) { - return (int) Math.ceil(Long.valueOf(LegacySchedulerService.MILLS_IN_A_DAY) / triggerPerDay); // with ceil the triggerPerDay is a max value - } - - public static int fromTriggerPerDayToSecInterval(long triggerPerDay) { - return (int) Math.ceil(Long.valueOf(LegacySchedulerService.SEC_IN_A_DAY) / triggerPerDay); - } - - public static long fromMillsIntervalToTriggerPerDay(long repeatIntervalInMills) { - return (int) Math.ceil(MILLS_IN_A_DAY / repeatIntervalInMills); - } - - public Scheduler getScheduler() { - return scheduler; - } - - public Optional getTriggerByKey(String triggerKeyName) throws SchedulerException { - return scheduler.getTriggerKeys(GroupMatcher.anyGroup()).stream() - .filter(triggerKey -> triggerKey.getName().equals(triggerKeyName)) - .findFirst(); - } - - public Optional getOneSimpleTrigger() throws SchedulerException { - return getOneTriggerKey() - .map(Try.with(triggerKey -> scheduler.getTrigger(triggerKey))) - .filter(Try::isSuccess).map(Try::getSuccess) - .filter(trigger -> trigger instanceof SimpleTrigger) - .map(trigger -> (SimpleTrigger) trigger); - } - - public Optional getOneTriggerKey() throws SchedulerException { - return scheduler.getTriggerKeys(GroupMatcher.anyGroup()).stream() - .findFirst(); - } - - public TriggerDTO getLegacyTriggerByName(String name) throws SchedulerException, TriggerNotFoundException { - Trigger trigger = getTriggerByName(name); - return conversionService.convert(trigger, TriggerDTO.class); - } - - - - public TriggerDTO scheduleNewTrigger(String name, String jobClassname, SchedulerConfigParam config) throws SchedulerException, ClassNotFoundException { - Class jobClass = (Class) Class.forName(jobClassname); - JobDetail jobDetail = JobBuilder.newJob() - .ofType(jobClass) - .storeDurably(false) - .build(); - - int intervalInMills = LegacySchedulerService.fromTriggerPerDayToMillsInterval(config.getTriggerPerDay()); - - Trigger newTrigger = newTrigger() - .withSchedule( - SimpleScheduleBuilder.simpleSchedule() - .withIntervalInMilliseconds(intervalInMills) - .withRepeatCount(config.getMaxCount() - 1) - .withMisfireHandlingInstructionNextWithRemainingCount() - ) - .withIdentity(name) - .build(); - - scheduler.scheduleJob(jobDetail, newTrigger); - - return conversionService.convert(newTrigger, TriggerDTO.class); - } - - - - public TriggerDTO rescheduleTrigger(String name, SchedulerConfigParam config) throws SchedulerException { - int intervalInMills = LegacySchedulerService.fromTriggerPerDayToMillsInterval(config.getTriggerPerDay()); - - Optional optionalTriggerKey = getTriggerByKey(name); - TriggerKey triggerKey = optionalTriggerKey.orElse(TriggerKey.triggerKey(name)); - Trigger trigger = scheduler.getTrigger(triggerKey); - - Trigger newTrigger = newTrigger() - .withSchedule( - SimpleScheduleBuilder.simpleSchedule() - .withIntervalInMilliseconds(intervalInMills) - .withRepeatCount(config.getMaxCount() - 1) - .withMisfireHandlingInstructionNextWithRemainingCount() - ) - .forJob(trigger.getJobKey().getName()) - .withIdentity(name) - .build(); - - scheduler.rescheduleJob(triggerKey, newTrigger); - - return conversionService.convert(newTrigger, TriggerDTO.class); - } - -} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SchedulerService.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SchedulerService.java index 40e4cd4..82c2da8 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SchedulerService.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SchedulerService.java @@ -2,6 +2,7 @@ package it.fabioformosa.quartzmanager.services; import it.fabioformosa.quartzmanager.dto.SchedulerDTO; import org.quartz.Scheduler; +import org.quartz.SchedulerException; import org.springframework.core.convert.ConversionService; import org.springframework.stereotype.Service; @@ -16,4 +17,14 @@ public class SchedulerService extends AbstractSchedulerService{ return conversionService.convert(scheduler, SchedulerDTO.class); } + public void standby() throws SchedulerException { + scheduler.standby(); + } + public void start() throws SchedulerException { + scheduler.start(); + } + public void shutdown() throws SchedulerException { + scheduler.shutdown(true); + } + } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerService.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SimpleTriggerService.java similarity index 91% rename from quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerService.java rename to quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SimpleTriggerService.java index 158f811..4540d1e 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerService.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/services/SimpleTriggerService.java @@ -9,9 +9,9 @@ import org.springframework.core.convert.ConversionService; import org.springframework.stereotype.Service; @Service -public class SimpleTriggerSchedulerService extends AbstractSchedulerService { +public class SimpleTriggerService extends AbstractSchedulerService { - public SimpleTriggerSchedulerService(Scheduler scheduler, ConversionService conversionService) { + public SimpleTriggerService(Scheduler scheduler, ConversionService conversionService) { super(scheduler, conversionService); } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerControllerTest.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerControllerTest.java index fa72c6b..781ce5c 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerControllerTest.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerControllerTest.java @@ -8,7 +8,7 @@ import it.fabioformosa.quartzmanager.dto.SimpleTriggerCommandDTO; import it.fabioformosa.quartzmanager.dto.SimpleTriggerDTO; import it.fabioformosa.quartzmanager.dto.SimpleTriggerInputDTO; import it.fabioformosa.quartzmanager.exceptions.TriggerNotFoundException; -import it.fabioformosa.quartzmanager.services.SimpleTriggerSchedulerService; +import it.fabioformosa.quartzmanager.services.SimpleTriggerService; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -37,17 +37,17 @@ class SimpleTriggerControllerTest { private MockMvc mockMvc; @MockBean - private SimpleTriggerSchedulerService simpleTriggerSchedulerService; + private SimpleTriggerService simpleTriggerService; @AfterEach void cleanUp(){ - Mockito.reset(simpleTriggerSchedulerService); + Mockito.reset(simpleTriggerService); } @Test void whenGetIsCalled_thenASimpleTriggerIsReturned() throws Exception { SimpleTriggerDTO expectedSimpleTriggerDTO = TriggerUtils.getSimpleTriggerInstance("mytrigger"); - Mockito.when(simpleTriggerSchedulerService.getSimpleTriggerByName("mytrigger")).thenReturn(expectedSimpleTriggerDTO); + Mockito.when(simpleTriggerService.getSimpleTriggerByName("mytrigger")).thenReturn(expectedSimpleTriggerDTO); mockMvc.perform(get(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") .contentType(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk()) @@ -56,7 +56,7 @@ class SimpleTriggerControllerTest { @Test void givenAnExistingTrigger_whenGetIsCalled_then404IsReturned() throws Exception { - Mockito.when(simpleTriggerSchedulerService.getSimpleTriggerByName("not_existing_trigger_name")).thenThrow(new TriggerNotFoundException("not_existing_trigger_name")); + Mockito.when(simpleTriggerService.getSimpleTriggerByName("not_existing_trigger_name")).thenThrow(new TriggerNotFoundException("not_existing_trigger_name")); mockMvc.perform(get(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL + "/not_existing_trigger_name") .contentType(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isNotFound()); @@ -66,7 +66,7 @@ class SimpleTriggerControllerTest { void givenASimpleTriggerCommandDTO_whenPosted_thenANewSimpleTriggerIsCreated() throws Exception { SimpleTriggerInputDTO simpleTriggerInputDTO = buildSimpleTriggerCommandDTO(); SimpleTriggerDTO expectedSimpleTriggerDTO = TriggerUtils.getSimpleTriggerInstance("mytrigger", simpleTriggerInputDTO); - Mockito.when(simpleTriggerSchedulerService.scheduleSimpleTrigger(any())).thenReturn(expectedSimpleTriggerDTO); + Mockito.when(simpleTriggerService.scheduleSimpleTrigger(any())).thenReturn(expectedSimpleTriggerDTO); mockMvc.perform( post(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") .contentType(MediaType.APPLICATION_JSON) @@ -103,7 +103,7 @@ class SimpleTriggerControllerTest { .triggerName("mytrigger") .simpleTriggerInputDTO(simpleTriggerInputDTO) .build(); - Mockito.when(simpleTriggerSchedulerService.rescheduleSimpleTrigger(simpleTriggerCommandDTO)).thenReturn(expectedSimpleTriggerDTO); + Mockito.when(simpleTriggerService.rescheduleSimpleTrigger(simpleTriggerCommandDTO)).thenReturn(expectedSimpleTriggerDTO); mockMvc.perform(put(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") .contentType(MediaType.APPLICATION_JSON) diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java index 783a32d..30a898c 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/TriggerControllerTest.java @@ -1,26 +1,14 @@ package it.fabioformosa.quartzmanager.controllers; import it.fabioformosa.quartzmanager.QuartManagerApplicationTests; -import it.fabioformosa.quartzmanager.controllers.utils.InvalidSchedulerConfigParamProvider; -import it.fabioformosa.quartzmanager.controllers.utils.TestUtils; -import it.fabioformosa.quartzmanager.controllers.utils.TriggerUtils; -import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam; -import it.fabioformosa.quartzmanager.dto.TriggerDTO; -import it.fabioformosa.quartzmanager.services.LegacySchedulerService; +import it.fabioformosa.quartzmanager.services.TriggerService; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ArgumentsSource; 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.*; @ContextConfiguration(classes = {QuartManagerApplicationTests.class}) @WebMvcTest(controllers = TriggerController.class, properties = { @@ -32,56 +20,13 @@ class TriggerControllerTest { private MockMvc mockMvc; @MockBean - private LegacySchedulerService schedulerService; + private TriggerService triggerService; @AfterEach void cleanUp(){ - Mockito.reset(schedulerService); + Mockito.reset(triggerService); } - @ParameterizedTest - @ArgumentsSource(InvalidSchedulerConfigParamProvider.class) - void givenAnInvalidSchedulerConfigParam_whenRequestedANewTrigger_thenAnErrorIsReturned(SchedulerConfigParam invalidSchedulerConfigParam) throws Exception { - mockMvc.perform(post(TriggerController.TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") - .contentType(MediaType.APPLICATION_JSON) - .content(TestUtils.toJson(invalidSchedulerConfigParam))) - .andExpect(MockMvcResultMatchers.status().is4xxClientError()); - } - @Test - void whenGetIsCalled_thenATriggerIsReturned() throws Exception { - TriggerDTO expectedTriggerDTO = TriggerUtils.getTriggerInstance("mytrigger"); - Mockito.when(schedulerService.getLegacyTriggerByName("mytrigger")).thenReturn(expectedTriggerDTO); - - mockMvc.perform(get(TriggerController.TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") - .contentType(MediaType.APPLICATION_JSON)).andExpect(MockMvcResultMatchers.status().isOk()) - .andExpect(MockMvcResultMatchers.content().json(TestUtils.toJson(expectedTriggerDTO))); - } - - @Test - void givenATriggerName_whenPutSchedulerConfigParam_thenTheTriggerIsRescheduled() throws Exception { - SchedulerConfigParam expectedConfigParam = buildSimpleSchedulerConfigParam(); - TriggerDTO expectedTriggerDTO = TriggerUtils.getTriggerInstance("mytrigger"); - Mockito.when(schedulerService.rescheduleTrigger("mytrigger", buildSimpleSchedulerConfigParam())).thenReturn(expectedTriggerDTO); - - mockMvc.perform(put(TriggerController.TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") - .contentType(MediaType.APPLICATION_JSON) - .content(TestUtils.toJson(expectedConfigParam))) - .andExpect(MockMvcResultMatchers.status().isOk()) - .andExpect(MockMvcResultMatchers.content().json(TestUtils.toJson(expectedTriggerDTO))); - } - - @ParameterizedTest - @ArgumentsSource(InvalidSchedulerConfigParamProvider.class) - void givenAnInvalidSchedulerConfigParam_whenATriggerIsRescheduled_thenAnErrorIsReturned(SchedulerConfigParam invalidSchedulerConfigParam) throws Exception { - mockMvc.perform(put(TriggerController.TRIGGER_CONTROLLER_BASE_URL + "/mytrigger") - .contentType(MediaType.APPLICATION_JSON) - .content(TestUtils.toJson(invalidSchedulerConfigParam))) - .andExpect(MockMvcResultMatchers.status().is4xxClientError()); - } - - private SchedulerConfigParam buildSimpleSchedulerConfigParam() { - return SchedulerConfigParam.builder().maxCount(20).triggerPerDay(20000L).build(); - } } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/InvalidSchedulerConfigParamProvider.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/InvalidSchedulerConfigParamProvider.java deleted file mode 100644 index 486f29e..0000000 --- a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/controllers/utils/InvalidSchedulerConfigParamProvider.java +++ /dev/null @@ -1,19 +0,0 @@ -package it.fabioformosa.quartzmanager.controllers.utils; - -import it.fabioformosa.quartzmanager.dto.SchedulerConfigParam; -import org.junit.jupiter.api.extension.ExtensionContext; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.ArgumentsProvider; - -import java.util.stream.Stream; - -public class InvalidSchedulerConfigParamProvider implements ArgumentsProvider { - @Override - public Stream provideArguments(ExtensionContext extensionContext) throws Exception { - return Stream.of( - Arguments.of(SchedulerConfigParam.builder().build()), - Arguments.of(SchedulerConfigParam.builder().maxCount(1).build()), - Arguments.of(SchedulerConfigParam.builder().triggerPerDay(1L).build()) - ); - } -} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerServiceTest.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/services/SimpleTriggerServiceTest.java similarity index 96% rename from quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerServiceTest.java rename to quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/services/SimpleTriggerServiceTest.java index 332854a..ad3b924 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/services/SimpleTriggerSchedulerServiceTest.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/services/SimpleTriggerServiceTest.java @@ -20,10 +20,10 @@ import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.MockitoAnnotations.openMocks; -class SimpleTriggerSchedulerServiceTest { +class SimpleTriggerServiceTest { @InjectMocks - private SimpleTriggerSchedulerService simpleSchedulerService; + private SimpleTriggerService simpleSchedulerService; @Mock private Scheduler scheduler; diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/configuration/WebSecurityConfigJWT.java b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/configuration/WebSecurityConfigJWT.java index b56dffe..acd4b43 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/configuration/WebSecurityConfigJWT.java +++ b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/configuration/WebSecurityConfigJWT.java @@ -62,7 +62,7 @@ public class WebSecurityConfigJWT extends WebSecurityConfigurerAdapter { private String contextPath; @Value("${app.name:quartz-manager}") - private String APP_NAME; + private String appName; @Value("${quartz-manager.security.login-model.form-login-enabled}") private Boolean formLoginEnabled; @@ -152,7 +152,7 @@ public class WebSecurityConfigJWT extends WebSecurityConfigurerAdapter { @Bean public JwtTokenHelper jwtTokenHelper() { - return new JwtTokenHelper(APP_NAME, jwtSecurityProps); + return new JwtTokenHelper(appName, jwtSecurityProps); } @Bean diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/AuthenticationSuccessHandler.java b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/AuthenticationSuccessHandler.java index aadde13..64d8d6d 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/AuthenticationSuccessHandler.java +++ b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/AuthenticationSuccessHandler.java @@ -1,32 +1,30 @@ -package it.fabioformosa.quartzmanager.security.helpers.impl; - -import java.io.IOException; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.springframework.security.core.Authentication; -import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; - -public class AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { - - private final JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler; - - public AuthenticationSuccessHandler(JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler) { - super(); - this.jwtAuthenticationSuccessHandler = jwtAuthenticationSuccessHandler; - } - - public String cookieMustBeDeletedAtLogout() { - return jwtAuthenticationSuccessHandler.cookieMustBeDeletedAtLogout(); - } - - @Override - public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, - Authentication authentication ) throws IOException, ServletException { - clearAuthenticationAttributes(request); - jwtAuthenticationSuccessHandler.onLoginSuccess(authentication, response); - } - -} +package it.fabioformosa.quartzmanager.security.helpers.impl; + +import org.springframework.security.core.Authentication; +import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class AuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { + + private final JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler; + + public AuthenticationSuccessHandler(JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler) { + super(); + this.jwtAuthenticationSuccessHandler = jwtAuthenticationSuccessHandler; + } + + public String cookieMustBeDeletedAtLogout() { + return jwtAuthenticationSuccessHandler.cookieMustBeDeletedAtLogout(); + } + + @Override + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, + Authentication authentication) throws IOException { + clearAuthenticationAttributes(request); + jwtAuthenticationSuccessHandler.onLoginSuccess(authentication, response); + } + +} diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/FormLoginConfig.java b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/FormLoginConfig.java index 206f969..22b660f 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/FormLoginConfig.java +++ b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/FormLoginConfig.java @@ -1,75 +1,75 @@ -package it.fabioformosa.quartzmanager.security.helpers.impl; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer; - -import it.fabioformosa.quartzmanager.security.helpers.LoginConfigurer; - -/** - * It delegates form to @FormLoginConfigurer of the httpSecurity. - * - */ -public class FormLoginConfig implements LoginConfigurer { - - private static final Logger log = LoggerFactory.getLogger(FormLoginConfig.class); - - private final AuthenticationSuccessHandler authenticationSuccessHandler; - - private final AuthenticationFailureHandler authenticationFailureHandler; - - - public FormLoginConfig() { - super(); - authenticationSuccessHandler = null; - authenticationFailureHandler = null; - } - - public FormLoginConfig(AuthenticationFailureHandler authenticationFailureHandler) { - super(); - authenticationSuccessHandler = null; - this.authenticationFailureHandler = authenticationFailureHandler; - } - - public FormLoginConfig(AuthenticationSuccessHandler authenticationSuccessHandler) { - super(); - this.authenticationSuccessHandler = authenticationSuccessHandler; - authenticationFailureHandler = null; - } - - public FormLoginConfig(AuthenticationSuccessHandler authenticationSuccessHandler, - AuthenticationFailureHandler authenticationFailureHandler) { - super(); - this.authenticationSuccessHandler = authenticationSuccessHandler; - this.authenticationFailureHandler = authenticationFailureHandler; - } - - @Override - public String cookieMustBeDeletedAtLogout() { - return authenticationSuccessHandler.cookieMustBeDeletedAtLogout(); - } - - @Override - public HttpSecurity login(String loginPath, - HttpSecurity http, AuthenticationManager authenticationManager) throws Exception { - log.debug("Configuring login through FormLoginConfigurer..."); - - FormLoginConfigurer login = http.formLogin().loginPage(loginPath); - - if(authenticationSuccessHandler != null) { - log.debug("Setting an authenticationSuccessHandler"); - login = login.successHandler(authenticationSuccessHandler); - } - - if(authenticationFailureHandler != null) { - log.debug("Setting an authenticationFailureHandler"); - login = login.failureHandler(authenticationFailureHandler); - } - - HttpSecurity httpSecurity = login.and(); - return httpSecurity; - } - -} +package it.fabioformosa.quartzmanager.security.helpers.impl; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer; + +import it.fabioformosa.quartzmanager.security.helpers.LoginConfigurer; + +/** + * It delegates the login to the @FormLoginConfigurer of the httpSecurity. + * + */ +public class FormLoginConfig implements LoginConfigurer { + + private static final Logger log = LoggerFactory.getLogger(FormLoginConfig.class); + + private final AuthenticationSuccessHandler authenticationSuccessHandler; + + private final AuthenticationFailureHandler authenticationFailureHandler; + + + public FormLoginConfig() { + super(); + authenticationSuccessHandler = null; + authenticationFailureHandler = null; + } + + public FormLoginConfig(AuthenticationFailureHandler authenticationFailureHandler) { + super(); + authenticationSuccessHandler = null; + this.authenticationFailureHandler = authenticationFailureHandler; + } + + public FormLoginConfig(AuthenticationSuccessHandler authenticationSuccessHandler) { + super(); + this.authenticationSuccessHandler = authenticationSuccessHandler; + authenticationFailureHandler = null; + } + + public FormLoginConfig(AuthenticationSuccessHandler authenticationSuccessHandler, + AuthenticationFailureHandler authenticationFailureHandler) { + super(); + this.authenticationSuccessHandler = authenticationSuccessHandler; + this.authenticationFailureHandler = authenticationFailureHandler; + } + + @Override + public String cookieMustBeDeletedAtLogout() { + return authenticationSuccessHandler.cookieMustBeDeletedAtLogout(); + } + + @Override + public HttpSecurity login(String loginPath, + HttpSecurity http, AuthenticationManager authenticationManager) throws Exception { + log.debug("Configuring login through FormLoginConfigurer..."); + + FormLoginConfigurer login = http.formLogin().loginPage(loginPath); + + if(authenticationSuccessHandler != null) { + log.debug("Setting an authenticationSuccessHandler"); + login = login.successHandler(authenticationSuccessHandler); + } + + if(authenticationFailureHandler != null) { + log.debug("Setting an authenticationFailureHandler"); + login = login.failureHandler(authenticationFailureHandler); + } + + HttpSecurity httpSecurity = login.and(); + return httpSecurity; + } + +} diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtAuthenticationSuccessHandlerImpl.java b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtAuthenticationSuccessHandlerImpl.java index ca5de80..8631920 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtAuthenticationSuccessHandlerImpl.java +++ b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtAuthenticationSuccessHandlerImpl.java @@ -1,25 +1,22 @@ package it.fabioformosa.quartzmanager.security.helpers.impl; -import java.io.IOException; - -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletResponse; - +import com.fasterxml.jackson.databind.ObjectMapper; +import it.fabioformosa.quartzmanager.security.configuration.properties.JwtSecurityProperties; +import it.fabioformosa.quartzmanager.security.models.UserTokenState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; import org.springframework.security.core.userdetails.User; -import com.fasterxml.jackson.databind.ObjectMapper; - -import it.fabioformosa.quartzmanager.security.configuration.properties.JwtSecurityProperties; -import it.fabioformosa.quartzmanager.security.models.UserTokenState; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; /** * It depends on @JwtTokenHelper to generate the jwtToken. * On login success, it generates the jwtToken and it returns it to the login according to possible strategies: cookie, response header. - * You can choice the strategy through @JwtSecurityProperties + * You can choose the strategy through @JwtSecurityProperties * */ public class JwtAuthenticationSuccessHandlerImpl implements JwtAuthenticationSuccessHandler { @@ -51,7 +48,7 @@ public class JwtAuthenticationSuccessHandlerImpl implements JwtAuthenticationSuc @Override public void onLoginSuccess(Authentication authentication, HttpServletResponse response) throws IOException { - log.debug("Login successed, generating jwtToken..."); + log.debug("Login succeeded, generating jwtToken..."); User user = (User) authentication.getPrincipal(); String jwtToken = jwtTokenHelper.generateToken(user.getUsername()); diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtUsernamePasswordFiterLoginConfig.java b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtUsernamePasswordFiterLoginConfig.java index 6f6991c..ffbc7bd 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtUsernamePasswordFiterLoginConfig.java +++ b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtUsernamePasswordFiterLoginConfig.java @@ -1,46 +1,46 @@ -package it.fabioformosa.quartzmanager.security.helpers.impl; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.HttpMethod; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter; -import org.springframework.security.web.util.matcher.RegexRequestMatcher; -import org.springframework.web.filter.GenericFilterBean; - -import it.fabioformosa.quartzmanager.security.helpers.LoginConfigurer; - -/** - * It adds a new filter @JwtAuthenticationFilter after @AbstractPreAuthenticatedProcessingFilter that match login path - * - */ -public class JwtUsernamePasswordFiterLoginConfig implements LoginConfigurer { - - private static final Logger log = LoggerFactory.getLogger(JwtUsernamePasswordFiterLoginConfig.class); - - private final JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler; - - public JwtUsernamePasswordFiterLoginConfig(JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler) { - super(); - this.jwtAuthenticationSuccessHandler = jwtAuthenticationSuccessHandler; - } - - public GenericFilterBean authenticationProcessingFilter(String loginPath, AuthenticationManager authenticationManager) throws Exception { - JwtAuthenticationFilter authenticationProcessingFilter = new JwtAuthenticationFilter(authenticationManager, jwtAuthenticationSuccessHandler); - authenticationProcessingFilter.setRequiresAuthenticationRequestMatcher(new RegexRequestMatcher(loginPath, HttpMethod.POST.name(), false)); - return authenticationProcessingFilter; - } - - @Override - public String cookieMustBeDeletedAtLogout() { - return jwtAuthenticationSuccessHandler.cookieMustBeDeletedAtLogout(); - } - - @Override - public HttpSecurity login(String loginPath, HttpSecurity http, AuthenticationManager authenticationManager) throws Exception { - log.debug("Configuring login through JwtAuthenticationFilter..."); - return http.addFilterAfter(authenticationProcessingFilter(loginPath, authenticationManager), AbstractPreAuthenticatedProcessingFilter.class); - } - -} +package it.fabioformosa.quartzmanager.security.helpers.impl; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter; +import org.springframework.security.web.util.matcher.RegexRequestMatcher; +import org.springframework.web.filter.GenericFilterBean; + +import it.fabioformosa.quartzmanager.security.helpers.LoginConfigurer; + +/** + * It adds a new filter @JwtAuthenticationFilter after @AbstractPreAuthenticatedProcessingFilter that match login path + * + */ +public class JwtUsernamePasswordFiterLoginConfig implements LoginConfigurer { + + private static final Logger log = LoggerFactory.getLogger(JwtUsernamePasswordFiterLoginConfig.class); + + private final JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler; + + public JwtUsernamePasswordFiterLoginConfig(JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler) { + super(); + this.jwtAuthenticationSuccessHandler = jwtAuthenticationSuccessHandler; + } + + public GenericFilterBean authenticationProcessingFilter(String loginPath, AuthenticationManager authenticationManager) throws Exception { + JwtAuthenticationFilter authenticationProcessingFilter = new JwtAuthenticationFilter(authenticationManager, jwtAuthenticationSuccessHandler); + authenticationProcessingFilter.setRequiresAuthenticationRequestMatcher(new RegexRequestMatcher(loginPath, HttpMethod.POST.name(), false)); + return authenticationProcessingFilter; + } + + @Override + public String cookieMustBeDeletedAtLogout() { + return jwtAuthenticationSuccessHandler.cookieMustBeDeletedAtLogout(); + } + + @Override + public HttpSecurity login(String loginPath, HttpSecurity http, AuthenticationManager authenticationManager) throws Exception { + log.debug("Configuring login via JwtAuthenticationFilter..."); + return http.addFilterAfter(authenticationProcessingFilter(loginPath, authenticationManager), AbstractPreAuthenticatedProcessingFilter.class); + } + +} From 0adb8bf94b27ec9237c3244b6d157fb22e7c2bba Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Thu, 29 Sep 2022 00:33:52 +0200 Subject: [PATCH 036/184] #59 fixed tests --- .../simple-trigger-config.component.html | 2 +- .../simple-trigger-config.component.spec.ts | 3 ++- .../simple-trigger-config.component.ts | 3 ++- .../trigger-list.component.spec.ts | 3 ++- .../src/app/services/auth.service.ts | 15 --------------- .../src/app/services/job.service.ts | 2 +- quartz-manager-parent/pom.xml | 18 +++++++++--------- 7 files changed, 17 insertions(+), 29 deletions(-) diff --git a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html index b29c701..3be161b 100644 --- a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html +++ b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.html @@ -25,7 +25,7 @@ class="full-size-input" > Job Class - + {{job}} diff --git a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.spec.ts b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.spec.ts index cce311a..809d258 100644 --- a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.spec.ts +++ b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.spec.ts @@ -19,6 +19,7 @@ import {JobDetail} from '../../model/jobDetail.model'; import {SimpleTriggerForm} from '../../model/simple-trigger.form'; import {SimpleTrigger} from '../../model/simple-trigger.model'; import JobService from '../../services/job.service'; +import {MatSelectModule} from '@angular/material/select'; describe('SimpleTriggerConfig', () => { @@ -30,7 +31,7 @@ describe('SimpleTriggerConfig', () => { beforeEach(async( () => { TestBed.configureTestingModule({ - imports: [FormsModule, MatFormFieldModule, MatFormFieldModule, MatInputModule, BrowserAnimationsModule, + imports: [FormsModule, MatFormFieldModule, MatFormFieldModule, MatSelectModule, MatInputModule, BrowserAnimationsModule, MatNativeDateModule, MatCardModule, MatIconModule, HttpClientTestingModule, RouterTestingModule], declarations: [SimpleTriggerConfigComponent], diff --git a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts index b304226..4c45124 100644 --- a/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts +++ b/quartz-manager-frontend/src/app/components/simple-trigger-config/simple-trigger-config.component.ts @@ -1,11 +1,12 @@ import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; -import {JobService, SchedulerService} from '../../services'; +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'; @Component({ selector: 'qrzmng-simple-trigger-config', diff --git a/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.spec.ts b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.spec.ts index 2f61fd0..dddeab5 100644 --- a/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.spec.ts +++ b/quartz-manager-frontend/src/app/components/trigger-list/trigger-list.component.spec.ts @@ -11,6 +11,7 @@ import {MatDividerModule} from '@angular/material/divider'; import {TriggerListComponent} from './trigger-list.component'; import {MatListModule} from '@angular/material/list'; import {TriggerKey} from '../../model/triggerKey.model'; +import {MatDialogModule} from '@angular/material/dialog'; describe('TriggerListComponent', () => { @@ -22,7 +23,7 @@ describe('TriggerListComponent', () => { beforeEach(async(() => { TestBed.configureTestingModule({ - imports: [MatCardModule, MatDividerModule, MatIconModule, MatListModule, HttpClientTestingModule, RouterTestingModule], + imports: [MatCardModule, MatDialogModule, MatDividerModule, MatIconModule, MatListModule, HttpClientTestingModule, RouterTestingModule], declarations: [TriggerListComponent], providers: [TriggerService, ApiService, ConfigService] }).compileComponents(); diff --git a/quartz-manager-frontend/src/app/services/auth.service.ts b/quartz-manager-frontend/src/app/services/auth.service.ts index 5520d6d..0fd771d 100644 --- a/quartz-manager-frontend/src/app/services/auth.service.ts +++ b/quartz-manager-frontend/src/app/services/auth.service.ts @@ -30,16 +30,6 @@ export class AuthService { ); } - signup(user) { - const signupHeaders = new HttpHeaders({ - 'Accept': 'application/json', - 'Content-Type': 'application/json' - }); - return this.apiService.post(this.config.signup_url, JSON.stringify(user), signupHeaders).pipe(map(() => { - console.log('Sign up success'); - })); - } - logout() { return this.apiService.post(this.config.logout_url, {}) .pipe(map(() => { @@ -48,9 +38,4 @@ export class AuthService { })); } - changePassword(passwordChanger) { - return this.apiService.post(this.config.change_password_url, passwordChanger); - } - - } diff --git a/quartz-manager-frontend/src/app/services/job.service.ts b/quartz-manager-frontend/src/app/services/job.service.ts index c5cbccc..037ed9a 100644 --- a/quartz-manager-frontend/src/app/services/job.service.ts +++ b/quartz-manager-frontend/src/app/services/job.service.ts @@ -4,7 +4,7 @@ import {CONTEXT_PATH, getBaseUrl} from './config.service'; import {Observable} from 'rxjs'; @Injectable() -export class JobService { +export default class JobService { constructor( private apiService: ApiService diff --git a/quartz-manager-parent/pom.xml b/quartz-manager-parent/pom.xml index 01b22d5..cb3da24 100644 --- a/quartz-manager-parent/pom.xml +++ b/quartz-manager-parent/pom.xml @@ -12,33 +12,33 @@ 3.1.1-SNAPSHOT pom - + Quartz Manager API and UI Manager for Quartz Scheduler - + https://github.com/fabioformosa/quartz-manager - + Apache License 2.0 https://github.com/fabioformosa/quartz-manager/blob/master/LICENSE - + scm:git:git://github.com/fabioformosa/quartz-manager.git scm:git:git@github.com:fabioformosa/quartz-manager.git https://github.com/fabioformosa/quartz-manager HEAD - + Fabio Formosa https://github.com/fabioformosa - + quartz-manager-starter-api quartz-manager-starter-ui @@ -77,7 +77,7 @@ - + ossrh @@ -89,7 +89,7 @@ - + @@ -160,7 +160,7 @@ - + From 8935d77d0f1d741cdb85ff1314d6d218a0008bee Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Thu, 29 Sep 2022 19:28:52 +0200 Subject: [PATCH 037/184] #63 extracted a constant for the quartz-manager --- .../AbstractQuartzManagerController.java | 7 +++++++ .../AbstractTriggerController.java | 5 ----- .../controllers/JobController.java | 9 +++++---- .../controllers/SchedulerController.java | 15 ++++++-------- .../controllers/SimpleTriggerController.java | 6 +++--- .../controllers/TriggerController.java | 6 +++--- .../controllers/UserController.java | 20 +++++++++++-------- .../controllers/WebsocketController.java | 6 ++++-- 8 files changed, 40 insertions(+), 34 deletions(-) create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AbstractQuartzManagerController.java delete mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AbstractTriggerController.java diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AbstractQuartzManagerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AbstractQuartzManagerController.java new file mode 100644 index 0000000..454ca75 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AbstractQuartzManagerController.java @@ -0,0 +1,7 @@ +package it.fabioformosa.quartzmanager.controllers; + +abstract public class AbstractQuartzManagerController { + + protected static final String QUARTZ_MANAGER_CONTEXT_PATH = "/quartz-manager"; + +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AbstractTriggerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AbstractTriggerController.java deleted file mode 100644 index 2d0da31..0000000 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AbstractTriggerController.java +++ /dev/null @@ -1,5 +0,0 @@ -package it.fabioformosa.quartzmanager.controllers; - -public class AbstractTriggerController { - -} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/JobController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/JobController.java index 5361818..55eaf84 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/JobController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/JobController.java @@ -8,11 +8,12 @@ import org.springframework.web.bind.annotation.RestController; import java.util.List; import java.util.stream.Collectors; -@RequestMapping("/quartz-manager/jobs") -@RestController -public class JobController extends AbstractTriggerController { +import static it.fabioformosa.quartzmanager.controllers.AbstractQuartzManagerController.QUARTZ_MANAGER_CONTEXT_PATH; - private JobService jobService; +@RequestMapping(QUARTZ_MANAGER_CONTEXT_PATH + "/jobs") +@RestController +public class JobController extends AbstractQuartzManagerController { + final private JobService jobService; public JobController(JobService jobService) { this.jobService = jobService; diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java index aa49f9f..95d658e 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SchedulerController.java @@ -10,14 +10,13 @@ import it.fabioformosa.quartzmanager.dto.SchedulerDTO; import it.fabioformosa.quartzmanager.services.SchedulerService; import lombok.extern.slf4j.Slf4j; import org.quartz.SchedulerException; -import org.springframework.core.convert.ConversionService; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; -import javax.annotation.Resource; +import static it.fabioformosa.quartzmanager.controllers.AbstractQuartzManagerController.QUARTZ_MANAGER_CONTEXT_PATH; /** * This controller provides scheduler info about config and status. It provides @@ -28,19 +27,17 @@ import javax.annotation.Resource; @Slf4j @RestController @SecurityRequirement(name = "basic-auth") -@RequestMapping("/quartz-manager/scheduler") +@RequestMapping(SchedulerController.SCHEDULER_CONTROLLER_BASE_URL) public class SchedulerController { - private SchedulerService schedulerService; + static protected final String SCHEDULER_CONTROLLER_BASE_URL = QUARTZ_MANAGER_CONTEXT_PATH + "/scheduler"; - public SchedulerController(SchedulerService schedulerService, ConversionService conversionService) { + final private SchedulerService schedulerService; + + public SchedulerController(SchedulerService schedulerService) { this.schedulerService = schedulerService; - this.conversionService = conversionService; } - @Resource - private ConversionService conversionService; - @GetMapping @Operation(summary = "Get the scheduler details") @ApiResponses(value = { diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerController.java index cd39af6..8bdf766 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/SimpleTriggerController.java @@ -23,11 +23,11 @@ import javax.validation.Valid; @RequestMapping(SimpleTriggerController.SIMPLE_TRIGGER_CONTROLLER_BASE_URL) @SecurityRequirement(name = "basic-auth") @RestController -public class SimpleTriggerController extends AbstractTriggerController { +public class SimpleTriggerController extends AbstractQuartzManagerController { - static public final String SIMPLE_TRIGGER_CONTROLLER_BASE_URL = "/quartz-manager/simple-triggers"; + static protected final String SIMPLE_TRIGGER_CONTROLLER_BASE_URL = QUARTZ_MANAGER_CONTEXT_PATH + "/simple-triggers"; - private SimpleTriggerService simpleSchedulerService; + final private SimpleTriggerService simpleSchedulerService; public SimpleTriggerController(SimpleTriggerService simpleSchedulerService) { this.simpleSchedulerService = simpleSchedulerService; diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java index 04f4a54..cd58752 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/TriggerController.java @@ -15,11 +15,11 @@ import java.util.List; @RequestMapping(TriggerController.TRIGGER_CONTROLLER_BASE_URL) @SecurityRequirement(name = "basic-auth") @RestController -public class TriggerController extends AbstractTriggerController { +public class TriggerController extends AbstractQuartzManagerController { - static public final String TRIGGER_CONTROLLER_BASE_URL = "/quartz-manager/triggers"; + static protected final String TRIGGER_CONTROLLER_BASE_URL = QUARTZ_MANAGER_CONTEXT_PATH + "/triggers"; - private TriggerService triggerService; + final private TriggerService triggerService; public TriggerController(TriggerService triggerService) { this.triggerService = triggerService; diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/UserController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/UserController.java index 64ffb37..832f544 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/UserController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/UserController.java @@ -8,16 +8,20 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; +import static it.fabioformosa.quartzmanager.controllers.AbstractQuartzManagerController.QUARTZ_MANAGER_CONTEXT_PATH; + @RestController -@RequestMapping(value = "/quartz-manager/api", produces = MediaType.APPLICATION_JSON_VALUE) +@RequestMapping(value = UserController.USER_CONTROLLER_BASE_URL, produces = MediaType.APPLICATION_JSON_VALUE) public class UserController { - @GetMapping("/whoami") - public @ResponseBody Object user() { - SecurityContext context = SecurityContextHolder.getContext(); - if(context != null && context.getAuthentication() != null) - return context.getAuthentication().getPrincipal(); - return "\"NO_AUTH\""; - } + static protected final String USER_CONTROLLER_BASE_URL = QUARTZ_MANAGER_CONTEXT_PATH + "/api"; + + @GetMapping("/whoami") + public @ResponseBody Object user() { + SecurityContext context = SecurityContextHolder.getContext(); + if (context != null && context.getAuthentication() != null) + return context.getAuthentication().getPrincipal(); + return "\"NO_AUTH\""; + } } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/WebsocketController.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/WebsocketController.java index ab53b6b..b2f3239 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/WebsocketController.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/WebsocketController.java @@ -4,12 +4,14 @@ import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.SendTo; import org.springframework.stereotype.Controller; +import static it.fabioformosa.quartzmanager.controllers.AbstractQuartzManagerController.QUARTZ_MANAGER_CONTEXT_PATH; + @Controller public class WebsocketController { - @MessageMapping({ "/quartz-manager/logs", "/quartz-manager/progress" }) + @MessageMapping({ QUARTZ_MANAGER_CONTEXT_PATH + "/logs", QUARTZ_MANAGER_CONTEXT_PATH + "/progress" }) @SendTo("/topic/logs") - public String subscribe() throws Exception { + public String subscribe() { return "subscribed"; } From 8cb0ac09e8aba618ad3579a3c7734d1a6ccb3c82 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Thu, 29 Sep 2022 19:31:19 +0200 Subject: [PATCH 038/184] #63 removed the app name which has a default value --- .../src/main/resources/application.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml b/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml index fde9f72..335fbb2 100644 --- a/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml +++ b/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml @@ -4,9 +4,6 @@ server: session.timeout : 28800 port: 8080 -app: - name: quartz-manager - spring: thymeleaf: cache: false From adb8e06f0a66a36789565ad3cf87c03d121fec83 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Fri, 30 Sep 2022 23:42:30 +0200 Subject: [PATCH 039/184] #63 added mvc tests on security --- quartz-manager-frontend/src/app/app.module.ts | 4 +- .../quartz-manager-starter-security/pom.xml | 51 ++++- .../security/WebSecurityConfigJWT.java | 184 +++++++++++++++++ .../configuration/WebSecurityConfigJWT.java | 194 ------------------ .../properties/JwtSecurityProperties.java | 33 --- .../JwtAuthenticationSuccessHandlerImpl.java | 2 +- .../security/helpers/impl/JwtTokenHelper.java | 2 +- .../security/models/Authority.java | 55 ----- .../quartzmanager/security/models/User.java | 132 ------------ .../properties/InMemoryAccountProperties.java | 2 +- .../properties/JwtSecurityProperties.java | 35 ++++ .../main/resources/META-INF/spring.factories | 8 +- .../security/AbstractSecurityLoginTest.java | 29 +++ .../security/SecurityControllerTest.java | 71 +++++++ .../security/SecurityLoginViaCookieTest.java | 40 ++++ .../SecurityLoginViaDefaultStrategyTest.java | 30 +++ ...urityLoginViaHeaderAndLoginFilterTest.java | 38 ++++ .../security/SecurityLoginViaHeaderTest.java | 37 ++++ .../security/SpringApplicationTest.java | 7 + .../security/controllers/TestController.java | 33 +++ .../quartz-manager-web-showcase/pom.xml | 14 +- .../src/main/resources/application.yml | 2 - 22 files changed, 559 insertions(+), 444 deletions(-) create mode 100644 quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/WebSecurityConfigJWT.java delete mode 100644 quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/configuration/WebSecurityConfigJWT.java delete mode 100644 quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/configuration/properties/JwtSecurityProperties.java delete mode 100644 quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/models/Authority.java delete mode 100644 quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/models/User.java rename quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/{configuration => }/properties/InMemoryAccountProperties.java (89%) create mode 100644 quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/properties/JwtSecurityProperties.java create mode 100644 quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/AbstractSecurityLoginTest.java create mode 100644 quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityControllerTest.java create mode 100644 quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaCookieTest.java create mode 100644 quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaDefaultStrategyTest.java create mode 100644 quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaHeaderAndLoginFilterTest.java create mode 100644 quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaHeaderTest.java create mode 100644 quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SpringApplicationTest.java create mode 100644 quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/controllers/TestController.java diff --git a/quartz-manager-frontend/src/app/app.module.ts b/quartz-manager-frontend/src/app/app.module.ts index 939bdf1..f0fbfb7 100644 --- a/quartz-manager-frontend/src/app/app.module.ts +++ b/quartz-manager-frontend/src/app/app.module.ts @@ -51,18 +51,17 @@ import { AuthService, UserService, SchedulerService, - JobService, ConfigService, ProgressWebsocketService, LogsWebsocketService, getHtmlBaseUrl, TriggerService } from './services'; -import { ChangePasswordComponent } from './views/change-password/change-password.component'; import { ForbiddenComponent } from './views/forbidden/forbidden.component'; import { APP_BASE_HREF } from '@angular/common'; import { environment } from '../environments/environment'; import {SimpleTriggerConfigComponent} from './components/simple-trigger-config'; +import JobService from './services/job.service'; export function initUserFactory(userService: UserService) { return () => userService.jsessionInitUser(); @@ -116,7 +115,6 @@ export function jwtOptionsFactory(apiService: ApiService) { SchedulerControlComponent, LogsPanelComponent, ProgressPanelComponent, - ChangePasswordComponent, ForbiddenComponent, TriggerListComponent ], diff --git a/quartz-manager-parent/quartz-manager-starter-security/pom.xml b/quartz-manager-parent/quartz-manager-starter-security/pom.xml index 5d80594..bda80c3 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/pom.xml +++ b/quartz-manager-parent/quartz-manager-starter-security/pom.xml @@ -1,16 +1,17 @@ - + 4.0.0 it.fabioformosa.quartz-manager quartz-manager-parent 3.1.1-SNAPSHOT - + quartz-manager-starter-security - + Quartz Manager Starter Security Security Layer for Quartz Manager. Import it in your spring webapp - + https://github.com/fabioformosa/quartz-manager ${basedir}/../.. @@ -18,16 +19,12 @@ UTF-8 1.8 - + org.springframework.boot spring-boot-starter-security - - org.springframework.boot - spring-boot-starter-data-jpa - org.springframework.boot spring-boot-configuration-processor @@ -51,7 +48,39 @@ javax.servlet javax.servlet-api provided - + + + + + org.junit.platform + junit-platform-launcher + test + + + org.springframework.boot + spring-boot-test-autoconfigure + test + + + org.springframework.security + spring-security-test + test + + + org.springframework + spring-test + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-web + test + - + diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/WebSecurityConfigJWT.java b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/WebSecurityConfigJWT.java new file mode 100644 index 0000000..80a5631 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/WebSecurityConfigJWT.java @@ -0,0 +1,184 @@ +package it.fabioformosa.quartzmanager.security; + +import org.apache.commons.lang3.BooleanUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.crypto.factory.PasswordEncoderFactories; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.AuthenticationEntryPoint; +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 com.fasterxml.jackson.databind.ObjectMapper; + +import it.fabioformosa.quartzmanager.security.properties.InMemoryAccountProperties; +import it.fabioformosa.quartzmanager.security.properties.JwtSecurityProperties; +import it.fabioformosa.quartzmanager.security.helpers.LoginConfigurer; +import it.fabioformosa.quartzmanager.security.helpers.impl.AuthenticationFailureHandler; +import it.fabioformosa.quartzmanager.security.helpers.impl.AuthenticationSuccessHandler; +import it.fabioformosa.quartzmanager.security.helpers.impl.FormLoginConfig; +import it.fabioformosa.quartzmanager.security.helpers.impl.JwtAuthenticationSuccessHandler; +import it.fabioformosa.quartzmanager.security.helpers.impl.JwtAuthenticationSuccessHandlerImpl; +import it.fabioformosa.quartzmanager.security.helpers.impl.JwtTokenAuthenticationFilter; +import it.fabioformosa.quartzmanager.security.helpers.impl.JwtTokenHelper; +import it.fabioformosa.quartzmanager.security.helpers.impl.JwtUsernamePasswordFiterLoginConfig; +import it.fabioformosa.quartzmanager.security.helpers.impl.LogoutSuccess; +import it.fabioformosa.quartzmanager.security.helpers.impl.QuartzManagerHttpSecurity; + +/** + * @author Fabio.Formosa + */ +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class WebSecurityConfigJWT extends WebSecurityConfigurerAdapter { + + private static final String[] PATTERNS_SWAGGER_UI = {"/swagger-ui.html", "/v3/api-docs/**", "/swagger-resources/**", "/webjars/**"}; + + public static final String QUARTZ_MANAGER_CONTEXT_PATH = "/quartz-manager"; + protected static final String LOGIN_PATH = QUARTZ_MANAGER_CONTEXT_PATH + "/api/login"; + private static final String LOGOUT_PATH = QUARTZ_MANAGER_CONTEXT_PATH + "/api/logout"; + + private static final String WEBJAR_PATH = "/quartz-manager-ui"; + + @Value("${server.servlet.context-path:/}") + private String contextPath; + + @Value("${app.name:quartz-manager}") + private String appName; + + @Value("${quartz-manager.security.login-model.form-login-enabled:true}") + private Boolean formLoginEnabled; + @Value("${quartz-manager.security.login-model.userpwd-filter-enabled:false}") + private Boolean userpwdFilterEnabled; + + @Autowired + private JwtSecurityProperties jwtSecurityProps; + + @Autowired + private ObjectMapper objectMapper; + + @Autowired + private UserDetailsService userDetailsService; + + @Autowired + private InMemoryAccountProperties inMemoryAccountProps; + + + @Override + public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { + configureInMemoryAuthentication(authenticationManagerBuilder); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.csrf().disable() // + .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() // + .exceptionHandling().authenticationEntryPoint(restAuthEntryPoint()).and() // + .addFilterBefore(jwtAuthenticationTokenFilter(), BasicAuthenticationFilter.class) // + .authorizeRequests().anyRequest().authenticated(); + + QuartzManagerHttpSecurity.from(http).withLoginConfigurer(loginConfigurer(), logoutConfigurer()) // + .login(LOGIN_PATH, authenticationManager()).logout(LOGOUT_PATH); + + // temporary disabled csfr + // http.csrf().ignoringAntMatchers("/api/login", "/api/signup") // + // .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) // + } + + @Override + public void configure(WebSecurity web) { + web.ignoring()// + .antMatchers(HttpMethod.GET, PATTERNS_SWAGGER_UI) // + .antMatchers(HttpMethod.GET, WEBJAR_PATH + "/css/**", WEBJAR_PATH + "/js/**", WEBJAR_PATH + "/img/**", WEBJAR_PATH + "/lib/**", WEBJAR_PATH + "/assets/**"); + } + + private void configureInMemoryAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { + PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); + if (inMemoryAccountProps.isEnabled() && inMemoryAccountProps.getUsers() != null && !inMemoryAccountProps.getUsers().isEmpty()) { + InMemoryUserDetailsManagerConfigurer inMemoryAuth = authenticationManagerBuilder.inMemoryAuthentication(); + inMemoryAccountProps.getUsers() + .forEach(u -> inMemoryAuth + .withUser(u.getName()) + .password(encoder.encode(u.getPassword())) + .roles(u.getRoles().toArray(new String[0]))); + } + } + + @Bean + CorsConfigurationSource corsConfigurationSource() { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues()); + return source; + } + + public LoginConfigurer formLoginConfigurer() { + JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler = jwtAuthenticationSuccessHandler(); + AuthenticationSuccessHandler authenticationSuccessHandler = new AuthenticationSuccessHandler(jwtAuthenticationSuccessHandler); + AuthenticationFailureHandler authenticationFailureHandler = new AuthenticationFailureHandler(); + LoginConfigurer loginConfigurer = new FormLoginConfig(authenticationSuccessHandler, authenticationFailureHandler); + return loginConfigurer; + } + + @Bean + public JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler() { + JwtTokenHelper jwtTokenHelper = jwtTokenHelper(); + JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler = new JwtAuthenticationSuccessHandlerImpl(contextPath, jwtSecurityProps, jwtTokenHelper, objectMapper); + return jwtAuthenticationSuccessHandler; + } + +// @Bean + public JwtTokenAuthenticationFilter jwtAuthenticationTokenFilter() { + return new JwtTokenAuthenticationFilter(jwtTokenHelper(), userDetailsService); + } + + @Bean + public JwtTokenHelper jwtTokenHelper() { + return new JwtTokenHelper(appName, jwtSecurityProps); + } + + public LoginConfigurer loginConfigurer() { + if (BooleanUtils.isTrue(userpwdFilterEnabled)) + return userpwdFilterLoginConfigurer(); + if (BooleanUtils.isNotFalse(formLoginEnabled)) + return formLoginConfigurer(); + throw new RuntimeException("No login configurer enabled!"); + } + + public LogoutSuccess logoutConfigurer() { + return new LogoutSuccess(objectMapper); + } + + @Bean + public AuthenticationEntryPoint restAuthEntryPoint() { + return new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED); + } + + @Bean + @Override + public UserDetailsService userDetailsServiceBean() throws Exception { + return super.userDetailsServiceBean(); + } + + public LoginConfigurer userpwdFilterLoginConfigurer() { + LoginConfigurer loginConfigurer = new JwtUsernamePasswordFiterLoginConfig(jwtAuthenticationSuccessHandler()); + return loginConfigurer; + } + +} diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/configuration/WebSecurityConfigJWT.java b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/configuration/WebSecurityConfigJWT.java deleted file mode 100644 index acd4b43..0000000 --- a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/configuration/WebSecurityConfigJWT.java +++ /dev/null @@ -1,194 +0,0 @@ -package it.fabioformosa.quartzmanager.security.configuration; - -import org.apache.commons.lang3.BooleanUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.builders.WebSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.crypto.factory.PasswordEncoderFactories; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.web.AuthenticationEntryPoint; -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 com.fasterxml.jackson.databind.ObjectMapper; - -import it.fabioformosa.quartzmanager.security.configuration.properties.InMemoryAccountProperties; -import it.fabioformosa.quartzmanager.security.configuration.properties.JwtSecurityProperties; -import it.fabioformosa.quartzmanager.security.helpers.LoginConfigurer; -import it.fabioformosa.quartzmanager.security.helpers.impl.AuthenticationFailureHandler; -import it.fabioformosa.quartzmanager.security.helpers.impl.AuthenticationSuccessHandler; -import it.fabioformosa.quartzmanager.security.helpers.impl.FormLoginConfig; -import it.fabioformosa.quartzmanager.security.helpers.impl.JwtAuthenticationSuccessHandler; -import it.fabioformosa.quartzmanager.security.helpers.impl.JwtAuthenticationSuccessHandlerImpl; -import it.fabioformosa.quartzmanager.security.helpers.impl.JwtTokenAuthenticationFilter; -import it.fabioformosa.quartzmanager.security.helpers.impl.JwtTokenHelper; -import it.fabioformosa.quartzmanager.security.helpers.impl.JwtUsernamePasswordFiterLoginConfig; -import it.fabioformosa.quartzmanager.security.helpers.impl.LogoutSuccess; -import it.fabioformosa.quartzmanager.security.helpers.impl.QuartzManagerHttpSecurity; - -/** - * - * @author Fabio.Formosa - * - */ -@Configuration -@EnableWebSecurity -@EnableGlobalMethodSecurity(prePostEnabled = true) -public class WebSecurityConfigJWT extends WebSecurityConfigurerAdapter { - - private static final String[] PATTERNS_SWAGGER_UI = {"/swagger-ui.html", "/v3/api-docs/**", "/swagger-resources/**", "/webjars/**"}; - - private static final String LOGIN_PATH = "/quartz-manager/api/login"; - private static final String LOGOUT_PATH = "/quartz-manager/api/logout"; - - private static final String WEBJAR_PATH = "/quartz-manager-ui"; - - @Value("${server.servlet.context-path:/}") - private String contextPath; - - @Value("${app.name:quartz-manager}") - private String appName; - - @Value("${quartz-manager.security.login-model.form-login-enabled}") - private Boolean formLoginEnabled; - @Value("${quartz-manager.security.login-model.userpwd-filter-enabled}") - private Boolean userpwdFilterEnabled; - - @Autowired - private JwtSecurityProperties jwtSecurityProps; - - @Autowired - private ObjectMapper objectMapper; - - @Autowired - private UserDetailsService userDetailsService; - - @Autowired - private InMemoryAccountProperties inMemoryAccountProps; - - - @Override - public void configure(AuthenticationManagerBuilder authenticationManagerBuilder)throws Exception { - configureInMemoryAuthentication(authenticationManagerBuilder); - } - - @Override - protected void configure(HttpSecurity http) throws Exception { - http.csrf().disable() // - .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() // - .exceptionHandling().authenticationEntryPoint(restAuthEntryPoint()).and() // - .addFilterBefore(jwtAuthenticationTokenFilter(), BasicAuthenticationFilter.class) // - .authorizeRequests().anyRequest().authenticated(); - - QuartzManagerHttpSecurity.from(http).withLoginConfigurer(loginConfigurer(), logoutConfigurer()) // - .login(LOGIN_PATH, authenticationManager()).logout(LOGOUT_PATH); - - // temporary disabled csfr - // http.csrf().ignoringAntMatchers("/api/login", "/api/signup") // - // .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) // - } - - @Override - public void configure(WebSecurity web) { - web.ignoring()// - .antMatchers(HttpMethod.GET, PATTERNS_SWAGGER_UI) // - .antMatchers(HttpMethod.GET, WEBJAR_PATH + "/css/**", WEBJAR_PATH + "/js/**", WEBJAR_PATH + "/img/**", WEBJAR_PATH + "/lib/**", WEBJAR_PATH + "/assets/**"); - } - - private void configureInMemoryAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { - PasswordEncoder encoder = PasswordEncoderFactories.createDelegatingPasswordEncoder(); - if(inMemoryAccountProps.isEnabled() && inMemoryAccountProps.getUsers() != null && !inMemoryAccountProps.getUsers().isEmpty()) { - InMemoryUserDetailsManagerConfigurer inMemoryAuth = authenticationManagerBuilder.inMemoryAuthentication(); - inMemoryAccountProps.getUsers() - .forEach(u -> inMemoryAuth - .withUser(u.getName()) - .password(encoder.encode(u.getPassword())) - .roles(u.getRoles().toArray(new String[0]))); - } - } - - @Bean - CorsConfigurationSource corsConfigurationSource() { - UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues()); - return source; - } - - @Bean - public LoginConfigurer formLoginConfigurer() { - JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler = jwtAuthenticationSuccessHandler(); - AuthenticationSuccessHandler authenticationSuccessHandler = new AuthenticationSuccessHandler(jwtAuthenticationSuccessHandler); - AuthenticationFailureHandler authenticationFailureHandler = new AuthenticationFailureHandler(); - LoginConfigurer loginConfigurer = new FormLoginConfig(authenticationSuccessHandler, authenticationFailureHandler); - return loginConfigurer; - } - - @Bean - public JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler() { - JwtTokenHelper jwtTokenHelper = jwtTokenHelper(); - JwtAuthenticationSuccessHandler jwtAuthenticationSuccessHandler = new JwtAuthenticationSuccessHandlerImpl(contextPath, jwtSecurityProps, jwtTokenHelper, objectMapper); - return jwtAuthenticationSuccessHandler; - } - - @Bean - public JwtTokenAuthenticationFilter jwtAuthenticationTokenFilter() { - return new JwtTokenAuthenticationFilter(jwtTokenHelper(), userDetailsService); - } - - @Bean - public JwtTokenHelper jwtTokenHelper() { - return new JwtTokenHelper(appName, jwtSecurityProps); - } - - @Bean - public LoginConfigurer loginConfigurer() { - if(BooleanUtils.isTrue(userpwdFilterEnabled)) - return userpwdFilterLoginConfigurer(); - if(BooleanUtils.isNotFalse(formLoginEnabled)) - return formLoginConfigurer(); - throw new RuntimeException("No login configurer enabled!"); - } - - @Bean - public LogoutSuccess logoutConfigurer() { - return new LogoutSuccess(objectMapper); - } - - @Bean - public AuthenticationEntryPoint restAuthEntryPoint() { - return new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED); - } - - @Bean - @Override - public UserDetailsService userDetailsServiceBean() throws Exception { - return super.userDetailsServiceBean(); - } - - @Bean - public LoginConfigurer userpwdFilterLoginConfigurer() { - LoginConfigurer loginConfigurer = new JwtUsernamePasswordFiterLoginConfig(jwtAuthenticationSuccessHandler()); - return loginConfigurer; - } - - // @Bean - // public PasswordEncoder passwordEncoder() { - // return new BCryptPasswordEncoder(); - // } - -} diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/configuration/properties/JwtSecurityProperties.java b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/configuration/properties/JwtSecurityProperties.java deleted file mode 100644 index 1afec00..0000000 --- a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/configuration/properties/JwtSecurityProperties.java +++ /dev/null @@ -1,33 +0,0 @@ -package it.fabioformosa.quartzmanager.security.configuration.properties; - -import lombok.Data; -import lombok.Getter; -import lombok.Setter; -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.context.annotation.Configuration; - - -@Configuration -@ConfigurationProperties(prefix = "quartz-manager.security.jwt") -@Getter @Setter -public class JwtSecurityProperties { - private boolean enabled; - private String secret; - private long expirationInSec; - - private CookieStrategy cookieStrategy; - private HeaderStrategy headerStrategy; - - @Data - public static class CookieStrategy { - private boolean enabled; - private String cookie; - } - - @Data - public static class HeaderStrategy { - private boolean enabled; - private String header; - } - -} \ No newline at end of file diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtAuthenticationSuccessHandlerImpl.java b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtAuthenticationSuccessHandlerImpl.java index 8631920..a11fbe8 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtAuthenticationSuccessHandlerImpl.java +++ b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtAuthenticationSuccessHandlerImpl.java @@ -1,7 +1,7 @@ package it.fabioformosa.quartzmanager.security.helpers.impl; import com.fasterxml.jackson.databind.ObjectMapper; -import it.fabioformosa.quartzmanager.security.configuration.properties.JwtSecurityProperties; +import it.fabioformosa.quartzmanager.security.properties.JwtSecurityProperties; import it.fabioformosa.quartzmanager.security.models.UserTokenState; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtTokenHelper.java b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtTokenHelper.java index fdaaf0c..433bf6d 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtTokenHelper.java +++ b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/helpers/impl/JwtTokenHelper.java @@ -3,7 +3,7 @@ package it.fabioformosa.quartzmanager.security.helpers.impl; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; -import it.fabioformosa.quartzmanager.security.configuration.properties.JwtSecurityProperties; +import it.fabioformosa.quartzmanager.security.properties.JwtSecurityProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/models/Authority.java b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/models/Authority.java deleted file mode 100644 index 6638a16..0000000 --- a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/models/Authority.java +++ /dev/null @@ -1,55 +0,0 @@ -package it.fabioformosa.quartzmanager.security.models; - -import javax.persistence.Column; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; - -import org.springframework.security.core.GrantedAuthority; - -import com.fasterxml.jackson.annotation.JsonIgnore; - -/** - * Temporary enabled only inMemoryAuthentication - * - * @author Fabio.Formosa - * - */ -//@Entity -//@Table(name="Authority") -public class Authority implements GrantedAuthority { - - private static final long serialVersionUID = 1L; - - @Id - @Column(name="id") - @GeneratedValue(strategy = GenerationType.IDENTITY) - Long id; - - @Column(name="name") - String name; - - @Override - public String getAuthority() { - return name; - } - - @JsonIgnore - public Long getId() { - return id; - } - - @JsonIgnore - public String getName() { - return name; - } - - public void setId(Long id) { - this.id = id; - } - - public void setName(String name) { - this.name = name; - } - -} diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/models/User.java b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/models/User.java deleted file mode 100644 index 622c311..0000000 --- a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/models/User.java +++ /dev/null @@ -1,132 +0,0 @@ -package it.fabioformosa.quartzmanager.security.models; - -import java.io.Serializable; -import java.util.Collection; -import java.util.List; - -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.JoinTable; -import javax.persistence.ManyToMany; - -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; - -import com.fasterxml.jackson.annotation.JsonIgnore; - -/** - * Temporary enabled only inMemoryAuthentication - * - * @author Fabio.Formosa - * - */ -//@Entity -//@Table(name = "USER") -public class User implements UserDetails, Serializable { - private static final long serialVersionUID = 1L; - - @Id - @Column(name = "id") - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @Column(name = "username") - private String username; - - @JsonIgnore - @Column(name = "password") - private String password; - - @Column(name = "firstname") - private String firstname; - - @Column(name = "lastname") - private String lastname; - - @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER) - @JoinTable(name = "user_authority", joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "authority_id", referencedColumnName = "id")) - private List authorities; - - @Override - public Collection getAuthorities() { - return authorities; - } - - public String getFirstname() { - return firstname; - } - - public Long getId() { - return id; - } - - public String getLastname() { - return lastname; - } - - @Override - public String getPassword() { - return password; - } - - @Override - public String getUsername() { - return username; - } - - // We can add the below fields in the users table. - // For now, they are hardcoded. - @JsonIgnore - @Override - public boolean isAccountNonExpired() { - return true; - } - - @JsonIgnore - @Override - public boolean isAccountNonLocked() { - return true; - } - - @JsonIgnore - @Override - public boolean isCredentialsNonExpired() { - return true; - } - - @JsonIgnore - @Override - public boolean isEnabled() { - return true; - } - - public void setAuthorities(List authorities) { - this.authorities = authorities; - } - - public void setFirstname(String firstname) { - this.firstname = firstname; - } - - public void setId(Long id) { - this.id = id; - } - - public void setLastname(String lastname) { - - this.lastname = lastname; - } - - public void setPassword(String password) { - this.password = password; - } - - public void setUsername(String username) { - this.username = username; - } -} diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/configuration/properties/InMemoryAccountProperties.java b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/properties/InMemoryAccountProperties.java similarity index 89% rename from quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/configuration/properties/InMemoryAccountProperties.java rename to quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/properties/InMemoryAccountProperties.java index 279a079..b6ffdaf 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/configuration/properties/InMemoryAccountProperties.java +++ b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/properties/InMemoryAccountProperties.java @@ -1,4 +1,4 @@ -package it.fabioformosa.quartzmanager.security.configuration.properties; +package it.fabioformosa.quartzmanager.security.properties; import lombok.Getter; import lombok.Setter; diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/properties/JwtSecurityProperties.java b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/properties/JwtSecurityProperties.java new file mode 100644 index 0000000..967f75f --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/properties/JwtSecurityProperties.java @@ -0,0 +1,35 @@ +package it.fabioformosa.quartzmanager.security.properties; + +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; + + +@Configuration +@ConfigurationProperties(prefix = "quartz-manager.security.jwt") +@Getter +@Setter +public class JwtSecurityProperties { + private boolean enabled = true; + private String secret = RandomStringUtils.randomAlphabetic(10); + private long expirationInSec = 28800; + + private CookieStrategy cookieStrategy = new CookieStrategy(); + private HeaderStrategy headerStrategy = new HeaderStrategy(); + + @Data + public static class CookieStrategy { + private boolean enabled = false; + private String cookie = "AUTH-TOKEN"; + } + + @Data + public static class HeaderStrategy { + private boolean enabled = true; + private String header = "Authorization"; + } + +} diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/main/resources/META-INF/spring.factories b/quartz-manager-parent/quartz-manager-starter-security/src/main/resources/META-INF/spring.factories index 9e6be3c..b1e78c3 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/main/resources/META-INF/spring.factories +++ b/quartz-manager-parent/quartz-manager-starter-security/src/main/resources/META-INF/spring.factories @@ -1,4 +1,4 @@ -org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -it.fabioformosa.quartzmanager.security.configuration.WebSecurityConfigJWT,\ -it.fabioformosa.quartzmanager.security.configuration.properties.JwtSecurityProperties,\ -it.fabioformosa.quartzmanager.security.configuration.properties.InMemoryAccountProperties \ No newline at end of file +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +it.fabioformosa.quartzmanager.security.WebSecurityConfigJWT,\ +it.fabioformosa.quartzmanager.security.properties.JwtSecurityProperties,\ +it.fabioformosa.quartzmanager.security.properties.InMemoryAccountProperties diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/AbstractSecurityLoginTest.java b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/AbstractSecurityLoginTest.java new file mode 100644 index 0000000..315492e --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/AbstractSecurityLoginTest.java @@ -0,0 +1,29 @@ +package it.fabioformosa.quartzmanager.security; + +import it.fabioformosa.quartzmanager.security.models.UserTokenState; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.*; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +import static it.fabioformosa.quartzmanager.security.WebSecurityConfigJWT.LOGIN_PATH; + +public class AbstractSecurityLoginTest { + @Autowired + private TestRestTemplate testRestTemplate; + + protected ResponseEntity doLogin() { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED); + + MultiValueMap map = new LinkedMultiValueMap<>(); + map.add("username", "foo"); + map.add("password", "bar"); + + HttpEntity> entity = new HttpEntity<>(map, headers); + + ResponseEntity responseEntity = testRestTemplate.exchange(LOGIN_PATH, HttpMethod.POST, entity, UserTokenState.class); + return responseEntity; + } +} diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityControllerTest.java b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityControllerTest.java new file mode 100644 index 0000000..0ca06f8 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityControllerTest.java @@ -0,0 +1,71 @@ +package it.fabioformosa.quartzmanager.security; + +import it.fabioformosa.quartzmanager.security.controllers.TestController; +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.security.WebSecurityConfigJWT.LOGIN_PATH; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@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.header-strategy.enabled=false", + "quartz-manager.security.jwt.header-strategy.header=Authorization", + "quartz-manager.security.jwt.cookie-strategy.enabled=true", + "quartz-manager.security.jwt.cookie-strategy.cookie=AUTH-TOKEN", + "quartz-manager.accounts.in-memory.enabled=true", + "quartz-manager.accounts.in-memory.users[0].name=foo", + "quartz-manager.accounts.in-memory.users[0].password=bar", + "quartz-manager.accounts.in-memory.users[0].roles[0]=admin", +}) +public class SecurityControllerTest { + + @Autowired + private MockMvc mockMvc; + + @Test + void givenAnAnonymousUser_whenCalledATestController_thenShouldRaiseForbidden() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/test")) + .andExpect(status().isUnauthorized()); + } + + @Test + void givenAnAnonymousUser_whenCalledATestScheduler_thenShouldRaiseForbidden() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get(TestController.QUARTZ_MANAGER + "/scheduler")) + .andExpect(status().isUnauthorized()); + } + + @Test + void givenAnAnonymousUser_whenRequestedSwaggerResource_thenShouldReturn2xx() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get("/swagger-ui.html")) + .andExpect(status().isOk()); + } + + @Test + @WithMockUser("admin") + void givenAnUser_whenCalledATestScheduler_thenShouldReturn2xx() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get(TestController.QUARTZ_MANAGER + "/scheduler")) + .andExpect(status().isOk()); + } + + @Test + void givenAnAnonymousUser_whenCalledTheLoginPath_thenShouldReturn2xx() throws Exception { + mockMvc.perform(MockMvcRequestBuilders.post(LOGIN_PATH) + .contentType("application/x-www-form-urlencoded") + .accept("application/json") + .param("username", "foo") + .param("password", "bar")) + .andExpect(status().isOk()); + } + +} diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaCookieTest.java b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaCookieTest.java new file mode 100644 index 0000000..c04cc9e --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaCookieTest.java @@ -0,0 +1,40 @@ +package it.fabioformosa.quartzmanager.security; + +import it.fabioformosa.quartzmanager.security.models.UserTokenState; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.TestPropertySource; + + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@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=false", + "quartz-manager.security.jwt.header-strategy.header=Authorization", + "quartz-manager.security.jwt.cookie-strategy.enabled=true", + "quartz-manager.security.jwt.cookie-strategy.cookie=AUTH-TOKEN", + "quartz-manager.accounts.in-memory.enabled=true", + "quartz-manager.accounts.in-memory.users[0].name=foo", + "quartz-manager.accounts.in-memory.users[0].password=bar", + "quartz-manager.accounts.in-memory.users[0].roles[0]=admin", +}) +public class SecurityLoginViaCookieTest extends AbstractSecurityLoginTest { + + @Test + void givenAnAnonymousUser_whenTheLoginIsSubmitted_thenShouldReturn2xx() { + ResponseEntity responseEntity = doLogin(); + Assertions.assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK); + Assertions.assertThat(responseEntity.getBody().getAccess_token()).isNotEmpty(); + Assertions.assertThat(responseEntity.getBody().getExpires_in_sec()).isNotNull().isPositive(); + Assertions.assertThat(responseEntity.getHeaders().get("set-cookie")).hasSizeGreaterThan(0); + Assertions.assertThat(responseEntity.getHeaders().get("set-cookie").get(0)).startsWith("AUTH-TOKEN"); + } + +} diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaDefaultStrategyTest.java b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaDefaultStrategyTest.java new file mode 100644 index 0000000..6f0a6ab --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaDefaultStrategyTest.java @@ -0,0 +1,30 @@ +package it.fabioformosa.quartzmanager.security; + +import it.fabioformosa.quartzmanager.security.models.UserTokenState; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.TestPropertySource; + + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@TestPropertySource(properties = { + "quartz-manager.accounts.in-memory.enabled=true", + "quartz-manager.accounts.in-memory.users[0].name=foo", + "quartz-manager.accounts.in-memory.users[0].password=bar", + "quartz-manager.accounts.in-memory.users[0].roles[0]=admin", +}) +public class SecurityLoginViaDefaultStrategyTest extends AbstractSecurityLoginTest { + + @Test + void givenAnAnonymousUser_whenTheLoginIsSubmitted_thenShouldReturn2xx() { + ResponseEntity responseEntity = doLogin(); + Assertions.assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK); + Assertions.assertThat(responseEntity.getBody().getAccess_token()).isNotEmpty(); + Assertions.assertThat(responseEntity.getBody().getExpires_in_sec()).isNotNull().isPositive(); + Assertions.assertThat(responseEntity.getHeaders().get("set-cookie")).isNull(); + } + +} diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaHeaderAndLoginFilterTest.java b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaHeaderAndLoginFilterTest.java new file mode 100644 index 0000000..8ae21b8 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaHeaderAndLoginFilterTest.java @@ -0,0 +1,38 @@ +package it.fabioformosa.quartzmanager.security; + +import it.fabioformosa.quartzmanager.security.models.UserTokenState; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.TestPropertySource; + + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@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", + "quartz-manager.security.jwt.header-strategy.header=Authorization", + "quartz-manager.security.jwt.cookie-strategy.enabled=false", + "quartz-manager.accounts.in-memory.enabled=true", + "quartz-manager.accounts.in-memory.users[0].name=foo", + "quartz-manager.accounts.in-memory.users[0].password=bar", + "quartz-manager.accounts.in-memory.users[0].roles[0]=admin", +}) +public class SecurityLoginViaHeaderAndLoginFilterTest extends AbstractSecurityLoginTest { + + @Test + void givenAnAnonymousUser_whenTheLoginIsSubmitted_thenShouldReturn2xx() { + ResponseEntity responseEntity = doLogin(); + Assertions.assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK); + Assertions.assertThat(responseEntity.getBody().getAccess_token()).isNotEmpty(); + Assertions.assertThat(responseEntity.getBody().getExpires_in_sec()).isNotNull().isPositive(); + Assertions.assertThat(responseEntity.getHeaders().get("set-cookie")).isNull(); + } + +} diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaHeaderTest.java b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaHeaderTest.java new file mode 100644 index 0000000..38c610e --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaHeaderTest.java @@ -0,0 +1,37 @@ +package it.fabioformosa.quartzmanager.security; + +import it.fabioformosa.quartzmanager.security.models.UserTokenState; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.*; +import org.springframework.test.context.TestPropertySource; + + +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +@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", + "quartz-manager.security.jwt.header-strategy.header=Authorization", + "quartz-manager.security.jwt.cookie-strategy.enabled=false", + "quartz-manager.accounts.in-memory.enabled=true", + "quartz-manager.accounts.in-memory.users[0].name=foo", + "quartz-manager.accounts.in-memory.users[0].password=bar", + "quartz-manager.accounts.in-memory.users[0].roles[0]=admin", +}) +public class SecurityLoginViaHeaderTest extends AbstractSecurityLoginTest { + + @Test + void givenAnAnonymousUser_whenTheLoginIsSubmitted_thenShouldReturn2xx() { + ResponseEntity responseEntity = doLogin(); + Assertions.assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK); + Assertions.assertThat(responseEntity.getBody().getAccess_token()).isNotEmpty(); + Assertions.assertThat(responseEntity.getBody().getExpires_in_sec()).isNotNull().isPositive(); + Assertions.assertThat(responseEntity.getHeaders().get("set-cookie")).isNull(); + } + +} diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SpringApplicationTest.java b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SpringApplicationTest.java new file mode 100644 index 0000000..5472bbf --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SpringApplicationTest.java @@ -0,0 +1,7 @@ +package it.fabioformosa.quartzmanager.security; + +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SpringApplicationTest { +} diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/controllers/TestController.java b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/controllers/TestController.java new file mode 100644 index 0000000..a5a8f49 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/controllers/TestController.java @@ -0,0 +1,33 @@ +package it.fabioformosa.quartzmanager.security.controllers; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; + +@RequestMapping +@RestController +public class TestController { + + public static final String QUARTZ_MANAGER = "/quartz-manager"; + + @ResponseStatus(HttpStatus.OK) + @GetMapping("/test") + public void getDMZTest(){ + + } + + @ResponseStatus(HttpStatus.OK) + @GetMapping("/swagger-ui.html") + public void getSwaggerUI(){ + + } + + @ResponseStatus(HttpStatus.OK) + @GetMapping(QUARTZ_MANAGER + "/scheduler") + public void getQuartzManagerScheduler(){ + + } + +} diff --git a/quartz-manager-parent/quartz-manager-web-showcase/pom.xml b/quartz-manager-parent/quartz-manager-web-showcase/pom.xml index 85b85d7..abf65c8 100644 --- a/quartz-manager-parent/quartz-manager-web-showcase/pom.xml +++ b/quartz-manager-parent/quartz-manager-web-showcase/pom.xml @@ -9,7 +9,7 @@ quartz-manager-web-showcase - + war Quartz Manager Web Showcase @@ -21,7 +21,7 @@ 2.9.2 1.8 - + it.fabioformosa.quartz-manager @@ -39,7 +39,7 @@ - + org.springframework.boot @@ -68,7 +68,7 @@ spring-boot-starter-test test - + io.jsonwebtoken @@ -118,8 +118,8 @@ test - - + + @@ -146,6 +146,6 @@ - + diff --git a/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml b/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml index 335fbb2..cd64605 100644 --- a/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml +++ b/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml @@ -26,8 +26,6 @@ logging: org.quartz: INFO quartz-manager: - trigger: - name: "sampletrigger" persistence: quartz: datasource: From 94107f2210588a71d636a452f211358fb740a321 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Sat, 1 Oct 2022 00:40:58 +0200 Subject: [PATCH 040/184] #63 removed properties which have a default value from the showcase --- .../properties/InMemoryAccountProperties.java | 2 +- .../security/SecurityControllerTest.java | 8 +-- .../security/SecurityLoginViaCookieTest.java | 8 +-- .../SecurityLoginViaDefaultStrategyTest.java | 8 +-- ...urityLoginViaHeaderAndLoginFilterTest.java | 8 +-- .../security/SecurityLoginViaHeaderTest.java | 8 +-- .../src/main/resources/application.yml | 62 +++++++------------ 7 files changed, 45 insertions(+), 59 deletions(-) diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/properties/InMemoryAccountProperties.java b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/properties/InMemoryAccountProperties.java index b6ffdaf..583bc0f 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/properties/InMemoryAccountProperties.java +++ b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/properties/InMemoryAccountProperties.java @@ -9,7 +9,7 @@ import java.util.ArrayList; import java.util.List; @Configuration -@ConfigurationProperties(prefix = "quartz-manager.accounts.in-memory") +@ConfigurationProperties(prefix = "quartz-manager.security.accounts.in-memory") @Getter @Setter public class InMemoryAccountProperties { private boolean enabled; diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityControllerTest.java b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityControllerTest.java index 0ca06f8..39ede6a 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityControllerTest.java +++ b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityControllerTest.java @@ -23,10 +23,10 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. "quartz-manager.security.jwt.header-strategy.header=Authorization", "quartz-manager.security.jwt.cookie-strategy.enabled=true", "quartz-manager.security.jwt.cookie-strategy.cookie=AUTH-TOKEN", - "quartz-manager.accounts.in-memory.enabled=true", - "quartz-manager.accounts.in-memory.users[0].name=foo", - "quartz-manager.accounts.in-memory.users[0].password=bar", - "quartz-manager.accounts.in-memory.users[0].roles[0]=admin", + "quartz-manager.security.accounts.in-memory.enabled=true", + "quartz-manager.security.accounts.in-memory.users[0].name=foo", + "quartz-manager.security.accounts.in-memory.users[0].password=bar", + "quartz-manager.security.accounts.in-memory.users[0].roles[0]=admin", }) public class SecurityControllerTest { diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaCookieTest.java b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaCookieTest.java index c04cc9e..5a2d484 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaCookieTest.java +++ b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaCookieTest.java @@ -20,10 +20,10 @@ import org.springframework.test.context.TestPropertySource; "quartz-manager.security.jwt.header-strategy.header=Authorization", "quartz-manager.security.jwt.cookie-strategy.enabled=true", "quartz-manager.security.jwt.cookie-strategy.cookie=AUTH-TOKEN", - "quartz-manager.accounts.in-memory.enabled=true", - "quartz-manager.accounts.in-memory.users[0].name=foo", - "quartz-manager.accounts.in-memory.users[0].password=bar", - "quartz-manager.accounts.in-memory.users[0].roles[0]=admin", + "quartz-manager.security.accounts.in-memory.enabled=true", + "quartz-manager.security.accounts.in-memory.users[0].name=foo", + "quartz-manager.security.accounts.in-memory.users[0].password=bar", + "quartz-manager.security.accounts.in-memory.users[0].roles[0]=admin", }) public class SecurityLoginViaCookieTest extends AbstractSecurityLoginTest { diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaDefaultStrategyTest.java b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaDefaultStrategyTest.java index 6f0a6ab..2997322 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaDefaultStrategyTest.java +++ b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaDefaultStrategyTest.java @@ -11,10 +11,10 @@ import org.springframework.test.context.TestPropertySource; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @TestPropertySource(properties = { - "quartz-manager.accounts.in-memory.enabled=true", - "quartz-manager.accounts.in-memory.users[0].name=foo", - "quartz-manager.accounts.in-memory.users[0].password=bar", - "quartz-manager.accounts.in-memory.users[0].roles[0]=admin", + "quartz-manager.security.accounts.in-memory.enabled=true", + "quartz-manager.security.accounts.in-memory.users[0].name=foo", + "quartz-manager.security.accounts.in-memory.users[0].password=bar", + "quartz-manager.security.accounts.in-memory.users[0].roles[0]=admin", }) public class SecurityLoginViaDefaultStrategyTest extends AbstractSecurityLoginTest { diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaHeaderAndLoginFilterTest.java b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaHeaderAndLoginFilterTest.java index 8ae21b8..edd7aea 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaHeaderAndLoginFilterTest.java +++ b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaHeaderAndLoginFilterTest.java @@ -19,10 +19,10 @@ import org.springframework.test.context.TestPropertySource; "quartz-manager.security.jwt.header-strategy.enabled=true", "quartz-manager.security.jwt.header-strategy.header=Authorization", "quartz-manager.security.jwt.cookie-strategy.enabled=false", - "quartz-manager.accounts.in-memory.enabled=true", - "quartz-manager.accounts.in-memory.users[0].name=foo", - "quartz-manager.accounts.in-memory.users[0].password=bar", - "quartz-manager.accounts.in-memory.users[0].roles[0]=admin", + "quartz-manager.security.accounts.in-memory.enabled=true", + "quartz-manager.security.accounts.in-memory.users[0].name=foo", + "quartz-manager.security.accounts.in-memory.users[0].password=bar", + "quartz-manager.security.accounts.in-memory.users[0].roles[0]=admin", }) public class SecurityLoginViaHeaderAndLoginFilterTest extends AbstractSecurityLoginTest { diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaHeaderTest.java b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaHeaderTest.java index 38c610e..7b556a6 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaHeaderTest.java +++ b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityLoginViaHeaderTest.java @@ -18,10 +18,10 @@ import org.springframework.test.context.TestPropertySource; "quartz-manager.security.jwt.header-strategy.enabled=true", "quartz-manager.security.jwt.header-strategy.header=Authorization", "quartz-manager.security.jwt.cookie-strategy.enabled=false", - "quartz-manager.accounts.in-memory.enabled=true", - "quartz-manager.accounts.in-memory.users[0].name=foo", - "quartz-manager.accounts.in-memory.users[0].password=bar", - "quartz-manager.accounts.in-memory.users[0].roles[0]=admin", + "quartz-manager.security.accounts.in-memory.enabled=true", + "quartz-manager.security.accounts.in-memory.users[0].name=foo", + "quartz-manager.security.accounts.in-memory.users[0].password=bar", + "quartz-manager.security.accounts.in-memory.users[0].roles[0]=admin", }) public class SecurityLoginViaHeaderTest extends AbstractSecurityLoginTest { diff --git a/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml b/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml index cd64605..88a03fe 100644 --- a/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml +++ b/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml @@ -4,19 +4,36 @@ server: session.timeout : 28800 port: 8080 +quartz: + enabled: true + +quartz-manager: + jobClassPackages: it.fabioformosa.quartzmanager.jobs + security: + jwt: + secret: "bibidibobidiboo" + expiration-in-sec: 28800 # 8 hours + accounts: + in-memory: + enabled: true + users: + - name: admin + password: admin + roles: + - ADMIN + persistence: + quartz: + datasource: + url: "jdbc:postgresql://localhost:5432/quartzmanager" + user: "quartzmanager" + password: "quartzmanager" + spring: thymeleaf: cache: false mode: LEGACYHTML5 jpa.open-in-view: false -quartz: - enabled: true - -job: - frequency: 4000 - repeatCount: 19 - logging: level: org.springframework.web: WARN @@ -24,34 +41,3 @@ logging: org.springframework.boot.autoconfigure.security: INFO it.fabioformosa: DEBUG org.quartz: INFO - -quartz-manager: - persistence: - quartz: - datasource: - url: "jdbc:postgresql://localhost:5432/quartzmanager" - user: "quartzmanager" - password: "quartzmanager" - security: - login-model: - form-login-enabled: true - userpwd-filter-enabled : false - jwt: - enabled: true - secret: "bibidibobidiboo" - expiration-in-sec: 28800 # 8 hours - header-strategy: - enabled: false - header: "Authorization" - cookie-strategy: - enabled: true - cookie: AUTH-TOKEN - jobClassPackages: it.fabioformosa.quartzmanager.jobs - accounts: - in-memory: - enabled: true - users: - - name: admin - password: admin - roles: - - ADMIN From b06b130d208a418ef722c053acb0f78fc0d906b6 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Sat, 1 Oct 2022 00:45:01 +0200 Subject: [PATCH 041/184] #63 clean up --- .../security/models/UserRequest.java | 56 ------------------- 1 file changed, 56 deletions(-) delete mode 100644 quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/models/UserRequest.java diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/models/UserRequest.java b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/models/UserRequest.java deleted file mode 100644 index 469df73..0000000 --- a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/models/UserRequest.java +++ /dev/null @@ -1,56 +0,0 @@ -package it.fabioformosa.quartzmanager.security.models; - - -public class UserRequest { - - private Long id; - - private String username; - - private String password; - - private String firstname; - - private String lastname; - - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getFirstname() { - return firstname; - } - - public void setFirstname(String firstname) { - this.firstname = firstname; - } - - public String getLastname() { - return lastname; - } - - public void setLastname(String lastname) { - this.lastname = lastname; - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } -} From 727403d420193a64adf0a2e092661844e91f132b Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Sat, 1 Oct 2022 00:50:24 +0200 Subject: [PATCH 042/184] #63 left the main security config class in the spring.factories --- .../quartzmanager/security/WebSecurityConfigJWT.java | 2 ++ .../src/main/resources/META-INF/spring.factories | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/WebSecurityConfigJWT.java b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/WebSecurityConfigJWT.java index 80a5631..296e4fd 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/WebSecurityConfigJWT.java +++ b/quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/WebSecurityConfigJWT.java @@ -4,6 +4,7 @@ import org.apache.commons.lang3.BooleanUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; @@ -44,6 +45,7 @@ import it.fabioformosa.quartzmanager.security.helpers.impl.QuartzManagerHttpSecu /** * @author Fabio.Formosa */ +@ComponentScan(basePackages = {"it.fabioformosa.quartzmanager.security"}) @Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true) diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/main/resources/META-INF/spring.factories b/quartz-manager-parent/quartz-manager-starter-security/src/main/resources/META-INF/spring.factories index b1e78c3..78062f5 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/main/resources/META-INF/spring.factories +++ b/quartz-manager-parent/quartz-manager-starter-security/src/main/resources/META-INF/spring.factories @@ -1,4 +1,2 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -it.fabioformosa.quartzmanager.security.WebSecurityConfigJWT,\ -it.fabioformosa.quartzmanager.security.properties.JwtSecurityProperties,\ -it.fabioformosa.quartzmanager.security.properties.InMemoryAccountProperties +it.fabioformosa.quartzmanager.security.WebSecurityConfigJWT From 44d6854bc50798dc5c37c0386f600709b3a355d4 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Sat, 1 Oct 2022 15:48:01 +0200 Subject: [PATCH 043/184] #63 added security test to test the whitelisted endpoints --- .../security/SecurityControllerTest.java | 11 +++++++---- .../security/controllers/TestController.java | 6 ------ 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityControllerTest.java b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityControllerTest.java index 39ede6a..5489952 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityControllerTest.java +++ b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/SecurityControllerTest.java @@ -2,6 +2,8 @@ package it.fabioformosa.quartzmanager.security; import it.fabioformosa.quartzmanager.security.controllers.TestController; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; @@ -45,10 +47,11 @@ public class SecurityControllerTest { .andExpect(status().isUnauthorized()); } - @Test - void givenAnAnonymousUser_whenRequestedSwaggerResource_thenShouldReturn2xx() throws Exception { - mockMvc.perform(MockMvcRequestBuilders.get("/swagger-ui.html")) - .andExpect(status().isOk()); + @ParameterizedTest + @ValueSource(strings = {"/swagger-ui.html", "/v3/api-docs/**", "/swagger-resources/**", "/webjars/**"}) + void givenAnAnonymousUser_whenRequestedAnEndpointInWhitelist_thenShouldnotReturnForbidden(String whitelistEndpoint) throws Exception { + mockMvc.perform(MockMvcRequestBuilders.get(whitelistEndpoint)) + .andExpect(status().isNotFound()); } @Test diff --git a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/controllers/TestController.java b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/controllers/TestController.java index a5a8f49..7edf0e0 100644 --- a/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/controllers/TestController.java +++ b/quartz-manager-parent/quartz-manager-starter-security/src/test/java/it/fabioformosa/quartzmanager/security/controllers/TestController.java @@ -18,12 +18,6 @@ public class TestController { } - @ResponseStatus(HttpStatus.OK) - @GetMapping("/swagger-ui.html") - public void getSwaggerUI(){ - - } - @ResponseStatus(HttpStatus.OK) @GetMapping(QUARTZ_MANAGER + "/scheduler") public void getQuartzManagerScheduler(){ From 1571ab6d12eb37a1a7197a0f167c869cc873ccaa Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Sat, 1 Oct 2022 16:12:26 +0200 Subject: [PATCH 044/184] #63 made as default behaviour the creation of a scheduler instance --- .../configuration/SchedulerConfig.java | 2 +- ...hedulerConfigDefaultAppPropertiesTest.java | 21 +++++++++++++++++++ .../src/main/resources/application.yml | 9 -------- 3 files changed, 22 insertions(+), 10 deletions(-) create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfigDefaultAppPropertiesTest.java diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfig.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfig.java index 63b897c..c6e9905 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfig.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfig.java @@ -18,7 +18,7 @@ import java.util.Properties; @ComponentScan(basePackages = {"it.fabioformosa.quartzmanager.controllers"}) @Configuration -@ConditionalOnProperty(name = "quartz.enabled") +@ConditionalOnProperty(name = "quartz.enabled", matchIfMissing = true) public class SchedulerConfig { @Autowired(required = false) diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfigDefaultAppPropertiesTest.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfigDefaultAppPropertiesTest.java new file mode 100644 index 0000000..6afedc4 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfigDefaultAppPropertiesTest.java @@ -0,0 +1,21 @@ +package it.fabioformosa.quartzmanager.configuration; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.quartz.Scheduler; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class SchedulerConfigDefaultAppPropertiesTest { + + @Autowired + private Scheduler scheduler; + + @Test + void givenTheQuartzPropMissing_whenTheBootstrapOccurs_thenAQuartzInstanceShouldBeInstanciated(){ + Assertions.assertThat(scheduler).isNotNull(); + } + + +} diff --git a/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml b/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml index 88a03fe..0d642cb 100644 --- a/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml +++ b/quartz-manager-parent/quartz-manager-web-showcase/src/main/resources/application.yml @@ -1,12 +1,3 @@ -server: - servlet: - context-path: / - session.timeout : 28800 - port: 8080 - -quartz: - enabled: true - quartz-manager: jobClassPackages: it.fabioformosa.quartzmanager.jobs security: From 1e99602c68405f7d3f1856802041111c79fc72df Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Sat, 1 Oct 2022 17:20:41 +0200 Subject: [PATCH 045/184] #63 tested the default name assignment to the scheduler instance --- .../configuration/SchedulerConfig.java | 68 +++++++++++-------- ...hedulerConfigDefaultAppPropertiesTest.java | 10 ++- .../configuration/SchedulerConfigTest.java | 42 ++++++++++++ .../src/test/resources/quartz.properties | 4 +- 4 files changed, 94 insertions(+), 30 deletions(-) create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfigTest.java diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfig.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfig.java index c6e9905..7db06c1 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfig.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfig.java @@ -21,34 +21,48 @@ import java.util.Properties; @ConditionalOnProperty(name = "quartz.enabled", matchIfMissing = true) public class SchedulerConfig { - @Autowired(required = false) - private QuartzModuleProperties quartzModuleProperties; + protected static final String QUARTZ_MANAGER_SCHEDULER_DEFAULT_NAME = "quartz-manager-scheduler"; - @Bean - public JobFactory jobFactory(ApplicationContext applicationContext) { - AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory(); - jobFactory.setApplicationContext(applicationContext); - return jobFactory; - } + private final QuartzModuleProperties quartzModuleProperties; - @Bean - public Properties quartzProperties() throws IOException { - PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); - propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties")); - propertiesFactoryBean.afterPropertiesSet(); - return propertiesFactoryBean.getObject(); - } + @Autowired(required = false) + public SchedulerConfig(QuartzModuleProperties quartzModuleProperties) { + this.quartzModuleProperties = quartzModuleProperties; + } - @Bean(name = "scheduler") - public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory) throws IOException { - SchedulerFactoryBean factory = new SchedulerFactoryBean(); - factory.setJobFactory(jobFactory); - Properties mergedProperties = new Properties(); - if(quartzModuleProperties != null) - mergedProperties.putAll(quartzModuleProperties.getProperties()); - mergedProperties.putAll(quartzProperties()); - factory.setQuartzProperties(mergedProperties); - factory.setAutoStartup(false); - return factory; - } + @Bean + public JobFactory jobFactory(ApplicationContext applicationContext) { + AutowiringSpringBeanJobFactory jobFactory = new AutowiringSpringBeanJobFactory(); + jobFactory.setApplicationContext(applicationContext); + return jobFactory; + } + + @Bean + public Properties quartzProperties() throws IOException { + PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean(); + propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties")); + propertiesFactoryBean.afterPropertiesSet(); + return propertiesFactoryBean.getObject(); + } + + @Bean("quartzDefaultProperties") + public QuartzModuleProperties persistenceQuartzProps() { + QuartzModuleProperties quartzModuleProperties = new QuartzModuleProperties(); + quartzModuleProperties.getProperties().setProperty("org.quartz.scheduler.instanceName", QUARTZ_MANAGER_SCHEDULER_DEFAULT_NAME); + quartzModuleProperties.getProperties().setProperty("org.quartz.threadPool.threadCount", "1"); + return quartzModuleProperties; + } + + @Bean(name = "scheduler") + public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory) throws IOException { + SchedulerFactoryBean factory = new SchedulerFactoryBean(); + factory.setJobFactory(jobFactory); + Properties mergedProperties = new Properties(); + if (quartzModuleProperties != null) + mergedProperties.putAll(quartzModuleProperties.getProperties()); + mergedProperties.putAll(quartzProperties()); + factory.setQuartzProperties(mergedProperties); + factory.setAutoStartup(false); + return factory; + } } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfigDefaultAppPropertiesTest.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfigDefaultAppPropertiesTest.java index 6afedc4..322dc04 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfigDefaultAppPropertiesTest.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfigDefaultAppPropertiesTest.java @@ -3,9 +3,12 @@ package it.fabioformosa.quartzmanager.configuration; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; import org.quartz.Scheduler; +import org.quartz.SchedulerException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; +import static it.fabioformosa.quartzmanager.configuration.SchedulerConfig.QUARTZ_MANAGER_SCHEDULER_DEFAULT_NAME; + @SpringBootTest class SchedulerConfigDefaultAppPropertiesTest { @@ -13,9 +16,14 @@ class SchedulerConfigDefaultAppPropertiesTest { private Scheduler scheduler; @Test - void givenTheQuartzPropMissing_whenTheBootstrapOccurs_thenAQuartzInstanceShouldBeInstanciated(){ + void givenTheQuartzPropMissing_whenTheBootstrapOccurs_thenAQuartzInstanceShouldBeInstantiated(){ Assertions.assertThat(scheduler).isNotNull(); } + @Test + void givenTheQuartzNameMissing_whenTheBootstrapOccurs_thenAQuartzInstanceShouldBeTheDefaultName() throws SchedulerException { + Assertions.assertThat(scheduler.getSchedulerName()).isEqualTo(QUARTZ_MANAGER_SCHEDULER_DEFAULT_NAME); + } + } diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfigTest.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfigTest.java new file mode 100644 index 0000000..88d094a --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfigTest.java @@ -0,0 +1,42 @@ +package it.fabioformosa.quartzmanager.configuration; + +import it.fabioformosa.quartzmanager.common.properties.QuartzModuleProperties; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.Test; +import org.quartz.Scheduler; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; + +class SchedulerConfigTest { + + public static final String TEST_SCHEDULER_NAME = "foo"; + 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); + SchedulerConfig schedulerConfig = new SchedulerConfig(quartzModuleProperties); + GenericApplicationContext applicationContext = new GenericApplicationContext(); + applicationContext.refresh(); + SchedulerFactoryBean schedulerFactoryBean = schedulerConfig.schedulerFactoryBean(schedulerConfig.jobFactory(applicationContext)); + + schedulerFactoryBean.afterPropertiesSet(); + Scheduler scheduler = schedulerFactoryBean.getScheduler(); + Assertions.assertThat(scheduler.getSchedulerName()).isEqualTo(TEST_SCHEDULER_NAME); + } + + @Test + void givenNoSchedulerName_whenTheSchedulerIsInstatiated_thenTheSchedulerHasTheDefaultName() throws Exception { + QuartzModuleProperties quartzModuleProperties = new QuartzModuleProperties(); + SchedulerConfig schedulerConfig = new SchedulerConfig(quartzModuleProperties); + GenericApplicationContext applicationContext = new GenericApplicationContext(); + applicationContext.refresh(); + SchedulerFactoryBean schedulerFactoryBean = schedulerConfig.schedulerFactoryBean(schedulerConfig.jobFactory(applicationContext)); + + schedulerFactoryBean.afterPropertiesSet(); + Scheduler scheduler = schedulerFactoryBean.getScheduler(); + Assertions.assertThat(scheduler.getSchedulerName()).isEqualTo(QUARTZ_SCHEDULER_DEFAULT_NAME); + } + +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/resources/quartz.properties b/quartz-manager-parent/quartz-manager-starter-api/src/test/resources/quartz.properties index cdcc9e4..524d636 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/test/resources/quartz.properties +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/resources/quartz.properties @@ -1,2 +1,2 @@ -org.quartz.scheduler.instanceName=example -org.quartz.threadPool.threadCount=1 +#org.quartz.scheduler.instanceName=test //disabled to use the default value +#org.quartz.threadPool.threadCount=1 //disabled to use the default value From 93152f815710cceb2bf98f6776a32fc4e26049ec Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Sat, 1 Oct 2022 17:29:05 +0200 Subject: [PATCH 046/184] #63 tested the default name assignment to the scheduler instance --- .../QuartzDefaultPropertiesConfig.java | 22 +++++++++++++++++++ .../configuration/SchedulerConfig.java | 10 --------- ...hedulerConfigDefaultAppPropertiesTest.java | 2 +- 3 files changed, 23 insertions(+), 11 deletions(-) create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/QuartzDefaultPropertiesConfig.java diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/QuartzDefaultPropertiesConfig.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/QuartzDefaultPropertiesConfig.java new file mode 100644 index 0000000..84d8189 --- /dev/null +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/QuartzDefaultPropertiesConfig.java @@ -0,0 +1,22 @@ +package it.fabioformosa.quartzmanager.configuration; + +import it.fabioformosa.quartzmanager.common.properties.QuartzModuleProperties; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ConditionalOnProperty(name = "quartz.enabled", matchIfMissing = true) +public class QuartzDefaultPropertiesConfig { + + protected static final String QUARTZ_MANAGER_SCHEDULER_DEFAULT_NAME = "quartz-manager-scheduler"; + + @Bean("quartzDefaultProperties") + public QuartzModuleProperties defaultApiQuartzProps() { + QuartzModuleProperties quartzModuleProperties = new QuartzModuleProperties(); + quartzModuleProperties.getProperties().setProperty("org.quartz.scheduler.instanceName", QUARTZ_MANAGER_SCHEDULER_DEFAULT_NAME); + quartzModuleProperties.getProperties().setProperty("org.quartz.threadPool.threadCount", "1"); + return quartzModuleProperties; + } + +} diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfig.java b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfig.java index 7db06c1..cd89e30 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfig.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfig.java @@ -21,8 +21,6 @@ import java.util.Properties; @ConditionalOnProperty(name = "quartz.enabled", matchIfMissing = true) public class SchedulerConfig { - protected static final String QUARTZ_MANAGER_SCHEDULER_DEFAULT_NAME = "quartz-manager-scheduler"; - private final QuartzModuleProperties quartzModuleProperties; @Autowired(required = false) @@ -45,14 +43,6 @@ public class SchedulerConfig { return propertiesFactoryBean.getObject(); } - @Bean("quartzDefaultProperties") - public QuartzModuleProperties persistenceQuartzProps() { - QuartzModuleProperties quartzModuleProperties = new QuartzModuleProperties(); - quartzModuleProperties.getProperties().setProperty("org.quartz.scheduler.instanceName", QUARTZ_MANAGER_SCHEDULER_DEFAULT_NAME); - quartzModuleProperties.getProperties().setProperty("org.quartz.threadPool.threadCount", "1"); - return quartzModuleProperties; - } - @Bean(name = "scheduler") public SchedulerFactoryBean schedulerFactoryBean(JobFactory jobFactory) throws IOException { SchedulerFactoryBean factory = new SchedulerFactoryBean(); diff --git a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfigDefaultAppPropertiesTest.java b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfigDefaultAppPropertiesTest.java index 322dc04..bc9ad1f 100644 --- a/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfigDefaultAppPropertiesTest.java +++ b/quartz-manager-parent/quartz-manager-starter-api/src/test/java/it/fabioformosa/quartzmanager/configuration/SchedulerConfigDefaultAppPropertiesTest.java @@ -7,7 +7,7 @@ import org.quartz.SchedulerException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; -import static it.fabioformosa.quartzmanager.configuration.SchedulerConfig.QUARTZ_MANAGER_SCHEDULER_DEFAULT_NAME; +import static it.fabioformosa.quartzmanager.configuration.QuartzDefaultPropertiesConfig.QUARTZ_MANAGER_SCHEDULER_DEFAULT_NAME; @SpringBootTest class SchedulerConfigDefaultAppPropertiesTest { From a313d8b19db8794fe9ed7ec6dc9f8192356f69c2 Mon Sep 17 00:00:00 2001 From: Fabio Formosa Date: Tue, 4 Oct 2022 20:17:57 +0200 Subject: [PATCH 047/184] #63 ui gets 404 calling whoami in case of no sec config --- .../src/app/app-routing.module.ts | 9 +- quartz-manager-frontend/src/app/app.module.ts | 2 +- .../components/header/header.component.html | 2 +- .../app/components/header/header.component.ts | 6 +- .../src/app/guards/admin.guard.ts | 22 +- .../src/app/services/auth.service.ts | 2 +- .../src/app/services/config.service.ts | 18 +- .../src/app/services/user.service.ts | 71 ++-- quartz-manager-parent/pom.xml | 87 ++--- .../common/config/OpenAPIConfigConsts.java | 7 + .../common/config/QuartzManagerPaths.java | 13 + .../quartz-manager-starter-api/pom.xml | 319 +++++++++--------- .../configuration/OpenApiConfig.java | 21 +- .../configuration/SecurityDiscover.java | 6 + .../configuration/SecurityDiscoverConfig.java | 19 ++ .../AbstractQuartzManagerController.java | 7 - .../controllers/JobController.java | 20 +- .../controllers/SchedulerController.java | 7 +- .../controllers/SimpleTriggerController.java | 11 +- .../controllers/TriggerController.java | 24 +- .../controllers/UserController.java | 27 -- .../controllers/WebsocketController.java | 4 +- .../services/TriggerService.java | 5 +- .../quartz-manager-starter-security/pom.xml | 18 + .../security/WebSecurityConfigJWT.java | 33 +- .../security/controllers/UserController.java | 32 ++ .../quartz-manager-web-showcase/pom.xml | 4 - .../controllers/QuartzManagerController.java | 9 +- .../controllers/SessionController.java | 5 +- 29 files changed, 446 insertions(+), 364 deletions(-) create mode 100644 quartz-manager-parent/quartz-manager-common/src/main/java/it/fabioformosa/quartzmanager/common/config/OpenAPIConfigConsts.java create mode 100644 quartz-manager-parent/quartz-manager-common/src/main/java/it/fabioformosa/quartzmanager/common/config/QuartzManagerPaths.java create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/SecurityDiscover.java create mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/configuration/SecurityDiscoverConfig.java delete mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/AbstractQuartzManagerController.java delete mode 100644 quartz-manager-parent/quartz-manager-starter-api/src/main/java/it/fabioformosa/quartzmanager/controllers/UserController.java create mode 100644 quartz-manager-parent/quartz-manager-starter-security/src/main/java/it/fabioformosa/quartzmanager/security/controllers/UserController.java diff --git a/quartz-manager-frontend/src/app/app-routing.module.ts b/quartz-manager-frontend/src/app/app-routing.module.ts index b955e6f..b2325dc 100644 --- a/quartz-manager-frontend/src/app/app-routing.module.ts +++ b/quartz-manager-frontend/src/app/app-routing.module.ts @@ -25,11 +25,6 @@ export const routes: Routes = [ component: LoginComponent, canActivate: [GuestGuard] }, - // { - // path: 'change-password', - // component: ChangePasswordComponent, - // canActivate: [LoginGuard] - // }, { path: '404', component: NotFoundComponent @@ -45,7 +40,9 @@ export const routes: Routes = [ ]; @NgModule({ - imports: [RouterModule.forRoot(routes)], + imports: [RouterModule.forRoot(routes, { + initialNavigation: false + })], exports: [RouterModule], providers: [] }) diff --git a/quartz-manager-frontend/src/app/app.module.ts b/quartz-manager-frontend/src/app/app.module.ts index f0fbfb7..351a6ee 100644 --- a/quartz-manager-frontend/src/app/app.module.ts +++ b/quartz-manager-frontend/src/app/app.module.ts @@ -64,7 +64,7 @@ import {SimpleTriggerConfigComponent} from './components/simple-trigger-config'; import JobService from './services/job.service'; export function initUserFactory(userService: UserService) { - return () => userService.jsessionInitUser(); + return () => userService.fetchLoggedUser(); } diff --git a/quartz-manager-frontend/src/app/components/header/header.component.html b/quartz-manager-frontend/src/app/components/header/header.component.html index b44ce6a..20ca8b5 100644 --- a/quartz-manager-frontend/src/app/components/header/header.component.html +++ b/quartz-manager-frontend/src/app/components/header/header.component.html @@ -6,7 +6,7 @@
-