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.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<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()
.where(ExamRecordDynamicSqlSupport.id, isIn(ids))
.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
public Result<Collection<EntityKey>> deleteRooms(final Long examId) {
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))
.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()

View file

@ -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<Exam> 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

View file

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

View file

@ -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<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(() -> {
log.info("Dispose and deleting proctoring rooms for exam: {}", exam.externalId);
final ProctoringServiceSettings proctoringSettings = this.examAdminService
.getProctoringServiceSettings(exam.id)
.getOrThrow();

View file

@ -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));