Merge remote-tracking branch 'origin/dev-1.2' into development

Conflicts:
	src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/ExamAdminService.java
	src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/ExamAdminServiceImpl.java
This commit is contained in:
anhefti 2021-10-07 10:50:59 +02:00
commit b1181bb97a
7 changed files with 73 additions and 8 deletions

View file

@ -30,6 +30,7 @@ import org.joda.time.DateTime;
import org.mybatis.dynamic.sql.SqlBuilder; import org.mybatis.dynamic.sql.SqlBuilder;
import org.mybatis.dynamic.sql.select.MyBatis3SelectModelAdapter; import org.mybatis.dynamic.sql.select.MyBatis3SelectModelAdapter;
import org.mybatis.dynamic.sql.select.QueryExpressionDSL; import org.mybatis.dynamic.sql.select.QueryExpressionDSL;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Propagation;
@ -78,17 +79,20 @@ public class ExamDAOImpl implements ExamDAO {
private final ExamRecordMapper examRecordMapper; private final ExamRecordMapper examRecordMapper;
private final ClientConnectionRecordMapper clientConnectionRecordMapper; private final ClientConnectionRecordMapper clientConnectionRecordMapper;
private final AdditionalAttributeRecordMapper additionalAttributeRecordMapper; private final AdditionalAttributeRecordMapper additionalAttributeRecordMapper;
private final ApplicationEventPublisher applicationEventPublisher;
private final LmsAPIService lmsAPIService; private final LmsAPIService lmsAPIService;
public ExamDAOImpl( public ExamDAOImpl(
final ExamRecordMapper examRecordMapper, final ExamRecordMapper examRecordMapper,
final ClientConnectionRecordMapper clientConnectionRecordMapper, final ClientConnectionRecordMapper clientConnectionRecordMapper,
final AdditionalAttributeRecordMapper additionalAttributeRecordMapper, final AdditionalAttributeRecordMapper additionalAttributeRecordMapper,
final ApplicationEventPublisher applicationEventPublisher,
final LmsAPIService lmsAPIService) { final LmsAPIService lmsAPIService) {
this.examRecordMapper = examRecordMapper; this.examRecordMapper = examRecordMapper;
this.clientConnectionRecordMapper = clientConnectionRecordMapper; this.clientConnectionRecordMapper = clientConnectionRecordMapper;
this.additionalAttributeRecordMapper = additionalAttributeRecordMapper; this.additionalAttributeRecordMapper = additionalAttributeRecordMapper;
this.applicationEventPublisher = applicationEventPublisher;
this.lmsAPIService = lmsAPIService; this.lmsAPIService = lmsAPIService;
} }
@ -651,6 +655,9 @@ public class ExamDAOImpl implements ExamDAO {
final List<Long> ids = extractListOfPKs(all); final List<Long> ids = extractListOfPKs(all);
// notify exam deletition listener about following deletion, to cleanup stuff before deletion
this.applicationEventPublisher.publishEvent(new ExamDeletionEvent(ids));
this.examRecordMapper.deleteByExample() this.examRecordMapper.deleteByExample()
.where(ExamRecordDynamicSqlSupport.id, isIn(ids)) .where(ExamRecordDynamicSqlSupport.id, isIn(ids))
.build() .build()

View file

@ -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<Long> ids;
public ExamDeletionEvent(final List<Long> ids) {
super(ids);
this.ids = Utils.immutableListOf(ids);
}
}

View file

@ -246,11 +246,17 @@ public class RemoteProctoringRoomDAOImpl implements RemoteProctoringRoomDAO {
@Transactional @Transactional
public Result<Collection<EntityKey>> deleteRooms(final Long examId) { public Result<Collection<EntityKey>> deleteRooms(final Long examId) {
final Result<Collection<EntityKey>> tryCatch = Result.tryCatch(() -> { final Result<Collection<EntityKey>> tryCatch = Result.tryCatch(() -> {
final List<Long> ids = this.remoteProctoringRoomRecordMapper.selectIdsByExample() final List<Long> ids = this.remoteProctoringRoomRecordMapper
.selectIdsByExample()
.where(RemoteProctoringRoomRecordDynamicSqlSupport.examId, isEqualTo(examId)) .where(RemoteProctoringRoomRecordDynamicSqlSupport.examId, isEqualTo(examId))
.build() .build()
.execute(); .execute();
if (ids == null || ids.isEmpty()) {
log.info("No proctoring rooms found for exam to delete: {}", examId);
return Collections.emptyList();
}
this.remoteProctoringRoomRecordMapper.deleteByExample() this.remoteProctoringRoomRecordMapper.deleteByExample()
.where(RemoteProctoringRoomRecordDynamicSqlSupport.id, isIn(ids)) .where(RemoteProctoringRoomRecordDynamicSqlSupport.id, isIn(ids))
.build() .build()

View file

@ -16,6 +16,12 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringServi
public interface ExamAdminService { 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<Exam> examForPK(Long examId);
/** Saves additional attributes for the exam that are specific to a type of LMS /** Saves additional attributes for the exam that are specific to a type of LMS
* *
* @param exam The Exam to add the LMS specific attributes * @param exam The Exam to add the LMS specific attributes

View file

@ -76,7 +76,11 @@ public class ExamAdminServiceImpl implements ExamAdminService {
this.cryptor = cryptor; this.cryptor = cryptor;
this.examProctoringServiceFactory = examProctoringServiceFactory; this.examProctoringServiceFactory = examProctoringServiceFactory;
this.remoteProctoringRoomDAO = remoteProctoringRoomDAO; this.remoteProctoringRoomDAO = remoteProctoringRoomDAO;
}
@Override
public Result<Exam> examForPK(final Long examId) {
return this.examDAO.byPK(examId);
} }
@Override @Override

View file

@ -21,12 +21,12 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import ch.ethz.seb.sebserver.gbl.Constants; import ch.ethz.seb.sebserver.gbl.Constants;
import ch.ethz.seb.sebserver.gbl.model.EntityKey; 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;
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.ProctoringRoomConnection;
import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings; import ch.ethz.seb.sebserver.gbl.model.exam.ProctoringServiceSettings;
import ch.ethz.seb.sebserver.gbl.model.session.ClientConnection; 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.datalayer.batis.model.ClientConnectionRecord;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ClientConnectionDAO; 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.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.exam.ExamAdminService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringRoomService; import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringRoomService;
import ch.ethz.seb.sebserver.webservice.servicelayer.session.ExamProctoringService; 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 @Override
public Result<Exam> disposeRoomsForExam(final Exam exam) { public Result<Exam> 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(() -> { return Result.tryCatch(() -> {
log.info("Dispose and deleting proctoring rooms for exam: {}", exam.externalId);
final ProctoringServiceSettings proctoringSettings = this.examAdminService final ProctoringServiceSettings proctoringSettings = this.examAdminService
.getProctoringServiceSettings(exam.id) .getProctoringServiceSettings(exam.id)
.getOrThrow(); .getOrThrow();

View file

@ -685,6 +685,7 @@ public class ZoomProctoringService implements ExamProctoringService {
private final static class ZoomRestTemplate { 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_TEST_ENDPOINT = "v2/users";
private static final String API_CREATE_USER_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"; 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, API_USER_CUST_CREATE,
new CreateUserRequest.UserInfo( new CreateUserRequest.UserInfo(
roomName + "@" + host, roomName + "@" + host,
1, LIZENSED_USER,
roomName, roomName,
API_ZOOM_ROOM_USER)); API_ZOOM_ROOM_USER));