diff --git a/README.md b/README.md index 59a4f63..09673b3 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A Spring Boot-based distributed job scheduling and execution system that provide ## Features - **REST API** to create workflow and one-off jobs -- **PostgreSQL database** persistence with Liquibase schema management +- **PostgreSQL database** persistence with Flyway schema management - **Automatic job executor** that polls every 5 seconds (configurable) for unassigned jobs - **Worker thread pool** for concurrent job execution (configurable pool size) - **Priority-based scheduling** (1-10, where 1 is highest priority) @@ -20,7 +20,7 @@ This is a multi-module Gradle project: - **`task-manager-service`**: Core job scheduling and execution framework - **`example-application`**: Example Spring Boot application demonstrating usage -- **`jobstore-liquibase`**: Database schema management via Liquibase changesets +- **`jobstore-flyway`**: Database schema management via Flyway ## Prerequisites @@ -68,35 +68,35 @@ spring.datasource.username=postgres spring.datasource.password=postgres ``` -### Running Liquibase Migrations Manually +### Running Flyway Migrations Manually -**Note**: Liquibase migrations run automatically when the Spring Boot application starts. However, if you need to run migrations manually (e.g., for database setup before starting the application), you can use the provided script: +**Note**: Flyway migrations run automatically when the Spring Boot application starts. However, if you need to run migrations manually (e.g., for database setup before starting the application), you can use the provided script: -1. **Using the provided script** (requires Liquibase CLI): +1. **Using the provided script** (requires Flyway CLI): ```bash # Make the script executable (if not already) -chmod +x run-liquibase.sh +chmod +x run-flyway.sh # Run the script -./run-liquibase.sh +./run-flyway.sh ``` The script will: - Connect to the PostgreSQL database using credentials from `application.properties` -- Run all pending Liquibase migrations +- Run all pending Flyway migrations - Display the migration status -2. **Installing Liquibase CLI** (if not already installed): - - **macOS**: `brew install liquibase` - - **Linux/Windows**: Download from [https://www.liquibase.org/download](https://www.liquibase.org/download) +2. **Installing Flyway CLI** (if not already installed): + - **macOS**: `brew install flyway` + - **Linux/Windows**: curl -L https://repo1.maven.org/maven2/org/flywaydb/flyway-commandline/11.14.1/flyway-commandline-11.14.1-linux-x64.tar.gz | tar xz -3. **Alternative**: If Liquibase CLI is not available, migrations will run automatically when you start the Spring Boot application. Simply start the application and Liquibase will apply any pending migrations. +3. **Alternative**: If Flyway CLI is not available, migrations will run automatically when you start the Spring Boot application. Simply start the application and Flyway will apply any pending migrations. -**Database Connection Details** (used by `run-liquibase.sh`): +**Database Connection Details** (used by `run-flyway.sh`): - URL: `jdbc:postgresql://localhost:5435/job_scheduler_db` - Username: `postgres` - Password: `postgres` -- Changelog: `classpath:liquibase/jobstore-db-changelog.xml` +- Changelog: `classpath:db/migration` ## Running the Application @@ -249,9 +249,9 @@ spring.datasource.url=jdbc:postgresql://localhost:5435/job_scheduler_db spring.datasource.username=postgres spring.datasource.password=postgres -# Liquibase Configuration -spring.liquibase.enabled=true -spring.liquibase.change-log=classpath:liquibase/jobstore-db-changelog.xml +# Flyway Configuration +spring.flyway.enabled=true +spring.flyway.locations=classpath:db/migration # JPA/Hibernate Configuration spring.jpa.hibernate.ddl-auto=validate @@ -259,7 +259,7 @@ spring.jpa.hibernate.ddl-auto=validate ## Database Schema -The `jobs` table is automatically created via Liquibase with the following structure: +The `jobs` table is automatically created via Flyway with the following structure: - `job_id` (UUID, Primary Key) - Auto-generated UUID - `worker_id` (UUID) - UUID of the worker assigned to this job (NULL if unassigned) @@ -272,8 +272,8 @@ The `jobs` table is automatically created via Liquibase with the following struc ### Schema Management -Database schema is managed via Liquibase changesets in the `jobstore-liquibase` module: -- `001-initial-schema.xml`: Initial schema creation with all tables and columns +Database schema is managed via Flyway migrations in the `jobstore-flyway` module: +- `V1__create_jobs_table.sql`: Initial schema creation with all tables and columns The schema includes: - `jobs` table with all required columns (`assigned_task_name`, `assigned_task_start_time`, etc.) @@ -282,9 +282,9 @@ The schema includes: - Default values for `priority` (10) and `retry_attempts_remaining` (0) **Note**: -- Liquibase migrations run automatically when the Spring Boot application starts. The application will apply any pending migrations on startup. -- To run migrations manually before starting the application, use the `run-liquibase.sh` script (see [Database Setup](#database-setup) section for details). -- For manual execution, you can also use the Liquibase CLI directly or restart the application to trigger migrations. +- Flyway migrations run automatically when the Spring Boot application starts. The application will apply any pending migrations on startup. +- To run migrations manually before starting the application, use the `run-fl;yway.sh` script (see [Database Setup](#database-setup) section for details). +- For manual execution, you can also use the Flyway CLI directly or restart the application to trigger migrations. ## Retry Mechanism diff --git a/TASK_MANAGER_SERVICE_EXPLANATION.md b/TASK_MANAGER_SERVICE_EXPLANATION.md index d8aa2c7..cbc13ef 100644 --- a/TASK_MANAGER_SERVICE_EXPLANATION.md +++ b/TASK_MANAGER_SERVICE_EXPLANATION.md @@ -538,7 +538,7 @@ The system uses PostgreSQL with: - `assigned_task_name`: Name of the task to execute (TEXT) - `assigned_task_start_time`: Scheduled start time (TIMESTAMP WITH TIME ZONE) - **Indexes**: On `worker_id`, `assigned_task_start_time`, `priority` -- **Liquibase**: Schema management via changesets +- **Flyway**: Schema management via migration scripts --- diff --git a/build.gradle b/build.gradle index 5469ac5..7aabfd8 100644 --- a/build.gradle +++ b/build.gradle @@ -6,7 +6,7 @@ plugins { } group = 'uk.gov.hmcts.cp' -version = System.getProperty('API_SPEC_VERSION') ?: '1.0.0' +version = System.getProperty('API_SPEC_VERSION') ?: '1.0.4' description = 'Spring Job Scheduler' def githubActor = project.findProperty("github.actor") ?: System.getenv("GITHUB_ACTOR") @@ -55,6 +55,8 @@ subprojects { // Lombok - using latest version for Java compatibility compileOnly 'org.projectlombok:lombok:1.18.42' annotationProcessor 'org.projectlombok:lombok:1.18.42' + testCompileOnly "org.projectlombok:lombok:1.18.42" + testAnnotationProcessor "org.projectlombok:lombok:1.18.42" // Test testImplementation 'org.springframework.boot:spring-boot-starter-test' diff --git a/example-application/build.gradle b/example-application/build.gradle index 46f777b..a02fb1f 100644 --- a/example-application/build.gradle +++ b/example-application/build.gradle @@ -6,7 +6,7 @@ dependencies { // Depend on task-manager-service implementation project(':task-manager-service') - // Depend on jobstore-liquibase for database schema + // Depend on jobstore-flyway for database schema implementation project(':jobstore-flyway') // Spring Boot Web @@ -17,8 +17,6 @@ dependencies { runtimeOnly "org.flywaydb:flyway-database-postgresql" // Flyway for Database Migration - implementation 'org.springframework.boot:spring-boot-starter-flyway' - implementation 'org.liquibase:liquibase-core' implementation "org.flywaydb:flyway-core" // Spring Boot Flyway starter for auto-configuration diff --git a/example-application/src/main/resources/application.properties b/example-application/src/main/resources/application.properties index 450a679..3dd3d44 100644 --- a/example-application/src/main/resources/application.properties +++ b/example-application/src/main/resources/application.properties @@ -33,8 +33,6 @@ logging.level.uk.gov.hmcts.cp.taskmanager=DEBUG logging.level.org.springframework.web=DEBUG logging.level.org.hibernate.SQL=DEBUG logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE -logging.level.liquibase=DEBUG -logging.level.org.springframework.boot.autoconfigure.liquibase=DEBUG logging.level.org.springframework.boot.autoconfigure=DEBUG debug=true diff --git a/integration-tests/README.md b/integration-tests/README.md index b11c711..7a6e31a 100644 --- a/integration-tests/README.md +++ b/integration-tests/README.md @@ -88,7 +88,7 @@ The module includes test task implementations used for integration testing: ## Test Database -The integration tests use an **H2 in-memory database** configured in `application-test.properties`. The database schema is managed via Liquibase using the same changesets as the main application. +The integration tests use an **H2 in-memory database** configured in `application-test.properties`. The database schema is managed via Flyway using the same changesets as the main application. ## Configuration diff --git a/integration-tests/build.gradle b/integration-tests/build.gradle index f83ff17..aecd577 100644 --- a/integration-tests/build.gradle +++ b/integration-tests/build.gradle @@ -19,7 +19,6 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-jdbc' - implementation 'org.springframework.boot:spring-boot-starter-flyway' implementation "org.flywaydb:flyway-core" testImplementation 'org.springframework.boot:spring-boot-starter-test' diff --git a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/ConcurrentExecutionIntegrationTest.java b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/ConcurrentExecutionIntegrationTest.java index 069d4ce..bbc231a 100644 --- a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/ConcurrentExecutionIntegrationTest.java +++ b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/ConcurrentExecutionIntegrationTest.java @@ -1,27 +1,32 @@ package uk.gov.hmcts.cp.taskmanager.integration; import static java.time.ZonedDateTime.now; +import static java.util.UUID.randomUUID; +import static java.util.concurrent.Executors.newFixedThreadPool; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionStatus.COMPLETED; import uk.gov.hmcts.cp.taskmanager.domain.ExecutionInfo; import uk.gov.hmcts.cp.taskmanager.domain.ExecutionStatus; import uk.gov.hmcts.cp.taskmanager.integration.config.IntegrationTestConfiguration; +import uk.gov.hmcts.cp.taskmanager.integration.persistence.TaskStatus; +import uk.gov.hmcts.cp.taskmanager.integration.persistence.TaskStatusRepository; import uk.gov.hmcts.cp.taskmanager.persistence.entity.Job; import uk.gov.hmcts.cp.taskmanager.persistence.repository.JobsRepository; import uk.gov.hmcts.cp.taskmanager.service.ExecutionService; import java.time.ZonedDateTime; +import java.util.ArrayList; import java.util.List; +import java.util.Optional; +import java.util.UUID; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import jakarta.json.Json; -import jakarta.json.JsonObject; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; @@ -41,18 +46,12 @@ class ConcurrentExecutionIntegrationTest extends PostgresIntegrationTestBase { @Autowired private JobsRepository jobsRepository; - private JsonObject testJobData; + @Autowired + private TaskStatusRepository taskStatusRepository; @Autowired JdbcTemplate jdbcTemplate; - @BeforeEach - void setUp() { - testJobData = Json.createObjectBuilder() - .add("test", "data") - .build(); - } - @AfterEach void tearDown() { jdbcTemplate.execute("delete from JOBS"); @@ -71,10 +70,16 @@ void testMultipleJobsExecutedConcurrently() { // Given - Create multiple jobs final int jobCount = 5; final ZonedDateTime startTime = now().minusSeconds(1); + final List taskIdList = new ArrayList<>(); for (int i = 0; i < jobCount; i++) { + final UUID taskId = randomUUID(); + taskIdList.add(taskId); final ExecutionInfo executionInfo = new ExecutionInfo( - testJobData, + Json.createObjectBuilder() + .add("test", "data") + .add(ID_KEY, taskId.toString()) + .build(), "TEST_COMPLETED_TASK", startTime, ExecutionStatus.STARTED, @@ -86,16 +91,26 @@ void testMultipleJobsExecutedConcurrently() { // When - Wait for all jobs to execute // Then - All jobs should be executed and deleted await().atMost(java.time.Duration.ofSeconds(15)).untilAsserted(() -> { - List jobs = jobsRepository.findAll(); + final List jobs = jobsRepository.findAll(); assertThat(jobs).isEmpty(); // All jobs should be completed }); + // And - All task status updated + await().atMost(java.time.Duration.ofSeconds(15)).untilAsserted(() -> { + final List tasks = taskStatusRepository.findAllById(taskIdList); + assertThat(tasks.size()).isEqualTo(jobCount); + assertThat(tasks.stream().allMatch(t -> COMPLETED.name().equals(t.getStatus()))).isTrue(); + }); } @Test void testJobLockingPreventsDuplicateExecution() { // Given - Create a single job + final UUID taskId = randomUUID(); final ExecutionInfo executionInfo = new ExecutionInfo( - testJobData, + Json.createObjectBuilder() + .add("test", "data") + .add(ID_KEY, taskId.toString()) + .build(), "TEST_COMPLETED_TASK", now().minusSeconds(1), ExecutionStatus.STARTED, @@ -112,17 +127,29 @@ void testJobLockingPreventsDuplicateExecution() { assertThat(job.getWorkerId() != null).isTrue(); } }); + + await().atMost(java.time.Duration.ofSeconds(5)).untilAsserted(() -> { + final Optional task = taskStatusRepository.findById(taskId); + assertThat(task.isEmpty()).isFalse(); + assertThat(task.stream().allMatch(t -> COMPLETED.name().equals(t.getStatus()))).isTrue(); + }); } @Test void testBatchProcessing() { // Given - Create more jobs than batch size int jobCount = 15; // More than default batch size of 10 - ZonedDateTime startTime = now().minusSeconds(1); + final ZonedDateTime startTime = now().minusSeconds(1); + final List taskIdList = new ArrayList<>(); for (int i = 0; i < jobCount; i++) { + final UUID taskId = randomUUID(); + taskIdList.add(taskId); ExecutionInfo executionInfo = new ExecutionInfo( - testJobData, + Json.createObjectBuilder() + .add("test", "data") + .add(ID_KEY, taskId.toString()) + .build(), "TEST_COMPLETED_TASK", startTime, ExecutionStatus.STARTED, @@ -137,6 +164,12 @@ void testBatchProcessing() { List jobs = jobsRepository.findAll(); assertThat(jobs).isEmpty(); // All jobs should be completed }); + + await().atMost(java.time.Duration.ofSeconds(30)).untilAsserted(() -> { + final List tasks = taskStatusRepository.findAllById(taskIdList); + assertThat(tasks.size()).isEqualTo(jobCount); + assertThat(tasks.stream().allMatch(t -> COMPLETED.name().equals(t.getStatus()))).isTrue(); + }); } @Test @@ -144,15 +177,21 @@ void testConcurrentJobCreation() throws InterruptedException { // Given - Create jobs concurrently from multiple threads int threadCount = 5; int jobsPerThread = 2; - ExecutorService executor = Executors.newFixedThreadPool(threadCount); - CountDownLatch latch = new CountDownLatch(threadCount * jobsPerThread); + final ExecutorService executor = newFixedThreadPool(threadCount); + final CountDownLatch latch = new CountDownLatch(threadCount * jobsPerThread); + final List taskIdList = new ArrayList<>(); // When - Create jobs concurrently for (int i = 0; i < threadCount; i++) { executor.submit(() -> { for (int j = 0; j < jobsPerThread; j++) { + final UUID taskId = randomUUID(); + taskIdList.add(taskId); ExecutionInfo executionInfo = new ExecutionInfo( - testJobData, + Json.createObjectBuilder() + .add("test", "data") + .add(ID_KEY, taskId.toString()) + .build(), "TEST_COMPLETED_TASK", now().minusSeconds(1), ExecutionStatus.STARTED, @@ -173,6 +212,12 @@ void testConcurrentJobCreation() throws InterruptedException { List jobs = jobsRepository.findAll(); assertThat(jobs).isEmpty(); // All jobs should be completed }); + + await().atMost(java.time.Duration.ofSeconds(20)).untilAsserted(() -> { + final List tasks = taskStatusRepository.findAllById(taskIdList); + assertThat(tasks.size()).isEqualTo(threadCount * jobsPerThread); + assertThat(tasks.stream().allMatch(t -> COMPLETED.name().equals(t.getStatus()))).isTrue(); + }); } } diff --git a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/IntegrationTestApplication.java b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/IntegrationTestApplication.java index 292e4fd..df5dbac 100644 --- a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/IntegrationTestApplication.java +++ b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/IntegrationTestApplication.java @@ -11,8 +11,8 @@ */ @SpringBootApplication(scanBasePackages = "uk.gov.hmcts.cp.taskmanager") @EnableScheduling -@EnableJpaRepositories(basePackages = "uk.gov.hmcts.cp.taskmanager.persistence.repository") -@EntityScan(basePackages = "uk.gov.hmcts.cp.taskmanager.persistence.entity") +@EnableJpaRepositories(basePackages = {"uk.gov.hmcts.cp.taskmanager.persistence.repository", "uk.gov.hmcts.cp.taskmanager.integration.persistence"}) +@EntityScan(basePackages = {"uk.gov.hmcts.cp.taskmanager.persistence.entity", "uk.gov.hmcts.cp.taskmanager.integration.persistence"}) public class IntegrationTestApplication { } diff --git a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/JobCreationAndExecutionIntegrationTest.java b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/JobCreationAndExecutionIntegrationTest.java index 60b78d3..524a319 100644 --- a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/JobCreationAndExecutionIntegrationTest.java +++ b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/JobCreationAndExecutionIntegrationTest.java @@ -1,21 +1,28 @@ package uk.gov.hmcts.cp.taskmanager.integration; +import static jakarta.json.Json.createObjectBuilder; import static java.time.ZonedDateTime.now; +import static java.util.UUID.randomUUID; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; +import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionStatus.COMPLETED; import uk.gov.hmcts.cp.taskmanager.domain.ExecutionInfo; import uk.gov.hmcts.cp.taskmanager.domain.ExecutionStatus; import uk.gov.hmcts.cp.taskmanager.domain.converter.JsonObjectConverter; import uk.gov.hmcts.cp.taskmanager.integration.config.IntegrationTestConfiguration; +import uk.gov.hmcts.cp.taskmanager.integration.persistence.TaskStatus; +import uk.gov.hmcts.cp.taskmanager.integration.persistence.TaskStatusRepository; import uk.gov.hmcts.cp.taskmanager.persistence.entity.Job; import uk.gov.hmcts.cp.taskmanager.persistence.repository.JobsRepository; import uk.gov.hmcts.cp.taskmanager.persistence.service.JobService; import uk.gov.hmcts.cp.taskmanager.service.ExecutionService; -import jakarta.json.Json; + +import java.util.Optional; +import java.util.UUID; + import jakarta.json.JsonObject; import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; @@ -43,18 +50,11 @@ class JobCreationAndExecutionIntegrationTest extends PostgresIntegrationTestBase @Autowired private JsonObjectConverter jsonObjectConverter; - private JsonObject testJobData; - @Autowired private JdbcTemplate jdbcTemplate; - @BeforeEach - void setUp() { - testJobData = Json.createObjectBuilder() - .add("testKey", "testValue") - .add("testNumber", 42) - .build(); - } + @Autowired + private TaskStatusRepository taskStatusRepository; @AfterEach void tearDown() { @@ -66,8 +66,12 @@ void tearDown() { @Test void testJobCreation() { // Given - ExecutionInfo executionInfo = new ExecutionInfo( - testJobData, + final UUID taskId = randomUUID(); + final ExecutionInfo executionInfo = new ExecutionInfo( + createObjectBuilder() + .add("test", "data") + .add(ID_KEY, taskId.toString()) + .build(), "TEST_COMPLETED_TASK", now(), ExecutionStatus.STARTED, @@ -88,13 +92,23 @@ void testJobCreation() { assertThat(job.getWorkerId()).isNull(); assertThat(job.getRetryAttemptsRemaining()).isEqualTo(0); }); + + await().atMost(java.time.Duration.ofSeconds(5)).untilAsserted(() -> { + final Optional task = taskStatusRepository.findById(taskId); + assertThat(task.isEmpty()).isFalse(); + assertThat(task.stream().allMatch(t -> COMPLETED.name().equals(t.getStatus()))).isTrue(); + }); } @Test void testJobExecutionAndCompletion() { // Given - Create a job - ExecutionInfo executionInfo = new ExecutionInfo( - testJobData, + final UUID taskId = randomUUID(); + final ExecutionInfo executionInfo = new ExecutionInfo( + createObjectBuilder() + .add("test", "data") + .add(ID_KEY, taskId.toString()) + .build(), "TEST_COMPLETED_TASK", now().minusSeconds(1), // Past time so it can execute immediately ExecutionStatus.STARTED, @@ -108,13 +122,23 @@ void testJobExecutionAndCompletion() { var jobs = jobsRepository.findAll(); assertThat(jobs).isEmpty(); }); + + await().atMost(java.time.Duration.ofSeconds(5)).untilAsserted(() -> { + final Optional task = taskStatusRepository.findById(taskId); + assertThat(task.isEmpty()).isFalse(); + assertThat(task.stream().allMatch(t -> COMPLETED.name().equals(t.getStatus()))).isTrue(); + }); } @Test void testJobWithFutureStartTime() { // Given - Create a job with future start time - ExecutionInfo executionInfo = new ExecutionInfo( - testJobData, + final UUID taskId = randomUUID(); + final ExecutionInfo executionInfo = new ExecutionInfo( + createObjectBuilder() + .add("test", "data") + .add(ID_KEY, taskId.toString()) + .build(), "TEST_COMPLETED_TASK", now().plusSeconds(8), // Future time ExecutionStatus.STARTED, @@ -138,13 +162,15 @@ void testJobWithFutureStartTime() { @Test void testJobDataPersistence() { // Given - JsonObject complexData = Json.createObjectBuilder() + final UUID taskId = randomUUID(); + final JsonObject complexData = createObjectBuilder() .add("name", "Test Job") + .add(ID_KEY, taskId.toString()) .add("count", 100) .add("active", true) .build(); - ExecutionInfo executionInfo = new ExecutionInfo( + final ExecutionInfo executionInfo = new ExecutionInfo( complexData, "TEST_COMPLETED_TASK", now().minusSeconds(1), @@ -166,6 +192,12 @@ void testJobDataPersistence() { assertThat(persistedData.getBoolean("active")).isTrue(); } }); + + await().atMost(java.time.Duration.ofSeconds(5)).untilAsserted(() -> { + final Optional task = taskStatusRepository.findById(taskId); + assertThat(task.isEmpty()).isFalse(); + assertThat(task.stream().allMatch(t -> COMPLETED.name().equals(t.getStatus()))).isTrue(); + }); } } diff --git a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/PostgresIntegrationTestBase.java b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/PostgresIntegrationTestBase.java index 4ec5ba7..32e00dc 100644 --- a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/PostgresIntegrationTestBase.java +++ b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/PostgresIntegrationTestBase.java @@ -11,6 +11,8 @@ @SpringBootTest public abstract class PostgresIntegrationTestBase { + public static final String ID_KEY = "id"; + @Container static PostgreSQLContainer postgres = new PostgreSQLContainer<>("postgres:16-alpine") @@ -30,8 +32,10 @@ static void registerDataSourceProperties(DynamicPropertyRegistry registry) { registry.add("spring.jpa.database-platform", () -> "org.hibernate.dialect.PostgreSQLDialect"); registry.add("spring.jpa.hibernate.ddl-auto", () -> "none"); - // Liquibase MUST run - registry.add("spring.liquibase.enabled", () -> "true"); + // Enable Flyway for tests + registry.add("spring.flyway.enabled", () -> "true"); + // Point Flyway to test migrations only + registry.add("spring.flyway.locations", () -> "classpath:db/migration"); // Always use UTC registry.add("spring.jpa.properties.hibernate.jdbc.time_zone", () -> "UTC"); diff --git a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/PriorityBasedSchedulingIntegrationTest.java b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/PriorityBasedSchedulingIntegrationTest.java index 74c80dd..ef4ff7a 100644 --- a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/PriorityBasedSchedulingIntegrationTest.java +++ b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/PriorityBasedSchedulingIntegrationTest.java @@ -1,24 +1,28 @@ package uk.gov.hmcts.cp.taskmanager.integration; +import static jakarta.json.Json.createObjectBuilder; import static java.time.ZonedDateTime.now; +import static java.util.UUID.randomUUID; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; - -import java.time.ZonedDateTime; -import java.util.List; -import java.util.UUID; +import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionStatus.COMPLETED; import uk.gov.hmcts.cp.taskmanager.domain.ExecutionInfo; import uk.gov.hmcts.cp.taskmanager.domain.ExecutionStatus; import uk.gov.hmcts.cp.taskmanager.integration.config.IntegrationTestConfiguration; +import uk.gov.hmcts.cp.taskmanager.integration.persistence.TaskStatus; +import uk.gov.hmcts.cp.taskmanager.integration.persistence.TaskStatusRepository; import uk.gov.hmcts.cp.taskmanager.persistence.entity.Job; import uk.gov.hmcts.cp.taskmanager.persistence.repository.JobsRepository; import uk.gov.hmcts.cp.taskmanager.persistence.service.JobService; import uk.gov.hmcts.cp.taskmanager.service.ExecutionService; -import jakarta.json.Json; -import jakarta.json.JsonObject; + +import java.time.ZonedDateTime; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; @@ -41,17 +45,11 @@ class PriorityBasedSchedulingIntegrationTest extends PostgresIntegrationTestBase @Autowired private JobsRepository jobsRepository; - private JsonObject testJobData; - @Autowired private JdbcTemplate jdbcTemplate; - @BeforeEach - void setUp() { - testJobData = Json.createObjectBuilder() - .add("test", "data") - .build(); - } + @Autowired + private TaskStatusRepository taskStatusRepository; @AfterEach void tearDown() { @@ -63,21 +61,33 @@ void tearDown() { @Test void testJobsOrderedByPriority() { // Given - Create jobs with different priorities - ZonedDateTime startTime = now().minusSeconds(1); + final ZonedDateTime startTime = now().minusSeconds(1); // Create job with priority 5 (medium) + final UUID taskId1 = randomUUID(); executionService.executeWith(new ExecutionInfo( - testJobData, "TEST_COMPLETED_TASK", startTime, ExecutionStatus.STARTED, false, 5 + createObjectBuilder() + .add("test", "data") + .add(ID_KEY, taskId1.toString()) + .build(), "TEST_COMPLETED_TASK", startTime, ExecutionStatus.STARTED, false, 5 )); // Create job with priority 1 (highest) + final UUID taskId2 = randomUUID(); executionService.executeWith(new ExecutionInfo( - testJobData, "TEST_COMPLETED_TASK", startTime, ExecutionStatus.STARTED, false, 1 + createObjectBuilder() + .add("test", "data") + .add(ID_KEY, taskId2.toString()) + .build(), "TEST_COMPLETED_TASK", startTime, ExecutionStatus.STARTED, false, 1 )); // Create job with priority 10 (lowest) + final UUID taskId3 = randomUUID(); executionService.executeWith(new ExecutionInfo( - testJobData, "TEST_COMPLETED_TASK", startTime, ExecutionStatus.STARTED, false, 10 + createObjectBuilder() + .add("test", "data") + .add(ID_KEY, taskId3.toString()) + .build(), "TEST_COMPLETED_TASK", startTime, ExecutionStatus.STARTED, false, 10 )); // When - Query unassigned jobs @@ -92,6 +102,12 @@ void testJobsOrderedByPriority() { assertThat(jobs.get(2).getPriority()).isEqualTo(10); } }); + + await().atMost(java.time.Duration.ofSeconds(5)).untilAsserted(() -> { + final List tasks = taskStatusRepository.findAllById(List.of(taskId1, taskId2, taskId3)); + assertThat(tasks.size()).isEqualTo(3); + assertThat(tasks.stream().allMatch(t -> COMPLETED.name().equals(t.getStatus()))).isTrue(); + }); } @Test @@ -100,16 +116,24 @@ void testJobsWithSamePriorityOrderedByStartTime() { ZonedDateTime earlierTime = now().minusSeconds(5); ZonedDateTime laterTime = now().minusSeconds(2); - UUID laterJobId = UUID.randomUUID(); + UUID laterJobId = randomUUID(); // Create job with earlier start time + final UUID taskId1 = randomUUID(); executionService.executeWith(new ExecutionInfo( - testJobData, "TEST_COMPLETED_TASK", earlierTime, ExecutionStatus.STARTED, false, 5 + createObjectBuilder() + .add("test", "data") + .add(ID_KEY, taskId1.toString()) + .build(), "TEST_COMPLETED_TASK", earlierTime, ExecutionStatus.STARTED, false, 5 )); // Create job with later start time + final UUID taskId2 = randomUUID(); executionService.executeWith(new ExecutionInfo( - testJobData, "TEST_COMPLETED_TASK", laterTime, ExecutionStatus.STARTED, false, 5 + createObjectBuilder() + .add("test", "data") + .add(ID_KEY, taskId2.toString()) + .build(), "TEST_COMPLETED_TASK", laterTime, ExecutionStatus.STARTED, false, 5 )); // When - Query unassigned jobs @@ -123,6 +147,12 @@ void testJobsWithSamePriorityOrderedByStartTime() { .isBeforeOrEqualTo(jobs.get(1).getAssignedTaskStartTime()); } }); + + await().atMost(java.time.Duration.ofSeconds(5)).untilAsserted(() -> { + final List tasks = taskStatusRepository.findAllById(List.of(taskId1, taskId2)); + assertThat(tasks.size()).isEqualTo(2); + assertThat(tasks.stream().allMatch(t -> COMPLETED.name().equals(t.getStatus()))).isTrue(); + }); } @Test @@ -131,13 +161,21 @@ void testHighPriorityJobExecutedFirst() { ZonedDateTime startTime = now().minusSeconds(1); // Create low priority job first + final UUID taskId1 = randomUUID(); executionService.executeWith(new ExecutionInfo( - testJobData, "TEST_COMPLETED_TASK", startTime, ExecutionStatus.STARTED, false, 10 + createObjectBuilder() + .add("test", "data") + .add(ID_KEY, taskId1.toString()) + .build(), "TEST_COMPLETED_TASK", startTime, ExecutionStatus.STARTED, false, 10 )); // Create high priority job second + final UUID taskId2 = randomUUID(); executionService.executeWith(new ExecutionInfo( - testJobData, "TEST_COMPLETED_TASK", startTime, ExecutionStatus.STARTED, false, 1 + createObjectBuilder() + .add("test", "data") + .add(ID_KEY, taskId2.toString()) + .build(), "TEST_COMPLETED_TASK", startTime, ExecutionStatus.STARTED, false, 1 )); // When - Wait for execution @@ -147,13 +185,24 @@ void testHighPriorityJobExecutedFirst() { // Both should eventually complete, but high priority should complete first assertThat(remainingJobs.size()).isLessThanOrEqualTo(1); }); + + await().atMost(java.time.Duration.ofSeconds(5)).untilAsserted(() -> { + final List tasks = taskStatusRepository.findAllById(List.of(taskId1, taskId2)); + assertThat(tasks.size()).isEqualTo(2); + assertThat(tasks.stream().allMatch(t -> COMPLETED.name().equals(t.getStatus()))).isTrue(); + }); + } @Test void testDefaultPriorityIsTen() { // Given - Create job without specifying priority + final UUID taskId = randomUUID(); ExecutionInfo executionInfo = new ExecutionInfo( - testJobData, + createObjectBuilder() + .add("test", "data") + .add(ID_KEY, taskId.toString()) + .build(), "TEST_COMPLETED_TASK", now().minusSeconds(1), ExecutionStatus.STARTED, @@ -171,6 +220,13 @@ void testDefaultPriorityIsTen() { assertThat(jobs.get(0).getPriority()).isEqualTo(10); } }); + + await().atMost(java.time.Duration.ofSeconds(5)).untilAsserted(() -> { + final Optional taskById = taskStatusRepository.findById(taskId); + assertThat(taskById.isEmpty()).isFalse(); + assertThat(taskById.stream().allMatch(t -> COMPLETED.name().equals(t.getStatus()))).isTrue(); + }); + } } diff --git a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/RetryMechanismIntegrationTest.java b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/RetryMechanismIntegrationTest.java index 816f62b..daf56af 100644 --- a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/RetryMechanismIntegrationTest.java +++ b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/RetryMechanismIntegrationTest.java @@ -1,28 +1,33 @@ package uk.gov.hmcts.cp.taskmanager.integration; +import static jakarta.json.Json.createObjectBuilder; +import static java.time.ZonedDateTime.now; +import static java.util.UUID.randomUUID; +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionStatus.COMPLETED; + import uk.gov.hmcts.cp.taskmanager.domain.ExecutionInfo; import uk.gov.hmcts.cp.taskmanager.domain.ExecutionStatus; import uk.gov.hmcts.cp.taskmanager.integration.config.IntegrationTestConfiguration; +import uk.gov.hmcts.cp.taskmanager.integration.persistence.TaskStatus; +import uk.gov.hmcts.cp.taskmanager.integration.persistence.TaskStatusRepository; import uk.gov.hmcts.cp.taskmanager.persistence.entity.Job; import uk.gov.hmcts.cp.taskmanager.persistence.repository.JobsRepository; import uk.gov.hmcts.cp.taskmanager.persistence.service.JobService; import uk.gov.hmcts.cp.taskmanager.service.ExecutionService; -import jakarta.json.Json; -import jakarta.json.JsonObject; + +import java.time.ZonedDateTime; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.test.annotation.DirtiesContext; -import java.time.ZonedDateTime; -import java.util.List; - -import static java.time.ZonedDateTime.now; -import static org.assertj.core.api.Assertions.assertThat; -import static org.awaitility.Awaitility.await; - /** * Integration tests for retry mechanism. * Tests that tasks with retry configuration are retried with exponential backoff. @@ -40,17 +45,11 @@ class RetryMechanismIntegrationTest extends PostgresIntegrationTestBase { @Autowired private JobsRepository jobsRepository; - private JsonObject testJobData; - @Autowired private JdbcTemplate jdbcTemplate; - @BeforeEach - void setUp() { - testJobData = Json.createObjectBuilder() - .add("test", "data") - .build(); - } + @Autowired + private TaskStatusRepository taskStatusRepository; @AfterEach void tearDown() { @@ -62,8 +61,12 @@ void tearDown() { @Test void testRetryTaskDecrementsAttempts() { // Given - Create a job with retry task + final UUID taskId = randomUUID(); ExecutionInfo executionInfo = new ExecutionInfo( - testJobData, + createObjectBuilder() + .add("test", "data") + .add(ID_KEY, taskId.toString()) + .build(), "TEST_RETRY_TASK", now().minusSeconds(1), ExecutionStatus.STARTED, @@ -76,7 +79,7 @@ void testRetryTaskDecrementsAttempts() { List jobs = jobsRepository.findAll(); // Job should still exist (not deleted) because it's retrying assertThat(jobs).isNotEmpty(); - + Job job = jobs.get(0); // Retry attempts should be decremented (starts with 3, should be 2 after first retry) assertThat(job.getRetryAttemptsRemaining()).isLessThan(3); @@ -86,8 +89,12 @@ void testRetryTaskDecrementsAttempts() { @Test void testRetryScheduledWithDelay() { // Given - Create a job with retry task + final UUID taskId = randomUUID(); ExecutionInfo executionInfo = new ExecutionInfo( - testJobData, + createObjectBuilder() + .add("test", "data") + .add(ID_KEY, taskId.toString()) + .build(), "TEST_RETRY_TASK", now().minusSeconds(1), ExecutionStatus.STARTED, @@ -106,13 +113,21 @@ void testRetryScheduledWithDelay() { assertThat(job.getAssignedTaskStartTime()).isAfter(initialStartTime); } }); + + final Optional task = taskStatusRepository.findById(taskId); + assertThat(task.isEmpty()).isFalse(); + assertThat(task.stream().allMatch(t -> COMPLETED.name().equals(t.getStatus()))).isTrue(); } @Test void testRetryExhausted() { // Given - Create a job with retry task that will exhaust retries + final UUID taskId = randomUUID(); ExecutionInfo executionInfo = new ExecutionInfo( - testJobData, + createObjectBuilder() + .add("test", "data") + .add(ID_KEY, taskId.toString()) + .build(), "TEST_RETRY_TASK", now().minusSeconds(1), ExecutionStatus.STARTED, @@ -130,13 +145,21 @@ void testRetryExhausted() { assertThat(job.getRetryAttemptsRemaining()).isEqualTo(0); } }); + + final Optional task = taskStatusRepository.findById(taskId); + assertThat(task.isEmpty()).isFalse(); + assertThat(task.stream().allMatch(t -> COMPLETED.name().equals(t.getStatus()))).isTrue(); } @Test void testRetryTaskWithNoRetryConfiguration() { // Given - Create a job with a task that doesn't have retry configuration + final UUID taskId = randomUUID(); ExecutionInfo executionInfo = new ExecutionInfo( - testJobData, + createObjectBuilder() + .add("test", "data") + .add(ID_KEY, taskId.toString()) + .build(), "TEST_COMPLETED_TASK", // This task doesn't have retry configuration now().minusSeconds(1), ExecutionStatus.STARTED, @@ -150,6 +173,10 @@ void testRetryTaskWithNoRetryConfiguration() { List jobs = jobsRepository.findAll(); assertThat(jobs).isEmpty(); // Job should be completed and deleted }); + + final Optional task = taskStatusRepository.findById(taskId); + assertThat(task.isEmpty()).isFalse(); + assertThat(task.stream().allMatch(t -> COMPLETED.name().equals(t.getStatus()))).isTrue(); } } diff --git a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/WorkflowExecutionIntegrationTest.java b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/WorkflowExecutionIntegrationTest.java index 5c61c05..c6da305 100644 --- a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/WorkflowExecutionIntegrationTest.java +++ b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/WorkflowExecutionIntegrationTest.java @@ -1,21 +1,26 @@ package uk.gov.hmcts.cp.taskmanager.integration; +import static jakarta.json.Json.createObjectBuilder; import static java.time.ZonedDateTime.now; +import static java.util.UUID.randomUUID; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; - -import java.util.List; +import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionStatus.COMPLETED; import uk.gov.hmcts.cp.taskmanager.domain.ExecutionInfo; import uk.gov.hmcts.cp.taskmanager.domain.ExecutionStatus; import uk.gov.hmcts.cp.taskmanager.integration.config.IntegrationTestConfiguration; +import uk.gov.hmcts.cp.taskmanager.integration.persistence.TaskStatus; +import uk.gov.hmcts.cp.taskmanager.integration.persistence.TaskStatusRepository; import uk.gov.hmcts.cp.taskmanager.persistence.entity.Job; import uk.gov.hmcts.cp.taskmanager.persistence.repository.JobsRepository; import uk.gov.hmcts.cp.taskmanager.service.ExecutionService; -import jakarta.json.Json; -import jakarta.json.JsonObject; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; @@ -35,17 +40,11 @@ class WorkflowExecutionIntegrationTest extends PostgresIntegrationTestBase { @Autowired private JobsRepository jobsRepository; - private JsonObject testJobData; - @Autowired private JdbcTemplate jdbcTemplate; - @BeforeEach - void setUp() { - testJobData = Json.createObjectBuilder() - .add("workflow", "test") - .build(); - } + @Autowired + private TaskStatusRepository taskStatusRepository; @AfterEach void tearDown() { @@ -57,8 +56,12 @@ void tearDown() { @Test void testWorkflowExecution() { // Given - Create a workflow job starting with TEST_WORKFLOW_TASK_1 - ExecutionInfo executionInfo = new ExecutionInfo( - testJobData, + final UUID taskId = randomUUID(); + final ExecutionInfo executionInfo = new ExecutionInfo( + createObjectBuilder() + .add("test", "data") + .add(ID_KEY, taskId.toString()) + .build(), "TEST_WORKFLOW_TASK_1", now().minusSeconds(1), ExecutionStatus.STARTED, @@ -69,27 +72,37 @@ void testWorkflowExecution() { // When - Wait for workflow to complete // Then - Job should progress through workflow and eventually complete await().atMost(java.time.Duration.ofSeconds(15)).untilAsserted(() -> { - List jobs = jobsRepository.findAll(); + final List jobs = jobsRepository.findAll(); // Workflow should eventually complete (job deleted) // Or if still in progress, should be on second task if (!jobs.isEmpty()) { - Job job = jobs.get(0); + final Job job = jobs.get(0); + assertThat(job.getWorkerId()).isNotNull(); // Should be on TEST_WORKFLOW_TASK_2 or completed - assertThat(job.getAssignedTaskName()) - .isIn("TEST_WORKFLOW_TASK_1", "TEST_WORKFLOW_TASK_2"); + assertThat(job.getAssignedTaskName()).isIn("TEST_WORKFLOW_TASK_1", "TEST_WORKFLOW_TASK_2"); } else { // Workflow completed successfully assertThat(jobs).isEmpty(); } }); + + await().atMost(java.time.Duration.ofSeconds(10)).untilAsserted(() -> { + final Optional task = taskStatusRepository.findById(taskId); + assertThat(task.isEmpty()).isFalse(); + assertThat(task.stream().allMatch(t -> COMPLETED.name().equals(t.getStatus()))).isTrue(); + }); } @Test void testWorkflowTaskTransition() { // Given - Create a workflow job - ExecutionInfo executionInfo = new ExecutionInfo( - testJobData, + final UUID taskId = randomUUID(); + final ExecutionInfo executionInfo = new ExecutionInfo( + createObjectBuilder() + .add("test", "data") + .add(ID_KEY, taskId.toString()) + .build(), "TEST_WORKFLOW_TASK_1", now().minusSeconds(1), ExecutionStatus.STARTED, @@ -102,18 +115,29 @@ void testWorkflowTaskTransition() { await().atMost(java.time.Duration.ofSeconds(10)).untilAsserted(() -> { List jobs = jobsRepository.findAll(); if (!jobs.isEmpty()) { - Job job = jobs.get(0); + final Job job = jobs.get(0); + assertThat(job.getWorkerId()).isNotNull(); // Should have transitioned to TEST_WORKFLOW_TASK_2 assertThat(job.getAssignedTaskName()).isEqualTo("TEST_WORKFLOW_TASK_2"); } }); + + await().atMost(java.time.Duration.ofSeconds(10)).untilAsserted(() -> { + final Optional task = taskStatusRepository.findById(taskId); + assertThat(task.isEmpty()).isFalse(); + assertThat(task.stream().allMatch(t -> COMPLETED.name().equals(t.getStatus()))).isTrue(); + }); } @Test void testWorkflowCompletion() { // Given - Create a workflow job - ExecutionInfo executionInfo = new ExecutionInfo( - testJobData, + final UUID taskId = randomUUID(); + final ExecutionInfo executionInfo = new ExecutionInfo( + createObjectBuilder() + .add("test", "data") + .add(ID_KEY, taskId.toString()) + .build(), "TEST_WORKFLOW_TASK_1", now().minusSeconds(1), ExecutionStatus.STARTED, @@ -127,13 +151,23 @@ void testWorkflowCompletion() { List jobs = jobsRepository.findAll(); assertThat(jobs).isEmpty(); // Workflow completed, job deleted }); + + await().atMost(java.time.Duration.ofSeconds(10)).untilAsserted(() -> { + final Optional task = taskStatusRepository.findById(taskId); + assertThat(task.isEmpty()).isFalse(); + assertThat(task.stream().allMatch(t -> COMPLETED.name().equals(t.getStatus()))).isTrue(); + }); } @Test void testSpawnMultipleTasksInTheWorkflow() { // Given - Create a task that internally spawns/schedules multiple jobs + final UUID taskId = randomUUID(); ExecutionInfo executionInfo = new ExecutionInfo( - testJobData, + createObjectBuilder() + .add("test", "data") + .add(ID_KEY, taskId.toString()) + .build(), "TEST_SCHEDULE_MULTI_JOBS_TASK", now().minusSeconds(1), ExecutionStatus.STARTED, @@ -144,9 +178,15 @@ void testSpawnMultipleTasksInTheWorkflow() { // When - Wait for all jobs execution // Then - all the jobs should be deleted after completion await().atMost(java.time.Duration.ofSeconds(20)).untilAsserted(() -> { - List jobs = jobsRepository.findAll(); + final List jobs = jobsRepository.findAll(); assertThat(jobs).isEmpty(); }); + + await().atMost(java.time.Duration.ofSeconds(10)).untilAsserted(() -> { + final Optional task = taskStatusRepository.findById(taskId); + assertThat(task.isEmpty()).isFalse(); + assertThat(task.stream().allMatch(t -> COMPLETED.name().equals(t.getStatus()))).isTrue(); + }); } } diff --git a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/persistence/TaskStatus.java b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/persistence/TaskStatus.java new file mode 100644 index 0000000..e155630 --- /dev/null +++ b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/persistence/TaskStatus.java @@ -0,0 +1,41 @@ +package uk.gov.hmcts.cp.taskmanager.integration.persistence; + +import uk.gov.hmcts.cp.taskmanager.domain.converter.JsonObjectConverter; + +import java.time.OffsetDateTime; +import java.util.UUID; + +import jakarta.json.JsonObject; +import jakarta.persistence.Column; +import jakarta.persistence.Convert; +import jakarta.persistence.Entity; +import jakarta.persistence.Id; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Table(name = "task_status") +public class TaskStatus { + + @Id + @Column(name = "id", nullable = false) + private UUID id; + + @Column(name = "job_data", columnDefinition = "TEXT") + @Convert(converter = JsonObjectConverter.class) + private JsonObject jobData; + + @Column(name = "status", nullable = false) + private String status; + + @Column(name = "created_at", nullable = false) + private OffsetDateTime createdAt; + +} \ No newline at end of file diff --git a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/persistence/TaskStatusRepository.java b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/persistence/TaskStatusRepository.java new file mode 100644 index 0000000..9a1e03a --- /dev/null +++ b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/persistence/TaskStatusRepository.java @@ -0,0 +1,18 @@ +package uk.gov.hmcts.cp.taskmanager.integration.persistence; + + +import java.util.UUID; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Modifying; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +@Repository +public interface TaskStatusRepository extends JpaRepository { + + @Modifying + @Query(value = "UPDATE taskStatus SET status = :status WHERE id = :id", nativeQuery = true) + void updateStatus(@Param("id") UUID id, @Param("status") String status); +} \ No newline at end of file diff --git a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/persistence/TaskStatusService.java b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/persistence/TaskStatusService.java new file mode 100644 index 0000000..21183f0 --- /dev/null +++ b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/persistence/TaskStatusService.java @@ -0,0 +1,28 @@ +package uk.gov.hmcts.cp.taskmanager.integration.persistence; + +import java.util.UUID; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class TaskStatusService { + + private final TaskStatusRepository taskStatusRepository; + + @Autowired + public TaskStatusService(final TaskStatusRepository taskStatusRepository) { + this.taskStatusRepository = taskStatusRepository; + } + + @Transactional + public void insertTaskStatus(final TaskStatus taskStatus) { + taskStatusRepository.save(taskStatus); + } + + @Transactional + public void updateTaskStatus(final UUID id, final String taskStatus) { + taskStatusRepository.updateStatus(id, taskStatus); + } +} diff --git a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/tasks/TestCompletedTask.java b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/tasks/TestCompletedTask.java index 86c2e81..e343346 100644 --- a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/tasks/TestCompletedTask.java +++ b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/tasks/TestCompletedTask.java @@ -1,15 +1,26 @@ package uk.gov.hmcts.cp.taskmanager.integration.tasks; +import static java.time.OffsetDateTime.now; +import static java.util.UUID.fromString; +import static java.util.UUID.randomUUID; +import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionInfo.executionInfo; +import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionStatus.COMPLETED; +import static uk.gov.hmcts.cp.taskmanager.integration.PostgresIntegrationTestBase.ID_KEY; + import uk.gov.hmcts.cp.taskmanager.domain.ExecutionInfo; +import uk.gov.hmcts.cp.taskmanager.integration.persistence.TaskStatus; +import uk.gov.hmcts.cp.taskmanager.integration.persistence.TaskStatusService; import uk.gov.hmcts.cp.taskmanager.service.task.ExecutableTask; import uk.gov.hmcts.cp.taskmanager.service.task.Task; + +import java.util.UUID; + +import jakarta.json.JsonObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionInfo.executionInfo; -import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionStatus.COMPLETED; - /** * Test task that completes immediately. * Used for integration testing of successful job execution. @@ -17,12 +28,21 @@ @Task("TEST_COMPLETED_TASK") @Component public class TestCompletedTask implements ExecutableTask { - + + @Autowired + private TaskStatusService taskStatusService; + private static final Logger logger = LoggerFactory.getLogger(TestCompletedTask.class); - + @Override public ExecutionInfo execute(ExecutionInfo executionInfo) { - logger.info("TestCompletedTask executing for job: {}", executionInfo.getJobData()); + final JsonObject jobData = executionInfo.getJobData(); + + logger.info("TestCompletedTask executing for job: {}", jobData); + + final UUID id = jobData.containsKey(ID_KEY) ? fromString(jobData.getString(ID_KEY)) : randomUUID(); + taskStatusService.insertTaskStatus(new TaskStatus(id, jobData, COMPLETED.name(), now())); + return executionInfo().from(executionInfo) .withExecutionStatus(COMPLETED) .build(); diff --git a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/tasks/TestInProgressTask.java b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/tasks/TestInProgressTask.java index 583b0bd..6650855 100644 --- a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/tasks/TestInProgressTask.java +++ b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/tasks/TestInProgressTask.java @@ -1,15 +1,27 @@ package uk.gov.hmcts.cp.taskmanager.integration.tasks; +import static java.time.OffsetDateTime.now; +import static java.util.UUID.fromString; +import static java.util.UUID.randomUUID; +import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionInfo.executionInfo; +import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionStatus.COMPLETED; +import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionStatus.INPROGRESS; +import static uk.gov.hmcts.cp.taskmanager.integration.PostgresIntegrationTestBase.ID_KEY; + import uk.gov.hmcts.cp.taskmanager.domain.ExecutionInfo; +import uk.gov.hmcts.cp.taskmanager.integration.persistence.TaskStatus; +import uk.gov.hmcts.cp.taskmanager.integration.persistence.TaskStatusService; import uk.gov.hmcts.cp.taskmanager.service.task.ExecutableTask; import uk.gov.hmcts.cp.taskmanager.service.task.Task; + +import java.util.UUID; + +import jakarta.json.JsonObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionInfo.executionInfo; -import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionStatus.INPROGRESS; - /** * Test task that returns INPROGRESS status. * Used for integration testing of workflow continuation. @@ -17,12 +29,19 @@ @Task("TEST_INPROGRESS_TASK") @Component public class TestInProgressTask implements ExecutableTask { - + private static final Logger logger = LoggerFactory.getLogger(TestInProgressTask.class); - + @Autowired + private TaskStatusService taskStatusService; + @Override public ExecutionInfo execute(ExecutionInfo executionInfo) { - logger.info("TestInProgressTask executing for job: {}", executionInfo); + final JsonObject jobData = executionInfo.getJobData(); + logger.info("TestInProgressTask executing for job: {}", jobData); + + final UUID id = jobData.containsKey(ID_KEY) ? fromString(jobData.getString(ID_KEY)) : randomUUID(); + taskStatusService.insertTaskStatus(new TaskStatus(id, jobData, COMPLETED.name(), now())); + return executionInfo().from(executionInfo) .withExecutionStatus(INPROGRESS) .build(); diff --git a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/tasks/TestRetryTask.java b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/tasks/TestRetryTask.java index 1c5442e..21b0d97 100644 --- a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/tasks/TestRetryTask.java +++ b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/tasks/TestRetryTask.java @@ -1,17 +1,28 @@ package uk.gov.hmcts.cp.taskmanager.integration.tasks; +import static java.time.OffsetDateTime.now; +import static java.util.UUID.fromString; +import static java.util.UUID.randomUUID; +import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionInfo.executionInfo; +import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionStatus.COMPLETED; +import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionStatus.INPROGRESS; +import static uk.gov.hmcts.cp.taskmanager.integration.PostgresIntegrationTestBase.ID_KEY; + import uk.gov.hmcts.cp.taskmanager.domain.ExecutionInfo; +import uk.gov.hmcts.cp.taskmanager.integration.persistence.TaskStatus; +import uk.gov.hmcts.cp.taskmanager.integration.persistence.TaskStatusService; import uk.gov.hmcts.cp.taskmanager.service.task.ExecutableTask; import uk.gov.hmcts.cp.taskmanager.service.task.Task; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; import java.util.List; import java.util.Optional; +import java.util.UUID; -import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionInfo.executionInfo; -import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionStatus.INPROGRESS; +import jakarta.json.JsonObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; /** * Test task that simulates retry behavior. @@ -21,18 +32,28 @@ @Task("TEST_RETRY_TASK") @Component public class TestRetryTask implements ExecutableTask { - + + @Autowired + private TaskStatusService taskStatusService; + private static final Logger logger = LoggerFactory.getLogger(TestRetryTask.class); - + @Override public ExecutionInfo execute(ExecutionInfo executionInfo) { - logger.info("TestRetryTask executing for job: {}", executionInfo); + + final JsonObject jobData = executionInfo.getJobData(); + + logger.info("TestRetryTask executing for job: {}", jobData); + + final UUID id = jobData.containsKey(ID_KEY) ? fromString(jobData.getString(ID_KEY)) : randomUUID(); + taskStatusService.insertTaskStatus(new TaskStatus(id, jobData, COMPLETED.name(), now())); + return executionInfo().from(executionInfo) .withExecutionStatus(INPROGRESS) .withShouldRetry(true) .build(); } - + @Override public Optional> getRetryDurationsInSecs() { // Return 3 retry attempts with delays: 1s, 2s, 3s diff --git a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/tasks/TestSpawnMultipleJobsTask.java b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/tasks/TestSpawnMultipleJobsTask.java index 564c692..176a127 100644 --- a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/tasks/TestSpawnMultipleJobsTask.java +++ b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/tasks/TestSpawnMultipleJobsTask.java @@ -1,15 +1,23 @@ package uk.gov.hmcts.cp.taskmanager.integration.tasks; +import static java.time.OffsetDateTime.now; +import static java.util.UUID.fromString; +import static java.util.UUID.randomUUID; import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionInfo.executionInfo; import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionStatus.COMPLETED; - -import java.time.ZonedDateTime; +import static uk.gov.hmcts.cp.taskmanager.integration.PostgresIntegrationTestBase.ID_KEY; import uk.gov.hmcts.cp.taskmanager.domain.ExecutionInfo; import uk.gov.hmcts.cp.taskmanager.domain.ExecutionStatus; +import uk.gov.hmcts.cp.taskmanager.integration.persistence.TaskStatus; +import uk.gov.hmcts.cp.taskmanager.integration.persistence.TaskStatusService; import uk.gov.hmcts.cp.taskmanager.service.ExecutionService; import uk.gov.hmcts.cp.taskmanager.service.task.ExecutableTask; import uk.gov.hmcts.cp.taskmanager.service.task.Task; + +import java.time.ZonedDateTime; +import java.util.UUID; + import jakarta.json.Json; import jakarta.json.JsonObject; import org.slf4j.Logger; @@ -26,16 +34,22 @@ public class TestSpawnMultipleJobsTask implements ExecutableTask { private static final Logger logger = LoggerFactory.getLogger(TestSpawnMultipleJobsTask.class); + @Autowired + private TaskStatusService taskStatusService; + @Autowired private ExecutionService executionService; @Override public ExecutionInfo execute(ExecutionInfo executionInfo) { - logger.info("TestSpawnMultipleJobsTask executing for job: {}", executionInfo); + final JsonObject jobDataPayload = executionInfo.getJobData(); + + logger.info("TestSpawnMultipleJobsTask executing for job: {}", jobDataPayload); for (int i = 0; i < 10; i++) { final JsonObject jobData = Json.createObjectBuilder() .add("workflow", "test") + .add(ID_KEY, randomUUID().toString()) .add("item", i) .build(); ExecutionInfo childTask = new ExecutionInfo( @@ -49,6 +63,9 @@ public ExecutionInfo execute(ExecutionInfo executionInfo) { executionService.executeWith(childTask); } + final UUID id = jobDataPayload.containsKey(ID_KEY) ? fromString(jobDataPayload.getString(ID_KEY)) : randomUUID(); + taskStatusService.insertTaskStatus(new TaskStatus(id, jobDataPayload, COMPLETED.name(), now())); + return executionInfo().from(executionInfo) .withExecutionStatus(COMPLETED) .build(); diff --git a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/tasks/TestWorkflowTask1.java b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/tasks/TestWorkflowTask1.java index becea3d..e2e169f 100644 --- a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/tasks/TestWorkflowTask1.java +++ b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/tasks/TestWorkflowTask1.java @@ -1,15 +1,26 @@ package uk.gov.hmcts.cp.taskmanager.integration.tasks; +import static java.time.OffsetDateTime.now; +import static java.util.UUID.fromString; +import static java.util.UUID.randomUUID; +import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionInfo.executionInfo; +import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionStatus.INPROGRESS; +import static uk.gov.hmcts.cp.taskmanager.integration.PostgresIntegrationTestBase.ID_KEY; + import uk.gov.hmcts.cp.taskmanager.domain.ExecutionInfo; +import uk.gov.hmcts.cp.taskmanager.integration.persistence.TaskStatus; +import uk.gov.hmcts.cp.taskmanager.integration.persistence.TaskStatusService; import uk.gov.hmcts.cp.taskmanager.service.task.ExecutableTask; import uk.gov.hmcts.cp.taskmanager.service.task.Task; + +import java.util.UUID; + +import jakarta.json.JsonObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionInfo.executionInfo; -import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionStatus.INPROGRESS; - /** * First step in a test workflow. * Returns INPROGRESS to continue to next task. @@ -17,12 +28,21 @@ @Task("TEST_WORKFLOW_TASK_1") @Component public class TestWorkflowTask1 implements ExecutableTask { - + + @Autowired + private TaskStatusService taskStatusService; + private static final Logger logger = LoggerFactory.getLogger(TestWorkflowTask1.class); - + @Override public ExecutionInfo execute(ExecutionInfo executionInfo) { - logger.info("TestWorkflowTask1 executing for job: {}", executionInfo); + final JsonObject jobData = executionInfo.getJobData(); + + logger.info("TestWorkflowTask1 executing for job: {}", jobData); + + final UUID id = jobData.containsKey(ID_KEY) ? fromString(jobData.getString(ID_KEY)) : randomUUID(); + taskStatusService.insertTaskStatus(new TaskStatus(id, jobData, INPROGRESS.name(), now())); + return executionInfo().from(executionInfo) .withAssignedTaskName("TEST_WORKFLOW_TASK_2") .withExecutionStatus(INPROGRESS) diff --git a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/tasks/TestWorkflowTask2.java b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/tasks/TestWorkflowTask2.java index 699fcad..de10edf 100644 --- a/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/tasks/TestWorkflowTask2.java +++ b/integration-tests/src/test/java/uk/gov/hmcts/cp/taskmanager/integration/tasks/TestWorkflowTask2.java @@ -1,15 +1,26 @@ package uk.gov.hmcts.cp.taskmanager.integration.tasks; +import static java.time.OffsetDateTime.now; +import static java.util.UUID.fromString; +import static java.util.UUID.randomUUID; +import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionInfo.executionInfo; +import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionStatus.COMPLETED; +import static uk.gov.hmcts.cp.taskmanager.integration.PostgresIntegrationTestBase.ID_KEY; + import uk.gov.hmcts.cp.taskmanager.domain.ExecutionInfo; +import uk.gov.hmcts.cp.taskmanager.integration.persistence.TaskStatus; +import uk.gov.hmcts.cp.taskmanager.integration.persistence.TaskStatusService; import uk.gov.hmcts.cp.taskmanager.service.task.ExecutableTask; import uk.gov.hmcts.cp.taskmanager.service.task.Task; + +import java.util.UUID; + +import jakarta.json.JsonObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionInfo.executionInfo; -import static uk.gov.hmcts.cp.taskmanager.domain.ExecutionStatus.COMPLETED; - /** * Second step in a test workflow. * Completes the workflow. @@ -17,12 +28,20 @@ @Task("TEST_WORKFLOW_TASK_2") @Component public class TestWorkflowTask2 implements ExecutableTask { - + private static final Logger logger = LoggerFactory.getLogger(TestWorkflowTask2.class); - + + @Autowired + private TaskStatusService taskStatusService; + @Override public ExecutionInfo execute(ExecutionInfo executionInfo) { - logger.info("TestWorkflowTask2 executing for job: {}", executionInfo); + final JsonObject jobData = executionInfo.getJobData(); + logger.info("TestWorkflowTask2 executing for job: {}", jobData); + + final UUID id = jobData.containsKey(ID_KEY) ? fromString(jobData.getString(ID_KEY)) : randomUUID(); + taskStatusService.insertTaskStatus(new TaskStatus(id, jobData, COMPLETED.name(), now())); + return executionInfo().from(executionInfo) .withExecutionStatus(COMPLETED) .build(); diff --git a/integration-tests/src/test/resources/application-test.properties b/integration-tests/src/test/resources/application-test.properties index c4ad44a..4b23f22 100644 --- a/integration-tests/src/test/resources/application-test.properties +++ b/integration-tests/src/test/resources/application-test.properties @@ -21,6 +21,4 @@ job.executor.batch-size=10 # Logging Configuration logging.level.root=INFO logging.level.uk.gov.hmcts.cp.taskmanager=INFO -logging.level.liquibase=INFO -logging.level.org.springframework.boot.autoconfigure.liquibase=INFO diff --git a/integration-tests/src/test/resources/db/migration/V9000__create_task_status_table.sql b/integration-tests/src/test/resources/db/migration/V9000__create_task_status_table.sql new file mode 100644 index 0000000..e90c8f4 --- /dev/null +++ b/integration-tests/src/test/resources/db/migration/V9000__create_task_status_table.sql @@ -0,0 +1,6 @@ +CREATE TABLE task_status ( + id UUID PRIMARY KEY, + job_data TEXT NOT NULL, + status TEXT NOT NULL, + created_at TIMESTAMP WITH TIME ZONE NOT NULL +); \ No newline at end of file diff --git a/jobstore-flyway/build.gradle b/jobstore-flyway/build.gradle index ac31e3a..6191376 100644 --- a/jobstore-flyway/build.gradle +++ b/jobstore-flyway/build.gradle @@ -3,5 +3,6 @@ plugins { } dependencies { - // No dependencies - this module only contains resources + //run flyway migration script on the start + api "org.springframework.boot:spring-boot-starter-flyway:4.0.0" } diff --git a/jobstore-flyway/run-flyway.sh b/jobstore-flyway/run-flyway.sh new file mode 100755 index 0000000..5b13459 --- /dev/null +++ b/jobstore-flyway/run-flyway.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +DB_NAME="job_scheduler_db" +DB_URL="jdbc:postgresql://localhost:5435/${DB_NAME}" +DB_USER="postgres" +DB_PASSWORD="postgres" + +# Fail script on error +set -e + +function runJobStoreFlyway() { + echo "Running jobstore Flyway migrations..." + + flyway \ + -url="${DB_URL}" \ + -user="${DB_USER}" \ + -password="${DB_PASSWORD}" \ + -locations=filesystem:src/main/resources/db/migration \ + -baselineOnMigrate=true \ + migrate + + echo "Finished running jobstore Flyway migrations" +} + +runJobStoreFlyway \ No newline at end of file diff --git a/jobstore-flyway/src/main/java/uk/gov/hmcts/cp/taskmanager/jobstore/autoconfig/TaskManagerAutoConfiguration.java b/jobstore-flyway/src/main/java/uk/gov/hmcts/cp/taskmanager/jobstore/autoconfig/TaskManagerAutoConfiguration.java new file mode 100644 index 0000000..e92f12d --- /dev/null +++ b/jobstore-flyway/src/main/java/uk/gov/hmcts/cp/taskmanager/jobstore/autoconfig/TaskManagerAutoConfiguration.java @@ -0,0 +1,16 @@ +package uk.gov.hmcts.cp.taskmanager.jobstore.autoconfig; + +import org.springframework.boot.autoconfigure.AutoConfiguration; +import org.springframework.boot.flyway.autoconfigure.FlywayConfigurationCustomizer; +import org.springframework.context.annotation.Bean; + +@AutoConfiguration +public class TaskManagerAutoConfiguration { + + @Bean + public FlywayConfigurationCustomizer jobstoreFlywayCustomizer() { + return configuration -> configuration.locations( + "classpath:db/migration" + ); + } +} diff --git a/jobstore-flyway/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/jobstore-flyway/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 0000000..64e5af7 --- /dev/null +++ b/jobstore-flyway/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +uk.gov.hmcts.cp.taskmanager.jobstore.autoconfig.TaskManagerAutoConfiguration \ No newline at end of file diff --git a/task-manager-service/build.gradle b/task-manager-service/build.gradle index 68439f3..9b54e92 100644 --- a/task-manager-service/build.gradle +++ b/task-manager-service/build.gradle @@ -21,7 +21,10 @@ dependencies { // Spring Context for @Component, @Autowired, etc. api 'org.springframework:spring-context' - + + //jobstore-flyway + api project(":jobstore-flyway") + // Test dependencies testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.mockito:mockito-core'