[AB-203] restructuring listeners to use more common code and common interfaces for listeners and models

restructuring entity listener to be async and added models
fixing usage of repository save method
adding interface dependencies to bundle dependency management
This commit is contained in:
Sheldan
2021-03-21 10:58:31 +01:00
parent cfe7786d4d
commit b4e36efafb
241 changed files with 3521 additions and 2049 deletions

View File

@@ -0,0 +1,18 @@
package dev.sheldan.abstracto.starboard.config;
import dev.sheldan.abstracto.core.service.ExecutorService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
@Configuration
public class StarboardListenerConfig {
@Autowired
private ExecutorService executorService;
@Bean(name = "starboardStatusListenerExecutor")
public TaskExecutor joinListenerExecutor() {
return executorService.setupExecutorFor("starboardStatusListener");
}
}

View File

@@ -0,0 +1,59 @@
package dev.sheldan.abstracto.starboard.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncReactionAddedListener;
import dev.sheldan.abstracto.core.metric.service.CounterMetric;
import dev.sheldan.abstracto.core.metric.service.MetricTag;
import dev.sheldan.abstracto.core.models.cache.CachedReactions;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.models.listener.ReactionAddedModel;
import dev.sheldan.abstracto.starboard.config.StarboardFeature;
import dev.sheldan.abstracto.starboard.config.StarboardFeatureDefinition;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.Arrays;
import java.util.Optional;
@Component
@Slf4j
public class StarAddedListener extends StarboardListener implements AsyncReactionAddedListener {
protected static final CounterMetric STARBOARD_STARS_ADDED = CounterMetric
.builder()
.name(STARBOARD_STARS)
.tagList(Arrays.asList(MetricTag.getTag(STAR_ACTION, "added")))
.build();
@Override
public DefaultListenerResult execute(ReactionAddedModel model) {
if(model.getUserReacting().getUserId().equals(model.getMessage().getAuthor().getAuthorId())) {
return DefaultListenerResult.IGNORED;
}
Long serverId = model.getServerId();
AEmote aEmote = emoteService.getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, serverId);
if(emoteService.isReactionEmoteAEmote(model.getReaction().getReactionEmote(), aEmote)) {
metricService.incrementCounter(STARBOARD_STARS_ADDED);
log.info("User {} in server {} reacted with star to put a message {} from channel {} on starboard.",
model.getUserReacting().getUserId(), model.getServerId(), model.getMessage().getMessageId(), model.getMessage().getChannelId());
Optional<CachedReactions> reactionOptional = emoteService.getReactionFromMessageByEmote(model.getMessage(), aEmote);
handleStarboardPostChange(model.getMessage(), reactionOptional.orElse(null), model.getUserReacting(), true);
return DefaultListenerResult.PROCESSED;
} else {
return DefaultListenerResult.IGNORED;
}
}
@PostConstruct
@Override
public void postConstruct() {
metricService.registerCounter(STARBOARD_STARS_ADDED, "Star reaction added");
}
@Override
public FeatureDefinition getFeature() {
return StarboardFeatureDefinition.STARBOARD;
}
}

View File

@@ -0,0 +1,39 @@
package dev.sheldan.abstracto.starboard.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncReactionClearedListener;
import dev.sheldan.abstracto.core.models.listener.ReactionClearedModel;
import dev.sheldan.abstracto.starboard.config.StarboardFeatureDefinition;
import dev.sheldan.abstracto.starboard.model.database.StarboardPost;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.Optional;
@Component
@Slf4j
public class StarClearedListener extends StarboardListener implements AsyncReactionClearedListener {
@Override
public DefaultListenerResult execute(ReactionClearedModel model) {
Optional<StarboardPost> starboardPostOptional = starboardPostManagementService.findByMessageId(model.getMessage().getMessageId());
if(starboardPostOptional.isPresent()) {
starboardPostOptional.ifPresent(starboardPost -> {
log.info("Reactions on message {} in channel {} in server {} were cleared. Completely deleting the starboard post {}.",
model.getMessage().getMessageId(), model.getMessage().getChannelId(), model.getServerId(), starboardPost.getId());
starboardPostReactorManagementService.removeReactors(starboardPost);
completelyRemoveStarboardPost(starboardPost, null);
});
return DefaultListenerResult.PROCESSED;
}
return DefaultListenerResult.IGNORED;
}
@Override
public FeatureDefinition getFeature() {
return StarboardFeatureDefinition.STARBOARD;
}
}

View File

@@ -0,0 +1,63 @@
package dev.sheldan.abstracto.starboard.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncReactionRemovedListener;
import dev.sheldan.abstracto.core.metric.service.CounterMetric;
import dev.sheldan.abstracto.core.metric.service.MetricTag;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.cache.CachedReactions;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.models.listener.ReactionRemovedModel;
import dev.sheldan.abstracto.starboard.config.StarboardFeature;
import dev.sheldan.abstracto.starboard.config.StarboardFeatureDefinition;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.Arrays;
import java.util.Optional;
@Component
@Slf4j
public class StarRemovedListener extends StarboardListener implements AsyncReactionRemovedListener {
protected static final CounterMetric STARBOARD_STARS_REMOVED = CounterMetric
.builder()
.name(STARBOARD_STARS)
.tagList(Arrays.asList(MetricTag.getTag(STAR_ACTION, "removed")))
.build();
@Override
@Transactional
public DefaultListenerResult execute(ReactionRemovedModel model) {
ServerUser userRemoving = model.getUserRemoving();
if(model.getMessage().getAuthor().getAuthorId().equals(userRemoving.getUserId())) {
return DefaultListenerResult.IGNORED;
}
Long guildId = model.getServerId();
AEmote aEmote = emoteService.getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, guildId);
if(emoteService.isReactionEmoteAEmote(model.getReaction().getReactionEmote(), aEmote)) {
metricService.incrementCounter(STARBOARD_STARS_REMOVED);
log.info("User {} in server {} removed star reaction from message {} on starboard.",
userRemoving.getUserId(), model.getServerId(), model.getMessage().getMessageId());
Optional<CachedReactions> reactionOptional = emoteService.getReactionFromMessageByEmote(model.getMessage(), aEmote);
handleStarboardPostChange(model.getMessage(), reactionOptional.orElse(null), userRemoving, false);
return DefaultListenerResult.PROCESSED;
} else {
return DefaultListenerResult.IGNORED;
}
}
@PostConstruct
@Override
public void postConstruct() {
metricService.registerCounter(STARBOARD_STARS_REMOVED, "Star reaction removed");
}
@Override
public FeatureDefinition getFeature() {
return StarboardFeatureDefinition.STARBOARD;
}
}

View File

@@ -1,23 +1,15 @@
package dev.sheldan.abstracto.starboard.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncReactionAddedListener;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncReactionClearedListener;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncReactionRemovedListener;
import dev.sheldan.abstracto.core.metric.service.CounterMetric;
import dev.sheldan.abstracto.core.metric.service.MetricService;
import dev.sheldan.abstracto.core.metric.service.MetricTag;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.cache.CachedReactions;
import dev.sheldan.abstracto.core.models.database.AEmote;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.management.ConfigManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.starboard.config.StarboardFeature;
import dev.sheldan.abstracto.starboard.config.StarboardFeatureDefinition;
import dev.sheldan.abstracto.starboard.model.database.StarboardPost;
import dev.sheldan.abstracto.starboard.service.StarboardService;
import dev.sheldan.abstracto.starboard.service.management.StarboardPostManagementService;
@@ -25,7 +17,6 @@ import dev.sheldan.abstracto.starboard.service.management.StarboardPostReactorMa
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.PostConstruct;
import java.util.Arrays;
@@ -36,84 +27,54 @@ import java.util.stream.Collectors;
@Component
@Slf4j
public class StarboardListener implements AsyncReactionAddedListener, AsyncReactionRemovedListener, AsyncReactionClearedListener {
public abstract class StarboardListener {
public static final String FIRST_LEVEL_THRESHOLD_KEY = "starLvl1";
@Autowired
private BotService botService;
protected ConfigManagementService configManagementService;
@Autowired
private ConfigManagementService configManagementService;
protected StarboardService starboardService;
@Autowired
private StarboardService starboardService;
protected StarboardPostManagementService starboardPostManagementService;
@Autowired
private StarboardPostManagementService starboardPostManagementService;
protected StarboardPostReactorManagementService starboardPostReactorManagementService;
@Autowired
private StarboardPostReactorManagementService starboardPostReactorManagementService;
protected UserInServerManagementService userInServerManagementService;
@Autowired
private UserInServerManagementService userInServerManagementService;
protected EmoteService emoteService;
@Autowired
private EmoteService emoteService;
@Autowired
private MetricService metricService;
protected MetricService metricService;
public static final String STARBOARD_STARS = "starboard.stars";
public static final String STARBOARD_POSTS = "starboard.posts";
public static final String STAR_ACTION = "action";
private static final CounterMetric STARBOARD_STARS_ADDED = CounterMetric
.builder()
.name(STARBOARD_STARS)
.tagList(Arrays.asList(MetricTag.getTag(STAR_ACTION, "added")))
.build();
private static final CounterMetric STARBOARD_STARS_REMOVED = CounterMetric
.builder()
.name(STARBOARD_STARS)
.tagList(Arrays.asList(MetricTag.getTag(STAR_ACTION, "removed")))
.build();
private static final CounterMetric STARBOARD_STARS_THRESHOLD_REACHED = CounterMetric
protected static final CounterMetric STARBOARD_STARS_THRESHOLD_REACHED = CounterMetric
.builder()
.name(STARBOARD_POSTS)
.tagList(Arrays.asList(MetricTag.getTag(STAR_ACTION, "threshold.reached")))
.build();
private static final CounterMetric STARBOARD_STARS_THRESHOLD_FELL = CounterMetric
protected static final CounterMetric STARBOARD_STARS_THRESHOLD_FELL = CounterMetric
.builder()
.name(STARBOARD_POSTS)
.tagList(Arrays.asList(MetricTag.getTag(STAR_ACTION, "threshold.below")))
.build();
@Override
@Transactional
public void executeReactionAdded(CachedMessage message, CachedReactions cachedReaction, ServerUser serverUser) {
if(serverUser.getUserId().equals(message.getAuthor().getAuthorId())) {
return;
}
Long guildId = message.getServerId();
AEmote aEmote = emoteService.getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, guildId);
if(emoteService.compareCachedEmoteWithAEmote(cachedReaction.getEmote(), aEmote)) {
metricService.incrementCounter(STARBOARD_STARS_ADDED);
log.info("User {} in server {} reacted with star to put a message {} from channel {} on starboard.", serverUser.getUserId(), message.getServerId(), message.getMessageId(), message.getChannelId());
Optional<CachedReactions> reactionOptional = emoteService.getReactionFromMessageByEmote(message, aEmote);
handleStarboardPostChange(message, reactionOptional.orElse(null), serverUser, true);
}
}
private void handleStarboardPostChange(CachedMessage message, CachedReactions reaction, ServerUser serverUser, boolean adding) {
protected void handleStarboardPostChange(CachedMessage message, CachedReactions reaction, ServerUser userReacting, boolean adding) {
Optional<StarboardPost> starboardPostOptional = starboardPostManagementService.findByMessageId(message.getMessageId());
if(reaction != null) {
AUserInAServer author = userInServerManagementService.loadOrCreateUser(message.getServerId(), message.getAuthor().getAuthorId());
List<AUserInAServer> userExceptAuthor = getUsersExcept(reaction.getUsers(), author);
Long starMinimum = getFromConfig(FIRST_LEVEL_THRESHOLD_KEY, message.getServerId());
AUserInAServer userAddingReaction = userInServerManagementService.loadOrCreateUser(serverUser);
AUserInAServer userAddingReaction = userInServerManagementService.loadOrCreateUser(userReacting);
if (userExceptAuthor.size() >= starMinimum) {
log.info("Post reached starboard minimum. Message {} in channel {} in server {} will be starred/updated.",
message.getMessageId(), message.getChannelId(), message.getServerId());
@@ -122,24 +83,27 @@ public class StarboardListener implements AsyncReactionAddedListener, AsyncReact
} else {
metricService.incrementCounter(STARBOARD_STARS_THRESHOLD_REACHED);
log.info("Creating starboard post for message {} in channel {} in server {}", message.getMessageId(), message.getChannelId(), message.getServerId());
starboardService.createStarboardPost(message, userExceptAuthor, userAddingReaction, author);
starboardService.createStarboardPost(message, userExceptAuthor, userAddingReaction, author).exceptionally(throwable -> {
log.error("Failed to persist starboard post for message {}.", message.getMessageId(), throwable);
return null;
});
}
} else {
if(starboardPostOptional.isPresent()) {
metricService.incrementCounter(STARBOARD_STARS_THRESHOLD_FELL);
log.info("Removing starboard post for message {} in channel {} in server {}. It fell under the threshold {}", message.getMessageId(), message.getChannelId(), message.getServerId(), starMinimum);
starboardPostOptional.ifPresent(this::completelyRemoveStarboardPost);
starboardPostOptional.ifPresent(starboardPost -> completelyRemoveStarboardPost(starboardPost, userReacting));
}
}
} else {
if(starboardPostOptional.isPresent()) {
log.info("Removing starboard post for message {} in channel {} in server {}", message.getMessageId(), message.getChannelId(), message.getServerId());
starboardPostOptional.ifPresent(this::completelyRemoveStarboardPost);
starboardPostOptional.ifPresent(starboardPost -> completelyRemoveStarboardPost(starboardPost, userReacting));
}
}
}
private void updateStarboardPost(CachedMessage message, AUserInAServer userReacting, boolean adding, StarboardPost starboardPost, List<AUserInAServer> userExceptAuthor) {
protected void updateStarboardPost(CachedMessage message, AUserInAServer userReacting, boolean adding, StarboardPost starboardPost, List<AUserInAServer> userExceptAuthor) {
starboardPost.setIgnored(false);
// TODO handle futures correctly
starboardService.updateStarboardPost(starboardPost, message, userExceptAuthor);
@@ -152,62 +116,24 @@ public class StarboardListener implements AsyncReactionAddedListener, AsyncReact
}
}
private void completelyRemoveStarboardPost(StarboardPost starboardPost) {
starboardService.deleteStarboardMessagePost(starboardPost);
starboardPostManagementService.removePost(starboardPost);
protected void completelyRemoveStarboardPost(StarboardPost starboardPost, ServerUser userReacting) {
starboardService.deleteStarboardPost(starboardPost, userReacting);
}
@Override
@Transactional
public void executeReactionRemoved(CachedMessage message, CachedReactions removedReaction, ServerUser userRemoving) {
if(message.getAuthor().getAuthorId().equals(userRemoving.getUserId())) {
return;
}
Long guildId = message.getServerId();
AEmote aEmote = emoteService.getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, guildId);
if(emoteService.compareCachedEmoteWithAEmote(removedReaction.getEmote(), aEmote)) {
metricService.incrementCounter(STARBOARD_STARS_REMOVED);
log.info("User {} in server {} removed star reaction from message {} on starboard.",
userRemoving.getUserId(), message.getServerId(), message.getMessageId());
Optional<CachedReactions> reactionOptional = emoteService.getReactionFromMessageByEmote(message, aEmote);
handleStarboardPostChange(message, reactionOptional.orElse(null), userRemoving, false);
}
}
private Long getFromConfig(String key, Long guildId) {
protected Long getFromConfig(String key, Long guildId) {
return configManagementService.loadConfig(guildId, key).getLongValue();
}
private List<AUserInAServer> getUsersExcept(List<ServerUser> users, AUserInAServer author) {
protected List<AUserInAServer> getUsersExcept(List<ServerUser> users, AUserInAServer author) {
return users.stream().filter(user -> !(user.getServerId().equals(author.getServerReference().getId()) && user.getUserId().equals(author.getUserReference().getId()))).map(serverUser -> {
Optional<AUserInAServer> aUserInAServer = userInServerManagementService.loadUserOptional(serverUser.getServerId(), serverUser.getUserId());
return aUserInAServer.orElse(null);
}).filter(Objects::nonNull).collect(Collectors.toList());
}
@Override
public FeatureDefinition getFeature() {
return StarboardFeatureDefinition.STARBOARD;
}
@Override
public void executeReactionCleared(CachedMessage message) {
Optional<StarboardPost> starboardPostOptional = starboardPostManagementService.findByMessageId(message.getMessageId());
starboardPostOptional.ifPresent(starboardPost -> {
log.info("Reactions on message {} in channel {} in server {} were cleared. Completely deleting the starboard post {}.",
message.getMessageId(), message.getChannelId(), message.getServerId(), starboardPost.getId());
starboardPostReactorManagementService.removeReactors(starboardPost);
completelyRemoveStarboardPost(starboardPost);
});
}
@PostConstruct
public void postConstruct() {
metricService.registerCounter(STARBOARD_STARS_ADDED, "Star reaction added");
metricService.registerCounter(STARBOARD_STARS_REMOVED, "Star reaction removed");
metricService.registerCounter(STARBOARD_STARS_THRESHOLD_REACHED, "Starboard posts reaching threshold");
metricService.registerCounter(STARBOARD_STARS_THRESHOLD_FELL, "Starboard posts falling below threshold");
}
}

View File

@@ -1,8 +1,10 @@
package dev.sheldan.abstracto.starboard.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.listener.DefaultListenerResult;
import dev.sheldan.abstracto.core.listener.async.jda.AsyncMessageDeletedListener;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.listener.MessageDeletedModel;
import dev.sheldan.abstracto.starboard.config.StarboardFeatureDefinition;
import dev.sheldan.abstracto.starboard.model.database.StarboardPost;
import dev.sheldan.abstracto.starboard.service.management.StarboardPostManagementService;
@@ -20,14 +22,16 @@ public class StarboardPostDeletedListener implements AsyncMessageDeletedListener
private StarboardPostManagementService starboardPostManagementService;
@Override
public void execute(CachedMessage messageBefore) {
Optional<StarboardPost> byStarboardPostId = starboardPostManagementService.findByStarboardPostId(messageBefore.getMessageId());
public DefaultListenerResult execute(MessageDeletedModel model) {
CachedMessage message = model.getCachedMessage();
Optional<StarboardPost> byStarboardPostId = starboardPostManagementService.findByStarboardPostId(message.getMessageId());
if(byStarboardPostId.isPresent()) {
StarboardPost post = byStarboardPostId.get();
log.info("Removing starboard post: message {}, channel {}, server {}, because the message was deleted",
post.getPostMessageId(), post.getSourceChannel().getId(), messageBefore.getServerId());
starboardPostManagementService.setStarboardPostIgnored(messageBefore.getMessageId(), true);
post.getPostMessageId(), post.getSourceChannel().getId(), message.getServerId());
starboardPostManagementService.setStarboardPostIgnored(message.getMessageId(), true);
}
return DefaultListenerResult.PROCESSED;
}
@Override

View File

@@ -0,0 +1,98 @@
package dev.sheldan.abstracto.starboard.service;
import dev.sheldan.abstracto.core.listener.ListenerService;
import dev.sheldan.abstracto.core.models.ServerChannelMessage;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.service.FeatureConfigService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.starboard.listener.StarboardPostState;
import dev.sheldan.abstracto.starboard.listener.StarboardPostUpdatedListener;
import dev.sheldan.abstracto.starboard.model.StarboardPostUpdatedModel;
import dev.sheldan.abstracto.starboard.model.database.StarboardPost;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.task.TaskExecutor;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.stream.Collectors;
@Component
@Slf4j
public class StarboardPostListenerManager {
@Autowired(required = false)
private List<StarboardPostUpdatedListener> listeners;
@Autowired
@Qualifier("starboardStatusListenerExecutor")
private TaskExecutor starboardStatusExecutor;
@Autowired
private StarboardPostListenerManager self;
@Autowired
private FeatureConfigService featureConfigService;
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private ListenerService listenerService;
public void sendStarboardPostCreatedEvent(Long userReactingId, StarboardPost post) {
if(listeners == null || listeners.isEmpty()) {
return;
}
ServerUser userReactingServerUser = ServerUser.builder().serverId(post.getServer().getId()).userId(userReactingId).build();
StarboardPostUpdatedModel model = createStarboardStatusModel(post, userReactingServerUser);
model.setNewState(StarboardPostState.CREATED);
listeners.forEach(listener -> listenerService.executeFeatureAwareListener(listener, model));
}
public void sendStarboardPostDeletedEvent(StarboardPost post, ServerUser userReacting) {
if(listeners == null || listeners.isEmpty()) {
return;
}
StarboardPostUpdatedModel model = createStarboardStatusModel(post, userReacting);
model.setNewState(StarboardPostState.DELETED);
}
private StarboardPostUpdatedModel createStarboardStatusModel(StarboardPost post, ServerUser userReacting) {
Long serverId = post.getServer().getId();
ServerUser starredUser = ServerUser
.builder()
.serverId(serverId)
.userId(post.getAuthor().getUserReference().getId())
.build();
ServerChannelMessage starboardMessagePayLoad = ServerChannelMessage
.builder()
.serverId(serverId)
.channelId(post.getStarboardChannel().getId())
.messageId(post.getStarboardMessageId())
.build();
ServerChannelMessage starredMessage = ServerChannelMessage
.builder()
.serverId(serverId)
.channelId(post.getSourceChannel().getId())
.messageId(post.getPostMessageId())
.build();
List<Long> starrerIds = post.
getReactions()
.stream()
.map(starboardPostReaction -> starboardPostReaction.getReactor().getUserReference().getId())
.collect(Collectors.toList());
return StarboardPostUpdatedModel
.builder()
.lastStarrer(userReacting)
.starredUser(starredUser)
.starboardMessage(starboardMessagePayLoad)
.starredMessage(starredMessage)
.allStarrer(starrerIds)
.build();
}
}

View File

@@ -2,9 +2,9 @@ package dev.sheldan.abstracto.starboard.service;
import dev.sheldan.abstracto.core.exception.UserInServerNotFoundException;
import dev.sheldan.abstracto.core.models.AServerAChannelMessage;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AUser;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.models.database.PostTarget;
import dev.sheldan.abstracto.core.models.property.SystemConfigProperty;
@@ -13,9 +13,9 @@ import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.DefaultConfigManagementService;
import dev.sheldan.abstracto.core.service.management.PostTargetManagement;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
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.core.utils.FutureUtils;
import dev.sheldan.abstracto.starboard.config.StarboardFeature;
import dev.sheldan.abstracto.starboard.config.StarboardPostTarget;
import dev.sheldan.abstracto.starboard.model.database.StarboardPost;
@@ -90,40 +90,47 @@ public class StarboardServiceBean implements StarboardService {
@Autowired
private StarboardServiceBean self;
@Autowired
private StarboardPostListenerManager starboardPostListenerManager;
@Autowired
private UserService userService;
@Override
public CompletableFuture<Void> createStarboardPost(CachedMessage message, List<AUserInAServer> userExceptAuthor, AUserInAServer userReacting, AUserInAServer starredUser) {
Long starredUserId = starredUser.getUserInServerId();
List<Long> userExceptAuthorIds = userExceptAuthor.stream().map(AUserInAServer::getUserInServerId).collect(Collectors.toList());
Long userReactingId = userReacting.getUserReference().getId();
return buildStarboardPostModel(message, userExceptAuthor.size()).thenCompose(starboardPostModel ->
self.sendStarboardPostAndStore(message, starredUserId, userExceptAuthorIds, starboardPostModel)
self.sendStarboardPostAndStore(message, starredUserId, userExceptAuthorIds, starboardPostModel, userReactingId)
);
}
@Transactional
public CompletionStage<Void> sendStarboardPostAndStore(CachedMessage message, Long starredUserId, List<Long> userExceptAuthorIds, StarboardPostModel starboardPostModel) {
public CompletionStage<Void> sendStarboardPostAndStore(CachedMessage message, Long starredUserId, List<Long> userExceptAuthorIds, StarboardPostModel starboardPostModel, Long userReactingId) {
MessageToSend messageToSend = templateService.renderEmbedTemplate(STARBOARD_POST_TEMPLATE, starboardPostModel);
PostTarget starboard = postTargetManagement.getPostTarget(StarboardPostTarget.STARBOARD.getKey(), message.getServerId());
List<CompletableFuture<Message>> completableFutures = postTargetService.sendEmbedInPostTarget(messageToSend, StarboardPostTarget.STARBOARD, message.getServerId());
Long starboardChannelId = starboard.getChannelReference().getId();
return CompletableFuture.allOf(completableFutures.toArray(new CompletableFuture[0])).thenAccept(aVoid ->
self.persistPost(message, userExceptAuthorIds, completableFutures, starboardChannelId, starredUserId)
self.persistPost(message, userExceptAuthorIds, completableFutures, starboardChannelId, starredUserId, userReactingId)
);
}
@Transactional
public void persistPost(CachedMessage message, List<Long> userExceptAuthorIds, List<CompletableFuture<Message>> completableFutures, Long starboardChannelId, Long starredUserId) {
public void persistPost(CachedMessage message, List<Long> userExceptAuthorIds, List<CompletableFuture<Message>> completableFutures, Long starboardChannelId, Long starredUserId, Long userReactingId) {
AUserInAServer innerStarredUser = userInServerManagementService.loadUserOptional(starredUserId).orElseThrow(() -> new UserInServerNotFoundException(starredUserId));
AChannel starboardChannel = channelManagementService.loadChannel(starboardChannelId);
Message message1 = completableFutures.get(0).join();
Message starboardMessage = completableFutures.get(0).join();
AServerAChannelMessage aServerAChannelMessage = AServerAChannelMessage
.builder()
.messageId(message1.getIdLong())
.messageId(starboardMessage.getIdLong())
.channel(starboardChannel)
.server(starboardChannel.getServer())
.build();
StarboardPost starboardPost = starboardPostManagementService.createStarboardPost(message, innerStarredUser, aServerAChannelMessage);
log.info("Persisting starboard post in channel {} with message {} with {} reactors.", message1.getId(),starboardChannelId, userExceptAuthorIds.size());
log.info("Persisting starboard post in channel {} with message {} with {} reactors.", starboardMessage.getId(),starboardChannelId, userExceptAuthorIds.size());
if(userExceptAuthorIds.isEmpty()) {
log.warn("There are no user ids except the author for the reactions in post {} in guild {} for message {} in channel {}.", starboardPost.getId(), message.getChannelId(), message.getMessageId(), message.getChannelId());
}
@@ -131,25 +138,23 @@ public class StarboardServiceBean implements StarboardService {
AUserInAServer user = userInServerManagementService.loadUserOptional(aLong).orElseThrow(() -> new UserInServerNotFoundException(aLong));
starboardPostReactorManagementService.addReactor(starboardPost, user);
});
starboardPostListenerManager.sendStarboardPostCreatedEvent(userReactingId, starboardPost);
}
private CompletableFuture<StarboardPostModel> buildStarboardPostModel(CachedMessage message, Integer starCount) {
return memberService.getMemberInServerAsync(message.getServerId(), message.getAuthor().getAuthorId()).thenApply(member -> {
return userService.retrieveUserForId(message.getAuthor().getAuthorId()).thenApply(user -> {
Optional<TextChannel> channel = channelService.getTextChannelFromServerOptional(message.getServerId(), message.getChannelId());
Optional<Guild> guild = guildService.getGuildByIdOptional(message.getServerId());
// TODO use model objects instead of building entity models
AChannel aChannel = AChannel.builder().id(message.getChannelId()).build();
AUser user = AUser.builder().id(message.getAuthor().getAuthorId()).build();
String starLevelEmote = getAppropriateEmote(message.getServerId(), starCount);
return StarboardPostModel
.builder()
.message(message)
.author(member)
.author(user)
.sourceChannelId(message.getChannelId())
.channel(channel.orElse(null))
.aChannel(aChannel)
.starCount(starCount)
.guild(guild.orElse(null))
.user(user)
.starLevelEmote(starLevelEmote)
.build();
});
@@ -241,6 +246,13 @@ public class StarboardServiceBean implements StarboardService {
.build();
}
@Override
public void deleteStarboardPost(StarboardPost starboardPost, ServerUser userReacting) {
deleteStarboardMessagePost(starboardPost);
starboardPostManagementService.removePost(starboardPost);
starboardPostListenerManager.sendStarboardPostDeletedEvent(starboardPost, userReacting);
}
private String getStarboardRankingEmote(Long serverId, Integer position) {
return emoteService.getUsableEmoteOrDefault(serverId, buildBadgeName(position));
}

View File

@@ -12,10 +12,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.time.Instant;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.*;
@Component
@Slf4j
@@ -36,6 +33,7 @@ public class StarboardPostManagementServiceBean implements StarboardPostManageme
.postMessageId(starredMessage.getMessageId())
.sourceChannel(build)
.ignored(false)
.reactions(new ArrayList<>())
.server(starboardPost.getServer())
.starboardMessageId(starboardPost.getMessageId())
.starboardChannel(starboardPost.getChannel())
@@ -45,8 +43,7 @@ public class StarboardPostManagementServiceBean implements StarboardPostManageme
starredMessage.getMessageId(), starredMessage.getChannelId(), starredMessage.getServerId(),
starboardPost.getMessageId(), starboardPost.getChannel().getId(), starboardPost.getServer().getId(),
starredUser.getUserReference().getId());
repository.save(post);
return post;
return repository.save(post);
}
@Override
@@ -57,7 +54,6 @@ public class StarboardPostManagementServiceBean implements StarboardPostManageme
@Override
public void setStarboardPostMessageId(StarboardPost post, Long messageId) {
post.setStarboardMessageId(messageId);
repository.save(post);
}
@Override

View File

@@ -33,8 +33,7 @@ public class StarboardPostReactorManagementServiceBean implements StarboardPostR
.server(user.getServerReference())
.build();
log.info("Persisting the reactor {} for starboard post {} in server {}.", user.getUserReference().getId(), post.getId(), user.getServerReference().getId());
repository.save(reactor);
return reactor;
return repository.save(reactor);
}
@Override

View File

@@ -3,10 +3,10 @@ package dev.sheldan.abstracto.starboard.listener;
import dev.sheldan.abstracto.core.metric.service.MetricService;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.cache.CachedAuthor;
import dev.sheldan.abstracto.core.models.cache.CachedEmote;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.cache.CachedReactions;
import dev.sheldan.abstracto.core.models.database.*;
import dev.sheldan.abstracto.core.models.listener.ReactionAddedModel;
import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.management.ConfigManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
@@ -15,6 +15,7 @@ import dev.sheldan.abstracto.starboard.model.database.StarboardPost;
import dev.sheldan.abstracto.starboard.service.StarboardService;
import dev.sheldan.abstracto.starboard.service.management.StarboardPostManagementService;
import dev.sheldan.abstracto.starboard.service.management.StarboardPostReactorManagementService;
import net.dv8tion.jda.api.entities.MessageReaction;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
@@ -22,18 +23,16 @@ import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class StarboardListenerTest {
public class StarAddedListenerTest {
@InjectMocks
private StarboardListener testUnit;
private StarAddedListener testUnit;
@Mock
private ConfigManagementService configManagementService;
@@ -57,7 +56,10 @@ public class StarboardListenerTest {
private MetricService metricService;
@Mock
private CachedReactions cachedReaction;
private MessageReaction reaction;
@Mock
private CachedReactions cachedReactions;
@Mock
private CachedMessage cachedMessage;
@@ -87,11 +89,14 @@ public class StarboardListenerTest {
private StarboardPost post;
@Mock
private CachedEmote cachedEmote;
private MessageReaction.ReactionEmote reactionEmote;
@Mock
private AEmote starEmote;
@Mock
private ReactionAddedModel model;
private static final Long MESSAGE_ID = 5L;
private static final Long SERVER_ID = 6L;
private static final Long AUTHOR_ID = 4L;
@@ -102,7 +107,9 @@ public class StarboardListenerTest {
when(cachedAuthor.getAuthorId()).thenReturn(AUTHOR_ID);
when(cachedMessage.getAuthor()).thenReturn(cachedAuthor);
when(serverUserActing.getUserId()).thenReturn(AUTHOR_ID);
testUnit.executeReactionAdded(cachedMessage, cachedReaction, serverUserActing);
when(model.getMessage()).thenReturn(cachedMessage);
when(model.getUserReacting()).thenReturn(serverUserActing);
testUnit.execute(model);
verify(emoteService, times(0)).getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID);
}
@@ -110,9 +117,13 @@ public class StarboardListenerTest {
public void testAddingWrongEmote() {
when(serverUserActing.getUserId()).thenReturn(USER_ACTING_ID);
setupWrongEmote(SERVER_ID, AUTHOR_ID, starEmote);
when(cachedReaction.getEmote()).thenReturn(cachedEmote);
when(emoteService.compareCachedEmoteWithAEmote(cachedEmote, starEmote)).thenReturn(false);
testUnit.executeReactionAdded(cachedMessage, cachedReaction, serverUserActing);
when(emoteService.isReactionEmoteAEmote(reactionEmote, starEmote)).thenReturn(false);
when(model.getMessage()).thenReturn(cachedMessage);
when(model.getUserReacting()).thenReturn(serverUserActing);
when(model.getServerId()).thenReturn(SERVER_ID);
when(model.getReaction()).thenReturn(reaction);
when(reaction.getReactionEmote()).thenReturn(reactionEmote);
testUnit.execute(model);
verify(emoteService, times(1)).getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID);
verify(emoteService, times(0)).getReactionFromMessageByEmote(any(CachedMessage.class), eq(starEmote));
}
@@ -122,8 +133,7 @@ public class StarboardListenerTest {
Long requiredStars = 5L;
setupActingAndAuthor();
executeAddingTest(requiredStars, post);
verify(starboardService, times(1)).deleteStarboardMessagePost(post);
verify(starboardPostManagementService, times(1)).removePost(post);
verify(starboardService, times(1)).deleteStarboardPost(post, serverUserActing);
}
@Test
@@ -154,67 +164,6 @@ public class StarboardListenerTest {
verify(starboardPostReactorManagementService, times(1)).addReactor(post, userInServerActing);
}
@Test
public void testAuthorRemovingReaction() {
when(cachedAuthor.getAuthorId()).thenReturn(AUTHOR_ID);
when(cachedMessage.getAuthor()).thenReturn(cachedAuthor);
when(serverUserActing.getUserId()).thenReturn(AUTHOR_ID);
testUnit.executeReactionRemoved(cachedMessage, cachedReaction, serverUserActing);
verify(emoteService, times(0)).getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID);
}
@Test
public void testRemovingWrongEmote() {
when(serverUserActing.getUserId()).thenReturn(USER_ACTING_ID);
setupWrongEmote(SERVER_ID, AUTHOR_ID, starEmote);
testUnit.executeReactionRemoved(cachedMessage, cachedReaction, serverUserActing);
verify(emoteService, times(1)).getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID);
verify(emoteService, times(0)).getReactionFromMessageByEmote(any(CachedMessage.class), eq(starEmote));
}
@Test
public void testRemoveReactionFromExistingPostBelowThreshold() {
Long requiredStars = 5L;
List<ServerUser> remainingUsers = Arrays.asList(serverUserActing);
setupActingAndAuthor();
executeRemovalTest(requiredStars, remainingUsers);
verify(starboardService, times(1)).deleteStarboardMessagePost(eq(post));
verify(starboardPostManagementService, times(1)).removePost(eq(post));
}
@Test
public void testRemoveReactionFromExistingPostAboveThreshold() {
Long requiredStars = 0L;
List<ServerUser> remainingUsers = Arrays.asList(serverUserActing);
setupActingAndAuthor();
when(userInServerManagementService.loadOrCreateUser(serverUserActing)).thenReturn(userInServerActing);
executeRemovalTest(requiredStars, remainingUsers);
verify(metricService, times(1)).incrementCounter(any());
verify(starboardService, times(0)).deleteStarboardMessagePost(eq(post));
verify(starboardPostManagementService, times(0)).removePost(eq(post));
}
@Test
public void testRemoveReactionFromExistingPostTriggeringThreshold() {
Long requiredStars = 1L;
ArrayList<ServerUser> usersRemaining = new ArrayList<>();
setupActingAndAuthor();
executeRemovalTest(requiredStars, usersRemaining);
verify(metricService, times(2)).incrementCounter(any());
verify(starboardService, times(1)).deleteStarboardMessagePost(eq(post));
verify(starboardPostManagementService, times(1)).removePost(eq(post));
}
@Test
public void testReactionsClearedOnStarredMessage() {
executeClearingTest(Mockito.mock(StarboardPost.class));
}
@Test
public void testReactionsClearedOnNotStarredMessage() {
executeClearingTest(null);
}
private void setupActingAndAuthor() {
when(userInServerActing.getUserReference()).thenReturn(userActing);
when(userActing.getId()).thenReturn(USER_ACTING_ID);
@@ -224,57 +173,16 @@ public class StarboardListenerTest {
when(aUser.getId()).thenReturn(AUTHOR_ID);
}
private void executeClearingTest(StarboardPost post) {
when(cachedMessage.getMessageId()).thenReturn(MESSAGE_ID);
when(starboardPostManagementService.findByMessageId(MESSAGE_ID)).thenReturn(Optional.ofNullable(post));
testUnit.executeReactionCleared(cachedMessage);
int callCount = post != null ? 1 : 0;
verify(starboardPostReactorManagementService, times(callCount)).removeReactors(post);
verify(starboardService, times(callCount)).deleteStarboardMessagePost(eq(post));
verify(starboardPostManagementService, times(callCount)).removePost(eq(post));
}
private void executeRemovalTest(Long requiredStars, List<ServerUser> remainingUsers) {
when(cachedMessage.getServerId()).thenReturn(SERVER_ID);
when(cachedMessage.getMessageId()).thenReturn(MESSAGE_ID);
when(cachedAuthor.getAuthorId()).thenReturn(AUTHOR_ID);
when(cachedMessage.getAuthor()).thenReturn(cachedAuthor);
when(cachedReaction.getEmote()).thenReturn(cachedEmote);
when(emoteService.compareCachedEmoteWithAEmote(cachedEmote, starEmote)).thenReturn(true);
when(emoteService.getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID)).thenReturn(starEmote);
CachedReactions reaction = Mockito.mock(CachedReactions.class);
when(reaction.getUsers()).thenReturn(remainingUsers);
when(emoteService.getReactionFromMessageByEmote(cachedMessage, starEmote)).thenReturn(Optional.of(reaction));
when(starboardPostManagementService.findByMessageId(MESSAGE_ID)).thenReturn(Optional.ofNullable(post));
when(userInServerManagementService.loadOrCreateUser(SERVER_ID, AUTHOR_ID)).thenReturn(userInAServer);
when(serverUserActing.getUserId()).thenReturn(USER_ACTING_ID);
when(serverUserActing.getServerId()).thenReturn(SERVER_ID);
if(!remainingUsers.isEmpty()) {
when(userInServerManagementService.loadUserOptional(SERVER_ID, USER_ACTING_ID)).thenReturn(Optional.of(userInServerActing));
}
AConfig starRequirementConfig = Mockito.mock(AConfig.class);
when(starRequirementConfig.getLongValue()).thenReturn(requiredStars);
when(configManagementService.loadConfig(SERVER_ID, StarboardListener.FIRST_LEVEL_THRESHOLD_KEY)).thenReturn(starRequirementConfig);
testUnit.executeReactionRemoved(cachedMessage, cachedReaction, serverUserActing);
verify(emoteService, times(1)).getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID);
verify(emoteService, times(1)).getReactionFromMessageByEmote(cachedMessage, starEmote);
}
private void executeAddingTest(Long requiredStars, StarboardPost postToUse) {
when(cachedMessage.getServerId()).thenReturn(SERVER_ID);
when(cachedMessage.getMessageId()).thenReturn(MESSAGE_ID);
when(cachedAuthor.getAuthorId()).thenReturn(AUTHOR_ID);
when(cachedMessage.getAuthor()).thenReturn(cachedAuthor);
when(cachedReaction.getEmote()).thenReturn(cachedEmote);
when(emoteService.compareCachedEmoteWithAEmote(cachedEmote, starEmote)).thenReturn(true);
when(reaction.getReactionEmote()).thenReturn(reactionEmote);
when(emoteService.isReactionEmoteAEmote(reactionEmote, starEmote)).thenReturn(true);
when(emoteService.getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID)).thenReturn(starEmote);
CachedReactions reaction = Mockito.mock(CachedReactions.class);
when(serverUserActing.getUserId()).thenReturn(USER_ACTING_ID);
when(serverUserActing.getServerId()).thenReturn(SERVER_ID);
when(reaction.getUsers()).thenReturn(Arrays.asList(serverUserActing));
when(emoteService.getReactionFromMessageByEmote(cachedMessage, starEmote)).thenReturn(Optional.of(reaction));
when(starboardPostManagementService.findByMessageId(MESSAGE_ID)).thenReturn(Optional.ofNullable(postToUse));
when(userInServerManagementService.loadOrCreateUser(SERVER_ID, AUTHOR_ID)).thenReturn(userInAServer);
when(userInServerManagementService.loadOrCreateUser(serverUserActing)).thenReturn(userInServerActing);
@@ -282,16 +190,21 @@ public class StarboardListenerTest {
AConfig starRequirementConfig = Mockito.mock(AConfig.class);
when(starRequirementConfig.getLongValue()).thenReturn(requiredStars);
when(configManagementService.loadConfig(SERVER_ID, StarboardListener.FIRST_LEVEL_THRESHOLD_KEY)).thenReturn(starRequirementConfig);
testUnit.executeReactionAdded(cachedMessage, cachedReaction, serverUserActing);
when(model.getMessage()).thenReturn(cachedMessage);
when(cachedReactions.getUsers()).thenReturn(Arrays.asList(serverUserActing));
when(emoteService.getReactionFromMessageByEmote(cachedMessage, starEmote)).thenReturn(Optional.of(cachedReactions));
when(model.getUserReacting()).thenReturn(serverUserActing);
when(model.getReaction()).thenReturn(reaction);
when(model.getServerId()).thenReturn(SERVER_ID);
testUnit.execute(model);
verify(emoteService, times(1)).getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID);
verify(emoteService, times(1)).getReactionFromMessageByEmote(cachedMessage, starEmote);
}
private void setupWrongEmote(Long serverId, Long authorId, AEmote starEmote) {
when(cachedMessage.getServerId()).thenReturn(serverId);
when(cachedAuthor.getAuthorId()).thenReturn(authorId);
when(cachedMessage.getAuthor()).thenReturn(cachedAuthor);
when(cachedReaction.getEmote()).thenReturn(cachedEmote);
when(reaction.getReactionEmote()).thenReturn(reactionEmote);
when(emoteService.getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, serverId)).thenReturn(starEmote);
}
}

View File

@@ -0,0 +1,66 @@
package dev.sheldan.abstracto.starboard.listener;
import dev.sheldan.abstracto.core.metric.service.MetricService;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.listener.ReactionClearedModel;
import dev.sheldan.abstracto.starboard.model.database.StarboardPost;
import dev.sheldan.abstracto.starboard.service.StarboardService;
import dev.sheldan.abstracto.starboard.service.management.StarboardPostManagementService;
import dev.sheldan.abstracto.starboard.service.management.StarboardPostReactorManagementService;
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.Optional;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class StarClearedListenerTest {
@InjectMocks
private StarClearedListener testUnit;
@Mock
private StarboardService starboardService;
@Mock
private StarboardPostManagementService starboardPostManagementService;
@Mock
private StarboardPostReactorManagementService starboardPostReactorManagementService;
@Mock
private MetricService metricService;
@Mock
private CachedMessage cachedMessage;
@Mock
private ReactionClearedModel model;
private static final Long MESSAGE_ID = 5L;
@Test
public void testReactionsClearedOnStarredMessage() {
executeClearingTest(Mockito.mock(StarboardPost.class));
}
@Test
public void testReactionsClearedOnNotStarredMessage() {
executeClearingTest(null);
}
private void executeClearingTest(StarboardPost post) {
when(cachedMessage.getMessageId()).thenReturn(MESSAGE_ID);
when(model.getMessage()).thenReturn(cachedMessage);
when(starboardPostManagementService.findByMessageId(MESSAGE_ID)).thenReturn(Optional.ofNullable(post));
testUnit.execute(model);
int callCount = post != null ? 1 : 0;
verify(starboardPostReactorManagementService, times(callCount)).removeReactors(post);
verify(starboardService, times(callCount)).deleteStarboardPost(post, null);
}
}

View File

@@ -0,0 +1,210 @@
package dev.sheldan.abstracto.starboard.listener;
import dev.sheldan.abstracto.core.metric.service.MetricService;
import dev.sheldan.abstracto.core.models.ServerUser;
import dev.sheldan.abstracto.core.models.cache.CachedAuthor;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.cache.CachedReactions;
import dev.sheldan.abstracto.core.models.database.*;
import dev.sheldan.abstracto.core.models.listener.ReactionRemovedModel;
import dev.sheldan.abstracto.core.service.EmoteService;
import dev.sheldan.abstracto.core.service.management.ConfigManagementService;
import dev.sheldan.abstracto.core.service.management.UserInServerManagementService;
import dev.sheldan.abstracto.starboard.config.StarboardFeature;
import dev.sheldan.abstracto.starboard.model.database.StarboardPost;
import dev.sheldan.abstracto.starboard.service.StarboardService;
import dev.sheldan.abstracto.starboard.service.management.StarboardPostManagementService;
import dev.sheldan.abstracto.starboard.service.management.StarboardPostReactorManagementService;
import net.dv8tion.jda.api.entities.MessageReaction;
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.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class StarRemovedListenerTest {
@InjectMocks
private StarRemovedListener testUnit;
@Mock
private ConfigManagementService configManagementService;
@Mock
private StarboardService starboardService;
@Mock
private StarboardPostManagementService starboardPostManagementService;
@Mock
private StarboardPostReactorManagementService starboardPostReactorManagementService;
@Mock
private UserInServerManagementService userInServerManagementService;
@Mock
private EmoteService emoteService;
@Mock
private MetricService metricService;
@Mock
private MessageReaction reaction;
@Mock
private CachedMessage cachedMessage;
@Mock
private ServerUser serverUserActing;
@Mock
private CachedAuthor cachedAuthor;
@Mock
private AUserInAServer userInServerActing;
@Mock
private AUser userActing;
@Mock
private CachedReactions cachedReactions;
@Mock
private AUserInAServer userInAServer;
@Mock
private AUser aUser;
@Mock
private AServer server;
@Mock
private StarboardPost post;
@Mock
private MessageReaction.ReactionEmote reactionEmote;
@Mock
private AEmote starEmote;
@Mock
private ReactionRemovedModel model;
private static final Long MESSAGE_ID = 5L;
private static final Long SERVER_ID = 6L;
private static final Long AUTHOR_ID = 4L;
private static final Long USER_ACTING_ID = 7L;
@Test
public void testAuthorRemovingReaction() {
when(cachedAuthor.getAuthorId()).thenReturn(AUTHOR_ID);
when(cachedMessage.getAuthor()).thenReturn(cachedAuthor);
when(serverUserActing.getUserId()).thenReturn(AUTHOR_ID);
when(model.getMessage()).thenReturn(cachedMessage);
when(model.getUserRemoving()).thenReturn(serverUserActing);
testUnit.execute(model);
verify(emoteService, times(0)).getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID);
}
@Test
public void testRemovingWrongEmote() {
when(serverUserActing.getUserId()).thenReturn(USER_ACTING_ID);
setupWrongEmote(SERVER_ID, AUTHOR_ID, starEmote);
when(model.getMessage()).thenReturn(cachedMessage);
when(model.getUserRemoving()).thenReturn(serverUserActing);
when(model.getReaction()).thenReturn(reaction);
when(reaction.getReactionEmote()).thenReturn(reactionEmote);
when(model.getServerId()).thenReturn(SERVER_ID);
testUnit.execute(model);
verify(emoteService, times(1)).getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID);
verify(emoteService, times(0)).getReactionFromMessageByEmote(any(CachedMessage.class), eq(starEmote));
}
@Test
public void testRemoveReactionFromExistingPostBelowThreshold() {
Long requiredStars = 5L;
List<ServerUser> remainingUsers = Arrays.asList(serverUserActing);
setupActingAndAuthor();
executeRemovalTest(requiredStars, remainingUsers);
verify(starboardService, times(1)).deleteStarboardPost(post, serverUserActing);
}
@Test
public void testRemoveReactionFromExistingPostAboveThreshold() {
Long requiredStars = 0L;
List<ServerUser> remainingUsers = Arrays.asList(serverUserActing);
setupActingAndAuthor();
when(userInServerManagementService.loadOrCreateUser(serverUserActing)).thenReturn(userInServerActing);
executeRemovalTest(requiredStars, remainingUsers);
verify(metricService, times(1)).incrementCounter(any());
verify(starboardService, times(0)).deleteStarboardMessagePost(eq(post));
verify(starboardPostManagementService, times(0)).removePost(eq(post));
}
@Test
public void testRemoveReactionFromExistingPostTriggeringThreshold() {
Long requiredStars = 1L;
ArrayList<ServerUser> usersRemaining = new ArrayList<>();
setupActingAndAuthor();
executeRemovalTest(requiredStars, usersRemaining);
verify(metricService, times(2)).incrementCounter(any());
verify(starboardService, times(1)).deleteStarboardPost(post, serverUserActing);
}
private void setupActingAndAuthor() {
when(userInServerActing.getUserReference()).thenReturn(userActing);
when(userActing.getId()).thenReturn(USER_ACTING_ID);
when(userInAServer.getServerReference()).thenReturn(server);
when(server.getId()).thenReturn(SERVER_ID);
when(userInAServer.getUserReference()).thenReturn(aUser);
when(aUser.getId()).thenReturn(AUTHOR_ID);
}
private void executeRemovalTest(Long requiredStars, List<ServerUser> remainingUsers) {
when(cachedMessage.getServerId()).thenReturn(SERVER_ID);
when(cachedMessage.getMessageId()).thenReturn(MESSAGE_ID);
when(cachedAuthor.getAuthorId()).thenReturn(AUTHOR_ID);
when(cachedMessage.getAuthor()).thenReturn(cachedAuthor);
when(reaction.getReactionEmote()).thenReturn(reactionEmote);
when(emoteService.isReactionEmoteAEmote(reactionEmote, starEmote)).thenReturn(true);
when(emoteService.getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID)).thenReturn(starEmote);
when(cachedReactions.getUsers()).thenReturn(remainingUsers);
when(emoteService.getReactionFromMessageByEmote(cachedMessage, starEmote)).thenReturn(Optional.of(cachedReactions));
when(starboardPostManagementService.findByMessageId(MESSAGE_ID)).thenReturn(Optional.ofNullable(post));
when(userInServerManagementService.loadOrCreateUser(SERVER_ID, AUTHOR_ID)).thenReturn(userInAServer);
when(serverUserActing.getUserId()).thenReturn(USER_ACTING_ID);
when(serverUserActing.getServerId()).thenReturn(SERVER_ID);
if(!remainingUsers.isEmpty()) {
when(userInServerManagementService.loadUserOptional(SERVER_ID, USER_ACTING_ID)).thenReturn(Optional.of(userInServerActing));
}
AConfig starRequirementConfig = Mockito.mock(AConfig.class);
when(starRequirementConfig.getLongValue()).thenReturn(requiredStars);
when(configManagementService.loadConfig(SERVER_ID, StarboardListener.FIRST_LEVEL_THRESHOLD_KEY)).thenReturn(starRequirementConfig);
when(model.getMessage()).thenReturn(cachedMessage);
when(model.getUserRemoving()).thenReturn(serverUserActing);
when(model.getReaction()).thenReturn(reaction);
when(model.getServerId()).thenReturn(SERVER_ID);
testUnit.execute(model);
verify(emoteService, times(1)).getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, SERVER_ID);
verify(emoteService, times(1)).getReactionFromMessageByEmote(cachedMessage, starEmote);
}
private void setupWrongEmote(Long serverId, Long authorId, AEmote starEmote) {
when(cachedAuthor.getAuthorId()).thenReturn(authorId);
when(cachedMessage.getAuthor()).thenReturn(cachedAuthor);
when(reaction.getReactionEmote()).thenReturn(reactionEmote);
when(emoteService.getEmoteOrDefaultEmote(StarboardFeature.STAR_EMOTE, serverId)).thenReturn(starEmote);
}
}

View File

@@ -2,6 +2,7 @@ package dev.sheldan.abstracto.starboard.listener;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.listener.MessageDeletedModel;
import dev.sheldan.abstracto.starboard.model.database.StarboardPost;
import dev.sheldan.abstracto.starboard.service.management.StarboardPostManagementService;
import org.junit.Test;
@@ -28,9 +29,11 @@ public class StarboardPostDeletedListenerTest {
public void deleteNonStarboardPost() {
Long messageId = 4L;
when(starboardPostManagementService.findByStarboardPostId(messageId)).thenReturn(Optional.empty());
MessageDeletedModel model = Mockito.mock(MessageDeletedModel.class);
CachedMessage cachedMessage = Mockito.mock(CachedMessage.class);
when(cachedMessage.getMessageId()).thenReturn(messageId);
testUnit.execute(cachedMessage);
when(model.getCachedMessage()).thenReturn(cachedMessage);
testUnit.execute(model);
verify( starboardPostManagementService, times(0)).setStarboardPostIgnored(messageId, true);
}
@@ -47,7 +50,9 @@ public class StarboardPostDeletedListenerTest {
CachedMessage cachedMessage = Mockito.mock(CachedMessage.class);
when(cachedMessage.getServerId()).thenReturn(serverId);
when(cachedMessage.getMessageId()).thenReturn(messageId);
testUnit.execute(cachedMessage);
MessageDeletedModel model = Mockito.mock(MessageDeletedModel.class);
when(model.getCachedMessage()).thenReturn(cachedMessage);
testUnit.execute(model);
verify( starboardPostManagementService, times(1)).setStarboardPostIgnored(messageId, true);
}

View File

@@ -20,15 +20,13 @@ import dev.sheldan.abstracto.starboard.model.database.StarboardPostReaction;
import dev.sheldan.abstracto.starboard.model.template.*;
import dev.sheldan.abstracto.starboard.service.management.StarboardPostManagementService;
import dev.sheldan.abstracto.starboard.service.management.StarboardPostReactorManagementService;
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 net.dv8tion.jda.api.entities.*;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.*;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.context.ApplicationEventPublisher;
import java.util.*;
import java.util.concurrent.CompletableFuture;
@@ -77,6 +75,12 @@ public class StarboardServiceBeanTest {
@Mock
private UserInServerManagementService userInServerManagementService;
@Mock
private ApplicationEventPublisher eventPublisher;
@Mock
private UserService userService;
@Mock
private MessageService messageService;
@@ -95,6 +99,9 @@ public class StarboardServiceBeanTest {
@Mock
private TextChannel mockedTextChannel;
@Mock
private User starredJdaUser;
@Mock
private Member starredMember;
@@ -107,6 +114,9 @@ public class StarboardServiceBeanTest {
@Mock
private AServer server;
@Mock
private AUser aUser;
private static final Long STARRED_USER_ID = 5L;
private static final Long STARRED_SERVER_USER_ID = 2L;
private static final Long SERVER_ID = 6L;
@@ -116,6 +126,7 @@ public class StarboardServiceBeanTest {
private static final Long CHANNEL_ID = 10L;
private static final Long MESSAGE_ID = 11L;
private static final Long SECOND_MESSAGE_ID = 12L;
private static final Long STARRING_USER_ID = 13L;
@Captor
private ArgumentCaptor<AUserInAServer> userInAServerArgumentCaptor;
@@ -131,6 +142,8 @@ public class StarboardServiceBeanTest {
AUserInAServer secondUserExceptAuthor = Mockito.mock(AUserInAServer.class);
userExceptAuthor.add(secondUserExceptAuthor);
AUserInAServer userReacting = Mockito.mock(AUserInAServer.class);
when(userReacting.getUserReference()).thenReturn(aUser);
when(aUser.getId()).thenReturn(STARRING_USER_ID);
AUserInAServer starredUser = Mockito.mock(AUserInAServer.class);
when(starredUser.getUserInServerId()).thenReturn(STARRED_SERVER_USER_ID);
CachedAuthor cachedAuthor = Mockito.mock(CachedAuthor.class);
@@ -139,8 +152,7 @@ public class StarboardServiceBeanTest {
when(message.getAuthor()).thenReturn(cachedAuthor);
when(message.getServerId()).thenReturn(SERVER_ID);
when(message.getChannelId()).thenReturn(CHANNEL_ID);
Member authorMember = Mockito.mock(Member.class);
when(memberService.getMemberInServerAsync(SERVER_ID, STARRED_USER_ID)).thenReturn(CompletableFuture.completedFuture(authorMember));
when(userService.retrieveUserForId(STARRED_USER_ID)).thenReturn(CompletableFuture.completedFuture(starredJdaUser));
when(channelService.getTextChannelFromServerOptional(SERVER_ID, CHANNEL_ID)).thenReturn(Optional.of(mockedTextChannel));
when(guildService.getGuildByIdOptional(SERVER_ID)).thenReturn(Optional.of(guild));
SystemConfigProperty config = Mockito.mock(SystemConfigProperty.class);
@@ -152,10 +164,11 @@ public class StarboardServiceBeanTest {
when(defaultConfigManagementService.getDefaultConfig(StarboardFeature.STAR_LVL_CONFIG_PREFIX + "2")).thenReturn(config);
when(defaultConfigManagementService.getDefaultConfig(StarboardFeature.STAR_LVL_CONFIG_PREFIX + "3")).thenReturn(config);
when(emoteService.getUsableEmoteOrDefault(SERVER_ID, StarboardFeature.STAR_EMOTE_PREFIX + "2")).thenReturn("b");
when(self.sendStarboardPostAndStore(eq(message), eq(STARRED_SERVER_USER_ID), anyList(), any())).thenReturn(CompletableFuture.completedFuture(null));
when(self.sendStarboardPostAndStore(eq(message), eq(STARRED_SERVER_USER_ID), anyList(), any(), eq(STARRING_USER_ID))).thenReturn(CompletableFuture.completedFuture(null));
CompletableFuture<Void> createPostFuture = testUnit.createStarboardPost(message, userExceptAuthor, userReacting, starredUser);
createPostFuture.join();
Assert.assertFalse(createPostFuture.isCompletedExceptionally());
verify(self, times(1)).sendStarboardPostAndStore(eq(message), eq(STARRED_SERVER_USER_ID), anyList(), any(), eq(STARRING_USER_ID));
}
@Test
@@ -170,8 +183,8 @@ public class StarboardServiceBeanTest {
when(postTargetManagement.getPostTarget(StarboardPostTarget.STARBOARD.getKey(), SERVER_ID)).thenReturn(postTarget);
when(postTargetService.sendEmbedInPostTarget(messageToSend, StarboardPostTarget.STARBOARD, SERVER_ID)).thenReturn(Arrays.asList(CompletableFuture.completedFuture(null)));
ArrayList<Long> userExceptAuthorIds = new ArrayList<>();
testUnit.sendStarboardPostAndStore(message, STARRED_USER_ID, userExceptAuthorIds, model);
verify(self, times(1)).persistPost(eq(message), eq(userExceptAuthorIds), any(), eq(STARBOARD_CHANNEL_ID), eq(STARRED_USER_ID));
testUnit.sendStarboardPostAndStore(message, STARRED_USER_ID, userExceptAuthorIds, model, STARRING_USER_ID);
verify(self, times(1)).persistPost(eq(message), eq(userExceptAuthorIds), any(), eq(STARBOARD_CHANNEL_ID), eq(STARRED_USER_ID), eq(STARRING_USER_ID));
}
@Test
@@ -181,9 +194,13 @@ public class StarboardServiceBeanTest {
CachedMessage message = Mockito.mock(CachedMessage.class);
List<Long> userExceptAuthorIds = Arrays.asList(FIRST_USER_IN_SERVER_ID, SECOND_USER_IN_SERVER_ID);
List<CompletableFuture<Message>> futures = Arrays.asList(CompletableFuture.completedFuture(sendPost));
when(sendPost.getGuild()).thenReturn(guild);
when(sendPost.getChannel()).thenReturn(mockedTextChannel);
when(sendPost.getIdLong()).thenReturn(MESSAGE_ID);
when(userInServerManagementService.loadUserOptional(STARRED_SERVER_USER_ID)).thenReturn(Optional.of(starredUser));
when(userInServerManagementService.loadUserOptional(FIRST_USER_IN_SERVER_ID)).thenReturn(Optional.of(userReacting));
when(userReacting.getUserInServerId()).thenReturn(FIRST_USER_IN_SERVER_ID);
when(userReacting.getUserReference()).thenReturn(aUser);
AChannel channel = Mockito.mock(AChannel.class);
when(channelManagementService.loadChannel(CHANNEL_ID)).thenReturn(channel);
StarboardPost post = Mockito.mock(StarboardPost.class);
@@ -191,7 +208,8 @@ public class StarboardServiceBeanTest {
AUserInAServer secondStarrerUserObj = Mockito.mock(AUserInAServer.class);
when(userInServerManagementService.loadUserOptional(SECOND_USER_IN_SERVER_ID)).thenReturn(Optional.of(secondStarrerUserObj));
when(secondStarrerUserObj.getUserInServerId()).thenReturn(SECOND_USER_IN_SERVER_ID);
testUnit.persistPost(message, userExceptAuthorIds, futures, CHANNEL_ID, STARRED_SERVER_USER_ID);
when(secondStarrerUserObj.getUserReference()).thenReturn(aUser);
testUnit.persistPost(message, userExceptAuthorIds, futures, CHANNEL_ID, STARRED_SERVER_USER_ID, STARRING_USER_ID);
verify(starboardPostReactorManagementService, times(2)).addReactor(eq(post), userInAServerArgumentCaptor.capture());
List<AUserInAServer> addedReactors = userInAServerArgumentCaptor.getAllValues();
Assert.assertEquals(FIRST_USER_IN_SERVER_ID, addedReactors.get(0).getUserInServerId());
@@ -225,7 +243,7 @@ public class StarboardServiceBeanTest {
when(defaultConfigManagementService.getDefaultConfig(StarboardFeature.STAR_LEVELS_CONFIG_KEY)).thenReturn(config);
when(defaultConfigManagementService.getDefaultConfig(StarboardFeature.STAR_LVL_CONFIG_PREFIX + 1)).thenReturn(config);
when(starboardPostManagementService.findByStarboardPostId(starboardPostId)).thenReturn(Optional.of(post));
when(memberService.getMemberInServerAsync(SERVER_ID, STARRED_USER_ID)).thenReturn(CompletableFuture.completedFuture(starredMember));
when(userService.retrieveUserForId(STARRED_USER_ID)).thenReturn(CompletableFuture.completedFuture(starredJdaUser));
List<AUserInAServer > userExceptAuthor = new ArrayList<>();
CompletableFuture<Void> future = testUnit.updateStarboardPost(post, message, userExceptAuthor);
future.join();
@@ -254,7 +272,7 @@ public class StarboardServiceBeanTest {
CachedMessage message = Mockito.mock(CachedMessage.class);
List<Long> userExceptAuthorIds = Arrays.asList(FIRST_USER_IN_SERVER_ID);
List<CompletableFuture<Message>> futures = Arrays.asList(CompletableFuture.completedFuture(sendPost));
testUnit.persistPost(message, userExceptAuthorIds, futures, CHANNEL_ID, SECOND_USER_IN_SERVER_ID);
testUnit.persistPost(message, userExceptAuthorIds, futures, CHANNEL_ID, SECOND_USER_IN_SERVER_ID, STARRING_USER_ID);
}
@Test