From b55e4bba4e97e46d5b7b736512052ad2a5bdc695 Mon Sep 17 00:00:00 2001
From: Hai Nguyen <haiphucnguyen@gmail.com>
Date: Wed, 27 Nov 2024 12:17:55 -0800
Subject: [PATCH] Update

---
 .github/workflows/gradle.yml                  |  8 +++---
 build.gradle                                  |  6 ++--
 .../groovy/flexwork.docker-conventions.gradle |  2 +-
 gradle/libs.versions.toml                     |  4 +++
 scripts/java_check.sh                         |  2 +-
 server/build.gradle                           |  4 ++-
 .../main/java/io/flexwork/FlexworkApp.java    |  2 --
 .../flexwork/config/AsyncConfiguration.java   |  4 ++-
 .../repository/WorkflowStateRepository.java   |  3 ++
 .../teams/service/WorkflowStateService.java   | 28 +++++++++++++++++++
 .../teams/service/dto/WorkflowStateDTO.java   | 16 +++++++++++
 .../service/mapper/WorkflowStateMapper.java   | 16 +++++++++++
 .../teams/web/rest/WorkflowController.java    | 20 ++++++++++++-
 .../00000000000000_initial_schema.xml         | 17 +++++++++++
 14 files changed, 118 insertions(+), 14 deletions(-)
 create mode 100644 server/src/main/java/io/flexwork/modules/teams/service/WorkflowStateService.java
 create mode 100644 server/src/main/java/io/flexwork/modules/teams/service/dto/WorkflowStateDTO.java
 create mode 100644 server/src/main/java/io/flexwork/modules/teams/service/mapper/WorkflowStateMapper.java

diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml
index 0ce2aaaa..b584d629 100644
--- a/.github/workflows/gradle.yml
+++ b/.github/workflows/gradle.yml
@@ -22,10 +22,10 @@ jobs:
 
     steps:
     - uses: actions/checkout@v4
-    - name: Set up JDK 17
+    - name: Set up JDK 21
       uses: actions/setup-java@v4
       with:
-        java-version: '17'
+        java-version: '21'
         distribution: 'temurin'
 
     # Configure Gradle for optimal use in GitHub Actions, including caching of downloaded dependencies.
@@ -44,10 +44,10 @@ jobs:
 
     steps:
     - uses: actions/checkout@v4
-    - name: Set up JDK 17
+    - name: Set up JDK 21
       uses: actions/setup-java@v4
       with:
-        java-version: '17'
+        java-version: '21'
         distribution: 'temurin'
 
     # Generates and submits a dependency graph, enabling Dependabot Alerts for all project dependencies.
diff --git a/build.gradle b/build.gradle
index f416c917..801439a7 100644
--- a/build.gradle
+++ b/build.gradle
@@ -7,7 +7,7 @@ plugins {
 version = "0.0.1-SNAPSHOT"
 description = "The flexible configurable crm service"
 
-assert System.properties["java.specification.version"] == "17" || "18" || "19" || "20" || "21" || "22"
+assert System.properties["java.specification.version"] == "21" || "22" || "23"
 
 allprojects {
     group = 'io.flexwork'
@@ -19,8 +19,8 @@ allprojects {
         afterEvaluate {
             if (it.plugins.hasPlugin('java') || it.plugins.hasPlugin('java-library')) {
                 java {
-                    sourceCompatibility=17
-                    targetCompatibility=17
+                    sourceCompatibility=21
+                    targetCompatibility=21
                 }
             }
         }
diff --git a/buildSrc/src/main/groovy/flexwork.docker-conventions.gradle b/buildSrc/src/main/groovy/flexwork.docker-conventions.gradle
index 1359f2e7..65dedfde 100644
--- a/buildSrc/src/main/groovy/flexwork.docker-conventions.gradle
+++ b/buildSrc/src/main/groovy/flexwork.docker-conventions.gradle
@@ -4,7 +4,7 @@ plugins {
 
 jib {
     from {
-        image = "eclipse-temurin:17-jre-focal"
+        image = "eclipse-temurin:21-jre-focal"
         platforms {
             platform {
                 os = "linux"
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 709633e9..c267aa57 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -17,6 +17,7 @@ springbootVersion = "3.3.5"
 springDependencyManagementVersion="1.1.6"
 jhisterVersion = "8.7.1"
 j2HtmlVersion = "1.6.0"
+shedlockVersion="6.0.1"
 
 [libraries]
 jhipster-framework = { module = "tech.jhipster:jhipster-framework", version.ref = "jhisterVersion" }
@@ -39,6 +40,8 @@ mockito = { module = "org.mockito:mockito-inline", version.ref = "mockitoVersion
 mockito-junit = { module = "org.mockito:mockito-junit-jupiter", version.ref = "mockitoJunitVersion" }
 json-api = { module = "jakarta.json:jakarta.json-api", version.ref = "jsonApiVersion" }
 parsson = { module = "org.eclipse.parsson:parsson", version.ref = "parssonVersion" }
+shedlock = {module="net.javacrumbs.shedlock:shedlock-spring", version.ref="shedlockVersion"}
+shedlock-jdbc-provider = {module="net.javacrumbs.shedlock:shedlock-provider-jdbc-template", version.ref="shedlockVersion"}
 
 [plugins]
 spring-boot = { id = "org.springframework.boot", version.ref = "springbootVersion" }
@@ -50,3 +53,4 @@ logback = ["logback-classic"]
 junit = ["junit-jupiter-api", "junit-jupiter-engine"]
 mockito = ["mockito", "mockito-junit"]
 json = ["json-api", "parsson"]
+shedlock = ["shedlock", "shedlock-jdbc-provider"]
diff --git a/scripts/java_check.sh b/scripts/java_check.sh
index 8f038832..fb70f320 100755
--- a/scripts/java_check.sh
+++ b/scripts/java_check.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 
 # Set the required Java version as a variable
-REQUIRED_JAVA_VERSION=17
+REQUIRED_JAVA_VERSION=21
 
 # Function to compare two versions
 # Function to compare two versions
diff --git a/server/build.gradle b/server/build.gradle
index 8640a512..d7a63c65 100644
--- a/server/build.gradle
+++ b/server/build.gradle
@@ -15,7 +15,7 @@ version = "0.0.1-SNAPSHOT"
 
 description = ""
 
-assert System.properties["java.specification.version"] == "17" || "18" || "19" || "20" || "21" || "22"
+assert System.properties["java.specification.version"] == "21" || "22" || "23"
 
 ext {
     springProfiles = ""
@@ -120,6 +120,8 @@ dependencies {
     implementation("org.hibernate.validator:hibernate-validator")
     implementation(libs.bundles.spring.statemachine)
     implementation(libs.liquibase)
+    implementation(libs.bundles.shedlock)
+
 
 //    testImplementation("com.tngtech.archunit:archunit-junit5-api:${archunitJunit5Version}") {
 //        exclude group: "org.slf4j", module: "slf4j-api"
diff --git a/server/src/main/java/io/flexwork/FlexworkApp.java b/server/src/main/java/io/flexwork/FlexworkApp.java
index 73ada483..1db1e186 100644
--- a/server/src/main/java/io/flexwork/FlexworkApp.java
+++ b/server/src/main/java/io/flexwork/FlexworkApp.java
@@ -28,7 +28,6 @@
 import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.core.annotation.Order;
 import org.springframework.core.env.Environment;
-import org.springframework.scheduling.annotation.EnableScheduling;
 import tech.jhipster.config.DefaultProfileUtil;
 
 @SpringBootApplication
@@ -38,7 +37,6 @@
     FlexworkProperties.class
 })
 @EntityScan("io.flexwork")
-@EnableScheduling
 @Order(1)
 public class FlexworkApp implements CommandLineRunner {
 
diff --git a/server/src/main/java/io/flexwork/config/AsyncConfiguration.java b/server/src/main/java/io/flexwork/config/AsyncConfiguration.java
index f396c629..be99abac 100644
--- a/server/src/main/java/io/flexwork/config/AsyncConfiguration.java
+++ b/server/src/main/java/io/flexwork/config/AsyncConfiguration.java
@@ -1,6 +1,7 @@
 package io.flexwork.config;
 
 import java.util.concurrent.Executor;
+import net.javacrumbs.shedlock.spring.annotation.EnableSchedulerLock;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
@@ -20,6 +21,7 @@
 @Configuration
 @EnableAsync
 @EnableScheduling
+@EnableSchedulerLock(defaultLockAtMostFor = "10m")
 @Profile("!testdev & !testprod")
 public class AsyncConfiguration implements AsyncConfigurer {
 
@@ -44,7 +46,7 @@ public Executor getAsyncExecutor() {
     }
 
     @Bean(name = "auditLogExecutor")
-    public TaskExecutor auditLogaskExecutor() {
+    public TaskExecutor auditLogTaskExecutor() {
         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
         executor.setCorePoolSize(10);
         executor.setMaxPoolSize(50);
diff --git a/server/src/main/java/io/flexwork/modules/teams/repository/WorkflowStateRepository.java b/server/src/main/java/io/flexwork/modules/teams/repository/WorkflowStateRepository.java
index 3dee3bf8..893e7054 100644
--- a/server/src/main/java/io/flexwork/modules/teams/repository/WorkflowStateRepository.java
+++ b/server/src/main/java/io/flexwork/modules/teams/repository/WorkflowStateRepository.java
@@ -1,6 +1,7 @@
 package io.flexwork.modules.teams.repository;
 
 import io.flexwork.modules.teams.domain.WorkflowState;
+import java.util.List;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.Query;
 import org.springframework.data.repository.query.Param;
@@ -12,4 +13,6 @@ public interface WorkflowStateRepository extends JpaRepository<WorkflowState, Lo
     @Query(
             "SELECT ws FROM WorkflowState ws WHERE ws.workflow.id = :workflowId AND ws.isInitial = true")
     WorkflowState findInitialStateByWorkflowId(@Param("workflowId") Long workflowId);
+
+    List<WorkflowState> findByWorkflowId(Long workflowId);
 }
diff --git a/server/src/main/java/io/flexwork/modules/teams/service/WorkflowStateService.java b/server/src/main/java/io/flexwork/modules/teams/service/WorkflowStateService.java
new file mode 100644
index 00000000..52527b6e
--- /dev/null
+++ b/server/src/main/java/io/flexwork/modules/teams/service/WorkflowStateService.java
@@ -0,0 +1,28 @@
+package io.flexwork.modules.teams.service;
+
+import io.flexwork.modules.teams.repository.WorkflowStateRepository;
+import io.flexwork.modules.teams.service.dto.WorkflowStateDTO;
+import io.flexwork.modules.teams.service.mapper.WorkflowStateMapper;
+import java.util.List;
+import org.springframework.stereotype.Service;
+
+@Service
+public class WorkflowStateService {
+
+    private final WorkflowStateRepository workflowStateRepository;
+
+    private final WorkflowStateMapper workflowStateMapper;
+
+    public WorkflowStateService(
+            WorkflowStateRepository workflowStateRepository,
+            WorkflowStateMapper workflowStateMapper) {
+        this.workflowStateRepository = workflowStateRepository;
+        this.workflowStateMapper = workflowStateMapper;
+    }
+
+    public List<WorkflowStateDTO> getStatesByWorkflowId(Long workflowId) {
+        return workflowStateRepository.findByWorkflowId(workflowId).stream()
+                .map(workflowStateMapper::toDto)
+                .toList();
+    }
+}
diff --git a/server/src/main/java/io/flexwork/modules/teams/service/dto/WorkflowStateDTO.java b/server/src/main/java/io/flexwork/modules/teams/service/dto/WorkflowStateDTO.java
new file mode 100644
index 00000000..a4f7ce6b
--- /dev/null
+++ b/server/src/main/java/io/flexwork/modules/teams/service/dto/WorkflowStateDTO.java
@@ -0,0 +1,16 @@
+package io.flexwork.modules.teams.service.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class WorkflowStateDTO {
+    private Long id;
+    private Long workflowId;
+    private String stateName;
+    private Boolean isInitial;
+    private Boolean isFinal;
+}
diff --git a/server/src/main/java/io/flexwork/modules/teams/service/mapper/WorkflowStateMapper.java b/server/src/main/java/io/flexwork/modules/teams/service/mapper/WorkflowStateMapper.java
new file mode 100644
index 00000000..a30b7348
--- /dev/null
+++ b/server/src/main/java/io/flexwork/modules/teams/service/mapper/WorkflowStateMapper.java
@@ -0,0 +1,16 @@
+package io.flexwork.modules.teams.service.mapper;
+
+import io.flexwork.modules.teams.domain.WorkflowState;
+import io.flexwork.modules.teams.service.dto.WorkflowStateDTO;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+
+@Mapper(componentModel = "spring")
+public interface WorkflowStateMapper {
+
+    @Mapping(source = "workflow.id", target = "workflowId")
+    WorkflowStateDTO toDto(WorkflowState workflowState);
+
+    @Mapping(source = "workflowId", target = "workflow.id")
+    WorkflowState toEntity(WorkflowStateDTO workflowStateDTO);
+}
diff --git a/server/src/main/java/io/flexwork/modules/teams/web/rest/WorkflowController.java b/server/src/main/java/io/flexwork/modules/teams/web/rest/WorkflowController.java
index 340844de..c0227d85 100644
--- a/server/src/main/java/io/flexwork/modules/teams/web/rest/WorkflowController.java
+++ b/server/src/main/java/io/flexwork/modules/teams/web/rest/WorkflowController.java
@@ -2,7 +2,9 @@
 
 import io.flexwork.modules.teams.domain.Workflow;
 import io.flexwork.modules.teams.service.WorkflowService;
+import io.flexwork.modules.teams.service.WorkflowStateService;
 import io.flexwork.modules.teams.service.dto.WorkflowDTO;
+import io.flexwork.modules.teams.service.dto.WorkflowStateDTO;
 import java.util.List;
 import org.springframework.http.ResponseEntity;
 import org.springframework.web.bind.annotation.*;
@@ -13,8 +15,12 @@ public class WorkflowController {
 
     private final WorkflowService workflowService;
 
-    public WorkflowController(WorkflowService workflowService) {
+    private final WorkflowStateService workflowStateService;
+
+    public WorkflowController(
+            WorkflowService workflowService, WorkflowStateService workflowStateService) {
         this.workflowService = workflowService;
+        this.workflowStateService = workflowStateService;
     }
 
     @PostMapping
@@ -69,4 +75,16 @@ public ResponseEntity<List<WorkflowDTO>> getWorkflowsByTeam(@PathVariable Long t
         List<WorkflowDTO> workflows = workflowService.getWorkflowsForTeam(teamId);
         return ResponseEntity.ok(workflows);
     }
+
+    /**
+     * Retrieve all workflow states for a given workflow ID.
+     *
+     * @param workflowId the ID of the workflow
+     * @return a list of workflow states
+     */
+    @GetMapping("/{workflowId}/states")
+    public ResponseEntity<List<WorkflowStateDTO>> getWorkflowStates(@PathVariable Long workflowId) {
+        List<WorkflowStateDTO> states = workflowStateService.getStatesByWorkflowId(workflowId);
+        return ResponseEntity.ok(states);
+    }
 }
diff --git a/tools/liquibase/src/main/resources/config/liquibase/tenant/changelog/00000000000000_initial_schema.xml b/tools/liquibase/src/main/resources/config/liquibase/tenant/changelog/00000000000000_initial_schema.xml
index 1bb25bc9..d20ce3b7 100644
--- a/tools/liquibase/src/main/resources/config/liquibase/tenant/changelog/00000000000000_initial_schema.xml
+++ b/tools/liquibase/src/main/resources/config/liquibase/tenant/changelog/00000000000000_initial_schema.xml
@@ -10,6 +10,23 @@
 		author="flexapp">
 		<createSequence sequenceName="sequence_generator"
 			minValue="100" startValue="100" incrementBy="1" />
+
+		<!--Table for shedLock, a distributed lock for spring scheduling service -->
+		<createTable tableName="shedlock">
+			<column name="name" type="VARCHAR(64)">
+				<constraints nullable="false" primaryKey="true" />
+			</column>
+			<column name="lock_until" type="TIMESTAMP">
+				<constraints nullable="false" />
+			</column>
+			<column name="locked_at" type="TIMESTAMP">
+				<constraints nullable="false" />
+			</column>
+			<column name="locked_by" type="VARCHAR(255)">
+				<constraints nullable="false" />
+			</column>
+		</createTable>
+
 		<createTable tableName="fw_user">
 			<column name="id" type="bigint">
 				<constraints primaryKey="true" />