diff --git a/src/main/java/com/tnite/jobwinner/JobWinnerApplication.java b/src/main/java/com/tnite/jobwinner/JobWinnerApplication.java index eb2fa273..9c8e0b8e 100644 --- a/src/main/java/com/tnite/jobwinner/JobWinnerApplication.java +++ b/src/main/java/com/tnite/jobwinner/JobWinnerApplication.java @@ -2,7 +2,9 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.scheduling.annotation.EnableScheduling; +@EnableScheduling @SpringBootApplication public class JobWinnerApplication { diff --git a/src/main/java/com/tnite/jobwinner/jobs/SetOutdatedInterviewToExpiredJob.java b/src/main/java/com/tnite/jobwinner/jobs/SetOutdatedInterviewToExpiredJob.java new file mode 100644 index 00000000..a78e0d43 --- /dev/null +++ b/src/main/java/com/tnite/jobwinner/jobs/SetOutdatedInterviewToExpiredJob.java @@ -0,0 +1,24 @@ +package com.tnite.jobwinner.jobs; + +import com.tnite.jobwinner.service.InterviewService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +@Slf4j +public class SetOutdatedInterviewToExpiredJob { + + @Autowired + private final InterviewService interviewService; + + @Scheduled(cron = "0 0 1 * * ?") // Runs every night at 1:00 AM + public void updateOutdatedInterviewToExpired() { + log.info("Running scheduled job to set outdated interview to expired"); + interviewService.updateOutdatedInterviewToExpired() + .subscribe(); // Executes the reactive operation + } +} diff --git a/src/main/java/com/tnite/jobwinner/repo/InterviewRepository.java b/src/main/java/com/tnite/jobwinner/repo/InterviewRepository.java index 1df9d993..dcc7b813 100644 --- a/src/main/java/com/tnite/jobwinner/repo/InterviewRepository.java +++ b/src/main/java/com/tnite/jobwinner/repo/InterviewRepository.java @@ -1,9 +1,16 @@ package com.tnite.jobwinner.repo; import com.tnite.jobwinner.model.Interview; +import org.springframework.data.r2dbc.repository.Modifying; +import org.springframework.data.r2dbc.repository.Query; import org.springframework.data.repository.reactive.ReactiveCrudRepository; import reactor.core.publisher.Flux; +import reactor.core.publisher.Mono; public interface InterviewRepository extends ReactiveCrudRepository { Flux findAllByJobApplicationId(Integer jobApplicationId); + + @Modifying + @Query("UPDATE interview SET status = 'Expired' WHERE interview_date < CURRENT_DATE AND status != 'Expired'") + Mono updateExpiredInterviews(); } diff --git a/src/main/java/com/tnite/jobwinner/service/InterviewService.java b/src/main/java/com/tnite/jobwinner/service/InterviewService.java index af8e5807..cfed1541 100644 --- a/src/main/java/com/tnite/jobwinner/service/InterviewService.java +++ b/src/main/java/com/tnite/jobwinner/service/InterviewService.java @@ -9,6 +9,7 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -74,9 +75,6 @@ public Flux getInterviewByJobApplicationId(Integer jobApplicationId) } public Flux allInterview() { -// return interviewRepository.findAll() -// .doOnComplete(() -> log.info("Retrieved all interviews")) -// .doOnError(e -> log.error("Failed to retrieve interviews", e)); return interviewRepository.findAll() .flatMap(interview -> jobApplicationRepository.findById(interview.getJobApplicationId()) @@ -113,4 +111,15 @@ public Mono getInterviewById(Integer id) { .doOnError(e -> log.error("Failed to retrieve interview with id {}", id, e)); } + @Transactional + public Mono updateOutdatedInterviewToExpired() { + return interviewRepository.updateExpiredInterviews() + .doOnNext(count -> { + if (count > 0) { + log.info("{} interview(s) marked as expired.", count); + } else { + log.info("No expired interviews found."); + } + }); + } } diff --git a/src/test/java/com/tnite/jobwinner/service/InterviewServiceTest.java b/src/test/java/com/tnite/jobwinner/service/InterviewServiceTest.java index eb742512..922214d8 100644 --- a/src/test/java/com/tnite/jobwinner/service/InterviewServiceTest.java +++ b/src/test/java/com/tnite/jobwinner/service/InterviewServiceTest.java @@ -17,6 +17,7 @@ import java.time.LocalDate; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @@ -146,4 +147,31 @@ void testAllInterview() { verify(interviewRepository, times(1)).findAll(); verify(jobApplicationRepository, times(2)).findById(anyInt()); } + + @Test + void testUpdateOutdatedInterviewToExpiredWithThreeRecords() { + when(interviewRepository.updateExpiredInterviews()).thenReturn(Mono.just(3)); + + Mono result = interviewService.updateOutdatedInterviewToExpired(); + + StepVerifier.create(result) + .assertNext(updatedRows -> assertEquals(3, updatedRows, "Expected 3 rows to be updated")) + .verifyComplete(); + + verify(interviewRepository, times(1)).updateExpiredInterviews(); + } + + @Test + void testUpdateOutdatedInterviewToExpiredNoRecordsToUpdate() { + when(interviewRepository.updateExpiredInterviews()) + .thenReturn(Mono.just(0)); + + Mono result = interviewService.updateOutdatedInterviewToExpired(); + + StepVerifier.create(result) + .assertNext(updatedRows -> assertEquals(0, updatedRows, "Expected no rows to be updated")) + .verifyComplete(); + + verify(interviewRepository, times(1)).updateExpiredInterviews(); + } }