[AB-197] splitting utility maven module into separate maven modules

aligning some package names
removing some unnecessary computed values from liquibase
This commit is contained in:
Sheldan
2021-03-12 17:29:49 +01:00
parent e2da800d84
commit 2ed456c164
835 changed files with 12790 additions and 3310 deletions

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>abstracto-modules</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>remind</artifactId>
<packaging>pom</packaging>
<modules>
<module>remind-int</module>
<module>remind-impl</module>
</modules>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>dev.sheldan.abstracto.core</groupId>
<artifactId>core-int</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>remind</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>remind-impl</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>src/main/assembly/liquibase.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>remind-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.scheduling</groupId>
<artifactId>scheduling-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.core</groupId>
<artifactId>metrics-int</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.core</groupId>
<artifactId>core-int</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,18 @@
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
<id>liquibase</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<outputDirectory>.</outputDirectory>
<directory>${project.basedir}/src/main/resources/migrations</directory>
<includes>
<include>**/*</include>
</includes>
</fileSet>
</fileSets>
</assembly>

View File

@@ -0,0 +1,83 @@
package dev.sheldan.abstracto.remind.command;
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.remind.config.RemindFeatureDefinition;
import dev.sheldan.abstracto.remind.model.database.Reminder;
import dev.sheldan.abstracto.remind.model.template.commands.ReminderModel;
import dev.sheldan.abstracto.remind.service.ReminderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class Remind extends AbstractConditionableCommand {
public static final String REMINDER_EMBED_KEY = "remind_response";
@Autowired
private ReminderService remindService;
@Autowired
private ChannelService channelService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
Duration remindTime = (Duration) parameters.get(0);
String text = (String) parameters.get(1);
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(commandContext.getAuthor());
ReminderModel remindModel = (ReminderModel) ContextConverter.fromCommandContext(commandContext, ReminderModel.class);
remindModel.setRemindText(text);
Reminder createdReminder = remindService.createReminderInForUser(aUserInAServer, text, remindTime, commandContext.getMessage());
remindModel.setReminder(createdReminder);
log.info("Notifying user {} about reminder being scheduled.", commandContext.getAuthor().getId());
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(REMINDER_EMBED_KEY, remindModel, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
parameters.add(Parameter.builder().name("duration").type(Duration.class).templated(true).build());
parameters.add(Parameter.builder().name("remindText").type(String.class).templated(true).remainder(true).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).hasExample(true).build();
return CommandConfiguration.builder()
.name("remind")
.async(true)
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return RemindFeatureDefinition.REMIND;
}
}

View File

@@ -0,0 +1,69 @@
package dev.sheldan.abstracto.remind.command;
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.remind.config.RemindFeatureDefinition;
import dev.sheldan.abstracto.remind.model.database.Reminder;
import dev.sheldan.abstracto.remind.model.template.commands.RemindersModel;
import dev.sheldan.abstracto.remind.service.management.ReminderManagementService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
@Slf4j
public class Reminders extends AbstractConditionableCommand {
public static final String REMINDERS_RESPONSE_TEMPLATE = "reminders_response";
@Autowired
private ReminderManagementService reminderManagementService;
@Autowired
private ChannelService channelService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
AUserInAServer aUserInAServer = userInServerManagementService.loadOrCreateUser(commandContext.getAuthor());
List<Reminder> activeReminders = reminderManagementService.getActiveRemindersForUser(aUserInAServer);
RemindersModel model = (RemindersModel) ContextConverter.fromCommandContext(commandContext, RemindersModel.class);
model.setReminders(activeReminders);
log.info("Showing {} reminders for user {} in server {}.", activeReminders.size(), commandContext.getAuthor().getId(), commandContext.getGuild().getId());
return FutureUtils.toSingleFutureGeneric(channelService.sendEmbedTemplateInTextChannelList(REMINDERS_RESPONSE_TEMPLATE, model, commandContext.getChannel()))
.thenApply(aVoid -> CommandResult.fromIgnored());
}
@Override
public CommandConfiguration getConfiguration() {
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("reminders")
.async(true)
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.supportsEmbedException(true)
.causesReaction(true)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return RemindFeatureDefinition.REMIND;
}
}

View File

@@ -0,0 +1,62 @@
package dev.sheldan.abstracto.remind.command;
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.config.ParameterValidator;
import dev.sheldan.abstracto.core.command.config.validator.MinIntegerValueValidator;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.remind.config.RemindFeatureDefinition;
import dev.sheldan.abstracto.remind.service.ReminderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@Slf4j
@Component
public class UnRemind extends AbstractConditionableCommand {
@Autowired
private ReminderService reminderService;
@Autowired
private UserInServerManagementService userInServerManagementService;
@Override
public CommandResult execute(CommandContext commandContext) {
Long reminderId = (Long) commandContext.getParameters().getParameters().get(0);
reminderService.unRemind(reminderId, userInServerManagementService.loadOrCreateUser(commandContext.getAuthor()));
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
List<Parameter> parameters = new ArrayList<>();
List<ParameterValidator> reminderIdValidator = Arrays.asList(MinIntegerValueValidator.min(1L));
parameters.add(Parameter.builder().name("reminderId").validators(reminderIdValidator).templated(true).type(Long.class).build());
HelpInfo helpInfo = HelpInfo.builder().templated(true).build();
return CommandConfiguration.builder()
.name("unRemind")
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return RemindFeatureDefinition.REMIND;
}
}

View File

@@ -0,0 +1,10 @@
package dev.sheldan.abstracto.remind.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
@PropertySource("classpath:remind-config.properties")
public class RemindConfig {
}

View File

@@ -0,0 +1,17 @@
package dev.sheldan.abstracto.remind.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@Configuration
public class RemindSpringConfig {
@Bean(name = "reminderScheduler")
public ScheduledExecutorService getUnMuteExecutor() {
return Executors.newScheduledThreadPool(1);
}
}

View File

@@ -0,0 +1,41 @@
package dev.sheldan.abstracto.remind.job;
import dev.sheldan.abstracto.remind.service.ReminderService;
import lombok.extern.slf4j.Slf4j;
import org.quartz.DisallowConcurrentExecution;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.PersistJobDataAfterExecution;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.stereotype.Component;
@Slf4j
@DisallowConcurrentExecution
@Component
@PersistJobDataAfterExecution
public class ReminderJob extends QuartzJobBean {
private Long reminderId;
@Autowired
private ReminderService reminderService;
@Override
protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
try {
log.info("Executing reminder job for reminder {}", reminderId);
reminderService.executeReminder(reminderId);
} catch (Exception e) {
log.error("Reminder job failed to execute.", e);
}
}
public Long getReminderId() {
return reminderId;
}
public void setReminderId(Long reminderId) {
this.reminderId = reminderId;
}
}

View File

@@ -0,0 +1,17 @@
package dev.sheldan.abstracto.remind.repository;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.remind.model.database.Reminder;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface ReminderRepository extends JpaRepository<Reminder, Long> {
List<Reminder> getByRemindedUserAndRemindedFalse(AUserInAServer aUserInAServer);
Reminder getByIdAndRemindedUserAndRemindedFalse(Long reminderId, AUserInAServer aUserInAServer);
}

View File

@@ -0,0 +1,162 @@
package dev.sheldan.abstracto.remind.service;
import dev.sheldan.abstracto.core.models.AServerAChannelAUser;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.GuildService;
import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.remind.exception.ReminderNotFoundException;
import dev.sheldan.abstracto.remind.model.database.Reminder;
import dev.sheldan.abstracto.remind.model.template.commands.ExecutedReminderModel;
import dev.sheldan.abstracto.remind.service.management.ReminderManagementService;
import dev.sheldan.abstracto.scheduling.model.JobParameters;
import dev.sheldan.abstracto.scheduling.service.SchedulerService;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.HashMap;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@Component
@Slf4j
public class RemindServiceBean implements ReminderService {
public static final String REMINDER_TEMPLATE_TEXT = "remind_reminder";
@Autowired
private ReminderManagementService reminderManagementService;
@Autowired
private ChannelManagementService channelManagementService;
@Autowired
private TemplateService templateService;
@Autowired
private SchedulerService schedulerService;
@Autowired
private MemberService memberService;
@Autowired
private GuildService guildService;
@Autowired
private RemindServiceBean self;
@Autowired
private ChannelService channelService;
@Autowired
@Qualifier("reminderScheduler")
private ScheduledExecutorService instantReminderScheduler;
@Override
public Reminder createReminderInForUser(AUserInAServer user, String remindText, Duration remindIn, Message message) {
AChannel channel = channelManagementService.loadChannel(message.getChannel().getIdLong());
AServerAChannelAUser aServerAChannelAUser = AServerAChannelAUser
.builder()
.user(user.getUserReference())
.aUserInAServer(user)
.guild(user.getServerReference())
.channel(channel)
.build();
Instant remindAt = Instant.now().plusNanos(remindIn.toNanos());
Reminder reminder = reminderManagementService.createReminder(aServerAChannelAUser, remindText, remindAt, message.getIdLong());
log.info("Creating reminder for user {} in guild {} due at {}.",
user.getUserReference().getId(), user.getServerReference().getId(), remindAt);
if(remindIn.getSeconds() < 60) {
log.info("Directly scheduling unremind for reminder {}, because it was below the threshold.", reminder.getId());
instantReminderScheduler.schedule(() -> {
try {
self.executeReminder(reminder.getId());
} catch (Exception exception) {
log.error("Failed to remind immediately.", exception);
}
}, remindIn.toNanos(), TimeUnit.NANOSECONDS);
} else {
HashMap<Object, Object> parameters = new HashMap<>();
parameters.put("reminderId", reminder.getId());
JobParameters jobParameters = JobParameters.builder().parameters(parameters).build();
String triggerKey = schedulerService.executeJobWithParametersOnce("reminderJob", "utility", jobParameters, Date.from(reminder.getTargetDate()));
log.info("Starting scheduled job with trigger {} to execute reminder. {}", triggerKey, reminder.getId());
reminder.setJobTriggerKey(triggerKey);
reminderManagementService.saveReminder(reminder);
}
return reminder;
}
@Override
@Transactional
public void executeReminder(Long reminderId) {
Reminder reminderToRemindFor = reminderManagementService.loadReminder(reminderId);
if(reminderToRemindFor.isReminded()) {
return;
}
AServer server = reminderToRemindFor.getServer();
AChannel channel = reminderToRemindFor.getChannel();
log.info("Executing reminder {} in channel {} in server {} for user {}.",
reminderId, channel.getId(), server.getId(), reminderToRemindFor.getRemindedUser().getUserReference().getId());
Optional<Guild> guildToAnswerIn = guildService.getGuildByIdOptional(server.getId());
if(guildToAnswerIn.isPresent()) {
Optional<TextChannel> channelToAnswerIn = channelService.getTextChannelFromServerOptional(server.getId(), channel.getId());
// only send the message if the channel still exists, if not, only set the reminder to reminded.
if(channelToAnswerIn.isPresent()) {
memberService.getMemberInServerAsync(server.getId(), reminderToRemindFor.getRemindedUser().getUserReference().getId()).thenAccept(member ->
self.sendReminderText(reminderId, channelToAnswerIn.get(), member)
);
} else {
log.warn("Channel {} in server {} to remind user did not exist anymore. Ignoring reminder {}", channel.getId(), server.getId(), reminderId);
}
} else {
log.warn("Guild {} to remind user in did not exist anymore. Ignoring reminder {}.", server.getId(), reminderId);
}
reminderManagementService.setReminded(reminderToRemindFor);
}
@Transactional
public CompletableFuture<Void> sendReminderText(Long reminderId, TextChannel channelToAnswerIn, Member member) {
Reminder reminder = reminderManagementService.loadReminder(reminderId);
log.trace("Sending remind message for reminder {} to user user {} in server {}.", reminderId, member.getIdLong(), member.getGuild().getIdLong());
ExecutedReminderModel build = ExecutedReminderModel
.builder()
.reminder(reminder)
.member(member)
.duration(Duration.between(reminder.getReminderDate(), reminder.getTargetDate()))
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate(REMINDER_TEMPLATE_TEXT, build);
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, channelToAnswerIn));
}
@Override
public void unRemind(Long reminderId, AUserInAServer aUserInAServer) {
log.info("Trying to end reminder {} for user {} in server {}.", reminderId, aUserInAServer.getUserReference().getId(), aUserInAServer.getServerReference().getId());
Reminder reminder = reminderManagementService.getReminderByAndByUserNotReminded(aUserInAServer, reminderId).orElseThrow(() -> new ReminderNotFoundException(reminderId));
reminder.setReminded(true);
if(reminder.getJobTriggerKey() != null) {
log.trace("Stopping scheduled trigger {} for reminder {}.", reminder.getJobTriggerKey(), reminderId);
schedulerService.stopTrigger(reminder.getJobTriggerKey());
}
}
}

View File

@@ -0,0 +1,75 @@
package dev.sheldan.abstracto.remind.service.management;
import dev.sheldan.abstracto.core.models.AServerAChannelAUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.remind.exception.ReminderNotFoundException;
import dev.sheldan.abstracto.remind.model.database.Reminder;
import dev.sheldan.abstracto.remind.repository.ReminderRepository;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
@Component
@Slf4j
public class ReminderManagementServiceBean implements ReminderManagementService {
@Autowired
private ReminderRepository reminderRepository;
@Override
public Reminder createReminder(AServerAChannelAUser userToBeReminded, String text, Instant timeToBeRemindedAt, Long messageId) {
Reminder reminder = Reminder.builder()
.channel(userToBeReminded.getChannel())
.server(userToBeReminded.getGuild())
.remindedUser(userToBeReminded.getAUserInAServer())
.reminded(false)
.text(text)
.reminderDate(Instant.now())
.targetDate(timeToBeRemindedAt)
.messageId(messageId)
.build();
log.info("Creating reminder for user {} in server {} in message {} to be reminded at {}.",
userToBeReminded.getAUserInAServer().getUserReference().getId(), userToBeReminded.getGuild().getId(), messageId, timeToBeRemindedAt);
reminderRepository.save(reminder);
return reminder;
}
@Override
public Optional<Reminder> loadReminderOptional(Long reminderId) {
return reminderRepository.findById(reminderId);
}
@Override
public Reminder loadReminder(Long reminderId) {
return loadReminderOptional(reminderId).orElseThrow(() -> new ReminderNotFoundException(reminderId));
}
@Override
public void setReminded(Reminder reminder) {
reminder.setReminded(true);
log.info("Setting reminder {} to reminded.", reminder.getId());
reminderRepository.save(reminder);
}
@Override
public Reminder saveReminder(Reminder reminder) {
return reminderRepository.save(reminder);
}
@Override
public List<Reminder> getActiveRemindersForUser(AUserInAServer aUserInAServer) {
return reminderRepository.getByRemindedUserAndRemindedFalse(aUserInAServer);
}
@Override
public Optional<Reminder> getReminderByAndByUserNotReminded(AUserInAServer aUserInAServer, Long reminderId) {
return Optional.ofNullable(reminderRepository.getByIdAndRemindedUserAndRemindedFalse(reminderId, aUserInAServer));
}
}

View File

@@ -0,0 +1,11 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../dbchangelog-3.8.xsd" >
<include file="remind-tables/tables.xml" relativeToChangelogFile="true"/>
<include file="remind-seedData/data.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,31 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<property name="utilityModule" value="(SELECT id FROM module WHERE name = 'utility')"/>
<property name="remindFeature" value="(SELECT id FROM feature WHERE key = 'remind')"/>
<changeSet author="Sheldan" id="remind-commands">
<insert tableName="command">
<column name="name" value="remind"/>
<column name="module_id" valueComputed="${utilityModule}"/>
<column name="feature_id" valueComputed="${remindFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="reminders"/>
<column name="module_id" valueComputed="${utilityModule}"/>
<column name="feature_id" valueComputed="${remindFeature}"/>
</insert>
<insert tableName="command">
<column name="name" value="unRemind"/>
<column name="module_id" valueComputed="${utilityModule}"/>
<column name="feature_id" valueComputed="${remindFeature}"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,12 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<include file="feature.xml" relativeToChangelogFile="true"/>
<include file="command.xml" relativeToChangelogFile="true"/>
<include file="reminder_job.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,14 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<changeSet author="Sheldan" id="remind_feature-insertion">
<insert tableName="feature">
<column name="key" value="remind"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,18 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<changeSet author="Sheldan" id="reminder-job-insert">
<insert tableName="scheduler_job">
<column name="name" value="reminderJob"/>
<column name="group_name" value="utility"/>
<column name="clazz" value="dev.sheldan.abstracto.remind.job.ReminderJob"/>
<column name="active" value="true"/>
<column name="recovery" value="false"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,57 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<changeSet author="Sheldan" id="reminder-table">
<createTable tableName="reminder">
<column autoIncrement="true" name="id" type="BIGINT">
<constraints nullable="false" primaryKey="true" primaryKeyName="reminder_pkey"/>
</column>
<column name="job_trigger_key" type="VARCHAR(255)"/>
<column name="message_id" type="BIGINT">
<constraints nullable="true"/>
</column>
<column name="reminded" type="BOOLEAN">
<constraints nullable="false"/>
</column>
<column name="created" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="true"/>
</column>
<column name="updated" type="TIMESTAMP WITHOUT TIME ZONE"/>
<column name="target_date" type="TIMESTAMP WITHOUT TIME ZONE">
<constraints nullable="true"/>
</column>
<column name="text" type="VARCHAR(255)">
<constraints nullable="true"/>
</column>
<column name="channel_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="user_in_server_id" type="BIGINT">
<constraints nullable="false"/>
</column>
<column name="server_id" type="BIGINT">
<constraints nullable="false"/>
</column>
</createTable>
<createIndex indexName="idx_reminder_user_reminded" tableName="reminder">
<column name="user_in_server_id"/>
<column name="reminded"/>
</createIndex>
<addForeignKeyConstraint baseColumnNames="channel_id" baseTableName="reminder" constraintName="fk_reminder_channel" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="channel" validate="true"/>
<addForeignKeyConstraint baseColumnNames="user_in_server_id" baseTableName="reminder" constraintName="fk_reminder_user" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="user_in_server_id" referencedTableName="user_in_server" validate="true"/>
<addForeignKeyConstraint baseColumnNames="server_id" baseTableName="reminder" constraintName="fk_reminder_server" deferrable="false" initiallyDeferred="false" onDelete="NO ACTION" onUpdate="NO ACTION" referencedColumnNames="id" referencedTableName="server" validate="true"/>
<sql>
DROP TRIGGER IF EXISTS reminder_update_trigger ON reminder;
CREATE TRIGGER reminder_update_trigger BEFORE UPDATE ON reminder FOR EACH ROW EXECUTE PROCEDURE update_trigger_procedure();
</sql>
<sql>
DROP TRIGGER IF EXISTS reminder_insert_trigger ON reminder;
CREATE TRIGGER reminder_insert_trigger BEFORE INSERT ON reminder FOR EACH ROW EXECUTE PROCEDURE insert_trigger_procedure();
</sql>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,10 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext ../../dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro ../../dbchangelog-3.8.xsd" >
<include file="reminder.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,10 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog-3.8.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog-3.8.xsd" >
<include file="1.0-remind/collection.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,2 @@
abstracto.featureFlags.remind.featureName=remind
abstracto.featureFlags.remind.enabled=false

View File

@@ -0,0 +1,63 @@
package dev.sheldan.abstracto.remind.command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.core.test.command.CommandTestUtilities;
import dev.sheldan.abstracto.remind.model.template.commands.ReminderModel;
import dev.sheldan.abstracto.remind.service.ReminderService;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.*;
import org.mockito.junit.MockitoJUnitRunner;
import java.time.Duration;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class RemindTest {
@InjectMocks
private Remind testUnit;
@Mock
private ReminderService remindService;
@Mock
private ChannelService channelService;
@Mock
private UserInServerManagementService userInServerManagementService;
@Captor
private ArgumentCaptor<ReminderModel> captor;
@Test
public void executeCommand() {
String reminderText = "text";
Duration duration = Duration.ofMinutes(10);
CommandContext withParameters = CommandTestUtilities.getWithParameters(Arrays.asList(duration, reminderText));
AUserInAServer user = Mockito.mock(AUserInAServer.class);
when(userInServerManagementService.loadOrCreateUser(withParameters.getAuthor())).thenReturn(user);
CompletableFuture<CommandResult> result = testUnit.executeAsync(withParameters);
verify(remindService, times(1)).createReminderInForUser(user, reminderText, duration, withParameters.getMessage());
verify(channelService, times(1)).sendEmbedTemplateInTextChannelList(eq(Remind.REMINDER_EMBED_KEY), captor.capture(), eq(withParameters.getChannel()));
ReminderModel reminderModel = captor.getValue();
Assert.assertEquals(reminderText, reminderModel.getRemindText());
Assert.assertEquals(withParameters.getMessage(), reminderModel.getMessage());
CommandTestUtilities.checkSuccessfulCompletionAsync(result);
}
@Test
public void validateCommand() {
CommandConfigValidator.validateCommandConfiguration(testUnit.getConfiguration());
}
}

View File

@@ -0,0 +1,65 @@
package dev.sheldan.abstracto.remind.command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.core.test.command.CommandTestUtilities;
import dev.sheldan.abstracto.remind.model.database.Reminder;
import dev.sheldan.abstracto.remind.model.template.commands.RemindersModel;
import dev.sheldan.abstracto.remind.service.management.ReminderManagementService;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.*;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class RemindersTest {
@InjectMocks
private Reminders testUnit;
@Mock
private ReminderManagementService reminderManagementService;
@Mock
private ChannelService channelService;
@Mock
private UserInServerManagementService userInServerManagementService;
@Captor
private ArgumentCaptor<RemindersModel> modelCaptor;
@Test
public void testExecuteCommand() {
CommandContext context = CommandTestUtilities.getNoParameters();
Reminder reminder = Mockito.mock(Reminder.class);
Reminder secondReminder = Mockito.mock(Reminder.class);
List<Reminder> reminders = Arrays.asList(reminder, secondReminder);
AUserInAServer user = Mockito.mock(AUserInAServer.class);
when(userInServerManagementService.loadOrCreateUser(context.getAuthor())).thenReturn(user);
when(reminderManagementService.getActiveRemindersForUser(user)).thenReturn(reminders);
CompletableFuture<CommandResult> result = testUnit.executeAsync(context);
verify(channelService, times(1)).sendEmbedTemplateInTextChannelList(eq(Reminders.REMINDERS_RESPONSE_TEMPLATE), modelCaptor.capture(), eq(context.getChannel()));
RemindersModel usedModel = modelCaptor.getValue();
Assert.assertEquals(reminder, usedModel.getReminders().get(0));
Assert.assertEquals(secondReminder, usedModel.getReminders().get(1));
Assert.assertEquals(reminders.size(), usedModel.getReminders().size());
CommandTestUtilities.checkSuccessfulCompletionAsync(result);
}
@Test
public void validateCommand() {
CommandConfigValidator.validateCommandConfiguration(testUnit.getConfiguration());
}
}

View File

@@ -0,0 +1,48 @@
package dev.sheldan.abstracto.remind.command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.core.test.command.CommandConfigValidator;
import dev.sheldan.abstracto.core.test.command.CommandTestUtilities;
import dev.sheldan.abstracto.remind.service.ReminderService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class UnRemindTest {
@InjectMocks
private UnRemind testUnit;
@Mock
private ReminderService reminderService;
@Mock
private UserInServerManagementService userInServerManagementService;
@Test
public void testExecuteCommand() {
Long reminderId = 6L;
CommandContext withParameters = CommandTestUtilities.getWithParameters(Arrays.asList(reminderId));
AUserInAServer user = Mockito.mock(AUserInAServer.class);
when(userInServerManagementService.loadOrCreateUser(withParameters.getAuthor())).thenReturn(user);
CommandResult result = testUnit.execute(withParameters);
verify(reminderService, times(1)).unRemind(reminderId, user);
CommandTestUtilities.checkSuccessfulCompletion(result);
}
@Test
public void validateCommand() {
CommandConfigValidator.validateCommandConfiguration(testUnit.getConfiguration());
}
}

View File

@@ -0,0 +1,256 @@
package dev.sheldan.abstracto.remind.service;
import dev.sheldan.abstracto.core.models.AServerAChannelAUser;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.GuildService;
import dev.sheldan.abstracto.core.service.MemberService;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.remind.exception.ReminderNotFoundException;
import dev.sheldan.abstracto.remind.model.database.Reminder;
import dev.sheldan.abstracto.remind.service.management.ReminderManagementService;
import dev.sheldan.abstracto.scheduling.model.JobParameters;
import dev.sheldan.abstracto.scheduling.service.SchedulerService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.TextChannel;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.Date;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ScheduledExecutorService;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class RemindServiceBeanTest {
@InjectMocks
private RemindServiceBean testUnit;
@Mock
private ReminderManagementService reminderManagementService;
@Mock
private ChannelManagementService channelManagementService;
@Mock
private TemplateService templateService;
@Mock
private SchedulerService schedulerService;
@Mock
private GuildService guildService;
@Mock
private MemberService memberService;
@Mock
private RemindServiceBean self;
@Mock
private ChannelService channelService;
@Mock
private Message message;
@Mock
private TextChannel channel;
@Mock
private ScheduledExecutorService instantReminderScheduler;
@Mock
private AServer server;
@Mock
private AUserInAServer aUserInAServer;
@Mock
private AUser user;
@Mock
private AChannel aChannel;
private static final Long REMINDER_ID = 5L;
private static final Long CHANNEL_ID = 6L;
private static final Long SERVER_ID = 8L;
private static final Long USER_ID = 9L;
@Before
public void setup() {
when(message.getIdLong()).thenReturn(5L);
when(channel.getIdLong()).thenReturn(5L);
when(message.getChannel()).thenReturn(channel);
}
@Test
public void createReminderWithScheduler() {
String remindText = "text";
String triggerKey = "trigger";
Duration duration = Duration.ofSeconds(62);
when(message.getChannel()).thenReturn(channel);
when(channel.getIdLong()).thenReturn(CHANNEL_ID);
when(aUserInAServer.getServerReference()).thenReturn(server);
when(aUserInAServer.getUserReference()).thenReturn(user);
when(channelManagementService.loadChannel(CHANNEL_ID)).thenReturn(aChannel);
Instant targetDate = Instant.now().plus(duration);
Reminder createdReminder = Mockito.mock(Reminder.class);
when(createdReminder.getTargetDate()).thenReturn(targetDate);
Long messageId = 5L;
when(reminderManagementService.createReminder(any(AServerAChannelAUser.class), eq(remindText), any(Instant.class), eq(messageId))).thenReturn(createdReminder);
when(schedulerService.executeJobWithParametersOnce(eq("reminderJob"), eq("utility"), any(JobParameters.class), eq(Date.from(targetDate)))).thenReturn(triggerKey);
Reminder returnedReminder = testUnit.createReminderInForUser(aUserInAServer, remindText, duration, message);
verify(reminderManagementService, times(1)).saveReminder(createdReminder);
Assert.assertEquals(createdReminder, returnedReminder);
}
@Test
public void createReminderWithoutScheduler() {
String remindText = "text";
Duration duration = Duration.ofSeconds(50);
when(message.getChannel()).thenReturn(channel);
when(channel.getIdLong()).thenReturn(CHANNEL_ID);
when(channelManagementService.loadChannel(CHANNEL_ID)).thenReturn(aChannel);
Reminder createdReminder = Mockito.mock(Reminder.class);
when(createdReminder.getText()).thenReturn(remindText);
Long messageId = 5L;
when(aUserInAServer.getServerReference()).thenReturn(server);
when(aUserInAServer.getUserReference()).thenReturn(user);
when(reminderManagementService.createReminder(any(AServerAChannelAUser.class), eq(remindText), any(Instant.class), eq(messageId))).thenReturn(createdReminder);
Reminder returnedReminder = testUnit.createReminderInForUser(aUserInAServer, remindText, duration, message);
verify(reminderManagementService, times(0)).saveReminder(createdReminder);
Assert.assertEquals(remindText, returnedReminder.getText());
Assert.assertEquals(createdReminder.getId(), returnedReminder.getId());
}
@Test
public void testExecuteReminder() {
Reminder remindedReminder = Mockito.mock(Reminder.class);
when(remindedReminder.getRemindedUser()).thenReturn(aUserInAServer);
when(remindedReminder.getChannel()).thenReturn(aChannel);
when(remindedReminder.getServer()).thenReturn(server);
when(aChannel.getId()).thenReturn(CHANNEL_ID);
when(server.getId()).thenReturn(SERVER_ID);
when(aUserInAServer.getUserReference()).thenReturn(user);
when(user.getId()).thenReturn(USER_ID);
when(reminderManagementService.loadReminder(REMINDER_ID)).thenReturn(remindedReminder);
Guild guildMock = Mockito.mock(Guild.class);
when(guildService.getGuildByIdOptional(SERVER_ID)).thenReturn(Optional.of(guildMock));
when(channelService.getTextChannelFromServerOptional(SERVER_ID, CHANNEL_ID)).thenReturn(Optional.of(channel));
Member mockedMember = Mockito.mock(Member.class);
when(memberService.getMemberInServerAsync(SERVER_ID, USER_ID)).thenReturn(CompletableFuture.completedFuture(mockedMember));
testUnit.executeReminder(REMINDER_ID);
verify(reminderManagementService, times(1)).setReminded(remindedReminder);
}
@Test
public void testExecuteReminderFromNotFoundChannel() {
Reminder remindedReminder = Mockito.mock(Reminder.class);
when(remindedReminder.getChannel()).thenReturn(aChannel);
when(remindedReminder.getServer()).thenReturn(server);
when(server.getId()).thenReturn(SERVER_ID);
when(aChannel.getId()).thenReturn(CHANNEL_ID);
when(reminderManagementService.loadReminder(REMINDER_ID)).thenReturn(remindedReminder);
Guild guildMock = Mockito.mock(Guild.class);
when(guildService.getGuildByIdOptional(SERVER_ID)).thenReturn(Optional.of(guildMock));
when(aUserInAServer.getUserReference()).thenReturn(user);
when(remindedReminder.getRemindedUser()).thenReturn(aUserInAServer);
when(channelService.getTextChannelFromServerOptional(SERVER_ID, CHANNEL_ID)).thenReturn(Optional.empty());
testUnit.executeReminder(REMINDER_ID);
verify(reminderManagementService, times(1)).setReminded(remindedReminder);
verify(self, times(0)).sendReminderText(anyLong(), any(), any(Member.class));
}
@Test
public void testExecuteReminderFromNotFoundGuild() {
Long reminderId = 5L;
Reminder remindedReminder = Mockito.mock(Reminder.class);
when(remindedReminder.getChannel()).thenReturn(aChannel);
when(remindedReminder.getServer()).thenReturn(server);
when(server.getId()).thenReturn(SERVER_ID);
when(remindedReminder.getRemindedUser()).thenReturn(aUserInAServer);
when(aUserInAServer.getUserReference()).thenReturn(user);
when(reminderManagementService.loadReminder(reminderId)).thenReturn(remindedReminder);
when(guildService.getGuildByIdOptional(SERVER_ID)).thenReturn(Optional.empty());
testUnit.executeReminder(reminderId);
verify(reminderManagementService, times(1)).setReminded(remindedReminder);
}
@Test
public void testExecuteRemindedReminder() {
Reminder remindedReminder = Mockito.mock(Reminder.class);
when(remindedReminder.isReminded()).thenReturn(true);
when(reminderManagementService.loadReminder(REMINDER_ID)).thenReturn(remindedReminder);
testUnit.executeReminder(REMINDER_ID);
verify(guildService, times(0)).getGuildByIdOptional(anyLong());
}
@Test
public void testUnRemindScheduledReminder() {
String triggerKey = "trigger";
Reminder reminderToUnRemind = Mockito.mock(Reminder.class);
when(reminderToUnRemind.getJobTriggerKey()).thenReturn(triggerKey);
when(aUserInAServer.getServerReference()).thenReturn(server);
when(aUserInAServer.getUserReference()).thenReturn(user);
when(reminderManagementService.getReminderByAndByUserNotReminded(aUserInAServer, REMINDER_ID)).thenReturn(Optional.of(reminderToUnRemind));
testUnit.unRemind(REMINDER_ID, aUserInAServer);
verify(schedulerService, times(1)).stopTrigger(triggerKey);
}
@Test
public void testUnRemindNonScheduledReminder() {
Reminder reminderToUnRemind = Mockito.mock(Reminder.class);
when(aUserInAServer.getServerReference()).thenReturn(server);
when(aUserInAServer.getUserReference()).thenReturn(user);
when(reminderManagementService.getReminderByAndByUserNotReminded(aUserInAServer, REMINDER_ID)).thenReturn(Optional.of(reminderToUnRemind));
testUnit.unRemind(REMINDER_ID, aUserInAServer);
verify(schedulerService, times(0)).stopTrigger(anyString());
}
@Test(expected = ReminderNotFoundException.class)
public void testUnRemindNonExistingReminder() {
when(aUserInAServer.getServerReference()).thenReturn(server);
when(aUserInAServer.getUserReference()).thenReturn(user);
when(reminderManagementService.getReminderByAndByUserNotReminded(aUserInAServer, REMINDER_ID)).thenReturn(Optional.empty());
testUnit.unRemind(REMINDER_ID, aUserInAServer);
}
@Test
public void testSendReminderText() {
Member remindedMember = Mockito.mock(Member.class);
Guild guild = Mockito.mock(Guild.class);
when(remindedMember.getGuild()).thenReturn(guild);
when(guild.getIdLong()).thenReturn(8L);
when(remindedMember.getIdLong()).thenReturn(9L);
Reminder remindedReminder = Mockito.mock(Reminder.class);
when(remindedReminder.getTargetDate()).thenReturn(Instant.now());
when(remindedReminder.getReminderDate()).thenReturn(Instant.now());
when(reminderManagementService.loadReminder(REMINDER_ID)).thenReturn(remindedReminder);
MessageToSend messageToSend = Mockito.mock(MessageToSend.class);
when(templateService.renderEmbedTemplate(eq(RemindServiceBean.REMINDER_TEMPLATE_TEXT), any())).thenReturn(messageToSend);
when(channelService.sendMessageToSendToChannel(messageToSend, channel)).thenReturn(Arrays.asList(CompletableFuture.completedFuture(null)));
CompletableFuture<Void> future = testUnit.sendReminderText(REMINDER_ID, channel, remindedMember);
future.join();
Assert.assertFalse(future.isCompletedExceptionally());
}
}

View File

@@ -0,0 +1,127 @@
package dev.sheldan.abstracto.remind.service.management;
import dev.sheldan.abstracto.core.models.AServerAChannelAUser;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.remind.model.database.Reminder;
import dev.sheldan.abstracto.remind.repository.ReminderRepository;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class ReminderManagementServiceBeanTest {
@InjectMocks
private ReminderManagementServiceBean testUnit;
@Mock
private ReminderRepository reminderRepository;
@Mock
private AUserInAServer aUserInAServer;
@Mock
private AChannel channel;
@Mock
private AServer server;
@Mock
private Reminder reminder;
private static final Long REMINDER_ID = 8L;
@Test
public void testCreateReminder() {
AServerAChannelAUser serverAChannelAUser = Mockito.mock(AServerAChannelAUser.class);
AUser user = Mockito.mock(AUser.class);
when(aUserInAServer.getUserReference()).thenReturn(user);
when(serverAChannelAUser.getAUserInAServer()).thenReturn(aUserInAServer);
when(serverAChannelAUser.getGuild()).thenReturn(server);
when(serverAChannelAUser.getChannel()).thenReturn(channel);
String reminderText = "text";
Instant reminderTargetDate = Instant.ofEpochSecond(1590615937);
Long messageId = 5L;
Reminder createdReminder = testUnit.createReminder(serverAChannelAUser, reminderText, reminderTargetDate, messageId);
Assert.assertEquals(messageId, createdReminder.getMessageId());
Assert.assertEquals(aUserInAServer, createdReminder.getRemindedUser());
Assert.assertEquals(server, createdReminder.getServer());
Assert.assertEquals(reminderText, createdReminder.getText());
Assert.assertEquals(reminderTargetDate, createdReminder.getTargetDate());
Assert.assertEquals(channel, createdReminder.getChannel());
Assert.assertFalse(createdReminder.isReminded());
verify(reminderRepository, times(1)).save(createdReminder);
}
@Test
public void testSetReminded() {
testUnit.setReminded(reminder);
verify(reminder, times(1)).setReminded(true);
verify(reminderRepository, times(1)).save(reminder);
}
@Test
public void testSaveReminder() {
testUnit.saveReminder(reminder);
verify(reminderRepository, times(1)).save(reminder);
}
@Test
public void testRetrieveActiveReminders() {
Reminder reminder2 = Mockito.mock(Reminder.class);
List<Reminder> reminders = Arrays.asList(reminder, reminder2);
when(reminderRepository.getByRemindedUserAndRemindedFalse(aUserInAServer)).thenReturn(reminders);
List<Reminder> activeRemindersForUser = testUnit.getActiveRemindersForUser(aUserInAServer);
for (int i = 0; i < reminders.size(); i++) {
Reminder reference = reminders.get(i);
Reminder returned = activeRemindersForUser.get(i);
Assert.assertEquals(reference, returned);
}
Assert.assertEquals(reminders.size(), activeRemindersForUser.size());
}
@Test
public void testGetReminderByIdAndNotReminded() {
when(reminderRepository.getByIdAndRemindedUserAndRemindedFalse(REMINDER_ID, aUserInAServer)).thenReturn(reminder);
Optional<Reminder> returned = testUnit.getReminderByAndByUserNotReminded(aUserInAServer, REMINDER_ID);
Assert.assertTrue(returned.isPresent());
returned.ifPresent(returnedReminder -> Assert.assertEquals(reminder, returnedReminder));
}
@Test
public void testGetReminderByIdAndNotRemindedNothingFound() {
when(reminderRepository.getByIdAndRemindedUserAndRemindedFalse(REMINDER_ID, aUserInAServer)).thenReturn(null);
Optional<Reminder> returned = testUnit.getReminderByAndByUserNotReminded(aUserInAServer, REMINDER_ID);
Assert.assertFalse(returned.isPresent());
}
@Test
public void testLoadingReminder() {
when(reminderRepository.findById(REMINDER_ID)).thenReturn(Optional.of(reminder));
Optional<Reminder> returned = testUnit.loadReminderOptional(REMINDER_ID);
Assert.assertTrue(returned.isPresent());
returned.ifPresent(returnedReminder -> Assert.assertEquals(reminder, returnedReminder));
}
@Test
public void testLoadingReminderNotFound() {
when(reminderRepository.findById(REMINDER_ID)).thenReturn(Optional.empty());
Optional<Reminder> returned = testUnit.loadReminderOptional(REMINDER_ID);
Assert.assertFalse(returned.isPresent());
}
}

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>remind</artifactId>
<groupId>dev.sheldan.abstracto.modules</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>remind-int</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
</project>

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.remind.config;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import org.springframework.stereotype.Component;
@Component
public class RemindFeature implements FeatureConfig {
@Override
public FeatureDefinition getFeature() {
return RemindFeatureDefinition.REMIND;
}
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.remind.config;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import lombok.Getter;
@Getter
public enum RemindFeatureDefinition implements FeatureDefinition {
REMIND("remind");
private String key;
RemindFeatureDefinition(String key) {
this.key = key;
}
}

View File

@@ -0,0 +1,24 @@
package dev.sheldan.abstracto.remind.exception;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.templating.Templatable;
import dev.sheldan.abstracto.remind.model.exception.ReminderNotFoundExceptionModel;
public class ReminderNotFoundException extends AbstractoRunTimeException implements Templatable {
private final ReminderNotFoundExceptionModel model;
public ReminderNotFoundException(Long reminderId) {
super("Reminder does not exist");
this.model = ReminderNotFoundExceptionModel.builder().reminderId(reminderId).build();
}
@Override
public String getTemplateName() {
return "reminder_does_not_exist_exception";
}
@Override
public Object getTemplateModel() {
return model;
}
}

View File

@@ -0,0 +1,60 @@
package dev.sheldan.abstracto.remind.model.database;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.*;
import javax.persistence.*;
import java.io.Serializable;
import java.time.Instant;
@Entity
@Table(name="reminder")
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@EqualsAndHashCode
public class Reminder implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_in_server_id", nullable = false)
private AUserInAServer remindedUser;
@Column(name = "message_id")
private Long messageId;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "channel_id", nullable = false)
private AChannel channel;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "server_id", nullable = false)
private AServer server;
@Column(name = "created")
private Instant reminderDate;
@Column(name = "updated")
private Instant updated;
@Column(name = "target_date")
private Instant targetDate;
@Column(name = "text")
private String text;
@Column(name = "reminded")
private boolean reminded;
@Column(name = "job_trigger_key")
private String jobTriggerKey;
}

View File

@@ -0,0 +1,12 @@
package dev.sheldan.abstracto.remind.model.exception;
import lombok.Builder;
import lombok.Getter;
import java.io.Serializable;
@Getter
@Builder
public class ReminderNotFoundExceptionModel implements Serializable {
private final Long reminderId;
}

View File

@@ -0,0 +1,24 @@
package dev.sheldan.abstracto.remind.model.template.commands;
import dev.sheldan.abstracto.core.models.context.ServerContext;
import dev.sheldan.abstracto.core.utils.MessageUtils;
import dev.sheldan.abstracto.remind.model.database.Reminder;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
import net.dv8tion.jda.api.entities.Member;
import java.time.Duration;
@Getter
@Setter
@SuperBuilder
public class ExecutedReminderModel extends ServerContext {
private Reminder reminder;
private Member member;
private Duration duration;
public String getMessageUrl() {
return MessageUtils.buildMessageUrl(this.reminder.getServer().getId() ,this.reminder.getChannel().getId(), this.reminder.getMessageId());
}
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.remind.model.template.commands;
import dev.sheldan.abstracto.core.models.context.UserInitiatedServerContext;
import dev.sheldan.abstracto.remind.model.database.Reminder;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
@Getter
@Setter
@SuperBuilder
public class ReminderModel extends UserInitiatedServerContext {
private String remindText;
private Reminder reminder;
}

View File

@@ -0,0 +1,16 @@
package dev.sheldan.abstracto.remind.model.template.commands;
import dev.sheldan.abstracto.core.models.context.UserInitiatedServerContext;
import dev.sheldan.abstracto.remind.model.database.Reminder;
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.SuperBuilder;
import java.util.List;
@Getter
@Setter
@SuperBuilder
public class RemindersModel extends UserInitiatedServerContext {
private List<Reminder> reminders;
}

View File

@@ -0,0 +1,13 @@
package dev.sheldan.abstracto.remind.service;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.remind.model.database.Reminder;
import net.dv8tion.jda.api.entities.Message;
import java.time.Duration;
public interface ReminderService {
Reminder createReminderInForUser(AUserInAServer user, String remindText, Duration remindIn, Message message);
void executeReminder(Long reminderId);
void unRemind(Long reminderId, AUserInAServer userInAServer);
}

View File

@@ -0,0 +1,19 @@
package dev.sheldan.abstracto.remind.service.management;
import dev.sheldan.abstracto.core.models.AServerAChannelAUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.remind.model.database.Reminder;
import java.time.Instant;
import java.util.List;
import java.util.Optional;
public interface ReminderManagementService {
Reminder createReminder(AServerAChannelAUser userToBeReminded, String text, Instant timeToBeRemindedAt, Long messageId);
Optional<Reminder> loadReminderOptional(Long reminderId);
Reminder loadReminder(Long reminderId);
void setReminded(Reminder reminder);
Reminder saveReminder(Reminder reminder);
List<Reminder> getActiveRemindersForUser(AUserInAServer aUserInAServer);
Optional<Reminder> getReminderByAndByUserNotReminded(AUserInAServer aUserInAServer, Long reminderId);
}