SEBSERV-10 back-end implementation
This commit is contained in:
		
							parent
							
								
									d7f79fb3cc
								
							
						
					
					
						commit
						2a0afe902a
					
				
					 17 changed files with 349 additions and 32 deletions
				
			
		|  | @ -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"; | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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<EntityKey> 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<EntityType> includeDependencies; | ||||
|     /** A Set of EntityKey containing collected depending entities during dependency collection and processing phase */ | ||||
|     final Set<EntityKey> 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<EntityKey> sources) { | ||||
|         this(type, sourceType, sources, null); | ||||
|     } | ||||
| 
 | ||||
|     public BulkAction( | ||||
|             final BulkActionType type, | ||||
|             final EntityType sourceType, | ||||
|             final Collection<EntityKey> sources, | ||||
|             final EnumSet<EntityType> 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<EntityKey> getDependencies() { | ||||
|         return Collections.unmodifiableSet(this.dependencies); | ||||
|     } | ||||
|  |  | |||
|  | @ -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<EntityType, EnumSet<EntityType>> directDependancyMap = | ||||
|             new EnumMap<>(EntityType.class); | ||||
| 
 | ||||
|     private final Map<EntityType, BulkActionSupportDAO<?>> 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"); | ||||
|         } | ||||
|  |  | |||
|  | @ -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<ClientConnection, ClientConnection> { | ||||
| public interface ClientConnectionDAO extends | ||||
|         EntityDAO<ClientConnection, ClientConnection>, | ||||
|         BulkActionSupportDAO<ClientConnection> { | ||||
| 
 | ||||
|     String CONNECTION_TOKENS_CACHE = "CONNECTION_TOKENS_CACHE"; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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<EntityKey> 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<EntityKey, Result<Collection<EntityKey>>> 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<Collection<EntityKey>> delete(final Set<EntityKey> all) { | ||||
|  | @ -186,6 +228,23 @@ public class ClientConnectionDAOImpl implements ClientConnectionDAO { | |||
|                     .build() | ||||
|                     .execute(); | ||||
| 
 | ||||
|             // then delete all related client instructions | ||||
|             final List<String> 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<Collection<EntityKey>> 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<Collection<EntityKey>> 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<Collection<EntityKey>> 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<Collection<EntityKey>> 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())); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
|  | @ -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<EntityKey> 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<EntityKey, Result<Collection<EntityKey>>> selectionFunction = | ||||
|                 (bulkAction.sourceType == EntityType.INSTITUTION) | ||||
|                         ? this::allIdsOfInstitution | ||||
|                         : key -> Result.of(Collections.emptyList()); // else : empty select function | ||||
|         Function<EntityKey, Result<Collection<EntityKey>>> 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<Collection<EntityKey>> 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<ConfigurationNodeRecord> recordById(final Long id) { | ||||
|         return Result.tryCatch(() -> { | ||||
|             final ConfigurationNodeRecord record = this.configurationNodeRecordMapper | ||||
|  |  | |||
|  | @ -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<EntityKey> 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<EntityKey, Result<Collection<EntityKey>>> 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<Collection<EntityKey>> allIdsOfUser(final EntityKey userKey) { | ||||
|         return Result.tryCatch(() -> { | ||||
|             final List<Long> 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<Collection<EntityKey>> allIdsOfLmsSetup(final EntityKey lmsSetupKey) { | ||||
|         return Result.tryCatch(() -> this.examConfigurationMapRecordMapper.selectIdsByExample() | ||||
|                 .leftJoin(ExamRecordDynamicSqlSupport.examRecord) | ||||
|  |  | |||
|  | @ -588,6 +588,10 @@ public class ExamDAOImpl implements ExamDAO { | |||
|     @Override | ||||
|     @Transactional(readOnly = true) | ||||
|     public Set<EntityKey> 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<EntityKey, Result<Collection<EntityKey>>> 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<Collection<EntityKey>> 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<ExamRecord> recordById(final Long id) { | ||||
|         return Result.tryCatch(() -> { | ||||
|             final ExamRecord record = this.examRecordMapper.selectByPrimaryKey(id); | ||||
|  |  | |||
|  | @ -221,9 +221,14 @@ public class IndicatorDAOImpl implements IndicatorDAO { | |||
|     @Override | ||||
|     @Transactional(readOnly = true) | ||||
|     public Set<EntityKey> 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<EntityKey, Result<Collection<EntityKey>>> 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<Collection<EntityKey>> 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<Collection<EntityKey>> allIdsOfExam(final EntityKey examKey) { | ||||
|         return Result.tryCatch(() -> this.indicatorRecordMapper.selectIdsByExample() | ||||
|                 .where( | ||||
|  |  | |||
|  | @ -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<ClientC | |||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public Collection<EntityKey> getDependencies(final String modelId, final BulkActionType bulkActionType) { | ||||
|     public Collection<EntityKey> getDependencies( | ||||
|             final String modelId, | ||||
|             final BulkActionType bulkActionType, | ||||
|             final List<String> includes) { | ||||
|         throw new UnsupportedOperationException(); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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<ClientEvent, | |||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public Collection<EntityKey> getDependencies(final String modelId, final BulkActionType bulkActionType) { | ||||
|     public Collection<EntityKey> getDependencies( | ||||
|             final String modelId, | ||||
|             final BulkActionType bulkActionType, | ||||
|             final List<String> includes) { | ||||
|         throw new UnsupportedOperationException(); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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<Configurat | |||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public Collection<EntityKey> getDependencies(final String modelId, final BulkActionType bulkActionType) { | ||||
|     public Collection<EntityKey> getDependencies( | ||||
|             final String modelId, | ||||
|             final BulkActionType bulkActionType, | ||||
|             final List<String> includes) { | ||||
|         throw new UnsupportedOperationException(); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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<Configuration | |||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public EntityProcessingReport hardDelete(final String modelId) { | ||||
|     public EntityProcessingReport hardDelete( | ||||
|             final String modelId, | ||||
|             final List<String> includes) { | ||||
|         throw new UnsupportedOperationException(); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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<T extends Entity, M extends Entity> { | |||
|             produces = MediaType.APPLICATION_JSON_UTF8_VALUE) | ||||
|     public Collection<EntityKey> 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<String> includes) { | ||||
| 
 | ||||
|         this.entityDAO | ||||
|                 .byModelId(modelId) | ||||
|  | @ -218,7 +221,8 @@ public abstract class EntityController<T extends Entity, M extends Entity> { | |||
|         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<T extends Entity, M extends Entity> { | |||
|             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<String> 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<Pair<T, EntityProcessingReport>> bulkDelete(final T entity) { | ||||
|         return Result.tryCatch(() -> new Pair<>( | ||||
|                 entity, | ||||
|                 this.bulkActionService.createReport(new BulkAction( | ||||
|     protected EnumSet<EntityType> convertToEntityType(final List<String> includes) { | ||||
|         final EnumSet<EntityType> 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<Pair<T, EntityProcessingReport>> bulkDelete( | ||||
|             final T entity, | ||||
|             final EnumSet<EntityType> includeDependencies) { | ||||
| 
 | ||||
|         final BulkAction bulkAction = new BulkAction( | ||||
|                 BulkActionType.HARD_DELETE, | ||||
|                 entity.entityType(), | ||||
|                         new EntityName(entity.getModelId(), entity.entityType(), entity.getName()))) | ||||
|                 Arrays.asList(new EntityName(entity.getModelId(), entity.entityType(), entity.getName())), | ||||
|                 includeDependencies); | ||||
| 
 | ||||
|         return Result.tryCatch(() -> new Pair<>( | ||||
|                 entity, | ||||
|                 this.bulkActionService | ||||
|                         .createReport(bulkAction) | ||||
|                         .getOrThrow())); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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<ExamCon | |||
|             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<String> 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(); | ||||
|  |  | |||
|  | @ -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<T extends Entity, M extends Entit | |||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public EntityProcessingReport hardDelete(final String modelId) { | ||||
|     public EntityProcessingReport hardDelete( | ||||
|             final String modelId, | ||||
|             final List<String> includes) { | ||||
|         throw new UnsupportedOperationException(ONLY_READ_ACCESS); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 anhefti
						anhefti