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 e4df65d9..fae8d9df 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 @@ -30,6 +30,7 @@ import org.joda.time.DateTime; import org.mybatis.dynamic.sql.SqlBuilder; import org.mybatis.dynamic.sql.select.MyBatis3SelectModelAdapter; import org.mybatis.dynamic.sql.select.QueryExpressionDSL; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Propagation; @@ -78,17 +79,20 @@ public class ExamDAOImpl implements ExamDAO { private final ExamRecordMapper examRecordMapper; private final ClientConnectionRecordMapper clientConnectionRecordMapper; private final AdditionalAttributeRecordMapper additionalAttributeRecordMapper; + private final ApplicationEventPublisher applicationEventPublisher; private final LmsAPIService lmsAPIService; public ExamDAOImpl( final ExamRecordMapper examRecordMapper, final ClientConnectionRecordMapper clientConnectionRecordMapper, final AdditionalAttributeRecordMapper additionalAttributeRecordMapper, + final ApplicationEventPublisher applicationEventPublisher, final LmsAPIService lmsAPIService) { this.examRecordMapper = examRecordMapper; this.clientConnectionRecordMapper = clientConnectionRecordMapper; this.additionalAttributeRecordMapper = additionalAttributeRecordMapper; + this.applicationEventPublisher = applicationEventPublisher; this.lmsAPIService = lmsAPIService; } @@ -651,6 +655,9 @@ public class ExamDAOImpl implements ExamDAO { final List ids = extractListOfPKs(all); + // notify exam deletition listener about following deletion, to cleanup stuff before deletion + this.applicationEventPublisher.publishEvent(new ExamDeletionEvent(ids)); + this.examRecordMapper.deleteByExample() .where(ExamRecordDynamicSqlSupport.id, isIn(ids)) .build() diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDeletionEvent.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDeletionEvent.java new file mode 100644 index 00000000..0c2e2f11 --- /dev/null +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDeletionEvent.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl; + +import java.util.List; + +import org.springframework.context.ApplicationEvent; + +import ch.ethz.seb.sebserver.gbl.util.Utils; + +public class ExamDeletionEvent extends ApplicationEvent { + + private static final long serialVersionUID = -8910368901893082365L; + + public final List ids; + + public ExamDeletionEvent(final List ids) { + super(ids); + this.ids = Utils.immutableListOf(ids); + } + +} diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/RemoteProctoringRoomDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/RemoteProctoringRoomDAOImpl.java index 9f11d9a0..2d4fef85 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/RemoteProctoringRoomDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/RemoteProctoringRoomDAOImpl.java @@ -246,11 +246,17 @@ public class RemoteProctoringRoomDAOImpl implements RemoteProctoringRoomDAO { @Transactional public Result> deleteRooms(final Long examId) { final Result> tryCatch = Result.tryCatch(() -> { - final List ids = this.remoteProctoringRoomRecordMapper.selectIdsByExample() + final List ids = this.remoteProctoringRoomRecordMapper + .selectIdsByExample() .where(RemoteProctoringRoomRecordDynamicSqlSupport.examId, isEqualTo(examId)) .build() .execute(); + if (ids == null || ids.isEmpty()) { + log.info("No proctoring rooms found for exam to delete: {}", examId); + return Collections.emptyList(); + } + this.remoteProctoringRoomRecordMapper.deleteByExample() .where(RemoteProctoringRoomRecordDynamicSqlSupport.id, isIn(ids)) .build() diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminService.java index 2856f162..c403bc0b 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminService.java @@ -16,6 +16,12 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringServi public interface ExamAdminService { + /** Get the exam domain object for the exam identifier (PK). + * + * @param examId the exam identifier + * @return Result refer to the domain object or to an error when happened */ + Result examForPK(Long examId); + /** Saves additional attributes for the exam that are specific to a type of LMS * * @param exam The Exam to add the LMS specific attributes diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamAdminServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamAdminServiceImpl.java index 5a2515b8..7fb6a265 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamAdminServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamAdminServiceImpl.java @@ -76,7 +76,11 @@ public class ExamAdminServiceImpl implements ExamAdminService { this.cryptor = cryptor; this.examProctoringServiceFactory = examProctoringServiceFactory; this.remoteProctoringRoomDAO = remoteProctoringRoomDAO; + } + @Override + public Result examForPK(final Long examId) { + return this.examDAO.byPK(examId); } @Override diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamProctoringRoomServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamProctoringRoomServiceImpl.java index 770e276f..90d2c1d0 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamProctoringRoomServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ExamProctoringRoomServiceImpl.java @@ -21,12 +21,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Lazy; +import org.springframework.context.event.EventListener; import org.springframework.stereotype.Service; import ch.ethz.seb.sebserver.gbl.Constants; 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.ExamStatus; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringRoomConnection; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings; import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; @@ -38,6 +38,7 @@ import ch.ethz.seb.sebserver.gbl.util.Result; import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ClientConnectionRecord; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO; import ch.ethz.seb.sebserver.webservice.servicelayer.dao.RemoteProctoringRoomDAO; +import ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl.ExamDeletionEvent; import ch.ethz.seb.sebserver.webservice.servicelayer.exam.ExamAdminService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringRoomService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringService; @@ -137,16 +138,28 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService } } + @EventListener(ExamDeletionEvent.class) + public void notifyExamDeletionEvent(final ExamDeletionEvent event) { + event.ids.forEach(examId -> { + try { + + this.examAdminService.examForPK(examId) + .flatMap(this::disposeRoomsForExam) + .getOrThrow(); + + } catch (final Exception e) { + log.error("Failed to delete depending proctoring data for exam: {}", examId, e); + } + }); + } + @Override public Result disposeRoomsForExam(final Exam exam) { - if (exam.status != ExamStatus.FINISHED) { - log.warn("The exam has not been finished yet. No proctoring rooms will be deleted: {} / {}", - exam.name, - exam.externalId); - } return Result.tryCatch(() -> { + log.info("Dispose and deleting proctoring rooms for exam: {}", exam.externalId); + final ProctoringServiceSettings proctoringSettings = this.examAdminService .getProctoringServiceSettings(exam.id) .getOrThrow(); diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ZoomProctoringService.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ZoomProctoringService.java index 836bcc91..e4157f39 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ZoomProctoringService.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/session/impl/proctoring/ZoomProctoringService.java @@ -685,6 +685,7 @@ public class ZoomProctoringService implements ExamProctoringService { private final static class ZoomRestTemplate { + private static final int LIZENSED_USER = 2; private static final String API_TEST_ENDPOINT = "v2/users"; private static final String API_CREATE_USER_ENDPOINT = "v2/users"; private static final String API_DELETE_USER_ENDPOINT = "v2/users/{userid}?action=delete"; @@ -749,7 +750,7 @@ public class ZoomProctoringService implements ExamProctoringService { API_USER_CUST_CREATE, new CreateUserRequest.UserInfo( roomName + "@" + host, - 1, + LIZENSED_USER, roomName, API_ZOOM_ROOM_USER));