renamed feature display to feature config

added concept of feature dependencies, if one feature depends on another feature, and it gets enabled, the other feature is enabled as well
changed some feature related apis
added ability to automatically decay warnings (separate feature)
added command to trigger the warn decay manually
added command to decay all active warnings
added method to scheduler service to directly start a cron job
This commit is contained in:
Sheldan
2020-04-29 21:25:26 +02:00
parent 85c47db5ed
commit d5482fabd4
60 changed files with 571 additions and 97 deletions

View File

@@ -31,7 +31,7 @@ public class CommandCreationListener {
@EventListener
@Transactional
public void handleContextRefreshEvent(ContextRefreshedEvent ctxStartEvt) {
featureFlagService.getAllFeatureDisplays().forEach((featureFlagKey) -> {
featureFlagService.getAllFeatureConfigs().forEach((featureFlagKey) -> {
String featureKey = featureFlagKey.getFeature().getKey();
if(!featureManagementService.featureExists(featureKey)) {
featureManagementService.createFeature(featureKey);

View File

@@ -9,6 +9,7 @@ import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.commands.config.ConfigModuleInterface;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.features.CoreFeatures;
import dev.sheldan.abstracto.core.models.template.commands.EnableModel;
@@ -43,8 +44,13 @@ public class Disable extends AbstractConditionableCommand {
channelService.sendTextInAChannel(response, commandContext.getChannel());
} else {
String flagKey = (String) commandContext.getParameters().getParameters().get(0);
FeatureEnum feature = featureFlagService.getFeatureEnum(flagKey);
FeatureConfig feature = featureFlagService.getFeatureDisplayForFeature(flagKey);
featureFlagService.disableFeature(feature, commandContext.getGuild().getIdLong());
if(feature.getDependantFeatures() != null) {
feature.getDependantFeatures().forEach(featureDisplay -> {
featureFlagService.disableFeature(featureDisplay, commandContext.getUserInitiatedContext().getServer());
});
}
}
return CommandResult.fromSuccess();
}

View File

@@ -9,6 +9,7 @@ import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.ContextConverter;
import dev.sheldan.abstracto.core.commands.config.ConfigModuleInterface;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.features.CoreFeatures;
import dev.sheldan.abstracto.core.models.template.commands.EnableModel;
@@ -42,8 +43,13 @@ public class Enable extends AbstractConditionableCommand {
channelService.sendTextInAChannel(response, commandContext.getChannel());
} else {
String flagKey = (String) commandContext.getParameters().getParameters().get(0);
FeatureEnum feature = featureFlagService.getFeatureEnum(flagKey);
featureFlagService.enableFeature(feature, commandContext.getGuild().getIdLong());
FeatureConfig feature = featureFlagService.getFeatureDisplayForFeature(flagKey);
featureFlagService.enableFeature(feature, commandContext.getUserInitiatedContext().getServer());
if(feature.getRequiredFeatures() != null) {
feature.getRequiredFeatures().forEach(featureDisplay -> {
featureFlagService.enableFeature(featureDisplay, commandContext.getUserInitiatedContext().getServer());
});
}
}
return CommandResult.fromSuccess();
}

View File

@@ -31,7 +31,7 @@ public class FeatureFlagListener implements ServerConfigListener {
@Override
public void updateServerConfig(AServer server) {
log.info("Setting up feature flags if necessary.");
featureFlagService.getAllFeatureDisplays().forEach((featureFlagKey) -> {
featureFlagService.getAllFeatureConfigs().forEach((featureFlagKey) -> {
String featureKey = featureFlagKey.getFeature().getKey();
AFeature feature = featureManagementService.getFeature(featureKey);
boolean featureFlagValue = BooleanUtils.toBoolean(environment.getProperty("abstracto.features." + featureKey, "false"));

View File

@@ -1,11 +1,11 @@
package dev.sheldan.abstracto.core.config.features;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.FeatureDisplay;
import org.springframework.stereotype.Component;
@Component
public class CoreFeature implements FeatureDisplay {
public class CoreFeature implements FeatureConfig {
@Override
public FeatureEnum getFeature() {

View File

@@ -1,6 +1,6 @@
package dev.sheldan.abstracto.core.converter;
import dev.sheldan.abstracto.core.config.FeatureDisplay;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.models.database.AFeatureFlag;
import dev.sheldan.abstracto.core.models.template.commands.FeatureFlagDisplay;
@@ -19,10 +19,10 @@ public class FeatureFlagConverter {
public FeatureFlagDisplay fromAFeatureFlag(AFeatureFlag featureFlag) {
FeatureEnum featureEnum = featureFlagService.getFeatureEnum(featureFlag.getFeature().getKey());
FeatureDisplay forFeature = featureFlagService.getFeatureDisplayforFeature(featureEnum);
FeatureConfig forFeature = featureFlagService.getFeatureDisplayForFeature(featureEnum);
return FeatureFlagDisplay
.builder()
.featureDisplay(forFeature)
.featureConfig(forFeature)
.featureFlag(featureFlag)
.build();
}

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.core.listener;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.management.UserManagementService;
@@ -31,7 +32,8 @@ public class JoinListenerBean extends ListenerAdapter {
@Transactional
public void onGuildMemberJoin(@Nonnull GuildMemberJoinEvent event) {
listenerList.forEach(joinListener -> {
if (!featureFlagService.isFeatureEnabled(joinListener.getFeature(), event.getGuild().getIdLong())) {
FeatureConfig feature = featureFlagService.getFeatureDisplayForFeature(joinListener.getFeature());
if (!featureFlagService.isFeatureEnabled(feature, event.getGuild().getIdLong())) {
return;
}
try {

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.core.listener;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import lombok.extern.slf4j.Slf4j;
@@ -26,7 +27,8 @@ public class LeaveListenerBean extends ListenerAdapter {
@Transactional
public void onGuildMemberLeave(@Nonnull GuildMemberLeaveEvent event) {
listenerList.forEach(leaveListener -> {
if(!featureFlagService.isFeatureEnabled(leaveListener.getFeature(), event.getGuild().getIdLong())) {
FeatureConfig feature = featureFlagService.getFeatureDisplayForFeature(leaveListener.getFeature());
if(!featureFlagService.isFeatureEnabled(feature, event.getGuild().getIdLong())) {
return;
}
try {

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.core.listener;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
@@ -40,7 +41,8 @@ public class MessageDeletedListenerBean extends ListenerAdapter {
@Transactional
public void executeListener(CachedMessage cachedMessage) {
listener.forEach(messageDeletedListener -> {
if(!featureFlagService.isFeatureEnabled(messageDeletedListener.getFeature(), cachedMessage.getServerId())) {
FeatureConfig feature = featureFlagService.getFeatureDisplayForFeature(messageDeletedListener.getFeature());
if(!featureFlagService.isFeatureEnabled(feature, cachedMessage.getServerId())) {
return;
}
try {

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.core.listener;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.MessageCache;
import lombok.extern.slf4j.Slf4j;
@@ -30,7 +31,8 @@ public class MessageReceivedListenerBean extends ListenerAdapter {
public void onGuildMessageReceived(@Nonnull GuildMessageReceivedEvent event) {
messageCache.putMessageInCache(event.getMessage());
listenerList.forEach(messageReceivedListener -> {
if(!featureFlagService.isFeatureEnabled(messageReceivedListener.getFeature(), event.getGuild().getIdLong())) {
FeatureConfig feature = featureFlagService.getFeatureDisplayForFeature(messageReceivedListener.getFeature());
if(!featureFlagService.isFeatureEnabled(feature, event.getGuild().getIdLong())) {
return;
}
messageReceivedListener.execute(event.getMessage());

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.core.listener;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.models.cache.CachedMessage;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
@@ -44,7 +45,8 @@ public class MessageUpdatedListener extends ListenerAdapter {
@Transactional
public void executeListener(Message message, CachedMessage cachedMessage) {
listener.forEach(messageTextUpdatedListener -> {
if(!featureFlagService.isFeatureEnabled(messageTextUpdatedListener.getFeature(), message.getGuild().getIdLong())) {
FeatureConfig feature = featureFlagService.getFeatureDisplayForFeature(messageTextUpdatedListener.getFeature());
if(!featureFlagService.isFeatureEnabled(feature, message.getGuild().getIdLong())) {
return;
}
try {

View File

@@ -1,5 +1,6 @@
package dev.sheldan.abstracto.core.listener;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.service.BotService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
@@ -97,7 +98,8 @@ public class ReactionUpdatedListener extends ListenerAdapter {
AUserInAServer userInAServer = userManagementService.loadUser(event.getGuild().getIdLong(), event.getUserIdLong());
addReactionIfNotThere(cachedMessage, reaction, userInAServer.getUserReference());
addedListenerList.forEach(reactedAddedListener -> {
if(!featureFlagService.isFeatureEnabled(reactedAddedListener.getFeature(), event.getGuild().getIdLong())) {
FeatureConfig feature = featureFlagService.getFeatureDisplayForFeature(reactedAddedListener.getFeature());
if(!featureFlagService.isFeatureEnabled(feature, event.getGuild().getIdLong())) {
return;
}
try {
@@ -130,14 +132,15 @@ public class ReactionUpdatedListener extends ListenerAdapter {
public void callRemoveListeners(@Nonnull GuildMessageReactionRemoveEvent event, CachedMessage cachedMessage, CachedReaction reaction) {
AUserInAServer userInAServer = userManagementService.loadUser(event.getGuild().getIdLong(), event.getUserIdLong());
removeReactionIfThere(cachedMessage, reaction, userInAServer.getUserReference());
reactionRemovedListener.forEach(reactedAddedListener -> {
if(!featureFlagService.isFeatureEnabled(reactedAddedListener.getFeature(), event.getGuild().getIdLong())) {
reactionRemovedListener.forEach(reactionRemovedListener -> {
FeatureConfig feature = featureFlagService.getFeatureDisplayForFeature(reactionRemovedListener.getFeature());
if(!featureFlagService.isFeatureEnabled(feature, event.getGuild().getIdLong())) {
return;
}
try {
reactedAddedListener.executeReactionRemoved(cachedMessage, event.getReaction(), userInAServer);
reactionRemovedListener.executeReactionRemoved(cachedMessage, event.getReaction(), userInAServer);
} catch (AbstractoRunTimeException e) {
log.warn(String.format("Failed to execute reaction removed listener %s.", reactedAddedListener.getClass().getName()), e);
log.warn(String.format("Failed to execute reaction removed listener %s.", reactionRemovedListener.getClass().getName()), e);
}
});
}

View File

@@ -0,0 +1,12 @@
package dev.sheldan.abstracto.core.repository;
import dev.sheldan.abstracto.core.models.database.AFeatureFlag;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface FeatureJobTriggerRepository extends JpaRepository<AFeatureJobTrigger, Long> {
List<AFeatureJobTrigger> findByFeature(AFeatureFlag featureFlag);
}

View File

@@ -1,10 +1,12 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.config.FeatureDisplay;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.exception.FeatureNotFoundException;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.management.FeatureFlagManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -19,28 +21,50 @@ public class FeatureFlagServiceBean implements FeatureFlagService {
private FeatureFlagManagementService managementService;
@Autowired
private List<FeatureDisplay> availableFeatures;
private List<FeatureConfig> availableFeatures;
@Autowired
private ServerManagementService serverManagementService;
@Override
public boolean isFeatureEnabled(FeatureEnum name, Long serverId) {
return managementService.getFeatureFlagValue(name, serverId);
public boolean isFeatureEnabled(FeatureConfig name, Long serverId) {
return managementService.getFeatureFlagValue(name.getFeature(), serverId);
}
@Override
public void enableFeature(FeatureEnum name, Long serverId) {
if(!doesFeatureExist(name)) {
throw new FeatureNotFoundException("Feature not found.", name.getKey(), getFeaturesAsList());
}
managementService.updateFeatureFlag(name, serverId, true);
public boolean isFeatureEnabled(FeatureConfig name, AServer server) {
return managementService.getFeatureFlagValue(name.getFeature(), server);
}
@Override
public void disableFeature(FeatureEnum name, Long serverId) {
public void enableFeature(FeatureConfig name, Long serverId) {
AServer server = serverManagementService.loadOrCreate(serverId);
enableFeature(name, server);
}
@Override
public void enableFeature(FeatureConfig name, AServer server) {
FeatureEnum feature = name.getFeature();
if(!doesFeatureExist(name)) {
throw new FeatureNotFoundException("Feature not found.", name.getKey(), getFeaturesAsList());
throw new FeatureNotFoundException("Feature not found.", feature.getKey(), getFeaturesAsList());
}
managementService.updateFeatureFlag(name, serverId, false);
managementService.updateFeatureFlag(feature, server, true);
}
@Override
public void disableFeature(FeatureConfig name, Long serverId) {
AServer server = serverManagementService.loadOrCreate(serverId);
disableFeature(name, server);
}
@Override
public void disableFeature(FeatureConfig name, AServer server) {
FeatureEnum feature = name.getFeature();
if(!doesFeatureExist(name)) {
throw new FeatureNotFoundException("Feature not found.", feature.getKey(), getFeaturesAsList());
}
managementService.updateFeatureFlag(feature, server, false);
}
@Override
@@ -52,13 +76,13 @@ public class FeatureFlagServiceBean implements FeatureFlagService {
}
@Override
public List<FeatureDisplay> getAllFeatureDisplays() {
public List<FeatureConfig> getAllFeatureConfigs() {
return availableFeatures;
}
@Override
public FeatureDisplay getFeatureDisplayforFeature(FeatureEnum featureEnum) {
Optional<FeatureDisplay> any = getAllFeatureDisplays().stream().filter(featureDisplay -> featureDisplay.getFeature().equals(featureEnum)).findAny();
public FeatureConfig getFeatureDisplayForFeature(FeatureEnum featureEnum) {
Optional<FeatureConfig> any = getAllFeatureConfigs().stream().filter(featureDisplay -> featureDisplay.getFeature().equals(featureEnum)).findAny();
if(any.isPresent()) {
return any.get();
}
@@ -66,8 +90,13 @@ public class FeatureFlagServiceBean implements FeatureFlagService {
}
@Override
public boolean doesFeatureExist(FeatureEnum name) {
return availableFeatures.stream().anyMatch(featureDisplay -> featureDisplay.getFeature().equals(name));
public FeatureConfig getFeatureDisplayForFeature(String key) {
return getFeatureDisplayForFeature(getFeatureEnum(key));
}
@Override
public boolean doesFeatureExist(FeatureConfig name) {
return availableFeatures.stream().anyMatch(featureDisplay -> featureDisplay.getFeature().equals(name.getFeature()));
}
@Override
@@ -80,7 +109,7 @@ public class FeatureFlagServiceBean implements FeatureFlagService {
@Override
public FeatureEnum getFeatureEnum(String key) {
Optional<FeatureDisplay> foundFeature = availableFeatures.stream().filter(featureDisplay -> featureDisplay.getFeature().getKey().equals(key)).findAny();
Optional<FeatureConfig> foundFeature = availableFeatures.stream().filter(featureDisplay -> featureDisplay.getFeature().getKey().equals(key)).findAny();
if(foundFeature.isPresent()) {
return foundFeature.get().getFeature();
}

View File

@@ -44,15 +44,27 @@ public class FeatureFlagManagementServiceBean implements FeatureFlagManagementSe
@Override
public boolean getFeatureFlagValue(FeatureEnum key, Long serverId) {
AServer server = serverManagementService.loadOrCreate(serverId);
return getFeatureFlagValue(key, server);
}
@Override
public boolean getFeatureFlagValue(FeatureEnum key, AServer server) {
AFeature feature = featureManagementService.getFeature(key.getKey());
Optional<AFeatureFlag> featureFlag = getFeatureFlag(feature, serverId);
Optional<AFeatureFlag> featureFlag = getFeatureFlag(feature, server);
return featureFlag.isPresent() && featureFlag.get().isEnabled();
}
@Override
public void updateFeatureFlag(FeatureEnum key, Long serverId, Boolean newValue) {
AServer server = serverManagementService.loadOrCreate(serverId);
updateFeatureFlag(key, server, newValue);
}
@Override
public void updateFeatureFlag(FeatureEnum key, AServer server, Boolean newValue) {
AFeature feature = featureManagementService.getFeature(key.getKey());
Optional<AFeatureFlag> existing = getFeatureFlag(feature, serverId);
Optional<AFeatureFlag> existing = getFeatureFlag(feature, server);
if(existing.isPresent()) {
AFeatureFlag flag = existing.get();
flag.setEnabled(newValue);
@@ -63,7 +75,12 @@ public class FeatureFlagManagementServiceBean implements FeatureFlagManagementSe
@Override
public Optional<AFeatureFlag> getFeatureFlag(AFeature feature, Long serverId) {
AServer server = serverManagementService.loadOrCreate(serverId);
return Optional.ofNullable(repository.findByServerAndFeature(server, feature));
return getFeatureFlag(feature, server);
}
@Override
public Optional<AFeatureFlag> getFeatureFlag(AFeature key, AServer server) {
return Optional.ofNullable(repository.findByServerAndFeature(server, key));
}
@Override

View File

@@ -9,7 +9,7 @@
},
"description": "
<#list features as feature>
${feature.featureFlag.enabled?string('✅', '❌')} **<#include "${feature.featureDisplay.feature.key}_feature">** Key: `${feature.featureDisplay.feature.key}`
${feature.featureFlag.enabled?string('✅', '❌')} **<#include "${feature.featureConfig.feature.key}_feature">** Key: `${feature.featureConfig.feature.key}`
</#list>
"
}

View File

@@ -1 +1 @@
Feature has been disabled. Necessary feature is: <#include "${featureDisplay.feature.key}_feature">, you can enable it by executing `enable ${featureDisplay.feature.key}`.
Feature has been disabled. Necessary feature is: <#include "${featureConfig.feature.key}_feature">, you can enable it by executing `enable ${featureConfig.feature.key}`.