refactored features to be components instead, so we have more runtime config (for example template config etc), this can be done, because features depend on the code anyway and do not need to be done via property files, the property files only define the default values when starting up

fixed feature disabled message not being templated and refactored the way condition checks are handled by the command received handler, so we do not use exceptions for this, this handled by a specified state in the result and a separate post execution handler
added separate config module to the commands
added command to see which features are available, and their current state
fixed scheduler test
This commit is contained in:
Sheldan
2020-04-26 11:38:27 +02:00
parent bd554537cc
commit 4800470f86
136 changed files with 950 additions and 332 deletions

View File

@@ -93,14 +93,18 @@ public class CommandReceivedHandler extends ListenerAdapter {
foundCommand = commandManager.findCommandByParameters(commandName, unparsedParameter);
Parameters parsedParameters = getParsedParameters(unparsedParameter, foundCommand, event.getMessage());
CommandContext commandContext = commandContextBuilder.parameters(parsedParameters).build();
CommandResult commandResult;
if(foundCommand instanceof ConditionalCommand) {
ConditionalCommand castedCommand = (ConditionalCommand) foundCommand;
ConditionResult conditionResult = checkConditions(commandContext, foundCommand, castedCommand.getConditions());
if(!conditionResult.isResult()) {
throw new AbstractoRunTimeException(conditionResult.getReason());
commandResult = CommandResult.fromCondition(conditionResult);
} else {
commandResult = self.executeCommand(foundCommand, commandContext);
}
} else {
commandResult = self.executeCommand(foundCommand, commandContext);
}
CommandResult commandResult = self.executeCommand(foundCommand, commandContext);
for (PostCommandExecution postCommandExecution : executions) {
postCommandExecution.execute(commandContext, commandResult, foundCommand);
}

View File

@@ -0,0 +1,28 @@
package dev.sheldan.abstracto.core.command.post;
import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.execution.ResultState;
import dev.sheldan.abstracto.core.command.service.PostCommandExecution;
import dev.sheldan.abstracto.core.service.MessageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class ConditionPostExecution implements PostCommandExecution {
public static final String WARN_REACTION_EMOTE = "warnReaction";
@Autowired
private MessageService messageService;
@Override
public void execute(CommandContext commandContext, CommandResult commandResult, Command command) {
if(commandResult.getResult().equals(ResultState.CONDITION)) {
messageService.addReactionToMessage(WARN_REACTION_EMOTE, commandContext.getGuild().getIdLong(), commandContext.getMessage());
if(commandResult.getConditionResult() != null && commandResult.getConditionResult().getReason() != null){
commandContext.getChannel().sendMessage(commandResult.getConditionResult().getReason()).queue();
}
}
}
}

View File

@@ -19,12 +19,13 @@ public class ReactionPostExecution implements PostCommandExecution {
@Override
public void execute(CommandContext commandContext, CommandResult commandResult, Command command) {
if(commandResult.getResult().equals(ResultState.ERROR)) {
ResultState result = commandResult.getResult();
if(result.equals(ResultState.ERROR)) {
messageService.addReactionToMessage(WARN_REACTION_EMOTE, commandContext.getGuild().getIdLong(), commandContext.getMessage());
if(commandResult.getMessage() != null && commandResult.getThrowable() == null){
commandContext.getChannel().sendMessage(commandResult.getMessage()).queue();
}
} else {
} else if(result.equals(ResultState.SUCCESSFUL)) {
if(command.getConfiguration().isCausesReaction()){
messageService.addReactionToMessage(SUCCESS_REACTION_EMOTE, commandContext.getGuild().getIdLong(), commandContext.getMessage());
}

View File

@@ -1,47 +0,0 @@
package dev.sheldan.abstracto.core.commands;
import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.config.AbstractoFeatures;
import dev.sheldan.abstracto.core.commands.utility.UtilityModuleInterface;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class Disable implements Command {
@Autowired
private FeatureFlagService featureFlagManagementService;
@Override
public CommandResult execute(CommandContext commandContext) {
String flagKey = (String) commandContext.getParameters().getParameters().get(0);
featureFlagManagementService.disableFeature(flagKey, commandContext.getGuild().getIdLong());
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter featureName = Parameter.builder().name("featureName").type(String.class).description("The feature to disable.").build();
List<Parameter> parameters = Arrays.asList(featureName);
return CommandConfiguration.builder()
.name("disable")
.module(UtilityModuleInterface.UTILITY)
.parameters(parameters)
.description("Disables features for this server.")
.causesReaction(true)
.build();
}
@Override
public String getFeature() {
return AbstractoFeatures.CORE;
}
}

View File

@@ -1,48 +0,0 @@
package dev.sheldan.abstracto.core.commands;
import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.config.AbstractoFeatures;
import dev.sheldan.abstracto.core.commands.utility.UtilityModuleInterface;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class Enable implements Command {
@Autowired
private FeatureFlagService featureFlagManagementService;
@Override
public CommandResult execute(CommandContext commandContext) {
String flagKey = (String) commandContext.getParameters().getParameters().get(0);
featureFlagManagementService.enableFeature(flagKey, commandContext.getGuild().getIdLong());
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter featureName = Parameter.builder().name("featureName").type(String.class).description("The feature to enable.").build();
List<Parameter> parameters = Arrays.asList(featureName);
return CommandConfiguration.builder()
.name("enable")
.module(UtilityModuleInterface.UTILITY)
.parameters(parameters)
.description("Enables features for this server.")
.causesReaction(true)
.build();
}
@Override
public String getFeature() {
return AbstractoFeatures.CORE;
}
}

View File

@@ -5,7 +5,8 @@ import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.config.AbstractoFeatures;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.features.CoreFeatures;
import dev.sheldan.abstracto.core.service.ChannelGroupService;
import net.dv8tion.jda.api.entities.TextChannel;
import org.springframework.beans.factory.annotation.Autowired;
@@ -46,7 +47,7 @@ public class AddToChannelGroup implements Command {
}
@Override
public String getFeature() {
return AbstractoFeatures.CORE;
public FeatureEnum getFeature() {
return CoreFeatures.CORE_FEATURE;
}
}

View File

@@ -5,7 +5,8 @@ import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.config.AbstractoFeatures;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.features.CoreFeatures;
import dev.sheldan.abstracto.core.service.ChannelGroupService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -42,7 +43,7 @@ public class CreateChannelGroup implements Command {
}
@Override
public String getFeature() {
return AbstractoFeatures.CORE;
public FeatureEnum getFeature() {
return CoreFeatures.CORE_FEATURE;
}
}

View File

@@ -5,7 +5,8 @@ import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.config.AbstractoFeatures;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.features.CoreFeatures;
import dev.sheldan.abstracto.core.service.ChannelGroupService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -42,7 +43,7 @@ public class DeleteChannelGroup implements Command {
}
@Override
public String getFeature() {
return AbstractoFeatures.CORE;
public FeatureEnum getFeature() {
return CoreFeatures.CORE_FEATURE;
}
}

View File

@@ -5,7 +5,8 @@ import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.config.AbstractoFeatures;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.features.CoreFeatures;
import dev.sheldan.abstracto.core.service.ChannelGroupService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -42,7 +43,7 @@ public class DisableCommand implements Command {
}
@Override
public String getFeature() {
return AbstractoFeatures.CORE;
public FeatureEnum getFeature() {
return CoreFeatures.CORE_FEATURE;
}
}

View File

@@ -5,7 +5,8 @@ import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.config.AbstractoFeatures;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.features.CoreFeatures;
import dev.sheldan.abstracto.core.service.ChannelGroupService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -42,7 +43,7 @@ public class EnableCommand implements Command {
}
@Override
public String getFeature() {
return AbstractoFeatures.CORE;
public FeatureEnum getFeature() {
return CoreFeatures.CORE_FEATURE;
}
}

View File

@@ -5,7 +5,8 @@ import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
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.AbstractoFeatures;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.features.CoreFeatures;
import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.models.template.commands.ChannelGroupChannelModel;
import dev.sheldan.abstracto.core.models.template.commands.ChannelGroupModel;
@@ -83,7 +84,7 @@ public class ListChannelGroups implements Command {
}
@Override
public String getFeature() {
return AbstractoFeatures.CORE;
public FeatureEnum getFeature() {
return CoreFeatures.CORE_FEATURE;
}
}

View File

@@ -4,7 +4,8 @@ import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.*;
import dev.sheldan.abstracto.core.config.AbstractoFeatures;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.features.CoreFeatures;
import dev.sheldan.abstracto.core.models.template.commands.PostTargetErrorModel;
import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.core.service.management.PostTargetManagement;
@@ -72,7 +73,7 @@ public class PostTarget implements Command {
}
@Override
public String getFeature() {
return AbstractoFeatures.CORE;
public FeatureEnum getFeature() {
return CoreFeatures.CORE_FEATURE;
}
}

View File

@@ -5,7 +5,8 @@ import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.config.AbstractoFeatures;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.features.CoreFeatures;
import dev.sheldan.abstracto.core.service.ChannelGroupService;
import net.dv8tion.jda.api.entities.TextChannel;
import org.springframework.beans.factory.annotation.Autowired;
@@ -46,7 +47,7 @@ public class RemoveFromChannelGroup implements Command {
}
@Override
public String getFeature() {
return AbstractoFeatures.CORE;
public FeatureEnum getFeature() {
return CoreFeatures.CORE_FEATURE;
}
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.abstracto.core.commands.config;
import dev.sheldan.abstracto.core.command.config.ModuleInfo;
import dev.sheldan.abstracto.core.command.config.ModuleInterface;
import org.springframework.stereotype.Component;
@Component
public class ConfigModuleInterface implements ModuleInterface {
public static final String CONFIG = "config";
@Override
public ModuleInfo getInfo() {
return ModuleInfo.builder().name(CONFIG).description("Utilities to configure the bot.").build();
}
@Override
public String getParentModule() {
return "default";
}
}

View File

@@ -5,8 +5,8 @@ import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
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.commands.channels.ChannelsModuleInterface;
import dev.sheldan.abstracto.core.config.AbstractoFeatures;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.features.CoreFeatures;
import dev.sheldan.abstracto.core.service.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@@ -36,7 +36,7 @@ public class SetNumber implements Command {
List<Parameter> parameters = Arrays.asList(channelGroupName, channelToAdd);
return CommandConfiguration.builder()
.name("setNumber")
.module(ChannelsModuleInterface.CHANNELS)
.module(ConfigModuleInterface.CONFIG)
.parameters(parameters)
.description("Used to change the config on this server.")
.causesReaction(true)
@@ -44,7 +44,7 @@ public class SetNumber implements Command {
}
@Override
public String getFeature() {
return AbstractoFeatures.CORE;
public FeatureEnum getFeature() {
return CoreFeatures.CORE_FEATURE;
}
}

View File

@@ -0,0 +1,67 @@
package dev.sheldan.abstracto.core.commands.config.features;
import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
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.FeatureEnum;
import dev.sheldan.abstracto.core.config.features.CoreFeatures;
import dev.sheldan.abstracto.core.models.template.commands.EnableModel;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.templating.service.TemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class Disable implements Command {
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private ChannelService channelService;
@Autowired
private TemplateService templateService;
@Override
public CommandResult execute(CommandContext commandContext) {
if(commandContext.getParameters().getParameters().size() == 0) {
EnableModel model = (EnableModel) ContextConverter.fromCommandContext(commandContext, EnableModel.class);
model.setFeatures(featureFlagService.getAllFeatures());
String response = templateService.renderTemplate("disable_features_response", model);
channelService.sendTextInAChannel(response, commandContext.getChannel());
} else {
String flagKey = (String) commandContext.getParameters().getParameters().get(0);
FeatureEnum feature = featureFlagService.getFeatureEnum(flagKey);
featureFlagService.disableFeature(feature, commandContext.getGuild().getIdLong());
}
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter featureName = Parameter.builder().name("featureName").type(String.class).optional(true).description("The feature to disable.").build();
List<Parameter> parameters = Arrays.asList(featureName);
return CommandConfiguration.builder()
.name("disable")
.module(ConfigModuleInterface.CONFIG)
.parameters(parameters)
.description("Disables features for this server.")
.causesReaction(true)
.build();
}
@Override
public FeatureEnum getFeature() {
return CoreFeatures.CORE_FEATURE;
}
}

View File

@@ -0,0 +1,66 @@
package dev.sheldan.abstracto.core.commands.config.features;
import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
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.FeatureEnum;
import dev.sheldan.abstracto.core.config.features.CoreFeatures;
import dev.sheldan.abstracto.core.models.template.commands.EnableModel;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.templating.service.TemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class Enable implements Command {
@Autowired
private FeatureFlagService featureFlagService;
@Autowired
private ChannelService channelService;
@Autowired
private TemplateService templateService;
@Override
public CommandResult execute(CommandContext commandContext) {
if(commandContext.getParameters().getParameters().size() == 0) {
EnableModel model = (EnableModel) ContextConverter.fromCommandContext(commandContext, EnableModel.class);
model.setFeatures(featureFlagService.getAllFeatures());
String response = templateService.renderTemplate("enable_features_response", model);
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());
}
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
Parameter featureName = Parameter.builder().name("featureName").type(String.class).optional(true).description("The feature to enable.").build();
List<Parameter> parameters = Arrays.asList(featureName);
return CommandConfiguration.builder()
.name("enable")
.module(ConfigModuleInterface.CONFIG)
.parameters(parameters)
.description("Enables features for this server.")
.causesReaction(true)
.build();
}
@Override
public FeatureEnum getFeature() {
return CoreFeatures.CORE_FEATURE;
}
}

View File

@@ -0,0 +1,62 @@
package dev.sheldan.abstracto.core.commands.config.features;
import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
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.commands.config.ConfigModuleInterface;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.features.CoreFeatures;
import dev.sheldan.abstracto.core.converter.FeatureFlagConverter;
import dev.sheldan.abstracto.core.models.database.AFeatureFlag;
import dev.sheldan.abstracto.core.models.template.commands.FeaturesModel;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.management.FeatureFlagManagementService;
import dev.sheldan.abstracto.templating.model.MessageToSend;
import dev.sheldan.abstracto.templating.service.TemplateService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class Features implements Command {
@Autowired
private FeatureFlagManagementService featureFlagManagementService;
@Autowired
private ChannelService channelService;
@Autowired
private TemplateService templateService;
@Autowired
private FeatureFlagConverter featureFlagConverter;
@Override
public CommandResult execute(CommandContext commandContext) {
List<AFeatureFlag> features = featureFlagManagementService.getFeatureFlagsOfServer(commandContext.getUserInitiatedContext().getServer());
FeaturesModel featuresModel = (FeaturesModel) ContextConverter.fromCommandContext(commandContext, FeaturesModel.class);
featuresModel.setFeatures(featureFlagConverter.fromFeatureFlags(features));
MessageToSend messageToSend = templateService.renderEmbedTemplate("features_response", featuresModel);
channelService.sendMessageToEndInTextChannel(messageToSend, commandContext.getChannel());
return CommandResult.fromSuccess();
}
@Override
public CommandConfiguration getConfiguration() {
return CommandConfiguration.builder()
.name("features")
.module(ConfigModuleInterface.CONFIG)
.description("Lists the available features and whether they are enabled or not.")
.causesReaction(true)
.build();
}
@Override
public FeatureEnum getFeature() {
return CoreFeatures.CORE_FEATURE;
}
}

View File

@@ -4,7 +4,8 @@ import dev.sheldan.abstracto.core.command.*;
import dev.sheldan.abstracto.core.command.config.*;
import dev.sheldan.abstracto.core.command.execution.*;
import dev.sheldan.abstracto.core.command.service.ModuleRegistry;
import dev.sheldan.abstracto.core.config.AbstractoFeatures;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.features.CoreFeatures;
import dev.sheldan.abstracto.templating.service.TemplateService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
@@ -135,7 +136,7 @@ public class Help implements Command {
}
@Override
public String getFeature() {
return AbstractoFeatures.CORE;
public FeatureEnum getFeature() {
return CoreFeatures.CORE_FEATURE;
}
}

View File

@@ -6,7 +6,8 @@ import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.AbstractoFeatures;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.features.CoreFeatures;
import dev.sheldan.abstracto.core.models.template.commands.EchoModel;
import dev.sheldan.abstracto.templating.service.TemplateService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -50,7 +51,7 @@ public class Echo implements Command {
}
@Override
public String getFeature() {
return AbstractoFeatures.CORE;
public FeatureEnum getFeature() {
return CoreFeatures.CORE_FEATURE;
}
}

View File

@@ -4,7 +4,8 @@ import dev.sheldan.abstracto.core.command.Command;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.AbstractoFeatures;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.features.CoreFeatures;
import dev.sheldan.abstracto.core.models.template.commands.PingModel;
import dev.sheldan.abstracto.templating.service.TemplateService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -38,8 +39,8 @@ public class Ping implements Command {
}
@Override
public String getFeature() {
return AbstractoFeatures.CORE;
public FeatureEnum getFeature() {
return CoreFeatures.CORE_FEATURE;
}
}

View File

@@ -5,7 +5,8 @@ import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.config.AbstractoFeatures;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.config.features.CoreFeatures;
import dev.sheldan.abstracto.core.service.management.EmoteManagementService;
import net.dv8tion.jda.api.entities.Emote;
import org.springframework.beans.factory.annotation.Autowired;
@@ -50,7 +51,7 @@ public class SetEmote implements Command {
}
@Override
public String getFeature() {
return AbstractoFeatures.CORE;
public FeatureEnum getFeature() {
return CoreFeatures.CORE_FEATURE;
}
}

View File

@@ -1,5 +0,0 @@
package dev.sheldan.abstracto.core.config;
public class AbstractoFeatures {
public static String CORE = "core";
}

View File

@@ -1,26 +0,0 @@
package dev.sheldan.abstracto.core.config;
import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
@Component
@Getter
@Setter
@ConfigurationProperties(prefix = "abstracto")
public class FeatureFlagConfig {
private HashMap<String, Boolean> features = new HashMap<>();
public boolean doesFeatureExist(String name) {
return features.containsKey(name);
}
public List<String> getFeaturesAsList() {
return new ArrayList<>(features.keySet());
}
}

View File

@@ -2,9 +2,12 @@ package dev.sheldan.abstracto.core.config;
import dev.sheldan.abstracto.core.listener.ServerConfigListener;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import dev.sheldan.abstracto.core.service.management.FeatureFlagManagementService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
@Component
@@ -12,17 +15,21 @@ import org.springframework.stereotype.Component;
public class FeatureFlagListener implements ServerConfigListener {
@Autowired
private FeatureFlagConfig featureFlagConfig;
private FeatureFlagService featureFlagService;
@Autowired
private FeatureFlagManagementService service;
@Autowired
private Environment environment;
@Override
public void updateServerConfig(AServer server) {
log.info("Setting up feature flags if necessary.");
featureFlagConfig.getFeatures().forEach((featureFlagKey, featureFlagValue) -> {
if(!service.getFeatureFlag(featureFlagKey, server.getId()).isPresent()) {
service.createFeatureFlag(featureFlagKey, server.getId(), featureFlagValue);
featureFlagService.getAllFeatureDisplays().forEach((featureFlagKey) -> {
boolean featureFlagValue = BooleanUtils.toBoolean(environment.getProperty("abstracto.features." + featureFlagKey.getFeature().getKey(), "false"));
if(!service.getFeatureFlag(featureFlagKey.getFeature(), server.getId()).isPresent()) {
service.createFeatureFlag(featureFlagKey.getFeature(), server.getId(), featureFlagValue);
}
});
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.core.config.features;
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 {
@Override
public FeatureEnum getFeature() {
return CoreFeatures.CORE_FEATURE;
}
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.abstracto.core.config.features;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import lombok.Getter;
@Getter
public enum CoreFeatures implements FeatureEnum {
CORE_FEATURE("core");
private String key;
CoreFeatures(String key) {
this.key = key;
}
}

View File

@@ -0,0 +1,33 @@
package dev.sheldan.abstracto.core.converter;
import dev.sheldan.abstracto.core.config.FeatureDisplay;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.models.database.AFeatureFlag;
import dev.sheldan.abstracto.core.models.template.commands.FeatureFlagDisplay;
import dev.sheldan.abstracto.core.service.FeatureFlagService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.stream.Collectors;
@Component
public class FeatureFlagConverter {
@Autowired
private FeatureFlagService featureFlagService;
public FeatureFlagDisplay fromAFeatureFlag(AFeatureFlag featureFlag) {
FeatureEnum featureEnum = featureFlagService.getFeatureEnum(featureFlag.getKey());
FeatureDisplay forFeature = featureFlagService.getFeatureDisplayforFeature(featureEnum);
return FeatureFlagDisplay
.builder()
.featureDisplay(forFeature)
.featureFlag(featureFlag)
.build();
}
public List<FeatureFlagDisplay> fromFeatureFlags(List<AFeatureFlag> featureFlags) {
return featureFlags.stream().map(this::fromAFeatureFlag).collect(Collectors.toList());
}
}

View File

@@ -4,6 +4,9 @@ import dev.sheldan.abstracto.core.models.database.AFeatureFlag;
import dev.sheldan.abstracto.core.models.database.AServer;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
public interface FeatureFlagRepository extends JpaRepository<AFeatureFlag, Long> {
AFeatureFlag findByServerAndKey(AServer server, String key);
List<AFeatureFlag> findAllByServer(AServer server);
}

View File

@@ -1,11 +1,17 @@
package dev.sheldan.abstracto.core.service;
import dev.sheldan.abstracto.core.config.FeatureFlagConfig;
import dev.sheldan.abstracto.core.config.FeatureDisplay;
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.service.management.FeatureFlagManagementService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
@Component
public class FeatureFlagServiceBean implements FeatureFlagService {
@@ -13,27 +19,71 @@ public class FeatureFlagServiceBean implements FeatureFlagService {
private FeatureFlagManagementService managementService;
@Autowired
private FeatureFlagConfig featureFlagConfig;
private List<FeatureDisplay> availableFeatures;
@Override
public boolean isFeatureEnabled(String name, Long serverId) {
public boolean isFeatureEnabled(FeatureEnum name, Long serverId) {
return managementService.getFeatureFlagValue(name, serverId);
}
@Override
public void enableFeature(String name, Long serverId) {
if(!featureFlagConfig.doesFeatureExist(name)) {
throw new FeatureNotFoundException("Feature not found.", name, featureFlagConfig.getFeaturesAsList());
public void enableFeature(FeatureEnum name, Long serverId) {
if(!doesFeatureExist(name)) {
throw new FeatureNotFoundException("Feature not found.", name.getKey(), getFeaturesAsList());
}
managementService.updateFeatureFlag(name, serverId, true);
}
@Override
public void disableFeature(String name, Long serverId) {
if(!featureFlagConfig.doesFeatureExist(name)) {
throw new FeatureNotFoundException("Feature not found.", name, featureFlagConfig.getFeaturesAsList());
public void disableFeature(FeatureEnum name, Long serverId) {
if(!doesFeatureExist(name)) {
throw new FeatureNotFoundException("Feature not found.", name.getKey(), getFeaturesAsList());
}
managementService.updateFeatureFlag(name, serverId, false);
}
@Override
public List<String> getAllFeatures() {
return availableFeatures
.stream()
.map(featureDisplay -> featureDisplay.getFeature().getKey())
.collect(Collectors.toList());
}
@Override
public List<FeatureDisplay> getAllFeatureDisplays() {
return availableFeatures;
}
@Override
public FeatureDisplay getFeatureDisplayforFeature(FeatureEnum featureEnum) {
Optional<FeatureDisplay> any = getAllFeatureDisplays().stream().filter(featureDisplay -> featureDisplay.getFeature().equals(featureEnum)).findAny();
if(any.isPresent()) {
return any.get();
}
throw new AbstractoRunTimeException(String.format("Feature %s not found in configuration", featureEnum.getKey()));
}
@Override
public boolean doesFeatureExist(FeatureEnum name) {
return availableFeatures.stream().anyMatch(featureDisplay -> featureDisplay.getFeature().equals(name));
}
@Override
public List<String> getFeaturesAsList() {
return availableFeatures
.stream()
.map(featureDisplay -> featureDisplay.getFeature().getKey())
.collect(Collectors.toList());
}
@Override
public FeatureEnum getFeatureEnum(String key) {
Optional<FeatureDisplay> foundFeature = availableFeatures.stream().filter(featureDisplay -> featureDisplay.getFeature().getKey().equals(key)).findAny();
if(foundFeature.isPresent()) {
return foundFeature.get().getFeature();
}
throw new AbstractoRunTimeException(String.format("Feature %s not found.", key));
}
}

View File

@@ -1,11 +1,13 @@
package dev.sheldan.abstracto.core.service.management;
import dev.sheldan.abstracto.core.config.FeatureEnum;
import dev.sheldan.abstracto.core.models.database.AFeatureFlag;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.repository.FeatureFlagRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Optional;
@Component
@@ -18,30 +20,30 @@ public class FeatureFlagManagementServiceBean implements FeatureFlagManagementSe
private ServerManagementService serverManagementService;
@Override
public void createFeatureFlag(String key, Long serverId, Boolean newValue) {
public void createFeatureFlag(FeatureEnum key, Long serverId, Boolean newValue) {
AServer server = serverManagementService.loadOrCreate(serverId);
createFeatureFlag(key, server, newValue);
}
@Override
public void createFeatureFlag(String key, AServer server, Boolean newValue) {
public void createFeatureFlag(FeatureEnum key, AServer server, Boolean newValue) {
AFeatureFlag featureFlag = AFeatureFlag
.builder()
.enabled(newValue)
.key(key)
.key(key.getKey())
.server(server)
.build();
repository.save(featureFlag);
}
@Override
public boolean getFeatureFlagValue(String key, Long serverId) {
public boolean getFeatureFlagValue(FeatureEnum key, Long serverId) {
Optional<AFeatureFlag> featureFlag = getFeatureFlag(key, serverId);
return featureFlag.isPresent() && featureFlag.get().isEnabled();
}
@Override
public void updateFeatureFlag(String key, Long serverId, Boolean newValue) {
public void updateFeatureFlag(FeatureEnum key, Long serverId, Boolean newValue) {
Optional<AFeatureFlag> existing = getFeatureFlag(key, serverId);
if(existing.isPresent()) {
AFeatureFlag flag = existing.get();
@@ -51,8 +53,13 @@ public class FeatureFlagManagementServiceBean implements FeatureFlagManagementSe
}
@Override
public Optional<AFeatureFlag> getFeatureFlag(String key, Long serverId) {
public Optional<AFeatureFlag> getFeatureFlag(FeatureEnum key, Long serverId) {
AServer server = serverManagementService.loadOrCreate(serverId);
return Optional.ofNullable(repository.findByServerAndKey(server, key));
return Optional.ofNullable(repository.findByServerAndKey(server, key.getKey()));
}
@Override
public List<AFeatureFlag> getFeatureFlagsOfServer(AServer server) {
return repository.findAllByServer(server);
}
}

View File

@@ -0,0 +1 @@
The following features are available: ${features?join(", ")}

View File

@@ -0,0 +1 @@
The following features are available: ${features?join(", ")}

View File

@@ -0,0 +1,15 @@
{
"title": {
"title": "Currently available features"
},
"color" : {
"r": 200,
"g": 0,
"b": 255
},
"description": "
<#list features as feature>
${feature.featureFlag.enabled?string('✅', '❌')} **<#include "${feature.featureDisplay.feature.key}_feature">** Key: `${feature.featureDisplay.feature.key}`
</#list>
"
}

View File

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