Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,19 @@
import br.com.springnoobs.reminderapi.reminder.entity.Reminder;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

@Repository
public interface ReminderRepository extends JpaRepository<Reminder, Long> {
Page<Reminder> findAllByOrderByRemindAtAsc(Pageable pageable);

List<Reminder> findBySentFalseAndExecutedAtIsNullAndRemindAtAfter(Instant now);

@Query("SELECT r FROM Reminder r JOIN FETCH r.user u JOIN FETCH u.contact WHERE r.id = :id")
Optional<Reminder> findByIdWithAssociations(Long id);
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,11 @@ public void delete(Long id) {

repository.deleteById(id);
}

public void registerReminderExecution(Reminder reminder) {
reminder.setExecutedAt(Instant.now());
reminder.setSent(true);

repository.save(reminder);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package br.com.springnoobs.reminderapi.schedule.job;

import br.com.springnoobs.reminderapi.mail.service.EmailService;
import br.com.springnoobs.reminderapi.reminder.repository.ReminderRepository;
import br.com.springnoobs.reminderapi.reminder.service.ReminderService;
import java.time.Instant;
import org.quartz.JobExecutionContext;
import org.slf4j.Logger;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;

@Component
public class ReminderJob extends QuartzJobBean {

Logger logger = org.slf4j.LoggerFactory.getLogger(ReminderJob.class);

private final ReminderRepository reminderRepository;

private final ReminderService reminderService;

private final EmailService emailService;

public ReminderJob(
ReminderRepository reminderRepository, ReminderService reminderService, EmailService emailService) {
this.reminderRepository = reminderRepository;
this.reminderService = reminderService;
this.emailService = emailService;
}

@Override
protected void executeInternal(JobExecutionContext context) {
long reminderId = context.getMergedJobDataMap().getLong("reminder-id");

reminderRepository.findByIdWithAssociations(reminderId).ifPresent(reminder -> {
emailService.send(reminder);

reminderService.registerReminderExecution(reminder);

logger.info("Executed reminder {} at {}", reminder.getId(), Instant.now());
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package br.com.springnoobs.reminderapi.schedule.service;

import br.com.springnoobs.reminderapi.reminder.entity.Reminder;
import br.com.springnoobs.reminderapi.schedule.job.ReminderJob;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import org.quartz.*;
import org.springframework.stereotype.Service;

@Service
public class JobService {

private static final String TRIGGER_NAME = "reminder-trigger";

private static final String JOB_NAME = "reminder-job";

private static final String JOB_GROUP = "reminders";

private final Scheduler scheduler;

public JobService(Scheduler scheduler) {
this.scheduler = scheduler;
}

public void scheduleJob(Reminder reminder) throws SchedulerException {

JobDetail jobDetail = JobBuilder.newJob(ReminderJob.class)
.withIdentity(JOB_NAME + "-" + reminder.getId(), JOB_GROUP)
.usingJobData("reminder-id", reminder.getId())
.storeDurably()
.build();

scheduler.addJob(jobDetail, true);

scheduleDueDateTriggers(reminder);
}

public void updateReminderSchedules(Reminder reminder) throws SchedulerException {
deleteReminderSchedules(reminder.getId());

scheduleJob(reminder);
}

public void deleteReminderSchedules(Long reminderId) throws SchedulerException {
JobKey jobKey = new JobKey(JOB_NAME + "-" + reminderId, JOB_GROUP);

if (scheduler.checkExists(jobKey)) {
scheduler.deleteJob(jobKey);
}
}

private void scheduleDueDateTriggers(Reminder reminder) throws SchedulerException {
scheduleTrigger(reminder, "10-days", reminder.getDueDate().minus(Duration.ofDays(10)));
scheduleTrigger(reminder, "5-days", reminder.getDueDate().minus(Duration.ofDays(5)));
scheduleTrigger(reminder, "2-days", reminder.getDueDate().minus(Duration.ofDays(2)));
}

private void scheduleTrigger(Reminder reminder, String suffix, Instant fireTime) throws SchedulerException {

if (fireTime.isBefore(Instant.now())) {
return;
}

Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity(TRIGGER_NAME + "-" + reminder.getId() + "-" + suffix, JOB_GROUP)
.forJob(JOB_NAME + "-" + reminder.getId(), JOB_GROUP)
.startAt(Date.from(fireTime))
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withMisfireHandlingInstructionFireNow())
.build();

scheduler.scheduleJob(trigger);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -231,4 +231,21 @@ void shouldThrowNotFoundExceptionWhenTryDeleteReminderWithInvalidId() {
// Act And Assert
assertThrows(NotFoundException.class, () -> service.delete(1L));
}

@Test
void shouldRegisterCompleteExecutionWhenReminderIsValid() {
// Arrange
Reminder reminder = new Reminder();
reminder.setTitle("Any Title");

when(repository.save(reminder)).thenReturn(reminder);

// Act
service.registerReminderExecution(reminder);

// Assert
assertTrue(reminder.isSent());
assertNotNull(reminder.getExecutedAt());
verify(repository).save(reminder);
}
}