mirror of
https://github.com/Sheldan/abstracto.git
synced 2026-01-06 17:29:50 +00:00
[AB-307] enabling dependent features and including them into the setup wizard
this comes with the usual limitation, that dependent features must be referenced via importing
This commit is contained in:
@@ -13,18 +13,22 @@ import dev.sheldan.abstracto.core.commands.config.ConfigModuleDefinition;
|
||||
import dev.sheldan.abstracto.core.config.FeatureConfig;
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||
import dev.sheldan.abstracto.core.models.template.commands.EnableModel;
|
||||
import dev.sheldan.abstracto.core.models.template.commands.FeatureSwitchModel;
|
||||
import dev.sheldan.abstracto.core.service.ChannelService;
|
||||
import dev.sheldan.abstracto.core.service.FeatureConfigService;
|
||||
import dev.sheldan.abstracto.core.service.FeatureFlagService;
|
||||
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
|
||||
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
|
||||
import dev.sheldan.abstracto.core.templating.service.TemplateService;
|
||||
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
public class DisableFeature extends AbstractConditionableCommand {
|
||||
@@ -43,26 +47,46 @@ public class DisableFeature extends AbstractConditionableCommand {
|
||||
|
||||
@Autowired
|
||||
private ServerManagementService serverManagementService;
|
||||
private static final String DISABLE_FEATURE_ALL_FEATURES_RESPONSE_TEMPLATE_KEY = "disableFeature_all_features_response";
|
||||
private static final String DISABLE_FEATURE_DEPENDENCIES_RESPONSE_TEMPLATE_KEY = "disableFeature_feature_dependencies_response";
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
|
||||
if(commandContext.getParameters().getParameters().isEmpty()) {
|
||||
EnableModel model = (EnableModel) ContextConverter.fromCommandContext(commandContext, EnableModel.class);
|
||||
if (commandContext.getParameters().getParameters().isEmpty()) {
|
||||
FeatureSwitchModel model = (FeatureSwitchModel) ContextConverter.fromCommandContext(commandContext, FeatureSwitchModel.class);
|
||||
model.setFeatures(featureConfigService.getAllFeatures());
|
||||
String response = templateService.renderTemplate("disable_features_response", model, commandContext.getGuild().getIdLong());
|
||||
return channelService.sendTextToChannel(response, commandContext.getChannel())
|
||||
.thenApply(aVoid -> CommandResult.fromSuccess());
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(DISABLE_FEATURE_ALL_FEATURES_RESPONSE_TEMPLATE_KEY, model, commandContext.getGuild().getIdLong());
|
||||
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
|
||||
.thenApply(message -> CommandResult.fromIgnored());
|
||||
} else {
|
||||
String flagKey = (String) commandContext.getParameters().getParameters().get(0);
|
||||
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(flagKey);
|
||||
featureFlagService.disableFeature(feature, commandContext.getGuild().getIdLong());
|
||||
if(feature.getDependantFeatures() != null) {
|
||||
List<FeatureConfig> featureDependencies = new ArrayList<>();
|
||||
if (feature.getDependantFeatures() != null) {
|
||||
AServer server = serverManagementService.loadServer(commandContext.getGuild());
|
||||
feature.getDependantFeatures().forEach(featureDisplay ->
|
||||
featureFlagService.disableFeature(featureDisplay, server)
|
||||
feature.getDependantFeatures().forEach(featureDisplay -> {
|
||||
if (featureFlagService.isFeatureEnabled(featureDisplay, server)) {
|
||||
featureFlagService.disableFeature(featureDisplay, server);
|
||||
featureDependencies.add(featureDisplay);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
return CompletableFuture.completedFuture(CommandResult.fromSuccess());
|
||||
if (featureDependencies.isEmpty()) {
|
||||
return CompletableFuture.completedFuture(CommandResult.fromSuccess());
|
||||
} else {
|
||||
List<String> additionalFeatures = featureDependencies
|
||||
.stream()
|
||||
.map(featureDef -> featureDef.getFeature().getKey()).
|
||||
collect(Collectors.toList());
|
||||
FeatureSwitchModel model = (FeatureSwitchModel) ContextConverter.fromCommandContext(commandContext, FeatureSwitchModel.class);
|
||||
model.setFeatures(additionalFeatures);
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(DISABLE_FEATURE_DEPENDENCIES_RESPONSE_TEMPLATE_KEY,
|
||||
model, commandContext.getGuild().getIdLong());
|
||||
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
|
||||
.thenApply(message -> CommandResult.fromIgnored());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,19 +14,23 @@ import dev.sheldan.abstracto.core.config.FeatureConfig;
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.models.FeatureValidationResult;
|
||||
import dev.sheldan.abstracto.core.models.database.AServer;
|
||||
import dev.sheldan.abstracto.core.models.template.commands.EnableModel;
|
||||
import dev.sheldan.abstracto.core.models.template.commands.FeatureSwitchModel;
|
||||
import dev.sheldan.abstracto.core.service.ChannelService;
|
||||
import dev.sheldan.abstracto.core.service.FeatureConfigService;
|
||||
import dev.sheldan.abstracto.core.service.FeatureFlagService;
|
||||
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
|
||||
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
|
||||
import dev.sheldan.abstracto.core.templating.service.TemplateService;
|
||||
import dev.sheldan.abstracto.core.utils.FutureUtils;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Component
|
||||
@Slf4j
|
||||
@@ -47,32 +51,52 @@ public class EnableFeature extends AbstractConditionableCommand {
|
||||
@Autowired
|
||||
private ServerManagementService serverManagementService;
|
||||
|
||||
private static final String ENABLE_FEATURE_DEPENDENCIES_RESPONSE_TEMPLATE_KEY = "enableFeature_feature_dependencies_response";
|
||||
private static final String ENABLE_FEATURE_ALL_FEATURES_RESPONSE = "enableFeature_all_features_response";
|
||||
|
||||
@Override
|
||||
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
|
||||
if(commandContext.getParameters().getParameters().isEmpty()) {
|
||||
EnableModel model = (EnableModel) ContextConverter.fromCommandContext(commandContext, EnableModel.class);
|
||||
FeatureSwitchModel model = (FeatureSwitchModel) ContextConverter.fromCommandContext(commandContext, FeatureSwitchModel.class);
|
||||
model.setFeatures(featureConfigService.getAllFeatures());
|
||||
String response = templateService.renderTemplate("enable_features_response", model, commandContext.getGuild().getIdLong());
|
||||
return channelService.sendTextToChannel(response, commandContext.getChannel())
|
||||
.thenApply(message -> CommandResult.fromSuccess());
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(ENABLE_FEATURE_ALL_FEATURES_RESPONSE, model, commandContext.getGuild().getIdLong());
|
||||
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
|
||||
.thenApply(message -> CommandResult.fromIgnored());
|
||||
} else {
|
||||
String flagKey = (String) commandContext.getParameters().getParameters().get(0);
|
||||
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(flagKey);
|
||||
String featureKey = (String) commandContext.getParameters().getParameters().get(0);
|
||||
FeatureConfig feature = featureConfigService.getFeatureDisplayForFeature(featureKey);
|
||||
AServer server = serverManagementService.loadServer(commandContext.getUserInitiatedContext().getGuild().getIdLong());
|
||||
FeatureValidationResult featureSetup = featureConfigService.validateFeatureSetup(feature, server);
|
||||
if(Boolean.FALSE.equals(featureSetup.getValidationResult())) {
|
||||
log.info("Feature {} has failed the setup validation. Notifying user.", flagKey);
|
||||
log.info("Feature {} has failed the setup validation. Notifying user.", featureKey);
|
||||
channelService.sendTextToChannelNotAsync(templateService.renderTemplatable(featureSetup, commandContext.getGuild().getIdLong()),
|
||||
commandContext.getChannel());
|
||||
}
|
||||
featureFlagService.enableFeature(feature, server);
|
||||
List<FeatureConfig> featureDependencies = new ArrayList<>();
|
||||
if(feature.getRequiredFeatures() != null) {
|
||||
feature.getRequiredFeatures().forEach(featureDisplay -> {
|
||||
log.info("Also enabling required feature {}.", featureDisplay.getFeature().getKey());
|
||||
featureFlagService.enableFeature(featureDisplay, server);
|
||||
if(!featureFlagService.isFeatureEnabled(featureDisplay, server)) {
|
||||
featureFlagService.enableFeature(featureDisplay, server);
|
||||
featureDependencies.add(featureDisplay);
|
||||
}
|
||||
});
|
||||
}
|
||||
return CompletableFuture.completedFuture(CommandResult.fromSuccess());
|
||||
if(featureDependencies.isEmpty()) {
|
||||
return CompletableFuture.completedFuture(CommandResult.fromSuccess());
|
||||
} else {
|
||||
List<String> additionalFeatures = featureDependencies
|
||||
.stream()
|
||||
.map(featureDef -> featureDef.getFeature().getKey()).
|
||||
collect(Collectors.toList());
|
||||
FeatureSwitchModel model = (FeatureSwitchModel) ContextConverter.fromCommandContext(commandContext, FeatureSwitchModel.class);
|
||||
model.setFeatures(additionalFeatures);
|
||||
MessageToSend messageToSend = templateService.renderEmbedTemplate(ENABLE_FEATURE_DEPENDENCIES_RESPONSE_TEMPLATE_KEY,
|
||||
model, commandContext.getGuild().getIdLong());
|
||||
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
|
||||
.thenApply(message -> CommandResult.fromIgnored());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package dev.sheldan.abstracto.core.commands.config;
|
||||
package dev.sheldan.abstracto.core.commands.config.features;
|
||||
|
||||
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
|
||||
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
|
||||
@@ -8,6 +8,7 @@ import dev.sheldan.abstracto.core.command.config.features.CoreFeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandContext;
|
||||
import dev.sheldan.abstracto.core.command.execution.CommandResult;
|
||||
import dev.sheldan.abstracto.core.command.service.management.FeatureManagementService;
|
||||
import dev.sheldan.abstracto.core.commands.config.ConfigModuleDefinition;
|
||||
import dev.sheldan.abstracto.core.config.FeatureConfig;
|
||||
import dev.sheldan.abstracto.core.config.FeatureDefinition;
|
||||
import dev.sheldan.abstracto.core.exception.FeatureNotFoundException;
|
||||
@@ -2,6 +2,7 @@ package dev.sheldan.abstracto.core.service;
|
||||
|
||||
import dev.sheldan.abstracto.core.command.service.ExceptionService;
|
||||
import dev.sheldan.abstracto.core.config.FeatureConfig;
|
||||
import dev.sheldan.abstracto.core.config.PostTargetEnum;
|
||||
import dev.sheldan.abstracto.core.exception.ChannelNotInGuildException;
|
||||
import dev.sheldan.abstracto.core.interactive.*;
|
||||
import dev.sheldan.abstracto.core.models.AServerChannelUserId;
|
||||
@@ -14,9 +15,7 @@ import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
@Component
|
||||
@@ -61,8 +60,13 @@ public class FeatureSetupServiceBean implements FeatureSetupService {
|
||||
log.info("Performing setup of feature {} for user {} in channel {} in server {}.",
|
||||
featureConfig.getFeature().getKey(), user.getUserId(), user.getChannelId(), user.getGuildId());
|
||||
Optional<TextChannel> textChannelInGuild = channelService.getTextChannelFromServerOptional(user.getGuildId(), user.getChannelId());
|
||||
if(textChannelInGuild.isPresent()) {
|
||||
List<String> requiredSystemConfigKeys = featureConfig.getRequiredSystemConfigKeys();
|
||||
if (textChannelInGuild.isPresent()) {
|
||||
Set<String> requiredSystemConfigKeys = new HashSet<>();
|
||||
Set<PostTargetEnum> requiredPostTargets = new HashSet<>();
|
||||
Set<SetupStep> customSetupSteps = new HashSet<>();
|
||||
|
||||
collectRequiredFeatureSteps(featureConfig, requiredSystemConfigKeys, requiredPostTargets, customSetupSteps, new HashSet<>());
|
||||
|
||||
List<SetupExecution> steps = new ArrayList<>();
|
||||
requiredSystemConfigKeys.forEach(s -> {
|
||||
log.debug("Feature requires system config key {}.", s);
|
||||
@@ -73,7 +77,7 @@ public class FeatureSetupServiceBean implements FeatureSetupService {
|
||||
.build();
|
||||
steps.add(execution);
|
||||
});
|
||||
featureConfig.getRequiredPostTargets().forEach(postTargetEnum -> {
|
||||
requiredPostTargets.forEach(postTargetEnum -> {
|
||||
log.debug("Feature requires post target {}.", postTargetEnum.getKey());
|
||||
SetupExecution execution = SetupExecution
|
||||
.builder()
|
||||
@@ -82,7 +86,7 @@ public class FeatureSetupServiceBean implements FeatureSetupService {
|
||||
.build();
|
||||
steps.add(execution);
|
||||
});
|
||||
featureConfig.getCustomSetupSteps().forEach(setupStep -> {
|
||||
customSetupSteps.forEach(setupStep -> {
|
||||
log.debug("Feature requires custom setup step {}.", setupStep.getClass().getName());
|
||||
SetupExecution execution = SetupExecution
|
||||
.builder()
|
||||
@@ -94,7 +98,7 @@ public class FeatureSetupServiceBean implements FeatureSetupService {
|
||||
for (int i = 0; i < steps.size(); i++) {
|
||||
SetupExecution setupExecution = steps.get(i);
|
||||
setupExecution.getParameter().setPreviousMessageId(initialMessageId);
|
||||
if(i < steps.size() - 1) {
|
||||
if (i < steps.size() - 1) {
|
||||
setupExecution.setNextStep(steps.get(i + 1));
|
||||
}
|
||||
}
|
||||
@@ -117,7 +121,7 @@ public class FeatureSetupServiceBean implements FeatureSetupService {
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> executeFeatureSetup(FeatureConfig featureConfig, List<SetupExecution> steps, AServerChannelUserId user, List<DelayedActionConfig> delayedActionConfigs) {
|
||||
if(!steps.isEmpty()) {
|
||||
if (!steps.isEmpty()) {
|
||||
SetupExecution nextStep = steps.get(0);
|
||||
return executeStep(user, nextStep, delayedActionConfigs, featureConfig);
|
||||
} else {
|
||||
@@ -130,10 +134,10 @@ public class FeatureSetupServiceBean implements FeatureSetupService {
|
||||
private CompletableFuture<Void> executeStep(AServerChannelUserId aUserInAServer, SetupExecution execution, List<DelayedActionConfig> delayedActionConfigs, FeatureConfig featureConfig) {
|
||||
log.debug("Executing step {} in server {} in channel {} for user {}.", execution.getStep().getClass(), aUserInAServer.getGuildId(), aUserInAServer.getChannelId(), aUserInAServer.getUserId());
|
||||
return execution.getStep().execute(aUserInAServer, execution.getParameter()).thenAccept(setpResult -> {
|
||||
if(setpResult.getResult().equals(SetupStepResultType.SUCCESS)) {
|
||||
if (setpResult.getResult().equals(SetupStepResultType.SUCCESS)) {
|
||||
log.info("Step {} in server {} has been executed successfully. Proceeding.", execution.getStep().getClass(), aUserInAServer.getGuildId());
|
||||
delayedActionConfigs.addAll(setpResult.getDelayedActionConfigList());
|
||||
if(execution.getNextStep() != null) {
|
||||
if (execution.getNextStep() != null) {
|
||||
log.debug("Executing next step {}.", execution.getNextStep().getStep().getClass());
|
||||
executeStep(aUserInAServer, execution.getNextStep(), delayedActionConfigs, featureConfig);
|
||||
} else {
|
||||
@@ -156,7 +160,7 @@ public class FeatureSetupServiceBean implements FeatureSetupService {
|
||||
public void showExceptionMessage(Throwable throwable, AServerChannelUserId aServerChannelUserId) {
|
||||
Optional<TextChannel> channelOptional = channelService.getTextChannelFromServerOptional(aServerChannelUserId.getGuildId(), aServerChannelUserId.getChannelId());
|
||||
memberService.getMemberInServerAsync(aServerChannelUserId.getGuildId(), aServerChannelUserId.getUserId()).thenAccept(member ->
|
||||
channelOptional.ifPresent(textChannel -> exceptionService.reportExceptionToChannel(throwable, textChannel, member))
|
||||
channelOptional.ifPresent(textChannel -> exceptionService.reportExceptionToChannel(throwable, textChannel, member))
|
||||
).exceptionally(innserThrowable -> {
|
||||
log.error("Failed to report exception message for exception {} for user {} in channel {} in server {}.", throwable, aServerChannelUserId.getUserId(), aServerChannelUserId.getChannelId(), aServerChannelUserId.getGuildId(), innserThrowable);
|
||||
return null;
|
||||
@@ -178,7 +182,7 @@ public class FeatureSetupServiceBean implements FeatureSetupService {
|
||||
log.debug("Notifying user {} in channel {} in server {} about completion of setup for feature {}.",
|
||||
aServerChannelUserId.getUserId(), aServerChannelUserId.getChannelId(), aServerChannelUserId.getGuildId(), featureConfig.getFeature().getKey());
|
||||
String templateKey;
|
||||
if(result.getResult().equals(SetupStepResultType.CANCELLED)) {
|
||||
if (result.getResult().equals(SetupStepResultType.CANCELLED)) {
|
||||
templateKey = FEATURE_SETUP_CANCELLATION_NOTIFICATION_TEMPLATE;
|
||||
} else {
|
||||
templateKey = FEATURE_SETUP_COMPLETION_NOTIFICATION_TEMPLATE;
|
||||
@@ -202,4 +206,19 @@ public class FeatureSetupServiceBean implements FeatureSetupService {
|
||||
aServerChannelUserId.getUserId(), aServerChannelUserId.getChannelId(), aServerChannelUserId.getGuildId(), featureConfig.getFeature().getKey());
|
||||
notifyUserWithTemplate(aServerChannelUserId, featureConfig, FEATURE_SETUP_CANCELLATION_NOTIFICATION_TEMPLATE);
|
||||
}
|
||||
|
||||
private void collectRequiredFeatureSteps(FeatureConfig featureConfig, Set<String> requiredSystemConfigKeys,
|
||||
Set<PostTargetEnum> requiredPostTargets, Set<SetupStep> customSetupSteps,
|
||||
Set<String> coveredFeatures) {
|
||||
if (coveredFeatures.contains(featureConfig.getFeature().getKey())) {
|
||||
return;
|
||||
}
|
||||
coveredFeatures.add(featureConfig.getFeature().getKey());
|
||||
requiredSystemConfigKeys.addAll(featureConfig.getRequiredSystemConfigKeys());
|
||||
requiredPostTargets.addAll(featureConfig.getRequiredPostTargets());
|
||||
customSetupSteps.addAll(featureConfig.getCustomSetupSteps());
|
||||
featureConfig.getRequiredFeatures()
|
||||
.forEach(requiredFeature -> collectRequiredFeatureSteps(requiredFeature, requiredSystemConfigKeys, requiredPostTargets, customSetupSteps, coveredFeatures));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,6 +10,6 @@ import java.util.List;
|
||||
@Getter
|
||||
@Setter
|
||||
@SuperBuilder
|
||||
public class EnableModel extends UserInitiatedServerContext {
|
||||
public class FeatureSwitchModel extends UserInitiatedServerContext {
|
||||
private List<String> features;
|
||||
}
|
||||
Reference in New Issue
Block a user