diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java b/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java index 4190bfb5..33fa38b3 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/api/API.java @@ -27,6 +27,7 @@ public final class API { public static final String PARAM_PARENT_MODEL_ID = "parentModelId"; public static final String PARAM_ENTITY_TYPE = "entityType"; public static final String PARAM_BULK_ACTION_TYPE = "bulkActionType"; + public static final String PARAM_BULK_ACTION_INCLUDES = "bulkActionIncludes"; public static final String PARAM_VIEW_ID = "viewId"; public static final String PARAM_INSTRUCTION_TYPE = "instructionType"; public static final String PARAM_INSTRUCTION_ATTRIBUTES = "instructionAttributes"; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionService.java index fa6dd40a..57e063fa 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/BulkActionService.java @@ -28,6 +28,8 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl.BulkAction; * / | \ \ * LMS Setup | User-Account Client Configuration * | | + * | ______________+______________/ + * |/ |/ * Exam Exam Configuration * |\ / * | Exam Config Mapping diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/BulkAction.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/BulkAction.java index 9c567acd..e00dd0b1 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/BulkAction.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/BulkAction.java @@ -11,6 +11,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.EnumSet; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.Set; @@ -35,6 +36,9 @@ public final class BulkAction { public final EntityType sourceType; /** A Set of EntityKey defining all source-entities of the BulkAction */ public final Set sources; + /** A Set defining the types of dependencies to include into the bulk action + * Null means all dependencies are included (ignore) and empty means no dependencies are included */ + public final EnumSet includeDependencies; /** A Set of EntityKey containing collected depending entities during dependency collection and processing phase */ final Set dependencies; /** A Set of EntityKey containing collected bulk action processing results during processing phase */ @@ -46,10 +50,19 @@ public final class BulkAction { final BulkActionType type, final EntityType sourceType, final Collection sources) { + this(type, sourceType, sources, null); + } + + public BulkAction( + final BulkActionType type, + final EntityType sourceType, + final Collection sources, + final EnumSet includeDependencies) { this.type = type; this.sourceType = sourceType; this.sources = Utils.immutableSetOf(sources); + this.includeDependencies = includeDependencies; this.dependencies = new LinkedHashSet<>(); this.result = new HashSet<>(); @@ -64,6 +77,10 @@ public final class BulkAction { this(type, sourceType, (sources != null) ? Arrays.asList(sources) : Collections.emptyList()); } + public boolean includesDependencyType(final EntityType type) { + return this.includeDependencies == null || this.includeDependencies.contains(type); + } + public Set getDependencies() { return Collections.unmodifiableSet(this.dependencies); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/BulkActionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/BulkActionServiceImpl.java index 9e3fcca9..a05ee986 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/BulkActionServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/bulkaction/impl/BulkActionServiceImpl.java @@ -11,6 +11,8 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.EnumMap; +import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -37,6 +39,9 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO; @WebServiceProfile public class BulkActionServiceImpl implements BulkActionService { + private final EnumMap> directDependancyMap = + new EnumMap<>(EntityType.class); + private final Map> supporter; private final UserActivityLogDAO userActivityLogDAO; private final ApplicationEventPublisher applicationEventPublisher; @@ -55,6 +60,20 @@ public class BulkActionServiceImpl implements BulkActionService { this.userActivityLogDAO = userActivityLogDAO; this.applicationEventPublisher = applicationEventPublisher; this.jsonMapper = jsonMapper; + + this.directDependancyMap.put(EntityType.INSTITUTION, EnumSet.of( + EntityType.LMS_SETUP, + EntityType.SEB_CLIENT_CONFIGURATION, + EntityType.CONFIGURATION_NODE, + EntityType.USER)); + this.directDependancyMap.put(EntityType.LMS_SETUP, EnumSet.of( + EntityType.EXAM)); + this.directDependancyMap.put(EntityType.EXAM, EnumSet.of( + EntityType.EXAM_CONFIGURATION_MAP, + EntityType.INDICATOR, + EntityType.CLIENT_CONNECTION)); + this.directDependancyMap.put(EntityType.CONFIGURATION_NODE, + EnumSet.noneOf(EntityType.class)); } @Override @@ -172,6 +191,8 @@ public class BulkActionServiceImpl implements BulkActionService { return dependantSupporterInHierarchicalOrder .stream() .filter(Objects::nonNull) + .filter(dao -> action.includeDependencies == null || + action.includeDependencies.contains(dao.entityType())) .collect(Collectors.toList()); } default: @@ -191,13 +212,13 @@ public class BulkActionServiceImpl implements BulkActionService { this.supporter.get(EntityType.EXAM_CONFIGURATION_MAP), this.supporter.get(EntityType.CLIENT_CONNECTION), this.supporter.get(EntityType.CONFIGURATION_NODE)); -// case USER: -// return Arrays.asList( -// this.supporter.get(EntityType.EXAM_CONFIGURATION_MAP), -// this.supporter.get(EntityType.EXAM), -// this.supporter.get(EntityType.INDICATOR), -// this.supporter.get(EntityType.CLIENT_CONNECTION), -// this.supporter.get(EntityType.CONFIGURATION_NODE)); + case USER: + return Arrays.asList( + this.supporter.get(EntityType.EXAM), + this.supporter.get(EntityType.INDICATOR), + this.supporter.get(EntityType.CLIENT_CONNECTION), + this.supporter.get(EntityType.CONFIGURATION_NODE), + this.supporter.get(EntityType.EXAM_CONFIGURATION_MAP)); case LMS_SETUP: return Arrays.asList( this.supporter.get(EntityType.EXAM), @@ -218,6 +239,15 @@ public class BulkActionServiceImpl implements BulkActionService { } private void checkProcessing(final BulkAction action) { + // complete this.directDependancyMap if needed + if (action.includeDependencies != null) { + this.directDependancyMap.entrySet().stream() + .forEach(entry -> { + if (action.includeDependencies.contains(entry.getKey())) { + action.includeDependencies.addAll(entry.getValue()); + } + }); + } if (action.alreadyProcessed) { throw new IllegalStateException("Given BulkAction has already been processed. Use a new one"); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ClientConnectionDAO.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ClientConnectionDAO.java index 48b68d23..5e3a2223 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ClientConnectionDAO.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/ClientConnectionDAO.java @@ -17,8 +17,11 @@ import org.springframework.cache.annotation.Cacheable; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; import ch.ethz.seb.sebserver.gbl.util.Result; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionSupportDAO; -public interface ClientConnectionDAO extends EntityDAO { +public interface ClientConnectionDAO extends + EntityDAO, + BulkActionSupportDAO { String CONNECTION_TOKENS_CACHE = "CONNECTION_TOKENS_CACHE"; diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientConnectionDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientConnectionDAOImpl.java index 77977c12..6718e1a6 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientConnectionDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ClientConnectionDAOImpl.java @@ -8,13 +8,14 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl; -import static org.mybatis.dynamic.sql.SqlBuilder.isEqualToWhenPresent; -import static org.mybatis.dynamic.sql.SqlBuilder.isIn; +import static org.mybatis.dynamic.sql.SqlBuilder.*; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Set; +import java.util.function.Function; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -23,6 +24,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; +import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; @@ -34,7 +36,11 @@ import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientConnectionR import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientConnectionRecordMapper; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientEventRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientEventRecordMapper; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientInstructionRecordDynamicSqlSupport; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientInstructionRecordMapper; +import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamRecordDynamicSqlSupport; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientConnectionRecord; +import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl.BulkAction; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.DAOLoggingSupport; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap; @@ -49,13 +55,16 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { private final ClientConnectionRecordMapper clientConnectionRecordMapper; private final ClientEventRecordMapper clientEventRecordMapper; + private final ClientInstructionRecordMapper clientInstructionRecordMapper; protected ClientConnectionDAOImpl( final ClientConnectionRecordMapper clientConnectionRecordMapper, - final ClientEventRecordMapper clientEventRecordMapper) { + final ClientEventRecordMapper clientEventRecordMapper, + final ClientInstructionRecordMapper clientInstructionRecordMapper) { this.clientConnectionRecordMapper = clientConnectionRecordMapper; this.clientEventRecordMapper = clientEventRecordMapper; + this.clientInstructionRecordMapper = clientInstructionRecordMapper; } @Override @@ -171,6 +180,39 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { .onError(TransactionHandler::rollback); } + @Override + public Set getDependencies(final BulkAction bulkAction) { + // only for deletion + if (bulkAction.type == BulkActionType.ACTIVATE || bulkAction.type == BulkActionType.DEACTIVATE) { + return Collections.emptySet(); + } + // only if included + if (!bulkAction.includesDependencyType(EntityType.CLIENT_CONNECTION)) { + return Collections.emptySet(); + } + + // define the select function in case of source type + Function>> selectionFunction; + switch (bulkAction.sourceType) { + case INSTITUTION: + selectionFunction = this::allIdsOfInstitution; + break; + case LMS_SETUP: + selectionFunction = this::allIdsOfLmsSetup; + case USER: + selectionFunction = this::allIdsOfUser; + break; + case EXAM: + selectionFunction = this::allIdsOfExam; + break; + default: + selectionFunction = key -> Result.of(Collections.emptyList()); //empty select function + break; + } + + return getDependencies(bulkAction, selectionFunction); + } + @Override @Transactional public Result> delete(final Set all) { @@ -186,6 +228,23 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { .build() .execute(); + // then delete all related client instructions + final List connectionTokens = this.clientConnectionRecordMapper.selectByExample() + .where( + ClientConnectionRecordDynamicSqlSupport.id, + SqlBuilder.isIn(ids)) + .build() + .execute() + .stream() + .map(r -> r.getConnectionToken()) + .collect(Collectors.toList()); + this.clientInstructionRecordMapper.deleteByExample() + .where( + ClientInstructionRecordDynamicSqlSupport.connectionToken, + SqlBuilder.isIn(connectionTokens)) + .build() + .execute(); + // then delete all requested client-connections this.clientConnectionRecordMapper.deleteByExample() .where( @@ -256,7 +315,62 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { record.getVirtualClientAddress(), record.getCreationTime()); }); + } + private Result> allIdsOfInstitution(final EntityKey institutionKey) { + return Result.tryCatch(() -> this.clientConnectionRecordMapper.selectIdsByExample() + .where( + ClientConnectionRecordDynamicSqlSupport.institutionId, + isEqualTo(Long.parseLong(institutionKey.modelId))) + .build() + .execute() + .stream() + .map(id -> new EntityKey(id, EntityType.EXAM)) + .collect(Collectors.toList())); + } + + private Result> allIdsOfLmsSetup(final EntityKey lmsSetupKey) { + return Result.tryCatch(() -> this.clientConnectionRecordMapper.selectIdsByExample() + .leftJoin(ExamRecordDynamicSqlSupport.examRecord) + .on( + ExamRecordDynamicSqlSupport.id, + equalTo(ClientConnectionRecordDynamicSqlSupport.examId)) + .where( + ExamRecordDynamicSqlSupport.lmsSetupId, + isEqualTo(Long.parseLong(lmsSetupKey.modelId))) + .build() + .execute() + .stream() + .map(id -> new EntityKey(id, EntityType.EXAM)) + .collect(Collectors.toList())); + } + + private Result> allIdsOfUser(final EntityKey userKey) { + return Result.tryCatch(() -> this.clientConnectionRecordMapper.selectIdsByExample() + .leftJoin(ExamRecordDynamicSqlSupport.examRecord) + .on( + ExamRecordDynamicSqlSupport.id, + equalTo(ClientConnectionRecordDynamicSqlSupport.examId)) + .where( + ExamRecordDynamicSqlSupport.owner, + isEqualTo(userKey.modelId)) + .build() + .execute() + .stream() + .map(id -> new EntityKey(id, EntityType.EXAM)) + .collect(Collectors.toList())); + } + + private Result> allIdsOfExam(final EntityKey examKey) { + return Result.tryCatch(() -> this.clientConnectionRecordMapper.selectIdsByExample() + .where( + ClientConnectionRecordDynamicSqlSupport.examId, + isEqualTo(Long.parseLong(examKey.modelId))) + .build() + .execute() + .stream() + .map(id -> new EntityKey(id, EntityType.EXAM)) + .collect(Collectors.toList())); } } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationNodeDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationNodeDAOImpl.java index 2abc4eee..5ea06d07 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationNodeDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ConfigurationNodeDAOImpl.java @@ -24,6 +24,7 @@ import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; +import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType; import ch.ethz.seb.sebserver.gbl.api.APIMessage.FieldValidationException; import ch.ethz.seb.sebserver.gbl.api.EntityType; import ch.ethz.seb.sebserver.gbl.model.EntityKey; @@ -134,11 +135,24 @@ public class ConfigurationNodeDAOImpl implements ConfigurationNodeDAO { @Override @Transactional(readOnly = true) public Set getDependencies(final BulkAction bulkAction) { + // only if included + if (!bulkAction.includesDependencyType(EntityType.CONFIGURATION_NODE)) { + return Collections.emptySet(); + } + // define the select function in case of source type - final Function>> selectionFunction = - (bulkAction.sourceType == EntityType.INSTITUTION) - ? this::allIdsOfInstitution - : key -> Result.of(Collections.emptyList()); // else : empty select function + Function>> selectionFunction = + key -> Result.of(Collections.emptyList()); + + if (bulkAction.sourceType == EntityType.INSTITUTION) { + selectionFunction = this::allIdsOfInstitution; + } + + // in case of user deletion with configuration dependency inclusion + if (bulkAction.sourceType == EntityType.USER && + bulkAction.type == BulkActionType.HARD_DELETE) { + selectionFunction = this::allIdsOfUser; + } return getDependencies(bulkAction, selectionFunction); } @@ -252,6 +266,18 @@ public class ConfigurationNodeDAOImpl implements ConfigurationNodeDAO { .collect(Collectors.toList())); } + private Result> allIdsOfUser(final EntityKey userKey) { + return Result.tryCatch(() -> this.configurationNodeRecordMapper.selectIdsByExample() + .where( + ConfigurationNodeRecordDynamicSqlSupport.owner, + isEqualTo(userKey.modelId)) + .build() + .execute() + .stream() + .map(id -> new EntityKey(id, EntityType.CONFIGURATION_NODE)) + .collect(Collectors.toList())); + } + private Result recordById(final Long id) { return Result.tryCatch(() -> { final ConfigurationNodeRecord record = this.configurationNodeRecordMapper diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java index fdd11fdd..845ce29c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamConfigurationMapDAOImpl.java @@ -28,8 +28,8 @@ import org.springframework.transaction.annotation.Transactional; import ch.ethz.seb.sebserver.gbl.api.API.BulkActionType; import ch.ethz.seb.sebserver.gbl.api.APIMessage.APIMessageException; import ch.ethz.seb.sebserver.gbl.api.APIMessage.ErrorMessage; -import ch.ethz.seb.sebserver.gbl.client.ClientCredentialService; import ch.ethz.seb.sebserver.gbl.api.EntityType; +import ch.ethz.seb.sebserver.gbl.client.ClientCredentialService; import ch.ethz.seb.sebserver.gbl.model.EntityKey; import ch.ethz.seb.sebserver.gbl.model.exam.Exam; import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType; @@ -281,9 +281,14 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO { @Override @Transactional(readOnly = true) public Set getDependencies(final BulkAction bulkAction) { + // only deletion here if (bulkAction.type == BulkActionType.ACTIVATE || bulkAction.type == BulkActionType.DEACTIVATE) { return Collections.emptySet(); } + // only if included + if (!bulkAction.includesDependencyType(EntityType.EXAM_CONFIGURATION_MAP)) { + return Collections.emptySet(); + } // define the select function in case of source type Function>> selectionFunction; @@ -291,6 +296,9 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO { case INSTITUTION: selectionFunction = this::allIdsOfInstitution; break; + case USER: + selectionFunction = this::allIdsOfUser; + break; case LMS_SETUP: selectionFunction = this::allIdsOfLmsSetup; break; @@ -424,6 +432,30 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO { .collect(Collectors.toList())); } + private Result> allIdsOfUser(final EntityKey userKey) { + return Result.tryCatch(() -> { + final List examsIds = this.examRecordMapper.selectByExample() + .where( + ExamRecordDynamicSqlSupport.owner, + isEqualTo(userKey.modelId)) + .build() + .execute() + .stream() + .map(r -> r.getId()) + .collect(Collectors.toList()); + + return this.examConfigurationMapRecordMapper.selectIdsByExample() + .where( + ExamConfigurationMapRecordDynamicSqlSupport.examId, + isIn(examsIds)) + .build() + .execute() + .stream() + .map(id -> new EntityKey(id, EntityType.EXAM_CONFIGURATION_MAP)) + .collect(Collectors.toList()); + }); + } + private Result> allIdsOfLmsSetup(final EntityKey lmsSetupKey) { return Result.tryCatch(() -> this.examConfigurationMapRecordMapper.selectIdsByExample() .leftJoin(ExamRecordDynamicSqlSupport.examRecord) diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java index baa5308a..5f46ecd2 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java @@ -588,6 +588,10 @@ public class ExamDAOImpl implements ExamDAO { @Override @Transactional(readOnly = true) public Set getDependencies(final BulkAction bulkAction) { + // only if included + if (!bulkAction.includesDependencyType(EntityType.EXAM)) { + return Collections.emptySet(); + } // define the select function in case of source type Function>> selectionFunction; @@ -598,6 +602,9 @@ public class ExamDAOImpl implements ExamDAO { case LMS_SETUP: selectionFunction = this::allIdsOfLmsSetup; break; + case USER: + selectionFunction = this::allIdsOfUser; + break; default: selectionFunction = key -> Result.of(Collections.emptyList()); //empty select function break; @@ -651,6 +658,17 @@ public class ExamDAOImpl implements ExamDAO { .collect(Collectors.toList())); } + private Result> allIdsOfUser(final EntityKey userKey) { + return Result.tryCatch(() -> this.examRecordMapper.selectIdsByExample() + .where(ExamRecordDynamicSqlSupport.owner, + isEqualTo(userKey.modelId)) + .build() + .execute() + .stream() + .map(id -> new EntityKey(id, EntityType.EXAM)) + .collect(Collectors.toList())); + } + private Result recordById(final Long id) { return Result.tryCatch(() -> { final ExamRecord record = this.examRecordMapper.selectByPrimaryKey(id); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/IndicatorDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/IndicatorDAOImpl.java index 23948d22..1fb76712 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/IndicatorDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/IndicatorDAOImpl.java @@ -221,9 +221,14 @@ public class IndicatorDAOImpl implements IndicatorDAO { @Override @Transactional(readOnly = true) public Set getDependencies(final BulkAction bulkAction) { + // only for deletion if (bulkAction.type == BulkActionType.ACTIVATE || bulkAction.type == BulkActionType.DEACTIVATE) { return Collections.emptySet(); } + // only if included + if (!bulkAction.includesDependencyType(EntityType.INDICATOR)) { + return Collections.emptySet(); + } // define the select function in case of source type Function>> selectionFunction; @@ -233,6 +238,8 @@ public class IndicatorDAOImpl implements IndicatorDAO { break; case LMS_SETUP: selectionFunction = this::allIdsOfLmsSetup; + case USER: + selectionFunction = this::allIdsOfUser; break; case EXAM: selectionFunction = this::allIdsOfExam; @@ -277,6 +284,22 @@ public class IndicatorDAOImpl implements IndicatorDAO { .collect(Collectors.toList())); } + private Result> allIdsOfUser(final EntityKey userKey) { + return Result.tryCatch(() -> this.indicatorRecordMapper.selectIdsByExample() + .leftJoin(ExamRecordDynamicSqlSupport.examRecord) + .on( + ExamRecordDynamicSqlSupport.id, + equalTo(IndicatorRecordDynamicSqlSupport.examId)) + .where( + ExamRecordDynamicSqlSupport.owner, + isEqualTo(userKey.modelId)) + .build() + .execute() + .stream() + .map(id -> new EntityKey(id, EntityType.EXAM)) + .collect(Collectors.toList())); + } + private Result> allIdsOfExam(final EntityKey examKey) { return Result.tryCatch(() -> this.indicatorRecordMapper.selectIdsByExample() .where( diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientConnectionController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientConnectionController.java index 6e62586b..95e07d79 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientConnectionController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientConnectionController.java @@ -9,6 +9,7 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api; import java.util.Collection; +import java.util.List; import org.mybatis.dynamic.sql.SqlTable; import org.springframework.web.bind.annotation.RequestMapping; @@ -52,7 +53,10 @@ public class ClientConnectionController extends ReadonlyEntityController getDependencies(final String modelId, final BulkActionType bulkActionType) { + public Collection getDependencies( + final String modelId, + final BulkActionType bulkActionType, + final List includes) { throw new UnsupportedOperationException(); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientEventController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientEventController.java index 705534c5..bcbaccc3 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientEventController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ClientEventController.java @@ -9,6 +9,7 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api; import java.util.Collection; +import java.util.List; import javax.servlet.http.HttpServletRequest; @@ -114,7 +115,10 @@ public class ClientEventController extends ReadonlyEntityController getDependencies(final String modelId, final BulkActionType bulkActionType) { + public Collection getDependencies( + final String modelId, + final BulkActionType bulkActionType, + final List includes) { throw new UnsupportedOperationException(); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationController.java index b92d9566..e2cca78c 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationController.java @@ -9,6 +9,7 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api; import java.util.Collection; +import java.util.List; import org.mybatis.dynamic.sql.SqlTable; import org.slf4j.Logger; @@ -110,7 +111,10 @@ public class ConfigurationController extends ReadonlyEntityController getDependencies(final String modelId, final BulkActionType bulkActionType) { + public Collection getDependencies( + final String modelId, + final BulkActionType bulkActionType, + final List includes) { throw new UnsupportedOperationException(); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationValueController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationValueController.java index 30740bd3..0e50cd9f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationValueController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ConfigurationValueController.java @@ -8,6 +8,7 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api; +import java.util.List; import java.util.Objects; import javax.validation.Valid; @@ -87,7 +88,9 @@ public class ConfigurationValueController extends EntityController includes) { throw new UnsupportedOperationException(); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java index 8ff49ae3..0565dc8d 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/EntityController.java @@ -10,7 +10,9 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api; import java.util.Arrays; import java.util.Collection; +import java.util.EnumSet; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; import javax.servlet.http.HttpServletRequest; @@ -209,7 +211,8 @@ public abstract class EntityController { produces = MediaType.APPLICATION_JSON_UTF8_VALUE) public Collection getDependencies( @PathVariable final String modelId, - @RequestParam(name = API.PARAM_BULK_ACTION_TYPE, required = true) final BulkActionType bulkActionType) { + @RequestParam(name = API.PARAM_BULK_ACTION_TYPE, required = true) final BulkActionType bulkActionType, + @RequestParam(name = API.PARAM_BULK_ACTION_INCLUDES, required = false) final List includes) { this.entityDAO .byModelId(modelId) @@ -218,7 +221,8 @@ public abstract class EntityController { final BulkAction bulkAction = new BulkAction( bulkActionType, this.entityDAO.entityType(), - Arrays.asList(new EntityKey(modelId, this.entityDAO.entityType()))); + Arrays.asList(new EntityKey(modelId, this.entityDAO.entityType())), + convertToEntityType(includes)); this.bulkActionService.collectDependencies(bulkAction); return bulkAction.getDependencies(); @@ -320,24 +324,48 @@ public abstract class EntityController { path = API.MODEL_ID_VAR_PATH_SEGMENT, method = RequestMethod.DELETE, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) - public EntityProcessingReport hardDelete(@PathVariable final String modelId) { + public EntityProcessingReport hardDelete( + @PathVariable final String modelId, + @RequestParam(name = API.PARAM_BULK_ACTION_INCLUDES, required = false) final List includes) { return this.entityDAO.byModelId(modelId) .flatMap(this::checkWriteAccess) .flatMap(this::validForDelete) - .flatMap(this::bulkDelete) + .flatMap(entity -> bulkDelete(entity, convertToEntityType(includes))) .flatMap(this::notifyDeleted) .flatMap(pair -> this.logBulkAction(pair.b)) .getOrThrow(); } - protected Result> bulkDelete(final T entity) { + protected EnumSet convertToEntityType(final List includes) { + final EnumSet includeDependencies = (includes != null) + ? EnumSet.copyOf(includes.stream().map(name -> { + try { + return EntityType.valueOf(name); + } catch (final Exception e) { + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toSet())) + : null; + return includeDependencies; + } + + protected Result> bulkDelete( + final T entity, + final EnumSet includeDependencies) { + + final BulkAction bulkAction = new BulkAction( + BulkActionType.HARD_DELETE, + entity.entityType(), + Arrays.asList(new EntityName(entity.getModelId(), entity.entityType(), entity.getName())), + includeDependencies); + return Result.tryCatch(() -> new Pair<>( entity, - this.bulkActionService.createReport(new BulkAction( - BulkActionType.HARD_DELETE, - entity.entityType(), - new EntityName(entity.getModelId(), entity.entityType(), entity.getName()))) + this.bulkActionService + .createReport(bulkAction) .getOrThrow())); } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamConfigurationMappingController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamConfigurationMappingController.java index f153c6d3..c022758f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamConfigurationMappingController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ExamConfigurationMappingController.java @@ -8,6 +8,8 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api; +import java.util.List; + import javax.servlet.http.HttpServletRequest; import org.mybatis.dynamic.sql.SqlTable; @@ -162,13 +164,15 @@ public class ExamConfigurationMappingController extends EntityController includes) { return this.entityDAO.byModelId(modelId) .flatMap(this::checkWriteAccess) .flatMap(entity -> this.examConfigUpdateService.processExamConfigurationMappingChange( entity, - this::bulkDelete)) + e -> bulkDelete(e, convertToEntityType(includes)))) .flatMap(this::notifyDeleted) .flatMap(pair -> this.logBulkAction(pair.b)) .getOrThrow(); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ReadonlyEntityController.java b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ReadonlyEntityController.java index 768ed4d0..c81f40ea 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ReadonlyEntityController.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/weblayer/api/ReadonlyEntityController.java @@ -8,6 +8,8 @@ package ch.ethz.seb.sebserver.webservice.weblayer.api; +import java.util.List; + import javax.servlet.http.HttpServletRequest; import javax.validation.Valid; @@ -59,7 +61,9 @@ public abstract class ReadonlyEntityController includes) { throw new UnsupportedOperationException(ONLY_READ_ACCESS); }